LCOV - code coverage report
Current view: top level - colvar - MultiColvarTemplate.h (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 148 151 98.0 %
Date: 2025-12-04 11:19:34 Functions: 82 88 93.2 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2011-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             : #ifndef __PLUMED_colvar_MultiColvarTemplate_h
      23             : #define __PLUMED_colvar_MultiColvarTemplate_h
      24             : 
      25             : #include "core/ActionWithVector.h"
      26             : #include "core/ParallelTaskManager.h"
      27             : #include "tools/ColvarOutput.h"
      28             : #include "ColvarInput.h"
      29             : 
      30             : namespace PLMD {
      31             : 
      32             : class Colvar;
      33             : 
      34             : namespace colvar {
      35             : 
      36             : struct MultiColvarInput {
      37             :   bool usepbc;
      38             :   unsigned mode;
      39             :   unsigned nindices_per_task;
      40             :   //this is so simple that it better to use the Rule of 0
      41             :   //and the openacc helpers
      42             :   void toACCDevice()const {
      43             : #pragma acc enter data copyin(this[0:1], usepbc, mode, nindices_per_task)
      44             :   }
      45             :   void removeFromACCDevice() const  {
      46             : #pragma acc exit data delete(nindices_per_task, mode, usepbc, this[0:1])
      47             :   }
      48             : };
      49             : 
      50             : template <class T>
      51             : class MultiColvarTemplate : public ActionWithVector {
      52             : public:
      53             :   using input_type = MultiColvarInput;
      54             :   using PTM = ParallelTaskManager<MultiColvarTemplate<T>>;
      55             :   constexpr static size_t virialSize = 9;
      56             : private:
      57             : /// The parallel task manager
      58             :   PTM taskmanager;
      59             : /// An index that decides what we are calculating
      60             :   unsigned mode;
      61             : /// Are we using pbc to calculate the CVs
      62             :   bool usepbc;
      63             : /// Do we reassemble the molecule
      64             :   bool wholemolecules;
      65             : /// The number of atoms per task
      66             :   unsigned natoms_per_task;
      67             : public:
      68             :   static void registerKeywords(Keywords&);
      69             :   explicit MultiColvarTemplate(const ActionOptions&);
      70             :   unsigned getNumberOfDerivatives() override ;
      71             :   void addValueWithDerivatives( const std::vector<std::size_t>& shape=std::vector<std::size_t>() ) override ;
      72             :   void addComponentWithDerivatives( const std::string& name, const std::vector<std::size_t>& shape=std::vector<std::size_t>() ) override ;
      73             :   void getInputData( std::vector<double>& inputdata ) const override ;
      74             :   void calculate() override;
      75             :   void applyNonZeroRankForces( std::vector<double>& outforces ) override ;
      76             :   static void performTask( std::size_t task_index,
      77             :                            const MultiColvarInput& actiondata,
      78             :                            ParallelActionsInput& input,
      79             :                            ParallelActionsOutput& output );
      80             :   static int getNumberOfValuesPerTask( std::size_t task_index, const MultiColvarInput& actiondata );
      81             :   static void getForceIndices( std::size_t task_index,
      82             :                                std::size_t colno,
      83             :                                std::size_t ntotal_force,
      84             :                                const MultiColvarInput& actiondata,
      85             :                                const ParallelActionsInput& input,
      86             :                                ForceIndexHolder force_indices );
      87             : };
      88             : 
      89             : template <class T>
      90         472 : void MultiColvarTemplate<T>::registerKeywords(Keywords& keys ) {
      91         472 :   T::registerKeywords( keys );
      92         472 :   PTM::registerKeywords( keys );
      93         944 :   keys.addInputKeyword("optional","MASK","vector","the label for a sparse vector that should be used to determine which elements of the vector should be computed");
      94         472 :   unsigned nkeys = keys.size();
      95        5774 :   for(unsigned i=0; i<nkeys; ++i) {
      96       10604 :     if( keys.style( keys.getKeyword(i), "atoms" ) ) {
      97        1296 :       keys.reset_style( keys.getKeyword(i), "numbered" );
      98             :     }
      99             :   }
     100         944 :   if( keys.outputComponentExists(".#!value") ) {
     101         708 :     keys.setValueDescription("vector","the " + keys.getDisplayName() + " for each set of specified atoms");
     102             :   }
     103         472 : }
     104             : 
     105             : template <class T>
     106         226 : MultiColvarTemplate<T>::MultiColvarTemplate(const ActionOptions&ao):
     107             :   Action(ao),
     108             :   ActionWithVector(ao),
     109         226 :   taskmanager(this),
     110         226 :   mode(0),
     111         226 :   usepbc(true),
     112         226 :   wholemolecules(false) {
     113             :   std::vector<AtomNumber> all_atoms;
     114         594 :   if( getName()=="POSITION_VECTOR" || getName()=="MASS_VECTOR" || getName()=="CHARGE_VECTOR" ) {
     115          84 :     parseAtomList( "ATOMS", all_atoms );
     116             :   }
     117         226 :   if( all_atoms.size()>0 ) {
     118          40 :     natoms_per_task=1;
     119             :   } else {
     120             :     std::vector<AtomNumber> t;
     121      192624 :     for(int i=1;; ++i ) {
     122      192624 :       T::parseAtomList( i, t, this );
     123      192624 :       if( t.empty() ) {
     124             :         break;
     125             :       }
     126             : 
     127      192438 :       if( i==1 ) {
     128         186 :         natoms_per_task=t.size();
     129             :       }
     130      192438 :       if( t.size()!=natoms_per_task ) {
     131             :         std::string ss;
     132           0 :         Tools::convert(i,ss);
     133           0 :         error("ATOMS" + ss + " keyword has the wrong number of atoms");
     134             :       }
     135      579625 :       for(unsigned j=0; j<natoms_per_task; ++j) {
     136      387187 :         all_atoms.push_back( t[j] );
     137             :       }
     138      192438 :       t.resize(0);
     139             :     }
     140             :   }
     141         226 :   if( all_atoms.size()==0 ) {
     142           0 :     error("No atoms have been specified");
     143             :   }
     144         226 :   requestAtoms(all_atoms);
     145         226 :   if( keywords.exists("NOPBC") ) {
     146         226 :     bool nopbc=!usepbc;
     147         226 :     parseFlag("NOPBC",nopbc);
     148         226 :     usepbc=!nopbc;
     149             :   }
     150         226 :   if( keywords.exists("WHOLEMOLECULES") ) {
     151          42 :     parseFlag("WHOLEMOLECULES",wholemolecules);
     152          42 :     if( wholemolecules ) {
     153           1 :       usepbc=false;
     154             :     }
     155             :   }
     156         226 :   if( usepbc ) {
     157         191 :     log.printf("  using periodic boundary conditions\n");
     158             :   } else {
     159          35 :     log.printf("  without periodic boundary conditions\n");
     160             :   }
     161             : 
     162             :   // Setup the values
     163         226 :   mode = T::getModeAndSetupValues( this );
     164             :   // This sets up an array in the parallel task manager to hold all the indices
     165             :   // Sets up the index list in the task manager
     166         226 :   taskmanager.setupParallelTaskManager( 3*natoms_per_task + virialSize, virialSize );
     167         226 :   taskmanager.setActionInput( MultiColvarInput{ usepbc, mode, natoms_per_task });
     168         226 : }
     169             : 
     170             : template <class T>
     171        1946 : unsigned MultiColvarTemplate<T>::getNumberOfDerivatives() {
     172        1946 :   return 3*getNumberOfAtoms()+9;
     173             : }
     174             : 
     175             : template <class T>
     176       25806 : void MultiColvarTemplate<T>::calculate() {
     177       25806 :   if( wholemolecules ) {
     178           1 :     makeWhole();
     179             :   }
     180       25806 :   taskmanager.runAllTasks();
     181       25806 : }
     182             : 
     183             : template <class T>
     184       21985 : void MultiColvarTemplate<T>::applyNonZeroRankForces( std::vector<double>& outforces ) {
     185       21985 :   taskmanager.applyForces( outforces );
     186       21985 : }
     187             : 
     188             : template <class T>
     189         119 : void MultiColvarTemplate<T>::addValueWithDerivatives( const std::vector<std::size_t>& shape ) {
     190         119 :   std::vector<std::size_t> s(1);
     191         119 :   s[0]=getNumberOfAtoms() / natoms_per_task;
     192         119 :   addValue( s );
     193         119 : }
     194             : 
     195             : template <class T>
     196         334 : void MultiColvarTemplate<T>::addComponentWithDerivatives( const std::string& compName, const std::vector<std::size_t>& shape ) {
     197         334 :   std::vector<std::size_t> s(1);
     198         334 :   s[0]=getNumberOfAtoms() / natoms_per_task;
     199         334 :   addComponent( compName, s );
     200         334 : }
     201             : 
     202             : template <class T>
     203       25806 : void MultiColvarTemplate<T>::getInputData( std::vector<double>& inputdata ) const {
     204       25806 :   std::size_t ntasks = getConstPntrToComponent(0)->getNumberOfStoredValues();
     205       25806 :   if( inputdata.size()!=5*natoms_per_task*ntasks ) {
     206         219 :     inputdata.resize( 5*natoms_per_task*ntasks );
     207             :   }
     208             : 
     209             :   std::size_t k=0;
     210     1439574 :   for(unsigned i=0; i<ntasks; ++i) {
     211     4136461 :     for(unsigned j=0; j<natoms_per_task; ++j) {
     212     2722693 :       Vector mypos( getPosition( natoms_per_task*i + j ) );
     213     2722693 :       inputdata[k] = mypos[0];
     214     2722693 :       k++;
     215     2722693 :       inputdata[k] = mypos[1];
     216     2722693 :       k++;
     217     2722693 :       inputdata[k] = mypos[2];
     218     2722693 :       k++;
     219             :     }
     220     4136461 :     for(unsigned j=0; j<natoms_per_task; ++j) {
     221     2722693 :       inputdata[k] = getMass( natoms_per_task*i + j );
     222     2722693 :       k++;
     223             :     }
     224     4136461 :     for(unsigned j=0; j<natoms_per_task; ++j) {
     225     2722693 :       inputdata[k] = getCharge( natoms_per_task*i + j );
     226     2722693 :       k++;
     227             :     }
     228             :   }
     229       25806 : }
     230             : 
     231             : template <class T>
     232     2230892 : void MultiColvarTemplate<T>::performTask( std::size_t task_index,
     233             :     const MultiColvarInput& actiondata,
     234             :     ParallelActionsInput& input,
     235             :     ParallelActionsOutput& output ) {
     236     2230892 :   std::size_t pos_start = 5*actiondata.nindices_per_task*task_index;
     237     2230892 :   if( actiondata.usepbc ) {
     238     1823243 :     if( actiondata.nindices_per_task==1 ) {
     239             :       //this may be changed to input.pbc.apply() en mass or only on this one
     240       49576 :       Vector fpos=input.pbc->distance(Vector(0.0,0.0,0.0),
     241       49576 :                                       Vector(input.inputdata[pos_start],
     242       24788 :                                              input.inputdata[pos_start+1],
     243       24788 :                                              input.inputdata[pos_start+2]) );
     244       24788 :       input.inputdata[pos_start]  =fpos[0];
     245       24788 :       input.inputdata[pos_start+1]=fpos[1];
     246       24788 :       input.inputdata[pos_start+2]=fpos[2];
     247             :     } else {
     248             :       //make whole?
     249             :       std::size_t apos_start = pos_start;
     250             :       //if accidentaly nindices_per_task is 0, this will work by looping on all possible unsigned integers!!!!
     251     3608846 :       for(unsigned j=0; j<actiondata.nindices_per_task-1; ++j) {
     252     1810391 :         Vector first(input.inputdata[apos_start],
     253     1810391 :                      input.inputdata[apos_start+1],
     254     1810391 :                      input.inputdata[apos_start+2]);
     255     1810391 :         Vector second(input.inputdata[apos_start+3],
     256     1810391 :                       input.inputdata[apos_start+4],
     257     1810391 :                       input.inputdata[apos_start+5]);
     258             :         //calling the pbc here gives problems
     259     1810391 :         second=first+input.pbc->distance(first,second);
     260     1810391 :         input.inputdata[apos_start+3]=second[0];
     261     1810391 :         input.inputdata[apos_start+4]=second[1];
     262     1810391 :         input.inputdata[apos_start+5]=second[2];
     263             :         apos_start += 3;
     264             :       }
     265             :     }
     266      407649 :   } else if( actiondata.nindices_per_task==1 ) {
     267             :     //isn't this equivalent to x = x-0?
     268             :     //why this is needed?
     269             :     Vector fpos=delta(Vector(0.0,0.0,0.0),
     270      134291 :                       Vector(input.inputdata[pos_start],
     271      134291 :                              input.inputdata[pos_start+1],
     272      134291 :                              input.inputdata[pos_start+2]));
     273             :     input.inputdata[pos_start]=fpos[0];
     274      134291 :     input.inputdata[pos_start+1]=fpos[1];
     275      134291 :     input.inputdata[pos_start+2]=fpos[2];
     276             :   }
     277     2230892 :   const size_t mass_start = pos_start + 3*actiondata.nindices_per_task;
     278     2230892 :   const size_t charge_start = mass_start + actiondata.nindices_per_task;
     279     2230892 :   const size_t local_ndev = 3*actiondata.nindices_per_task+virialSize;
     280             : 
     281     2230892 :   ColvarOutput cvout { output.values,
     282             :                        local_ndev,
     283             :                        output.derivatives.data() };
     284     4461784 :   T::calculateCV( ColvarInput{actiondata.mode,
     285             :                               actiondata.nindices_per_task,
     286     2230892 :                               input.inputdata+pos_start,
     287     2230892 :                               input.inputdata+mass_start,
     288     2230892 :                               input.inputdata+charge_start,
     289     2230892 :                               *input.pbc},
     290             :                   cvout );
     291     2230892 : }
     292             : 
     293             : template <class T>
     294     1030449 : int MultiColvarTemplate<T>::getNumberOfValuesPerTask( std::size_t task_index,
     295             :     const MultiColvarInput& actiondata ) {
     296     1030449 :   return 1;
     297             : }
     298             : 
     299             : template <class T>
     300     1030449 : void MultiColvarTemplate<T>::getForceIndices( std::size_t task_index,
     301             :     std::size_t colno,
     302             :     std::size_t ntotal_force,
     303             :     const MultiColvarInput& actiondata,
     304             :     const ParallelActionsInput& input,
     305             :     ForceIndexHolder force_indices ) {
     306     2228186 :   for(unsigned i=0; i<input.ncomponents; ++i) {
     307             :     std::size_t m=0;
     308     1197737 :     std::size_t base = 3*task_index*actiondata.nindices_per_task;
     309     3442683 :     for(unsigned j=0; j<actiondata.nindices_per_task; ++j) {
     310     2244946 :       force_indices.indices[i][m] = base + m;
     311     2244946 :       ++m;
     312     2244946 :       force_indices.indices[i][m] = base + m;
     313     2244946 :       ++m;
     314     2244946 :       force_indices.indices[i][m] = base + m;
     315     2244946 :       ++m;
     316             :     }
     317     1197737 :     force_indices.threadsafe_derivatives_end[i] = 3*actiondata.nindices_per_task;
     318     1197737 :     force_indices.indices[i][m+0] = ntotal_force - 9;
     319     1197737 :     force_indices.indices[i][m+1] = ntotal_force - 8;
     320     1197737 :     force_indices.indices[i][m+2] = ntotal_force - 7;
     321     1197737 :     force_indices.indices[i][m+3] = ntotal_force - 6;
     322     1197737 :     force_indices.indices[i][m+4] = ntotal_force - 5;
     323     1197737 :     force_indices.indices[i][m+5] = ntotal_force - 4;
     324     1197737 :     force_indices.indices[i][m+6] = ntotal_force - 3;
     325     1197737 :     force_indices.indices[i][m+7] = ntotal_force - 2;
     326     1197737 :     force_indices.indices[i][m+8] = ntotal_force - 1;
     327     1197737 :     force_indices.tot_indices[i] = 3*actiondata.nindices_per_task + virialSize;
     328             :   }
     329     1030449 : }
     330             : 
     331             : } // namespace colvar
     332             : } // namespace PLMD
     333             : #endif

Generated by: LCOV version 1.16