Simpatico  v1.10
Integrator.cpp
1 /*
2 * Simpatico - Simulation Package for Polymeric and Molecular Liquids
3 *
4 * Copyright 2010 - 2017, The Regents of the University of Minnesota
5 * Distributed under the terms of the GNU General Public License.
6 */
7 
8 #include "Integrator.h"
9 #include <ddMd/simulation/Simulation.h>
10 #include <ddMd/storage/AtomStorage.h>
11 #include <ddMd/storage/AtomIterator.h>
12 #include <ddMd/storage/GroupStorage.tpp>
13 #include <ddMd/communicate/Exchanger.h>
14 #include <ddMd/analyzers/AnalyzerManager.h>
15 #include <ddMd/potentials/pair/PairPotential.h>
16 #ifdef SIMP_BOND
17 #include <ddMd/potentials/bond/BondPotential.h>
18 #endif
19 #ifdef SIMP_ANGLE
20 #include <ddMd/potentials/angle/AnglePotential.h>
21 #endif
22 #ifdef SIMP_DIHEDRAL
23 #include <ddMd/potentials/dihedral/DihedralPotential.h>
24 #endif
25 #ifdef SIMP_EXTERNAL
26 #include <ddMd/potentials/external/ExternalPotential.h>
27 #endif
28 
29 #include <simp/ensembles/BoundaryEnsemble.h>
30 
31 #include <util/mpi/MpiLoader.h>
32 #include <util/format/Dbl.h>
33 #include <util/format/Int.h>
34 #include <util/format/Bool.h>
35 #include <util/global.h>
36 
37 namespace DdMd
38 {
39 
40  class Simulation;
41 
42  using namespace Util;
43  using namespace Simp;
44 
45  /*
46  * Constructor.
47  */
49  : SimulationAccess(simulation),
50  timer_(Integrator::NTime),
51  isSetup_(false),
52  saveFileName_(),
53  saveInterval_(0)
54  {}
55 
56  /*
57  * Destructor.
58  */
60  {}
61 
62  /*
63  * Read saveInterval and saveFileName.
64  */
65  void Integrator::readParameters(std::istream& in)
66  {
67  read<int>(in, "saveInterval", saveInterval_);
68  if (saveInterval_ > 0) {
69  if (Analyzer::baseInterval > 0) {
70  if (saveInterval_ % Analyzer::baseInterval != 0) {
71  UTIL_THROW("saveInterval is not a multiple of baseInterval");
72  }
73  } else {
74  UTIL_THROW("Analyzer::baseInterval is not positive");
75  }
76  read<std::string>(in, "saveFileName", saveFileName_);
77  }
78  }
79 
80  /*
81  * Load saveInterval and saveFileName from restart archive.
82  */
84  {
85  loadParameter<int>(ar, "saveInterval", saveInterval_);
86  if (saveInterval_ > 0) {
87  if (Analyzer::baseInterval > 0) {
88  if (saveInterval_ % Analyzer::baseInterval != 0) {
89  UTIL_THROW("saveInterval is not a multiple of baseInterval");
90  }
91  } else {
92  UTIL_THROW("Analyzer::baseInterval is not positive");
93  }
94  loadParameter<std::string>(ar, "saveFileName", saveFileName_);
95  }
96 
97  MpiLoader<Serializable::IArchive> loader(*this, ar);
98  loader.load(iStep_);
99  loader.load(isSetup_);
100  }
101 
102  /*
103  * Save saveInterval and saveFileName to restart archive.
104  */
106  {
107  ar << saveInterval_;
108  if (saveInterval_ > 0) {
109  ar << saveFileName_;
110  }
111  ar << iStep_;
112  ar << isSetup_;
113  }
114 
115  /*
116  * Exchange atoms, build PairList and compute forces.
117  */
119  {
120  // Precondition
121  if (atomStorage().isCartesian()) {
122  UTIL_THROW("Atom coordinates are Cartesian");
123  }
124 
126  exchanger().exchange();
131  if (simulation().boundaryEnsemble().isRigid()) {
133  } else {
135  }
136 
137  // Postcondition - coordinates are Cartesian
138  if (!atomStorage().isCartesian()) {
139  UTIL_THROW("Atom coordinates are not Cartesian");
140  }
141  }
142 
143  /*
144  * Compute forces for all atoms, with timing.
145  */
147  {
148  // Precondition
149  if (!atomStorage().isCartesian()) {
150  UTIL_THROW("Atom coordinates are not Cartesian");
151  }
152 
153  timer_.stamp(MISC);
155  timer_.stamp(ZERO_FORCE);
157  timer_.stamp(PAIR_FORCE);
158  #ifdef SIMP_BOND
159  if (nBondType()) {
161  timer_.stamp(BOND_FORCE);
162  }
163  #endif
164  #ifdef SIMP_ANGLE
165  if (nAngleType()) {
167  timer_.stamp(ANGLE_FORCE);
168  }
169  #endif
170  #ifdef SIMP_DIHEDRAL
171  if (nDihedralType()) {
173  timer_.stamp(DIHEDRAL_FORCE);
174  }
175  #endif
176  #ifdef SIMP_EXTERNAL
177  if (hasExternal()) {
179  timer_.stamp(EXTERNAL_FORCE);
180  }
181  #endif
182 
183  // Reverse communication (if any)
184  if (reverseUpdateFlag()) {
186  }
187 
188  // Send signal indicating change in atomic forces
189  // simulation().forceSignal().notify();
190  }
191 
192  /*
193  * Compute forces for all local atoms and virial, with timing.
194  */
196  {
197  // Precondition
198  if (!atomStorage().isCartesian()) {
199  UTIL_THROW("Atom coordinates are not Cartesian");
200  }
201 
202  timer_.stamp(MISC);
204  timer_.stamp(ZERO_FORCE);
205  pairPotential().computeForcesAndStress(domain().communicator());
206  timer_.stamp(PAIR_FORCE);
207  #ifdef SIMP_BOND
208  if (nBondType()) {
209  bondPotential().computeForcesAndStress(domain().communicator());
210  timer_.stamp(BOND_FORCE);
211  }
212  #endif
213  #ifdef SIMP_ANGLE
214  if (nAngleType()) {
215  anglePotential().computeForcesAndStress(domain().communicator());
216  timer_.stamp(ANGLE_FORCE);
217  }
218  #endif
219  #ifdef SIMP_DIHEDRAL
220  if (nDihedralType()) {
221  dihedralPotential().computeForcesAndStress(domain().communicator());
222  timer_.stamp(DIHEDRAL_FORCE);
223  }
224  #endif
225  #ifdef SIMP_EXTERNAL
226  if (hasExternal()) {
228  timer_.stamp(EXTERNAL_FORCE);
229  }
230  #endif
231 
232  // Reverse communication (if any)
233  if (reverseUpdateFlag()) {
235  }
236 
237  // Send signal indicating change in atomic forces
238  // simulation().forceSignal().notify();
239  }
240 
241  /*
242  * Determine whether an atom exchange and reneighboring is needed.
243  */
244  bool Integrator::isExchangeNeeded(double skin)
245  {
246 
247  if (!atomStorage().isCartesian()) {
248  UTIL_THROW("Error: Coordinates not Cartesian in isExchangeNeeded");
249  }
250 
251  // Calculate maximum square displacment on this node
252  double maxSqDisp = atomStorage().maxSqDisplacement();
253  int needed = 0;
254  if (sqrt(maxSqDisp) > 0.5*skin) {
255  needed = 1;
256  }
257  timer_.stamp(CHECK);
258 
259  #if UTIL_MPI
260  int neededAll;
261  domain().communicator().Allreduce(&needed, &neededAll,
262  1, MPI::INT, MPI::MAX);
263  timer_.stamp(ALLREDUCE);
264  return bool(neededAll);
265  #else
266  return bool(needed);
267  #endif
268  }
269 
270  #if 0
271  /*
272  * Determine whether an atom exchange and reneighboring is needed.
273  */
274  bool Integrator::isExchangeNeeded(double skin)
275  {
276 
277  if (!atomStorage().isCartesian()) {
278  UTIL_THROW("Error: Coordinates not Cartesian in isExchangeNeeded");
279  }
280 
281  // Calculate maximum square displacment on this node
282  double maxSqDisp = atomStorage().maxSqDisplacement();
283  timer_.stamp(CHECK);
284 
285  // Decide on master node if maximum exceeds threshhold.
286  int needed;
287 
288  #if UTIL_MPI
289  double maxSqDispAll; // global maximum
290  domain().communicator().Reduce(&maxSqDisp, &maxSqDispAll, 1,
291  MPI::DOUBLE, MPI::MAX, 0);
292  if (domain().communicator().Get_rank() == 0) {
293  needed = 0;
294  if (sqrt(maxSqDispAll) > 0.5*skin) {
295  needed = 1;
296  }
297  }
298  domain().communicator().Bcast(&needed, 1, MPI::INT, 0);
299  timer_.stamp(ALLREDUCE);
300  #else
301  if (sqrt(maxSqDisp) > 0.5*skin) {
302  needed = 1;
303  }
304  #endif
305 
306  return bool(needed);
307  }
308  #endif
309 
310  /*
311  * Return time per processor for last run.
312  */
313  double Integrator::time() const
314  { return timer_.time(); }
315 
316  /*
317  * Reduce timing statistics data from all processors.
318  */
319  void Integrator::computeStatistics()
320  {
321  #ifdef UTIL_MPI
322  timer().reduce(domain().communicator());
323  #endif
324  }
325 
326  /*
327  * Output statistics.
328  */
329  void Integrator::outputStatistics(std::ostream& out)
330  {
331  if (!domain().isMaster()) {
332  UTIL_THROW("May be called only on domain master");
333  }
334 
335  double time = timer().time();
336  int nAtomTot = atomStorage().nAtomTotal();
337  int nProc = 1;
338  #ifdef UTIL_MPI
339  nProc = domain().communicator().Get_size();
340  #endif
341 
342  // Output total time for the run
343  out << std::endl;
344  out << "Time Statistics" << std::endl;
345  out << "nStep " << iStep_ << std::endl;
346  out << "run time " << time << " sec" << std::endl;
347  out << "time / nStep " << time/double(iStep_)
348  << " sec" << std::endl;
349 
350 
351  double factor1 = 1.0/double(iStep_);
352  double factor2 = double(nProc)/(double(iStep_)*double(nAtomTot));
353  double totalT = 0.0;
354 
355  out << std::endl;
356  out << "T = Time per processor, M = nstep = # steps" << std::endl
357  << "P = # procs, N = # atoms (total, all processors)"
358  << std::endl << std::endl;
359  out << " "
360  << " T/M [sec] "
361  << " T*P/(N*M) "
362  << " Percent (%)" << std::endl;
363  out << "Total "
364  << Dbl(time*factor1, 12, 6)
365  << " "
366  << Dbl(time*factor2, 12, 6)
367  << " " << Dbl(100.0, 12, 6, true) << std::endl;
368  double integrate1T = timer().time(INTEGRATE1);
369  totalT += integrate1T;
370  out << "Integrate1 "
371  << Dbl(integrate1T*factor1, 12, 6)
372  << " "
373  << Dbl(integrate1T*factor2, 12, 6)
374  << " " << Dbl(100.0*integrate1T/time, 12, 6, true) << std::endl;
375  double checkT = timer().time(CHECK);
376  totalT += checkT;
377  out << "Check "
378  << Dbl(checkT*factor1, 12, 6)
379  << " "
380  << Dbl(checkT*factor2, 12, 6)
381  << " " << Dbl(100.0*checkT/time, 12, 6, true) << std::endl;
382  double allReduceT = timer().time(ALLREDUCE);
383  totalT += allReduceT;
384  out << "AllReduce "
385  << Dbl(allReduceT*factor1, 12, 6)
386  << " "
387  << Dbl(allReduceT*factor2, 12, 6)
388  << " " << Dbl(100.0*allReduceT/time, 12, 6, true) << std::endl;
389  double transformFT = timer().time(TRANSFORM_F);
390  totalT += transformFT;
391  out << "Transform (forward) "
392  << Dbl(transformFT*factor1, 12, 6)
393  << " "
394  << Dbl(transformFT*factor2, 12, 6)
395  << " " << Dbl(100.0*transformFT/time, 12, 6, true) << std::endl;
396  double exchangeT = timer().time(EXCHANGE);
397  totalT += exchangeT;
398  out << "Exchange "
399  << Dbl(exchangeT*factor1, 12, 6)
400  << " "
401  << Dbl(exchangeT*factor2, 12, 6)
402  << " " << Dbl(100.0*exchangeT/time, 12, 6, true) << std::endl;
403  double cellListT = timer().time(CELLLIST);
404  totalT += cellListT;
405  out << "CellList "
406  << Dbl(cellListT*factor1, 12, 6)
407  << " "
408  << Dbl(cellListT*factor2, 12, 6)
409  << " " << Dbl(100.0*cellListT/time, 12, 6, true) << std::endl;
410  double transformRT = timer().time(TRANSFORM_R);
411  totalT += transformRT;
412  out << "Transform (reverse) "
413  << Dbl(transformRT*factor1, 12, 6)
414  << " "
415  << Dbl(transformRT*factor2, 12, 6)
416  << " " << Dbl(100.0*transformRT/time, 12, 6, true) << std::endl;
417  double pairListT = timer().time(PAIRLIST);
418  totalT += pairListT;
419  out << "PairList "
420  << Dbl(pairListT*factor1, 12, 6)
421  << " "
422  << Dbl(pairListT*factor2, 12, 6)
423  << " " << Dbl(100.0*pairListT/time, 12, 6, true) << std::endl;
424  double updateT = timer().time(UPDATE);
425  totalT += updateT;
426  out << "Update "
427  << Dbl(updateT*factor1, 12, 6)
428  << " "
429  << Dbl(updateT*factor2, 12, 6)
430  << " " << Dbl(100.0*updateT/time, 12, 6, true) << std::endl;
431  double zeroForceT = timer().time(ZERO_FORCE);
432  totalT += zeroForceT;
433  out << "Zero Forces "
434  << Dbl(zeroForceT*factor1, 12, 6)
435  << " "
436  << Dbl(zeroForceT*factor2, 12, 6)
437  << " " << Dbl(100.0*zeroForceT/time, 12 , 6, true) << std::endl;
438  double pairForceT = timer().time(PAIR_FORCE);
439  totalT += pairForceT;
440  out << "Pair Forces "
441  << Dbl(pairForceT*factor1, 12, 6)
442  << " "
443  << Dbl(pairForceT*factor2, 12, 6)
444  << " " << Dbl(100.0*pairForceT/time, 12 , 6, true) << std::endl;
445  #ifdef SIMP_BOND
446  if (nBondType()) {
447  double bondForceT = timer().time(BOND_FORCE);
448  totalT += bondForceT;
449  out << "Bond Forces "
450  << Dbl(bondForceT*factor1, 12, 6)
451  << " "
452  << Dbl(bondForceT*factor2, 12, 6)
453  << " " << Dbl(100.0*bondForceT/time, 12 , 6, true) << std::endl;
454  }
455  #endif
456  #ifdef SIMP_ANGLE
457  if (nAngleType()) {
458  double angleForceT = timer().time(ANGLE_FORCE);
459  totalT += angleForceT;
460  out << "Angle Forces "
461  << Dbl(angleForceT*factor1, 12, 6)
462  << " "
463  << Dbl(angleForceT*factor2, 12, 6)
464  << " " << Dbl(100.0*angleForceT/time, 12 , 6, true) << std::endl;
465  }
466  #endif
467  #ifdef SIMP_DIHEDRAL
468  if (nDihedralType()) {
469  double dihedralForceT = timer().time(DIHEDRAL_FORCE);
470  totalT += dihedralForceT;
471  out << "Dihedral Forces "
472  << Dbl(dihedralForceT*factor1, 12, 6)
473  << " "
474  << Dbl(dihedralForceT*factor2, 12, 6)
475  << " " << Dbl(100.0*dihedralForceT/time, 12 , 6, true) << std::endl;
476  }
477  #endif
478  #ifdef SIMP_EXTERNAL
479  if (hasExternal()) {
480  double externalForceT = timer().time(DIHEDRAL_FORCE);
481  totalT += externalForceT;
482  out << "External Forces "
483  << Dbl(externalForceT*factor1, 12, 6)
484  << " "
485  << Dbl(externalForceT*factor2, 12, 6)
486  << " " << Dbl(100.0*externalForceT/time, 12 , 6, true) << std::endl;
487  }
488  #endif
489  double integrate2T = timer().time(INTEGRATE2);
490  totalT += integrate2T;
491  out << "Integrate2 "
492  << Dbl(integrate2T*factor1, 12, 6)
493  << " "
494  << Dbl(integrate2T*factor2, 12, 6)
495  << " " << Dbl(100.0*integrate2T/time, 12, 6, true) << std::endl;
496  double analyzerT = timer().time(ANALYZER);
497  totalT += analyzerT;
498  out << "Analyzers "
499  << Dbl(analyzerT*factor1, 12, 6)
500  << " "
501  << Dbl(analyzerT*factor2, 12, 6)
502  << " " << Dbl(100.0*analyzerT/time, 12, 6, true) << std::endl;
503  #ifdef DDMD_MODIFIERS
504  double modifierT = timer().time(MODIFIER);
505  totalT += modifierT;
506  out << "Modifiers "
507  << Dbl(modifierT*factor1, 12, 6)
508  << " "
509  << Dbl(modifierT*factor2, 12, 6)
510  << " " << Dbl(100.0*modifierT/time, 12, 6, true) << std::endl;
511  #endif
512  out << std::endl;
513 
514  // Output info about timer resolution
515  double tick = MPI::Wtick();
516  out << "Timer resolution "
517  << Dbl(tick, 12, 6)
518  << " "
519  << Dbl(tick*double(nProc)/double(nAtomTot), 12, 6)
520  << " " << Dbl(100.0*tick*double(iStep_)/time, 12, 6, true)
521  << std::endl;
522  out << std::endl;
523 
524  // Output exchange / reneighbor statistics
525  int buildCounter = pairPotential().pairList().buildCounter();
526  out << "buildCounter "
527  << Int(buildCounter, 12)
528  << std::endl;
529  out << "steps per build "
530  << Dbl(double(iStep_)/double(buildCounter), 12, 6)
531  << std::endl;
532  out << std::endl;
533 
534  }
535 
536  /*
537  * Clear timing, dynamical state, statistics, and analyzer accumulators.
538  */
540  {
541  iStep_ = 0;
543  timer().clear();
548  #ifdef SIMP_BOND
550  #endif
551  #ifdef SIMP_ANGLE
553  #endif
554  #ifdef SIMP_DIHEDRAL
556  #endif
558  }
559 
560 }
virtual void outputStatistics(std::ostream &out)
Output timing statistics from a run.
Definition: Integrator.cpp:329
void loadParameters(Serializable::IArchive &ar)
Load saveInterval and saveFileName from restart archive.
Definition: Integrator.cpp:83
void reduce(MPI::Intracomm &communicator)
Upon return, times on every processor replaced by average over procs.
Definition: DdTimer.cpp:48
void makeSnapshot()
Record current positions of all local atoms and lock storage.
PairPotential & pairPotential()
Get the PairPotential.
void computeForcesAndVirial()
Compute forces for all local atoms and virial stress.
int iStep_
Current step number.
Definition: Integrator.h:207
void computeForces()
Compute forces for all local atoms, with timing.
Definition: Integrator.cpp:146
Provides access to members of Simulation object.
void transformGenToCart(const Boundary &boundary)
Transform positions from generalized to Cartesian coordinates.
void clearStatistics()
Clear statistical accumulators (call on all processors).
void computeForces()
Compute forces for all local atoms.
int nAtomTotal() const
Get total number of atoms on all processors.
double time() const
Get average time per processor of previous run.
Definition: Integrator.cpp:313
BoundaryEnsemble & boundaryEnsemble()
Get the BoundaryEnsemble.
void clearStatistics()
Clear statistical accumulators (call on all processors).
~Integrator()
Destructor.
Definition: Integrator.cpp:59
void readParameters(std::istream &in)
Read saveInterval and saveFileName.
Definition: Integrator.cpp:65
virtual void initDynamicalState()
Set any internal dynamical to default initial values.
Definition: Integrator.h:248
int nAngleType()
Get maximum number of angle types.
int nDihedralType()
Get maximum number of dihedral types.
int nBondType()
Get maximum number of bond types.
Boundary & boundary()
Get the Boundary.
void buildPairList()
Build the Verlet Pair list.
virtual void computeForces()=0
Add force contributions to all atomic forces.
AnalyzerManager & analyzerManager()
Return the AnalyzerManager by reference.
void zeroForces()
Set forces for all atoms to zero.
Wrapper for a double precision number, for formatted ostream output.
Definition: Dbl.h:39
bool isExchangeNeeded(double skin)
Determine whether an atom exchange and reneighboring is needed.
Definition: Integrator.cpp:244
File containing preprocessor macros for error handling.
Parallel domain decomposition (DD) MD simulation.
Classes used by all simpatico molecular simulations.
GroupStorage< 2 > & bondStorage()
Get the BondStorage.
Main object for a domain-decomposition MD simulation.
void buildCellList()
Build a cell list.
static long baseInterval
The interval for an Analyzer must be a multiple of baseInterval.
Exchanger & exchanger()
Get the Exchanger by reference.
virtual void computeForcesAndStress(MPI::Intracomm &communicator)
Compute forces and stress for all processors.
Definition: Potential.cpp:103
MPI::Intracomm & communicator() const
Return Cartesian communicator by reference.
Definition: Domain.h:257
ExternalPotential & externalPotential()
Get the ExternalPotential.
void clearStatistics()
Clear statistical accumulators (call on all processors).
Saving / output archive for binary ostream.
GroupStorage< 3 > & angleStorage()
Get the AngleStorage.
AnglePotential & anglePotential()
Get the AnglePotential.
#define UTIL_THROW(msg)
Macro for throwing an Exception, reporting function, file and line number.
Definition: global.h:51
double time(int id) const
Get accumulated time for interval i, average per processor.
Definition: DdTimer.cpp:61
void computeForcesAndVirial()
Compute forces for all local atoms and virial, with timing.
Definition: Integrator.cpp:195
void stamp(int id)
Mark end of interval id.
Definition: DdTimer.cpp:37
void setupAtoms()
Setup state of atoms just before integration.
Definition: Integrator.cpp:118
An Integrator numerically integrates the equations of motion.
Definition: Integrator.h:29
Utility classes for scientific computation.
Definition: accumulators.mod:1
GroupStorage< 4 > & dihedralStorage()
Get the angleStorage.
Domain & domain()
Get the Domain.
Wrapper for an int, for formatted ostream output.
Definition: Int.h:36
Exchanger & exchanger()
Get the Exchanger.
void clearSnapshot()
Clear previous snapshot.
AtomStorage & atomStorage()
Get the AtomStorage.
int buildCounter() const
Return number of times the PairList has been built thus far.
void save(Serializable::OArchive &ar)
Save saveInterval and saveFileName from restart archive.
Definition: Integrator.cpp:105
Simulation & simulation()
Get the parent simulation.
bool reverseUpdateFlag() const
Are forces evaluated by reverse communication (true) or not (false)?
DihedralPotential & dihedralPotential()
Get the DihedralPotential.
BondPotential & bondPotential()
Get the PairPotential.
DdTimer & timer()
Return internal timer by reference.
Definition: Exchanger.h:234
PairList & pairList()
Get the PairList by reference.
Saving archive for binary istream.
Provides methods for MPI-aware loading of data from input archive.
Definition: MpiLoader.h:43
void clear()
Call clear method of each Analyzer.
bool hasExternal()
Does this simulation have an external potential?
Buffer & buffer()
Get the Buffer by reference.
void exchange()
Exchange local atoms and ghosts.
Definition: Exchanger.cpp:106
double maxSqDisplacement()
Return max-squared displacement since the last snapshot.
void reverseUpdate()
Update ghost atom forces.
Definition: Exchanger.cpp:902
Integrator(Simulation &simulation)
Constructor.
Definition: Integrator.cpp:48
void clear()
Clear all time statistics.
Definition: DdTimer.cpp:23
virtual void clear()
Set integrator to initial state and clears all statistics.
Definition: Integrator.cpp:539
void clearStatistics()
Clear any accumulated usage statistics.
Definition: Buffer.cpp:466