OpenMD 3.2
Molecular Dynamics in the Open
Loading...
Searching...
No Matches
ForceMatrixDecomposition.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#include "parallel/ForceMatrixDecomposition.hpp"
48
49#include "brains/PairList.hpp"
52#include "nonbonded/NonBondedInteraction.hpp"
53
54using namespace std;
55namespace OpenMD {
56
57 ForceMatrixDecomposition::ForceMatrixDecomposition(SimInfo* info,
58 InteractionManager* iMan) :
59 ForceDecomposition(info, iMan) {
60 // Row and colum scans must visit all surrounding cells
61 cellOffsets_.clear();
62 cellOffsets_.push_back(Vector3i(-1, -1, -1));
63 cellOffsets_.push_back(Vector3i(0, -1, -1));
64 cellOffsets_.push_back(Vector3i(1, -1, -1));
65 cellOffsets_.push_back(Vector3i(-1, 0, -1));
66 cellOffsets_.push_back(Vector3i(0, 0, -1));
67 cellOffsets_.push_back(Vector3i(1, 0, -1));
68 cellOffsets_.push_back(Vector3i(-1, 1, -1));
69 cellOffsets_.push_back(Vector3i(0, 1, -1));
70 cellOffsets_.push_back(Vector3i(1, 1, -1));
71 cellOffsets_.push_back(Vector3i(-1, -1, 0));
72 cellOffsets_.push_back(Vector3i(0, -1, 0));
73 cellOffsets_.push_back(Vector3i(1, -1, 0));
74 cellOffsets_.push_back(Vector3i(-1, 0, 0));
75 cellOffsets_.push_back(Vector3i(0, 0, 0));
76 cellOffsets_.push_back(Vector3i(1, 0, 0));
77 cellOffsets_.push_back(Vector3i(-1, 1, 0));
78 cellOffsets_.push_back(Vector3i(0, 1, 0));
79 cellOffsets_.push_back(Vector3i(1, 1, 0));
80 cellOffsets_.push_back(Vector3i(-1, -1, 1));
81 cellOffsets_.push_back(Vector3i(0, -1, 1));
82 cellOffsets_.push_back(Vector3i(1, -1, 1));
83 cellOffsets_.push_back(Vector3i(-1, 0, 1));
84 cellOffsets_.push_back(Vector3i(0, 0, 1));
85 cellOffsets_.push_back(Vector3i(1, 0, 1));
86 cellOffsets_.push_back(Vector3i(-1, 1, 1));
87 cellOffsets_.push_back(Vector3i(0, 1, 1));
88 cellOffsets_.push_back(Vector3i(1, 1, 1));
89 }
90
91 ForceMatrixDecomposition::~ForceMatrixDecomposition() {
92#ifdef IS_MPI
93 delete AtomPlanIntRow;
94 delete AtomPlanRealRow;
95 delete AtomPlanVectorRow;
96 delete AtomPlanMatrixRow;
97 delete AtomPlanPotRow;
98 delete AtomPlanIntColumn;
99 delete AtomPlanRealColumn;
100 delete AtomPlanVectorColumn;
101 delete AtomPlanMatrixColumn;
102 delete AtomPlanPotColumn;
103 delete cgPlanIntRow;
104 delete cgPlanVectorRow;
105 delete cgPlanIntColumn;
106 delete cgPlanVectorColumn;
107#endif
108 }
109
110 /**
111 * distributeInitialData is essentially a copy of the older fortran
112 * SimulationSetup
113 */
115 snap_ = sman_->getCurrentSnapshot();
116 atomStorageLayout_ = sman_->getAtomStorageLayout();
117 ff_ = info_->getForceField();
118 nLocal_ = snap_->getNumberOfAtoms();
119
120 nGroups_ = info_->getNLocalCutoffGroups();
121 // gather the information for atomtype IDs (atids):
122 idents = info_->getIdentArray();
123 regions = info_->getRegions();
124 AtomLocalToGlobal = info_->getGlobalAtomIndices();
125 cgLocalToGlobal = info_->getGlobalGroupIndices();
126 vector<int> globalGroupMembership = info_->getGlobalGroupMembership();
127
128 massFactors = info_->getMassFactors();
129
130 PairList* excludes = info_->getExcludedInteractions();
131 PairList* oneTwo = info_->getOneTwoInteractions();
132 PairList* oneThree = info_->getOneThreeInteractions();
133 PairList* oneFour = info_->getOneFourInteractions();
134
135 if (needVelocities_)
136 snap_->cgData.setStorageLayout(DataStorage::dslPosition |
137 DataStorage::dslVelocity);
138 else
139 snap_->cgData.setStorageLayout(DataStorage::dslPosition);
140
141#ifdef IS_MPI
142
143 MPI_Comm row = rowComm.getComm();
144 MPI_Comm col = colComm.getComm();
145
146 AtomPlanIntRow = new Plan<int>(row, nLocal_);
147 AtomPlanRealRow = new Plan<RealType>(row, nLocal_);
148 AtomPlanVectorRow = new Plan<Vector3d>(row, nLocal_);
149 AtomPlanMatrixRow = new Plan<Mat3x3d>(row, nLocal_);
150 AtomPlanPotRow = new Plan<potVec>(row, nLocal_);
151
152 AtomPlanIntColumn = new Plan<int>(col, nLocal_);
153 AtomPlanRealColumn = new Plan<RealType>(col, nLocal_);
154 AtomPlanVectorColumn = new Plan<Vector3d>(col, nLocal_);
155 AtomPlanMatrixColumn = new Plan<Mat3x3d>(col, nLocal_);
156 AtomPlanPotColumn = new Plan<potVec>(col, nLocal_);
157
158 cgPlanIntRow = new Plan<int>(row, nGroups_);
159 cgPlanVectorRow = new Plan<Vector3d>(row, nGroups_);
160 cgPlanIntColumn = new Plan<int>(col, nGroups_);
161 cgPlanVectorColumn = new Plan<Vector3d>(col, nGroups_);
162
163 nAtomsInRow_ = AtomPlanIntRow->getSize();
164 nAtomsInCol_ = AtomPlanIntColumn->getSize();
165 nGroupsInRow_ = cgPlanIntRow->getSize();
166 nGroupsInCol_ = cgPlanIntColumn->getSize();
167
168 // Modify the data storage objects with the correct layouts and sizes:
169 atomRowData.resize(nAtomsInRow_);
170 atomRowData.setStorageLayout(atomStorageLayout_);
171 atomColData.resize(nAtomsInCol_);
172 atomColData.setStorageLayout(atomStorageLayout_);
173 cgRowData.resize(nGroupsInRow_);
174 cgRowData.setStorageLayout(DataStorage::dslPosition);
175 cgColData.resize(nGroupsInCol_);
176 if (needVelocities_)
177 // we only need column velocities if we need them.
178 cgColData.setStorageLayout(DataStorage::dslPosition |
179 DataStorage::dslVelocity);
180 else
181 cgColData.setStorageLayout(DataStorage::dslPosition);
182
183 identsRow.resize(nAtomsInRow_);
184 identsCol.resize(nAtomsInCol_);
185
186 AtomPlanIntRow->gather(idents, identsRow);
187 AtomPlanIntColumn->gather(idents, identsCol);
188
189 regionsRow.resize(nAtomsInRow_);
190 regionsCol.resize(nAtomsInCol_);
191
192 AtomPlanIntRow->gather(regions, regionsRow);
193 AtomPlanIntColumn->gather(regions, regionsCol);
194
195 // allocate memory for the parallel objects
196 atypesRow.resize(nAtomsInRow_);
197 atypesCol.resize(nAtomsInCol_);
198
199 for (int i = 0; i < nAtomsInRow_; i++)
200 atypesRow[i] = ff_->getAtomType(identsRow[i]);
201 for (int i = 0; i < nAtomsInCol_; i++)
202 atypesCol[i] = ff_->getAtomType(identsCol[i]);
203
204 pot_row.resize(nAtomsInRow_);
205 pot_col.resize(nAtomsInCol_);
206
207 expot_row.resize(nAtomsInRow_);
208 expot_col.resize(nAtomsInCol_);
209
210 selepot_row.resize(nAtomsInRow_);
211 selepot_col.resize(nAtomsInCol_);
212
213 AtomRowToGlobal.resize(nAtomsInRow_);
214 AtomColToGlobal.resize(nAtomsInCol_);
215 AtomPlanIntRow->gather(AtomLocalToGlobal, AtomRowToGlobal);
216 AtomPlanIntColumn->gather(AtomLocalToGlobal, AtomColToGlobal);
217
218 cgRowToGlobal.resize(nGroupsInRow_);
219 cgColToGlobal.resize(nGroupsInCol_);
220 cgPlanIntRow->gather(cgLocalToGlobal, cgRowToGlobal);
221 cgPlanIntColumn->gather(cgLocalToGlobal, cgColToGlobal);
222
223 massFactorsRow.resize(nAtomsInRow_);
224 massFactorsCol.resize(nAtomsInCol_);
225 AtomPlanRealRow->gather(massFactors, massFactorsRow);
226 AtomPlanRealColumn->gather(massFactors, massFactorsCol);
227
228 groupListRow_.clear();
229 groupListRow_.resize(nGroupsInRow_);
230 for (int i = 0; i < nGroupsInRow_; i++) {
231 int gid = cgRowToGlobal[i];
232 for (int j = 0; j < nAtomsInRow_; j++) {
233 int aid = AtomRowToGlobal[j];
234 if (globalGroupMembership[aid] == gid) groupListRow_[i].push_back(j);
235 }
236 }
237
238 groupListCol_.clear();
239 groupListCol_.resize(nGroupsInCol_);
240 for (int i = 0; i < nGroupsInCol_; i++) {
241 int gid = cgColToGlobal[i];
242 for (int j = 0; j < nAtomsInCol_; j++) {
243 int aid = AtomColToGlobal[j];
244 if (globalGroupMembership[aid] == gid) groupListCol_[i].push_back(j);
245 }
246 }
247
248 excludesForAtom.clear();
249 excludesForAtom.resize(nAtomsInRow_);
250 toposForAtom.clear();
251 toposForAtom.resize(nAtomsInRow_);
252 topoDist.clear();
253 topoDist.resize(nAtomsInRow_);
254 for (int i = 0; i < nAtomsInRow_; i++) {
255 int iglob = AtomRowToGlobal[i];
256
257 for (int j = 0; j < nAtomsInCol_; j++) {
258 int jglob = AtomColToGlobal[j];
259
260 if (excludes->hasPair(iglob, jglob)) excludesForAtom[i].push_back(j);
261
262 if (oneTwo->hasPair(iglob, jglob)) {
263 toposForAtom[i].push_back(j);
264 topoDist[i].push_back(1);
265 } else {
266 if (oneThree->hasPair(iglob, jglob)) {
267 toposForAtom[i].push_back(j);
268 topoDist[i].push_back(2);
269 } else {
270 if (oneFour->hasPair(iglob, jglob)) {
271 toposForAtom[i].push_back(j);
272 topoDist[i].push_back(3);
273 }
274 }
275 }
276 }
277 }
278
279#else
280 excludesForAtom.clear();
281 excludesForAtom.resize(nLocal_);
282 toposForAtom.clear();
283 toposForAtom.resize(nLocal_);
284 topoDist.clear();
285 topoDist.resize(nLocal_);
286
287 for (int i = 0; i < nLocal_; i++) {
288 int iglob = AtomLocalToGlobal[i];
289
290 for (int j = 0; j < nLocal_; j++) {
291 int jglob = AtomLocalToGlobal[j];
292
293 if (excludes->hasPair(iglob, jglob)) excludesForAtom[i].push_back(j);
294
295 if (oneTwo->hasPair(iglob, jglob)) {
296 toposForAtom[i].push_back(j);
297 topoDist[i].push_back(1);
298 } else {
299 if (oneThree->hasPair(iglob, jglob)) {
300 toposForAtom[i].push_back(j);
301 topoDist[i].push_back(2);
302 } else {
303 if (oneFour->hasPair(iglob, jglob)) {
304 toposForAtom[i].push_back(j);
305 topoDist[i].push_back(3);
306 }
307 }
308 }
309 }
310 }
311#endif
312
313 // allocate memory for the parallel objects
314 atypesLocal.resize(nLocal_);
315
316 for (int i = 0; i < nLocal_; i++)
317 atypesLocal[i] = ff_->getAtomType(idents[i]);
318
319 groupList_.clear();
320 groupList_.resize(nGroups_);
321 for (int i = 0; i < nGroups_; i++) {
322 int gid = cgLocalToGlobal[i];
323 for (int j = 0; j < nLocal_; j++) {
324 int aid = AtomLocalToGlobal[j];
325 if (globalGroupMembership[aid] == gid) { groupList_[i].push_back(j); }
326 }
327 }
328 }
329
330 int ForceMatrixDecomposition::getTopologicalDistance(int atom1, int atom2) {
331 for (unsigned int j = 0; j < toposForAtom[atom1].size(); j++) {
332 if (toposForAtom[atom1][j] == atom2) return topoDist[atom1][j];
333 }
334 return 0;
335 }
336
337 void ForceMatrixDecomposition::zeroWorkArrays() {
338 pairwisePot = 0.0;
339 selfPot = 0.0;
340 excludedPot = 0.0;
341 excludedSelfPot = 0.0;
342 selectedPot = 0.0;
343 selectedSelfPot = 0.0;
344
345#ifdef IS_MPI
346 if (atomStorageLayout_ & DataStorage::dslForce) {
347 fill(atomRowData.force.begin(), atomRowData.force.end(), V3Zero);
348 fill(atomColData.force.begin(), atomColData.force.end(), V3Zero);
349 }
350
351 if (atomStorageLayout_ & DataStorage::dslTorque) {
352 fill(atomRowData.torque.begin(), atomRowData.torque.end(), V3Zero);
353 fill(atomColData.torque.begin(), atomColData.torque.end(), V3Zero);
354 }
355
356 fill(pot_row.begin(), pot_row.end(),
357 Vector<RealType, N_INTERACTION_FAMILIES>(0.0));
358
359 fill(pot_col.begin(), pot_col.end(),
360 Vector<RealType, N_INTERACTION_FAMILIES>(0.0));
361
362 fill(expot_row.begin(), expot_row.end(),
363 Vector<RealType, N_INTERACTION_FAMILIES>(0.0));
364
365 fill(expot_col.begin(), expot_col.end(),
366 Vector<RealType, N_INTERACTION_FAMILIES>(0.0));
367
368 fill(selepot_row.begin(), selepot_row.end(),
369 Vector<RealType, N_INTERACTION_FAMILIES>(0.0));
370
371 fill(selepot_col.begin(), selepot_col.end(),
372 Vector<RealType, N_INTERACTION_FAMILIES>(0.0));
373
374 if (atomStorageLayout_ & DataStorage::dslParticlePot) {
375 fill(atomRowData.particlePot.begin(), atomRowData.particlePot.end(), 0.0);
376 fill(atomColData.particlePot.begin(), atomColData.particlePot.end(), 0.0);
377 }
378
379 if (atomStorageLayout_ & DataStorage::dslDensity) {
380 fill(atomRowData.density.begin(), atomRowData.density.end(), 0.0);
381 fill(atomColData.density.begin(), atomColData.density.end(), 0.0);
382 }
383
384 if (atomStorageLayout_ & DataStorage::dslFunctional) {
385 fill(atomRowData.functional.begin(), atomRowData.functional.end(), 0.0);
386 fill(atomColData.functional.begin(), atomColData.functional.end(), 0.0);
387 }
388
389 if (atomStorageLayout_ & DataStorage::dslFunctionalDerivative) {
390 fill(atomRowData.functionalDerivative.begin(),
391 atomRowData.functionalDerivative.end(), 0.0);
392 fill(atomColData.functionalDerivative.begin(),
393 atomColData.functionalDerivative.end(), 0.0);
394 }
395
396 if (atomStorageLayout_ & DataStorage::dslSkippedCharge) {
397 fill(atomRowData.skippedCharge.begin(), atomRowData.skippedCharge.end(),
398 0.0);
399 fill(atomColData.skippedCharge.begin(), atomColData.skippedCharge.end(),
400 0.0);
401 }
402
403 if (atomStorageLayout_ & DataStorage::dslFlucQForce) {
404 fill(atomRowData.flucQFrc.begin(), atomRowData.flucQFrc.end(), 0.0);
405 fill(atomColData.flucQFrc.begin(), atomColData.flucQFrc.end(), 0.0);
406 }
407
408 if (atomStorageLayout_ & DataStorage::dslElectricField) {
409 fill(atomRowData.electricField.begin(), atomRowData.electricField.end(),
410 V3Zero);
411 fill(atomColData.electricField.begin(), atomColData.electricField.end(),
412 V3Zero);
413 }
414
415 if (atomStorageLayout_ & DataStorage::dslSitePotential) {
416 fill(atomRowData.sitePotential.begin(), atomRowData.sitePotential.end(),
417 0.0);
418 fill(atomColData.sitePotential.begin(), atomColData.sitePotential.end(),
419 0.0);
420 }
421
422#endif
423 // even in parallel, we need to zero out the local arrays:
424
425 if (atomStorageLayout_ & DataStorage::dslParticlePot) {
426 fill(snap_->atomData.particlePot.begin(),
427 snap_->atomData.particlePot.end(), 0.0);
428 }
429
430 if (atomStorageLayout_ & DataStorage::dslDensity) {
431 fill(snap_->atomData.density.begin(), snap_->atomData.density.end(), 0.0);
432 }
433
434 if (atomStorageLayout_ & DataStorage::dslFunctional) {
435 fill(snap_->atomData.functional.begin(), snap_->atomData.functional.end(),
436 0.0);
437 }
438
439 if (atomStorageLayout_ & DataStorage::dslFunctionalDerivative) {
440 fill(snap_->atomData.functionalDerivative.begin(),
441 snap_->atomData.functionalDerivative.end(), 0.0);
442 }
443
444 if (atomStorageLayout_ & DataStorage::dslSkippedCharge) {
445 fill(snap_->atomData.skippedCharge.begin(),
446 snap_->atomData.skippedCharge.end(), 0.0);
447 }
448
449 if (atomStorageLayout_ & DataStorage::dslElectricField) {
450 fill(snap_->atomData.electricField.begin(),
451 snap_->atomData.electricField.end(), V3Zero);
452 }
453 if (atomStorageLayout_ & DataStorage::dslSitePotential) {
454 fill(snap_->atomData.sitePotential.begin(),
455 snap_->atomData.sitePotential.end(), 0.0);
456 }
457 }
458
459 void ForceMatrixDecomposition::distributeData() {
460#ifdef IS_MPI
461
462 snap_ = sman_->getCurrentSnapshot();
463 atomStorageLayout_ = sman_->getAtomStorageLayout();
464
465 bool needsCG = true;
466 if (info_->getNCutoffGroups() != info_->getNAtoms()) needsCG = false;
467
468 // gather up the atomic positions
469 AtomPlanVectorRow->gather(snap_->atomData.position, atomRowData.position);
470 AtomPlanVectorColumn->gather(snap_->atomData.position,
471 atomColData.position);
472
473 // gather up the cutoff group positions
474
475 if (needsCG) {
476 cgPlanVectorRow->gather(snap_->cgData.position, cgRowData.position);
477
478 cgPlanVectorColumn->gather(snap_->cgData.position, cgColData.position);
479 }
480
481 if (needVelocities_) {
482 // gather up the atomic velocities
483 AtomPlanVectorColumn->gather(snap_->atomData.velocity,
484 atomColData.velocity);
485
486 if (needsCG) {
487 cgPlanVectorColumn->gather(snap_->cgData.velocity, cgColData.velocity);
488 }
489 }
490
491 // if needed, gather the atomic rotation matrices
492 if (atomStorageLayout_ & DataStorage::dslAmat) {
493 AtomPlanMatrixRow->gather(snap_->atomData.aMat, atomRowData.aMat);
494 AtomPlanMatrixColumn->gather(snap_->atomData.aMat, atomColData.aMat);
495 }
496
497 // if needed, gather the atomic eletrostatic information
498 if (atomStorageLayout_ & DataStorage::dslDipole) {
499 AtomPlanVectorRow->gather(snap_->atomData.dipole, atomRowData.dipole);
500 AtomPlanVectorColumn->gather(snap_->atomData.dipole, atomColData.dipole);
501 }
502
503 if (atomStorageLayout_ & DataStorage::dslQuadrupole) {
504 AtomPlanMatrixRow->gather(snap_->atomData.quadrupole,
505 atomRowData.quadrupole);
506 AtomPlanMatrixColumn->gather(snap_->atomData.quadrupole,
507 atomColData.quadrupole);
508 }
509
510 // if needed, gather the atomic fluctuating charge values
511 if (atomStorageLayout_ & DataStorage::dslFlucQPosition) {
512 AtomPlanRealRow->gather(snap_->atomData.flucQPos, atomRowData.flucQPos);
513 AtomPlanRealColumn->gather(snap_->atomData.flucQPos,
514 atomColData.flucQPos);
515 }
516
517#endif
518 }
519
520 /* collects information obtained during the pre-pair loop onto local
521 * data structures.
522 */
523 void ForceMatrixDecomposition::collectIntermediateData() {
524#ifdef IS_MPI
525
526 snap_ = sman_->getCurrentSnapshot();
527 atomStorageLayout_ = sman_->getAtomStorageLayout();
528
529 if (atomStorageLayout_ & DataStorage::dslDensity) {
530 AtomPlanRealRow->scatter(atomRowData.density, snap_->atomData.density);
531
532 int n = snap_->atomData.density.size();
533 vector<RealType> rho_tmp(n, 0.0);
534 AtomPlanRealColumn->scatter(atomColData.density, rho_tmp);
535 for (int i = 0; i < n; i++)
536 snap_->atomData.density[i] += rho_tmp[i];
537 }
538
539 // this isn't necessary if we don't have polarizable atoms, but
540 // we'll leave it here for now.
541 if (atomStorageLayout_ & DataStorage::dslElectricField) {
542 AtomPlanVectorRow->scatter(atomRowData.electricField,
543 snap_->atomData.electricField);
544
545 int n = snap_->atomData.electricField.size();
546 vector<Vector3d> field_tmp(n, V3Zero);
547 AtomPlanVectorColumn->scatter(atomColData.electricField, field_tmp);
548 for (int i = 0; i < n; i++)
549 snap_->atomData.electricField[i] += field_tmp[i];
550 }
551#endif
552 }
553
554 /*
555 * redistributes information obtained during the pre-pair loop out to
556 * row and column-indexed data structures
557 */
558 void ForceMatrixDecomposition::distributeIntermediateData() {
559#ifdef IS_MPI
560 snap_ = sman_->getCurrentSnapshot();
561 atomStorageLayout_ = sman_->getAtomStorageLayout();
562
563 if (atomStorageLayout_ & DataStorage::dslFunctional) {
564 AtomPlanRealRow->gather(snap_->atomData.functional,
565 atomRowData.functional);
566 AtomPlanRealColumn->gather(snap_->atomData.functional,
567 atomColData.functional);
568 }
569
570 if (atomStorageLayout_ & DataStorage::dslFunctionalDerivative) {
571 AtomPlanRealRow->gather(snap_->atomData.functionalDerivative,
572 atomRowData.functionalDerivative);
573 AtomPlanRealColumn->gather(snap_->atomData.functionalDerivative,
574 atomColData.functionalDerivative);
575 }
576#endif
577 }
578
579 void ForceMatrixDecomposition::collectData() {
580#ifdef IS_MPI
581 snap_ = sman_->getCurrentSnapshot();
582 atomStorageLayout_ = sman_->getAtomStorageLayout();
583
584 int n = snap_->atomData.force.size();
585 vector<Vector3d> frc_tmp(n, V3Zero);
586
587 AtomPlanVectorRow->scatter(atomRowData.force, frc_tmp);
588 for (int i = 0; i < n; i++) {
589 snap_->atomData.force[i] += frc_tmp[i];
590 frc_tmp[i] = 0.0;
591 }
592
593 AtomPlanVectorColumn->scatter(atomColData.force, frc_tmp);
594 for (int i = 0; i < n; i++) {
595 snap_->atomData.force[i] += frc_tmp[i];
596 }
597
598 if (atomStorageLayout_ & DataStorage::dslTorque) {
599 int nt = snap_->atomData.torque.size();
600 vector<Vector3d> trq_tmp(nt, V3Zero);
601
602 AtomPlanVectorRow->scatter(atomRowData.torque, trq_tmp);
603 for (int i = 0; i < nt; i++) {
604 snap_->atomData.torque[i] += trq_tmp[i];
605 trq_tmp[i] = 0.0;
606 }
607
608 AtomPlanVectorColumn->scatter(atomColData.torque, trq_tmp);
609 for (int i = 0; i < nt; i++)
610 snap_->atomData.torque[i] += trq_tmp[i];
611 }
612
613 if (atomStorageLayout_ & DataStorage::dslSkippedCharge) {
614 int ns = snap_->atomData.skippedCharge.size();
615 vector<RealType> skch_tmp(ns, 0.0);
616
617 AtomPlanRealRow->scatter(atomRowData.skippedCharge, skch_tmp);
618 for (int i = 0; i < ns; i++) {
619 snap_->atomData.skippedCharge[i] += skch_tmp[i];
620 skch_tmp[i] = 0.0;
621 }
622
623 AtomPlanRealColumn->scatter(atomColData.skippedCharge, skch_tmp);
624 for (int i = 0; i < ns; i++)
625 snap_->atomData.skippedCharge[i] += skch_tmp[i];
626 }
627
628 if (atomStorageLayout_ & DataStorage::dslFlucQForce) {
629 int nq = snap_->atomData.flucQFrc.size();
630 vector<RealType> fqfrc_tmp(nq, 0.0);
631
632 AtomPlanRealRow->scatter(atomRowData.flucQFrc, fqfrc_tmp);
633 for (int i = 0; i < nq; i++) {
634 snap_->atomData.flucQFrc[i] += fqfrc_tmp[i];
635 fqfrc_tmp[i] = 0.0;
636 }
637
638 AtomPlanRealColumn->scatter(atomColData.flucQFrc, fqfrc_tmp);
639 for (int i = 0; i < nq; i++)
640 snap_->atomData.flucQFrc[i] += fqfrc_tmp[i];
641 }
642
643 if (atomStorageLayout_ & DataStorage::dslElectricField) {
644 int nef = snap_->atomData.electricField.size();
645 vector<Vector3d> efield_tmp(nef, V3Zero);
646
647 AtomPlanVectorRow->scatter(atomRowData.electricField, efield_tmp);
648 for (int i = 0; i < nef; i++) {
649 snap_->atomData.electricField[i] += efield_tmp[i];
650 efield_tmp[i] = 0.0;
651 }
652
653 AtomPlanVectorColumn->scatter(atomColData.electricField, efield_tmp);
654 for (int i = 0; i < nef; i++)
655 snap_->atomData.electricField[i] += efield_tmp[i];
656 }
657
658 if (atomStorageLayout_ & DataStorage::dslSitePotential) {
659 int nsp = snap_->atomData.sitePotential.size();
660 vector<RealType> sp_tmp(nsp, 0.0);
661
662 AtomPlanRealRow->scatter(atomRowData.sitePotential, sp_tmp);
663 for (int i = 0; i < nsp; i++) {
664 snap_->atomData.sitePotential[i] += sp_tmp[i];
665 sp_tmp[i] = 0.0;
666 }
667
668 AtomPlanRealColumn->scatter(atomColData.sitePotential, sp_tmp);
669 for (int i = 0; i < nsp; i++)
670 snap_->atomData.sitePotential[i] += sp_tmp[i];
671 }
672
673 nLocal_ = snap_->getNumberOfAtoms();
674
675 vector<potVec> pot_temp(nLocal_,
676 Vector<RealType, N_INTERACTION_FAMILIES>(0.0));
677 vector<potVec> expot_temp(nLocal_,
678 Vector<RealType, N_INTERACTION_FAMILIES>(0.0));
679 vector<potVec> selepot_temp(nLocal_,
680 Vector<RealType, N_INTERACTION_FAMILIES>(0.0));
681
682 // scatter/gather pot_row into the members of my column
683
684 AtomPlanPotRow->scatter(pot_row, pot_temp);
685 AtomPlanPotRow->scatter(expot_row, expot_temp);
686 AtomPlanPotRow->scatter(selepot_row, selepot_temp);
687
688 for (std::size_t ii = 0; ii < pot_temp.size(); ii++)
689 pairwisePot += pot_temp[ii];
690
691 for (std::size_t ii = 0; ii < expot_temp.size(); ii++)
692 excludedPot += expot_temp[ii];
693
694 for (std::size_t ii = 0; ii < selepot_temp.size(); ii++)
695 selectedPot += selepot_temp[ii];
696
697 if (atomStorageLayout_ & DataStorage::dslParticlePot) {
698 // This is the pairwise contribution to the particle pot. The
699 // embedding contribution is added in each of the low level
700 // non-bonded routines. In single processor, this is done in
701 // unpackInteractionData, not in collectData.
702 for (int ii = 0; ii < N_INTERACTION_FAMILIES; ii++) {
703 for (int i = 0; i < nLocal_; i++) {
704 // factor of two is because the total potential terms are divided
705 // by 2 in parallel due to row/ column scatter
706 snap_->atomData.particlePot[i] += 2.0 * pot_temp[i](ii);
707 }
708 }
709 }
710
711 fill(pot_temp.begin(), pot_temp.end(),
712 Vector<RealType, N_INTERACTION_FAMILIES>(0.0));
713 fill(expot_temp.begin(), expot_temp.end(),
714 Vector<RealType, N_INTERACTION_FAMILIES>(0.0));
715 fill(selepot_temp.begin(), selepot_temp.end(),
716 Vector<RealType, N_INTERACTION_FAMILIES>(0.0));
717
718 AtomPlanPotColumn->scatter(pot_col, pot_temp);
719 AtomPlanPotColumn->scatter(expot_col, expot_temp);
720 AtomPlanPotColumn->scatter(selepot_col, selepot_temp);
721
722 for (std::size_t ii = 0; ii < pot_temp.size(); ii++)
723 pairwisePot += pot_temp[ii];
724
725 for (std::size_t ii = 0; ii < expot_temp.size(); ii++)
726 excludedPot += expot_temp[ii];
727
728 for (std::size_t ii = 0; ii < selepot_temp.size(); ii++)
729 selectedPot += selepot_temp[ii];
730
731 if (atomStorageLayout_ & DataStorage::dslParticlePot) {
732 // This is the pairwise contribution to the particle pot. The
733 // embedding contribution is added in each of the low level
734 // non-bonded routines. In single processor, this is done in
735 // unpackInteractionData, not in collectData.
736 for (int ii = 0; ii < N_INTERACTION_FAMILIES; ii++) {
737 for (int i = 0; i < nLocal_; i++) {
738 // factor of two is because the total potential terms are divided
739 // by 2 in parallel due to row/ column scatter
740 snap_->atomData.particlePot[i] += 2.0 * pot_temp[i](ii);
741 }
742 }
743 }
744
745 if (atomStorageLayout_ & DataStorage::dslParticlePot) {
746 int npp = snap_->atomData.particlePot.size();
747 vector<RealType> ppot_temp(npp, 0.0);
748
749 // This is the direct or embedding contribution to the particle
750 // pot.
751
752 AtomPlanRealRow->scatter(atomRowData.particlePot, ppot_temp);
753 for (int i = 0; i < npp; i++) {
754 snap_->atomData.particlePot[i] += ppot_temp[i];
755 }
756
757 fill(ppot_temp.begin(), ppot_temp.end(), 0.0);
758
759 AtomPlanRealColumn->scatter(atomColData.particlePot, ppot_temp);
760 for (int i = 0; i < npp; i++) {
761 snap_->atomData.particlePot[i] += ppot_temp[i];
762 }
763 }
764
765 MPI_Allreduce(MPI_IN_PLACE, &pairwisePot[0], N_INTERACTION_FAMILIES,
766 MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
767
768 MPI_Allreduce(MPI_IN_PLACE, &excludedPot[0], N_INTERACTION_FAMILIES,
769 MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
770
771 MPI_Allreduce(MPI_IN_PLACE, &selectedPot[0], N_INTERACTION_FAMILIES,
772 MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
773
774 // Here be dragons.
775 MPI_Comm col = colComm.getComm();
776
777 MPI_Allreduce(MPI_IN_PLACE, &snap_->frameData.conductiveHeatFlux[0], 3,
778 MPI_REALTYPE, MPI_SUM, col);
779#endif
780 }
781
782 /**
783 * Collects information obtained during the post-pair (and embedding
784 * functional) loops onto local data structures.
785 */
787#ifdef IS_MPI
788 MPI_Allreduce(MPI_IN_PLACE, &selfPot[0], N_INTERACTION_FAMILIES,
789 MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
790
791 MPI_Allreduce(MPI_IN_PLACE, &excludedSelfPot[0], N_INTERACTION_FAMILIES,
792 MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
793
794 MPI_Allreduce(MPI_IN_PLACE, &selectedSelfPot[0], N_INTERACTION_FAMILIES,
795 MPI_REALTYPE, MPI_SUM, MPI_COMM_WORLD);
796#endif
797 }
798
799 int& ForceMatrixDecomposition::getNAtomsInRow() {
800#ifdef IS_MPI
801 return nAtomsInRow_;
802#else
803 return nLocal_;
804#endif
805 }
806
807 /**
808 * returns the list of atoms belonging to this group.
809 */
811#ifdef IS_MPI
812 return groupListRow_[cg1];
813#else
814 return groupList_[cg1];
815#endif
816 }
817
818 vector<int>& ForceMatrixDecomposition::getAtomsInGroupColumn(int cg2) {
819#ifdef IS_MPI
820 return groupListCol_[cg2];
821#else
822 return groupList_[cg2];
823#endif
824 }
825
826 Vector3d ForceMatrixDecomposition::getIntergroupVector(int cg1, int cg2) {
827 Vector3d d;
828#ifdef IS_MPI
829 d = cgColData.position[cg2] - cgRowData.position[cg1];
830#else
831 d = snap_->cgData.position[cg2] - snap_->cgData.position[cg1];
832#endif
833
834 if (usePeriodicBoundaryConditions_) { snap_->wrapVector(d); }
835 return d;
836 }
837
838 Vector3d& ForceMatrixDecomposition::getGroupVelocityColumn(int cg2) {
839#ifdef IS_MPI
840 return cgColData.velocity[cg2];
841#else
842 return snap_->cgData.velocity[cg2];
843#endif
844 }
845
846 Vector3d& ForceMatrixDecomposition::getAtomVelocityColumn(int atom2) {
847#ifdef IS_MPI
848 return atomColData.velocity[atom2];
849#else
850 return snap_->atomData.velocity[atom2];
851#endif
852 }
853
854 Vector3d ForceMatrixDecomposition::getAtomToGroupVectorRow(int atom1,
855 int cg1) {
856 Vector3d d;
857
858#ifdef IS_MPI
859 d = cgRowData.position[cg1] - atomRowData.position[atom1];
860#else
861 d = snap_->cgData.position[cg1] - snap_->atomData.position[atom1];
862#endif
863 if (usePeriodicBoundaryConditions_) { snap_->wrapVector(d); }
864 return d;
865 }
866
867 Vector3d ForceMatrixDecomposition::getAtomToGroupVectorColumn(int atom2,
868 int cg2) {
869 Vector3d d;
870
871#ifdef IS_MPI
872 d = cgColData.position[cg2] - atomColData.position[atom2];
873#else
874 d = snap_->cgData.position[cg2] - snap_->atomData.position[atom2];
875#endif
876 if (usePeriodicBoundaryConditions_) { snap_->wrapVector(d); }
877 return d;
878 }
879
880 RealType& ForceMatrixDecomposition::getMassFactorRow(int atom1) {
881#ifdef IS_MPI
882 return massFactorsRow[atom1];
883#else
884 return massFactors[atom1];
885#endif
886 }
887
888 RealType& ForceMatrixDecomposition::getMassFactorColumn(int atom2) {
889#ifdef IS_MPI
890 return massFactorsCol[atom2];
891#else
892 return massFactors[atom2];
893#endif
894 }
895
896 Vector3d ForceMatrixDecomposition::getInteratomicVector(int atom1,
897 int atom2) {
898 Vector3d d;
899
900#ifdef IS_MPI
901 d = atomColData.position[atom2] - atomRowData.position[atom1];
902#else
903 d = snap_->atomData.position[atom2] - snap_->atomData.position[atom1];
904#endif
905 if (usePeriodicBoundaryConditions_) { snap_->wrapVector(d); }
906 return d;
907 }
908
909 vector<int>& ForceMatrixDecomposition::getExcludesForAtom(int atom1) {
910 return excludesForAtom[atom1];
911 }
912
913 /**
914 * We need to exclude some overcounted interactions that result from
915 * the parallel decomposition.
916 */
917#ifdef IS_MPI
918 bool ForceMatrixDecomposition::skipAtomPair(int atom1, int atom2, int, int) {
919 // in MPI, we have to look up the unique IDs for each atom
920 int unique_id_1 = AtomRowToGlobal[atom1];
921 int unique_id_2 = AtomColToGlobal[atom2];
922
923 if (unique_id_1 == unique_id_2) return true;
924
925 // this prevents us from doing the pair on multiple processors
926 if (unique_id_1 < unique_id_2) {
927 if ((unique_id_1 + unique_id_2) % 2 == 0) return true;
928 } else {
929 if ((unique_id_1 + unique_id_2) % 2 == 1) return true;
930 }
931
932 return false;
933 }
934
935#else
936 bool ForceMatrixDecomposition::skipAtomPair(int atom1, int atom2, int cg1,
937 int cg2) {
938 int unique_id_1 = AtomLocalToGlobal[atom1];
939 int unique_id_2 = AtomLocalToGlobal[atom2];
940 int group1 = cgLocalToGlobal[cg1];
941 int group2 = cgLocalToGlobal[cg2];
942
943 if (unique_id_1 == unique_id_2) return true;
944
945 if (group1 == group2) {
946 if (unique_id_1 < unique_id_2) return true;
947 }
948
949 return false;
950 }
951#endif
952
953 /**
954 * We need to handle the interactions for atoms who are involved in
955 * the same rigid body as well as some short range interactions
956 * (bonds, bends, torsions) differently from other interactions.
957 * We'll still visit the pairwise routines, but with a flag that
958 * tells those routines to exclude the pair from direct long range
959 * interactions. Some indirect interactions (notably reaction
960 * field) must still be handled for these pairs.
961 */
962 bool ForceMatrixDecomposition::excludeAtomPair(int atom1, int atom2) {
963 // excludesForAtom was constructed to use row/column indices in the MPI
964 // version, and to use local IDs in the non-MPI version:
965
966 for (vector<int>::iterator i = excludesForAtom[atom1].begin();
967 i != excludesForAtom[atom1].end(); ++i) {
968 if ((*i) == atom2) return true;
969 }
970
971 return false;
972 }
973
974 void ForceMatrixDecomposition::addForceToAtomRow(int atom1, Vector3d fg) {
975#ifdef IS_MPI
976 atomRowData.force[atom1] += fg;
977#else
978 snap_->atomData.force[atom1] += fg;
979#endif
980 }
981
982 void ForceMatrixDecomposition::addForceToAtomColumn(int atom2, Vector3d fg) {
983#ifdef IS_MPI
984 atomColData.force[atom2] += fg;
985#else
986 snap_->atomData.force[atom2] += fg;
987#endif
988 }
989
990 // filling interaction blocks with pointers
991 void ForceMatrixDecomposition::fillInteractionData(InteractionData& idat,
992 int atom1, int atom2,
993 bool newAtom1) {
994 idat.excluded = excludeAtomPair(atom1, atom2);
995
996 if (newAtom1) {
997#ifdef IS_MPI
998 idat.atid1 = identsRow[atom1];
999 idat.atid2 = identsCol[atom2];
1000
1001 if (regionsRow[atom1] >= 0 && regionsCol[atom2] >= 0) {
1002 idat.sameRegion = (regionsRow[atom1] == regionsCol[atom2]);
1003 } else {
1004 idat.sameRegion = false;
1005 }
1006
1007 if (atomStorageLayout_ & DataStorage::dslAmat) {
1008 idat.A1 = atomRowData.aMat[atom1];
1009 idat.A2 = atomColData.aMat[atom2];
1010 }
1011
1012 if (atomStorageLayout_ & DataStorage::dslTorque) {
1013 idat.t1 = atomRowData.torque[atom1];
1014 idat.t2 = atomColData.torque[atom2];
1015 }
1016
1017 if (atomStorageLayout_ & DataStorage::dslDipole) {
1018 idat.D_1 = atomRowData.dipole[atom1];
1019 idat.D_2 = atomColData.dipole[atom2];
1020 }
1021
1022 if (atomStorageLayout_ & DataStorage::dslQuadrupole) {
1023 idat.Q_1 = atomRowData.quadrupole[atom1];
1024 idat.Q_2 = atomColData.quadrupole[atom2];
1025 }
1026
1027 if (atomStorageLayout_ & DataStorage::dslDensity) {
1028 idat.rho1 = atomRowData.density[atom1];
1029 idat.rho2 = atomColData.density[atom2];
1030 }
1031
1032 if (atomStorageLayout_ & DataStorage::dslFunctional) {
1033 idat.frho1 = atomRowData.functional[atom1];
1034 idat.frho2 = atomColData.functional[atom2];
1035 }
1036
1037 if (atomStorageLayout_ & DataStorage::dslFunctionalDerivative) {
1038 idat.dfrho1 = atomRowData.functionalDerivative[atom1];
1039 idat.dfrho2 = atomColData.functionalDerivative[atom2];
1040 }
1041
1042 if (atomStorageLayout_ & DataStorage::dslParticlePot) {
1043 idat.particlePot1 = atomRowData.particlePot[atom1];
1044 idat.particlePot2 = atomColData.particlePot[atom2];
1045 }
1046
1047 if (atomStorageLayout_ & DataStorage::dslSkippedCharge) {
1048 idat.skippedCharge1 = atomRowData.skippedCharge[atom1];
1049 idat.skippedCharge2 = atomColData.skippedCharge[atom2];
1050 }
1051
1052 if (atomStorageLayout_ & DataStorage::dslFlucQPosition) {
1053 idat.flucQ1 = atomRowData.flucQPos[atom1];
1054 idat.flucQ2 = atomColData.flucQPos[atom2];
1055 }
1056
1057#else
1058
1059 idat.atid1 = idents[atom1];
1060 idat.atid2 = idents[atom2];
1061
1062 if (regions[atom1] >= 0 && regions[atom2] >= 0) {
1063 idat.sameRegion = (regions[atom1] == regions[atom2]);
1064 } else {
1065 idat.sameRegion = false;
1066 }
1067
1068 if (atomStorageLayout_ & DataStorage::dslAmat) {
1069 idat.A1 = snap_->atomData.aMat[atom1];
1070 idat.A2 = snap_->atomData.aMat[atom2];
1071 }
1072
1073 if (atomStorageLayout_ & DataStorage::dslTorque) {
1074 idat.t1 = snap_->atomData.torque[atom1];
1075 idat.t2 = snap_->atomData.torque[atom2];
1076 }
1077
1078 if (atomStorageLayout_ & DataStorage::dslDipole) {
1079 idat.D_1 = snap_->atomData.dipole[atom1];
1080 idat.D_2 = snap_->atomData.dipole[atom2];
1081 }
1082
1083 if (atomStorageLayout_ & DataStorage::dslQuadrupole) {
1084 idat.Q_1 = snap_->atomData.quadrupole[atom1];
1085 idat.Q_2 = snap_->atomData.quadrupole[atom2];
1086 }
1087
1088 if (atomStorageLayout_ & DataStorage::dslDensity) {
1089 idat.rho1 = snap_->atomData.density[atom1];
1090 idat.rho2 = snap_->atomData.density[atom2];
1091 }
1092
1093 if (atomStorageLayout_ & DataStorage::dslFunctional) {
1094 idat.frho1 = snap_->atomData.functional[atom1];
1095 idat.frho2 = snap_->atomData.functional[atom2];
1096 }
1097
1098 if (atomStorageLayout_ & DataStorage::dslFunctionalDerivative) {
1099 idat.dfrho1 = snap_->atomData.functionalDerivative[atom1];
1100 idat.dfrho2 = snap_->atomData.functionalDerivative[atom2];
1101 }
1102
1103 if (atomStorageLayout_ & DataStorage::dslParticlePot) {
1104 idat.particlePot1 = snap_->atomData.particlePot[atom1];
1105 idat.particlePot2 = snap_->atomData.particlePot[atom2];
1106 }
1107
1108 if (atomStorageLayout_ & DataStorage::dslSkippedCharge) {
1109 idat.skippedCharge1 = snap_->atomData.skippedCharge[atom1];
1110 idat.skippedCharge2 = snap_->atomData.skippedCharge[atom2];
1111 }
1112
1113 if (atomStorageLayout_ & DataStorage::dslFlucQPosition) {
1114 idat.flucQ1 = snap_->atomData.flucQPos[atom1];
1115 idat.flucQ2 = snap_->atomData.flucQPos[atom2];
1116 }
1117#endif
1118
1119 } else {
1120 // atom1 is not new, so don't bother updating properties of that atom:
1121#ifdef IS_MPI
1122 idat.atid2 = identsCol[atom2];
1123
1124 if (regionsRow[atom1] >= 0 && regionsCol[atom2] >= 0) {
1125 idat.sameRegion = (regionsRow[atom1] == regionsCol[atom2]);
1126 } else {
1127 idat.sameRegion = false;
1128 }
1129
1130 if (atomStorageLayout_ & DataStorage::dslAmat) {
1131 idat.A2 = atomColData.aMat[atom2];
1132 }
1133
1134 if (atomStorageLayout_ & DataStorage::dslTorque) {
1135 idat.t2 = atomColData.torque[atom2];
1136 }
1137
1138 if (atomStorageLayout_ & DataStorage::dslDipole) {
1139 idat.D_2 = atomColData.dipole[atom2];
1140 }
1141
1142 if (atomStorageLayout_ & DataStorage::dslQuadrupole) {
1143 idat.Q_2 = atomColData.quadrupole[atom2];
1144 }
1145
1146 if (atomStorageLayout_ & DataStorage::dslDensity) {
1147 idat.rho2 = atomColData.density[atom2];
1148 }
1149
1150 if (atomStorageLayout_ & DataStorage::dslFunctional) {
1151 idat.frho2 = atomColData.functional[atom2];
1152 }
1153
1154 if (atomStorageLayout_ & DataStorage::dslFunctionalDerivative) {
1155 idat.dfrho2 = atomColData.functionalDerivative[atom2];
1156 }
1157
1158 if (atomStorageLayout_ & DataStorage::dslParticlePot) {
1159 idat.particlePot2 = atomColData.particlePot[atom2];
1160 }
1161
1162 if (atomStorageLayout_ & DataStorage::dslSkippedCharge) {
1163 idat.skippedCharge2 = atomColData.skippedCharge[atom2];
1164 }
1165
1166 if (atomStorageLayout_ & DataStorage::dslFlucQPosition) {
1167 idat.flucQ2 = atomColData.flucQPos[atom2];
1168 }
1169
1170#else
1171 idat.atid2 = idents[atom2];
1172
1173 if (regions[atom1] >= 0 && regions[atom2] >= 0) {
1174 idat.sameRegion = (regions[atom1] == regions[atom2]);
1175 } else {
1176 idat.sameRegion = false;
1177 }
1178
1179 if (atomStorageLayout_ & DataStorage::dslAmat) {
1180 idat.A2 = snap_->atomData.aMat[atom2];
1181 }
1182
1183 if (atomStorageLayout_ & DataStorage::dslTorque) {
1184 idat.t2 = snap_->atomData.torque[atom2];
1185 }
1186
1187 if (atomStorageLayout_ & DataStorage::dslDipole) {
1188 idat.D_2 = snap_->atomData.dipole[atom2];
1189 }
1190
1191 if (atomStorageLayout_ & DataStorage::dslQuadrupole) {
1192 idat.Q_2 = snap_->atomData.quadrupole[atom2];
1193 }
1194
1195 if (atomStorageLayout_ & DataStorage::dslDensity) {
1196 idat.rho2 = snap_->atomData.density[atom2];
1197 }
1198
1199 if (atomStorageLayout_ & DataStorage::dslFunctional) {
1200 idat.frho2 = snap_->atomData.functional[atom2];
1201 }
1202
1203 if (atomStorageLayout_ & DataStorage::dslFunctionalDerivative) {
1204 idat.dfrho2 = snap_->atomData.functionalDerivative[atom2];
1205 }
1206
1207 if (atomStorageLayout_ & DataStorage::dslParticlePot) {
1208 idat.particlePot2 = snap_->atomData.particlePot[atom2];
1209 }
1210
1211 if (atomStorageLayout_ & DataStorage::dslSkippedCharge) {
1212 idat.skippedCharge2 = snap_->atomData.skippedCharge[atom2];
1213 }
1214
1215 if (atomStorageLayout_ & DataStorage::dslFlucQPosition) {
1216 idat.flucQ2 = snap_->atomData.flucQPos[atom2];
1217 }
1218
1219#endif
1220 }
1221 }
1222
1223 void ForceMatrixDecomposition::unpackInteractionData(InteractionData& idat,
1224 int atom1, int atom2) {
1225#ifdef IS_MPI
1226 pot_row[atom1] += 0.5 * idat.pot;
1227 pot_col[atom2] += 0.5 * idat.pot;
1228 expot_row[atom1] += 0.5 * idat.excludedPot;
1229 expot_col[atom2] += 0.5 * idat.excludedPot;
1230 selepot_row[atom1] += 0.5 * idat.selePot;
1231 selepot_col[atom2] += 0.5 * idat.selePot;
1232
1233 atomRowData.force[atom1] += idat.f1;
1234 atomColData.force[atom2] -= idat.f1;
1235
1236 if (atomStorageLayout_ & DataStorage::dslFlucQForce) {
1237 atomRowData.flucQFrc[atom1] -= idat.dVdFQ1;
1238 atomColData.flucQFrc[atom2] -= idat.dVdFQ2;
1239 }
1240
1241 if (atomStorageLayout_ & DataStorage::dslElectricField) {
1242 atomRowData.electricField[atom1] += idat.eField1;
1243 atomColData.electricField[atom2] += idat.eField2;
1244 }
1245
1246 if (atomStorageLayout_ & DataStorage::dslSitePotential) {
1247 atomRowData.sitePotential[atom1] += idat.sPot1;
1248 atomColData.sitePotential[atom2] += idat.sPot2;
1249 }
1250
1251 if (atomStorageLayout_ & DataStorage::dslTorque) {
1252 atomRowData.torque[atom1] = idat.t1;
1253 atomColData.torque[atom2] = idat.t2;
1254 }
1255
1256 if (atomStorageLayout_ & DataStorage::dslSkippedCharge) {
1257 atomRowData.skippedCharge[atom1] = idat.skippedCharge1;
1258 atomColData.skippedCharge[atom2] = idat.skippedCharge2;
1259 }
1260
1261#else
1262 pairwisePot += idat.pot;
1263 excludedPot += idat.excludedPot;
1264 selectedPot += idat.selePot;
1265
1266 snap_->atomData.force[atom1] += idat.f1;
1267 snap_->atomData.force[atom2] -= idat.f1;
1268
1269 if (idat.doParticlePot) {
1270 // This is the pairwise contribution to the particle pot. The
1271 // self and embedding contribution is added in each of the low
1272 // level non-bonded routines. In parallel, this calculation is
1273 // done in collectData, not in unpackInteractionData.
1274 snap_->atomData.particlePot[atom1] += idat.vpair * idat.sw;
1275 snap_->atomData.particlePot[atom2] += idat.vpair * idat.sw;
1276 }
1277
1278 if (atomStorageLayout_ & DataStorage::dslFlucQForce) {
1279 snap_->atomData.flucQFrc[atom1] -= idat.dVdFQ1;
1280 snap_->atomData.flucQFrc[atom2] -= idat.dVdFQ2;
1281 }
1282
1283 if (atomStorageLayout_ & DataStorage::dslElectricField) {
1284 snap_->atomData.electricField[atom1] += idat.eField1;
1285 snap_->atomData.electricField[atom2] += idat.eField2;
1286 }
1287
1288 if (atomStorageLayout_ & DataStorage::dslSitePotential) {
1289 snap_->atomData.sitePotential[atom1] += idat.sPot1;
1290 snap_->atomData.sitePotential[atom2] += idat.sPot2;
1291 }
1292
1293 if (atomStorageLayout_ & DataStorage::dslTorque) {
1294 snap_->atomData.torque[atom1] = idat.t1;
1295 snap_->atomData.torque[atom2] = idat.t2;
1296 }
1297
1298 if (atomStorageLayout_ & DataStorage::dslSkippedCharge) {
1299 snap_->atomData.skippedCharge[atom1] = idat.skippedCharge1;
1300 snap_->atomData.skippedCharge[atom2] = idat.skippedCharge2;
1301 }
1302
1303#endif
1304 }
1305 void ForceMatrixDecomposition::unpackPrePairData(InteractionData& idat,
1306 int atom1, int atom2) {
1307#ifdef IS_MPI
1308
1309 if (atomStorageLayout_ & DataStorage::dslDensity) {
1310 atomRowData.density[atom1] = idat.rho1;
1311 atomColData.density[atom2] = idat.rho2;
1312 }
1313
1314#else
1315
1316 if (atomStorageLayout_ & DataStorage::dslDensity) {
1317 snap_->atomData.density[atom1] = idat.rho1;
1318 snap_->atomData.density[atom2] = idat.rho2;
1319 }
1320
1321#endif
1322 }
1323
1324 /*
1325 * buildNeighborList
1326 *
1327 * Constructs the Verlet neighbor list for a force-matrix
1328 * decomposition. In this case, each processor is responsible for
1329 * row-site interactions with column-sites.
1330 *
1331 * neighborList is returned as a packed array of neighboring
1332 * column-ordered CutoffGroups. The starting position in
1333 * neighborList for each row-ordered CutoffGroup is given by the
1334 * returned vector point.
1335 */
1336 void ForceMatrixDecomposition::buildNeighborList(
1337 vector<int>& neighborList, vector<int>& point,
1338 vector<Vector3d>& savedPositions) {
1339 neighborList.clear();
1340 point.clear();
1341 int len = 0;
1342
1343 bool doAllPairs = false;
1344
1345 Snapshot* snap_ = sman_->getCurrentSnapshot();
1346 Mat3x3d box;
1347 Mat3x3d invBox;
1348
1349 Vector3d rs, scaled, dr;
1350 Vector3i whichCell;
1351 int cellIndex;
1352
1353#ifdef IS_MPI
1354 cellListRow_.clear();
1355 cellListCol_.clear();
1356 point.resize(nGroupsInRow_ + 1);
1357#else
1358 cellList_.clear();
1359 point.resize(nGroups_ + 1);
1360#endif
1361
1362 if (!usePeriodicBoundaryConditions_) {
1363 box = snap_->getBoundingBox();
1364 invBox = snap_->getInvBoundingBox();
1365 } else {
1366 box = snap_->getHmat();
1367 invBox = snap_->getInvHmat();
1368 }
1369
1370 Vector3d A = box.getColumn(0);
1371 Vector3d B = box.getColumn(1);
1372 Vector3d C = box.getColumn(2);
1373
1374 // Required for triclinic cells
1375 Vector3d AxB = cross(A, B);
1376 Vector3d BxC = cross(B, C);
1377 Vector3d CxA = cross(C, A);
1378
1379 // unit vectors perpendicular to the faces of the triclinic cell:
1380 AxB.normalize();
1381 BxC.normalize();
1382 CxA.normalize();
1383
1384 // A set of perpendicular lengths in triclinic cells:
1385 RealType Wa = abs(dot(A, BxC));
1386 RealType Wb = abs(dot(B, CxA));
1387 RealType Wc = abs(dot(C, AxB));
1388
1389 nCells_.x() = int(Wa / rList_);
1390 nCells_.y() = int(Wb / rList_);
1391 nCells_.z() = int(Wc / rList_);
1392
1393 // handle small boxes where the cell offsets can end up repeating cells
1394 if (nCells_.x() < 3) doAllPairs = true;
1395 if (nCells_.y() < 3) doAllPairs = true;
1396 if (nCells_.z() < 3) doAllPairs = true;
1397
1398 int nCtot = nCells_.x() * nCells_.y() * nCells_.z();
1399
1400#ifdef IS_MPI
1401 cellListRow_.resize(nCtot);
1402 cellListCol_.resize(nCtot);
1403#else
1404 cellList_.resize(nCtot);
1405#endif
1406
1407 if (!doAllPairs) {
1408#ifdef IS_MPI
1409
1410 for (int i = 0; i < nGroupsInRow_; i++) {
1411 rs = cgRowData.position[i];
1412
1413 // scaled positions relative to the box vectors
1414 scaled = invBox * rs;
1415
1416 // wrap the vector back into the unit box by subtracting integer box
1417 // numbers
1418 for (int j = 0; j < 3; j++) {
1419 scaled[j] -= roundMe(scaled[j]);
1420 scaled[j] += 0.5;
1421 // Handle the special case when an object is exactly on the
1422 // boundary (a scaled coordinate of 1.0 is the same as
1423 // scaled coordinate of 0.0)
1424 if (scaled[j] >= 1.0) scaled[j] -= 1.0;
1425 }
1426
1427 // find xyz-indices of cell that cutoffGroup is in.
1428 whichCell.x() = int(nCells_.x() * scaled.x());
1429 whichCell.y() = int(nCells_.y() * scaled.y());
1430 whichCell.z() = int(nCells_.z() * scaled.z());
1431
1432 // find single index of this cell:
1433 cellIndex = Vlinear(whichCell, nCells_);
1434
1435 // add this cutoff group to the list of groups in this cell;
1436 cellListRow_[cellIndex].push_back(i);
1437 }
1438 for (int i = 0; i < nGroupsInCol_; i++) {
1439 rs = cgColData.position[i];
1440
1441 // scaled positions relative to the box vectors
1442 scaled = invBox * rs;
1443
1444 // wrap the vector back into the unit box by subtracting integer box
1445 // numbers
1446 for (int j = 0; j < 3; j++) {
1447 scaled[j] -= roundMe(scaled[j]);
1448 scaled[j] += 0.5;
1449 // Handle the special case when an object is exactly on the
1450 // boundary (a scaled coordinate of 1.0 is the same as
1451 // scaled coordinate of 0.0)
1452 if (scaled[j] >= 1.0) scaled[j] -= 1.0;
1453 }
1454
1455 // find xyz-indices of cell that cutoffGroup is in.
1456 whichCell.x() = int(nCells_.x() * scaled.x());
1457 whichCell.y() = int(nCells_.y() * scaled.y());
1458 whichCell.z() = int(nCells_.z() * scaled.z());
1459
1460 // find single index of this cell:
1461 cellIndex = Vlinear(whichCell, nCells_);
1462
1463 // add this cutoff group to the list of groups in this cell;
1464 cellListCol_[cellIndex].push_back(i);
1465 }
1466
1467#else
1468 for (int i = 0; i < nGroups_; i++) {
1469 rs = snap_->cgData.position[i];
1470
1471 // scaled positions relative to the box vectors
1472 scaled = invBox * rs;
1473
1474 // wrap the vector back into the unit box by subtracting integer box
1475 // numbers
1476 for (int j = 0; j < 3; j++) {
1477 scaled[j] -= roundMe(scaled[j]);
1478 scaled[j] += 0.5;
1479 // Handle the special case when an object is exactly on the
1480 // boundary (a scaled coordinate of 1.0 is the same as
1481 // scaled coordinate of 0.0)
1482 if (scaled[j] >= 1.0) scaled[j] -= 1.0;
1483 }
1484
1485 // find xyz-indices of cell that cutoffGroup is in.
1486 whichCell.x() = int(nCells_.x() * scaled.x());
1487 whichCell.y() = int(nCells_.y() * scaled.y());
1488 whichCell.z() = int(nCells_.z() * scaled.z());
1489
1490 // find single index of this cell:
1491 cellIndex = Vlinear(whichCell, nCells_);
1492
1493 // add this cutoff group to the list of groups in this cell;
1494 cellList_[cellIndex].push_back(i);
1495 }
1496
1497#endif
1498
1499#ifdef IS_MPI
1500 for (int j1 = 0; j1 < nGroupsInRow_; j1++) {
1501 rs = cgRowData.position[j1];
1502#else
1503
1504 for (int j1 = 0; j1 < nGroups_; j1++) {
1505 rs = snap_->cgData.position[j1];
1506#endif
1507 point[j1] = len;
1508
1509 // scaled positions relative to the box vectors
1510 scaled = invBox * rs;
1511
1512 // wrap the vector back into the unit box by subtracting integer box
1513 // numbers
1514 for (int j = 0; j < 3; j++) {
1515 scaled[j] -= roundMe(scaled[j]);
1516 scaled[j] += 0.5;
1517 // Handle the special case when an object is exactly on the
1518 // boundary (a scaled coordinate of 1.0 is the same as
1519 // scaled coordinate of 0.0)
1520 if (scaled[j] >= 1.0) scaled[j] -= 1.0;
1521 }
1522
1523 // find xyz-indices of cell that cutoffGroup is in.
1524 whichCell.x() = int(nCells_.x() * scaled.x());
1525 whichCell.y() = int(nCells_.y() * scaled.y());
1526 whichCell.z() = int(nCells_.z() * scaled.z());
1527
1528 for (vector<Vector3i>::iterator os = cellOffsets_.begin();
1529 os != cellOffsets_.end(); ++os) {
1530 Vector3i m2v = whichCell + (*os);
1531
1532 if (m2v.x() >= nCells_.x()) {
1533 m2v.x() = 0;
1534 } else if (m2v.x() < 0) {
1535 m2v.x() = nCells_.x() - 1;
1536 }
1537
1538 if (m2v.y() >= nCells_.y()) {
1539 m2v.y() = 0;
1540 } else if (m2v.y() < 0) {
1541 m2v.y() = nCells_.y() - 1;
1542 }
1543
1544 if (m2v.z() >= nCells_.z()) {
1545 m2v.z() = 0;
1546 } else if (m2v.z() < 0) {
1547 m2v.z() = nCells_.z() - 1;
1548 }
1549 int m2 = Vlinear(m2v, nCells_);
1550#ifdef IS_MPI
1551 for (vector<int>::iterator j2 = cellListCol_[m2].begin();
1552 j2 != cellListCol_[m2].end(); ++j2) {
1553 // In parallel, we need to visit *all* pairs of row
1554 // & column indicies and will divide labor in the
1555 // force evaluation later.
1556 dr = cgColData.position[(*j2)] - rs;
1557 if (usePeriodicBoundaryConditions_) { snap_->wrapVector(dr); }
1558 if (dr.lengthSquare() < rListSq_) {
1559 neighborList.push_back((*j2));
1560 ++len;
1561 }
1562 }
1563#else
1564 for (vector<int>::iterator j2 = cellList_[m2].begin();
1565 j2 != cellList_[m2].end(); ++j2) {
1566 // Always do this if we're in different cells or if
1567 // we're in the same cell and the global index of
1568 // the j2 cutoff group is greater than or equal to
1569 // the j1 cutoff group. Note that Rappaport's code
1570 // has a "less than" conditional here, but that
1571 // deals with atom-by-atom computation. OpenMD
1572 // allows atoms within a single cutoff group to
1573 // interact with each other.
1574
1575 if ((*j2) >= j1) {
1576 dr = snap_->cgData.position[(*j2)] - rs;
1577 if (usePeriodicBoundaryConditions_) { snap_->wrapVector(dr); }
1578 if (dr.lengthSquare() < rListSq_) {
1579 neighborList.push_back((*j2));
1580 ++len;
1581 }
1582 }
1583 }
1584#endif
1585 }
1586 }
1587 } else {
1588 // branch to do all cutoff group pairs
1589#ifdef IS_MPI
1590 for (int j1 = 0; j1 < nGroupsInRow_; j1++) {
1591 point[j1] = len;
1592 rs = cgRowData.position[j1];
1593 for (int j2 = 0; j2 < nGroupsInCol_; j2++) {
1594 dr = cgColData.position[j2] - rs;
1595 if (usePeriodicBoundaryConditions_) { snap_->wrapVector(dr); }
1596 if (dr.lengthSquare() < rListSq_) {
1597 neighborList.push_back(j2);
1598 ++len;
1599 }
1600 }
1601 }
1602#else
1603 // include all groups here.
1604 for (int j1 = 0; j1 < nGroups_; j1++) {
1605 point[j1] = len;
1606 rs = snap_->cgData.position[j1];
1607 // include self group interactions j2 == j1
1608 for (int j2 = j1; j2 < nGroups_; j2++) {
1609 dr = snap_->cgData.position[j2] - rs;
1610 if (usePeriodicBoundaryConditions_) { snap_->wrapVector(dr); }
1611 if (dr.lengthSquare() < rListSq_) {
1612 neighborList.push_back(j2);
1613 ++len;
1614 }
1615 }
1616 }
1617#endif
1618 }
1619
1620#ifdef IS_MPI
1621 point[nGroupsInRow_] = len;
1622#else
1623 point[nGroups_] = len;
1624#endif
1625
1626 // save the local cutoff group positions for the check that is
1627 // done on each loop:
1628 savedPositions.clear();
1629 savedPositions.reserve(nGroups_);
1630 for (int i = 0; i < nGroups_; i++)
1631 savedPositions.push_back(snap_->cgData.position[i]);
1632 }
1633
1634 int ForceMatrixDecomposition::getGlobalIDRow(int atom1) {
1635#ifdef IS_MPI
1636 return AtomRowToGlobal[atom1];
1637#else
1638 return atom1;
1639#endif
1640 }
1641
1642 int ForceMatrixDecomposition::getGlobalIDCol(int atom2) {
1643#ifdef IS_MPI
1644 return AtomColToGlobal[atom2];
1645#else
1646 return atom2;
1647#endif
1648 }
1649
1650 int ForceMatrixDecomposition::getGlobalID(int atom1) {
1651#ifdef IS_MPI
1652 return AtomLocalToGlobal[atom1];
1653#else
1654 return atom1;
1655#endif
1656 }
1657} // namespace OpenMD
ForceDecomposition is an interface for passing out and collecting information from many processors at...
vector< vector< int > > toposForAtom
The topological distance between two atomic sites is handled via two vector structures for speed.
bool excludeAtomPair(int atom1, int atom2)
We need to handle the interactions for atoms who are involved in the same rigid body as well as some ...
vector< int > & getAtomsInGroupRow(int cg1)
returns the list of atoms belonging to this group.
bool skipAtomPair(int atom1, int atom2, int cg1, int cg2)
We need to exclude some overcounted interactions that result from the parallel decomposition.
void distributeInitialData()
distributeInitialData is essentially a copy of the older fortran SimulationSetup
void collectSelfData()
Collects information obtained during the post-pair (and embedding functional) loops onto local data s...
InteractionManager is responsible for keeping track of the non-bonded interactions (C++).
PairList class maintains a general purpose list of atom pairs using the global indices of the atoms.
Definition PairList.hpp:66
bool hasPair(int i, int j)
Checks whether pair (i, j) is in this PairList class.
Definition PairList.cpp:143
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.
Vector3< Real > cross(const Vector3< Real > &v1, const Vector3< Real > &v2)
Returns the cross product of two Vectors.
Definition Vector3.hpp:139
Real dot(const DynamicVector< Real > &v1, const DynamicVector< Real > &v2)
Returns the dot product of two DynamicVectors.
std::size_t Vlinear(const Vector2< std::size_t > &p, const Vector2< std::size_t > &s)
Returns the linear indexing for size_t vectors.
Definition Vector2.hpp:114