OpenMD 3.0
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 appropriate papers when you publish your
33 * work. Good starting points are:
34 *
35 * [1] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
36 * [2] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
37 * [3] Sun, Lin & Gezelter, J. Chem. Phys. 128, 234107 (2008).
38 * [4] Vardeman, Stocker & Gezelter, J. Chem. Theory Comput. 7, 834 (2011).
39 * [5] Kuang & Gezelter, Mol. Phys., 110, 691-701 (2012).
40 * [6] Lamichhane, Gezelter & Newman, J. Chem. Phys. 141, 134109 (2014).
41 * [7] Lamichhane, Newman & Gezelter, J. Chem. Phys. 141, 134110 (2014).
42 * [8] Bhattarai, Newman & Gezelter, Phys. Rev. B 99, 094106 (2019).
43 */
44
45#include "FluctuatingChargeConstraints.hpp"
46
47#ifdef IS_MPI
48#include <mpi.h>
49#endif
50
52
53namespace OpenMD {
54
55 FluctuatingChargeConstraints::FluctuatingChargeConstraints(SimInfo* info) :
56 info_(info), initialized_(false), hasFlucQ_(false),
57 constrainRegions_(false) {}
58
59 void FluctuatingChargeConstraints::initialize() {
60 if (info_->usesFluctuatingCharges()) {
61 if (info_->getNFluctuatingCharges() > 0) { hasFlucQ_ = true; }
62 }
63 initialized_ = true;
64 }
65
66 void FluctuatingChargeConstraints::setConstrainRegions(bool cr) {
67 constrainRegions_ = cr;
68
69 if (!initialized_) initialize();
70
71 regionKeys_.clear();
72 regionForce_.clear();
73 regionCMom_.clear();
74 regionCharges_.clear();
75
76 if (constrainRegions_) {
77 std::vector<int> localRegions = info_->getRegions();
78
79#ifdef IS_MPI
80 int size;
81 MPI_Comm_size(MPI_COMM_WORLD, &size);
82 int mylen = localRegions.size();
83
84 std::vector<int> counts;
85 std::vector<int> displs;
86
87 counts.resize(size, 0);
88 displs.resize(size, 0);
89
90 MPI_Allgather(&mylen, 1, MPI_INT, &counts[0], 1, MPI_INT, MPI_COMM_WORLD);
91
92 int total = counts[0];
93
94 for (int i = 1; i < size; i++) {
95 total += counts[i];
96 displs[i] = displs[i - 1] + counts[i - 1];
97 }
98
99 std::vector<int> globalRegions(total, 0);
100
101 MPI_Allgatherv(&localRegions[0], mylen, MPI_INT, &globalRegions[0],
102 &counts[0], &displs[0], MPI_INT, MPI_COMM_WORLD);
103
104 localRegions = globalRegions;
105#endif
106
107 std::set<int> regions;
108 std::vector<int>::iterator iter;
109 for (iter = localRegions.begin(); iter != localRegions.end(); ++iter) {
110 if (*iter >= 0) regions.insert(*iter);
111 }
112
113 // resize the keys vector to the largest found value for regions.
114 regionKeys_.resize(*(regions.end()));
115 int which = 0;
116 for (std::set<int>::iterator r = regions.begin(); r != regions.end();
117 ++r) {
118 regionKeys_[(*r)] = which;
119 which++;
120 }
121 regionForce_.resize(regionKeys_.size());
122 regionCMom_.resize(regionKeys_.size());
123 regionCharges_.resize(regionKeys_.size());
124 regionChargeMass_.resize(regionKeys_.size());
125 }
126 }
127
128 void FluctuatingChargeConstraints::applyConstraints() {
129 if (!initialized_) initialize();
130 if (!hasFlucQ_) return;
131
132 SimInfo::MoleculeIterator i;
133 Molecule::FluctuatingChargeIterator j;
134 Molecule* mol;
135 Atom* atom;
136
137 RealType frc, systemFrc, molFrc, regionFrc;
138 int systemCharges;
139
140 // accumulate the system fluctuating charge forces, but we have
141 // separate constraints for any charges in defined regions and for
142 // molecules with constrained charges:
143
144 systemFrc = 0.0;
145 systemCharges = 0;
146 if (constrainRegions_) {
147 std::fill(regionForce_.begin(), regionForce_.end(), 0.0);
148 std::fill(regionCharges_.begin(), regionCharges_.end(), 0);
149 }
150
151 for (mol = info_->beginMolecule(i); mol != NULL;
152 mol = info_->nextMolecule(i)) {
153 if (!mol->constrainTotalCharge()) {
154 int region = mol->getRegion();
155
156 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
157 atom = mol->nextFluctuatingCharge(j)) {
158 frc = atom->getFlucQFrc();
159 if (constrainRegions_ && region >= 0) {
160 regionForce_[regionKeys_[region]] += frc;
161 regionCharges_[regionKeys_[region]] += 1;
162 } else {
163 systemFrc += frc;
164 systemCharges += 1;
165 }
166 }
167 }
168 }
169
170#ifdef IS_MPI
171 // in parallel, we need to add up the contributions from all
172 // processors:
173 MPI_Allreduce(MPI_IN_PLACE, &systemFrc, 1, MPI_REALTYPE, MPI_SUM,
174 MPI_COMM_WORLD);
175 MPI_Allreduce(MPI_IN_PLACE, &systemCharges, 1, MPI_INT, MPI_SUM,
176 MPI_COMM_WORLD);
177
178 if (constrainRegions_) {
179 MPI_Allreduce(MPI_IN_PLACE, &regionForce_[0], regionForce_.size(),
180 MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
181 MPI_Allreduce(MPI_IN_PLACE, &regionCharges_[0], regionCharges_.size(),
182 MPI_INT, MPI_SUM, MPI_COMM_WORLD);
183 }
184
185#endif
186
187 // divide by the total number of fluctuating charges:
188 systemFrc /= systemCharges;
189
190 // do the same in the regions:
191 if (constrainRegions_) {
192 for (unsigned int i = 0; i < regionForce_.size(); ++i) {
193 regionForce_[i] /= regionCharges_[i];
194 }
195 }
196
197 for (mol = info_->beginMolecule(i); mol != NULL;
198 mol = info_->nextMolecule(i)) {
199 molFrc = 0.0;
200
201 if (mol->constrainTotalCharge()) {
202 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
203 atom = mol->nextFluctuatingCharge(j)) {
204 molFrc += atom->getFlucQFrc();
205 }
206 molFrc /= mol->getNFluctuatingCharges();
207
208 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
209 atom = mol->nextFluctuatingCharge(j)) {
210 frc = atom->getFlucQFrc() - molFrc;
211 atom->setFlucQFrc(frc);
212 }
213 } else {
214 int region = mol->getRegion();
215
216 regionFrc = 0.0;
217 if (constrainRegions_ && region >= 0) {
218 regionFrc = regionForce_[regionKeys_[region]];
219
220 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
221 atom = mol->nextFluctuatingCharge(j)) {
222 frc = atom->getFlucQFrc() - regionFrc;
223 atom->setFlucQFrc(frc);
224 }
225 } else {
226 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
227 atom = mol->nextFluctuatingCharge(j)) {
228 frc = atom->getFlucQFrc() - systemFrc;
229 atom->setFlucQFrc(frc);
230 }
231 }
232 }
233 }
234 }
235
236 void FluctuatingChargeConstraints::applyConstraintsOnChargeVelocities() {
237 if (!initialized_) initialize();
238 if (!hasFlucQ_) return;
239
240 SimInfo::MoleculeIterator i;
241 Molecule::FluctuatingChargeIterator j;
242 Molecule* mol;
243 Atom* atom;
244
245 RealType flucqP, systemCMom, regionCMom, molCMom, molFlucQMass, flucqW;
246 RealType systemChargeMass;
247
248 // accumulate the system fluctuating charge velocities, but we have
249 // separate constraints for any charges in defined regions and for
250 // molecules with constrained charges:
251
252 systemCMom = 0.0;
253 systemChargeMass = 0.0;
254 if (constrainRegions_) {
255 std::fill(regionCMom_.begin(), regionCMom_.end(), 0.0);
256 std::fill(regionChargeMass_.begin(), regionChargeMass_.end(), 0);
257 }
258
259 for (mol = info_->beginMolecule(i); mol != NULL;
260 mol = info_->nextMolecule(i)) {
261 if (!mol->constrainTotalCharge()) {
262 int region = mol->getRegion();
263
264 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
265 atom = mol->nextFluctuatingCharge(j)) {
266 flucqP = atom->getFlucQVel() * atom->getChargeMass();
267 if (constrainRegions_ && region >= 0) {
268 regionCMom_[regionKeys_[region]] += flucqP;
269 regionChargeMass_[regionKeys_[region]] += atom->getChargeMass();
270 } else {
271 systemCMom += flucqP;
272 systemChargeMass += atom->getChargeMass();
273 }
274 }
275 }
276 }
277
278#ifdef IS_MPI
279 // in parallel, we need to add up the contributions from all
280 // processors:
281 MPI_Allreduce(MPI_IN_PLACE, &systemCMom, 1, MPI_REALTYPE, MPI_SUM,
282 MPI_COMM_WORLD);
283 MPI_Allreduce(MPI_IN_PLACE, &systemChargeMass, 1, MPI_REALTYPE, MPI_SUM,
284 MPI_COMM_WORLD);
285
286 if (constrainRegions_) {
287 MPI_Allreduce(MPI_IN_PLACE, &regionCMom_[0], regionCMom_.size(),
288 MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
289 MPI_Allreduce(MPI_IN_PLACE, &regionChargeMass_[0],
290 regionChargeMass_.size(), MPI_REALTYPE, MPI_SUM,
291 MPI_COMM_WORLD);
292 }
293#endif
294
295 // divide by the total number of fluctuating charges:
296 systemCMom /= systemChargeMass;
297
298 // do the same in the regions:
299 if (constrainRegions_) {
300 for (unsigned int i = 0; i < regionCMom_.size(); ++i) {
301 regionCMom_[i] /= regionChargeMass_[i];
302 }
303 }
304
305 for (mol = info_->beginMolecule(i); mol != NULL;
306 mol = info_->nextMolecule(i)) {
307 molCMom = 0.0;
308 molFlucQMass = 0.0;
309
310 if (mol->constrainTotalCharge()) {
311 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
312 atom = mol->nextFluctuatingCharge(j)) {
313 molCMom += atom->getFlucQVel() * atom->getChargeMass();
314 molFlucQMass += atom->getChargeMass();
315 }
316 molCMom /= molFlucQMass;
317
318 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
319 atom = mol->nextFluctuatingCharge(j)) {
320 flucqW = atom->getFlucQVel() - molCMom;
321 atom->setFlucQVel(flucqW);
322 }
323 } else {
324 int region = mol->getRegion();
325
326 regionCMom = 0.0;
327 if (constrainRegions_ && region >= 0) {
328 regionCMom = regionCMom_[regionKeys_[region]];
329
330 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
331 atom = mol->nextFluctuatingCharge(j)) {
332 flucqW = atom->getFlucQVel() - regionCMom;
333 atom->setFlucQVel(flucqW);
334 }
335 } else {
336 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
337 atom = mol->nextFluctuatingCharge(j)) {
338 flucqW = atom->getFlucQVel() - systemCMom;
339 atom->setFlucQVel(flucqW);
340 }
341 }
342 }
343 }
344 }
345
346 int FluctuatingChargeConstraints::getNumberOfFlucQConstraints() {
347 int nConstraints = 0;
348 if (!initialized_) initialize();
349 if (!hasFlucQ_) return 0;
350 SimInfo::MoleculeIterator i;
351 Molecule* mol;
352 int systemConstrain = 0;
353 for (mol = info_->beginMolecule(i); mol != NULL;
354 mol = info_->nextMolecule(i)) {
355 if (mol->constrainTotalCharge()) {
356 nConstraints++;
357 } else {
358 int region = mol->getRegion();
359 if (!constrainRegions_ || region < 0) { systemConstrain = 1; }
360 }
361 }
362 return nConstraints + regionCMom_.size() + systemConstrain;
363 }
364
365 int FluctuatingChargeConstraints::getNumberOfFlucQAtoms() {
366 int nFlucq = 0;
367 if (!initialized_) initialize();
368 if (!hasFlucQ_) return 0;
369 SimInfo::MoleculeIterator i;
370 Molecule::FluctuatingChargeIterator j;
371 Molecule* mol;
372 Atom* atom;
373 for (mol = info_->beginMolecule(i); mol != NULL;
374 mol = info_->nextMolecule(i)) {
375 if (mol->constrainTotalCharge()) {
376 nFlucq += mol->getNFluctuatingCharges();
377 } else {
378 int region = mol->getRegion();
379 if (constrainRegions_ && region >= 0) {
380 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
381 atom = mol->nextFluctuatingCharge(j)) {
382 nFlucq++;
383 }
384 } else {
385 for (atom = mol->beginFluctuatingCharge(j); atom != NULL;
386 atom = mol->nextFluctuatingCharge(j)) {
387 nFlucq++;
388 }
389 }
390 }
391 }
392 return nFlucq;
393 }
394} // namespace OpenMD
This basic Periodic Table class was originally taken from the data.cpp file in OpenBabel.