OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
FluctuatingChargeConstraints.cpp
1/*
2 * Copyright (c) 2004-present, The University of Notre Dame. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * 3. Neither the name of the copyright holder nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
32 * research, please cite the following paper when you publish your work:
33 *
34 * [1] Drisko et al., J. Open Source Softw. 9, 7004 (2024).
35 *
36 * Good starting points for code and simulation methodology are:
37 *
38 * [2] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
39 * [3] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
40 * [4] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
41 * [5] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
42 * [6] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
43 * [7] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
44 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
45 * [9] Drisko & Gezelter, J. Chem. Theory Comput. 20, 4986-4997 (2024).
46 */
47
48#include "FluctuatingChargeConstraints.hpp"
49
50#ifdef IS_MPI
51#include <mpi.h>
52#endif
53
55
56namespace OpenMD {
57
58 FluctuatingChargeConstraints::FluctuatingChargeConstraints(SimInfo* info) :
59 info_(info), initialized_(false), hasFlucQ_(false),
60 constrainRegions_(false) {}
61
62 void FluctuatingChargeConstraints::initialize() {
63 if (info_->usesFluctuatingCharges()) {
64 if (info_->getNFluctuatingCharges() > 0) { hasFlucQ_ = true; }
65 }
66 initialized_ = true;
67 }
68
69 void FluctuatingChargeConstraints::setConstrainRegions(bool cr) {
70 constrainRegions_ = cr;
71
72 if (!initialized_) initialize();
73
74 regionKeys_.clear();
75 regionForce_.clear();
76 regionCMom_.clear();
77 regionCharges_.clear();
78
79 if (constrainRegions_) {
80 std::vector<int> localRegions = info_->getRegions();
81
82#ifdef IS_MPI
83 int size;
84 MPI_Comm_size(MPI_COMM_WORLD, &size);
85 int mylen = localRegions.size();
86
87 std::vector<int> counts;
88 std::vector<int> displs;
89
90 counts.resize(size, 0);
91 displs.resize(size, 0);
92
93 MPI_Allgather(&mylen, 1, MPI_INT, &counts[0], 1, MPI_INT, MPI_COMM_WORLD);
94
95 int total = counts[0];
96
97 for (int i = 1; i < size; i++) {
98 total += counts[i];
99 displs[i] = displs[i - 1] + counts[i - 1];
100 }
101
102 std::vector<int> globalRegions(total, 0);
103
104 MPI_Allgatherv(&localRegions[0], mylen, MPI_INT, &globalRegions[0],
105 &counts[0], &displs[0], MPI_INT, MPI_COMM_WORLD);
106
107 localRegions = globalRegions;
108#endif
109
110 std::set<int> regions;
111 std::vector<int>::iterator iter;
112 for (iter = localRegions.begin(); iter != localRegions.end(); ++iter) {
113 if (*iter >= 0) regions.insert(*iter);
114 }
115
116 // resize the keys vector to the largest found value for regions.
117 regionKeys_.resize(*(regions.end()));
118 int which = 0;
119 for (std::set<int>::iterator r = regions.begin(); r != regions.end();
120 ++r) {
121 regionKeys_[(*r)] = which;
122 which++;
123 }
124 regionForce_.resize(regionKeys_.size());
125 regionCMom_.resize(regionKeys_.size());
126 regionCharges_.resize(regionKeys_.size());
127 regionChargeMass_.resize(regionKeys_.size());
128 }
129 }
130
131 void FluctuatingChargeConstraints::applyConstraints() {
132 if (!initialized_) initialize();
133 if (!hasFlucQ_) return;
134
135 SimInfo::MoleculeIterator i;
136 Molecule::FluctuatingChargeIterator j;
137 Molecule* mol;
138 Atom* atom;
139
140 RealType frc, systemFrc, molFrc, regionFrc;
141 int systemCharges;
142
143 // accumulate the system fluctuating charge forces, but we have
144 // separate constraints for any charges in defined regions and for
145 // molecules with constrained charges:
146
147 systemFrc = 0.0;
148 systemCharges = 0;
149 if (constrainRegions_) {
150 std::fill(regionForce_.begin(), regionForce_.end(), 0.0);
151 std::fill(regionCharges_.begin(), regionCharges_.end(), 0);
152 }
153
154 for (mol = info_->beginMolecule(i); mol != NULL;
155 mol = info_->nextMolecule(i)) {
156 if (!mol->constrainTotalCharge()) {
157 int region = mol->getRegion();
158
159 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
160 atom = mol->nextFluctuatingCharge(j)) {
161 frc = atom->getFlucQFrc();
162 if (constrainRegions_ && region >= 0) {
163 regionForce_[regionKeys_[region]] += frc;
164 regionCharges_[regionKeys_[region]] += 1;
165 } else {
166 systemFrc += frc;
167 systemCharges += 1;
168 }
169 }
170 }
171 }
172
173#ifdef IS_MPI
174 // in parallel, we need to add up the contributions from all
175 // processors:
176 MPI_Allreduce(MPI_IN_PLACE, &systemFrc, 1, MPI_REALTYPE, MPI_SUM,
177 MPI_COMM_WORLD);
178 MPI_Allreduce(MPI_IN_PLACE, &systemCharges, 1, MPI_INT, MPI_SUM,
179 MPI_COMM_WORLD);
180
181 if (constrainRegions_) {
182 MPI_Allreduce(MPI_IN_PLACE, &regionForce_[0], regionForce_.size(),
183 MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
184 MPI_Allreduce(MPI_IN_PLACE, &regionCharges_[0], regionCharges_.size(),
185 MPI_INT, MPI_SUM, MPI_COMM_WORLD);
186 }
187
188#endif
189
190 // divide by the total number of fluctuating charges:
191 systemFrc /= systemCharges;
192
193 // do the same in the regions:
194 if (constrainRegions_) {
195 for (unsigned int i = 0; i < regionForce_.size(); ++i) {
196 regionForce_[i] /= regionCharges_[i];
197 }
198 }
199
200 for (mol = info_->beginMolecule(i); mol != NULL;
201 mol = info_->nextMolecule(i)) {
202 molFrc = 0.0;
203
204 if (mol->constrainTotalCharge()) {
205 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
206 atom = mol->nextFluctuatingCharge(j)) {
207 molFrc += atom->getFlucQFrc();
208 }
209 molFrc /= mol->getNFluctuatingCharges();
210
211 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
212 atom = mol->nextFluctuatingCharge(j)) {
213 frc = atom->getFlucQFrc() - molFrc;
214 atom->setFlucQFrc(frc);
215 }
216 } else {
217 int region = mol->getRegion();
218
219 regionFrc = 0.0;
220 if (constrainRegions_ && region >= 0) {
221 regionFrc = regionForce_[regionKeys_[region]];
222
223 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
224 atom = mol->nextFluctuatingCharge(j)) {
225 frc = atom->getFlucQFrc() - regionFrc;
226 atom->setFlucQFrc(frc);
227 }
228 } else {
229 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
230 atom = mol->nextFluctuatingCharge(j)) {
231 frc = atom->getFlucQFrc() - systemFrc;
232 atom->setFlucQFrc(frc);
233 }
234 }
235 }
236 }
237 }
238
239 void FluctuatingChargeConstraints::applyConstraintsOnChargeVelocities() {
240 if (!initialized_) initialize();
241 if (!hasFlucQ_) return;
242
243 SimInfo::MoleculeIterator i;
244 Molecule::FluctuatingChargeIterator j;
245 Molecule* mol;
246 Atom* atom;
247
248 RealType flucqP, systemCMom, regionCMom, molCMom, molFlucQMass, flucqW;
249 RealType systemChargeMass;
250
251 // accumulate the system fluctuating charge velocities, but we have
252 // separate constraints for any charges in defined regions and for
253 // molecules with constrained charges:
254
255 systemCMom = 0.0;
256 systemChargeMass = 0.0;
257 if (constrainRegions_) {
258 std::fill(regionCMom_.begin(), regionCMom_.end(), 0.0);
259 std::fill(regionChargeMass_.begin(), regionChargeMass_.end(), 0);
260 }
261
262 for (mol = info_->beginMolecule(i); mol != NULL;
263 mol = info_->nextMolecule(i)) {
264 if (!mol->constrainTotalCharge()) {
265 int region = mol->getRegion();
266
267 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
268 atom = mol->nextFluctuatingCharge(j)) {
269 flucqP = atom->getFlucQVel() * atom->getChargeMass();
270 if (constrainRegions_ && region >= 0) {
271 regionCMom_[regionKeys_[region]] += flucqP;
272 regionChargeMass_[regionKeys_[region]] += atom->getChargeMass();
273 } else {
274 systemCMom += flucqP;
275 systemChargeMass += atom->getChargeMass();
276 }
277 }
278 }
279 }
280
281#ifdef IS_MPI
282 // in parallel, we need to add up the contributions from all
283 // processors:
284 MPI_Allreduce(MPI_IN_PLACE, &systemCMom, 1, MPI_REALTYPE, MPI_SUM,
285 MPI_COMM_WORLD);
286 MPI_Allreduce(MPI_IN_PLACE, &systemChargeMass, 1, MPI_REALTYPE, MPI_SUM,
287 MPI_COMM_WORLD);
288
289 if (constrainRegions_) {
290 MPI_Allreduce(MPI_IN_PLACE, &regionCMom_[0], regionCMom_.size(),
291 MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
292 MPI_Allreduce(MPI_IN_PLACE, &regionChargeMass_[0],
293 regionChargeMass_.size(), MPI_REALTYPE, MPI_SUM,
294 MPI_COMM_WORLD);
295 }
296#endif
297
298 // divide by the total number of fluctuating charges:
299 systemCMom /= systemChargeMass;
300
301 // do the same in the regions:
302 if (constrainRegions_) {
303 for (unsigned int i = 0; i < regionCMom_.size(); ++i) {
304 regionCMom_[i] /= regionChargeMass_[i];
305 }
306 }
307
308 for (mol = info_->beginMolecule(i); mol != NULL;
309 mol = info_->nextMolecule(i)) {
310 molCMom = 0.0;
311 molFlucQMass = 0.0;
312
313 if (mol->constrainTotalCharge()) {
314 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
315 atom = mol->nextFluctuatingCharge(j)) {
316 molCMom += atom->getFlucQVel() * atom->getChargeMass();
317 molFlucQMass += atom->getChargeMass();
318 }
319 molCMom /= molFlucQMass;
320
321 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
322 atom = mol->nextFluctuatingCharge(j)) {
323 flucqW = atom->getFlucQVel() - molCMom;
324 atom->setFlucQVel(flucqW);
325 }
326 } else {
327 int region = mol->getRegion();
328
329 regionCMom = 0.0;
330 if (constrainRegions_ && region >= 0) {
331 regionCMom = regionCMom_[regionKeys_[region]];
332
333 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
334 atom = mol->nextFluctuatingCharge(j)) {
335 flucqW = atom->getFlucQVel() - regionCMom;
336 atom->setFlucQVel(flucqW);
337 }
338 } else {
339 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
340 atom = mol->nextFluctuatingCharge(j)) {
341 flucqW = atom->getFlucQVel() - systemCMom;
342 atom->setFlucQVel(flucqW);
343 }
344 }
345 }
346 }
347 }
348
349 int FluctuatingChargeConstraints::getNumberOfFlucQConstraints() {
350 int nConstraints = 0;
351 if (!initialized_) initialize();
352 if (!hasFlucQ_) return 0;
353 SimInfo::MoleculeIterator i;
354 Molecule* mol;
355 int systemConstrain = 0;
356 for (mol = info_->beginMolecule(i); mol != NULL;
357 mol = info_->nextMolecule(i)) {
358 if (mol->constrainTotalCharge()) {
359 nConstraints++;
360 } else {
361 int region = mol->getRegion();
362 if (!constrainRegions_ || region < 0) { systemConstrain = 1; }
363 }
364 }
365 return nConstraints + regionCMom_.size() + systemConstrain;
366 }
367
368 int FluctuatingChargeConstraints::getNumberOfFlucQAtoms() {
369 int nFlucq = 0;
370 if (!initialized_) initialize();
371 if (!hasFlucQ_) return 0;
372 SimInfo::MoleculeIterator i;
373 Molecule::FluctuatingChargeIterator j;
374 Molecule* mol;
375 Atom* atom;
376 for (mol = info_->beginMolecule(i); mol != NULL;
377 mol = info_->nextMolecule(i)) {
378 if (mol->constrainTotalCharge()) {
379 nFlucq += mol->getNFluctuatingCharges();
380 } else {
381 int region = mol->getRegion();
382 if (constrainRegions_ && region >= 0) {
383 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
384 atom = mol->nextFluctuatingCharge(j)) {
385 nFlucq++;
386 }
387 } else {
388 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
389 atom = mol->nextFluctuatingCharge(j)) {
390 nFlucq++;
391 }
392 }
393 }
394 }
395 return nFlucq;
396 }
397} // namespace OpenMD
One of the heavy-weight classes of OpenMD, SimInfo maintains objects and variables relating to the cu...
Definition SimInfo.hpp:96
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.