LCOV - code coverage report
Current view: top level - colvar - CoordinationBase.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 105 107 98.1 %
Date: 2026-11-05 10:19:04 Functions: 7 10 70.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2013-2023 The plumed team
       3             :    (see the PEOPLE file at the root of the distribution for a list of names)
       4             : 
       5             :    See http://www.plumed.org for more information.
       6             : 
       7             :    This file is part of plumed, version 2.
       8             : 
       9             :    plumed is free software: you can redistribute it and/or modify
      10             :    it under the terms of the GNU Lesser General Public License as published by
      11             :    the Free Software Foundation, either version 3 of the License, or
      12             :    (at your option) any later version.
      13             : 
      14             :    plumed is distributed in the hope that it will be useful,
      15             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      16             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      17             :    GNU Lesser General Public License for more details.
      18             : 
      19             :    You should have received a copy of the GNU Lesser General Public License
      20             :    along with plumed.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : #include "CoordinationBase.h"
      23             : #include <array>
      24             : #include <algorithm>
      25             : #include "tools/NeighborList.h"
      26             : #include "tools/Communicator.h"
      27             : #include "tools/OpenMP.h"
      28             : 
      29             : namespace PLMD {
      30             : namespace colvar {
      31             : 
      32         266 : void CoordinationBase::registerKeywords( Keywords& keys ) {
      33         266 :   Colvar::registerKeywords(keys);
      34         266 :   keys.addFlag("SERIAL",false,"Perform the calculation in serial - for debug purpose");
      35         266 :   keys.addFlag("PAIR",false,"Pair only 1st element of the 1st group with 1st element in the second, etc");
      36         266 :   keys.addFlag("NLIST",false,"Use a neighbor list to speed up the calculation");
      37         266 :   keys.addFlag("NLISTCELLS",false,"Use a neighbor list to speed up the calculation - use the cell list implementation instead of the classical one");
      38         266 :   keys.add("optional","NL_CUTOFF","The cutoff for the neighbor list");
      39         266 :   keys.add("optional","NL_STRIDE","The frequency with which we are updating the atoms in the neighbor list");
      40         266 :   keys.add("atoms","GROUPA","First list of atoms");
      41         266 :   keys.add("atoms","GROUPB","Second list of atoms (if empty, N*(N-1)/2 pairs in GROUPA are counted)");
      42         266 : }
      43             : 
      44         254 : CoordinationBase::CoordinationBase(const ActionOptions&ao):
      45             :   PLUMED_COLVAR_INIT(ao),
      46         254 :   pbc(true),
      47         254 :   serial(false),
      48         254 :   invalidateList(true),
      49         254 :   firsttime(true) {
      50             : 
      51         254 :   parseFlag("SERIAL",serial);
      52             : 
      53         254 :   std::vector<AtomNumber> ga_lista,gb_lista;
      54         254 :   parseAtomList("GROUPA",ga_lista);
      55         254 :   parseAtomList("GROUPB",gb_lista);
      56             : 
      57         254 :   bool nopbc=!pbc;
      58         254 :   parseFlag("NOPBC",nopbc);
      59         254 :   pbc=!nopbc;
      60             : 
      61             : // pair stuff
      62         254 :   bool dopair=false;
      63         254 :   parseFlag("PAIR",dopair);
      64             : 
      65             : // neighbor list stuff
      66         254 :   bool doneigh_classic=false;
      67         254 :   double nl_cut=0.0;
      68         254 :   int nl_st=0;
      69         254 :   parseFlag("NLIST",doneigh_classic);
      70         254 :   bool doneighcells=false;
      71         254 :   parseFlag("NLISTCELLS",doneighcells);
      72             :   //temporary message
      73         254 :   plumed_assert(!(doneighcells && doneigh_classic)) << "Please activate only one of the two version of the NL";
      74         254 :   plumed_assert(!(doneighcells && dopair)) << "Pair is not compatible with the CELLS implementation of the NL";
      75         254 :   bool doneigh=doneighcells||doneigh_classic;
      76             :   if(doneigh) {
      77          45 :     parse("NL_CUTOFF",nl_cut);
      78          45 :     if(nl_cut<=0.0) {
      79           0 :       error("NL_CUTOFF should be explicitly specified and positive");
      80             :     }
      81          45 :     parse("NL_STRIDE",nl_st);
      82          45 :     if(nl_st<=0) {
      83           0 :       error("NL_STRIDE should be explicitly specified and positive");
      84             :     }
      85             :   }
      86             : 
      87         254 :   addValueWithDerivatives();
      88         254 :   setNotPeriodic();
      89         254 :   if(gb_lista.size()>0) {
      90         239 :     if(doneigh) {
      91          82 :       nl=Tools::make_unique<NeighborList>(ga_lista,gb_lista,serial,dopair,pbc,getPbc(),comm,nl_cut,nl_st,doneighcells);
      92             :     } else {
      93         396 :       nl=Tools::make_unique<NeighborList>(ga_lista,gb_lista,serial,dopair,pbc,getPbc(),comm);
      94             :     }
      95             :   } else {
      96          15 :     if(doneigh) {
      97           8 :       nl=Tools::make_unique<NeighborList>(ga_lista,serial,pbc,getPbc(),comm,nl_cut,nl_st,doneighcells);
      98             :     } else {
      99          22 :       nl=Tools::make_unique<NeighborList>(ga_lista,serial,pbc,getPbc(),comm);
     100             :     }
     101             :   }
     102             : 
     103         254 :   requestAtoms(nl->getFullAtomList());
     104             : 
     105         254 :   log.printf("  between two groups of %u and %u atoms\n",static_cast<unsigned>(ga_lista.size()),static_cast<unsigned>(gb_lista.size()));
     106         254 :   log.printf("  first group:\n");
     107        7317 :   for(unsigned int i=0; i<ga_lista.size(); ++i) {
     108        7063 :     if ( (i+1) % 25 == 0 ) {
     109         221 :       log.printf("  \n");
     110             :     }
     111        7063 :     log.printf("  %d", ga_lista[i].serial());
     112             :   }
     113         254 :   log.printf("  \n  second group:\n");
     114       12061 :   for(unsigned int i=0; i<gb_lista.size(); ++i) {
     115       11807 :     if ( (i+1) % 25 == 0 ) {
     116         387 :       log.printf("  \n");
     117             :     }
     118       11807 :     log.printf("  %d", gb_lista[i].serial());
     119             :   }
     120         254 :   log.printf("  \n");
     121         254 :   if(pbc) {
     122         250 :     log.printf("  using periodic boundary conditions\n");
     123             :   } else {
     124           4 :     log.printf("  without periodic boundary conditions\n");
     125             :   }
     126         254 :   if(dopair) {
     127           6 :     log.printf("  with PAIR option\n");
     128             :   }
     129         254 :   if(doneigh) {
     130          45 :     log.printf("  using neighbor lists with\n");
     131          45 :     log.printf("  update every %d steps and cutoff %f\n",nl_st,nl_cut);
     132             :   }
     133         254 : }
     134             : 
     135         254 : CoordinationBase::~CoordinationBase() {
     136             : // destructor required to delete forward declared class
     137         254 : }
     138             : 
     139        4616 : void CoordinationBase::prepare() {
     140        4616 :   std::tie(firsttime,invalidateList) =nl->prepare(this,firsttime, invalidateList).get();
     141        4616 : }
     142             : 
     143             : // calculator
     144        4391 : void CoordinationBase::calculate() {
     145        4391 :   if(pbc) {
     146        4371 :     calculateImpl<true>();
     147             :   } else {
     148          20 :     calculateImpl<false>();
     149             :   }
     150        4391 : }
     151             : 
     152             : template<bool useBatch>
     153        4391 : void CoordinationBase::calculateImpl() {
     154             : 
     155        4391 :   double ncoord=0.;
     156        4391 :   Tensor virial;
     157        4391 :   std::vector<Vector> deriv(getNumberOfAtoms());
     158             : 
     159        4391 :   if(nl->getStride()>0 && invalidateList) {
     160         251 :     nl->update(getPositions());
     161             :   }
     162             : 
     163             :   unsigned stride;
     164             :   unsigned rank;
     165        4391 :   if(serial) {
     166             :     stride=1;
     167             :     rank=0;
     168             :   } else {
     169        4391 :     stride=comm.Get_size();
     170        4391 :     rank=comm.Get_rank();
     171             :   }
     172             : 
     173        4391 :   unsigned nt=OpenMP::getNumThreads();
     174        4391 :   const unsigned nn=nl->size();
     175        4391 :   if(nt*stride*10>nn) {
     176             :     nt=1;
     177             :   }
     178             : 
     179        4391 :   const unsigned elementsPerRank = std::ceil(double(nn)/stride);
     180        4391 :   const unsigned int start= rank*elementsPerRank;
     181        4391 :   const unsigned int end = ((start + elementsPerRank)< nn)?(start + elementsPerRank): nn;
     182             : 
     183        4391 :   #pragma omp parallel num_threads(nt)
     184             :   {
     185             :     // the per-thread buffers are only needed when several threads accumulate (otherwise deriv and virial are used directly)
     186             :     std::vector<Vector> omp_deriv(nt>1 ? getPositions().size() : 0);
     187             :     Tensor omp_virial;
     188             : 
     189             :     if constexpr (useBatch) {
     190             :       // Keep scratch storage bounded and leave enough chunks for all threads.
     191             :       constexpr unsigned batchSize = 1024;
     192             :       const unsigned work=end>start ? end-start : 0;
     193             :       const unsigned batchStep=nt>1 ? std::min(batchSize,std::max(1u,work/(4*nt))) : batchSize;
     194             :       std::array<Vector, batchSize> distances;
     195             :       std::array<unsigned, batchSize> firstAtoms, secondAtoms;
     196             :       #pragma omp for reduction(+:ncoord) nowait
     197             :       for(unsigned begin=start; begin<end; begin+=batchStep) {
     198             :         const unsigned limit=std::min(batchStep,end-begin);
     199             :         unsigned count=0;
     200             :         for(unsigned j=0; j<limit; ++j) {
     201             :           const auto [i0,i1]=nl->getUpdatedPair(begin+j);
     202             :           if(getAbsoluteIndex(i0)==getAbsoluteIndex(i1)) {
     203             :             continue;
     204             :           }
     205             :           firstAtoms[count]=i0;
     206             :           secondAtoms[count]=i1;
     207             :           distances[count]=delta(getPosition(i0),getPosition(i1));
     208             :           ++count;
     209             :         }
     210             :         if(count) {
     211             :           getPbc().apply(VectorView(distances[0].data(),count));
     212             :         }
     213             :         for(unsigned j=0; j<count; ++j) {
     214             :           const auto i0=firstAtoms[j],i1=secondAtoms[j];
     215             :           const auto& distance=distances[j];
     216             :           double dfunc=0.;
     217             :           ncoord += pairing(distance.modulo2(),dfunc,i0,i1);
     218             :           Vector dd(dfunc*distance);
     219             :           Tensor vv(dd,distance);
     220             :           if(nt>1) {
     221             :             omp_deriv[i0]-=dd;
     222             :             omp_deriv[i1]+=dd;
     223             :             omp_virial-=vv;
     224             :           } else {
     225             :             deriv[i0]-=dd;
     226             :             deriv[i1]+=dd;
     227             :             virial-=vv;
     228             :           }
     229             :         }
     230             :       }
     231             :     } else {
     232             :       #pragma omp for reduction(+:ncoord) nowait
     233             :       for(unsigned int i=start; i<end; ++i) {
     234             : 
     235             :         Vector distance;
     236             :         const auto [i0, i1] = nl->getUpdatedPair(i);
     237             : 
     238             :         if(getAbsoluteIndex(i0)==getAbsoluteIndex(i1)) {
     239             :           continue;
     240             :         }
     241             : 
     242             :         distance=delta(getPosition(i0),getPosition(i1));
     243             : 
     244             :         double dfunc=0.;
     245             :         ncoord += pairing(distance.modulo2(), dfunc,i0,i1);
     246             : 
     247             :         Vector dd(dfunc*distance);
     248             :         Tensor vv(dd,distance);
     249             :         if(nt>1) {
     250             :           omp_deriv[i0]-=dd;
     251             :           omp_deriv[i1]+=dd;
     252             :           omp_virial-=vv;
     253             :         } else {
     254             :           deriv[i0]-=dd;
     255             :           deriv[i1]+=dd;
     256             :           virial-=vv;
     257             :         }
     258             : 
     259             :       }
     260             :     }
     261             :     #pragma omp critical
     262             :     if(nt>1) {
     263             :       for(unsigned i=0; i<getPositions().size(); i++) {
     264             :         deriv[i]+=omp_deriv[i];
     265             :       }
     266             :       virial+=omp_virial;
     267             :     }
     268             :   }
     269             : 
     270        4391 :   if(!serial) {
     271        4391 :     comm.Sum(ncoord);
     272        4391 :     if(!deriv.empty()) {
     273        4391 :       comm.Sum(&deriv[0][0],3*deriv.size());
     274             :     }
     275        4391 :     comm.Sum(virial);
     276             :   }
     277             : 
     278      414197 :   for(unsigned i=0; i<deriv.size(); ++i) {
     279      409806 :     setAtomsDerivatives(i,deriv[i]);
     280             :   }
     281        4391 :   setValue           (ncoord);
     282        4391 :   setBoxDerivatives  (virial);
     283             : 
     284        4391 : }
     285             : }
     286             : }

Generated by: LCOV version 1.16