LCOV - code coverage report
Current view: top level - colvar - Dimer.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 87 94 92.6 %
Date: 2026-11-05 10:19:04 Functions: 4 5 80.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2017-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 "core/ActionRegister.h"
      24             : #include "core/PlumedMain.h"
      25             : #include "tools/Communicator.h"
      26             : 
      27             : namespace PLMD {
      28             : namespace colvar {
      29             : 
      30             : //+PLUMEDOC COLVAR DIMER
      31             : /*
      32             : This CV computes the dimer interaction energy for a collection of dimers.
      33             : 
      34             : Each dimer represents an atom, as described in the dimer paper that is referenced in the bibliography
      35             : A system of N atoms is thus represented with N dimers, each
      36             : Dimer being composed of two beads and a virtual site representing its center of mass.
      37             : 
      38             : A typical configuration for a dimerized system has the following ordering of atoms:
      39             : 
      40             : ````
      41             : 1    TAG1 X Y Z          N atoms representing the first bead of each Dimer
      42             : 
      43             : 2    TAG2 X Y Z
      44             : 
      45             : ...
      46             : 
      47             : N    TAGN X Y Z          N atoms representing the second bead of each Dimer
      48             : 
      49             : N+1  TAG1 X Y Z
      50             : 
      51             : N+2  TAG2 X Y Z
      52             : 
      53             : ...
      54             : 
      55             : 2N   TAGN X Y Z          Optional: N atoms representing the center of mass of each Dimer
      56             : 
      57             : 2N+1 TAG1 X Y Z
      58             : 
      59             : 2N+2 TAG2 X Y Z
      60             : 
      61             : ...
      62             : 
      63             : 3N   TAGN X Y Z          The configuration might go on with un-dimerized atoms (like a solvent)
      64             : 
      65             : 3N+1
      66             : 
      67             : 3N+2
      68             : 
      69             : ...
      70             : ````
      71             : 
      72             : The Dimer interaction energy is defined between atoms x and N+x, for x=1,...,N and is
      73             : characterized by two parameters Q and DSIGMA. These are passed as mandatory arguments along with
      74             : the temperature of the system.
      75             : 
      76             : ## Examples
      77             : 
      78             : This line tells Plumed to compute the Dimer interaction energy for every dimer in the system.
      79             : 
      80             : ```plumed
      81             : dim: DIMER TEMP=300 Q=0.5 ALLATOMS DSIGMA=0.002
      82             : ```
      83             : 
      84             : If the simulation doesn't use virtual sites for the dimers centers of mass,
      85             : Plumed has to know in order to determine correctly the total number of dimers from
      86             : the total number of atoms:
      87             : 
      88             : ```plumed
      89             : dim: DIMER TEMP=300 Q=0.5 ALLATOMS DSIGMA=0.002 NOVSITES
      90             : ```
      91             : 
      92             : The NOVSITES flag is not required if one provides the atom serials of each Dimer. These are
      93             : defined through two lists of atoms provided __instead__ of the ALLATOMS keyword.
      94             : For example, the Dimer interaction energy of dimers specified by beads (1;23),(5;27),(7;29) is:
      95             : 
      96             : ```plumed
      97             : dim: DIMER TEMP=300 Q=0.5 ATOMS1=1,5,7 ATOMS2=23,27,29 DSIGMA=0.002
      98             : ```
      99             : 
     100             : Note that the ATOMS1,ATOMS2 keywords can support atom groups and
     101             : interval notation as defined in [GROUP](GROUP.md).
     102             : 
     103             : In a Replica Exchange simulations the keyword DSIGMA can be used in two ways:
     104             : if a plumed.n.dat file is provided for each replica, then DSIGMA is passed as a single value,
     105             : like in the previous examples, and each replica will read its own DSIGMA value. If
     106             : a unique plumed.dat is given, DSIGMA has to be a list containing a value for each replica.
     107             : For 4 replicas:
     108             : 
     109             : ```plumed
     110             : #SETTINGS NREPLICAS=4
     111             : dim: DIMER TEMP=300 Q=0.5 ATOMS1=1,5,7 ATOMS2=23,27,29 DSIGMA=0.002,0.002,0.004,0.01
     112             : ```
     113             : 
     114             : ## Using the CV
     115             : 
     116             : The dimer interaction is not coded in the driver program and has to be inserted
     117             : in the Hamiltonian of the system as a linear RESTRAINT (see [RESTRAINT](RESTRAINT.md)):
     118             : 
     119             : ```plumed
     120             : dim: DIMER TEMP=300 Q=0.5 ALLATOMS DSIGMA=0.002
     121             : RESTRAINT ARG=dim AT=0 KAPPA=0 SLOPE=1 LABEL=dimforces
     122             : ```
     123             : 
     124             : In a replica exchange, Metadynamics (see [METAD](METAD.md)) can be used on the Dimer CV to reduce
     125             : the number of replicas. Just keep in mind that METAD SIGMA values should be tuned
     126             : in the standard way for each replica according to the value of DSIGMA.
     127             : */
     128             : //+ENDPLUMEDOC
     129             : 
     130             : class Dimer : public Colvar {
     131             : public:
     132             :   static void registerKeywords( Keywords& keys);
     133             :   explicit Dimer(const ActionOptions&);
     134             :   void calculate() override;
     135             : protected:
     136             :   bool trimer,useall;
     137             :   unsigned myrank;
     138             :   unsigned nranks;
     139             :   double qexp,temperature,beta,dsigma;
     140             :   std::vector<double> dsigmas;
     141             : private:
     142             :   void consistencyCheck();
     143             :   std::vector<AtomNumber> usedatoms1;
     144             :   std::vector<AtomNumber> usedatoms2;
     145             : 
     146             : };
     147             : 
     148             : PLUMED_REGISTER_ACTION(Dimer, "DIMER")
     149             : 
     150             : 
     151             : 
     152          10 : void Dimer::registerKeywords( Keywords& keys) {
     153          10 :   Colvar::registerKeywords(keys);
     154             : 
     155          10 :   keys.add("compulsory","DSIGMA","The interaction strength of the dimer bond.");
     156          10 :   keys.add("compulsory", "Q", "The exponent of the dimer potential.");
     157          10 :   keys.add("compulsory", "TEMP", "The temperature (in Kelvin) of the simulation.");
     158          10 :   keys.add("atoms", "ATOMS1", "The list of atoms representing the first bead of each Dimer being considered by this CV. Used if ALLATOMS flag is missing");
     159          10 :   keys.add("atoms", "ATOMS2", "The list of atoms representing the second bead of each Dimer being considered by this CV. Used if ALLATOMS flag is missing");
     160          10 :   keys.addFlag("ALLATOMS", false, "Use EVERY atom of the system. Overrides ATOMS keyword.");
     161          10 :   keys.addFlag("NOVSITES", false, "If present the configuration is without virtual sites at the centroid positions.");
     162          20 :   keys.setValueDescription("scalar","the dimer interaction energy");
     163          10 :   keys.addDOI("10.1021/acs.jctc.6b00691");
     164          10 : }
     165             : 
     166             : 
     167             : 
     168           6 : Dimer::Dimer(const ActionOptions& ao):
     169           6 :   PLUMED_COLVAR_INIT(ao) {
     170             : 
     171           6 :   log<<" Bibliography "<<plumed.cite("M Nava, F. Palazzesi, C. Perego and M. Parrinello, J. Chem. Theory Comput. 13, 425(2017)")<<"\n";
     172           6 :   parseVector("DSIGMA",dsigmas);
     173           6 :   parse("Q",qexp);
     174           6 :   parse("TEMP",temperature);
     175             : 
     176             : 
     177           6 :   std::vector<AtomNumber> atoms;
     178           6 :   parseFlag("ALLATOMS",useall);
     179           6 :   trimer=true;
     180             :   bool notrim;
     181           6 :   parseFlag("NOVSITES",notrim);
     182           6 :   trimer=!notrim;
     183             : 
     184           6 :   nranks=getNumberOfReplicas();
     185           6 :   myrank=getReplicaIndex();
     186             :   // this check must be done before dsigmas is indexed with myrank
     187           6 :   if(dsigmas.size()!=nranks && dsigmas.size()!=1) {
     188           0 :     error("Mismatch between provided sigmas and number of replicas");
     189             :   }
     190           6 :   if(dsigmas.size()==1) {
     191           2 :     dsigma=dsigmas[0];
     192             :   } else {
     193           4 :     dsigma=dsigmas[myrank];
     194             :   }
     195             : 
     196             : 
     197             : 
     198             : 
     199           6 :   if(useall) {
     200             :     // go with every atom in the system but not the virtuals...
     201             :     int natoms;
     202           1 :     if(trimer) {
     203           1 :       natoms= 2*getTotAtoms()/3;
     204             :     } else {
     205           0 :       natoms=getTotAtoms()/2;
     206             :     }
     207             : 
     208          45 :     for(unsigned int i=0; i<((unsigned int)natoms); i++) {
     209             :       AtomNumber ati;
     210             :       ati.setIndex(i);
     211          44 :       atoms.push_back(ati);
     212             :     }
     213             :   } else { // serials for the first beads of each dimer are given
     214           5 :     parseAtomList("ATOMS1",usedatoms1);
     215          10 :     parseAtomList("ATOMS2",usedatoms2);
     216             : 
     217           5 :     unsigned int isz1 = usedatoms1.size();
     218             : 
     219          25 :     for(unsigned int i=0; i<isz1; i++) {
     220             :       AtomNumber ati;
     221          20 :       ati.setIndex(usedatoms1[i].index());
     222          20 :       atoms.push_back(ati);
     223             :     }
     224             : 
     225           5 :     unsigned int isz2 = usedatoms2.size();
     226          25 :     for(unsigned int i=0; i<isz2; i++) {
     227             :       AtomNumber atip2;
     228          20 :       atip2.setIndex(usedatoms2[i].index());
     229          20 :       atoms.push_back(atip2);
     230             :     }
     231             : 
     232             :   }
     233           6 :   consistencyCheck();
     234           6 :   checkRead();
     235           6 :   beta = 1./(kBoltzmann*temperature);
     236             : 
     237           6 :   addValueWithDerivatives();  // allocate
     238           6 :   requestAtoms(atoms);
     239           6 :   setNotPeriodic();
     240           6 : }
     241             : 
     242          16 : void Dimer::calculate() {
     243          16 :   double cv_val=0;
     244          16 :   Tensor virial;
     245          16 :   std::vector<Vector> derivatives;
     246             :   const std::vector<Vector>& my_pos=getPositions();
     247          16 :   int atms = my_pos.size();
     248          16 :   std::vector<Vector> der_b2;
     249          98 :   for(int i=0; i<atms/2; i++) {
     250             :     Vector dist;
     251          82 :     dist = pbcDistance(my_pos[i],my_pos[i+atms/2]);
     252             :     double distquad=0;
     253         328 :     for(int j=0; j<3; j++) {
     254         246 :       distquad += dist[j]*dist[j];
     255             :     }
     256             : 
     257          82 :     double dsigquad = dsigma*dsigma;
     258          82 :     double fac1 = 1.0 + distquad/(2*qexp*dsigquad);
     259          82 :     double fac1qm1 = std::pow(fac1,qexp-1);
     260             : 
     261             : 
     262          82 :     cv_val += (fac1*fac1qm1-1.0)/beta;
     263          82 :     Vector der_val;
     264          82 :     Vector mder_val;
     265         328 :     for(int j=0; j<3; j++) {
     266         246 :       der_val[j] = -fac1qm1*dist[j]/(dsigquad*beta);
     267         246 :       mder_val[j]=-der_val[j];
     268             :     }
     269          82 :     derivatives.push_back(der_val);
     270          82 :     der_b2.push_back(mder_val);
     271             : 
     272             :     // virial part: each dimer contributes -x_{ij}*ds/dx_{ij}  (s is the CV)
     273          82 :     double dfunc = fac1qm1/(beta*dsigquad);
     274             :     Vector dd(dfunc*dist);
     275          82 :     Tensor vv(dd,dist);
     276             :     virial -= vv;
     277             : 
     278             :   }
     279             : 
     280          16 :   derivatives.insert(derivatives.end(), der_b2.begin(), der_b2.end());
     281             : 
     282         180 :   for(unsigned int i=0; i<derivatives.size(); i++) {
     283         164 :     setAtomsDerivatives(i,derivatives[i]);
     284             :   }
     285             : 
     286          16 :   setValue(cv_val);
     287          16 :   setBoxDerivatives(virial);
     288             : 
     289          16 : }
     290             : 
     291             : 
     292             : 
     293             : /*****************
     294             : There are some conditions that a valid input should satisfy.
     295             : These are checked here and PLUMED error handlers are (eventually) called.
     296             : ******************/
     297           6 : void Dimer::consistencyCheck() {
     298           6 :   if(!useall && usedatoms1.size()!=usedatoms2.size()) {
     299           0 :     error("The provided atom lists are of different sizes.");
     300             :   }
     301             : 
     302           6 :   if(qexp<0.5 || qexp>1) {
     303           0 :     warning("Dimer CV is meant to be used with q-exponents between 0.5 and 1. We are not responsible for any black hole. :-)");
     304             :   }
     305             : 
     306           6 :   if(dsigma<0) {
     307           0 :     error("Please use positive sigma values for the Dimer strength constant");
     308             :   }
     309             : 
     310           6 :   if(temperature<0) {
     311           0 :     error("Please, use a positive value for the temperature...");
     312             :   }
     313             : 
     314             :   // if dsigmas has only one element means that either
     315             :   // you are using different plumed.x.dat files or a plumed.dat with a single replica
     316           6 :   if(dsigmas.size()!=nranks && dsigmas.size()!=1) {
     317           0 :     error("Mismatch between provided sigmas and number of replicas");
     318             :   }
     319             : 
     320           6 : }
     321             : 
     322             : 
     323             : }
     324             : }
     325             : 

Generated by: LCOV version 1.16