LCOV - code coverage report
Current view: top level - core - ActionAtomistic.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 393 420 93.6 %
Date: 2026-11-05 10:19:04 Functions: 28 34 82.4 %

          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             : #include "ActionAtomistic.h"
      23             : #include "PlumedMain.h"
      24             : #include "ActionSet.h"
      25             : #include "GenericMolInfo.h"
      26             : #include "PbcAction.h"
      27             : #include <vector>
      28             : #include <string>
      29             : #include "ActionWithValue.h"
      30             : #include "ActionShortcut.h"
      31             : #include "Group.h"
      32             : #include "ActionWithVirtualAtom.h"
      33             : #include "tools/Exception.h"
      34             : #include "tools/Pbc.h"
      35             : #include "tools/PDB.h"
      36             : #include "tools/Tree.h"
      37             : 
      38             : namespace PLMD {
      39             : 
      40       18779 : ActionAtomistic::~ActionAtomistic() {
      41             : // empty destructor to delete unique_ptr
      42       56337 : }
      43             : 
      44       18779 : ActionAtomistic::ActionAtomistic(const ActionOptions&ao):
      45             :   Action(ao),
      46       18779 :   unique_local_needs_update(true),
      47       18779 :   boxValue(NULL),
      48       18779 :   lockRequestAtoms(false),
      49       18779 :   donotretrieve(false),
      50       18779 :   donotforce(false),
      51       18779 :   massesWereSet(false),
      52       18779 :   chargesWereSet(false) {
      53       18779 :   ActionWithValue* bv = plumed.getActionSet().selectWithLabel<ActionWithValue*>("Box");
      54       18779 :   actionMoldat=plumed.getActionSet().selectLatest<GenericMolInfo*>(this);
      55       18779 :   if( bv ) {
      56       18615 :     boxValue=bv->copyOutput(0);
      57             :   }
      58             :   // We now get all the information about atoms that are lying about
      59       18779 :   getAtomValuesFromPlumedObject( plumed, xpos, ypos, zpos, masv, chargev );
      60       18779 :   if( xpos.size()!=ypos.size() || xpos.size()!=zpos.size() || xpos.size()!=masv.size() || xpos.size()!=chargev.size() ) {
      61           0 :     error("mismatch between value arrays");
      62             :   }
      63       18779 : }
      64             : 
      65       28325 : void ActionAtomistic::getAtomValuesFromPlumedObject( const PlumedMain& plumed, std::vector<Value*>& xpos, std::vector<Value*>& ypos, std::vector<Value*>& zpos, std::vector<Value*>& masv, std::vector<Value*>& chargev ) {
      66             :   // the lists are kept by the ActionSet, so that they are not rebuilt by scanning
      67             :   // all the actions every time a new atomistic action is created
      68       28325 :   plumed.getActionSet().getAtomValues( xpos, ypos, zpos, masv, chargev );
      69       28325 : }
      70             : 
      71       34911 : void ActionAtomistic::registerKeywords( Keywords& keys ) {
      72             :   (void) keys; // avoid warning
      73       34911 : }
      74             : 
      75             : 
      76       15549 : void ActionAtomistic::requestAtoms(const std::vector<AtomNumber> & a, const bool clearDep) {
      77       15549 :   plumed_massert(!lockRequestAtoms,"requested atom list can only be changed in the prepare() method");
      78             :   // this makes the EMST invalid so that it will be regenerated at next request
      79             :   tree.reset();
      80       15549 :   int nat=a.size();
      81       15549 :   indexes=a;
      82       15549 :   actionPositions.resize(nat);
      83       15549 :   forces.resize(nat);
      84       15549 :   masses.resize(nat);
      85       15549 :   charges.resize(nat);
      86       15549 :   atom_value_ind.resize( a.size() );
      87       15549 :   unsigned n=getTotAtoms();
      88       15549 :   if(clearDep) {
      89       14715 :     clearDependencies();
      90             :   }
      91       15549 :   unique.clear();
      92       15549 :   std::vector<bool> requirements( xpos.size(), false );
      93       15549 :   if( boxValue ) {
      94       15549 :     addDependency( boxValue->getPntrToAction() );
      95             :   }
      96     1508592 :   for(unsigned i=0; i<indexes.size(); i++) {
      97     1493044 :     if(indexes[i].index()>=n) {
      98             :       std::string num;
      99           1 :       Tools::convert( indexes[i].serial(),num );
     100           3 :       error("atom " + num + " out of range");
     101             :     }
     102     1493043 :     atom_value_ind[i] = getValueIndices( indexes[i] );
     103     1493043 :     requirements[atom_value_ind[i].first] = true;
     104     1493043 :     if( atom_value_ind[i].first==0 ) {
     105     1477761 :       unique.push_back(indexes[i]);
     106       15282 :     } else if( atom_value_ind[i].second>0 ) {
     107           0 :       error("action atomistic is not set up to deal with multiple vectors in position input");
     108             :     }
     109             :   }
     110             : 
     111       15548 :   atom_value_ind_grouped.clear();
     112             : 
     113       15548 :   if(atom_value_ind.size()>0) {
     114       15494 :     auto prev_nn=atom_value_ind[0].first;
     115             :     atom_value_ind_grouped.push_back(
     116       15494 :       std::pair<std::size_t,std::vector<std::size_t>>(
     117             :         prev_nn, {}));
     118       15494 :     atom_value_ind_grouped.back().second.push_back(atom_value_ind[0].second);
     119     1493036 :     for(unsigned i=1; i<atom_value_ind.size(); i++) {
     120     1477542 :       auto nn = atom_value_ind[i].first;
     121     1477542 :       auto kk = atom_value_ind[i].second;
     122     1477542 :       if(nn!=prev_nn)
     123       16966 :         atom_value_ind_grouped.push_back(std::pair<std::size_t,std::vector<std::size_t>>(nn, {}));
     124     1477542 :       atom_value_ind_grouped.back().second.push_back(kk);
     125             :       prev_nn=nn;
     126             :     }
     127             :   }
     128             : 
     129             :   // Add the dependencies to the actions that we require
     130       15548 :   Tools::removeDuplicates(unique);
     131       15548 :   value_depends.resize(0);
     132     6636057 :   for(unsigned i=0; i<requirements.size(); ++i ) {
     133     6620509 :     if( !requirements[i] ) {
     134     6591039 :       continue;
     135             :     }
     136       29471 :     value_depends.push_back( i );
     137       29470 :     addDependency( xpos[i]->getPntrToAction() );
     138       29470 :     addDependency( ypos[i]->getPntrToAction() );
     139       29470 :     addDependency( zpos[i]->getPntrToAction() );
     140       29470 :     addDependency( masv[i]->getPntrToAction() );
     141       29470 :     addDependency( chargev[i]->getPntrToAction() );
     142             :   }
     143       15548 :   unique_local_needs_update=true;
     144       15548 : }
     145             : 
     146           0 : void ActionAtomistic::pbcApply(std::vector<Vector>& dlist, unsigned max_index)const {
     147           0 :   actionPbc.apply(dlist, max_index);
     148           0 : }
     149             : 
     150         186 : void ActionAtomistic::calculateNumericalDerivatives( ActionWithValue* a ) {
     151         186 :   calculateAtomicNumericalDerivatives( a, 0 );
     152         186 : }
     153             : 
     154           0 : void ActionAtomistic::changeBox( const Tensor& newbox ) {
     155           0 :   actionPbc.setBox( newbox );
     156           0 : }
     157             : 
     158         261 : void ActionAtomistic::calculateAtomicNumericalDerivatives( ActionWithValue* a, const unsigned& startnum ) {
     159         261 :   if(!a) {
     160         261 :     a=castToActionWithValue();
     161         261 :     plumed_massert(a,"only Actions with a value can be differentiated");
     162             :   }
     163             : 
     164             :   const size_t nval=a->getNumberOfComponents();
     165         261 :   const size_t natoms=getNumberOfAtoms();
     166         261 :   std::vector<Vector> value(nval*natoms);
     167         261 :   std::vector<Tensor> valuebox(nval);
     168         261 :   std::vector<Vector> savedPositions(natoms);
     169             :   const double delta=std::sqrt(epsilon);
     170             : 
     171       15445 :   for(unsigned i=0; i<natoms; i++)
     172       60736 :     for(int k=0; k<3; k++) {
     173       45552 :       savedPositions[i][k]=actionPositions[i][k];
     174       45552 :       actionPositions[i][k]=actionPositions[i][k]+delta;
     175       45552 :       a->calculate();
     176       45552 :       actionPositions[i][k]=savedPositions[i][k];
     177      118764 :       for(unsigned j=0; j<nval; j++) {
     178       73212 :         value[j*natoms+i][k]=a->getOutputQuantity(j);
     179             :       }
     180             :     }
     181         261 :   Tensor box(actionPbc.getBox());
     182        1044 :   for(int i=0; i<3; i++)
     183        3132 :     for(int k=0; k<3; k++) {
     184        2349 :       double arg0=box(i,k);
     185      139005 :       for(unsigned j=0; j<natoms; j++) {
     186      136656 :         actionPositions[j]=actionPbc.realToScaled(actionPositions[j]);
     187             :       }
     188        2349 :       box(i,k)=box(i,k)+delta;
     189        2349 :       actionPbc.setBox(box);
     190      139005 :       for(unsigned j=0; j<natoms; j++) {
     191      136656 :         actionPositions[j]=actionPbc.scaledToReal(actionPositions[j]);
     192             :       }
     193        2349 :       a->calculate();
     194        2349 :       box(i,k)=arg0;
     195        2349 :       actionPbc.setBox(box);
     196      139005 :       for(unsigned j=0; j<natoms; j++) {
     197      136656 :         actionPositions[j]=savedPositions[j];
     198             :       }
     199       15165 :       for(unsigned j=0; j<nval; j++) {
     200       12816 :         valuebox[j](i,k)=a->getOutputQuantity(j);
     201             :       }
     202             :     }
     203             : 
     204         261 :   a->calculate();
     205         261 :   a->clearDerivatives();
     206        1685 :   for(unsigned j=0; j<nval; j++) {
     207        1424 :     Value* v=a->copyOutput(j);
     208        1424 :     double ref=v->get();
     209        1424 :     if(v->hasDerivatives()) {
     210       15874 :       for(unsigned i=0; i<natoms; i++)
     211       62224 :         for(int k=0; k<3; k++) {
     212       46668 :           double d=(value[j*natoms+i][k]-ref)/delta;
     213       46668 :           v->addDerivative(startnum+3*i+k,d);
     214             :         }
     215         318 :       Tensor virial;
     216        1272 :       for(int i=0; i<3; i++)
     217        3816 :         for(int k=0; k<3; k++) {
     218        2862 :           virial(i,k)= (valuebox[j](i,k)-ref)/delta;
     219             :         }
     220             : // BE CAREFUL WITH NON ORTHORHOMBIC CELL
     221         318 :       virial=-matmul(box.transpose(),virial);
     222        1272 :       for(int i=0; i<3; i++)
     223        3816 :         for(int k=0; k<3; k++) {
     224        2862 :           v->addDerivative(startnum+3*natoms+3*k+i,virial(k,i));
     225             :         }
     226             :     }
     227             :   }
     228         261 : }
     229             : 
     230      383545 : bool ActionAtomistic::actionHasForces() {
     231      383545 :   ActionWithValue* av = castToActionWithValue();
     232      383545 :   if( av ) {
     233      383545 :     return !av->doNotCalculateDerivatives();
     234             :   }
     235           0 :   if( indexes.size()>0 ) {
     236           0 :     plumed_merror("you have to overwrite the function actionHasForce to tell plumed if you method applies forces");
     237             :   }
     238             :   return true;
     239             : }
     240             : 
     241       14305 : void ActionAtomistic::parseAtomList(const std::string&key, std::vector<AtomNumber> &t) {
     242       14305 :   parseAtomList(key,-1,t);
     243       14305 : }
     244             : 
     245      255239 : void ActionAtomistic::parseAtomList(const std::string&key,const int num, std::vector<AtomNumber> &t) {
     246      258008 :   plumed_massert( keywords.style(key,"atoms") || keywords.style(key,"hidden") || keywords.style(key,"deprecated"), "keyword " + key + " should be registered as atoms");
     247      255239 :   std::vector<std::string> strings;
     248      255239 :   if( num<0 ) {
     249       19308 :     parseVector(key,strings);
     250       19308 :     if(strings.empty()) {
     251             :       return;
     252             :     }
     253             :   } else {
     254      235931 :     if ( !parseNumberedVector(key,num,strings) ) {
     255             :       return;
     256             :     }
     257             :   }
     258      249985 :   t.resize(0);
     259      249985 :   interpretAtomList( strings, xpos, this, t );
     260      255239 : }
     261             : 
     262          40 : void ActionAtomistic::interpretAtomList(std::vector<std::string>& strings, std::vector<AtomNumber> &t) {
     263          40 :   interpretAtomList( strings, xpos, this, t );
     264          40 : }
     265             : 
     266      250661 : void ActionAtomistic::interpretAtomList(std::vector<std::string>& strings, const std::vector<Value*>& xpos, Action* action, std::vector<AtomNumber> &t) {
     267      250661 :   Tools::interpretRanges(strings);
     268     1879664 :   for(unsigned i=0; i<strings.size(); ++i) {
     269             :     AtomNumber atom;
     270     1629003 :     bool ok=Tools::convertNoexcept(strings[i],atom); // this is converting strings to AtomNumbers
     271     1629003 :     if(ok) {
     272     1603240 :       t.push_back(atom);
     273             :     }
     274             : // here we check if this is a special symbol for MOLINFO
     275       25763 :     if( !ok && strings[i].compare(0,1,"@")==0 ) {
     276         859 :       std::string symbol=strings[i].substr(1);
     277         859 :       if(symbol=="allatoms") {
     278             :         unsigned n=0;
     279          24 :         for(unsigned ii=0; ii<xpos.size(); ++ii) {
     280          14 :           n += xpos[ii]->getNumberOfValues();
     281             :         }
     282          10 :         t.reserve(n);
     283         365 :         for(unsigned ii=0; ii<n; ii++) {
     284         355 :           t.push_back(AtomNumber::index(ii));
     285             :         }
     286             :         ok=true;
     287         849 :       } else if(symbol=="mdatoms") {
     288          16 :         const auto n=xpos[0]->getNumberOfValues();
     289          16 :         t.reserve(t.size()+n);
     290        1246 :         for(unsigned ii=0; ii<n; ii++) {
     291        1230 :           t.push_back(AtomNumber::index(ii));
     292             :         }
     293             :         ok=true;
     294         833 :       } else if(Tools::startWith(symbol,"ndx:")) {
     295          64 :         auto words=Tools::getWords(symbol.substr(4));
     296             :         std::string ndxfile,ndxgroup;
     297          32 :         if(words.size()==1) {
     298             :           ndxfile=words[0];
     299          30 :         } else if(words.size()==2) {
     300             :           ndxfile=words[0];
     301             :           ndxgroup=words[1];
     302             :         } else {
     303           0 :           plumed_error()<<"Cannot intepret selection "<<symbol;
     304             :         }
     305             : 
     306          32 :         if(ndxgroup.size()>0) {
     307          90 :           action->log<<"  importing group '"+ndxgroup+"'";
     308             :         } else {
     309           2 :           action->log<<"  importing first group";
     310             :         }
     311          32 :         action->log<<" from index file "<<ndxfile<<"\n";
     312             : 
     313          32 :         IFile ifile;
     314          32 :         ifile.open(ndxfile);
     315             :         std::string line;
     316             :         std::string groupname;
     317             :         bool firstgroup=true;
     318             :         bool groupfound=false;
     319        5990 :         while(ifile.getline(line)) {
     320        5958 :           std::vector<std::string> groupWords=Tools::getWords(line);
     321        5958 :           if(groupWords.size()>=3 && groupWords[0]=="[" && groupWords[2]=="]") {
     322         288 :             if(groupname.length()>0) {
     323             :               firstgroup=false;
     324             :             }
     325             :             groupname=groupWords[1];
     326         288 :             if(groupname==ndxgroup || ndxgroup.length()==0) {
     327             :               groupfound=true;
     328             :             }
     329        5670 :           } else if(groupname==ndxgroup || (firstgroup && ndxgroup.length()==0)) {
     330        9532 :             for(unsigned ii=0; ii<groupWords.size(); ii++) {
     331             :               AtomNumber at;
     332        8912 :               Tools::convert(groupWords[ii],at);
     333        8912 :               t.push_back(at);
     334             :             }
     335             :           }
     336        5958 :         }
     337          32 :         if(!groupfound) {
     338           0 :           plumed_error()<<"group has not been found in index file";
     339             :         }
     340             :         ok=true;
     341          64 :       } else {
     342         801 :         auto* moldat=action->plumed.getActionSet().selectLatest<GenericMolInfo*>(action);
     343         801 :         if( moldat ) {
     344         801 :           std::vector<AtomNumber> atom_list;
     345         801 :           moldat->interpretSymbol( symbol, atom_list );
     346         801 :           if( atom_list.size()>0 ) {
     347             :             ok=true;
     348         801 :             t.insert(t.end(),atom_list.begin(),atom_list.end());
     349             :           } else {
     350           0 :             action->error(strings[i] + " is not a label plumed knows");
     351             :           }
     352             :         } else {
     353           0 :           action->error("atoms specified using @ symbol but no MOLINFO was available");
     354             :         }
     355             :       }
     356             :     }
     357             : // here we check if the atom name is the name of a group
     358     1628144 :     if(!ok) {
     359       24904 :       Group* mygrp=action->plumed.getActionSet().selectWithLabel<Group*>(strings[i]);
     360       24904 :       if(mygrp) {
     361         509 :         std::vector<std::string> grp_str( mygrp->getGroupAtoms() );
     362         509 :         interpretAtomList( grp_str, xpos, action, t );
     363             :         ok=true;
     364         509 :       } else {
     365       24395 :         Group* mygrp2=action->plumed.getActionSet().selectWithLabel<Group*>(strings[i]+"_grp");
     366       24395 :         if(mygrp2) {
     367         118 :           std::vector<std::string> grp_str( mygrp2->getGroupAtoms() );
     368         118 :           interpretAtomList( grp_str, xpos, action, t );
     369             :           ok=true;
     370         118 :         }
     371             :       }
     372             :     }
     373             : // here we check if the atom name is the name of an added virtual atom
     374             :     if(!ok) {
     375             :       unsigned ind = 0;
     376    13305227 :       for(unsigned j=0; j<xpos.size(); ++j) {
     377    13305227 :         if( xpos[j]->getPntrToAction()->getLabel()==strings[i] ) {
     378       24277 :           t.push_back( AtomNumber::index(ind) );
     379             :           ok=true;
     380             :           break;
     381             :         }
     382    13280950 :         ind = ind + xpos[j]->getNumberOfValues();
     383             :       }
     384             :     }
     385             :     if(!ok) {
     386           0 :       action->error("it was not possible to interpret atom name " + strings[i]);
     387             :     }
     388             :   }
     389      250661 : }
     390             : 
     391     2034198 : std::pair<std::size_t, std::size_t> ActionAtomistic::getValueIndices( const AtomNumber& i ) const {
     392     2034198 :   std::size_t valno=0, k = i.index();
     393    15479728 :   for(unsigned j=0; j<xpos.size(); ++j) {
     394    15479728 :     if( k<xpos[j]->getNumberOfValues() ) {
     395             :       valno=j;
     396             :       break;
     397             :     }
     398    13445530 :     k = k - xpos[j]->getNumberOfValues();
     399             :   }
     400     2034198 :   return std::pair<std::size_t, std::size_t>( valno, k );
     401             : }
     402             : 
     403      606519 : void ActionAtomistic::retrieveAtoms( const bool& force ) {
     404      606519 :   if( boxValue ) {
     405      576218 :     auto* ptr=boxValue->getPntrToAction();
     406      576218 :     plumed_assert(ptr); // needed for following calls, see #1046
     407      576218 :     PbcAction* pbca = ptr->castToPbcAction();
     408      576218 :     plumed_assert( pbca );
     409      576218 :     actionPbc=pbca->pbc;
     410             :   }
     411      606519 :   if( donotretrieve || indexes.size()==0 ) {
     412             :     return;
     413             :   }
     414      407739 :   auto * mtr=masv[0]->getPntrToAction();
     415      407739 :   plumed_assert(mtr); // needed for following calls, see #1046
     416      407739 :   ActionToPutData* mv = mtr->castToActionToPutData();
     417      407739 :   if(mv) {
     418      407737 :     massesWereSet=mv->hasBeenSet();
     419           2 :   } else if( (masv[0]->getPntrToAction())->getName()=="CONSTANT" ) {
     420           2 :     massesWereSet=true;  // Read masses from PDB file
     421             :   }
     422      407739 :   auto * ptr=chargev[0]->getPntrToAction();
     423      407739 :   plumed_assert(ptr); // needed for following calls, see #1046
     424      407739 :   ActionToPutData* cv = ptr->castToActionToPutData();
     425      407739 :   if(cv) {
     426      407737 :     chargesWereSet=cv->hasBeenSet();
     427           2 :   } else if( (chargev[0]->getPntrToAction())->getName()=="CONSTANT" ) {
     428           2 :     chargesWereSet=true;  // Read masses from PDB file
     429             :   }
     430             :   unsigned j = 0;
     431             : 
     432             : 
     433     1218514 :   for(const auto & a : atom_value_ind_grouped) {
     434      810775 :     const auto nn=a.first;
     435      810775 :     auto & xp=xpos[nn]->data;
     436      810775 :     auto & yp=ypos[nn]->data;
     437      810775 :     auto & zp=zpos[nn]->data;
     438      810775 :     auto & ch=chargev[nn]->data;
     439      810775 :     auto & ma=masv[nn]->data;
     440    11147898 :     for(const auto & kk : a.second) {
     441    10337123 :       actionPositions[j][0] = xp[kk];
     442    10337123 :       actionPositions[j][1] = yp[kk];
     443    10337123 :       actionPositions[j][2] = zp[kk];
     444    10337123 :       charges[j] = ch[kk];
     445    10337123 :       masses[j] = ma[kk];
     446    10337123 :       j++;
     447             :     }
     448             :   }
     449             : 
     450             : }
     451             : 
     452      428635 : void ActionAtomistic::setForcesOnAtoms(const std::vector<double>& forcesToApply, unsigned& ind) {
     453      428635 :   if( donotforce || (indexes.size()==0 && getName()!="FIXEDATOM") ) {
     454             :     return;
     455             :   }
     456      676968 :   for(unsigned i=0; i<value_depends.size(); ++i) {
     457      361139 :     xpos[value_depends[i]]->hasForce = true;
     458      361139 :     ypos[value_depends[i]]->hasForce = true;
     459      361139 :     zpos[value_depends[i]]->hasForce = true;
     460             :   }
     461             : 
     462             : 
     463      987586 :   for(const auto & a : atom_value_ind_grouped) {
     464      671757 :     const auto nn=a.first;
     465             :     plumed_dbg_assert(ind<forcesToApply.size()) << "problem setting forces in " << getLabel();
     466      671757 :     auto & xp=xpos[nn]->inputForce;
     467      671757 :     auto & yp=ypos[nn]->inputForce;
     468      671757 :     auto & zp=zpos[nn]->inputForce;
     469     7155870 :     for(const auto & kk : a.second) {
     470     6484113 :       xp[kk] += forcesToApply[ind];
     471     6484113 :       ind++;
     472     6484113 :       yp[kk] += forcesToApply[ind];
     473     6484113 :       ind++;
     474     6484113 :       zp[kk] += forcesToApply[ind];
     475     6484113 :       ind++;
     476             :     }
     477             :   }
     478             : 
     479      315829 :   setForcesOnCell( forcesToApply, ind );
     480             : }
     481             : 
     482      315917 : void ActionAtomistic::setForcesOnCell(const std::vector<double>& forcesToApply, unsigned& ind) {
     483      315917 :   setForcesOnCell(forcesToApply.data(),forcesToApply.size(),ind);
     484      315917 : }
     485             : 
     486      338924 : void ActionAtomistic::setForcesOnCell(const double* forcesToApply, std::size_t size, unsigned& ind) {
     487     3389240 :   for(unsigned i=0; i<9; ++i) {
     488             :     plumed_dbg_massert( ind<size, "problem setting forces in " + getLabel() );
     489     3050316 :     boxValue->addForce( i, forcesToApply[ind] );
     490     3050316 :     ind++;
     491             :   }
     492      338924 : }
     493             : 
     494           6 : Tensor ActionAtomistic::getVirial() const {
     495           6 :   Tensor vir;
     496          24 :   for(unsigned i=0; i<3; ++i)
     497          72 :     for(unsigned j=0; j<3; ++j) {
     498          54 :       vir[i][j] = boxValue->getForce(3*i+j);
     499             :     }
     500           6 :   return vir;
     501             : }
     502             : 
     503           0 : void ActionAtomistic::readAtomsFromPDB(const PDB& pdb) {
     504             : 
     505           0 :   for(unsigned j=0; j<indexes.size(); j++) {
     506           0 :     if( indexes[j].index()>pdb.size() ) {
     507           0 :       error("there are not enough atoms in the input pdb file");
     508             :     }
     509           0 :     if( pdb.getAtomNumbers()[j].index()!=indexes[j].index() ) {
     510           0 :       error("there are atoms missing in the pdb file");
     511             :     }
     512           0 :     actionPositions[j]=pdb.getPositions()[indexes[j].index()];
     513             :   }
     514           0 :   for(unsigned j=0; j<indexes.size(); j++) {
     515           0 :     charges[j]=pdb.getBeta()[indexes[j].index()];
     516             :   }
     517           0 :   for(unsigned j=0; j<indexes.size(); j++) {
     518           0 :     masses[j]=pdb.getOccupancy()[indexes[j].index()];
     519             :   }
     520           0 : }
     521             : 
     522       15571 : unsigned ActionAtomistic::getTotAtoms()const {
     523             :   unsigned natoms = 0;
     524     6636103 :   for(unsigned i=0; i<xpos.size(); ++i ) {
     525     6620532 :     natoms += xpos[i]->getNumberOfValues();
     526             :   }
     527       15571 :   return natoms;
     528             : }
     529             : 
     530             : namespace {
     531             : /// matmul(rotation,Vector(x,y,z)) written out for the loops on all the atoms below, where it is several times faster.
     532             : /// The sums start from zero and are done in the same order as in matmul, so that the result is identical.
     533       19100 : inline void rotateVector( const Tensor& r, const double x, const double y, const double z,
     534             :                           double& rx, double& ry, double& rz ) {
     535       19100 :   rx=((0.0+r(0,0)*x)+r(0,1)*y)+r(0,2)*z;
     536       19100 :   ry=((0.0+r(1,0)*x)+r(1,1)*y)+r(1,2)*z;
     537       19100 :   rz=((0.0+r(2,0)*x)+r(2,1)*y)+r(2,2)*z;
     538       19100 : }
     539             : }
     540             : 
     541          48 : void ActionAtomistic::translateAllGlobalPositions(const Vector& shift) {
     542          96 :   for(unsigned j=0; j<xpos.size(); ++j) {
     543          48 :     auto & xp=xpos[j]->data;
     544          48 :     auto & yp=ypos[j]->data;
     545          48 :     auto & zp=zpos[j]->data;
     546          48 :     const std::size_t n=xpos[j]->getNumberOfValues();
     547        6384 :     for(std::size_t i=0; i<n; ++i) {
     548        6336 :       xp[i]+=shift[0];
     549        6336 :       yp[i]+=shift[1];
     550        6336 :       zp[i]+=shift[2];
     551             :     }
     552             :   }
     553          48 : }
     554             : 
     555          17 : void ActionAtomistic::rotateAllGlobalPositions(const Tensor& rotation) {
     556          46 :   for(unsigned j=0; j<xpos.size(); ++j) {
     557          29 :     auto & xp=xpos[j]->data;
     558          29 :     auto & yp=ypos[j]->data;
     559          29 :     auto & zp=zpos[j]->data;
     560          29 :     const std::size_t n=xpos[j]->getNumberOfValues();
     561        1635 :     for(std::size_t i=0; i<n; ++i) {
     562             :       double rx, ry, rz;
     563        1606 :       rotateVector( rotation, xp[i], yp[i], zp[i], rx, ry, rz );
     564        1606 :       xp[i]=rx;
     565        1606 :       yp[i]=ry;
     566        1606 :       zp[i]=rz;
     567             :     }
     568             :   }
     569          17 : }
     570             : 
     571          60 : void ActionAtomistic::rotateAllGlobalPositions(const Tensor& rotation, const Vector& shiftBefore, const Vector& shiftAfter) {
     572         144 :   for(unsigned j=0; j<xpos.size(); ++j) {
     573          84 :     auto & xp=xpos[j]->data;
     574          84 :     auto & yp=ypos[j]->data;
     575          84 :     auto & zp=zpos[j]->data;
     576          84 :     const std::size_t n=xpos[j]->getNumberOfValues();
     577        8028 :     for(std::size_t i=0; i<n; ++i) {
     578             :       double rx, ry, rz;
     579        7944 :       rotateVector( rotation, xp[i]-shiftBefore[0], yp[i]-shiftBefore[1], zp[i]-shiftBefore[2], rx, ry, rz );
     580        7944 :       xp[i]=rx+shiftAfter[0];
     581        7944 :       yp[i]=ry+shiftAfter[1];
     582        7944 :       zp[i]=rz+shiftAfter[2];
     583             :     }
     584             :   }
     585          60 : }
     586             : 
     587          48 : Vector ActionAtomistic::getTotalForceOnAllAtoms() const {
     588          48 :   Vector totForce;
     589          96 :   for(unsigned j=0; j<xpos.size(); ++j) {
     590          48 :     const auto & fx=xpos[j]->inputForce;
     591          48 :     const auto & fy=ypos[j]->inputForce;
     592          48 :     const auto & fz=zpos[j]->inputForce;
     593          48 :     const std::size_t n=xpos[j]->getNumberOfValues();
     594        6384 :     for(std::size_t i=0; i<n; ++i) {
     595        6336 :       totForce+=Vector(fx[i],fy[i],fz[i]);
     596             :     }
     597             :   }
     598          48 :   return totForce;
     599             : }
     600             : 
     601          77 : Vector ActionAtomistic::rotateForcesOnAllAtoms(const Tensor& rotation) {
     602          77 :   Vector totForce;
     603         190 :   for(unsigned j=0; j<xpos.size(); ++j) {
     604         113 :     auto & fx=xpos[j]->inputForce;
     605         113 :     auto & fy=ypos[j]->inputForce;
     606         113 :     auto & fz=zpos[j]->inputForce;
     607         113 :     const std::size_t n=xpos[j]->getNumberOfValues();
     608         113 :     if( n>0 ) {
     609         113 :       xpos[j]->hasForce=true;
     610         113 :       ypos[j]->hasForce=true;
     611         113 :       zpos[j]->hasForce=true;
     612             :     }
     613         113 :     double tx=totForce[0], ty=totForce[1], tz=totForce[2];
     614        9663 :     for(std::size_t i=0; i<n; ++i) {
     615             :       double nx, ny, nz;
     616        9550 :       rotateVector( rotation, fx[i], fy[i], fz[i], nx, ny, nz );
     617             :       // the difference is added (as addForce would do) so that the result is identical
     618        9550 :       fx[i]+=nx-fx[i];
     619        9550 :       fy[i]+=ny-fy[i];
     620        9550 :       fz[i]+=nz-fz[i];
     621        9550 :       tx+=nx;
     622        9550 :       ty+=ny;
     623        9550 :       tz+=nz;
     624             :     }
     625         113 :     totForce=Vector(tx,ty,tz);
     626             :   }
     627          77 :   return totForce;
     628             : }
     629             : 
     630      180703 : void ActionAtomistic::makeWhole() {
     631      180703 :   if(actionMoldat && actionMoldat->isWhole()) {
     632             :     // make sure the tree has been constructed
     633          17 :     if(!tree) {
     634          12 :       tree=std::make_unique<Tree>(actionMoldat);
     635           6 :       tree->buildTree(getAbsoluteIndexes());
     636             :     }
     637          17 :     const auto & tree_indexes=tree->getTreeIndexes();
     638          17 :     const auto & root_indexes=tree->getRootIndexes();
     639         300 :     for(unsigned j=0; j<root_indexes.size(); j++) {
     640         283 :       const Vector & first (actionPositions[root_indexes[j]]);
     641         283 :       Vector & second (actionPositions[tree_indexes[j+1]]);
     642         283 :       second=first+pbcDistance(first,second);
     643             :     }
     644      180686 :   } else if(actionPositions.size()>1) {
     645     1554827 :     for(unsigned j=0; j<actionPositions.size()-1; ++j) {
     646             :       const Vector & first (actionPositions[j]);
     647     1374728 :       Vector & second (actionPositions[j+1]);
     648     1374728 :       second=first+pbcDistance(first,second);
     649             :     }
     650             :   }
     651      180703 : }
     652             : 
     653       15002 : void ActionAtomistic::getGradient( const unsigned& ind, Vector& deriv, std::map<AtomNumber,Vector>& gradients ) const {
     654       15002 :   std::size_t nn = atom_value_ind[ind].first;
     655       15002 :   if( nn==0 ) {
     656       14963 :     gradients[indexes[ind]] += deriv;
     657       14963 :     return;
     658             :   }
     659          39 :   xpos[nn]->passGradients( deriv[0], gradients );
     660          39 :   ypos[nn]->passGradients( deriv[1], gradients );
     661          39 :   zpos[nn]->passGradients( deriv[2], gradients );
     662             : }
     663             : 
     664       12048 : void ActionAtomistic::updateUniqueLocal( const bool& useunique, const std::vector<int>& g2l ) {
     665             :   // The flag is set again when the atoms are requested again or when the local atoms change
     666       12048 :   unique_local_needs_update=false;
     667       12048 :   if( useunique ) {
     668        6311 :     unique_local=unique;
     669        6311 :     return;
     670             :   }
     671        5737 :   unique_local.clear();
     672       60739 :   for(auto pp=unique.begin(); pp!=unique.end(); ++pp) {
     673       55002 :     if(g2l[pp->index()]>=0) {
     674       23045 :       unique_local.push_back(*pp);  // already sorted
     675             :     }
     676             :   }
     677             : }
     678             : 
     679             : }

Generated by: LCOV version 1.16