LCOV - code coverage report
Current view: top level - vatom - Center.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 103 104 99.0 %
Date: 2021-11-18 15:22:58 Functions: 13 14 92.9 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-2020 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 "ActionWithVirtualAtom.h"
      23             : #include "ActionRegister.h"
      24             : #include "core/PlumedMain.h"
      25             : #include "core/Atoms.h"
      26             : #include <cmath>
      27             : 
      28             : using namespace std;
      29             : 
      30             : namespace PLMD {
      31             : namespace vatom {
      32             : 
      33             : //+PLUMEDOC VATOM CENTER
      34             : /*
      35             : Calculate the center for a group of atoms, with arbitrary weights.
      36             : 
      37             : The computed
      38             : center is stored as a virtual atom that can be accessed in
      39             : an atom list through the label for the CENTER action that creates it.
      40             : Notice that the generated virtual atom has charge equal to the sum of the
      41             : charges and mass equal to the sum of the masses. If used with the MASS flag,
      42             : then it provides a result identical to \ref COM.
      43             : 
      44             : When running with periodic boundary conditions, the atoms should be
      45             : in the proper periodic image. This is done automatically since PLUMED 2.2,
      46             : by considering the ordered list of atoms and rebuilding the molecule using a procedure
      47             : that is equivalent to that done in \ref WHOLEMOLECULES . Notice that
      48             : rebuilding is local to this action. This is different from \ref WHOLEMOLECULES
      49             : which actually modifies the coordinates stored in PLUMED.
      50             : 
      51             : In case you want to recover the old behavior you should use the NOPBC flag.
      52             : In that case you need to take care that atoms are in the correct
      53             : periodic image.
      54             : 
      55             : \note As an experimental feature, CENTER also supports a keyword PHASES.
      56             : This keyword finds the center of mass for sets of atoms that have been split by the period boundaries by computing scaled coordinates and average
      57             : trigonometric functions, similarly to \ref CENTER_OF_MULTICOLVAR.
      58             : Notice that by construction this center position is
      59             : not invariant with respect to rotations of the atoms at fixed cell lattice.
      60             : In addition, for symmetric Bravais lattices, it is not invariant with respect
      61             : to special symmetries. E.g., if you have an hexagonal cell, the center will
      62             : not be invariant with respect to rotations of 120 degrees.
      63             : On the other hand, it might make the treatment of PBC easier in difficult cases.
      64             : 
      65             : \par Examples
      66             : 
      67             : \plumedfile
      68             : # a point which is on the line connecting atoms 1 and 10, so that its distance
      69             : # from 10 is twice its distance from 1:
      70             : c1: CENTER ATOMS=1,1,10
      71             : # this is another way of stating the same:
      72             : c1bis: CENTER ATOMS=1,10 WEIGHTS=2,1
      73             : 
      74             : # center of mass among these atoms:
      75             : c2: CENTER ATOMS=2,3,4,5 MASS
      76             : 
      77             : d1: DISTANCE ATOMS=c1,c2
      78             : 
      79             : PRINT ARG=d1
      80             : \endplumedfile
      81             : 
      82             : */
      83             : //+ENDPLUMEDOC
      84             : 
      85             : //+PLUMEDOC VATOM COM
      86             : /*
      87             : Calculate the center of mass for a group of atoms.
      88             : 
      89             : The computed
      90             : center of mass is stored as a virtual atom that can be accessed in
      91             : an atom list through the label for the COM action that creates it.
      92             : 
      93             : For arbitrary weights (e.g. geometric center) see \ref CENTER.
      94             : 
      95             : When running with periodic boundary conditions, the atoms should be
      96             : in the proper periodic image. This is done automatically since PLUMED 2.2,
      97             : by considering the ordered list of atoms and rebuilding the molecule using a procedure
      98             : that is equivalent to that done in \ref WHOLEMOLECULES . Notice that
      99             : rebuilding is local to this action. This is different from \ref WHOLEMOLECULES
     100             : which actually modifies the coordinates stored in PLUMED.
     101             : 
     102             : In case you want to recover the old behavior you should use the NOPBC flag.
     103             : In that case you need to take care that atoms are in the correct
     104             : periodic image.
     105             : 
     106             : \par Examples
     107             : 
     108             : The following input instructs plumed to print the distance between the
     109             : center of mass for atoms 1,2,3,4,5,6,7 and that for atoms 15,20:
     110             : \plumedfile
     111             : c1: COM ATOMS=1-7
     112             : c2: COM ATOMS=15,20
     113             : d1: DISTANCE ATOMS=c1,c2
     114             : PRINT ARG=d1
     115             : \endplumedfile
     116             : 
     117             : */
     118             : //+ENDPLUMEDOC
     119             : 
     120             : 
     121         528 : class Center:
     122             :   public ActionWithVirtualAtom
     123             : {
     124             :   std::vector<double> weights;
     125             :   std::vector<Tensor> dcenter_sin;
     126             :   std::vector<Tensor> dcenter_cos;
     127             :   bool weight_mass;
     128             :   bool nopbc;
     129             :   bool first;
     130             :   bool phases;
     131             : public:
     132             :   explicit Center(const ActionOptions&ao);
     133             :   void calculate();
     134             :   static void registerKeywords( Keywords& keys );
     135             : };
     136             : 
     137        7580 : PLUMED_REGISTER_ACTION(Center,"CENTER")
     138        7486 : PLUMED_REGISTER_ACTION(Center,"COM")
     139             : 
     140         180 : void Center::registerKeywords(Keywords& keys) {
     141         180 :   ActionWithVirtualAtom::registerKeywords(keys);
     142         720 :   keys.add("optional","WEIGHTS","Center is computed as a weighted average.");
     143         540 :   keys.addFlag("NOPBC",false,"ignore the periodic boundary conditions when calculating distances");
     144         540 :   keys.addFlag("MASS",false,"If set center is mass weighted");
     145         540 :   keys.addFlag("PHASES",false,"Compute center using trigonometric phases");
     146         180 : }
     147             : 
     148         178 : Center::Center(const ActionOptions&ao):
     149             :   Action(ao),
     150             :   ActionWithVirtualAtom(ao),
     151             :   weight_mass(false),
     152             :   nopbc(false),
     153             :   first(true),
     154         358 :   phases(false)
     155             : {
     156             :   vector<AtomNumber> atoms;
     157         356 :   parseAtomList("ATOMS",atoms);
     158         178 :   if(atoms.size()==0) error("at least one atom should be specified");
     159         356 :   parseVector("WEIGHTS",weights);
     160         356 :   parseFlag("MASS",weight_mass);
     161         356 :   parseFlag("NOPBC",nopbc);
     162         356 :   parseFlag("PHASES",phases);
     163         178 :   if( getName()=="COM") weight_mass=true;
     164         178 :   checkRead();
     165         178 :   log.printf("  of atoms:");
     166        6635 :   for(unsigned i=0; i<atoms.size(); ++i) {
     167        2093 :     if(i%25==0) log<<"\n";
     168        4186 :     log.printf(" %d",atoms[i].serial());
     169             :   }
     170         178 :   log<<"\n";
     171         178 :   if(weight_mass) {
     172          68 :     log<<"  mass weighted\n";
     173          69 :     if(weights.size()!=0) error("WEIGHTS and MASS keywords should not be used simultaneously");
     174             :   } else {
     175         110 :     if( weights.size()==0) {
     176         105 :       log<<" using the geometric center\n";
     177         105 :       weights.resize( atoms.size() );
     178        3801 :       for(unsigned i=0; i<atoms.size(); i++) weights[i] = 1.;
     179             :     } else {
     180           5 :       log<<" with weights:";
     181           6 :       if( weights.size()!=atoms.size() ) error("number of elements in weight vector does not match the number of atoms");
     182          86 :       for(unsigned i=0; i<weights.size(); ++i) {
     183          26 :         if(i%25==0) log<<"\n";
     184          52 :         log.printf(" %f",weights[i]);
     185             :       }
     186           4 :       log.printf("\n");
     187             :     }
     188             :   }
     189         176 :   if(phases) {
     190           3 :     log<<"  Phases will be used to take into account PBC\n";
     191         173 :   } else if(nopbc) {
     192          45 :     log<<"  PBC will be ignored\n";
     193             :   } else {
     194         128 :     log<<"  broken molecules will be rebuilt assuming atoms are in the proper order\n";
     195             :   }
     196         176 :   requestAtoms(atoms);
     197         176 : }
     198             : 
     199        6599 : void Center::calculate() {
     200        6599 :   Vector pos;
     201             :   double mass(0.0);
     202        6599 :   const bool dophases=(getPbc().isSet() ? phases : false);
     203             : 
     204        6599 :   if(!nopbc && !dophases) makeWhole();
     205             : 
     206        6599 :   if( first && weight_mass) {
     207        1236 :     for(unsigned i=0; i<getNumberOfAtoms(); i++) {
     208         589 :       if(std::isnan(getMass(i))) {
     209           0 :         error(
     210             :           "You are trying to compute a CENTER or COM but masses are not known.\n"
     211             :           "        If you are using plumed driver, please use the --mc option"
     212             :         );
     213             :       }
     214             :     }
     215          58 :     first=false;
     216             :   }
     217             : 
     218        6599 :   vector<Tensor> deriv(getNumberOfAtoms());
     219      105429 :   for(unsigned i=0; i<getNumberOfAtoms(); i++) mass+=getMass(i);
     220       13198 :   if( plumed.getAtoms().chargesWereSet() ) {
     221             :     double charge(0.0);
     222       23399 :     for(unsigned i=0; i<getNumberOfAtoms(); i++) charge+=getCharge(i);
     223             :     setCharge(charge);
     224             :   } else {
     225             :     setCharge(0.0);
     226             :   }
     227             :   double wtot=0.0;
     228       61084 :   for(unsigned i=0; i<weights.size(); i++) wtot+=weights[i];
     229             : 
     230        6599 :   if(dophases) {
     231         456 :     dcenter_sin.resize(getNumberOfAtoms());
     232         456 :     dcenter_cos.resize(getNumberOfAtoms());
     233         228 :     Vector center_sin;
     234         228 :     Vector center_cos;
     235         228 :     Tensor invbox2pi=2*pi*getPbc().getInvBox();
     236         228 :     Tensor box2pi=getPbc().getBox() / (2*pi);
     237        1596 :     for(unsigned i=0; i<getNumberOfAtoms(); ++i) {
     238             :       double w=0;
     239         684 :       if(weight_mass) w=getMass(i)/mass;
     240        1368 :       else w=weights[i]/wtot;
     241             : 
     242             : // real to scaled
     243        1368 :       const Vector scaled=matmul(getPosition(i),invbox2pi);
     244             :       const Vector ccos(
     245         684 :         w*std::cos(scaled[0]),
     246         684 :         w*std::cos(scaled[1]),
     247         684 :         w*std::cos(scaled[2])
     248        2052 :       );
     249             :       const Vector csin(
     250         684 :         w*std::sin(scaled[0]),
     251         684 :         w*std::sin(scaled[1]),
     252         684 :         w*std::sin(scaled[2])
     253        2052 :       );
     254         684 :       center_cos+=ccos;
     255         684 :       center_sin+=csin;
     256        8892 :       for(unsigned l=0; l<3; l++) for(unsigned k=0; k<3; k++) {
     257             : // k over real coordinates
     258             : // l over scaled coordinates
     259       12312 :           dcenter_sin[i][l][k]=ccos[l]*invbox2pi[k][l];
     260       12312 :           dcenter_cos[i][l][k]=-csin[l]*invbox2pi[k][l];
     261             :         }
     262             :     }
     263             :     const Vector c(
     264         456 :       std::atan2(center_sin[0],center_cos[0]),
     265         456 :       std::atan2(center_sin[1],center_cos[1]),
     266         456 :       std::atan2(center_sin[2],center_cos[2])
     267        1368 :     );
     268             : 
     269             : // normalization is convenient for doing derivatives later
     270        1596 :     for(unsigned l=0; l<3; l++) {
     271         684 :       double norm=1.0/(center_sin[l]*center_sin[l]+center_cos[l]*center_cos[l]);
     272         684 :       center_sin[l]*=norm;
     273         684 :       center_cos[l]*=norm;
     274             :     }
     275             : 
     276        1596 :     for(unsigned i=0; i<getNumberOfAtoms(); ++i) {
     277         684 :       Tensor dd;
     278        8892 :       for(unsigned l=0; l<3; l++) for(unsigned k=0; k<3; k++) {
     279             : // k over real coordinates
     280             : // l over scaled coordinates
     281       18468 :           dd[l][k]= (center_cos[l]*dcenter_sin[i][l][k] - center_sin[l]*dcenter_cos[i][l][k]);
     282             :         }
     283             : // scaled to real
     284        1368 :       deriv[i]=matmul(dd,box2pi);
     285             :     }
     286             :     setMass(mass);
     287             :     setAtomsDerivatives(deriv);
     288             : // scaled to real
     289         456 :     setPosition(matmul(c,box2pi));
     290             :   } else {
     291      103833 :     for(unsigned i=0; i<getNumberOfAtoms(); i++) {
     292             :       double w=0;
     293       82184 :       if(weight_mass) w=getMass(i)/mass;
     294       30556 :       else w=weights[i]/wtot;
     295       97462 :       pos+=w*getPosition(i);
     296       97462 :       deriv[i]=w*Tensor::identity();
     297             :     }
     298             :     setPosition(pos);
     299             :     setMass(mass);
     300             :     setAtomsDerivatives(deriv);
     301             :   }
     302        6599 : }
     303             : 
     304             : }
     305        5517 : }

Generated by: LCOV version 1.14