LCOV - code coverage report
Current view: top level - ves - MD_LinearExpansionPES.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 333 345 96.5 %
Date: 2025-12-04 11:19:34 Functions: 8 8 100.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2016-2021 The VES code team
       3             :    (see the PEOPLE-VES file at the root of this folder for a list of names)
       4             : 
       5             :    See http://www.ves-code.org for more information.
       6             : 
       7             :    This file is part of VES code module.
       8             : 
       9             :    The VES code module 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             :    The VES code module 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 the VES code module.  If not, see <http://www.gnu.org/licenses/>.
      21             : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */
      22             : 
      23             : #include "BasisFunctions.h"
      24             : #include "LinearBasisSetExpansion.h"
      25             : #include "CoeffsVector.h"
      26             : #include "GridIntegrationWeights.h"
      27             : #include "GridProjWeights.h"
      28             : 
      29             : #include "cltools/CLTool.h"
      30             : #include "core/CLToolRegister.h"
      31             : #include "tools/Vector.h"
      32             : #include "tools/Random.h"
      33             : #include "tools/Grid.h"
      34             : #include "tools/Communicator.h"
      35             : #include "tools/FileBase.h"
      36             : #include "core/PlumedMain.h"
      37             : #include "core/ActionRegister.h"
      38             : #include "core/ActionSet.h"
      39             : #include "core/Value.h"
      40             : 
      41             : #include <string>
      42             : #include <cstdio>
      43             : #include <cmath>
      44             : #include <vector>
      45             : 
      46             : #ifdef __PLUMED_HAS_MPI
      47             : #include <mpi.h>
      48             : #endif
      49             : 
      50             : 
      51             : namespace PLMD {
      52             : namespace ves {
      53             : 
      54             : //+PLUMEDOC TOOLS ves_md_linearexpansion
      55             : /*
      56             : Simple MD code for dynamics on a potential energy surface given by a linear basis set expansion.
      57             : 
      58             : This is simple MD code that allows running dynamics of a single particle on a
      59             : potential energy surface given by some linear basis set expansion in one to three
      60             : dimensions.
      61             : 
      62             : It is possible to run more than one replica of the system in parallel.
      63             : 
      64             : ## Examples
      65             : 
      66             : In the following example we perform dynamics on the
      67             : Wolfe-Quapp potential that is defined as
      68             : 
      69             : $$
      70             : U(x,y) = x^4 + y^4 - 2 x^2 - 4 y^2 + xy + 0.3 x + 0.1 y
      71             : $$
      72             : 
      73             : This function has minima at (-1.174,1.477); (-0.831,-1.366); (1.124,-1.486),
      74             : a maxima at (0.100,0.050) and saddle points around (-1.013,-0.036); (0.093,0.174); (-0.208,-1.407).
      75             : 
      76             : To define the potential we employ polynomial power basis
      77             : functions ([BF_POWERS](BF_POWERS.md)). The input file is given as
      78             : 
      79             : ```plumed
      80             : #TOOL=ves_md_linearexpansion
      81             : nstep             10000
      82             : tstep             0.005
      83             : temperature       1.0
      84             : friction          10.0
      85             : random_seed       4525
      86             : plumed_input      plumed.dat
      87             : dimension         2
      88             : replicas          1
      89             : basis_functions_1 BF_POWERS ORDER=4 MINIMUM=-3.0 MAXIMUM=+3.0
      90             : basis_functions_2 BF_POWERS ORDER=4 MINIMUM=-3.0 MAXIMUM=+3.0
      91             : input_coeffs       pot_coeffs_input.data
      92             : initial_position   -1.174,+1.477
      93             : output_potential        potential.data
      94             : output_potential_grid   150
      95             : output_histogram        histogram.data
      96             : ```
      97             : 
      98             : This input is then run by using the following command.
      99             : 
     100             : ```plumed
     101             : plumed ves_md_linearexpansion input
     102             : ```
     103             : 
     104             : The corresponding pot_coeffs_input.data file is
     105             : 
     106             : ````
     107             : #! FIELDS idx_dim1 idx_dim2 pot.coeffs index description
     108             : #! SET type LinearBasisSet
     109             : #! SET ndimensions  2
     110             : #! SET ncoeffs_total  25
     111             : #! SET shape_dim1  5
     112             : #! SET shape_dim2  5
     113             :        0       0         0.0000000000000000e+00       0  1*1
     114             :        1       0         0.3000000000000000e+00       1  s^1*1
     115             :        2       0        -2.0000000000000000e+00       2  s^2*1
     116             :        4       0         1.0000000000000000e+00       4  s^4*1
     117             :        0       1         0.1000000000000000e+00       5  1*s^1
     118             :        1       1        +1.0000000000000000e+00       6  s^1*s^1
     119             :        0       2        -4.0000000000000000e+00      10  1*s^2
     120             :        0       4         1.0000000000000000e+00      20  1*s^4
     121             : #!-------------------
     122             : ````
     123             : 
     124             : One then uses the (x,y) position of the particle as CVs by using the [POSITION](POSITION.md)
     125             : action as shown in the following PLUMED input
     126             : 
     127             : ```plumed
     128             : p: POSITION ATOM=1
     129             : ene: ENERGY
     130             : PRINT ARG=p.x,p.y,ene FILE=colvar.data FMT=%8.4f
     131             : ```
     132             : 
     133             : 
     134             : 
     135             : */
     136             : //+ENDPLUMEDOC
     137             : 
     138             : class MD_LinearExpansionPES : public PLMD::CLTool {
     139             : public:
     140           5 :   std::string description() const override {
     141           5 :     return "MD of a one particle on a linear expansion PES";
     142             :   }
     143             :   static void registerKeywords( Keywords& keys );
     144             :   explicit MD_LinearExpansionPES( const CLToolOptions& co );
     145             :   int main( FILE* in, FILE* out, PLMD::Communicator& pc) override;
     146             : private:
     147             :   size_t dim;
     148             :   std::string dim_string_prefix;
     149             :   std::unique_ptr<LinearBasisSetExpansion> potential_expansion_pntr;
     150             :   //
     151             :   double calc_energy( const std::vector<Vector>&, std::vector<Vector>& );
     152             :   double calc_temp( const std::vector<Vector>& );
     153             : };
     154             : 
     155       17094 : PLUMED_REGISTER_CLTOOL(MD_LinearExpansionPES,"ves_md_linearexpansion")
     156             : 
     157        5683 : void MD_LinearExpansionPES::registerKeywords( Keywords& keys ) {
     158        5683 :   CLTool::registerKeywords( keys );
     159        5683 :   keys.add("compulsory","nstep","10","The number of steps of dynamics you want to run.");
     160        5683 :   keys.add("compulsory","tstep","0.005","The integration timestep.");
     161        5683 :   keys.add("compulsory","temperature","1.0","The temperature to perform the simulation at. For multiple replica you can give a separate value for each replica.");
     162        5683 :   keys.add("compulsory","friction","10.","The friction of the Langevin thermostat. For multiple replica you can give a separate value for each replica.");
     163        5683 :   keys.add("compulsory","random_seed","5293818","Value of random number seed.");
     164        5683 :   keys.add("compulsory","plumed_input","plumed.dat","The name of the plumed input file(s). For multiple replica you can give a separate value for each replica.");
     165        5683 :   keys.add("compulsory","dimension","1","Number of dimensions, supports 1 to 3.");
     166        5683 :   keys.add("compulsory","initial_position","Initial position of the particle. For multiple replica you can give a separate value for each replica.");
     167        5683 :   keys.add("compulsory","replicas","1","Number of replicas.");
     168        5683 :   keys.add("compulsory","basis_functions_1","Basis functions for dimension 1.");
     169        5683 :   keys.add("optional","basis_functions_2","Basis functions for dimension 2 if needed.");
     170        5683 :   keys.add("optional","basis_functions_3","Basis functions for dimension 3 if needed.");
     171        5683 :   keys.add("compulsory","input_coeffs","potential-coeffs.in.data","Filename of the input coefficient file for the potential. For multiple replica you can give a separate value for each replica.");
     172        5683 :   keys.add("compulsory","output_coeffs","potential-coeffs.out.data","Filename of the output coefficient file for the potential.");
     173        5683 :   keys.add("compulsory","output_coeffs_fmt","%30.16e","Format of the output coefficient file for the potential. Useful for regtests.");
     174        5683 :   keys.add("optional","coeffs_prefactor","prefactor for multiplying the coefficients with. For multiple replica you can give a separate value for each replica.");
     175        5683 :   keys.add("optional","template_coeffs_file","only generate a template coefficient file with the filename given and exit.");
     176        5683 :   keys.add("compulsory","output_potential_grid","100","The number of grid points used for the potential and histogram output files.");
     177        5683 :   keys.add("compulsory","output_potential","potential.data","Filename of the potential output file.");
     178        5683 :   keys.add("compulsory","output_histogram","histogram.data","Filename of the histogram output file.");
     179        5683 : }
     180             : 
     181             : 
     182          45 : MD_LinearExpansionPES::MD_LinearExpansionPES( const CLToolOptions& co ):
     183             :   CLTool(co),
     184          45 :   dim(0),
     185          45 :   dim_string_prefix("dim") {
     186          45 :   inputdata=inputType::ifile; //inputType::commandline;
     187          45 : }
     188             : 
     189             : inline
     190        3939 : double MD_LinearExpansionPES::calc_energy( const std::vector<Vector>& pos, std::vector<Vector>& forces) {
     191        3939 :   std::vector<double> pos_tmp(dim);
     192        3939 :   std::vector<double> forces_tmp(dim,0.0);
     193        8585 :   for(unsigned int j=0; j<dim; ++j) {
     194        4646 :     pos_tmp[j]=pos[0][j];
     195             :   }
     196        3939 :   bool all_inside = true;
     197        3939 :   double potential = potential_expansion_pntr->getBiasAndForces(pos_tmp,all_inside,forces_tmp);
     198        8585 :   for(unsigned int j=0; j<dim; ++j) {
     199        4646 :     forces[0][j] = forces_tmp[j];
     200             :   }
     201        3939 :   return potential;
     202             : }
     203             : 
     204             : 
     205             : inline
     206             : double MD_LinearExpansionPES::calc_temp( const std::vector<Vector>& vel) {
     207             :   double total_KE=0.0;
     208             :   //! Double the total kinetic energy of the system
     209        8585 :   for(unsigned int j=0; j<dim; ++j) {
     210        4646 :     total_KE+=vel[0][j]*vel[0][j];
     211             :   }
     212          39 :   return total_KE / (double) dim; // total_KE is actually 2*KE
     213             : }
     214             : 
     215          40 : int MD_LinearExpansionPES::main( FILE* in, FILE* out, PLMD::Communicator& pc) {
     216             :   int plumedWantsToStop;
     217          40 :   Random random;
     218             :   unsigned int stepWrite=1000;
     219             : 
     220          40 :   std::unique_ptr<PLMD::PlumedMain> plumed;
     221             : 
     222             :   size_t replicas;
     223             :   unsigned int coresPerReplica;
     224          40 :   parse("replicas",replicas);
     225          40 :   if(replicas==1) {
     226           9 :     coresPerReplica = pc.Get_size();
     227             :   } else {
     228          31 :     if(pc.Get_size()%replicas!=0) {
     229           0 :       error("the number of MPI processes is not a multiple of the number of replicas.");
     230             :     }
     231          31 :     coresPerReplica = pc.Get_size()/replicas;
     232             :   }
     233             :   // create intra and inter communicators
     234          40 :   Communicator intra, inter;
     235          40 :   if(Communicator::initialized()) {
     236          33 :     int iworld=(pc.Get_rank() / coresPerReplica);
     237          33 :     pc.Split(iworld,0,intra);
     238          33 :     pc.Split(intra.Get_rank(),0,inter);
     239             :   }
     240             : 
     241             :   long long unsigned int nsteps;
     242          40 :   parse("nstep",nsteps);
     243             :   double tstep;
     244          40 :   parse("tstep",tstep);
     245             :   // initialize to solve a cppcheck 1.86 warning
     246          40 :   double temp=0.0;
     247          40 :   std::vector<double> temps_vec(0);
     248          80 :   parseVector("temperature",temps_vec);
     249          40 :   if(temps_vec.size()==1) {
     250          36 :     temp = temps_vec[0];
     251           4 :   } else if(replicas > 1 && temps_vec.size()==replicas) {
     252           4 :     temp = temps_vec[inter.Get_rank()];
     253             :   } else {
     254           0 :     error("problem with temperature keyword, you need to give either one value or a value for each replica.");
     255             :   }
     256             :   //
     257             :   double friction;
     258          40 :   std::vector<double> frictions_vec(0);
     259          80 :   parseVector("friction",frictions_vec);
     260          40 :   if(frictions_vec.size()==1) {
     261          36 :     friction = frictions_vec[0];
     262           4 :   } else if(frictions_vec.size()==replicas) {
     263           4 :     friction = frictions_vec[inter.Get_rank()];
     264             :   } else {
     265           0 :     error("problem with friction keyword, you need to give either one value or a value for each replica.");
     266             :   }
     267             :   //
     268             :   int seed;
     269          40 :   std::vector<int> seeds_vec(0);
     270          40 :   parseVector("random_seed",seeds_vec);
     271          92 :   for(unsigned int i=0; i<seeds_vec.size(); i++) {
     272          52 :     if(seeds_vec[i]>0) {
     273          44 :       seeds_vec[i] = -seeds_vec[i];
     274             :     }
     275             :   }
     276          40 :   if(replicas==1) {
     277           9 :     if(seeds_vec.size()>1) {
     278           0 :       error("problem with random_seed keyword, for a single replica you should only give one value");
     279             :     }
     280           9 :     seed = seeds_vec[0];
     281             :   } else {
     282          31 :     if(seeds_vec.size()!=1 && seeds_vec.size()!=replicas) {
     283           0 :       error("problem with random_seed keyword, for multiple replicas you should give either one value or a separate value for each replica");
     284             :     }
     285          31 :     if(seeds_vec.size()==1) {
     286          27 :       seeds_vec.resize(replicas);
     287         109 :       for(unsigned int i=1; i<seeds_vec.size(); i++) {
     288          82 :         seeds_vec[i] = seeds_vec[0] + i;
     289             :       }
     290             :     }
     291          31 :     seed = seeds_vec[inter.Get_rank()];
     292             :   }
     293             : 
     294             :   //
     295          80 :   parse("dimension",dim);
     296             : 
     297             :   std::vector<std::string> plumed_inputfiles;
     298          80 :   parseVector("plumed_input",plumed_inputfiles);
     299          40 :   if(plumed_inputfiles.size()!=1 && plumed_inputfiles.size()!=replicas) {
     300           0 :     error("in plumed_input you should either give one file or separate files for each replica.");
     301             :   }
     302             : 
     303          40 :   std::vector<Vector> initPos(replicas);
     304             :   std::vector<double> initPosTmp;
     305          80 :   parseVector("initial_position",initPosTmp);
     306          40 :   if(initPosTmp.size()==dim) {
     307          48 :     for(unsigned int i=0; i<replicas; i++) {
     308          72 :       for(unsigned int k=0; k<dim; k++) {
     309          38 :         initPos[i][k]=initPosTmp[k];
     310             :       }
     311             :     }
     312          26 :   } else if(initPosTmp.size()==dim*replicas) {
     313         126 :     for(unsigned int i=0; i<replicas; i++) {
     314         216 :       for(unsigned int k=0; k<dim; k++) {
     315         116 :         initPos[i][k]=initPosTmp[i*dim+k];
     316             :       }
     317             :     }
     318             :   } else {
     319           0 :     error("problem with initial_position keyword, you need to give either one value or a value for each replica.");
     320             :   }
     321             : 
     322             :   auto deleter=[](FILE* f) {
     323          79 :     fclose(f);
     324          39 :   };
     325          40 :   FILE* file_dummy = fopen("/dev/null","w+");
     326          40 :   plumed_assert(file_dummy);
     327             :   // call fclose when file_dummy_deleter goes out of scope
     328             :   std::unique_ptr<FILE,decltype(deleter)> file_dummy_deleter(file_dummy,deleter);
     329             :   // Note: this should be declared before plumed_bf to make sure the file is closed after plumed_bf has been destroyed
     330             : 
     331             :   auto plumed_bf = Tools::make_unique<PLMD::PlumedMain>();
     332          40 :   unsigned int nn=1;
     333          40 :   plumed_bf->cmd("setNatoms",&nn);
     334          40 :   plumed_bf->cmd("setLog",file_dummy);
     335          40 :   plumed_bf->cmd("init",&nn);
     336          40 :   std::vector<BasisFunctions*> basisf_pntrs(dim);
     337          40 :   std::vector<std::string> basisf_keywords(dim);
     338          40 :   std::vector<std::unique_ptr<Value>> args(dim);
     339          40 :   std::vector<bool> periodic(dim);
     340          40 :   std::vector<double> interval_min(dim);
     341          40 :   std::vector<double> interval_max(dim);
     342          40 :   std::vector<double> interval_range(dim);
     343          88 :   for(unsigned int i=0; i<dim; i++) {
     344             :     std::string bf_keyword;
     345             :     std::string is;
     346          48 :     Tools::convert(i+1,is);
     347          96 :     parse("basis_functions_"+is,bf_keyword);
     348          48 :     if(bf_keyword.size()==0) {
     349           0 :       error("basis_functions_"+is+" is needed");
     350             :     }
     351          48 :     if(bf_keyword.at(0)=='{' && bf_keyword.at(bf_keyword.size()-1)=='}') {
     352           4 :       bf_keyword = bf_keyword.substr(1,bf_keyword.size()-2);
     353             :     }
     354             :     basisf_keywords[i] = bf_keyword;
     355          96 :     plumed_bf->readInputLine(bf_keyword+" LABEL="+dim_string_prefix+is);
     356          48 :     basisf_pntrs[i] = plumed_bf->getActionSet().selectWithLabel<BasisFunctions*>(dim_string_prefix+is);
     357          96 :     args[i] = Tools::make_unique<Value>(nullptr,dim_string_prefix+is,false);
     358          48 :     args[i]->setNotPeriodic();
     359          48 :     periodic[i] = basisf_pntrs[i]->arePeriodic();
     360          48 :     interval_min[i] = basisf_pntrs[i]->intervalMin();
     361          48 :     interval_max[i] = basisf_pntrs[i]->intervalMax();
     362          48 :     interval_range[i] = basisf_pntrs[i]->intervalMax()-basisf_pntrs[i]->intervalMin();
     363             :   }
     364          40 :   Communicator comm_dummy;
     365          80 :   auto coeffs_pntr = Tools::make_unique<CoeffsVector>("pot.coeffs",Tools::unique2raw(args),basisf_pntrs,comm_dummy,false);
     366          80 :   potential_expansion_pntr = Tools::make_unique<LinearBasisSetExpansion>("potential",1.0/temp,comm_dummy,Tools::unique2raw(args),basisf_pntrs,coeffs_pntr.get());
     367             : 
     368          40 :   std::string template_coeffs_fname="";
     369          80 :   parse("template_coeffs_file",template_coeffs_fname);
     370          40 :   if(template_coeffs_fname.size()>0) {
     371           1 :     OFile ofile_coeffstmpl;
     372           1 :     ofile_coeffstmpl.link(pc);
     373           1 :     ofile_coeffstmpl.open(template_coeffs_fname);
     374           1 :     coeffs_pntr->writeToFile(ofile_coeffstmpl,true);
     375           1 :     ofile_coeffstmpl.close();
     376             :     std::printf("Only generating a template coefficient file - Should stop now.");
     377             :     return 0;
     378           1 :   }
     379             : 
     380          39 :   std::vector<std::string> input_coeffs_fnames(0);
     381          78 :   parseVector("input_coeffs",input_coeffs_fnames);
     382             :   std::string input_coeffs_fname;
     383             :   bool diff_input_coeffs = false;
     384          39 :   if(input_coeffs_fnames.size()==1) {
     385             :     input_coeffs_fname = input_coeffs_fnames[0];
     386           9 :   } else if(replicas > 1 && input_coeffs_fnames.size()==replicas) {
     387             :     diff_input_coeffs = true;
     388           9 :     input_coeffs_fname = input_coeffs_fnames[inter.Get_rank()];
     389             :   } else {
     390           0 :     error("problem with coeffs_file keyword, you need to give either one value or a value for each replica.");
     391             :   }
     392          39 :   coeffs_pntr->readFromFile(input_coeffs_fname,true,true);
     393          39 :   std::vector<double> coeffs_prefactors(0);
     394          78 :   parseVector("coeffs_prefactor",coeffs_prefactors);
     395          39 :   if(coeffs_prefactors.size()>0) {
     396             :     double coeffs_prefactor = 1.0;
     397           7 :     if(coeffs_prefactors.size()==1) {
     398           3 :       coeffs_prefactor = coeffs_prefactors[0];
     399           4 :     } else if(replicas > 1 && coeffs_prefactors.size()==replicas) {
     400             :       diff_input_coeffs = true;
     401           4 :       coeffs_prefactor = coeffs_prefactors[inter.Get_rank()];
     402             :     } else {
     403           0 :       error("problem with coeffs_prefactor keyword, you need to give either one value or a value for each replica.");
     404             :     }
     405           7 :     coeffs_pntr->scaleAllValues(coeffs_prefactor);
     406             :   }
     407             :   unsigned int pot_grid_bins;
     408          78 :   parse("output_potential_grid",pot_grid_bins);
     409          39 :   potential_expansion_pntr->setGridBins(pot_grid_bins);
     410          39 :   potential_expansion_pntr->setupBiasGrid(false);
     411          39 :   potential_expansion_pntr->updateBiasGrid();
     412          39 :   potential_expansion_pntr->setBiasMinimumToZero();
     413          39 :   potential_expansion_pntr->updateBiasGrid();
     414             : 
     415          39 :   OFile ofile_potential;
     416          39 :   ofile_potential.link(pc);
     417             :   std::string output_potential_fname;
     418          39 :   parse("output_potential",output_potential_fname);
     419          39 :   if(diff_input_coeffs) {
     420          13 :     ofile_potential.link(intra);
     421             :     std::string suffix;
     422          13 :     Tools::convert(inter.Get_rank(),suffix);
     423          26 :     output_potential_fname = FileBase::appendSuffix(output_potential_fname,"."+suffix);
     424             :   }
     425          39 :   ofile_potential.open(output_potential_fname);
     426          39 :   potential_expansion_pntr->writeBiasGridToFile(ofile_potential);
     427          39 :   ofile_potential.close();
     428          39 :   if(dim>1) {
     429          21 :     for(unsigned int i=0; i<dim; i++) {
     430             :       std::string is;
     431          14 :       Tools::convert(i+1,is);
     432          14 :       std::vector<std::string> proj_arg(1);
     433          14 :       proj_arg[0] = dim_string_prefix+is;
     434          14 :       auto Fw = Tools::make_unique<FesWeight>(1/temp);
     435          14 :       Grid proj_grid = (potential_expansion_pntr->getPntrToBiasGrid())->project(proj_arg,Fw.get());
     436          14 :       proj_grid.setMinToZero();
     437             : 
     438          28 :       std::string output_potential_proj_fname = FileBase::appendSuffix(output_potential_fname,"."+dim_string_prefix+is);
     439          14 :       OFile ofile_potential_proj;
     440          14 :       ofile_potential_proj.link(pc);
     441          14 :       ofile_potential_proj.open(output_potential_proj_fname);
     442          14 :       proj_grid.writeToFile(ofile_potential_proj);
     443          14 :       ofile_potential_proj.close();
     444          28 :     }
     445             :   }
     446             : 
     447             : 
     448          39 :   Grid histo_grid(*potential_expansion_pntr->getPntrToBiasGrid());
     449          78 :   std::vector<double> integration_weights = GridIntegrationWeights::getIntegrationWeights(&histo_grid);
     450             :   double norm=0.0;
     451      169278 :   for(Grid::index_t i=0; i<histo_grid.getSize(); i++) {
     452      169239 :     double value = integration_weights[i]*exp(-histo_grid.getValue(i)/temp);
     453      169239 :     norm += value;
     454      169239 :     histo_grid.setValue(i,value);
     455             :   }
     456          39 :   histo_grid.scaleAllValuesAndDerivatives(1.0/norm);
     457          39 :   OFile ofile_histogram;
     458          39 :   ofile_histogram.link(pc);
     459             :   std::string output_histogram_fname;
     460          39 :   parse("output_histogram",output_histogram_fname);
     461          39 :   if(diff_input_coeffs || temps_vec.size()>1) {
     462          17 :     ofile_histogram.link(intra);
     463             :     std::string suffix;
     464          17 :     Tools::convert(inter.Get_rank(),suffix);
     465          34 :     output_histogram_fname = FileBase::appendSuffix(output_histogram_fname,"."+suffix);
     466             :   }
     467          39 :   ofile_histogram.open(output_histogram_fname);
     468          39 :   histo_grid.writeToFile(ofile_histogram);
     469          39 :   ofile_histogram.close();
     470             : 
     471             :   std::string output_coeffs_fname;
     472          78 :   parse("output_coeffs",output_coeffs_fname);
     473             :   std::string output_coeffs_fmt;
     474          78 :   parse("output_coeffs_fmt",output_coeffs_fmt);
     475             :   coeffs_pntr->setOutputFmt(output_coeffs_fmt);
     476          39 :   OFile ofile_coeffsout;
     477          39 :   ofile_coeffsout.link(pc);
     478          39 :   if(diff_input_coeffs) {
     479          13 :     ofile_coeffsout.link(intra);
     480             :     std::string suffix;
     481          13 :     Tools::convert(inter.Get_rank(),suffix);
     482          26 :     output_coeffs_fname = FileBase::appendSuffix(output_coeffs_fname,"."+suffix);
     483             :   }
     484          39 :   ofile_coeffsout.open(output_coeffs_fname);
     485          39 :   coeffs_pntr->writeToFile(ofile_coeffsout,true);
     486          39 :   ofile_coeffsout.close();
     487             : 
     488          39 :   if(pc.Get_rank() == 0) {
     489          15 :     std::fprintf(out,"Replicas                              %zu\n",replicas);
     490             :     std::fprintf(out,"Cores per replica                     %u\n",coresPerReplica);
     491          15 :     std::fprintf(out,"Number of steps                       %llu\n",nsteps);
     492          15 :     std::fprintf(out,"Timestep                              %f\n",tstep);
     493          15 :     std::fprintf(out,"Temperature                           %f",temps_vec[0]);
     494          18 :     for(unsigned int i=1; i<temps_vec.size(); i++) {
     495           3 :       std::fprintf(out,",%f",temps_vec[i]);
     496             :     }
     497             :     std::fprintf(out,"\n");
     498          15 :     std::fprintf(out,"Friction                              %f",frictions_vec[0]);
     499          18 :     for(unsigned int i=1; i<frictions_vec.size(); i++) {
     500           3 :       std::fprintf(out,",%f",frictions_vec[i]);
     501             :     }
     502             :     std::fprintf(out,"\n");
     503          15 :     std::fprintf(out,"Random seed                           %d",seeds_vec[0]);
     504          38 :     for(unsigned int i=1; i<seeds_vec.size(); i++) {
     505          23 :       std::fprintf(out,",%d",seeds_vec[i]);
     506             :     }
     507             :     std::fprintf(out,"\n");
     508          15 :     std::fprintf(out,"Dimensions                            %zu\n",dim);
     509          34 :     for(unsigned int i=0; i<dim; i++) {
     510          19 :       std::fprintf(out,"Basis Function %u                      %s\n",i+1,basisf_keywords[i].c_str());
     511             :     }
     512             :     std::fprintf(out,"PLUMED input                          %s",plumed_inputfiles[0].c_str());
     513          16 :     for(unsigned int i=1; i<plumed_inputfiles.size(); i++) {
     514             :       std::fprintf(out,",%s",plumed_inputfiles[i].c_str());
     515             :     }
     516             :     std::fprintf(out,"\n");
     517             :     std::fprintf(out,"kBoltzmann taken as 1, use NATURAL_UNITS in the plumed input\n");
     518          15 :     if(diff_input_coeffs) {
     519             :       std::fprintf(out,"using different coefficients for each replica\n");
     520             :     }
     521             :   }
     522             : 
     523             : 
     524          39 :   plumed=Tools::make_unique<PLMD::PlumedMain>();
     525             : 
     526             : 
     527             : 
     528          39 :   if(plumed) {
     529          39 :     int s=sizeof(double);
     530          39 :     plumed->cmd("setRealPrecision",&s);
     531          39 :     if(replicas>1) {
     532          31 :       if (Communicator::initialized()) {
     533          62 :         plumed->cmd("GREX setMPIIntracomm",&intra.Get_comm());
     534          31 :         if (intra.Get_rank()==0) {
     535          62 :           plumed->cmd("GREX setMPIIntercomm",&inter.Get_comm());
     536             :         }
     537          31 :         plumed->cmd("GREX init");
     538          62 :         plumed->cmd("setMPIComm",&intra.Get_comm());
     539             :       } else {
     540           0 :         error("More than 1 replica but no MPI");
     541             :       }
     542             :     } else {
     543           8 :       if(Communicator::initialized()) {
     544           4 :         plumed->cmd("setMPIComm",&pc.Get_comm());
     545             :       }
     546             :     }
     547             :   }
     548             : 
     549          39 :   std::string plumed_logfile = "plumed.log";
     550          39 :   std::string stats_filename = "stats.out";
     551          39 :   std::string plumed_input = plumed_inputfiles[0];
     552          39 :   if(inter.Get_size()>1) {
     553             :     std::string suffix;
     554          31 :     Tools::convert(inter.Get_rank(),suffix);
     555          62 :     plumed_logfile = FileBase::appendSuffix(plumed_logfile,"."+suffix);
     556          62 :     stats_filename = FileBase::appendSuffix(stats_filename,"."+suffix);
     557          31 :     if(plumed_inputfiles.size()>1) {
     558           2 :       plumed_input = plumed_inputfiles[inter.Get_rank()];
     559             :     }
     560             :   }
     561             : 
     562          39 :   if(plumed) {
     563          39 :     int natoms=1;
     564          39 :     plumed->cmd("setNatoms",&natoms);
     565          39 :     plumed->cmd("setNoVirial");
     566          39 :     plumed->cmd("setMDEngine","mdrunner_linearexpansion");
     567          39 :     plumed->cmd("setTimestep",&tstep);
     568          39 :     plumed->cmd("setPlumedDat",plumed_input.c_str());
     569          39 :     plumed->cmd("setLogFile",plumed_logfile.c_str());
     570          39 :     plumed->cmd("setKbT",&temp);
     571          39 :     double energyunits=1.0;
     572          39 :     plumed->cmd("setMDEnergyUnits",&energyunits);
     573          39 :     plumed->cmd("init");
     574             :   }
     575             : 
     576             :   // Setup random number generator
     577          39 :   random.setSeed(seed);
     578             : 
     579             :   double potential, therm_eng=0;
     580          39 :   std::vector<double> masses(1,1);
     581          39 :   std::vector<Vector> positions(1), velocities(1), forces(1);
     582          85 :   for(unsigned int k=0; k<dim; k++) {
     583          46 :     positions[0][k] = initPos[inter.Get_rank()][k];
     584          46 :     if(periodic[k]) {
     585           4 :       positions[0][k] = positions[0][k] - floor((positions[0][k]-interval_min[k])/interval_range[k])*interval_range[k];
     586             :     } else {
     587          42 :       if(positions[0][k]>interval_max[k]) {
     588           7 :         positions[0][k]=interval_max[k];
     589             :       }
     590          42 :       if(positions[0][k]<interval_min[k]) {
     591           1 :         positions[0][k]=interval_min[k];
     592             :       }
     593             :     }
     594             :   }
     595             : 
     596             : 
     597          85 :   for(unsigned k=0; k<dim; ++k) {
     598          46 :     velocities[0][k]=random.Gaussian() * sqrt( temp );
     599             :   }
     600             : 
     601          39 :   potential=calc_energy(positions,forces);
     602             :   double ttt=calc_temp(velocities);
     603             : 
     604          39 :   FILE* fp=fopen(stats_filename.c_str(),"w+");
     605             :   // call fclose when fp_deleter goes out of scope
     606             :   std::unique_ptr<FILE,decltype(deleter)> fp_deleter(fp,deleter);
     607             : 
     608          39 :   double conserved = potential+1.5*ttt+therm_eng;
     609             :   //std::fprintf(fp,"%d %f %f %f %f %f %f %f %f \n", 0, 0., positions[0][0], positions[0][1], positions[0][2], conserved, ttt, potential, therm_eng );
     610          39 :   if( intra.Get_rank()==0 ) {
     611          38 :     std::fprintf(fp,"%d %f %f %f %f %f %f %f %f \n", 0, 0., positions[0][0], positions[0][1], positions[0][2], conserved, ttt, potential, therm_eng );
     612             :   }
     613             : 
     614          39 :   if(plumed) {
     615          39 :     long long unsigned int step_tmp = 0;
     616          39 :     plumed->cmd("setStepLongLong",&step_tmp);
     617          39 :     plumed->cmd("setMasses",&masses[0]);
     618          39 :     plumed->cmd("setForces",&forces[0][0]);
     619          39 :     plumed->cmd("setEnergy",&potential);
     620          39 :     plumed->cmd("setPositions",&positions[0][0]);
     621          39 :     plumed->cmd("calc");
     622             :   }
     623             : 
     624        3939 :   for(long long unsigned int istep=0; istep<nsteps; ++istep) {
     625             :     //if( istep%20==0 && pc.Get_rank()==0 ) printf("Doing step %llu\n",istep);
     626             : 
     627             :     // Langevin thermostat
     628        3900 :     double lscale=exp(-0.5*tstep*friction); //exp(-0.5*tstep/friction);
     629        3900 :     double lrand=sqrt((1.-lscale*lscale)*temp);
     630        8500 :     for(unsigned k=0; k<dim; ++k) {
     631        4600 :       therm_eng=therm_eng+0.5*velocities[0][k]*velocities[0][k];
     632        4600 :       velocities[0][k]=lscale*velocities[0][k]+lrand*random.Gaussian();
     633        4600 :       therm_eng=therm_eng-0.5*velocities[0][k]*velocities[0][k];
     634             :     }
     635             : 
     636             :     // First step of velocity verlet
     637        8500 :     for(unsigned k=0; k<dim; ++k) {
     638        4600 :       velocities[0][k] = velocities[0][k] + 0.5*tstep*forces[0][k];
     639        4600 :       positions[0][k] = positions[0][k] + tstep*velocities[0][k];
     640             : 
     641        4600 :       if(periodic[k]) {
     642         400 :         positions[0][k] = positions[0][k] - floor((positions[0][k]-interval_min[k])/interval_range[k])*interval_range[k];
     643             :       } else {
     644        4200 :         if(positions[0][k]>interval_max[k]) {
     645           7 :           positions[0][k]=interval_max[k];
     646           7 :           velocities[0][k]=-std::abs(velocities[0][k]);
     647             :         }
     648        4200 :         if(positions[0][k]<interval_min[k]) {
     649           2 :           positions[0][k]=interval_min[k];
     650           2 :           velocities[0][k]=-std::abs(velocities[0][k]);
     651             :         }
     652             :       }
     653             :     }
     654             : 
     655        3900 :     potential=calc_energy(positions,forces);
     656             : 
     657        3900 :     if(plumed) {
     658        3900 :       long long unsigned int istepplusone=istep+1;
     659        3900 :       plumedWantsToStop=0;
     660        3900 :       plumed->cmd("setStepLongLong",&istepplusone);
     661        3900 :       plumed->cmd("setMasses",&masses[0]);
     662        3900 :       plumed->cmd("setForces",&forces[0][0]);
     663        3900 :       plumed->cmd("setEnergy",&potential);
     664        3900 :       plumed->cmd("setPositions",&positions[0][0]);
     665        3900 :       plumed->cmd("setStopFlag",&plumedWantsToStop);
     666        3900 :       plumed->cmd("calc");
     667             :       //if(istep%2000==0) plumed->cmd("writeCheckPointFile");
     668        3900 :       if(plumedWantsToStop) {
     669           0 :         nsteps=istep;
     670             :       }
     671             :     }
     672             : 
     673             :     // Second step of velocity verlet
     674        8500 :     for(unsigned k=0; k<dim; ++k) {
     675        4600 :       velocities[0][k] = velocities[0][k] + 0.5*tstep*forces[0][k];
     676             :     }
     677             : 
     678             :     // Langevin thermostat
     679        3900 :     lscale=exp(-0.5*tstep*friction); //exp(-0.5*tstep/friction);
     680        3900 :     lrand=sqrt((1.-lscale*lscale)*temp);
     681        8500 :     for(unsigned k=0; k<dim; ++k) {
     682        4600 :       therm_eng=therm_eng+0.5*velocities[0][k]*velocities[0][k];
     683        4600 :       velocities[0][k]=lscale*velocities[0][k]+lrand*random.Gaussian();
     684        4600 :       therm_eng=therm_eng-0.5*velocities[0][k]*velocities[0][k];
     685             :     }
     686             : 
     687             :     // Print everything
     688             :     ttt = calc_temp( velocities );
     689        3900 :     conserved = potential+1.5*ttt+therm_eng;
     690        3900 :     if( (intra.Get_rank()==0) && ((istep % stepWrite)==0) ) {
     691          38 :       std::fprintf(fp,"%llu %f %f %f %f %f %f %f %f \n", istep, istep*tstep, positions[0][0], positions[0][1], positions[0][2], conserved, ttt, potential, therm_eng );
     692             :     }
     693             :   }
     694             : 
     695             :   //printf("Rank: %d, Size: %d \n", pc.Get_rank(), pc.Get_size() );
     696             :   //printf("Rank: %d, Size: %d, MultiSimCommRank: %d, MultiSimCommSize: %d \n", pc.Get_rank(), pc.Get_size(), multi_sim_comm.Get_rank(), multi_sim_comm.Get_size() );
     697             : 
     698             :   return 0;
     699         395 : }
     700             : 
     701             : }
     702             : }

Generated by: LCOV version 1.16