LCOV - code coverage report
Current view: top level - core - ActionAtomistic.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 210 231 90.9 %
Date: 2026-03-30 13:16:06 Functions: 17 22 77.3 %

          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 <vector>
      27             : #include <string>
      28             : #include "ActionWithValue.h"
      29             : #include "Colvar.h"
      30             : #include "ActionWithVirtualAtom.h"
      31             : #include "tools/Exception.h"
      32             : #include "Atoms.h"
      33             : #include "tools/Pbc.h"
      34             : #include "tools/PDB.h"
      35             : 
      36             : namespace PLMD {
      37             : 
      38       10380 : ActionAtomistic::~ActionAtomistic() {
      39             : // forget the pending request
      40       10380 :   atoms.remove(this);
      41       10380 : }
      42             : 
      43       10380 : ActionAtomistic::ActionAtomistic(const ActionOptions&ao):
      44             :   Action(ao),
      45       10380 :   lockRequestAtoms(false),
      46       10380 :   donotretrieve(false),
      47       10380 :   donotforce(false),
      48       10380 :   atoms(plumed.getAtoms()) {
      49       10380 :   atoms.add(this);
      50             : //  if(atoms.getNatoms()==0) error("Cannot perform calculations involving atoms without atoms");
      51       10380 : }
      52             : 
      53       10982 : void ActionAtomistic::registerKeywords( Keywords& keys ) {
      54             :   (void) keys; // avoid warning
      55       10982 : }
      56             : 
      57             : 
      58       10371 : void ActionAtomistic::requestAtoms(const std::vector<AtomNumber> & a, const bool clearDep) {
      59       10371 :   plumed_massert(!lockRequestAtoms,"requested atom list can only be changed in the prepare() method");
      60       10371 :   int nat=a.size();
      61       10371 :   indexes=a;
      62       10371 :   positions.resize(nat);
      63       10371 :   forces.resize(nat);
      64       10371 :   masses.resize(nat);
      65       10371 :   charges.resize(nat);
      66       10371 :   int n=atoms.positions.size();
      67       10371 :   if(clearDep) {
      68       10251 :     clearDependencies();
      69             :   }
      70       10371 :   unique.clear();
      71     1408480 :   for(unsigned i=0; i<indexes.size(); i++) {
      72     1398110 :     if(indexes[i].index()>=n) {
      73             :       std::string num;
      74           1 :       Tools::convert( indexes[i].serial(),num );
      75           3 :       error("atom " + num + " out of range");
      76             :     }
      77     1398109 :     if(atoms.isVirtualAtom(indexes[i])) {
      78        7225 :       addDependency(atoms.getVirtualAtomsAction(indexes[i]));
      79             :     }
      80             : // only real atoms are requested to lower level Atoms class
      81             :     else {
      82     1390884 :       unique.push_back(indexes[i]);
      83             :     }
      84             :   }
      85       10370 :   Tools::removeDuplicates(unique);
      86       10370 :   updateUniqueLocal();
      87       10370 :   atoms.unique.clear();
      88       10370 : }
      89             : 
      90   186514274 : Vector ActionAtomistic::pbcDistance(const Vector &v1,const Vector &v2)const {
      91   186514274 :   return pbc.distance(v1,v2);
      92             : }
      93             : 
      94      222049 : void ActionAtomistic::pbcApply(std::vector<Vector>& dlist, unsigned max_index)const {
      95      222049 :   pbc.apply(dlist, max_index);
      96      222049 : }
      97             : 
      98         195 : void ActionAtomistic::calculateNumericalDerivatives( ActionWithValue* a ) {
      99         195 :   calculateAtomicNumericalDerivatives( a, 0 );
     100         195 : }
     101             : 
     102           0 : void ActionAtomistic::changeBox( const Tensor& newbox ) {
     103           0 :   pbc.setBox( newbox );
     104           0 : }
     105             : 
     106         493 : void ActionAtomistic::calculateAtomicNumericalDerivatives( ActionWithValue* a, const unsigned& startnum ) {
     107         493 :   if(!a) {
     108         280 :     a=dynamic_cast<ActionWithValue*>(this);
     109         280 :     plumed_massert(a,"only Actions with a value can be differentiated");
     110             :   }
     111             : 
     112         493 :   const size_t nval=a->getNumberOfComponents();
     113         493 :   const size_t natoms=getNumberOfAtoms();
     114         493 :   std::vector<Vector> value(nval*natoms);
     115         493 :   std::vector<Tensor> valuebox(nval);
     116         493 :   std::vector<Vector> savedPositions(natoms);
     117             :   const double delta=std::sqrt(epsilon);
     118             : 
     119       23936 :   for(int i=0; i<natoms; i++)
     120       93772 :     for(int k=0; k<3; k++) {
     121       70329 :       savedPositions[i][k]=positions[i][k];
     122       70329 :       positions[i][k]=positions[i][k]+delta;
     123       70329 :       a->calculate();
     124       70329 :       positions[i][k]=savedPositions[i][k];
     125      211947 :       for(int j=0; j<nval; j++) {
     126      141618 :         value[j*natoms+i][k]=a->getOutputQuantity(j);
     127             :       }
     128             :     }
     129         493 :   Tensor box(pbc.getBox());
     130        1972 :   for(int i=0; i<3; i++)
     131        5916 :     for(int k=0; k<3; k++) {
     132        4437 :       double arg0=box(i,k);
     133      215424 :       for(int j=0; j<natoms; j++) {
     134      210987 :         positions[j]=pbc.realToScaled(positions[j]);
     135             :       }
     136        4437 :       box(i,k)=box(i,k)+delta;
     137        4437 :       pbc.setBox(box);
     138      215424 :       for(int j=0; j<natoms; j++) {
     139      210987 :         positions[j]=pbc.scaledToReal(positions[j]);
     140             :       }
     141        4437 :       a->calculate();
     142        4437 :       box(i,k)=arg0;
     143        4437 :       pbc.setBox(box);
     144      215424 :       for(int j=0; j<natoms; j++) {
     145      210987 :         positions[j]=savedPositions[j];
     146             :       }
     147       19557 :       for(int j=0; j<nval; j++) {
     148       15120 :         valuebox[j](i,k)=a->getOutputQuantity(j);
     149             :       }
     150             :     }
     151             : 
     152         493 :   a->calculate();
     153         493 :   a->clearDerivatives();
     154        2173 :   for(int j=0; j<nval; j++) {
     155        1680 :     Value* v=a->copyOutput(j);
     156             :     double ref=v->get();
     157        1680 :     if(v->hasDerivatives()) {
     158       26188 :       for(int i=0; i<natoms; i++)
     159      101784 :         for(int k=0; k<3; k++) {
     160       76338 :           double d=(value[j*natoms+i][k]-ref)/delta;
     161       76338 :           v->addDerivative(startnum+3*i+k,d);
     162             :         }
     163         742 :       Tensor virial;
     164        2968 :       for(int i=0; i<3; i++)
     165        8904 :         for(int k=0; k<3; k++) {
     166        6678 :           virial(i,k)= (valuebox[j](i,k)-ref)/delta;
     167             :         }
     168             : // BE CAREFUL WITH NON ORTHOROMBIC CELL
     169         742 :       virial=-matmul(box.transpose(),virial);
     170        2968 :       for(int i=0; i<3; i++)
     171        8904 :         for(int k=0; k<3; k++) {
     172        6678 :           v->addDerivative(startnum+3*natoms+3*k+i,virial(k,i));
     173             :         }
     174             :     }
     175             :   }
     176         493 : }
     177             : 
     178       11905 : void ActionAtomistic::parseAtomList(const std::string&key, std::vector<AtomNumber> &t) {
     179       11905 :   parseAtomList(key,-1,t);
     180       11904 : }
     181             : 
     182       14415 : void ActionAtomistic::parseAtomList(const std::string&key,const int num, std::vector<AtomNumber> &t) {
     183       14415 :   plumed_massert( keywords.style(key,"atoms") || keywords.style(key,"hidden"), "keyword " + key + " should be registered as atoms");
     184             :   std::vector<std::string> strings;
     185       14415 :   if( num<0 ) {
     186       11905 :     parseVector(key,strings);
     187       11904 :     if(strings.empty()) {
     188             :       return;
     189             :     }
     190             :   } else {
     191        2510 :     if ( !parseNumberedVector(key,num,strings) ) {
     192             :       return;
     193             :     }
     194             :   }
     195       11713 :   interpretAtomList( strings, t );
     196       14415 : }
     197             : 
     198       11914 : void ActionAtomistic::interpretAtomList(std::vector<std::string>& strings, std::vector<AtomNumber> &t) {
     199       11914 :   Tools::interpretRanges(strings);
     200       11914 :   t.resize(0);
     201      458942 :   for(unsigned i=0; i<strings.size(); ++i) {
     202             :     AtomNumber atom;
     203      447028 :     bool ok=Tools::convertNoexcept(strings[i],atom); // this is converting strings to AtomNumbers
     204      447028 :     if(ok) {
     205      438642 :       t.push_back(atom);
     206             :     }
     207             : // here we check if this is a special symbol for MOLINFO
     208      447028 :     if( !ok && strings[i].compare(0,1,"@")==0 ) {
     209         763 :       std::string symbol=strings[i].substr(1);
     210         763 :       if(symbol=="allatoms") {
     211          10 :         const auto n=plumed.getAtoms().getNatoms() + plumed.getAtoms().getNVirtualAtoms();
     212          10 :         t.reserve(t.size()+n);
     213         365 :         for(unsigned i=0; i<n; i++) {
     214         355 :           t.push_back(AtomNumber::index(i));
     215             :         }
     216             :         ok=true;
     217         753 :       } else if(symbol=="mdatoms") {
     218           4 :         const auto n=plumed.getAtoms().getNatoms();
     219           4 :         t.reserve(t.size()+n);
     220         228 :         for(unsigned i=0; i<n; i++) {
     221         224 :           t.push_back(AtomNumber::index(i));
     222             :         }
     223             :         ok=true;
     224             :       } else {
     225         749 :         auto* moldat=plumed.getActionSet().selectLatest<GenericMolInfo*>(this);
     226         749 :         if( moldat ) {
     227             :           std::vector<AtomNumber> atom_list;
     228         749 :           moldat->interpretSymbol( symbol, atom_list );
     229         749 :           if( atom_list.size()>0 ) {
     230             :             ok=true;
     231         749 :             t.insert(t.end(),atom_list.begin(),atom_list.end());
     232             :           } else {
     233           0 :             error(strings[i] + " is not a label plumed knows");
     234             :           }
     235             :         } else {
     236           0 :           error("atoms specified using @ symbol but no MOLINFO was available");
     237             :         }
     238             :       }
     239             :     }
     240             : // here we check if the atom name is the name of a group
     241      446265 :     if(!ok) {
     242        7623 :       if(atoms.groups.count(strings[i])) {
     243             :         const auto m=atoms.groups.find(strings[i]);
     244         404 :         t.insert(t.end(),m->second.begin(),m->second.end());
     245             :         ok=true;
     246             :       }
     247             :     }
     248             : // here we check if the atom name is the name of an added virtual atom
     249      446624 :     if(!ok) {
     250        7219 :       const ActionSet&actionSet(plumed.getActionSet());
     251     6088236 :       for(const auto & a : actionSet) {
     252     6088236 :         ActionWithVirtualAtom* c=dynamic_cast<ActionWithVirtualAtom*>(a.get());
     253     6088236 :         if(c)
     254     6080098 :           if(c->getLabel()==strings[i]) {
     255             :             ok=true;
     256        7219 :             t.push_back(c->getIndex());
     257             :             break;
     258             :           }
     259             :       }
     260             :     }
     261      439809 :     if(!ok) {
     262           0 :       error("it was not possible to interpret atom name " + strings[i]);
     263             :     }
     264             :     // plumed_massert(ok,"it was not possible to interpret atom name " + strings[i]);
     265             :   }
     266       11914 : }
     267             : 
     268      174709 : void ActionAtomistic::retrieveAtoms() {
     269      174709 :   pbc=atoms.pbc;
     270      174709 :   Colvar*cc=dynamic_cast<Colvar*>(this);
     271      174709 :   if(cc && cc->checkIsEnergy()) {
     272        3989 :     energy=atoms.getEnergy();
     273             :   }
     274      174709 :   if(donotretrieve) {
     275             :     return;
     276             :   }
     277      168753 :   chargesWereSet=atoms.chargesWereSet();
     278             :   const std::vector<Vector> & p(atoms.positions);
     279             :   const std::vector<double> & c(atoms.charges);
     280             :   const std::vector<double> & m(atoms.masses);
     281     4759435 :   for(unsigned j=0; j<indexes.size(); j++) {
     282     4590682 :     positions[j]=p[indexes[j].index()];
     283             :   }
     284     4759435 :   for(unsigned j=0; j<indexes.size(); j++) {
     285     4590682 :     charges[j]=c[indexes[j].index()];
     286             :   }
     287     4759435 :   for(unsigned j=0; j<indexes.size(); j++) {
     288     4590682 :     masses[j]=m[indexes[j].index()];
     289             :   }
     290             : }
     291             : 
     292         864 : void ActionAtomistic::setForcesOnAtoms(const std::vector<double>& forcesToApply, unsigned ind) {
     293         864 :   if(donotforce) {
     294             :     return;
     295             :   }
     296      295850 :   for(unsigned i=0; i<indexes.size(); ++i) {
     297      294986 :     forces[i][0]=forcesToApply[ind];
     298      294986 :     ind++;
     299      294986 :     forces[i][1]=forcesToApply[ind];
     300      294986 :     ind++;
     301      294986 :     forces[i][2]=forcesToApply[ind];
     302      294986 :     ind++;
     303             :   }
     304         864 :   virial(0,0)=forcesToApply[ind];
     305         864 :   ind++;
     306         864 :   virial(0,1)=forcesToApply[ind];
     307         864 :   ind++;
     308         864 :   virial(0,2)=forcesToApply[ind];
     309         864 :   ind++;
     310         864 :   virial(1,0)=forcesToApply[ind];
     311         864 :   ind++;
     312         864 :   virial(1,1)=forcesToApply[ind];
     313         864 :   ind++;
     314         864 :   virial(1,2)=forcesToApply[ind];
     315         864 :   ind++;
     316         864 :   virial(2,0)=forcesToApply[ind];
     317         864 :   ind++;
     318         864 :   virial(2,1)=forcesToApply[ind];
     319         864 :   ind++;
     320         864 :   virial(2,2)=forcesToApply[ind];
     321             :   plumed_dbg_assert( ind+1==forcesToApply.size());
     322             : }
     323             : 
     324      174246 : void ActionAtomistic::applyForces() {
     325      174246 :   if(donotforce) {
     326             :     return;
     327             :   }
     328      168290 :   std::vector<Vector>& f(atoms.forces);
     329      168290 :   Tensor& v(atoms.virial);
     330     4739485 :   for(unsigned j=0; j<indexes.size(); j++) {
     331     4571195 :     f[indexes[j].index()]+=forces[j];
     332             :   }
     333      168290 :   v+=virial;
     334      168290 :   atoms.forceOnEnergy+=forceOnEnergy;
     335      168290 :   if(extraCV.length()>0) {
     336          48 :     atoms.updateExtraCVForce(extraCV,forceOnExtraCV);
     337             :   }
     338             : }
     339             : 
     340      174709 : void ActionAtomistic::clearOutputForces() {
     341      174709 :   virial.zero();
     342      174709 :   if(donotforce) {
     343             :     return;
     344             :   }
     345      168753 :   Tools::set_to_zero(forces);
     346      168753 :   forceOnEnergy=0.0;
     347      168753 :   forceOnExtraCV=0.0;
     348             : }
     349             : 
     350             : 
     351           0 : void ActionAtomistic::readAtomsFromPDB(const PDB& pdb) {
     352           0 :   Colvar*cc=dynamic_cast<Colvar*>(this);
     353           0 :   if(cc && cc->checkIsEnergy()) {
     354           0 :     error("can't read energies from pdb files");
     355             :   }
     356             : 
     357           0 :   for(unsigned j=0; j<indexes.size(); j++) {
     358           0 :     if( indexes[j].index()>pdb.size() ) {
     359           0 :       error("there are not enough atoms in the input pdb file");
     360             :     }
     361           0 :     if( pdb.getAtomNumbers()[j].index()!=indexes[j].index() ) {
     362           0 :       error("there are atoms missing in the pdb file");
     363             :     }
     364           0 :     positions[j]=pdb.getPositions()[indexes[j].index()];
     365             :   }
     366           0 :   for(unsigned j=0; j<indexes.size(); j++) {
     367           0 :     charges[j]=pdb.getBeta()[indexes[j].index()];
     368             :   }
     369           0 :   for(unsigned j=0; j<indexes.size(); j++) {
     370           0 :     masses[j]=pdb.getOccupancy()[indexes[j].index()];
     371             :   }
     372           0 : }
     373             : 
     374      107745 : void ActionAtomistic::makeWhole() {
     375     1160578 :   for(unsigned j=0; j<positions.size()-1; ++j) {
     376             :     const Vector & first (positions[j]);
     377     1052833 :     Vector & second (positions[j+1]);
     378     1052833 :     second=first+pbcDistance(first,second);
     379             :   }
     380      107745 : }
     381             : 
     382       20022 : void ActionAtomistic::updateUniqueLocal() {
     383       20022 :   if(atoms.dd && atoms.shuffledAtoms>0) {
     384        9706 :     unique_local.clear();
     385       91318 :     for(auto pp=unique.begin(); pp!=unique.end(); ++pp) {
     386       81612 :       if(atoms.g2l[pp->index()]>=0) {
     387       32944 :         unique_local.push_back(*pp);  // already sorted
     388             :       }
     389             :     }
     390             :   } else {
     391       10316 :     unique_local=unique; // copy
     392             :   }
     393       20022 : }
     394             : 
     395             : }

Generated by: LCOV version 1.16