Simpatico  v1.10
PairPotentialImpl.h
1 #ifndef DDMD_PAIR_POTENTIAL_IMPL_H
2 #define DDMD_PAIR_POTENTIAL_IMPL_H
3 
4 /*
5 * Simpatico - Simulation Package for Polymeric and Molecular Liquids
6 *
7 * Copyright 2010 - 2017, The Regents of the University of Minnesota
8 * Distributed under the terms of the GNU General Public License.
9 */
10 
11 #include <ddMd/potentials/pair/PairPotential.h>
12 #include <util/space/Tensor.h>
13 #include <util/global.h>
14 
15 #include <algorithm>
16 
17 // Block size used in cache-optimized algorithm
18 #define PAIR_BLOCK_SIZE 16
19 
20 namespace DdMd
21 {
22 
23  using namespace Util;
24 
25  class Simulation;
26 
41  template <class Interaction>
43  {
44 
45  public:
46 
54  PairPotentialImpl(Simulation& simulation);
55 
60 
64  virtual ~PairPotentialImpl();
65 
75  virtual void setNAtomType(int nAtomType);
76 
90  virtual void readParameters(std::istream& in);
91 
103  virtual void loadParameters(Serializable::IArchive &ar);
104 
112  virtual void save(Serializable::OArchive &ar);
113 
115 
116 
125  virtual
126  double pairEnergy(double rsq, int iAtomType, int jAtomType) const;
127 
136  virtual
137  double pairForceOverR(double rsq, int iAtomType, int jAtomType) const;
138 
147  void set(std::string name, int i, int j, double value)
148  { interactionPtr_->set(name, i, j, value); }
149 
157  double get(std::string name, int i, int j) const
158  { return interactionPtr_->get(name, i, j); }
159 
163  virtual double maxPairCutoff() const;
164 
168  virtual std::string interactionClassName() const;
169 
173  const Interaction& interaction() const
174  { return *interactionPtr_; }
175 
179  Interaction& interaction()
180  { return *interactionPtr_; }
181 
183 
185 
194  virtual void computeForces();
195 
201  #ifdef UTIL_MPI
202  virtual void computeEnergy(MPI::Intracomm& communicator);
203  #else
204  virtual void computeEnergy();
205  #endif
206 
212  #ifdef UTIL_MPI
213  virtual void computeStress(MPI::Intracomm& communicator);
214  #else
215  virtual void computeStress();
216  #endif
217 
224  #ifdef UTIL_MPI
225  virtual void computePairEnergies(MPI::Intracomm& communicator);
226  #else
227  virtual void computePairEnergies();
228  #endif
229 
235  #ifdef UTIL_MPI
236  virtual void computeForcesAndStress(MPI::Intracomm& communicator);
237  #else
238  virtual void computeForcesAndStress();
239  #endif
240 
242 
243  private:
244 
245  #ifdef PAIR_BLOCK_SIZE
246  /*
247  * Struct used in inner-loop of cache-optimized algorithm.
248  */
249  struct PairForce {
250  Vector f;
251  double rsq;
252  Atom* ptr0;
253  Atom* ptr1;
254  };
255  #endif
256 
260  Interaction* interactionPtr_;
261 
267  int nAtomType_;
268 
269  #ifdef PAIR_BLOCK_SIZE
270  PairForce pairs_[PAIR_BLOCK_SIZE];
271  PairForce* inPairs_[PAIR_BLOCK_SIZE];
272  #endif
273 
277  bool isInitialized_;
278 
282  double energyList();
283 
287  void computeForcesList();
288 
292  double energyCell();
293 
297  void computeForcesCell();
298 
304  double energyNSq();
305 
309  void computeForcesNSq();
310 
311  };
312 
313 }
314 
315 #include <ddMd/simulation/Simulation.h>
316 #include <ddMd/storage/AtomStorage.h>
317 #include <ddMd/storage/AtomIterator.h>
318 #include <ddMd/storage/GhostIterator.h>
319 #include <ddMd/neighbor/PairIterator.h>
320 #include <ddMd/communicate/Domain.h>
321 
322 #include <util/space/Dimension.h>
323 #include <util/space/Vector.h>
324 #include <util/accumulators/setToZero.h>
325 #include <util/global.h>
326 
327 #include <fstream>
328 
329 namespace DdMd
330 {
331 
332  using namespace Util;
333 
334  /*
335  * Constructor.
336  */
337  template <class Interaction>
339  : PairPotential(simulation),
340  interactionPtr_(0),
341  isInitialized_(false)
342  {
343  interactionPtr_ = new Interaction;
344  setNAtomType(simulation.nAtomType());
345  }
346 
347  /*
348  * Default constructor.
349  */
350  template <class Interaction>
352  : PairPotential(),
353  interactionPtr_(0),
354  isInitialized_(false)
355  { interactionPtr_ = new Interaction; }
356 
357  /*
358  * Destructor.
359  */
360  template <class Interaction>
362  {
363  if (interactionPtr_) {
364  delete interactionPtr_;
365  }
366  }
367 
368  /*
369  * Set the maximum number of atom types.
370  */
371  template <class Interaction>
373  {
374  UTIL_CHECK(!isInitialized_);
375  nAtomType_ = nAtomType;
376  interaction().setNAtomType(nAtomType);
377  }
378 
379  /*
380  * Read parameters from file
381  */
382  template <class Interaction>
384  {
385  // Preconditions
386  UTIL_CHECK(!isInitialized_);
387  UTIL_CHECK(nAtomType_ > 0);
388 
389  // Read Interaction parameter block without indent or brackets
390  bool nextIndent = false;
391  addParamComposite(interaction(), nextIndent);
392  interaction().readParameters(in);
393 
395  isInitialized_ = true;
396  }
397 
398  /*
399  * Load internal state from an archive.
400  */
401  template <class Interaction>
402  void
404  {
405  UTIL_CHECK(!isInitialized_);
406  UTIL_CHECK(nAtomType_ > 0);
407 
408  bool nextIndent = false;
409  addParamComposite(interaction(), nextIndent);
410  interaction().loadParameters(ar);
412  isInitialized_ = true;
413  }
414 
415  /*
416  * Save internal state to an archive.
417  */
418  template <class Interaction>
420  {
421  interaction().save(ar);
423  }
424 
425  /*
426  * Return pair energy for a single pair.
427  */
428  template <class Interaction> double
430  int iAtomType, int jAtomType) const
431  { return interaction().energy(rsq, iAtomType, jAtomType); }
432 
433  /*
434  * Return force / separation for a single pair.
435  */
436  template <class Interaction> double
438  int iAtomType, int jAtomType) const
439  {
440  if (rsq < interaction().cutoffSq(iAtomType, jAtomType)) {
441  return interaction().forceOverR(rsq, iAtomType, jAtomType);
442  } else {
443  return 0.0;
444  }
445  }
446 
447  /*
448  * Return maximum cutoff.
449  */
450  template <class Interaction>
452  { return interaction().maxPairCutoff(); }
453 
454  /*
455  * Return pair interaction class name.
456  */
457  template <class Interaction>
459  { return interaction().className(); }
460 
461  /*
462  * Increment atomic forces, without calculating energy.
463  */
464  template <class Interaction>
466  {
467  if (methodId() == 0) {
468  computeForcesList();
469  } else
470  if (methodId() == 1) {
471  computeForcesCell();
472  } else {
473  computeForcesNSq();
474  }
475  }
476 
477  /*
478  * Compute total pair energy on all processors.
479  */
480  template <class Interaction>
481  #ifdef UTIL_MPI
482  void
483  PairPotentialImpl<Interaction>::computeEnergy(MPI::Intracomm& communicator)
484  #else
486  #endif
487  {
488  // Do nothing (return) if energy is already set.
489  if (isEnergySet()) return;
490 
491  double localEnergy = 0.0;
492  if (methodId() == 0) {
493  localEnergy = energyList();
494  } else
495  if (methodId() == 1) {
496  localEnergy = energyCell();
497  } else {
498  localEnergy = energyNSq();
499  }
500 
501  // Add localEnergy from all nodes, set energy to sum on master.
502  reduceEnergy(localEnergy, communicator);
503  }
504 
505  /*
506  * Increment atomic forces and/or pair energy (private).
507  */
508  template <class Interaction>
510  {
511  Vector f;
512  double rsq;
513  double energy = 0.0;
514  PairIterator iter;
515  Atom* atom0Ptr;
516  Atom* atom1Ptr;
517  int type0, type1;
518  if (reverseUpdateFlag()) {
519  for (pairList_.begin(iter); iter.notEnd(); ++iter) {
520  iter.getPair(atom0Ptr, atom1Ptr);
521  assert(!atom0Ptr->isGhost());
522  type0 = atom0Ptr->typeId();
523  type1 = atom1Ptr->typeId();
524  f.subtract(atom0Ptr->position(), atom1Ptr->position());
525  rsq = f.square();
526  energy += interactionPtr_->energy(rsq, type0, type1);
527  }
528  } else {
529  for (pairList_.begin(iter); iter.notEnd(); ++iter) {
530  iter.getPair(atom0Ptr, atom1Ptr);
531  assert(!atom0Ptr->isGhost());
532  type0 = atom0Ptr->typeId();
533  type1 = atom1Ptr->typeId();
534  f.subtract(atom0Ptr->position(), atom1Ptr->position());
535  rsq = f.square();
536  if (!atom1Ptr->isGhost()) {
537  energy += interactionPtr_->energy(rsq, type0, type1);
538  } else {
539  energy += 0.5*interactionPtr_->energy(rsq, type0, type1);
540  }
541  }
542  }
543  return energy;
544  }
545 
546  /*
547  * Increment atomic forces and/or pair energy (private).
548  */
549  template <class Interaction>
551  {
552  double rsq;
553  PairIterator iter;
554  Atom* atom0Ptr;
555  Atom* atom1Ptr;
556  int type0, type1;
557 
558  if (reverseUpdateFlag()) {
559 
560  Vector f;
561  for (pairList_.begin(iter); iter.notEnd(); ++iter) {
562  iter.getPair(atom0Ptr, atom1Ptr);
563  f.subtract(atom0Ptr->position(), atom1Ptr->position());
564  rsq = f.square();
565  type0 = atom0Ptr->typeId();
566  type1 = atom1Ptr->typeId();
567  if (rsq < interactionPtr_->cutoffSq(type0, type1)) {
568  f *= interactionPtr_->forceOverR(rsq, type0, type1);
569  atom0Ptr->force() += f;
570  atom1Ptr->force() -= f;
571  }
572  }
573 
574  } else {
575 
576  #ifdef PAIR_BLOCK_SIZE
577  PairForce* pairPtr;
578  Vector* forcePtr;
579  int i, j, m, n;
580 
581  pairList_.begin(iter);
582  j = pairList_.nPair(); // j = # of remaining unprocessed pairs
583  while (j) {
584 
585  // Determine n = number of pairs in this block
586  n = std::min(PAIR_BLOCK_SIZE, j);
587 
588  // Compute separation for each pair in block
589  for (i = 0; i < n; ++i) {
590  pairPtr = &pairs_[i];
591  iter.getPair(atom0Ptr, atom1Ptr);
592  forcePtr = &(pairPtr->f);
593  forcePtr->subtract(atom0Ptr->position(), atom1Ptr->position());
594  pairPtr->ptr0 = atom0Ptr;
595  pairPtr->ptr1 = atom1Ptr;
596  pairPtr->rsq = forcePtr->square();
597  ++iter;
598  }
599 
600  // Identify pairs in this block with rsq < cutoff
601  // Determine m = number of pairs with rsq < cutoff
602  m = 0;
603  for (i = 0; i < n; ++i) {
604  pairPtr = &pairs_[i];
605  atom0Ptr = pairPtr->ptr0;
606  atom1Ptr = pairPtr->ptr1;
607  type0 = atom0Ptr->typeId();
608  type1 = atom1Ptr->typeId();
609  inPairs_[m] = pairPtr;
610  if (pairPtr->rsq < interactionPtr_->cutoffSq(type0, type1)) {
611  ++m;
612  }
613  }
614 
615  // Compute and increment forces for pairs with rsq < cutoff
616  for (i = 0; i < m; ++i) {
617  pairPtr = inPairs_[i];
618  atom0Ptr = pairPtr->ptr0;
619  atom1Ptr = pairPtr->ptr1;
620  forcePtr = &(pairPtr->f);
621  type0 = atom0Ptr->typeId();
622  type1 = atom1Ptr->typeId();
623  rsq = pairPtr->rsq;
624  *forcePtr *= interactionPtr_->forceOverR(rsq, type0, type1);
625  atom0Ptr->force() += *forcePtr;
626  if (!atom1Ptr->isGhost()) {
627  atom1Ptr->force() -= *forcePtr;
628  }
629  }
630 
631  // Decrement number of remaining unprocess pairs
632  j = j - n;
633  }
634 
635  #ifdef UTIL_DEBUG
636  if (j != 0) {
637  UTIL_THROW("Error in counting");
638  }
639  if (iter.notEnd()) {
640  UTIL_THROW("Error in iterator");
641  }
642  #endif // ifdef UTIL_DEBUG
643 
644  #else // ifndef PAIR_BLOCK_SIZE
645 
646  Vector f;
647  for (pairList_.begin(iter); iter.notEnd(); ++iter) {
648  iter.getPair(atom0Ptr, atom1Ptr);
649  f.subtract(atom0Ptr->position(), atom1Ptr->position());
650  rsq = f.square();
651  type0 = atom0Ptr->typeId();
652  type1 = atom1Ptr->typeId();
653  if (rsq < interactionPtr_->cutoffSq(type0, type1)) {
654  f *= interactionPtr_->forceOverR(rsq, type0, type1);
655  atom0Ptr->force() += f;
656  if (!atom1Ptr->isGhost()) {
657  atom1Ptr->force() -= f;
658  }
659  }
660  }
661  #endif // ifdef PAIR_BLOCK_SIZE
662 
663  }
664  }
665 
666  /*
667  * Increment atomic forces and/or pair energy (private).
668  */
669  template <class Interaction>
671  {
672  Cell::NeighborArray neighbors;
673  Vector f;
674  double rsq;
675  double energy = 0.0;
676  Atom* atomPtr0;
677  Atom* atomPtr1;
678  const Cell* cellPtr;
679  int type0, type1, na, nn, i, j;
680 
681  // Iterate over linked list of local cells.
682  cellPtr = cellList_.begin();
683  while (cellPtr) {
684  cellPtr->getNeighbors(neighbors, reverseUpdateFlag());
685  na = cellPtr->nAtom();
686  nn = neighbors.size();
687  for (i = 0; i < na; ++i) {
688  atomPtr0 = neighbors[i]->ptr();
689  type0 = atomPtr0->typeId();
690 
691  // Loop over atoms in this cell
692  for (j = 0; j < na; ++j) {
693  atomPtr1 = neighbors[j]->ptr();
694  type1 = atomPtr1->typeId();
695  if (atomPtr1 > atomPtr0) {
696  f.subtract(atomPtr0->position(), atomPtr1->position());
697  rsq = f.square();
698  energy += interactionPtr_->energy(rsq, type0, type1);
699  }
700  }
701 
702  // Loop over atoms in neighboring cells.
703  if (reverseUpdateFlag()) {
704  for (j = na; j < nn; ++j) {
705  atomPtr1 = neighbors[j]->ptr();
706  type1 = atomPtr1->typeId();
707  f.subtract(atomPtr0->position(), atomPtr1->position());
708  rsq = f.square();
709  energy += interactionPtr_->energy(rsq, type0, type1);
710  }
711  } else {
712  for (j = na; j < nn; ++j) {
713  atomPtr1 = neighbors[j]->ptr();
714  type1 = atomPtr1->typeId();
715  f.subtract(atomPtr0->position(), atomPtr1->position());
716  rsq = f.square();
717  if (!atomPtr1->isGhost()) {
718  energy += interactionPtr_->energy(rsq, type0, type1);
719  } else {
720  energy += 0.5*interactionPtr_->energy(rsq, type0, type1);
721  }
722  }
723  }
724 
725  }
726  cellPtr = cellPtr->nextCellPtr();
727  } // while (cellPtr)
728  return energy;
729  }
730 
731  /*
732  * Increment atomic forces using Cell List (private).
733  */
734  template <class Interaction>
736  {
737  // Find all neighbors (cell list)
738  Cell::NeighborArray neighbors;
739  Vector f;
740  double rsq;
741  Atom* atomPtr0;
742  Atom* atomPtr1;
743  const Cell* cellPtr;
744  int type0, type1, na, nn, i, j;
745 
746  // Iterate over local cells.
747  cellPtr = cellList_.begin();
748  while (cellPtr) {
749  cellPtr->getNeighbors(neighbors);
750  na = cellPtr->nAtom();
751  nn = neighbors.size();
752  for (i = 0; i < na; ++i) {
753  atomPtr0 = neighbors[i]->ptr();
754  type0 = atomPtr0->typeId();
755  // Loop over atoms in this cell
756  for (j = 0; j < na; ++j) {
757  atomPtr1 = neighbors[j]->ptr();
758  type1 = atomPtr1->typeId();
759  if (atomPtr1 > atomPtr0) {
760  f.subtract(atomPtr0->position(), atomPtr1->position());
761  rsq = f.square();
762  if (rsq < interactionPtr_->cutoffSq(type0, type1)) {
763  f *= interactionPtr_->forceOverR(rsq, type0, type1);
764  atomPtr0->force() += f;
765  atomPtr1->force() -= f;
766  }
767  }
768  }
769 
770  // Loop over atoms in neighboring cells.
771  if (reverseUpdateFlag()) {
772  for (j = na; j < nn; ++j) {
773  atomPtr1 = neighbors[j]->ptr();
774  type1 = atomPtr1->typeId();
775  f.subtract(atomPtr0->position(), atomPtr1->position());
776  rsq = f.square();
777  if (rsq < interactionPtr_->cutoffSq(type0, type1)) {
778  f *= interactionPtr_->forceOverR(rsq, type0, type1);
779  atomPtr0->force() += f;
780  atomPtr1->force() -= f;
781  }
782  }
783  } else {
784  for (j = na; j < nn; ++j) {
785  atomPtr1 = neighbors[j]->ptr();
786  type1 = atomPtr1->typeId();
787  f.subtract(atomPtr0->position(), atomPtr1->position());
788  rsq = f.square();
789  if (rsq < interactionPtr_->cutoffSq(type0, type1)) {
790  f *= interactionPtr_->forceOverR(rsq, type0, type1);
791  atomPtr0->force() += f;
792  if (!atomPtr1->isGhost()) {
793  atomPtr1->force() -= f;
794  }
795  }
796  }
797  }
798 
799  }
800  cellPtr = cellPtr->nextCellPtr();
801  } // while (cellPtr)
802  }
803 
804  /*
805  * Increment atomic forces and/or pair energy (private).
806  */
807  template <class Interaction>
809  {
810  Vector f;
811  double rsq;
812  double energy = 0.0;
813  AtomIterator atomIter0, atomIter1;
814  GhostIterator ghostIter;
815  int id0, id1, type0, type1;
816 
817  // Iterate over local atom 0
818  storage().begin(atomIter0);
819  for ( ; atomIter0.notEnd(); ++atomIter0) {
820  id0 = atomIter0->id();
821  type0 = atomIter0->typeId();
822 
823  // Iterate over local atom 1
824  storage().begin(atomIter1);
825  for ( ; atomIter1.notEnd(); ++atomIter1) {
826  id1 = atomIter1->id();
827  if (id0 < id1) {
828  if (!atomIter0->mask().isMasked(id1)) {
829  f.subtract(atomIter0->position(), atomIter1->position());
830  rsq = f.square();
831  type1 = atomIter1->typeId();
832  energy += interactionPtr_->energy(rsq, type0, type1);
833  }
834  }
835  }
836 
837  // Iterate over ghost atoms
838  storage().begin(ghostIter);
839  if (reverseUpdateFlag()) {
840  for ( ; ghostIter.notEnd(); ++ghostIter) {
841  id1 = ghostIter->id();
842  if (id0 < id1) {
843  if (!atomIter0->mask().isMasked(id1)) {
844  f.subtract(atomIter0->position(), ghostIter->position());
845  rsq = f.square();
846  type1 = ghostIter->typeId();
847  energy += interactionPtr_->energy(rsq, type0, type1);
848  }
849  }
850  }
851  } else {
852  for ( ; ghostIter.notEnd(); ++ghostIter) {
853  id1 = ghostIter->id();
854  if (!atomIter0->mask().isMasked(id1)) {
855  f.subtract(atomIter0->position(), ghostIter->position());
856  rsq = f.square();
857  type1 = ghostIter->typeId();
858  energy += 0.5*interactionPtr_->energy(rsq, type0, type1);
859  }
860  }
861  }
862 
863  }
864  return energy;
865  }
866 
867  /*
868  * Increment atomic forces and/or pair energy (private).
869  */
870  template <class Interaction>
872  {
873  Vector f;
874  double rsq;
875  AtomIterator atomIter0, atomIter1;
876  GhostIterator ghostIter;
877  int type0, type1, id0, id1;
878 
879  // Iterate over atom 0
880  storage().begin(atomIter0);
881  for ( ; atomIter0.notEnd(); ++atomIter0) {
882  id0 = atomIter0->id();
883  type0 = atomIter0->typeId();
884 
885  // Iterate over local atom 1
886  storage().begin(atomIter1);
887  for ( ; atomIter1.notEnd(); ++atomIter1) {
888  id1 = atomIter1->id();
889  if (id0 < id1) {
890  if (!atomIter0->mask().isMasked(id1)) {
891  // Set f = r0 - r1, separation between atoms
892  f.subtract(atomIter0->position(), atomIter1->position());
893  rsq = f.square();
894  type1 = atomIter1->typeId();
895  // Set vector force = (r0-r1)*(forceOverR)
896  if (rsq < interactionPtr_->cutoffSq(type0, type1)) {
897  f *= interactionPtr_->forceOverR(rsq, type0, type1);
898  atomIter0->force() += f;
899  atomIter1->force() -= f;
900  }
901  }
902  }
903  }
904 
905  // Iterate over ghosts
906  storage().begin(ghostIter);
907  if (reverseUpdateFlag()) {
908 
909  for ( ; ghostIter.notEnd(); ++ghostIter) {
910  id1 = ghostIter->id();
911  if (id0 < id1) {
912  if (!atomIter0->mask().isMasked(id1)) {
913  // Set f = r0 - r1, separation between atoms
914  f.subtract(atomIter0->position(), ghostIter->position());
915  rsq = f.square();
916  type1 = ghostIter->typeId();
917  // force = (r0-r1)*(forceOverR)
918  if (rsq < interactionPtr_->cutoffSq(type0, type1)) {
919  f *= interactionPtr_->forceOverR(rsq, type0, type1);
920  atomIter0->force() += f;
921  ghostIter->force() -= f;
922  // Note: If reverseUpdateFlag, increment ghost force
923  }
924  }
925  }
926  }
927 
928  } else {
929 
930  for ( ; ghostIter.notEnd(); ++ghostIter) {
931  id1 = ghostIter->id();
932  if (!atomIter0->mask().isMasked(id1)) {
933  // Set f = r0 - r1, separation between atoms
934  f.subtract(atomIter0->position(), ghostIter->position());
935  rsq = f.square();
936  type1 = ghostIter->typeId();
937  // force = (r0-r1)*(forceOverR)
938  if (rsq < interactionPtr_->cutoffSq(type0, type1)) {
939  f *= interactionPtr_->forceOverR(rsq, type0, type1);
940  atomIter0->force() += f;
941  // Note: If !reverseUpdateFlag, do not increment ghost force
942  }
943  }
944  }
945 
946  }
947 
948  }
949  }
950 
951  /*
952  * Compute total pair stress (Call on all processors).
953  */
954  template <class Interaction>
955  #ifdef UTIL_MPI
956  void PairPotentialImpl<Interaction>::computeStress(MPI::Intracomm& communicator)
957  #else
959  #endif
960  {
961  // Do nothing if stress is already set.
962  if (isStressSet()) return;
963 
964  Tensor localStress;
965  Vector dr;
966  Vector f;
967  double rsq, forceOverR;
968  PairIterator iter;
969  Atom* atom0Ptr;
970  Atom* atom1Ptr;
971  int type0, type1;
972 
973  localStress.zero();
974  if (reverseUpdateFlag()) {
975 
976  for (pairList_.begin(iter); iter.notEnd(); ++iter) {
977  iter.getPair(atom0Ptr, atom1Ptr);
978  dr.subtract(atom0Ptr->position(), atom1Ptr->position());
979  rsq = dr.square();
980  type0 = atom0Ptr->typeId();
981  type1 = atom1Ptr->typeId();
982  if (rsq < interactionPtr_->cutoffSq(type0, type1)) {
983  f = dr;
984  f *= interactionPtr_->forceOverR(rsq, type0, type1);
985  incrementPairStress(f, dr, localStress);
986  }
987  }
988 
989  } else {
990 
991  for (pairList_.begin(iter); iter.notEnd(); ++iter) {
992  iter.getPair(atom0Ptr, atom1Ptr);
993  dr.subtract(atom0Ptr->position(), atom1Ptr->position());
994  rsq = dr.square();
995  type0 = atom0Ptr->typeId();
996  type1 = atom1Ptr->typeId();
997  if (rsq < interactionPtr_->cutoffSq(type0, type1)) {
998  f = dr;
999  forceOverR = interactionPtr_->forceOverR(rsq, type0, type1);
1000  assert(!atom0Ptr->isGhost());
1001  if (atom1Ptr->isGhost()) {
1002  forceOverR *= 0.5;
1003  }
1004  f *= forceOverR;
1005  incrementPairStress(f, dr, localStress);
1006  }
1007  }
1008 
1009  }
1010 
1011  // Normalize by volume
1012  localStress /= boundary().volume();
1013 
1014  // Add localStress from all nodes, set stress to sum on master.
1015  reduceStress(localStress, communicator);
1016  }
1017 
1018  /*
1019  * Compute total pair stress (Call on all processors).
1020  */
1021  template <class Interaction>
1022  #ifdef UTIL_MPI
1024  #else
1026  #endif
1027  {
1028  // If stress is already set, just calculate forces
1029  if (isStressSet()) {
1030  computeForces();
1031  return;
1032  }
1033 
1034  Tensor localStress;
1035  Vector dr;
1036  Vector f;
1037  double rsq;
1038  PairIterator iter;
1039  Atom* atom0Ptr;
1040  Atom* atom1Ptr;
1041  int type0, type1;
1042 
1043  localStress.zero();
1044  if (reverseUpdateFlag()) {
1045 
1046  for (pairList_.begin(iter); iter.notEnd(); ++iter) {
1047  iter.getPair(atom0Ptr, atom1Ptr);
1048  dr.subtract(atom0Ptr->position(), atom1Ptr->position());
1049  rsq = dr.square();
1050  type0 = atom0Ptr->typeId();
1051  type1 = atom1Ptr->typeId();
1052  if (rsq < interactionPtr_->cutoffSq(type0, type1)) {
1053  f = dr;
1054  f *= interactionPtr_->forceOverR(rsq, type0, type1);
1055  assert(!atom0Ptr->isGhost());
1056  atom0Ptr->force() += f;
1057  atom1Ptr->force() -= f;
1058  incrementPairStress(f, dr, localStress);
1059  }
1060  }
1061 
1062  } else {
1063 
1064  for (pairList_.begin(iter); iter.notEnd(); ++iter) {
1065  iter.getPair(atom0Ptr, atom1Ptr);
1066  dr.subtract(atom0Ptr->position(), atom1Ptr->position());
1067  rsq = dr.square();
1068  type0 = atom0Ptr->typeId();
1069  type1 = atom1Ptr->typeId();
1070  if (rsq < interactionPtr_->cutoffSq(type0, type1)) {
1071  f = dr;
1072  f *= interactionPtr_->forceOverR(rsq, type0, type1);
1073  assert(!atom0Ptr->isGhost());
1074  atom0Ptr->force() += f;
1075  if (!atom1Ptr->isGhost()) {
1076  atom1Ptr->force() -= f;
1077  } else { // if atom 1 is a ghost
1078  f *= 0.5;
1079  }
1080  incrementPairStress(f, dr, localStress);
1081  }
1082  }
1083 
1084  }
1085 
1086  // Normalize by volume
1087  localStress /= boundary().volume();
1088 
1089  // Add localStress from all nodes, set stress to sum on master.
1090  reduceStress(localStress, communicator);
1091  }
1092 
1093  /*
1094  * Compute total pair energies (Call on all processors).
1095  */
1096  template <class Interaction>
1097  #ifdef UTIL_MPI
1098  void PairPotentialImpl<Interaction>::computePairEnergies(MPI::Intracomm& communicator)
1099  #else
1101  #endif
1102  {
1103  Vector f;
1104  double rsq;
1105  PairIterator iter;
1106  Atom* atom0Ptr;
1107  Atom* atom1Ptr;
1108  int type0, type1;
1109 
1110  DMatrix<double> localPairEnergies;
1111  localPairEnergies.allocate(nAtomType_, nAtomType_);
1112  for (int i = 0; i < nAtomType_; ++i) {
1113  for (int j = 0; j < nAtomType_; ++j) {
1114  localPairEnergies(i,j) = 0.0;
1115  }
1116  }
1117 
1118  //if (methodId() == 0) {
1119  if (reverseUpdateFlag()) {
1120  for (pairList_.begin(iter); iter.notEnd(); ++iter) {
1121  iter.getPair(atom0Ptr, atom1Ptr);
1122  assert(!atom0Ptr->isGhost());
1123  type0 = atom0Ptr->typeId();
1124  type1 = atom1Ptr->typeId();
1125  f.subtract(atom0Ptr->position(), atom1Ptr->position());
1126  rsq = f.square();
1127  localPairEnergies(type0, type1) += interactionPtr_->energy(rsq, type0, type1);
1128  }
1129  } else {
1130  for (pairList_.begin(iter); iter.notEnd(); ++iter) {
1131  iter.getPair(atom0Ptr, atom1Ptr);
1132  assert(!atom0Ptr->isGhost());
1133  type0 = atom0Ptr->typeId();
1134  type1 = atom1Ptr->typeId();
1135  f.subtract(atom0Ptr->position(), atom1Ptr->position());
1136  rsq = f.square();
1137  if (!atom1Ptr->isGhost()) {
1138  localPairEnergies(type0, type1) += interactionPtr_->energy(rsq, type0, type1);
1139  } else {
1140  localPairEnergies(type0, type1) += 0.5*interactionPtr_->energy(rsq, type0, type1);
1141  }
1142  }
1143  }
1144 
1145  DMatrix<double> totalPairEnergies;
1146  totalPairEnergies.allocate(nAtomType_, nAtomType_);
1147  for (int i = 0; i < nAtomType_; ++i) {
1148  for (int j = 0; j < nAtomType_; ++j) {
1149  totalPairEnergies(i,j) = 0.0;
1150  }
1151  }
1152 
1153  #ifdef UTIL_MPI
1154  communicator.Reduce(&localPairEnergies(0,0), &totalPairEnergies(0,0), nAtomType_*nAtomType_,
1155  MPI::DOUBLE, MPI::SUM, 0);
1156  if (communicator.Get_rank() == 0) {
1157  setPairEnergies(totalPairEnergies);
1158  } else {
1160  }
1161  #else
1162  setPairEnergies(localPairEnergies);
1163  #endif
1164  }
1165 
1166 }
1167 #endif
const Cell * begin() const
Return pointer to first local cell in linked list.
Boundary & boundary()
Get the PairList by const reference.
int typeId() const
Get atom type index.
A Vector is a Cartesian vector.
Definition: Vector.h:75
void allocate(int capacity1, int capacity2)
Allocate memory for a matrix.
Definition: DMatrix.h:170
virtual void computeEnergy(MPI::Intracomm &communicator)
Compute the total nonBonded pair energy for all processors.
virtual void computePairEnergies(MPI::Intracomm &communicator)
Compute total pair energies for all processors Compute nonbonded forces and sress for all processors...
int nPair() const
Get the number of pairs in the PairList.
bool isStressSet() const
Is the stress set (known)?
Definition: Potential.cpp:94
void reduceStress(Tensor &localStress, MPI::Intracomm &communicator)
Add local stresses from all processors, set total on master.
Definition: Potential.cpp:142
double volume() const
Return unit cell volume.
Vector & position()
Get position Vector by reference.
PairPotentialImpl()
Default constructor (for unit testing).
Tensor & zero()
Set all elements of this tensor to zero.
Definition: Tensor.h:441
void getPair(Atom *&atom1Ptr, Atom *&atom2Ptr) const
Get pointers for current pair of Atoms.
File containing preprocessor macros for error handling.
void begin(PairIterator &iterator) const
Initialize a PairIterator.
A point particle in an MD simulation.
Parallel domain decomposition (DD) MD simulation.
Main object for a domain-decomposition MD simulation.
A Tensor represents a Cartesian tensor.
Definition: Tensor.h:32
virtual void computeStress(MPI::Intracomm &communicator)
Compute the total nonBonded stress for all processors.
virtual void setNAtomType(int nAtomType)
Set the maximum number of atom types.
void setPairEnergies(DMatrix< double > pairEnergies)
Set values for pair energies.
Saving / output archive for binary ostream.
bool notEnd() const
Return true if not at end of PairList.
int methodId() const
Return integer id for algorithm (0=PAIR, 1=CELL, 2=NSQ)
A fixed capacity (static) contiguous array with a variable logical size.
Definition: FSArray.h:37
CellList cellList_
CellList to construct PairList or calculate nonbonded pair forces.
void unsetPairEnergies()
Mark pair energy as unknown (nullify).
#define UTIL_THROW(msg)
Macro for throwing an Exception, reporting function, file and line number.
Definition: global.h:51
bool isGhost() const
Is this atom a ghost?
Interaction & interaction()
Return underlying pair interaction object by reference.
int nAtom() const
Number of atoms in cell.
virtual void loadParameters(Serializable::IArchive &ar)
Load internal state from an archive.
virtual double pairEnergy(double rsq, int iAtomType, int jAtomType) const
Return energy for a single pair.
bool notEnd() const
Is the current pointer not at the end of the PArray?
Utility classes for scientific computation.
Definition: accumulators.mod:1
const Interaction & interaction() const
Return underlying pair interaction object by const reference.
virtual void save(Serializable::OArchive &ar)
Save internal state to an output archive.
virtual double maxPairCutoff() const
Return maximum cutoff.
virtual void computeForcesAndStress(MPI::Intracomm &communicator)
Compute nonbonded forces and sress for all processors.
void incrementPairStress(const Vector &f, const Vector &dr, Tensor &stress) const
Add a pair contribution to the stress tensor.
Definition: Potential.h:235
bool reverseUpdateFlag() const
Get flag to identify if reverse communication is enabled.
Definition: Potential.h:228
PairList pairList_
Verlet pair list, to calculate nonbonded pair forces.
virtual double pairForceOverR(double rsq, int iAtomType, int jAtomType) const
Return force / separation for a single pair.
double energy() const
Return the total potential, from all processors.
Definition: Potential.cpp:39
Saving archive for binary istream.
Vector & force()
Get force Vector by reference.
void getNeighbors(NeighborArray &neighbors, bool reverseUpdateFlag=false) const
Fill an array with pointers to atoms in a cell and neighboring cells.
AtomStorage & storage()
Get the AtomStorage by reference.
Iterator for all ghost atoms owned by an AtomStorage.
Definition: GhostIterator.h:24
void addParamComposite(ParamComposite &child, bool next=true)
Add a child ParamComposite object to the format array.
virtual void save(Serializable::OArchive &ar)
Save internal state to an archive.
void reduceEnergy(double localEnergy, MPI::Intracomm &communicator)
Add local energies from all processors, set energy on master.
Definition: Potential.cpp:120
Vector & subtract(const Vector &v1, const Vector &v2)
Subtract vector v2 from v1.
Definition: Vector.h:672
#define UTIL_CHECK(condition)
Assertion macro suitable for serial or parallel production code.
Definition: global.h:68
virtual void loadParameters(Serializable::IArchive &ar)
Load parameters for PairList from archive, and allocate memory.
int nAtomType()
Get maximum number of atom types.
void readParameters(std::istream &in)
Initialize, by reading parameters and allocating memory for PairList.
Implementation template for a PairPotential.
virtual void computeForces()
Compute non-bonded pair forces for all atoms in this Simulation.
Iterator for pairs in a PairList.
Iterator for all atoms owned by an AtomStorage.
Definition: AtomIterator.h:24
int size() const
Return logical size of this array (i.e., number of elements).
Definition: FSArray.h:207
virtual std::string interactionClassName() const
Return pair interaction class name (e.g., "LJPair").
double square() const
Return square magnitude of this vector.
Definition: Vector.h:616
void begin(AtomIterator &iterator)
Set iterator to beginning of the set of atoms.
Abstract base class for computing nonbonded pair forces and energies.
const Cell * nextCellPtr() const
Return a pointer to neighbor cell i.
virtual void readParameters(std::istream &in)
Read pair potential interaction and pair list blocks.
bool isEnergySet() const
Is the energy set (known)?
Definition: Potential.cpp:57
virtual ~PairPotentialImpl()
Destructor.
A single Cell in a CellList.