LCOV - code coverage report
Current view: top level - colvar - MultiColvarTemplate.h (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 155 181 85.6 %
Date: 2026-11-05 10:19:04 Functions: 92 108 85.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 CV, typename myPTM=defaultPTM>
      51             : class MultiColvarTemplate : public ActionWithVector {
      52             : public:
      53             :   using input_type = MultiColvarInput;
      54             :   using mytype=MultiColvarTemplate<CV,myPTM>;
      55             :   using PTM =typename myPTM::template PTM<mytype>;
      56             :   using precision = typename cvprecision<CV>::type;
      57             :   typedef typename PTM::ParallelActionsInput ParallelActionsInput;
      58             :   typedef typename PTM::ParallelActionsOutput ParallelActionsOutput;
      59             :   constexpr static size_t virialSize = 9;
      60             : private:
      61             : /// The parallel task manager
      62             :   PTM taskmanager;
      63             : /// An index that decides what we are calculating
      64             :   unsigned mode;
      65             : /// Are we using pbc to calculate the CVs
      66             :   bool usepbc;
      67             : /// Do we reassemble the molecule
      68             :   bool wholemolecules;
      69             : /// Variable that is turned true if charges are needed to compute the underlying CVs
      70             : /// Add the command:
      71             : /// keys.add("hidden","NEEDS_CHARGES","Inform the parallel task manager that we need the charges to compute this CV");
      72             : /// in the registerKeywords method for your colvar to turn this to true
      73             :   bool needs_charges;
      74             : /// The number of atoms per task
      75             :   unsigned natoms_per_task;
      76             : public:
      77             :   static void registerKeywords(Keywords&);
      78             :   explicit MultiColvarTemplate(const ActionOptions&);
      79             :   unsigned getNumberOfDerivatives() override ;
      80             :   void addValueWithDerivatives( const std::vector<std::size_t>& shape=std::vector<std::size_t>() ) override ;
      81             :   void addComponentWithDerivatives( const std::string& name, const std::vector<std::size_t>& shape=std::vector<std::size_t>() ) override ;
      82             :   void getAtomicInputData( std::vector<double>& inputdata ) const override ;
      83             :   void getAtomicInputData( std::vector<float>& inputdata ) const override ;
      84             :   void calculate() override;
      85             :   void applyNonZeroRankForces( std::vector<double>& outforces ) override ;
      86             :   static void performTask( std::size_t task_index,
      87             :                            const MultiColvarInput& actiondata,
      88             :                            ParallelActionsInput& input,
      89             :                            ParallelActionsOutput& output );
      90             :   static int getNumberOfValuesPerTask( std::size_t task_index, const MultiColvarInput& actiondata );
      91             :   static void getForceIndices( std::size_t task_index,
      92             :                                std::size_t colno,
      93             :                                std::size_t ntotal_force,
      94             :                                const MultiColvarInput& actiondata,
      95             :                                const ParallelActionsInput& input,
      96             :                                ForceIndexHolder force_indices );
      97             : };
      98             : 
      99             : template <class CV, typename myPTM>
     100         681 : void MultiColvarTemplate<CV,myPTM>::registerKeywords(Keywords& keys ) {
     101         681 :   CV::registerKeywords( keys );
     102         681 :   PTM::registerKeywords( keys );
     103        1362 :   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");
     104         681 :   unsigned nkeys = keys.size();
     105        8768 :   for(unsigned i=0; i<nkeys; ++i) {
     106       16174 :     if( keys.style( keys.getKeyword(i), "atoms" ) ) {
     107        1850 :       keys.reset_style( keys.getKeyword(i), "numbered" );
     108             :     }
     109             :   }
     110        1362 :   if( keys.outputComponentExists(".#!value") ) {
     111        2160 :     keys.setValueDescription("vector","the " + keys.getDisplayName() + " for each set of specified atoms");
     112             :   }
     113         681 : }
     114             : 
     115             : template <class CV, typename myPTM>
     116         321 : MultiColvarTemplate<CV,myPTM>::MultiColvarTemplate(const ActionOptions&ao):
     117             :   Action(ao),
     118             :   ActionWithVector(ao),
     119         321 :   taskmanager(this),
     120         321 :   mode(0),
     121         321 :   usepbc(true),
     122         321 :   wholemolecules(false),
     123         321 :   needs_charges(keywords.exists("NEEDS_CHARGES")) {
     124         321 :   std::vector<AtomNumber> all_atoms;
     125         321 :   if( getName()=="POSITION_VECTOR" || getName()=="MASS_VECTOR" || getName()=="CHARGE_VECTOR" ) {
     126          96 :     parseAtomList( "ATOMS", all_atoms );
     127             :   }
     128         321 :   if( all_atoms.size()>0 ) {
     129          46 :     natoms_per_task=1;
     130             :   } else {
     131         275 :     std::vector<AtomNumber> t;
     132      232191 :     for(int i=1;; ++i ) {
     133      232191 :       CV::parseAtomList( i, t, this );
     134      232191 :       if( t.empty() ) {
     135             :         break;
     136             :       }
     137             : 
     138      231916 :       if( i==1 ) {
     139         275 :         natoms_per_task=t.size();
     140             :       }
     141      231916 :       if( t.size()!=natoms_per_task ) {
     142             :         std::string ss;
     143           0 :         Tools::convert(i,ss);
     144           0 :         error("ATOMS" + ss + " keyword has the wrong number of atoms");
     145             :       }
     146      698316 :       for(unsigned j=0; j<natoms_per_task; ++j) {
     147      466400 :         all_atoms.push_back( t[j] );
     148             :       }
     149      231916 :       t.resize(0);
     150             :     }
     151         275 :   }
     152         321 :   if( all_atoms.size()==0 ) {
     153           0 :     error("No atoms have been specified");
     154             :   }
     155         321 :   requestAtoms(all_atoms);
     156         321 :   if( keywords.exists("NOPBC") ) {
     157         321 :     bool nopbc=!usepbc;
     158         321 :     parseFlag("NOPBC",nopbc);
     159         321 :     usepbc=!nopbc;
     160             :   }
     161         321 :   if( keywords.exists("WHOLEMOLECULES") ) {
     162          48 :     parseFlag("WHOLEMOLECULES",wholemolecules);
     163          48 :     if( wholemolecules ) {
     164           1 :       usepbc=false;
     165             :     }
     166             :   }
     167         321 :   if( usepbc ) {
     168         278 :     log.printf("  using periodic boundary conditions\n");
     169             :   } else {
     170          43 :     log.printf("  without periodic boundary conditions\n");
     171             :   }
     172             : 
     173             :   // Setup the values
     174         321 :   mode = CV::getModeAndSetupValues( this );
     175             :   // This sets up an array in the parallel task manager to hold all the indices
     176             :   // Sets up the index list in the task manager
     177         321 :   taskmanager.setupParallelTaskManager( 3*natoms_per_task + virialSize, virialSize );
     178         321 :   taskmanager.setActionInput( MultiColvarInput{ usepbc, mode, natoms_per_task });
     179         321 : }
     180             : 
     181             : template <class CV, typename myPTM>
     182        2560 : unsigned MultiColvarTemplate<CV,myPTM>::getNumberOfDerivatives() {
     183        2560 :   return 3*getNumberOfAtoms()+9;
     184             : }
     185             : 
     186             : template <class CV, typename myPTM>
     187       31974 : void MultiColvarTemplate<CV,myPTM>::calculate() {
     188       31974 :   if( wholemolecules ) {
     189           1 :     makeWhole();
     190             :   }
     191       31974 :   taskmanager.runAllTasks();
     192       31974 : }
     193             : 
     194             : template <class CV, typename myPTM>
     195       24168 : void MultiColvarTemplate<CV,myPTM>::applyNonZeroRankForces( std::vector<double>& outforces ) {
     196       24168 :   taskmanager.applyForces( outforces );
     197       24168 : }
     198             : 
     199             : template <class CV, typename myPTM>
     200         184 : void MultiColvarTemplate<CV,myPTM>::addValueWithDerivatives( const std::vector<std::size_t>& shape ) {
     201         184 :   std::vector<std::size_t> s(1);
     202         184 :   s[0]=getNumberOfAtoms() / natoms_per_task;
     203         184 :   addValue( s );
     204         184 : }
     205             : 
     206             : template <class T, typename myPTM>
     207         425 : void MultiColvarTemplate<T,myPTM>::addComponentWithDerivatives( const std::string& compName, const std::vector<std::size_t>& shape ) {
     208         425 :   std::vector<std::size_t> s(1);
     209         425 :   s[0]=getNumberOfAtoms() / natoms_per_task;
     210         425 :   addComponent( compName, s );
     211         425 : }
     212             : 
     213             : template <class CV, typename myPTM>
     214       31974 : void MultiColvarTemplate<CV,myPTM>::getAtomicInputData( std::vector<double>& inputdata ) const {
     215       31974 :   std::size_t ntasks = getConstPntrToComponent(0)->getNumberOfStoredValues();
     216       31974 :   if( inputdata.size()!=5*natoms_per_task*ntasks ) {
     217         300 :     inputdata.resize( 5*natoms_per_task*ntasks );
     218             :   }
     219             : 
     220             :   std::size_t k=0;
     221     1727128 :   for(unsigned i=0; i<ntasks; ++i) {
     222     4953888 :     for(unsigned j=0; j<natoms_per_task; ++j) {
     223     3258734 :       Vector mypos( getPosition( natoms_per_task*i + j ) );
     224     3258734 :       inputdata[k] = mypos[0];
     225     3258734 :       k++;
     226     3258734 :       inputdata[k] = mypos[1];
     227     3258734 :       k++;
     228     3258734 :       inputdata[k] = mypos[2];
     229     3258734 :       k++;
     230             :     }
     231     4953888 :     for(unsigned j=0; j<natoms_per_task; ++j) {
     232     3258734 :       inputdata[k] = getMass( natoms_per_task*i + j );
     233     3258734 :       k++;
     234             :     }
     235     1695154 :     if( !chargesWereSet && !needs_charges ) {
     236          15 :       k += natoms_per_task;
     237          15 :       continue ;
     238             :     }
     239     4953843 :     for(unsigned j=0; j<natoms_per_task; ++j) {
     240     3258704 :       inputdata[k] = getCharge( natoms_per_task*i + j );
     241     3258704 :       k++;
     242             :     }
     243             :   }
     244       31974 : }
     245             : 
     246             : template <class CV, typename myPTM>
     247           0 : void MultiColvarTemplate<CV,myPTM>::getAtomicInputData( std::vector<float>& inputdata ) const {
     248           0 :   std::size_t ntasks = getConstPntrToComponent(0)->getNumberOfStoredValues();
     249           0 :   if( inputdata.size()!=5*natoms_per_task*ntasks ) {
     250           0 :     inputdata.resize( 5*natoms_per_task*ntasks );
     251             :   }
     252             : 
     253             :   std::size_t k=0;
     254           0 :   for(unsigned i=0; i<ntasks; ++i) {
     255           0 :     for(unsigned j=0; j<natoms_per_task; ++j) {
     256           0 :       Vector mypos( getPosition( natoms_per_task*i + j ) );
     257           0 :       inputdata[k] = mypos[0];
     258           0 :       k++;
     259           0 :       inputdata[k] = mypos[1];
     260           0 :       k++;
     261           0 :       inputdata[k] = mypos[2];
     262           0 :       k++;
     263             :     }
     264           0 :     for(unsigned j=0; j<natoms_per_task; ++j) {
     265           0 :       inputdata[k] = getMass( natoms_per_task*i + j );
     266           0 :       k++;
     267             :     }
     268           0 :     if( !chargesWereSet && !needs_charges ) {
     269           0 :       k += natoms_per_task;
     270           0 :       continue ;
     271             :     }
     272           0 :     for(unsigned j=0; j<natoms_per_task; ++j) {
     273           0 :       inputdata[k] = getCharge( natoms_per_task*i + j );
     274           0 :       k++;
     275             :     }
     276             :   }
     277           0 : }
     278             : 
     279             : template <class CV, typename myPTM>
     280     2724769 : void MultiColvarTemplate<CV,myPTM>::performTask( std::size_t task_index,
     281             :     const MultiColvarInput& actiondata,
     282             :     ParallelActionsInput& input,
     283             :     ParallelActionsOutput& output ) {
     284     2724769 :   std::size_t pos_start = 5*actiondata.nindices_per_task*task_index;
     285             :   using V3=VectorT<precision>;
     286     2724769 :   if( actiondata.usepbc ) {
     287     2288376 :     if( actiondata.nindices_per_task==1 ) {
     288             :       //this may be changed to input.pbc.apply() en mass or only on this one
     289       49576 :       V3 fpos=input.pbc->distance(V3(0.0,0.0,0.0),
     290       49576 :                                   V3(input.inputdata[pos_start],
     291       24788 :                                      input.inputdata[pos_start+1],
     292       24788 :                                      input.inputdata[pos_start+2]) );
     293       24788 :       input.inputdata[pos_start]  =fpos[0];
     294       24788 :       input.inputdata[pos_start+1]=fpos[1];
     295       24788 :       input.inputdata[pos_start+2]=fpos[2];
     296             :     } else {
     297             :       //make whole?
     298             :       std::size_t apos_start = pos_start;
     299             :       //if accidentaly nindices_per_task is 0, this will work by looping on all possible unsigned integers!!!!
     300     4541226 :       for(unsigned j=0; j<actiondata.nindices_per_task-1; ++j) {
     301     2277638 :         V3 first(input.inputdata[apos_start],
     302     2277638 :                  input.inputdata[apos_start+1],
     303     2277638 :                  input.inputdata[apos_start+2]);
     304     2277638 :         V3 second(input.inputdata[apos_start+3],
     305     2277638 :                   input.inputdata[apos_start+4],
     306     2277638 :                   input.inputdata[apos_start+5]);
     307             :         //calling the pbc here gives problems
     308     2277638 :         second=first+input.pbc->distance(first,second);
     309     2277638 :         input.inputdata[apos_start+3]=second[0];
     310     2277638 :         input.inputdata[apos_start+4]=second[1];
     311     2277638 :         input.inputdata[apos_start+5]=second[2];
     312             :         apos_start += 3;
     313             :       }
     314             :     }
     315      436393 :   } else if( actiondata.nindices_per_task==1 ) {
     316             :     //isn't this equivalent to x = x-0?
     317             :     //why this is needed?
     318             :     V3 fpos=delta(V3(0.0,0.0,0.0),
     319      162779 :                   V3(input.inputdata[pos_start],
     320      162779 :                      input.inputdata[pos_start+1],
     321      162779 :                      input.inputdata[pos_start+2]));
     322             :     input.inputdata[pos_start]=fpos[0];
     323      162779 :     input.inputdata[pos_start+1]=fpos[1];
     324      162779 :     input.inputdata[pos_start+2]=fpos[2];
     325             :   }
     326     2724769 :   const size_t mass_start = pos_start + 3*actiondata.nindices_per_task;
     327     2724769 :   const size_t charge_start = mass_start + actiondata.nindices_per_task;
     328     2724769 :   const size_t local_ndev = 3*actiondata.nindices_per_task+virialSize;
     329             : 
     330     2724769 :   ColvarOutput cvout { output.values,
     331             :                        local_ndev,
     332             :                        output.derivatives.data() };
     333     5449538 :   CV::calculateCV( ColvarInput{actiondata.mode,
     334             :                                actiondata.nindices_per_task,
     335     2724769 :                                input.inputdata+pos_start,
     336     2724769 :                                input.inputdata+mass_start,
     337     2724769 :                                input.inputdata+charge_start,
     338     2724769 :                                *input.pbc},
     339             :                    cvout );
     340     2724769 : }
     341             : 
     342             : template <class CV, typename myPTM>
     343     1242940 : int MultiColvarTemplate<CV,myPTM>::getNumberOfValuesPerTask( std::size_t task_index,
     344             :     const MultiColvarInput& actiondata ) {
     345     1242940 :   return 1;
     346             : }
     347             : 
     348             : template <class CV, typename myPTM>
     349     1242940 : void MultiColvarTemplate<CV,myPTM>::getForceIndices( std::size_t task_index,
     350             :     std::size_t colno,
     351             :     std::size_t ntotal_force,
     352             :     const MultiColvarInput& actiondata,
     353             :     const ParallelActionsInput& input,
     354             :     ForceIndexHolder force_indices ) {
     355     2653789 :   for(unsigned i=0; i<input.ncomponents; ++i) {
     356             :     std::size_t m=0;
     357     1410849 :     std::size_t base = 3*task_index*actiondata.nindices_per_task;
     358     4083385 :     for(unsigned j=0; j<actiondata.nindices_per_task; ++j) {
     359     2672536 :       force_indices.indices[i][m] = base + m;
     360     2672536 :       ++m;
     361     2672536 :       force_indices.indices[i][m] = base + m;
     362     2672536 :       ++m;
     363     2672536 :       force_indices.indices[i][m] = base + m;
     364     2672536 :       ++m;
     365             :     }
     366     1410849 :     force_indices.threadsafe_derivatives_end[i] = 3*actiondata.nindices_per_task;
     367     1410849 :     force_indices.indices[i][m+0] = ntotal_force - 9;
     368     1410849 :     force_indices.indices[i][m+1] = ntotal_force - 8;
     369     1410849 :     force_indices.indices[i][m+2] = ntotal_force - 7;
     370     1410849 :     force_indices.indices[i][m+3] = ntotal_force - 6;
     371     1410849 :     force_indices.indices[i][m+4] = ntotal_force - 5;
     372     1410849 :     force_indices.indices[i][m+5] = ntotal_force - 4;
     373     1410849 :     force_indices.indices[i][m+6] = ntotal_force - 3;
     374     1410849 :     force_indices.indices[i][m+7] = ntotal_force - 2;
     375     1410849 :     force_indices.indices[i][m+8] = ntotal_force - 1;
     376     1410849 :     force_indices.tot_indices[i] = 3*actiondata.nindices_per_task + virialSize;
     377             :   }
     378     1242940 : }
     379             : 
     380             : } // namespace colvar
     381             : } // namespace PLMD
     382             : #endif

Generated by: LCOV version 1.16