LCOV - code coverage report
Current view: top level - generic - Plumed.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 170 184 92.4 %
Date: 2019-08-13 10:15:31 Functions: 16 18 88.9 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2018,2019 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 "core/ActionAtomistic.h"
      23             : #include "core/ActionWithValue.h"
      24             : #include "core/ActionPilot.h"
      25             : #include "core/ActionRegister.h"
      26             : #include "tools/Tools.h"
      27             : #include "tools/PlumedHandle.h"
      28             : #include "core/PlumedMain.h"
      29             : #include <cstring>
      30             : #ifdef __PLUMED_HAS_DLOPEN
      31             : #include <dlfcn.h>
      32             : #endif
      33             : 
      34             : #include <iostream>
      35             : 
      36             : using namespace std;
      37             : 
      38             : namespace PLMD {
      39             : namespace generic {
      40             : 
      41             : //+PLUMEDOC GENERIC PLUMED
      42             : /*
      43             : Embed a separate PLUMED instance.
      44             : 
      45             : This command can be used to embed a separate PLUMED instance.
      46             : Only required atoms will be passed to that instance, using an interface
      47             : that is similar to the one used when calling PLUMED from the NAMD engine.
      48             : 
      49             : Notice that the two instances are running in the same UNIX process, so that they cannot be perfectly isolated.
      50             : However, most of the features are expected to work correctly.
      51             : 
      52             : Notes:
      53             : - The \ref LOAD action will not work correctly since registers will be shared among the two instances.
      54             :   In particular, the loaded actions will be visible to both guest and host irrespective of where they are loaded from.
      55             :   This can be fixed and will probably be fixed in a later version.
      56             : - `CHDIR` is not thread safe.
      57             :    However, in most implementations there will be a single process running PLUMED, with perhaps multiple OpenMP threads
      58             :    spawn in order to parallelize the calculation of individual variables. So, this is likely not a problem.
      59             : - MPI is working correctly. However, notice that the guest PLUMED will always run with a single process.
      60             :   Multiple replicas should be handled correctly.
      61             : 
      62             : As an advanced feature, one can use the option `KERNEL` to select the version of the guest PLUMED instance.
      63             : In particular, an empty `KERNEL` (default) implies that the guest PLUMED instance is the same as the host one
      64             : (no library is loaded).
      65             : On the other hand, `KERNEL=/path/to/libplumedKernel.so` will allow specifying a library to be loaded for the
      66             : guest instance.
      67             : In addition to those mentioned above, this feature has limitations mostly related to
      68             : clashes in the symbols defined in the different instances of the PLUMED library:
      69             : - On OSX, if you load a KERNEL with version >=2.5 there should be no problem thanks to the use
      70             :   of two-level namespaces.
      71             : - On OSX, if you load a KERNEL with version <=2.4 there should be clashes in symbol resolution.
      72             :   The only possible workarounds are:
      73             :   - If you are are using PLUMED with an MD code, it should be patched with `--runtime` and you should
      74             :     `export PLUMED_LOAD_NAMESPACE=LOCAL` before starting the MD engine.
      75             :   - If you are using PLUMED driver, you should launch the `plumed-runtime` executable (contained in the
      76             :     `prefix/lib/plumed/` directory), export `PLUMED_KERNEL` equal to the path of the host kernel library
      77             :    (as usual in runtime loading) and `export PLUMED_LOAD_NAMESPACE=LOCAL` before launching `plumed-runtime driver`.
      78             : - On Linux, any `KERNEL` should in principle work correctly. To achieve namespace separation we are loading
      79             :   the guest kernel with `RTLD_DEEPBIND`. However, this might create difficult to track problems in other linked libraries.
      80             : - On Unix systems where `RTLD_DEEPBIND` is not available kernels will not load correctly.
      81             : - In general, there might be unexpected crashes. Particularly difficult are situations where different
      82             :   kernels were compiled with different libraries.
      83             : 
      84             : A possible solution for the symbol clashes (not tested) could be to recompile the alternative PLUMED
      85             : versions using separate C++ namespaces (e.g. `./configure CPPFLAGS=-DPLMD=PLMD_2_3`).
      86             : 
      87             : \todo
      88             : - Add support for multiple time stepping (`STRIDE` different from 1).
      89             : - Add the possibility to import CVs calculated in the host PLUMED instance into the guest PLUMED instance.
      90             :   Will be possible after \issue{83} will be closed.
      91             : - Add the possibility to export CVs calculated in the guest PLUMED instance into the host PLUMED instance.
      92             :   Could be implemented using the `DataFetchingObject` class.
      93             : 
      94             : \par Examples
      95             : 
      96             : Here an example plumed file:
      97             : \plumedfile
      98             : # plumed.dat
      99             : p: PLUMED FILE=plumed2.dat
     100             : PRINT ARG=p.bias FILE=COLVAR
     101             : \endplumedfile
     102             : `plumed2.dat` can be an arbitrary plumed input file, for instance
     103             : \plumedfile
     104             : # plumed2.dat
     105             : d: DISTANCE ATOMS=1,10
     106             : RESTRAINT ARG=d KAPPA=10 AT=2
     107             : \endplumedfile
     108             : 
     109             : Now a more useful example.
     110             : Imagine that you ran simulations using two different PLUMED input files.
     111             : The files are long and complex and there are some clashes in the name of the variables (that is: same names
     112             : are used in both files, same files are written, etc). In addition, files might have been written using different units (see \ref UNITS`).
     113             : If you want to run a single simulation with a bias potential
     114             : that is the sum of the two bias potentials, you can:
     115             : - Place the two input files, as well as all the files required by plumed, in separate directories `directory1` and `directory2`.
     116             : - Run with the following input file in the parent directory:
     117             : \plumedfile
     118             : # plumed.dat
     119             : PLUMED FILE=plumed.dat CHDIR=directory1
     120             : PLUMED FILE=plumed.dat CHDIR=directory2
     121             : \endplumedfile
     122             : 
     123             : */
     124             : //+ENDPLUMEDOC
     125             : 
     126          44 : class Plumed:
     127             :   public ActionAtomistic,
     128             :   public ActionWithValue,
     129             :   public ActionPilot
     130             : {
     131             : /// True on root processor
     132             :   const bool root;
     133             : /// Separate directory.
     134             :   const std::string directory;
     135             : /// Interface to underlying plumed object.
     136             :   PlumedHandle p;
     137             : /// API number.
     138             :   const int API;
     139             : /// Self communicator
     140             :   Communicator comm_self;
     141             : /// Intercommunicator
     142             :   Communicator intercomm;
     143             : /// Detect first usage.
     144             :   bool first=true;
     145             : /// Stop flag, used to stop e.g. in committor analysis
     146             :   int stop=0;
     147             : /// Index of requested atoms.
     148             :   std::vector<int> index;
     149             : /// Masses of requested atoms.
     150             :   std::vector<double> masses;
     151             : /// Charges of requested atoms.
     152             :   std::vector<double> charges;
     153             : /// Forces on requested atoms.
     154             :   std::vector<double> forces;
     155             : /// Requested positions.
     156             :   std::vector<double> positions;
     157             : /// Applied virial.
     158             :   Tensor virial;
     159             : public:
     160             : /// Constructor.
     161             :   explicit Plumed(const ActionOptions&);
     162             : /// Documentation.
     163             :   static void registerKeywords( Keywords& keys );
     164             :   void prepare() override;
     165             :   void calculate() override;
     166             :   void apply() override;
     167             :   void update() override;
     168           0 :   unsigned getNumberOfDerivatives() override {
     169           0 :     return 0;
     170             :   }
     171             : };
     172             : 
     173        7854 : PLUMED_REGISTER_ACTION(Plumed,"PLUMED")
     174             : 
     175          12 : void Plumed::registerKeywords( Keywords& keys ) {
     176          12 :   Action::registerKeywords( keys );
     177          12 :   ActionPilot::registerKeywords( keys );
     178          12 :   ActionAtomistic::registerKeywords( keys );
     179          60 :   keys.add("compulsory","STRIDE","1","stride different from 1 are not supported yet");
     180          48 :   keys.add("optional","FILE","input file for the guest PLUMED instance");
     181          48 :   keys.add("optional","KERNEL","kernel to be used for the guest PLUMED instance (USE WITH CAUTION!)");
     182          48 :   keys.add("optional","LOG","log file for the guest PLUMED instance. By default the host log is used");
     183          48 :   keys.add("optional","CHDIR","run guest in a separate directory");
     184          36 :   keys.addFlag("NOREPLICAS",false,"run multiple replicas as isolated ones, without letting them know that the host has multiple replicas");
     185          48 :   keys.addOutputComponent("bias","default","the instantaneous value of the bias potential");
     186          12 : }
     187             : 
     188          11 : Plumed::Plumed(const ActionOptions&ao):
     189             :   Action(ao),
     190             :   ActionAtomistic(ao),
     191             :   ActionWithValue(ao),
     192             :   ActionPilot(ao),
     193          11 :   root(comm.Get_rank()==0),
     194          11 :   directory([&]() {
     195             :   std::string directory;
     196          22 :   parse("CHDIR",directory);
     197          11 :   if(directory.length()>0) {
     198           0 :     log<<"  running on separate directory "<<directory<<"\n";
     199             :   }
     200          11 :   return directory;
     201             : }()),
     202          11 : p([&]() {
     203             :   std::string kernel;
     204          22 :   parse("KERNEL",kernel);
     205          11 :   if(kernel.length()==0) {
     206          11 :     log<<"  using the current kernel\n";
     207          11 :     return PlumedHandle();
     208             :   } else {
     209           0 :     log<<"  using the kernel "<<kernel<<"\n";
     210           0 :     return PlumedHandle::dlopen(kernel.c_str());
     211             :   }
     212             : }()),
     213          11 : API([&]() {
     214          11 :   int api=0;
     215          11 :   p.cmd("getApiVersion",&api);
     216          11 :   log<<"  reported API version is "<<api<<"\n";
     217             :   // note: this is required in order to have cmd performCalcNoUpdate and cmd update
     218             :   // as a matter of fact, any version <2.5 will not even load due to namespace pollution
     219          11 :   plumed_assert(api>3) << "API>3 is required for the PLUMED action to work correctly\n";
     220          11 :   return api;
     221          33 : }())
     222             : {
     223          11 :   Tools::DirectoryChanger directoryChanger(directory.c_str());
     224             : 
     225             :   bool noreplicas;
     226          22 :   parseFlag("NOREPLICAS",noreplicas);
     227             :   int nreps;
     228          11 :   if(root) nreps=multi_sim_comm.Get_size();
     229          11 :   comm.Bcast(nreps,0);
     230          11 :   if(nreps>1) {
     231           6 :     if(noreplicas) {
     232           0 :       log<<"  running replicas as independent (no suffix used)\n";
     233             :     } else {
     234           6 :       log<<"  running replicas as standard multi replic (with suffix)\n";
     235           6 :       if(root) {
     236           3 :         intercomm.Set_comm(&multi_sim_comm.Get_comm());
     237           3 :         p.cmd("GREX setMPIIntercomm",&intercomm.Get_comm());
     238           3 :         p.cmd("GREX setMPIIntracomm",&comm_self.Get_comm());
     239           3 :         p.cmd("GREX init");
     240             :       }
     241             :     }
     242             :   } else {
     243           5 :     if(noreplicas) {
     244           0 :       log<<"  WARNING: flag NOREPLICAS ignored since we are running without replicas\n";
     245             :     }
     246             :   }
     247             : 
     248          22 :   int natoms=plumed.getAtoms().getNatoms();
     249             : 
     250          11 :   plumed_assert(getStride()==1) << "currently only supports STRIDE=1";
     251             : 
     252          11 :   double dt=getTimeStep();
     253             : 
     254             :   std::string file;
     255          22 :   parse("FILE",file);
     256          11 :   if(file.length()>0) {
     257          11 :     log<<"  with input file "<<file<<"\n";
     258           0 :   } else plumed_error() << "you must provide an input file\n";
     259             : 
     260             :   bool inherited_logfile=false;
     261             :   std::string logfile;
     262          22 :   parse("LOG",logfile);
     263          11 :   if(logfile.length()>0) {
     264           0 :     log<<"  with log file "<<logfile<<"\n";
     265           0 :     if(root) p.cmd("setLogFile",logfile.c_str());
     266          22 :   } else if(log.getFILE()) {
     267          11 :     log<<"  with inherited log file\n";
     268          16 :     if(root) p.cmd("setLog",log.getFILE());
     269             :     inherited_logfile=true;
     270             :   } else {
     271           0 :     log<<"  with log on stdout\n";
     272           0 :     if(root) p.cmd("setLog",stdout);
     273             :   }
     274             : 
     275          11 :   checkRead();
     276             : 
     277          11 :   if(root) p.cmd("setMDEngine","plumed");
     278             : 
     279          22 :   double engunits=plumed.getAtoms().getUnits().getEnergy();
     280          11 :   if(root) p.cmd("setMDEnergyUnits",&engunits);
     281             : 
     282          22 :   double lenunits=plumed.getAtoms().getUnits().getLength();
     283          11 :   if(root) p.cmd("setMDLengthUnits",&lenunits);
     284             : 
     285          22 :   double timunits=plumed.getAtoms().getUnits().getTime();
     286          11 :   if(root) p.cmd("setMDTimeUnits",&timunits);
     287             : 
     288          22 :   double chaunits=plumed.getAtoms().getUnits().getCharge();
     289          11 :   if(root) p.cmd("setMDChargeUnits",&chaunits);
     290          22 :   double masunits=plumed.getAtoms().getUnits().getMass();
     291          11 :   if(root) p.cmd("setMDMassUnits",&masunits);
     292             : 
     293          22 :   double kbt=plumed.getAtoms().getKbT();
     294          11 :   if(root) p.cmd("setKbT",&kbt);
     295             : 
     296          11 :   int res=0;
     297          11 :   if(getRestart()) res=1;
     298          11 :   if(root) p.cmd("setRestart",&res);
     299             : 
     300          11 :   if(root) p.cmd("setNatoms",&natoms);
     301          11 :   if(root) p.cmd("setTimestep",&dt);
     302          16 :   if(root) p.cmd("setPlumedDat",file.c_str());
     303             : 
     304          22 :   addComponentWithDerivatives("bias");
     305          22 :   componentIsNotPeriodic("bias");
     306             : 
     307          11 :   if(inherited_logfile) log<<"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
     308          11 :   if(root) p.cmd("init");
     309          22 :   if(inherited_logfile) log<<"++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n";
     310          11 : }
     311             : 
     312         152 : void Plumed::prepare() {
     313         152 :   Tools::DirectoryChanger directoryChanger(directory.c_str());
     314         152 :   int step=getStep();
     315         152 :   if(root) p.cmd("setStep",&step);
     316         152 :   if(root) p.cmd("prepareDependencies");
     317         152 :   int ene=0;
     318         152 :   if(root) p.cmd("isEnergyNeeded",&ene);
     319         152 :   if(ene) plumed_error()<<"It is not currently possible to use ENERGY in a guest PLUMED";
     320         152 :   int n=0;
     321         152 :   if(root) p.cmd("createFullList",&n);
     322         152 :   int *pointer=nullptr;
     323         152 :   if(root) p.cmd("getFullList",&pointer);
     324         152 :   bool redo=(index.size()!=n);
     325         152 :   if(first) redo=true;
     326         152 :   first=false;
     327        4310 :   if(root && !redo) for(int i=0; i<n; i++) if(index[i]!=pointer[i]) { redo=true; break;};
     328         152 :   if(root && redo) {
     329          20 :     index.resize(n);
     330          20 :     masses.resize(n);
     331          20 :     forces.resize(3*n);
     332          20 :     positions.resize(3*n);
     333          20 :     charges.resize(n);
     334         887 :     for(int i=0; i<n; i++) {
     335        1774 :       index[i]=pointer[i];
     336             :     };
     337          20 :     p.cmd("setAtomsNlocal",&n);
     338          20 :     p.cmd("setAtomsGatindex",index.data());
     339             :   }
     340         152 :   if(root) p.cmd("clearFullList");
     341         152 :   int tmp=0;
     342         152 :   if(root && redo) {
     343          20 :     tmp=1;
     344             :   }
     345         152 :   comm.Bcast(tmp,0);
     346         152 :   if(tmp) {
     347          32 :     int s=index.size();
     348          32 :     comm.Bcast(s,0);
     349          32 :     if(!root) index.resize(s);
     350          32 :     comm.Bcast(index,0);
     351             :     std::vector<AtomNumber> numbers;
     352          32 :     numbers.reserve(index.size());
     353        2034 :     for(auto i : index) numbers.emplace_back(AtomNumber::index(i));
     354          32 :     requestAtoms(numbers);
     355         152 :   }
     356         152 : }
     357             : 
     358         122 : void Plumed::calculate() {
     359         122 :   Tools::DirectoryChanger directoryChanger(directory.c_str());
     360         122 :   if(root) p.cmd("setStopFlag",&stop);
     361         122 :   Tensor box=getPbc().getBox();
     362         122 :   if(root) p.cmd("setBox",&box[0][0]);
     363             : 
     364         122 :   virial.zero();
     365       48802 :   for(int i=0; i<forces.size(); i++) forces[i]=0.0;
     366        8022 :   for(int i=0; i<masses.size(); i++) masses[i]=getMass(i);
     367        8022 :   for(int i=0; i<charges.size(); i++) charges[i]=getCharge(i);
     368             : 
     369         184 :   if(root) p.cmd("setMasses",masses.data());
     370         184 :   if(root) p.cmd("setCharges",charges.data());
     371         184 :   if(root) p.cmd("setPositions",positions.data());
     372         184 :   if(root) p.cmd("setForces",forces.data());
     373         122 :   if(root) p.cmd("setVirial",&virial[0][0]);
     374             : 
     375             : 
     376        8084 :   if(root) for(unsigned i=0; i<getNumberOfAtoms(); i++) {
     377       11850 :       positions[3*i+0]=getPosition(i)[0];
     378        7900 :       positions[3*i+1]=getPosition(i)[1];
     379        7900 :       positions[3*i+2]=getPosition(i)[2];
     380             :     }
     381             : 
     382         122 :   if(root) p.cmd("shareData");
     383         122 :   if(root) p.cmd("performCalcNoUpdate");
     384             : 
     385         122 :   int s=forces.size();
     386         122 :   comm.Bcast(s,0);
     387         122 :   if(!root) forces.resize(s);
     388         122 :   comm.Bcast(forces,0);
     389         122 :   comm.Bcast(virial,0);
     390             : 
     391         122 :   double bias=0.0;
     392         122 :   if(root) p.cmd("getBias",&bias);
     393         122 :   comm.Bcast(bias,0);
     394         244 :   getPntrToComponent("bias")->set(bias);
     395         122 : }
     396             : 
     397          92 : void Plumed::apply() {
     398          92 :   Tools::DirectoryChanger directoryChanger(directory.c_str());
     399             :   auto & f(modifyForces());
     400       12768 :   for(unsigned i=0; i<getNumberOfAtoms(); i++) {
     401       19014 :     f[i][0]+=forces[3*i+0];
     402       19014 :     f[i][1]+=forces[3*i+1];
     403       19014 :     f[i][2]+=forces[3*i+2];
     404             :   }
     405          92 :   auto & v(modifyVirial());
     406          92 :   v+=virial;
     407          92 : }
     408             : 
     409          92 : void Plumed::update() {
     410          92 :   Tools::DirectoryChanger directoryChanger(directory.c_str());
     411          92 :   if(root) p.cmd("update");
     412          92 :   comm.Bcast(stop,0);
     413          92 :   if(stop) {
     414           0 :     log<<"  Action " << getLabel()<<" asked to stop\n";
     415           0 :     plumed.stop();
     416          92 :   }
     417          92 : }
     418             : 
     419             : }
     420        5874 : }

Generated by: LCOV version 1.14