LCOV - code coverage report
Current view: top level - colvar - Distance.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 145 145 100.0 %
Date: 2025-11-25 13:55:50 Functions: 6 7 85.7 %

          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 "Colvar.h"
      23             : #include "ColvarShortcut.h"
      24             : #include "MultiColvarTemplate.h"
      25             : #include "core/ActionRegister.h"
      26             : #include "tools/Pbc.h"
      27             : 
      28             : namespace PLMD {
      29             : namespace colvar {
      30             : 
      31             : //+PLUMEDOC COLVAR DISTANCE
      32             : /*
      33             : Calculate the distance between a pair of atoms.
      34             : 
      35             : By default the distance is computed taking into account periodic
      36             : boundary conditions. This behavior can be changed with the NOPBC flag.
      37             : Moreover, single components in Cartesian space (x,y, and z, with COMPONENTS)
      38             : or single components projected to the three lattice vectors (a,b, and c, with SCALED_COMPONENTS)
      39             : can be also computed.
      40             : 
      41             : Notice that Cartesian components will not have the proper periodicity!
      42             : If you have to study e.g. the permeation of a molecule across a membrane,
      43             : better to use SCALED_COMPONENTS.
      44             : 
      45             : \par Examples
      46             : 
      47             : The following input tells plumed to print the distance between atoms 3 and 5,
      48             : the distance between atoms 2 and 4 and the x component of the distance between atoms 2 and 4.
      49             : \plumedfile
      50             : d1:  DISTANCE ATOMS=3,5
      51             : d2:  DISTANCE ATOMS=2,4
      52             : d2c: DISTANCE ATOMS=2,4 COMPONENTS
      53             : PRINT ARG=d1,d2,d2c.x
      54             : \endplumedfile
      55             : 
      56             : The following input computes the end-to-end distance for a polymer
      57             : of 100 atoms and keeps it at a value around 5.
      58             : \plumedfile
      59             : WHOLEMOLECULES ENTITY0=1-100
      60             : e2e: DISTANCE ATOMS=1,100 NOPBC
      61             : RESTRAINT ARG=e2e KAPPA=1 AT=5
      62             : \endplumedfile
      63             : 
      64             : Notice that NOPBC is used
      65             : to be sure that if the end-to-end distance is larger than half the simulation
      66             : box the distance is compute properly. Also notice that, since many MD
      67             : codes break molecules across cell boundary, it might be necessary to
      68             : use the \ref WHOLEMOLECULES keyword (also notice that it should be
      69             : _before_ distance). The list of atoms provided to \ref WHOLEMOLECULES
      70             : here contains all the atoms between 1 and 100. Strictly speaking, this
      71             : is not necessary. If you know for sure that atoms with difference in
      72             : the index say equal to 10 are _not_ going to be farther than half cell
      73             : you can e.g. use
      74             : \plumedfile
      75             : WHOLEMOLECULES ENTITY0=1,10,20,30,40,50,60,70,80,90,100
      76             : e2e: DISTANCE ATOMS=1,100 NOPBC
      77             : RESTRAINT ARG=e2e KAPPA=1 AT=5
      78             : \endplumedfile
      79             : Just be sure that the ordered list provide to \ref WHOLEMOLECULES has the following
      80             : properties:
      81             : - Consecutive atoms should be closer than half-cell throughout the entire simulation.
      82             : - Atoms required later for the distance (e.g. 1 and 100) should be included in the list
      83             : 
      84             : The following example shows how to take into account periodicity e.g.
      85             : in z-component of a distance
      86             : \plumedfile
      87             : # this is a center of mass of a large group
      88             : c: COM ATOMS=1-100
      89             : # this is the distance between atom 101 and the group
      90             : d: DISTANCE ATOMS=c,101 COMPONENTS
      91             : # this makes a new variable, dd, equal to d and periodic, with domain -10,10
      92             : # this is the right choise if e.g. the cell is orthorombic and its size in
      93             : # z direction is 20.
      94             : dz: COMBINE ARG=d.z PERIODIC=-10,10
      95             : # metadynamics on dd
      96             : METAD ARG=dz SIGMA=0.1 HEIGHT=0.1 PACE=200
      97             : \endplumedfile
      98             : 
      99             : Using SCALED_COMPONENTS this problem should not arise because they are always periodic
     100             : with domain (-0.5,+0.5).
     101             : 
     102             : 
     103             : 
     104             : 
     105             : */
     106             : //+ENDPLUMEDOC
     107             : 
     108             : //+PLUMEDOC MCOLVAR DISTANCE_SCALAR
     109             : /*
     110             : Calculate the distance between a pair of atoms
     111             : 
     112             : \par Examples
     113             : 
     114             : */
     115             : //+ENDPLUMEDOC
     116             : 
     117             : //+PLUMEDOC MCOLVAR DISTANCE_VECTOR
     118             : /*
     119             : Calculate a vector containing the distances between various pairs of atoms
     120             : 
     121             : \par Examples
     122             : 
     123             : */
     124             : //+ENDPLUMEDOC
     125             : 
     126             : class Distance : public Colvar {
     127             :   bool components;
     128             :   bool scaled_components;
     129             :   bool pbc;
     130             : 
     131             :   std::vector<double> value, masses, charges;
     132             :   std::vector<std::vector<Vector> > derivs;
     133             :   std::vector<Tensor> virial;
     134             : public:
     135             :   static void registerKeywords( Keywords& keys );
     136             :   explicit Distance(const ActionOptions&);
     137             :   static void parseAtomList( const int& num, std::vector<AtomNumber>& t, ActionAtomistic* aa );
     138             :   static unsigned getModeAndSetupValues( ActionWithValue* av );
     139             : // active methods:
     140             :   void calculate() override;
     141             :   static void calculateCV( const unsigned& mode, const std::vector<double>& masses, const std::vector<double>& charges,
     142             :                            const std::vector<Vector>& pos, std::vector<double>& vals, std::vector<std::vector<Vector> >& derivs,
     143             :                            std::vector<Tensor>& virial, const ActionAtomistic* aa );
     144             : };
     145             : 
     146             : typedef ColvarShortcut<Distance> DistanceShortcut;
     147             : PLUMED_REGISTER_ACTION(DistanceShortcut,"DISTANCE")
     148             : PLUMED_REGISTER_ACTION(Distance,"DISTANCE_SCALAR")
     149             : typedef MultiColvarTemplate<Distance> DistanceMulti;
     150             : PLUMED_REGISTER_ACTION(DistanceMulti,"DISTANCE_VECTOR")
     151             : 
     152        2380 : void Distance::registerKeywords( Keywords& keys ) {
     153        2380 :   Colvar::registerKeywords( keys );
     154        2380 :   keys.setDisplayName("DISTANCE");
     155        4760 :   keys.add("atoms","ATOMS","the pair of atom that we are calculating the distance between");
     156        4760 :   keys.addFlag("COMPONENTS",false,"calculate the x, y and z components of the distance separately and store them as label.x, label.y and label.z");
     157        4760 :   keys.addFlag("SCALED_COMPONENTS",false,"calculate the a, b and c scaled components of the distance separately and store them as label.a, label.b and label.c");
     158        4760 :   keys.addOutputComponent("x","COMPONENTS","the x-component of the vector connecting the two atoms");
     159        4760 :   keys.addOutputComponent("y","COMPONENTS","the y-component of the vector connecting the two atoms");
     160        4760 :   keys.addOutputComponent("z","COMPONENTS","the z-component of the vector connecting the two atoms");
     161        4760 :   keys.addOutputComponent("a","SCALED_COMPONENTS","the normalized projection on the first lattice vector of the vector connecting the two atoms");
     162        4760 :   keys.addOutputComponent("b","SCALED_COMPONENTS","the normalized projection on the second lattice vector of the vector connecting the two atoms");
     163        4760 :   keys.addOutputComponent("c","SCALED_COMPONENTS","the normalized projection on the third lattice vector of the vector connecting the two atoms");
     164        4760 :   keys.add("hidden","NO_ACTION_LOG","suppresses printing from action on the log");
     165        2380 :   keys.setValueDescription("the DISTANCE between this pair of atoms");
     166        2380 : }
     167             : 
     168         623 : Distance::Distance(const ActionOptions&ao):
     169             :   PLUMED_COLVAR_INIT(ao),
     170         623 :   components(false),
     171         623 :   scaled_components(false),
     172         623 :   pbc(true),
     173         623 :   value(1),
     174         625 :   derivs(1),
     175        1246 :   virial(1) {
     176         623 :   derivs[0].resize(2);
     177             :   std::vector<AtomNumber> atoms;
     178         623 :   parseAtomList(-1,atoms,this);
     179         623 :   if(atoms.size()!=2) {
     180           1 :     error("Number of specified atoms should be 2");
     181             :   }
     182             : 
     183         622 :   bool nopbc=!pbc;
     184         624 :   parseFlag("NOPBC",nopbc);
     185         622 :   pbc=!nopbc;
     186             : 
     187         622 :   if(pbc) {
     188         618 :     log.printf("  using periodic boundary conditions\n");
     189             :   } else {
     190           4 :     log.printf("  without periodic boundary conditions\n");
     191             :   }
     192             : 
     193         622 :   unsigned mode = getModeAndSetupValues( this );
     194         621 :   if(mode==1) {
     195          35 :     components=true;
     196         586 :   } else if(mode==2) {
     197           5 :     scaled_components=true;
     198             :   }
     199         621 :   if( components || scaled_components ) {
     200          40 :     value.resize(3);
     201          40 :     derivs.resize(3);
     202          40 :     virial.resize(3);
     203         160 :     for(unsigned i=0; i<3; ++i) {
     204         120 :       derivs[i].resize(2);
     205             :     }
     206             :   }
     207         621 :   requestAtoms(atoms);
     208         627 : }
     209             : 
     210       30941 : void Distance::parseAtomList( const int& num, std::vector<AtomNumber>& t, ActionAtomistic* aa ) {
     211       61882 :   aa->parseAtomList("ATOMS",num,t);
     212       30941 :   if( t.size()==2 ) {
     213       30832 :     aa->log.printf("  between atoms %d %d\n",t[0].serial(),t[1].serial());
     214             :   }
     215       30941 : }
     216             : 
     217         730 : unsigned Distance::getModeAndSetupValues( ActionWithValue* av ) {
     218             :   bool c;
     219         730 :   av->parseFlag("COMPONENTS",c);
     220             :   bool sc;
     221         730 :   av->parseFlag("SCALED_COMPONENTS",sc);
     222         730 :   if( c && sc ) {
     223           1 :     av->error("COMPONENTS and SCALED_COMPONENTS are not compatible");
     224             :   }
     225             : 
     226         729 :   if(c) {
     227         160 :     av->addComponentWithDerivatives("x");
     228          80 :     av->componentIsNotPeriodic("x");
     229         160 :     av->addComponentWithDerivatives("y");
     230          80 :     av->componentIsNotPeriodic("y");
     231         160 :     av->addComponentWithDerivatives("z");
     232          80 :     av->componentIsNotPeriodic("z");
     233          80 :     av->log<<"  WARNING: components will not have the proper periodicity - see manual\n";
     234          80 :     return 1;
     235         649 :   } else if(sc) {
     236          12 :     av->addComponentWithDerivatives("a");
     237          12 :     av->componentIsPeriodic("a","-0.5","+0.5");
     238          12 :     av->addComponentWithDerivatives("b");
     239          12 :     av->componentIsPeriodic("b","-0.5","+0.5");
     240          12 :     av->addComponentWithDerivatives("c");
     241          12 :     av->componentIsPeriodic("c","-0.5","+0.5");
     242           6 :     return 2;
     243             :   }
     244         643 :   av->addValueWithDerivatives();
     245         643 :   av->setNotPeriodic();
     246             :   return 0;
     247             : }
     248             : 
     249             : // calculator
     250       74406 : void Distance::calculate() {
     251             : 
     252       74406 :   if(pbc) {
     253       74386 :     makeWhole();
     254             :   }
     255             : 
     256       74406 :   if( components ) {
     257         409 :     calculateCV( 1, masses, charges, getPositions(), value, derivs, virial, this );
     258         409 :     Value* valuex=getPntrToComponent("x");
     259         409 :     Value* valuey=getPntrToComponent("y");
     260         409 :     Value* valuez=getPntrToComponent("z");
     261             : 
     262        1227 :     for(unsigned i=0; i<2; ++i) {
     263         818 :       setAtomsDerivatives(valuex,i,derivs[0][i] );
     264             :     }
     265         409 :     setBoxDerivatives(valuex,virial[0]);
     266         409 :     valuex->set(value[0]);
     267             : 
     268        1227 :     for(unsigned i=0; i<2; ++i) {
     269         818 :       setAtomsDerivatives(valuey,i,derivs[1][i] );
     270             :     }
     271         409 :     setBoxDerivatives(valuey,virial[1]);
     272         409 :     valuey->set(value[1]);
     273             : 
     274        1227 :     for(unsigned i=0; i<2; ++i) {
     275         818 :       setAtomsDerivatives(valuez,i,derivs[2][i] );
     276             :     }
     277         409 :     setBoxDerivatives(valuez,virial[2]);
     278         409 :     valuez->set(value[2]);
     279       73997 :   } else if( scaled_components ) {
     280          26 :     calculateCV( 2, masses, charges, getPositions(), value, derivs, virial, this );
     281             : 
     282          26 :     Value* valuea=getPntrToComponent("a");
     283          26 :     Value* valueb=getPntrToComponent("b");
     284          26 :     Value* valuec=getPntrToComponent("c");
     285          78 :     for(unsigned i=0; i<2; ++i) {
     286          52 :       setAtomsDerivatives(valuea,i,derivs[0][i] );
     287             :     }
     288          26 :     valuea->set(value[0]);
     289          78 :     for(unsigned i=0; i<2; ++i) {
     290          52 :       setAtomsDerivatives(valueb,i,derivs[1][i] );
     291             :     }
     292          26 :     valueb->set(value[1]);
     293          78 :     for(unsigned i=0; i<2; ++i) {
     294          52 :       setAtomsDerivatives(valuec,i,derivs[2][i] );
     295             :     }
     296          26 :     valuec->set(value[2]);
     297             :   } else  {
     298       73971 :     calculateCV( 0, masses, charges, getPositions(), value, derivs, virial, this );
     299      221913 :     for(unsigned i=0; i<2; ++i) {
     300      147942 :       setAtomsDerivatives(i,derivs[0][i] );
     301             :     }
     302       73971 :     setBoxDerivatives(virial[0]);
     303       73971 :     setValue           (value[0]);
     304             :   }
     305       74406 : }
     306             : 
     307      406099 : void Distance::calculateCV( const unsigned& mode, const std::vector<double>& masses, const std::vector<double>& charges,
     308             :                             const std::vector<Vector>& pos, std::vector<double>& vals, std::vector<std::vector<Vector> >& derivs,
     309             :                             std::vector<Tensor>& virial, const ActionAtomistic* aa ) {
     310      406099 :   Vector distance=delta(pos[0],pos[1]);
     311      406099 :   const double value=distance.modulo();
     312      406099 :   const double invvalue=1.0/value;
     313             : 
     314      406099 :   if(mode==1) {
     315       94377 :     derivs[0][0] = Vector(-1,0,0);
     316       94377 :     derivs[0][1] = Vector(+1,0,0);
     317       94377 :     vals[0] = distance[0];
     318             : 
     319       94377 :     derivs[1][0] = Vector(0,-1,0);
     320       94377 :     derivs[1][1] = Vector(0,+1,0);
     321       94377 :     vals[1] = distance[1];
     322             : 
     323       94377 :     derivs[2][0] = Vector(0,0,-1);
     324       94377 :     derivs[2][1] = Vector(0,0,+1);
     325       94377 :     vals[2] = distance[2];
     326       94377 :     setBoxDerivativesNoPbc( pos, derivs, virial );
     327      311722 :   } else if(mode==2) {
     328          41 :     Vector d=aa->getPbc().realToScaled(distance);
     329          41 :     derivs[0][0] = matmul(aa->getPbc().getInvBox(),Vector(-1,0,0));
     330          41 :     derivs[0][1] = matmul(aa->getPbc().getInvBox(),Vector(+1,0,0));
     331          41 :     vals[0] = Tools::pbc(d[0]);
     332          41 :     derivs[1][0] = matmul(aa->getPbc().getInvBox(),Vector(0,-1,0));
     333          41 :     derivs[1][1] = matmul(aa->getPbc().getInvBox(),Vector(0,+1,0));
     334          41 :     vals[1] = Tools::pbc(d[1]);
     335          41 :     derivs[2][0] = matmul(aa->getPbc().getInvBox(),Vector(0,0,-1));
     336          41 :     derivs[2][1] = matmul(aa->getPbc().getInvBox(),Vector(0,0,+1));
     337          41 :     vals[2] = Tools::pbc(d[2]);
     338             :   } else {
     339      311681 :     derivs[0][0] = -invvalue*distance;
     340      311681 :     derivs[0][1] = invvalue*distance;
     341      311681 :     setBoxDerivativesNoPbc( pos, derivs, virial );
     342      311681 :     vals[0] = value;
     343             :   }
     344      406099 : }
     345             : 
     346             : }
     347             : }
     348             : 
     349             : 
     350             : 

Generated by: LCOV version 1.16