ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/OpenMD/trunk/src/parallel/ForceMatrixDecomposition.cpp
Revision: 1601
Committed: Thu Aug 4 20:04:35 2011 UTC (13 years, 8 months ago) by gezelter
Original Path: branches/development/src/parallel/ForceMatrixDecomposition.cpp
File size: 40941 byte(s)
Log Message:
removed spurious prints, fixed one bug, but there's still a parallel problem

File Contents

# Content
1 /*
2 * Copyright (c) 2005 The University of Notre Dame. All Rights Reserved.
3 *
4 * The University of Notre Dame grants you ("Licensee") a
5 * non-exclusive, royalty free, license to use, modify and
6 * redistribute this software in source and binary code form, provided
7 * that the following conditions are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the
15 * distribution.
16 *
17 * This software is provided "AS IS," without a warranty of any
18 * kind. All express or implied conditions, representations and
19 * warranties, including any implied warranty of merchantability,
20 * fitness for a particular purpose or non-infringement, are hereby
21 * excluded. The University of Notre Dame and its licensors shall not
22 * be liable for any damages suffered by licensee as a result of
23 * using, modifying or distributing the software or its
24 * derivatives. In no event will the University of Notre Dame or its
25 * licensors be liable for any lost revenue, profit or data, or for
26 * direct, indirect, special, consequential, incidental or punitive
27 * damages, however caused and regardless of the theory of liability,
28 * arising out of the use of or inability to use software, even if the
29 * University of Notre Dame has been advised of the possibility of
30 * such damages.
31 *
32 * SUPPORT OPEN SCIENCE! If you use OpenMD or its source code in your
33 * research, please cite the appropriate papers when you publish your
34 * work. Good starting points are:
35 *
36 * [1] Meineke, et al., J. Comp. Chem. 26, 252-271 (2005).
37 * [2] Fennell & Gezelter, J. Chem. Phys. 124, 234104 (2006).
38 * [3] Sun, Lin & Gezelter, J. Chem. Phys. 128, 24107 (2008).
39 * [4] Vardeman & Gezelter, in progress (2009).
40 */
41 #include "parallel/ForceMatrixDecomposition.hpp"
42 #include "math/SquareMatrix3.hpp"
43 #include "nonbonded/NonBondedInteraction.hpp"
44 #include "brains/SnapshotManager.hpp"
45 #include "brains/PairList.hpp"
46
47 using namespace std;
48 namespace OpenMD {
49
50 ForceMatrixDecomposition::ForceMatrixDecomposition(SimInfo* info, InteractionManager* iMan) : ForceDecomposition(info, iMan) {
51
52 // In a parallel computation, row and colum scans must visit all
53 // surrounding cells (not just the 14 upper triangular blocks that
54 // are used when the processor can see all pairs)
55 #ifdef IS_MPI
56 cellOffsets_.push_back( Vector3i(-1, 0, 0) );
57 cellOffsets_.push_back( Vector3i(-1,-1, 0) );
58 cellOffsets_.push_back( Vector3i( 0,-1, 0) );
59 cellOffsets_.push_back( Vector3i( 1,-1, 0) );
60 cellOffsets_.push_back( Vector3i( 0, 0,-1) );
61 cellOffsets_.push_back( Vector3i(-1, 0, 1) );
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( 1, 1,-1) );
67 cellOffsets_.push_back( Vector3i( 0, 1,-1) );
68 cellOffsets_.push_back( Vector3i(-1, 1,-1) );
69 #endif
70 }
71
72
73 /**
74 * distributeInitialData is essentially a copy of the older fortran
75 * SimulationSetup
76 */
77 void ForceMatrixDecomposition::distributeInitialData() {
78 snap_ = sman_->getCurrentSnapshot();
79 storageLayout_ = sman_->getStorageLayout();
80 ff_ = info_->getForceField();
81 nLocal_ = snap_->getNumberOfAtoms();
82
83 nGroups_ = info_->getNLocalCutoffGroups();
84 // gather the information for atomtype IDs (atids):
85 idents = info_->getIdentArray();
86 AtomLocalToGlobal = info_->getGlobalAtomIndices();
87 cgLocalToGlobal = info_->getGlobalGroupIndices();
88 vector<int> globalGroupMembership = info_->getGlobalGroupMembership();
89
90 massFactors = info_->getMassFactors();
91
92 PairList* excludes = info_->getExcludedInteractions();
93 PairList* oneTwo = info_->getOneTwoInteractions();
94 PairList* oneThree = info_->getOneThreeInteractions();
95 PairList* oneFour = info_->getOneFourInteractions();
96
97 #ifdef IS_MPI
98
99 MPI::Intracomm row = rowComm.getComm();
100 MPI::Intracomm col = colComm.getComm();
101
102 AtomPlanIntRow = new Plan<int>(row, nLocal_);
103 AtomPlanRealRow = new Plan<RealType>(row, nLocal_);
104 AtomPlanVectorRow = new Plan<Vector3d>(row, nLocal_);
105 AtomPlanMatrixRow = new Plan<Mat3x3d>(row, nLocal_);
106 AtomPlanPotRow = new Plan<potVec>(row, nLocal_);
107
108 AtomPlanIntColumn = new Plan<int>(col, nLocal_);
109 AtomPlanRealColumn = new Plan<RealType>(col, nLocal_);
110 AtomPlanVectorColumn = new Plan<Vector3d>(col, nLocal_);
111 AtomPlanMatrixColumn = new Plan<Mat3x3d>(col, nLocal_);
112 AtomPlanPotColumn = new Plan<potVec>(col, nLocal_);
113
114 cgPlanIntRow = new Plan<int>(row, nGroups_);
115 cgPlanVectorRow = new Plan<Vector3d>(row, nGroups_);
116 cgPlanIntColumn = new Plan<int>(col, nGroups_);
117 cgPlanVectorColumn = new Plan<Vector3d>(col, nGroups_);
118
119 nAtomsInRow_ = AtomPlanIntRow->getSize();
120 nAtomsInCol_ = AtomPlanIntColumn->getSize();
121 nGroupsInRow_ = cgPlanIntRow->getSize();
122 nGroupsInCol_ = cgPlanIntColumn->getSize();
123
124 // Modify the data storage objects with the correct layouts and sizes:
125 atomRowData.resize(nAtomsInRow_);
126 atomRowData.setStorageLayout(storageLayout_);
127 atomColData.resize(nAtomsInCol_);
128 atomColData.setStorageLayout(storageLayout_);
129 cgRowData.resize(nGroupsInRow_);
130 cgRowData.setStorageLayout(DataStorage::dslPosition);
131 cgColData.resize(nGroupsInCol_);
132 cgColData.setStorageLayout(DataStorage::dslPosition);
133
134 identsRow.resize(nAtomsInRow_);
135 identsCol.resize(nAtomsInCol_);
136
137 AtomPlanIntRow->gather(idents, identsRow);
138 AtomPlanIntColumn->gather(idents, identsCol);
139
140 // allocate memory for the parallel objects
141 atypesRow.resize(nAtomsInRow_);
142 atypesCol.resize(nAtomsInCol_);
143
144 for (int i = 0; i < nAtomsInRow_; i++)
145 atypesRow[i] = ff_->getAtomType(identsRow[i]);
146 for (int i = 0; i < nAtomsInCol_; i++)
147 atypesCol[i] = ff_->getAtomType(identsCol[i]);
148
149 pot_row.resize(nAtomsInRow_);
150 pot_col.resize(nAtomsInCol_);
151
152 AtomRowToGlobal.resize(nAtomsInRow_);
153 AtomColToGlobal.resize(nAtomsInCol_);
154 AtomPlanIntRow->gather(AtomLocalToGlobal, AtomRowToGlobal);
155 AtomPlanIntColumn->gather(AtomLocalToGlobal, AtomColToGlobal);
156
157 cgRowToGlobal.resize(nGroupsInRow_);
158 cgColToGlobal.resize(nGroupsInCol_);
159 cgPlanIntRow->gather(cgLocalToGlobal, cgRowToGlobal);
160 cgPlanIntColumn->gather(cgLocalToGlobal, cgColToGlobal);
161
162 massFactorsRow.resize(nAtomsInRow_);
163 massFactorsCol.resize(nAtomsInCol_);
164 AtomPlanRealRow->gather(massFactors, massFactorsRow);
165 AtomPlanRealColumn->gather(massFactors, massFactorsCol);
166
167 groupListRow_.clear();
168 groupListRow_.resize(nGroupsInRow_);
169 for (int i = 0; i < nGroupsInRow_; i++) {
170 int gid = cgRowToGlobal[i];
171 for (int j = 0; j < nAtomsInRow_; j++) {
172 int aid = AtomRowToGlobal[j];
173 if (globalGroupMembership[aid] == gid)
174 groupListRow_[i].push_back(j);
175 }
176 }
177
178 groupListCol_.clear();
179 groupListCol_.resize(nGroupsInCol_);
180 for (int i = 0; i < nGroupsInCol_; i++) {
181 int gid = cgColToGlobal[i];
182 for (int j = 0; j < nAtomsInCol_; j++) {
183 int aid = AtomColToGlobal[j];
184 if (globalGroupMembership[aid] == gid)
185 groupListCol_[i].push_back(j);
186 }
187 }
188
189 excludesForAtom.clear();
190 excludesForAtom.resize(nAtomsInRow_);
191 toposForAtom.clear();
192 toposForAtom.resize(nAtomsInRow_);
193 topoDist.clear();
194 topoDist.resize(nAtomsInRow_);
195 for (int i = 0; i < nAtomsInRow_; i++) {
196 int iglob = AtomRowToGlobal[i];
197
198 for (int j = 0; j < nAtomsInCol_; j++) {
199 int jglob = AtomColToGlobal[j];
200
201 if (excludes->hasPair(iglob, jglob))
202 excludesForAtom[i].push_back(j);
203
204 if (oneTwo->hasPair(iglob, jglob)) {
205 toposForAtom[i].push_back(j);
206 topoDist[i].push_back(1);
207 } else {
208 if (oneThree->hasPair(iglob, jglob)) {
209 toposForAtom[i].push_back(j);
210 topoDist[i].push_back(2);
211 } else {
212 if (oneFour->hasPair(iglob, jglob)) {
213 toposForAtom[i].push_back(j);
214 topoDist[i].push_back(3);
215 }
216 }
217 }
218 }
219 }
220
221 #endif
222
223 // allocate memory for the parallel objects
224 atypesLocal.resize(nLocal_);
225
226 for (int i = 0; i < nLocal_; i++)
227 atypesLocal[i] = ff_->getAtomType(idents[i]);
228
229 groupList_.clear();
230 groupList_.resize(nGroups_);
231 for (int i = 0; i < nGroups_; i++) {
232 int gid = cgLocalToGlobal[i];
233 for (int j = 0; j < nLocal_; j++) {
234 int aid = AtomLocalToGlobal[j];
235 if (globalGroupMembership[aid] == gid) {
236 groupList_[i].push_back(j);
237 }
238 }
239 }
240
241 excludesForAtom.clear();
242 excludesForAtom.resize(nLocal_);
243 toposForAtom.clear();
244 toposForAtom.resize(nLocal_);
245 topoDist.clear();
246 topoDist.resize(nLocal_);
247
248 for (int i = 0; i < nLocal_; i++) {
249 int iglob = AtomLocalToGlobal[i];
250
251 for (int j = 0; j < nLocal_; j++) {
252 int jglob = AtomLocalToGlobal[j];
253
254 if (excludes->hasPair(iglob, jglob))
255 excludesForAtom[i].push_back(j);
256
257 if (oneTwo->hasPair(iglob, jglob)) {
258 toposForAtom[i].push_back(j);
259 topoDist[i].push_back(1);
260 } else {
261 if (oneThree->hasPair(iglob, jglob)) {
262 toposForAtom[i].push_back(j);
263 topoDist[i].push_back(2);
264 } else {
265 if (oneFour->hasPair(iglob, jglob)) {
266 toposForAtom[i].push_back(j);
267 topoDist[i].push_back(3);
268 }
269 }
270 }
271 }
272 }
273
274 createGtypeCutoffMap();
275
276 }
277
278 void ForceMatrixDecomposition::createGtypeCutoffMap() {
279
280 RealType tol = 1e-6;
281 largestRcut_ = 0.0;
282 RealType rc;
283 int atid;
284 set<AtomType*> atypes = info_->getSimulatedAtomTypes();
285
286 map<int, RealType> atypeCutoff;
287
288 for (set<AtomType*>::iterator at = atypes.begin();
289 at != atypes.end(); ++at){
290 atid = (*at)->getIdent();
291 if (userChoseCutoff_)
292 atypeCutoff[atid] = userCutoff_;
293 else
294 atypeCutoff[atid] = interactionMan_->getSuggestedCutoffRadius(*at);
295 }
296
297 vector<RealType> gTypeCutoffs;
298 // first we do a single loop over the cutoff groups to find the
299 // largest cutoff for any atypes present in this group.
300 #ifdef IS_MPI
301 vector<RealType> groupCutoffRow(nGroupsInRow_, 0.0);
302 groupRowToGtype.resize(nGroupsInRow_);
303 for (int cg1 = 0; cg1 < nGroupsInRow_; cg1++) {
304 vector<int> atomListRow = getAtomsInGroupRow(cg1);
305 for (vector<int>::iterator ia = atomListRow.begin();
306 ia != atomListRow.end(); ++ia) {
307 int atom1 = (*ia);
308 atid = identsRow[atom1];
309 if (atypeCutoff[atid] > groupCutoffRow[cg1]) {
310 groupCutoffRow[cg1] = atypeCutoff[atid];
311 }
312 }
313
314 bool gTypeFound = false;
315 for (int gt = 0; gt < gTypeCutoffs.size(); gt++) {
316 if (abs(groupCutoffRow[cg1] - gTypeCutoffs[gt]) < tol) {
317 groupRowToGtype[cg1] = gt;
318 gTypeFound = true;
319 }
320 }
321 if (!gTypeFound) {
322 gTypeCutoffs.push_back( groupCutoffRow[cg1] );
323 groupRowToGtype[cg1] = gTypeCutoffs.size() - 1;
324 }
325
326 }
327 vector<RealType> groupCutoffCol(nGroupsInCol_, 0.0);
328 groupColToGtype.resize(nGroupsInCol_);
329 for (int cg2 = 0; cg2 < nGroupsInCol_; cg2++) {
330 vector<int> atomListCol = getAtomsInGroupColumn(cg2);
331 for (vector<int>::iterator jb = atomListCol.begin();
332 jb != atomListCol.end(); ++jb) {
333 int atom2 = (*jb);
334 atid = identsCol[atom2];
335 if (atypeCutoff[atid] > groupCutoffCol[cg2]) {
336 groupCutoffCol[cg2] = atypeCutoff[atid];
337 }
338 }
339 bool gTypeFound = false;
340 for (int gt = 0; gt < gTypeCutoffs.size(); gt++) {
341 if (abs(groupCutoffCol[cg2] - gTypeCutoffs[gt]) < tol) {
342 groupColToGtype[cg2] = gt;
343 gTypeFound = true;
344 }
345 }
346 if (!gTypeFound) {
347 gTypeCutoffs.push_back( groupCutoffCol[cg2] );
348 groupColToGtype[cg2] = gTypeCutoffs.size() - 1;
349 }
350 }
351 #else
352
353 vector<RealType> groupCutoff(nGroups_, 0.0);
354 groupToGtype.resize(nGroups_);
355 for (int cg1 = 0; cg1 < nGroups_; cg1++) {
356 groupCutoff[cg1] = 0.0;
357 vector<int> atomList = getAtomsInGroupRow(cg1);
358 for (vector<int>::iterator ia = atomList.begin();
359 ia != atomList.end(); ++ia) {
360 int atom1 = (*ia);
361 atid = idents[atom1];
362 if (atypeCutoff[atid] > groupCutoff[cg1])
363 groupCutoff[cg1] = atypeCutoff[atid];
364 }
365
366 bool gTypeFound = false;
367 for (int gt = 0; gt < gTypeCutoffs.size(); gt++) {
368 if (abs(groupCutoff[cg1] - gTypeCutoffs[gt]) < tol) {
369 groupToGtype[cg1] = gt;
370 gTypeFound = true;
371 }
372 }
373 if (!gTypeFound) {
374 gTypeCutoffs.push_back( groupCutoff[cg1] );
375 groupToGtype[cg1] = gTypeCutoffs.size() - 1;
376 }
377 }
378 #endif
379
380 // Now we find the maximum group cutoff value present in the simulation
381
382 RealType groupMax = *max_element(gTypeCutoffs.begin(),
383 gTypeCutoffs.end());
384
385 #ifdef IS_MPI
386 MPI::COMM_WORLD.Allreduce(&groupMax, &groupMax, 1, MPI::REALTYPE,
387 MPI::MAX);
388 #endif
389
390 RealType tradRcut = groupMax;
391
392 for (int i = 0; i < gTypeCutoffs.size(); i++) {
393 for (int j = 0; j < gTypeCutoffs.size(); j++) {
394 RealType thisRcut;
395 switch(cutoffPolicy_) {
396 case TRADITIONAL:
397 thisRcut = tradRcut;
398 break;
399 case MIX:
400 thisRcut = 0.5 * (gTypeCutoffs[i] + gTypeCutoffs[j]);
401 break;
402 case MAX:
403 thisRcut = max(gTypeCutoffs[i], gTypeCutoffs[j]);
404 break;
405 default:
406 sprintf(painCave.errMsg,
407 "ForceMatrixDecomposition::createGtypeCutoffMap "
408 "hit an unknown cutoff policy!\n");
409 painCave.severity = OPENMD_ERROR;
410 painCave.isFatal = 1;
411 simError();
412 break;
413 }
414
415 pair<int,int> key = make_pair(i,j);
416 gTypeCutoffMap[key].first = thisRcut;
417 if (thisRcut > largestRcut_) largestRcut_ = thisRcut;
418 gTypeCutoffMap[key].second = thisRcut*thisRcut;
419 gTypeCutoffMap[key].third = pow(thisRcut + skinThickness_, 2);
420 // sanity check
421
422 if (userChoseCutoff_) {
423 if (abs(gTypeCutoffMap[key].first - userCutoff_) > 0.0001) {
424 sprintf(painCave.errMsg,
425 "ForceMatrixDecomposition::createGtypeCutoffMap "
426 "user-specified rCut (%lf) does not match computed group Cutoff\n", userCutoff_);
427 painCave.severity = OPENMD_ERROR;
428 painCave.isFatal = 1;
429 simError();
430 }
431 }
432 }
433 }
434 }
435
436
437 groupCutoffs ForceMatrixDecomposition::getGroupCutoffs(int cg1, int cg2) {
438 int i, j;
439 #ifdef IS_MPI
440 i = groupRowToGtype[cg1];
441 j = groupColToGtype[cg2];
442 #else
443 i = groupToGtype[cg1];
444 j = groupToGtype[cg2];
445 #endif
446 return gTypeCutoffMap[make_pair(i,j)];
447 }
448
449 int ForceMatrixDecomposition::getTopologicalDistance(int atom1, int atom2) {
450 for (int j = 0; j < toposForAtom[atom1].size(); j++) {
451 if (toposForAtom[atom1][j] == atom2)
452 return topoDist[atom1][j];
453 }
454 return 0;
455 }
456
457 void ForceMatrixDecomposition::zeroWorkArrays() {
458 pairwisePot = 0.0;
459 embeddingPot = 0.0;
460
461 #ifdef IS_MPI
462 if (storageLayout_ & DataStorage::dslForce) {
463 fill(atomRowData.force.begin(), atomRowData.force.end(), V3Zero);
464 fill(atomColData.force.begin(), atomColData.force.end(), V3Zero);
465 }
466
467 if (storageLayout_ & DataStorage::dslTorque) {
468 fill(atomRowData.torque.begin(), atomRowData.torque.end(), V3Zero);
469 fill(atomColData.torque.begin(), atomColData.torque.end(), V3Zero);
470 }
471
472 fill(pot_row.begin(), pot_row.end(),
473 Vector<RealType, N_INTERACTION_FAMILIES> (0.0));
474
475 fill(pot_col.begin(), pot_col.end(),
476 Vector<RealType, N_INTERACTION_FAMILIES> (0.0));
477
478 if (storageLayout_ & DataStorage::dslParticlePot) {
479 fill(atomRowData.particlePot.begin(), atomRowData.particlePot.end(),
480 0.0);
481 fill(atomColData.particlePot.begin(), atomColData.particlePot.end(),
482 0.0);
483 }
484
485 if (storageLayout_ & DataStorage::dslDensity) {
486 fill(atomRowData.density.begin(), atomRowData.density.end(), 0.0);
487 fill(atomColData.density.begin(), atomColData.density.end(), 0.0);
488 }
489
490 if (storageLayout_ & DataStorage::dslFunctional) {
491 fill(atomRowData.functional.begin(), atomRowData.functional.end(),
492 0.0);
493 fill(atomColData.functional.begin(), atomColData.functional.end(),
494 0.0);
495 }
496
497 if (storageLayout_ & DataStorage::dslFunctionalDerivative) {
498 fill(atomRowData.functionalDerivative.begin(),
499 atomRowData.functionalDerivative.end(), 0.0);
500 fill(atomColData.functionalDerivative.begin(),
501 atomColData.functionalDerivative.end(), 0.0);
502 }
503
504 if (storageLayout_ & DataStorage::dslSkippedCharge) {
505 fill(atomRowData.skippedCharge.begin(),
506 atomRowData.skippedCharge.end(), 0.0);
507 fill(atomColData.skippedCharge.begin(),
508 atomColData.skippedCharge.end(), 0.0);
509 }
510
511 #endif
512 // even in parallel, we need to zero out the local arrays:
513
514 if (storageLayout_ & DataStorage::dslParticlePot) {
515 fill(snap_->atomData.particlePot.begin(),
516 snap_->atomData.particlePot.end(), 0.0);
517 }
518
519 if (storageLayout_ & DataStorage::dslDensity) {
520 fill(snap_->atomData.density.begin(),
521 snap_->atomData.density.end(), 0.0);
522 }
523 if (storageLayout_ & DataStorage::dslFunctional) {
524 fill(snap_->atomData.functional.begin(),
525 snap_->atomData.functional.end(), 0.0);
526 }
527 if (storageLayout_ & DataStorage::dslFunctionalDerivative) {
528 fill(snap_->atomData.functionalDerivative.begin(),
529 snap_->atomData.functionalDerivative.end(), 0.0);
530 }
531 if (storageLayout_ & DataStorage::dslSkippedCharge) {
532 fill(snap_->atomData.skippedCharge.begin(),
533 snap_->atomData.skippedCharge.end(), 0.0);
534 }
535
536 }
537
538
539 void ForceMatrixDecomposition::distributeData() {
540 snap_ = sman_->getCurrentSnapshot();
541 storageLayout_ = sman_->getStorageLayout();
542 #ifdef IS_MPI
543
544 // gather up the atomic positions
545 AtomPlanVectorRow->gather(snap_->atomData.position,
546 atomRowData.position);
547 AtomPlanVectorColumn->gather(snap_->atomData.position,
548 atomColData.position);
549
550 // gather up the cutoff group positions
551
552 cgPlanVectorRow->gather(snap_->cgData.position,
553 cgRowData.position);
554
555 cgPlanVectorColumn->gather(snap_->cgData.position,
556 cgColData.position);
557
558
559 // if needed, gather the atomic rotation matrices
560 if (storageLayout_ & DataStorage::dslAmat) {
561 AtomPlanMatrixRow->gather(snap_->atomData.aMat,
562 atomRowData.aMat);
563 AtomPlanMatrixColumn->gather(snap_->atomData.aMat,
564 atomColData.aMat);
565 }
566
567 // if needed, gather the atomic eletrostatic frames
568 if (storageLayout_ & DataStorage::dslElectroFrame) {
569 AtomPlanMatrixRow->gather(snap_->atomData.electroFrame,
570 atomRowData.electroFrame);
571 AtomPlanMatrixColumn->gather(snap_->atomData.electroFrame,
572 atomColData.electroFrame);
573 }
574
575 #endif
576 }
577
578 /* collects information obtained during the pre-pair loop onto local
579 * data structures.
580 */
581 void ForceMatrixDecomposition::collectIntermediateData() {
582 snap_ = sman_->getCurrentSnapshot();
583 storageLayout_ = sman_->getStorageLayout();
584 #ifdef IS_MPI
585
586 if (storageLayout_ & DataStorage::dslDensity) {
587
588 AtomPlanRealRow->scatter(atomRowData.density,
589 snap_->atomData.density);
590
591 int n = snap_->atomData.density.size();
592 vector<RealType> rho_tmp(n, 0.0);
593 AtomPlanRealColumn->scatter(atomColData.density, rho_tmp);
594 for (int i = 0; i < n; i++)
595 snap_->atomData.density[i] += rho_tmp[i];
596 }
597 #endif
598 }
599
600 /*
601 * redistributes information obtained during the pre-pair loop out to
602 * row and column-indexed data structures
603 */
604 void ForceMatrixDecomposition::distributeIntermediateData() {
605 snap_ = sman_->getCurrentSnapshot();
606 storageLayout_ = sman_->getStorageLayout();
607 #ifdef IS_MPI
608 if (storageLayout_ & DataStorage::dslFunctional) {
609 AtomPlanRealRow->gather(snap_->atomData.functional,
610 atomRowData.functional);
611 AtomPlanRealColumn->gather(snap_->atomData.functional,
612 atomColData.functional);
613 }
614
615 if (storageLayout_ & DataStorage::dslFunctionalDerivative) {
616 AtomPlanRealRow->gather(snap_->atomData.functionalDerivative,
617 atomRowData.functionalDerivative);
618 AtomPlanRealColumn->gather(snap_->atomData.functionalDerivative,
619 atomColData.functionalDerivative);
620 }
621 #endif
622 }
623
624
625 void ForceMatrixDecomposition::collectData() {
626 snap_ = sman_->getCurrentSnapshot();
627 storageLayout_ = sman_->getStorageLayout();
628 #ifdef IS_MPI
629 int n = snap_->atomData.force.size();
630 vector<Vector3d> frc_tmp(n, V3Zero);
631
632 AtomPlanVectorRow->scatter(atomRowData.force, frc_tmp);
633 for (int i = 0; i < n; i++) {
634 snap_->atomData.force[i] += frc_tmp[i];
635 frc_tmp[i] = 0.0;
636 }
637
638 AtomPlanVectorColumn->scatter(atomColData.force, frc_tmp);
639 for (int i = 0; i < n; i++) {
640 snap_->atomData.force[i] += frc_tmp[i];
641 }
642
643 if (storageLayout_ & DataStorage::dslTorque) {
644
645 int nt = snap_->atomData.torque.size();
646 vector<Vector3d> trq_tmp(nt, V3Zero);
647
648 AtomPlanVectorRow->scatter(atomRowData.torque, trq_tmp);
649 for (int i = 0; i < nt; i++) {
650 snap_->atomData.torque[i] += trq_tmp[i];
651 trq_tmp[i] = 0.0;
652 }
653
654 AtomPlanVectorColumn->scatter(atomColData.torque, trq_tmp);
655 for (int i = 0; i < nt; i++)
656 snap_->atomData.torque[i] += trq_tmp[i];
657 }
658
659 if (storageLayout_ & DataStorage::dslSkippedCharge) {
660
661 int ns = snap_->atomData.skippedCharge.size();
662 vector<RealType> skch_tmp(ns, 0.0);
663
664 AtomPlanRealRow->scatter(atomRowData.skippedCharge, skch_tmp);
665 for (int i = 0; i < ns; i++) {
666 snap_->atomData.skippedCharge[i] += skch_tmp[i];
667 skch_tmp[i] = 0.0;
668 }
669
670 AtomPlanRealColumn->scatter(atomColData.skippedCharge, skch_tmp);
671 for (int i = 0; i < ns; i++)
672 snap_->atomData.skippedCharge[i] += skch_tmp[i];
673 }
674
675 nLocal_ = snap_->getNumberOfAtoms();
676
677 vector<potVec> pot_temp(nLocal_,
678 Vector<RealType, N_INTERACTION_FAMILIES> (0.0));
679
680 // scatter/gather pot_row into the members of my column
681
682 AtomPlanPotRow->scatter(pot_row, pot_temp);
683
684 for (int ii = 0; ii < pot_temp.size(); ii++ )
685 pairwisePot += pot_temp[ii];
686
687 fill(pot_temp.begin(), pot_temp.end(),
688 Vector<RealType, N_INTERACTION_FAMILIES> (0.0));
689
690 AtomPlanPotColumn->scatter(pot_col, pot_temp);
691
692 for (int ii = 0; ii < pot_temp.size(); ii++ )
693 pairwisePot += pot_temp[ii];
694
695 for (int ii = 0; ii < N_INTERACTION_FAMILIES; ii++) {
696 RealType ploc1 = pairwisePot[ii];
697 RealType ploc2 = 0.0;
698 MPI::COMM_WORLD.Allreduce(&ploc1, &ploc2, 1, MPI::REALTYPE, MPI::SUM);
699 pairwisePot[ii] = ploc2;
700 }
701
702 #endif
703
704 }
705
706 int ForceMatrixDecomposition::getNAtomsInRow() {
707 #ifdef IS_MPI
708 return nAtomsInRow_;
709 #else
710 return nLocal_;
711 #endif
712 }
713
714 /**
715 * returns the list of atoms belonging to this group.
716 */
717 vector<int> ForceMatrixDecomposition::getAtomsInGroupRow(int cg1){
718 #ifdef IS_MPI
719 return groupListRow_[cg1];
720 #else
721 return groupList_[cg1];
722 #endif
723 }
724
725 vector<int> ForceMatrixDecomposition::getAtomsInGroupColumn(int cg2){
726 #ifdef IS_MPI
727 return groupListCol_[cg2];
728 #else
729 return groupList_[cg2];
730 #endif
731 }
732
733 Vector3d ForceMatrixDecomposition::getIntergroupVector(int cg1, int cg2){
734 Vector3d d;
735
736 #ifdef IS_MPI
737 d = cgColData.position[cg2] - cgRowData.position[cg1];
738 #else
739 d = snap_->cgData.position[cg2] - snap_->cgData.position[cg1];
740 #endif
741
742 snap_->wrapVector(d);
743 return d;
744 }
745
746
747 Vector3d ForceMatrixDecomposition::getAtomToGroupVectorRow(int atom1, int cg1){
748
749 Vector3d d;
750
751 #ifdef IS_MPI
752 d = cgRowData.position[cg1] - atomRowData.position[atom1];
753 #else
754 d = snap_->cgData.position[cg1] - snap_->atomData.position[atom1];
755 #endif
756
757 snap_->wrapVector(d);
758 return d;
759 }
760
761 Vector3d ForceMatrixDecomposition::getAtomToGroupVectorColumn(int atom2, int cg2){
762 Vector3d d;
763
764 #ifdef IS_MPI
765 d = cgColData.position[cg2] - atomColData.position[atom2];
766 #else
767 d = snap_->cgData.position[cg2] - snap_->atomData.position[atom2];
768 #endif
769
770 snap_->wrapVector(d);
771 return d;
772 }
773
774 RealType ForceMatrixDecomposition::getMassFactorRow(int atom1) {
775 #ifdef IS_MPI
776 return massFactorsRow[atom1];
777 #else
778 return massFactors[atom1];
779 #endif
780 }
781
782 RealType ForceMatrixDecomposition::getMassFactorColumn(int atom2) {
783 #ifdef IS_MPI
784 return massFactorsCol[atom2];
785 #else
786 return massFactors[atom2];
787 #endif
788
789 }
790
791 Vector3d ForceMatrixDecomposition::getInteratomicVector(int atom1, int atom2){
792 Vector3d d;
793
794 #ifdef IS_MPI
795 d = atomColData.position[atom2] - atomRowData.position[atom1];
796 #else
797 d = snap_->atomData.position[atom2] - snap_->atomData.position[atom1];
798 #endif
799
800 snap_->wrapVector(d);
801 return d;
802 }
803
804 vector<int> ForceMatrixDecomposition::getExcludesForAtom(int atom1) {
805 return excludesForAtom[atom1];
806 }
807
808 /**
809 * We need to exclude some overcounted interactions that result from
810 * the parallel decomposition.
811 */
812 bool ForceMatrixDecomposition::skipAtomPair(int atom1, int atom2) {
813 int unique_id_1, unique_id_2;
814
815 #ifdef IS_MPI
816 // in MPI, we have to look up the unique IDs for each atom
817 unique_id_1 = AtomRowToGlobal[atom1];
818 unique_id_2 = AtomColToGlobal[atom2];
819
820 // this situation should only arise in MPI simulations
821 if (unique_id_1 == unique_id_2) return true;
822
823 // this prevents us from doing the pair on multiple processors
824 if (unique_id_1 < unique_id_2) {
825 if ((unique_id_1 + unique_id_2) % 2 == 0) return true;
826 } else {
827 if ((unique_id_1 + unique_id_2) % 2 == 1) return true;
828 }
829 #endif
830 return false;
831 }
832
833 /**
834 * We need to handle the interactions for atoms who are involved in
835 * the same rigid body as well as some short range interactions
836 * (bonds, bends, torsions) differently from other interactions.
837 * We'll still visit the pairwise routines, but with a flag that
838 * tells those routines to exclude the pair from direct long range
839 * interactions. Some indirect interactions (notably reaction
840 * field) must still be handled for these pairs.
841 */
842 bool ForceMatrixDecomposition::excludeAtomPair(int atom1, int atom2) {
843 int unique_id_2;
844 #ifdef IS_MPI
845 // in MPI, we have to look up the unique IDs for the row atom.
846 unique_id_2 = AtomColToGlobal[atom2];
847 #else
848 // in the normal loop, the atom numbers are unique
849 unique_id_2 = atom2;
850 #endif
851
852 for (vector<int>::iterator i = excludesForAtom[atom1].begin();
853 i != excludesForAtom[atom1].end(); ++i) {
854 if ( (*i) == unique_id_2 ) return true;
855 }
856
857 return false;
858 }
859
860
861 void ForceMatrixDecomposition::addForceToAtomRow(int atom1, Vector3d fg){
862 #ifdef IS_MPI
863 atomRowData.force[atom1] += fg;
864 #else
865 snap_->atomData.force[atom1] += fg;
866 #endif
867 }
868
869 void ForceMatrixDecomposition::addForceToAtomColumn(int atom2, Vector3d fg){
870 #ifdef IS_MPI
871 atomColData.force[atom2] += fg;
872 #else
873 snap_->atomData.force[atom2] += fg;
874 #endif
875 }
876
877 // filling interaction blocks with pointers
878 void ForceMatrixDecomposition::fillInteractionData(InteractionData &idat,
879 int atom1, int atom2) {
880
881 idat.excluded = excludeAtomPair(atom1, atom2);
882
883 #ifdef IS_MPI
884 idat.atypes = make_pair( atypesRow[atom1], atypesCol[atom2]);
885 //idat.atypes = make_pair( ff_->getAtomType(identsRow[atom1]),
886 // ff_->getAtomType(identsCol[atom2]) );
887
888 if (storageLayout_ & DataStorage::dslAmat) {
889 idat.A1 = &(atomRowData.aMat[atom1]);
890 idat.A2 = &(atomColData.aMat[atom2]);
891 }
892
893 if (storageLayout_ & DataStorage::dslElectroFrame) {
894 idat.eFrame1 = &(atomRowData.electroFrame[atom1]);
895 idat.eFrame2 = &(atomColData.electroFrame[atom2]);
896 }
897
898 if (storageLayout_ & DataStorage::dslTorque) {
899 idat.t1 = &(atomRowData.torque[atom1]);
900 idat.t2 = &(atomColData.torque[atom2]);
901 }
902
903 if (storageLayout_ & DataStorage::dslDensity) {
904 idat.rho1 = &(atomRowData.density[atom1]);
905 idat.rho2 = &(atomColData.density[atom2]);
906 }
907
908 if (storageLayout_ & DataStorage::dslFunctional) {
909 idat.frho1 = &(atomRowData.functional[atom1]);
910 idat.frho2 = &(atomColData.functional[atom2]);
911 }
912
913 if (storageLayout_ & DataStorage::dslFunctionalDerivative) {
914 idat.dfrho1 = &(atomRowData.functionalDerivative[atom1]);
915 idat.dfrho2 = &(atomColData.functionalDerivative[atom2]);
916 }
917
918 if (storageLayout_ & DataStorage::dslParticlePot) {
919 idat.particlePot1 = &(atomRowData.particlePot[atom1]);
920 idat.particlePot2 = &(atomColData.particlePot[atom2]);
921 }
922
923 if (storageLayout_ & DataStorage::dslSkippedCharge) {
924 idat.skippedCharge1 = &(atomRowData.skippedCharge[atom1]);
925 idat.skippedCharge2 = &(atomColData.skippedCharge[atom2]);
926 }
927
928 #else
929
930 idat.atypes = make_pair( atypesLocal[atom1], atypesLocal[atom2]);
931 //idat.atypes = make_pair( ff_->getAtomType(idents[atom1]),
932 // ff_->getAtomType(idents[atom2]) );
933
934 if (storageLayout_ & DataStorage::dslAmat) {
935 idat.A1 = &(snap_->atomData.aMat[atom1]);
936 idat.A2 = &(snap_->atomData.aMat[atom2]);
937 }
938
939 if (storageLayout_ & DataStorage::dslElectroFrame) {
940 idat.eFrame1 = &(snap_->atomData.electroFrame[atom1]);
941 idat.eFrame2 = &(snap_->atomData.electroFrame[atom2]);
942 }
943
944 if (storageLayout_ & DataStorage::dslTorque) {
945 idat.t1 = &(snap_->atomData.torque[atom1]);
946 idat.t2 = &(snap_->atomData.torque[atom2]);
947 }
948
949 if (storageLayout_ & DataStorage::dslDensity) {
950 idat.rho1 = &(snap_->atomData.density[atom1]);
951 idat.rho2 = &(snap_->atomData.density[atom2]);
952 }
953
954 if (storageLayout_ & DataStorage::dslFunctional) {
955 idat.frho1 = &(snap_->atomData.functional[atom1]);
956 idat.frho2 = &(snap_->atomData.functional[atom2]);
957 }
958
959 if (storageLayout_ & DataStorage::dslFunctionalDerivative) {
960 idat.dfrho1 = &(snap_->atomData.functionalDerivative[atom1]);
961 idat.dfrho2 = &(snap_->atomData.functionalDerivative[atom2]);
962 }
963
964 if (storageLayout_ & DataStorage::dslParticlePot) {
965 idat.particlePot1 = &(snap_->atomData.particlePot[atom1]);
966 idat.particlePot2 = &(snap_->atomData.particlePot[atom2]);
967 }
968
969 if (storageLayout_ & DataStorage::dslSkippedCharge) {
970 idat.skippedCharge1 = &(snap_->atomData.skippedCharge[atom1]);
971 idat.skippedCharge2 = &(snap_->atomData.skippedCharge[atom2]);
972 }
973 #endif
974 }
975
976
977 void ForceMatrixDecomposition::unpackInteractionData(InteractionData &idat, int atom1, int atom2) {
978 #ifdef IS_MPI
979 pot_row[atom1] += 0.5 * *(idat.pot);
980 pot_col[atom2] += 0.5 * *(idat.pot);
981
982 atomRowData.force[atom1] += *(idat.f1);
983 atomColData.force[atom2] -= *(idat.f1);
984 #else
985 pairwisePot += *(idat.pot);
986
987 snap_->atomData.force[atom1] += *(idat.f1);
988 snap_->atomData.force[atom2] -= *(idat.f1);
989 #endif
990
991 }
992
993 /*
994 * buildNeighborList
995 *
996 * first element of pair is row-indexed CutoffGroup
997 * second element of pair is column-indexed CutoffGroup
998 */
999 vector<pair<int, int> > ForceMatrixDecomposition::buildNeighborList() {
1000
1001 vector<pair<int, int> > neighborList;
1002 groupCutoffs cuts;
1003 bool doAllPairs = false;
1004
1005 #ifdef IS_MPI
1006 cellListRow_.clear();
1007 cellListCol_.clear();
1008 #else
1009 cellList_.clear();
1010 #endif
1011
1012 RealType rList_ = (largestRcut_ + skinThickness_);
1013 RealType rl2 = rList_ * rList_;
1014 Snapshot* snap_ = sman_->getCurrentSnapshot();
1015 Mat3x3d Hmat = snap_->getHmat();
1016 Vector3d Hx = Hmat.getColumn(0);
1017 Vector3d Hy = Hmat.getColumn(1);
1018 Vector3d Hz = Hmat.getColumn(2);
1019
1020 nCells_.x() = (int) ( Hx.length() )/ rList_;
1021 nCells_.y() = (int) ( Hy.length() )/ rList_;
1022 nCells_.z() = (int) ( Hz.length() )/ rList_;
1023
1024 // handle small boxes where the cell offsets can end up repeating cells
1025
1026 if (nCells_.x() < 3) doAllPairs = true;
1027 if (nCells_.y() < 3) doAllPairs = true;
1028 if (nCells_.z() < 3) doAllPairs = true;
1029
1030 Mat3x3d invHmat = snap_->getInvHmat();
1031 Vector3d rs, scaled, dr;
1032 Vector3i whichCell;
1033 int cellIndex;
1034 int nCtot = nCells_.x() * nCells_.y() * nCells_.z();
1035
1036 #ifdef IS_MPI
1037 cellListRow_.resize(nCtot);
1038 cellListCol_.resize(nCtot);
1039 #else
1040 cellList_.resize(nCtot);
1041 #endif
1042
1043 if (!doAllPairs) {
1044 #ifdef IS_MPI
1045
1046 for (int i = 0; i < nGroupsInRow_; i++) {
1047 rs = cgRowData.position[i];
1048
1049 // scaled positions relative to the box vectors
1050 scaled = invHmat * rs;
1051
1052 // wrap the vector back into the unit box by subtracting integer box
1053 // numbers
1054 for (int j = 0; j < 3; j++) {
1055 scaled[j] -= roundMe(scaled[j]);
1056 scaled[j] += 0.5;
1057 }
1058
1059 // find xyz-indices of cell that cutoffGroup is in.
1060 whichCell.x() = nCells_.x() * scaled.x();
1061 whichCell.y() = nCells_.y() * scaled.y();
1062 whichCell.z() = nCells_.z() * scaled.z();
1063
1064 // find single index of this cell:
1065 cellIndex = Vlinear(whichCell, nCells_);
1066
1067 // add this cutoff group to the list of groups in this cell;
1068 cellListRow_[cellIndex].push_back(i);
1069 }
1070 for (int i = 0; i < nGroupsInCol_; i++) {
1071 rs = cgColData.position[i];
1072
1073 // scaled positions relative to the box vectors
1074 scaled = invHmat * rs;
1075
1076 // wrap the vector back into the unit box by subtracting integer box
1077 // numbers
1078 for (int j = 0; j < 3; j++) {
1079 scaled[j] -= roundMe(scaled[j]);
1080 scaled[j] += 0.5;
1081 }
1082
1083 // find xyz-indices of cell that cutoffGroup is in.
1084 whichCell.x() = nCells_.x() * scaled.x();
1085 whichCell.y() = nCells_.y() * scaled.y();
1086 whichCell.z() = nCells_.z() * scaled.z();
1087
1088 // find single index of this cell:
1089 cellIndex = Vlinear(whichCell, nCells_);
1090
1091 // add this cutoff group to the list of groups in this cell;
1092 cellListCol_[cellIndex].push_back(i);
1093 }
1094 #else
1095 for (int i = 0; i < nGroups_; i++) {
1096 rs = snap_->cgData.position[i];
1097
1098 // scaled positions relative to the box vectors
1099 scaled = invHmat * rs;
1100
1101 // wrap the vector back into the unit box by subtracting integer box
1102 // numbers
1103 for (int j = 0; j < 3; j++) {
1104 scaled[j] -= roundMe(scaled[j]);
1105 scaled[j] += 0.5;
1106 }
1107
1108 // find xyz-indices of cell that cutoffGroup is in.
1109 whichCell.x() = nCells_.x() * scaled.x();
1110 whichCell.y() = nCells_.y() * scaled.y();
1111 whichCell.z() = nCells_.z() * scaled.z();
1112
1113 // find single index of this cell:
1114 cellIndex = Vlinear(whichCell, nCells_);
1115
1116 // add this cutoff group to the list of groups in this cell;
1117 cellList_[cellIndex].push_back(i);
1118 }
1119 #endif
1120
1121 for (int m1z = 0; m1z < nCells_.z(); m1z++) {
1122 for (int m1y = 0; m1y < nCells_.y(); m1y++) {
1123 for (int m1x = 0; m1x < nCells_.x(); m1x++) {
1124 Vector3i m1v(m1x, m1y, m1z);
1125 int m1 = Vlinear(m1v, nCells_);
1126
1127 for (vector<Vector3i>::iterator os = cellOffsets_.begin();
1128 os != cellOffsets_.end(); ++os) {
1129
1130 Vector3i m2v = m1v + (*os);
1131
1132 if (m2v.x() >= nCells_.x()) {
1133 m2v.x() = 0;
1134 } else if (m2v.x() < 0) {
1135 m2v.x() = nCells_.x() - 1;
1136 }
1137
1138 if (m2v.y() >= nCells_.y()) {
1139 m2v.y() = 0;
1140 } else if (m2v.y() < 0) {
1141 m2v.y() = nCells_.y() - 1;
1142 }
1143
1144 if (m2v.z() >= nCells_.z()) {
1145 m2v.z() = 0;
1146 } else if (m2v.z() < 0) {
1147 m2v.z() = nCells_.z() - 1;
1148 }
1149
1150 int m2 = Vlinear (m2v, nCells_);
1151
1152 #ifdef IS_MPI
1153 for (vector<int>::iterator j1 = cellListRow_[m1].begin();
1154 j1 != cellListRow_[m1].end(); ++j1) {
1155 for (vector<int>::iterator j2 = cellListCol_[m2].begin();
1156 j2 != cellListCol_[m2].end(); ++j2) {
1157
1158 // In parallel, we need to visit *all* pairs of row &
1159 // column indicies and will truncate later on.
1160 dr = cgColData.position[(*j2)] - cgRowData.position[(*j1)];
1161 snap_->wrapVector(dr);
1162 cuts = getGroupCutoffs( (*j1), (*j2) );
1163 if (dr.lengthSquare() < cuts.third) {
1164 neighborList.push_back(make_pair((*j1), (*j2)));
1165 }
1166 }
1167 }
1168 #else
1169
1170 for (vector<int>::iterator j1 = cellList_[m1].begin();
1171 j1 != cellList_[m1].end(); ++j1) {
1172 for (vector<int>::iterator j2 = cellList_[m2].begin();
1173 j2 != cellList_[m2].end(); ++j2) {
1174
1175 // Always do this if we're in different cells or if
1176 // we're in the same cell and the global index of the
1177 // j2 cutoff group is less than the j1 cutoff group
1178
1179 if (m2 != m1 || (*j2) < (*j1)) {
1180 dr = snap_->cgData.position[(*j2)] - snap_->cgData.position[(*j1)];
1181 snap_->wrapVector(dr);
1182 cuts = getGroupCutoffs( (*j1), (*j2) );
1183 if (dr.lengthSquare() < cuts.third) {
1184 neighborList.push_back(make_pair((*j1), (*j2)));
1185 }
1186 }
1187 }
1188 }
1189 #endif
1190 }
1191 }
1192 }
1193 }
1194 } else {
1195 // branch to do all cutoff group pairs
1196 #ifdef IS_MPI
1197 for (int j1 = 0; j1 < nGroupsInRow_; j1++) {
1198 for (int j2 = 0; j2 < nGroupsInCol_; j2++) {
1199 dr = cgColData.position[j2] - cgRowData.position[j1];
1200 snap_->wrapVector(dr);
1201 cuts = getGroupCutoffs( j1, j2 );
1202 if (dr.lengthSquare() < cuts.third) {
1203 neighborList.push_back(make_pair(j1, j2));
1204 }
1205 }
1206 }
1207 #else
1208 for (int j1 = 0; j1 < nGroups_ - 1; j1++) {
1209 for (int j2 = j1 + 1; j2 < nGroups_; j2++) {
1210 dr = snap_->cgData.position[j2] - snap_->cgData.position[j1];
1211 snap_->wrapVector(dr);
1212 cuts = getGroupCutoffs( j1, j2 );
1213 if (dr.lengthSquare() < cuts.third) {
1214 neighborList.push_back(make_pair(j1, j2));
1215 }
1216 }
1217 }
1218 #endif
1219 }
1220
1221 // save the local cutoff group positions for the check that is
1222 // done on each loop:
1223 saved_CG_positions_.clear();
1224 for (int i = 0; i < nGroups_; i++)
1225 saved_CG_positions_.push_back(snap_->cgData.position[i]);
1226
1227 return neighborList;
1228 }
1229 } //end namespace OpenMD