LCOV - code coverage report
Current view: top level - generic - EffectiveEnergyDrift.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 183 195 93.8 %
Date: 2026-11-05 10:19:04 Functions: 7 9 77.8 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2013-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             : 
      23             : /*
      24             :  This class was originally written by Marco Jacopo Ferrarotti
      25             :  (marco.ferrarotti@gmail.com) and Giovanni Bussi
      26             : */
      27             : 
      28             : #include "core/Action.h"
      29             : #include "core/ActionPilot.h"
      30             : #include "core/ActionWithValue.h"
      31             : #include "core/ActionSet.h"
      32             : #include "core/ActionRegister.h"
      33             : #include "core/DomainDecomposition.h"
      34             : #include "core/ActionToPutData.h"
      35             : #include "core/PbcAction.h"
      36             : #include "core/PlumedMain.h"
      37             : 
      38             : #include "tools/File.h"
      39             : #include "tools/Pbc.h"
      40             : #include "tools/OpenMP.h"
      41             : 
      42             : #include <algorithm>
      43             : 
      44             : namespace PLMD {
      45             : namespace generic {
      46             : 
      47             : //+PLUMEDOC GENERIC EFFECTIVE_ENERGY_DRIFT
      48             : /*
      49             : Print the effective energy drift
      50             : 
      51             : The method that is used to calculate the effective energy drift here is described in the
      52             : paper in the bibliography.
      53             : 
      54             : ## Examples
      55             : 
      56             : This input is to monitor the effective energy drift for a metadynamics
      57             : simulation on the Debye-Huckel energy. Since this variable is very expensive,
      58             : it could be conveniently computed every second step.
      59             : 
      60             : ```plumed
      61             : dh: DHENERGY GROUPA=1-10 GROUPB=11-20 EPSILON=80.0 I=0.1 TEMP=300.0
      62             : METAD ARG=dh HEIGHT=0.5 SIGMA=0.1 PACE=500 STRIDE=2
      63             : EFFECTIVE_ENERGY_DRIFT PRINT_STRIDE=100 FILE=eff
      64             : ```
      65             : 
      66             : This exampls shows how to monitor if a restraint is too stiff
      67             : 
      68             : ```plumed
      69             : d: DISTANCE ATOMS=10,20
      70             : RESTRAINT ARG=d KAPPA=100000 AT=0.6
      71             : EFFECTIVE_ENERGY_DRIFT PRINT_STRIDE=100 FILE=eff
      72             : ```
      73             : 
      74             : */
      75             : //+ENDPLUMEDOC
      76             : 
      77             : 
      78             : class EffectiveEnergyDrift:
      79             :   public ActionPilot {
      80             :   OFile output;
      81             :   long long int printStride;
      82             :   std::string fmt;
      83             : 
      84             :   double eed;
      85             : 
      86             :   std::vector<ActionWithValue*> biases;
      87             : 
      88             :   unsigned nLocalAtoms;
      89             :   unsigned pNLocalAtoms;
      90             :   std::vector<int> pGatindex;
      91             :   std::vector<double> xpositions;
      92             :   std::vector<double> ypositions;
      93             :   std::vector<double> zpositions;
      94             :   std::vector<Vector> positions;
      95             :   std::vector<Vector> pPositions;
      96             :   std::vector<Vector> forces;
      97             :   std::vector<Vector> pForces;
      98             :   Tensor box,pbox;
      99             :   Tensor fbox,pfbox;
     100             : 
     101             :   const int nProc;
     102             :   std::vector<int> indexCnt;
     103             :   std::vector<int> indexDsp;
     104             :   std::vector<int> dataCnt;
     105             :   std::vector<int> dataDsp;
     106             :   std::vector<int> indexS;
     107             :   std::vector<int> indexR;
     108             :   std::vector<double> dataS;
     109             :   std::vector<double> dataR;
     110             :   std::vector<int> backmap;
     111             : 
     112             :   double initialBias;
     113             :   bool isFirstStep;
     114             : 
     115             :   bool ensemble;
     116             :   PbcAction* pbc_action;
     117             :   DomainDecomposition* domains;
     118             :   ActionToPutData* posx;
     119             :   ActionToPutData* posy;
     120             :   ActionToPutData* posz;
     121             : public:
     122             :   explicit EffectiveEnergyDrift(const ActionOptions&);
     123             :   ~EffectiveEnergyDrift();
     124             : 
     125             :   static void registerKeywords( Keywords& keys );
     126             : 
     127         474 :   void calculate() override {};
     128         474 :   void apply() override {};
     129             :   void update() override;
     130             : };
     131             : 
     132             : PLUMED_REGISTER_ACTION(EffectiveEnergyDrift,"EFFECTIVE_ENERGY_DRIFT")
     133             : 
     134          15 : void EffectiveEnergyDrift::registerKeywords( Keywords& keys ) {
     135          15 :   Action::registerKeywords( keys );
     136          15 :   ActionPilot::registerKeywords( keys );
     137             : 
     138          15 :   keys.add("compulsory","STRIDE","1","should be set to 1. Effective energy drift computation has to be active at each step.");
     139          15 :   keys.add("compulsory", "FILE", "file on which to output the effective energy drift.");
     140          15 :   keys.add("compulsory", "PRINT_STRIDE", "frequency to which output the effective energy drift on FILE");
     141          15 :   keys.addFlag("ENSEMBLE",false,"Set to TRUE if you want to average over multiple replicas.");
     142          15 :   keys.add("optional","FMT","the format that should be used to output real numbers");
     143          15 :   keys.use("RESTART");
     144          15 :   keys.use("UPDATE_FROM");
     145          15 :   keys.use("UPDATE_UNTIL");
     146          15 :   keys.addDOI("10.1021/ct5007086");
     147          15 : }
     148             : 
     149          11 : EffectiveEnergyDrift::EffectiveEnergyDrift(const ActionOptions&ao):
     150             :   Action(ao),
     151             :   ActionPilot(ao),
     152          11 :   fmt("%f"),
     153          11 :   eed(0.0),
     154          11 :   nProc(plumed.comm.Get_size()),
     155          11 :   initialBias(0.0),
     156          11 :   isFirstStep(true),
     157          11 :   ensemble(false),
     158          11 :   pbc_action(NULL),
     159          11 :   domains(NULL),
     160          11 :   posx(NULL),
     161          11 :   posy(NULL),
     162          11 :   posz(NULL) {
     163             :   //stride must be == 1
     164          11 :   if(getStride()!=1) {
     165           0 :     error("EFFECTIVE_ENERGY_DRIFT must have STRIDE=1 to work properly");
     166             :   }
     167             : 
     168             :   //parse and open FILE
     169             :   std::string fileName;
     170          22 :   parse("FILE",fileName);
     171          11 :   if(fileName.length()==0) {
     172           0 :     error("name out output file was not specified\n");
     173             :   }
     174          11 :   output.link(*this);
     175          11 :   output.open(fileName);
     176             : 
     177             :   //parse PRINT_STRIDE
     178          11 :   parse("PRINT_STRIDE",printStride);
     179             : 
     180             : 
     181             :   //parse FMT
     182          11 :   parse("FMT",fmt);
     183          11 :   fmt=" "+fmt;
     184          11 :   log.printf("  with format %s\n",fmt.c_str());
     185             : 
     186             :   //parse ENSEMBLE
     187          11 :   ensemble=false;
     188          11 :   parseFlag("ENSEMBLE",ensemble);
     189          11 :   if(ensemble && getNumberOfReplicas()<2) {
     190           0 :     error("You CANNOT run Replica-Averaged simulations without running multiple replicas!\n");
     191             :   }
     192             : 
     193          11 :   log<<"Bibliography "<<cite("Ferrarotti, Bottaro, Perez-Villa, and Bussi, J. Chem. Theory Comput. 11, 139 (2015)")<<"\n";
     194             : 
     195             :   //construct biases from ActionWithValue with a component named bias
     196          11 :   std::vector<ActionWithValue*> tmpActions=plumed.getActionSet().select<ActionWithValue*>();
     197         128 :   for(unsigned i=0; i<tmpActions.size(); i++)
     198         234 :     if(tmpActions[i]->exists(tmpActions[i]->getLabel()+".bias")) {
     199          13 :       biases.push_back(tmpActions[i]);
     200             :     }
     201             : 
     202             :   //resize counters and displacements useful to communicate with MPI_Allgatherv
     203          11 :   indexCnt.resize(nProc);
     204          11 :   indexDsp.resize(nProc);
     205          11 :   dataCnt.resize(nProc);
     206          11 :   dataDsp.resize(nProc);
     207             :   // Retrieve the box
     208          11 :   pbc_action=plumed.getActionSet().selectWithLabel<PbcAction*>("Box");
     209             :   // Get the domain decomposition object
     210          11 :   std::vector<DomainDecomposition*> ddact=plumed.getActionSet().select<DomainDecomposition*>();
     211          11 :   if( ddact.empty() ) {
     212           0 :     error("cannot find the atoms passed by the MD code");
     213             :   }
     214          11 :   if( ddact.size()>1 ) {
     215           0 :     warning("found more than one interface so don't know get gatindex");
     216             :   }
     217          11 :   domains = ddact[0];
     218          11 :   std::vector<ActionToPutData*> inputs=plumed.getActionSet().select<ActionToPutData*>();
     219          89 :   for(const auto & pp : inputs ) {
     220          78 :     if( pp->getRole()=="x" ) {
     221          11 :       posx = pp;
     222             :     }
     223          78 :     if( pp->getRole()=="y" ) {
     224          11 :       posy = pp;
     225             :     }
     226          78 :     if( pp->getRole()=="z" ) {
     227          11 :       posz = pp;
     228             :     }
     229             :   }
     230          11 :   plumed_assert( posx && posy && posz );
     231             :   //resize the received buffers
     232          11 :   indexR.resize((posx->copyOutput(0))->getShape()[0]);
     233          11 :   dataR.resize((posx->copyOutput(0))->getShape()[0]*6);
     234          11 :   backmap.resize((posx->copyOutput(0))->getShape()[0]);
     235          22 : }
     236             : 
     237          22 : EffectiveEnergyDrift::~EffectiveEnergyDrift() {
     238             : 
     239          22 : }
     240             : 
     241         474 : void EffectiveEnergyDrift::update() {
     242         474 :   Pbc & tpbc(pbc_action->getPbc());
     243             :   bool pbc=tpbc.isSet();
     244             : 
     245             :   //retrieve data of local atoms
     246         474 :   const std::vector<int>& gatindex = domains->getGatindex();
     247         474 :   nLocalAtoms = gatindex.size();
     248         474 :   xpositions.resize( gatindex.size() );
     249         474 :   posx->getLocalValues( xpositions );
     250         474 :   ypositions.resize( gatindex.size() );
     251         474 :   posy->getLocalValues( ypositions );
     252         474 :   zpositions.resize( gatindex.size() );
     253         474 :   posz->getLocalValues( zpositions );
     254         474 :   positions.resize( gatindex.size() );
     255         474 :   forces.resize( gatindex.size() );
     256         474 :   const Value* fx=posx->copyOutput(0);
     257         474 :   const Value* fy=posy->copyOutput(0);
     258         474 :   const Value* fz=posz->copyOutput(0);
     259       17154 :   for(unsigned i=0; i<gatindex.size(); ++i ) {
     260       16680 :     positions[i][0] = xpositions[i];
     261       16680 :     positions[i][1] = ypositions[i];
     262       16680 :     positions[i][2] = zpositions[i];
     263       16680 :     forces[i][0] = fx->getForce( gatindex[i] );
     264       16680 :     forces[i][1] = fy->getForce( gatindex[i] );
     265       16680 :     forces[i][2] = fz->getForce( gatindex[i] );
     266             :   }
     267         474 :   if(pbc) {
     268         474 :     Tensor B=tpbc.getBox();
     269         474 :     Tensor IB=tpbc.getInvBox();
     270         474 :     #pragma omp parallel for num_threads(OpenMP::getGoodNumThreads(positions))
     271             :     for(unsigned i=0; i<positions.size(); ++i) {
     272             :       positions[i]=matmul(positions[i],IB);
     273             :       forces[i]=matmul(B,forces[i]);
     274             :     }
     275         474 :     box=B;
     276         474 :     Tensor virial;
     277         474 :     Value* boxValue = pbc_action->copyOutput(0);
     278        1896 :     for(unsigned i=0; i<3; ++i)
     279        5688 :       for(unsigned j=0; j<3; ++j) {
     280        4266 :         virial[i][j]=boxValue->getForce(3*i+j);
     281             :       }
     282         474 :     fbox=matmul(transpose(inverse(box)),virial);
     283             :   }
     284             : 
     285             :   //init stored data at the first step
     286         474 :   if(isFirstStep) {
     287          11 :     pGatindex = domains->getGatindex();
     288          11 :     pNLocalAtoms = pGatindex.size();
     289          11 :     pPositions=positions;
     290          11 :     pForces=forces;
     291          11 :     pbox=box;
     292          11 :     pfbox=fbox;
     293          11 :     initialBias=plumed.getBias();
     294             : 
     295          11 :     isFirstStep=false;
     296             :   }
     297             : 
     298             :   // The local atoms, or their order, may have changed since the previous step:
     299             :   // domain decomposition, or a serial MD code that reorders its atoms (as LAMMPS does).
     300             :   // In that case the stored data is remapped. The local lists are compared at each step:
     301             :   // DomainDecomposition::getDdStep() cannot be used, since most MD codes pass the
     302             :   // local atoms at every step.
     303         474 :   int changed=(pGatindex.size()!=gatindex.size() || !std::equal(gatindex.begin(),gatindex.end(),pGatindex.begin()));
     304         474 :   if(nProc>1) {
     305         400 :     plumed.comm.Sum(changed);
     306             :   }
     307         474 :   if(changed) {
     308             :     unsigned nrecv=0;
     309         215 :     if(nProc>1) {
     310             :       //prepare the data to be sent
     311         204 :       indexS.resize(pNLocalAtoms);
     312         204 :       dataS.resize(pNLocalAtoms*6);
     313        5712 :       for(unsigned i=0; i<pNLocalAtoms; i++) {
     314        5508 :         indexS[i] = pGatindex[i];
     315        5508 :         dataS[i*6] = pPositions[i][0];
     316        5508 :         dataS[i*6+1] = pPositions[i][1];
     317        5508 :         dataS[i*6+2] = pPositions[i][2];
     318        5508 :         dataS[i*6+3] = pForces[i][0];
     319        5508 :         dataS[i*6+4] = pForces[i][1];
     320        5508 :         dataS[i*6+5] = pForces[i][2];
     321             :       }
     322             :       //setup the counters and displacements for the communication
     323         204 :       plumed.comm.Allgather(&pNLocalAtoms,1,&indexCnt[0],1);
     324         204 :       indexDsp[0] = 0;
     325        1020 :       for(int i=0; i<nProc; i++) {
     326         816 :         dataCnt[i] = indexCnt[i]*6;
     327         816 :         if(i+1<nProc) {
     328         612 :           indexDsp[i+1] = indexDsp[i]+indexCnt[i];
     329             :         }
     330         816 :         dataDsp[i] = indexDsp[i]*6;
     331         816 :         nrecv += indexCnt[i];
     332             :       }
     333         204 :       plumed_assert(nrecv<=indexR.size());
     334             :       //share stored data
     335         402 :       plumed.comm.Allgatherv((!indexS.empty()?&indexS[0]:NULL), pNLocalAtoms, &indexR[0], &indexCnt[0], &indexDsp[0]);
     336         402 :       plumed.comm.Allgatherv((!dataS.empty()?&dataS[0]:NULL), pNLocalAtoms*6, &dataR[0], &dataCnt[0], &dataDsp[0]);
     337             :     } else {
     338             :       // a single process: the stored data is only reordered
     339          11 :       nrecv=pNLocalAtoms;
     340          11 :       plumed_assert(nrecv<=indexR.size());
     341         231 :       for(unsigned i=0; i<pNLocalAtoms; i++) {
     342         220 :         indexR[i] = pGatindex[i];
     343         220 :         dataR[i*6] = pPositions[i][0];
     344         220 :         dataR[i*6+1] = pPositions[i][1];
     345         220 :         dataR[i*6+2] = pPositions[i][2];
     346         220 :         dataR[i*6+3] = pForces[i][0];
     347         220 :         dataR[i*6+4] = pForces[i][1];
     348         220 :         dataR[i*6+5] = pForces[i][2];
     349             :       }
     350             :     }
     351             :     //resize vectors to store the proper amount of data
     352         215 :     pPositions.resize(nLocalAtoms);
     353         215 :     pForces.resize(nLocalAtoms);
     354             :     //compute backmap (atoms that were not stored anywhere keep -1)
     355             :     std::fill(backmap.begin(),backmap.end(),-1);
     356       22467 :     for(unsigned j=0; j<nrecv; j++) {
     357       22252 :       backmap[indexR[j]]=j;
     358             :     }
     359             :     //fill the vectors pPositions and pForces
     360        5943 :     for(unsigned i=0; i<nLocalAtoms; i++) {
     361        5728 :       int glb=backmap[gatindex[i]];
     362        5728 :       if(glb<0) {
     363             :         // no data from the previous step: this atom does not contribute to this step
     364           0 :         pPositions[i] = positions[i];
     365           0 :         pForces[i] = forces[i];
     366           0 :         continue;
     367             :       }
     368        5728 :       pPositions[i][0] = dataR[glb*6];
     369        5728 :       pPositions[i][1] = dataR[glb*6+1];
     370        5728 :       pPositions[i][2] = dataR[glb*6+2];
     371        5728 :       pForces[i][0] = dataR[glb*6+3];
     372        5728 :       pForces[i][1] = dataR[glb*6+4];
     373        5728 :       pForces[i][2] = dataR[glb*6+5];
     374             :     }
     375         215 :     pGatindex=gatindex;
     376             :   }
     377             : 
     378             :   //compute the effective energy drift on local atoms
     379             : 
     380         474 :   double eed_tmp=eed;
     381         474 :   #pragma omp parallel for reduction(+:eed_tmp) num_threads(OpenMP::getGoodNumThreads(positions))
     382             :   for(unsigned i=0; i<nLocalAtoms; i++) {
     383             :     Vector dst=delta(pPositions[i],positions[i]);
     384             :     if(pbc)
     385             :       for(unsigned k=0; k<3; k++) {
     386             :         dst[k]=Tools::pbc(dst[k]);
     387             :       }
     388             :     eed_tmp += dotProduct(dst, forces[i]+pForces[i])*0.5;
     389             :   }
     390             : 
     391         474 :   eed=eed_tmp;
     392             : 
     393         474 :   if(plumed.comm.Get_rank()==0) {
     394         696 :     for(unsigned i=0; i<3; i++)
     395        2088 :       for(unsigned j=0; j<3; j++) {
     396        1566 :         eed-=0.5*(pfbox(i,j)+fbox(i,j))*(box(i,j)-pbox(i,j));
     397             :       }
     398             :   }
     399             : 
     400             : 
     401             :   //print the effective energy drift on FILE with frequency PRINT_STRIDE
     402         474 :   if(plumed.getStep()%printStride==0) {
     403         474 :     double eedSum = eed;
     404             :     double bias = 0.0;
     405             : 
     406             :     //we cannot just use plumed.getBias() because it will be ==0.0 if PRINT_STRIDE
     407             :     //is not a multiple of the bias actions stride
     408         972 :     for(unsigned i=0; i<biases.size(); i++) {
     409         498 :       bias+=biases[i]->getOutputQuantity("bias");
     410             :     }
     411             : 
     412         474 :     plumed.comm.Sum(&eedSum,1);
     413             : 
     414         474 :     double effective = eedSum+bias-initialBias-plumed.getWork();
     415             :     // this is to take into account ensemble averaging
     416         474 :     if(ensemble) {
     417           0 :       if(plumed.comm.Get_rank()==0) {
     418           0 :         plumed.multi_sim_comm.Sum(&effective,1);
     419             :       } else {
     420           0 :         effective=0.;
     421             :       }
     422           0 :       plumed.comm.Sum(&effective,1);
     423             :     }
     424         474 :     output.fmtField(" %f");
     425         474 :     output.printField("time",getTime());
     426         474 :     output.fmtField(fmt);
     427         474 :     output.printField("effective-energy",effective);
     428         474 :     output.printField();
     429             :   }
     430             : 
     431             :   //store the data of the current step
     432         474 :   pNLocalAtoms = nLocalAtoms;
     433             :   pPositions.swap(positions);
     434             :   pForces.swap(forces);
     435         474 :   pbox=box;
     436         474 :   pfbox=fbox;
     437         474 : }
     438             : 
     439             : }
     440             : }

Generated by: LCOV version 1.16