LCOV - code coverage report
Current view: top level - cltools - SimpleMD.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 256 269 95.2 %
Date: 2025-04-08 21:11:17 Functions: 20 20 100.0 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2012-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 "CLTool.h"
      23             : #include "core/CLToolRegister.h"
      24             : #include "core/PlumedMain.h"
      25             : #include "tools/Vector.h"
      26             : #include "tools/Random.h"
      27             : #include "tools/OpenMP.h"
      28             : #include <string>
      29             : #include <cstdio>
      30             : #include <cmath>
      31             : #include <vector>
      32             : #include <memory>
      33             : 
      34             : namespace PLMD {
      35             : namespace cltools {
      36             : 
      37             : //+PLUMEDOC TOOLS simplemd
      38             : /*
      39             : simplemd allows one to do molecular dynamics on systems of Lennard-Jones atoms.
      40             : 
      41             : You can use simplemd to do a molecular dynamics simulation on a system of Lennard-Jones particles by using the following command
      42             : 
      43             : ```plumed
      44             : plumed simplemd < in
      45             : ```
      46             : 
      47             : The input to simplemd is specified in an input file that contains one directive per line like the one shown below.
      48             : 
      49             : ```plumed
      50             : #TOOL=simplemd
      51             : inputfile input.xyz
      52             : outputfile output.xyz
      53             : temperature 0.722
      54             : tstep 0.005
      55             : friction 1
      56             : forcecutoff 2.5
      57             : listcutoff  3.0
      58             : nstep 50
      59             : nconfig 10 trajectory.xyz
      60             : nstat   10 energies.dat
      61             : ```
      62             : 
      63             : This input instructs  simplemd to do 50 steps of MD at a temperature of 0.722 [Lennard Jones Units](https://en.wikipedia.org/wiki/Lennard-Jones_potential).
      64             : Notice that the atomic configurations are input and output in xyz format.
      65             : 
      66             : If you run the following command a description of all the directives that can be used in the input file will be output.
      67             : 
      68             : ```plumed
      69             : plumed simplemd --help
      70             : ```
      71             : 
      72             : */
      73             : //+ENDPLUMEDOC
      74             : 
      75             : // simple static function to close a file
      76             : // defined once here since it's used in many places in this file
      77             : // in addition, this seems the only way to use it in the write_statistics_fp_deleter member
      78             : static void (*deleter)(FILE* f) = [](auto f) {
      79             :   if(f) {
      80             :     std::fclose(f);
      81             :   }
      82             : };
      83             : 
      84             : class SimpleMD:
      85             :   public PLMD::CLTool {
      86           5 :   std::string description()const override {
      87           5 :     return "run lj code";
      88             :   }
      89             : 
      90             :   bool write_positions_first;
      91             :   bool write_statistics_first;
      92             :   int write_statistics_last_time_reopened;
      93             :   FILE* write_statistics_fp;
      94             :   std::unique_ptr<FILE,decltype(deleter)> write_statistics_fp_deleter{nullptr,deleter};
      95             : 
      96             : 
      97             : public:
      98        5418 :   static void registerKeywords( Keywords& keys ) {
      99        5418 :     CLTool::registerKeywords( keys );
     100        5418 :     keys.add("compulsory","nstep","The number of steps of dynamics you want to run");
     101        5418 :     keys.add("compulsory","temperature","NVE","the temperature at which you wish to run the simulation in LJ units");
     102        5418 :     keys.add("compulsory","friction","off","The friction (in LJ units) for the Langevin thermostat that is used to keep the temperature constant");
     103        5418 :     keys.add("compulsory","tstep","0.005","the integration timestep in LJ units");
     104        5418 :     keys.add("compulsory","epsilon","1.0","LJ parameter");
     105        5418 :     keys.add("compulsory","sigma","1.0","LJ parameter");
     106        5418 :     keys.add("compulsory","inputfile","An xyz file containing the initial configuration of the system");
     107        5418 :     keys.add("compulsory","forcecutoff","2.5","");
     108        5418 :     keys.add("compulsory","listcutoff","3.0","");
     109        5418 :     keys.add("compulsory","outputfile","An output xyz file containing the final configuration of the system");
     110        5418 :     keys.add("compulsory","nconfig","10","The frequency with which to write configurations to the trajectory file followed by the name of the trajectory file");
     111        5418 :     keys.add("compulsory","nstat","1","The frequency with which to write the statistics to the statistics file followed by the name of the statistics file");
     112        5418 :     keys.add("compulsory","idum","0","The random number seed");
     113        5418 :     keys.add("compulsory","ndim","3","The dimensionality of the system (some interesting LJ clusters are two dimensional)");
     114        5418 :     keys.add("compulsory","wrapatoms","false","If true, atomic coordinates are written wrapped in minimal cell");
     115        5418 :   }
     116             : 
     117          14 :   explicit SimpleMD( const CLToolOptions& co ) :
     118             :     CLTool(co),
     119          14 :     write_positions_first(true),
     120          14 :     write_statistics_first(true),
     121          14 :     write_statistics_last_time_reopened(0),
     122          14 :     write_statistics_fp(NULL) {
     123          14 :     inputdata=ifile;
     124          14 :   }
     125             : 
     126             : private:
     127             : 
     128             :   void
     129           9 :   read_input(double& temperature,
     130             :              double& tstep,
     131             :              double& friction,
     132             :              double& forcecutoff,
     133             :              double& listcutoff,
     134             :              int&    nstep,
     135             :              int&    nconfig,
     136             :              int&    nstat,
     137             :              bool&   wrapatoms,
     138             :              std::string& inputfile,
     139             :              std::string& outputfile,
     140             :              std::string& trajfile,
     141             :              std::string& statfile,
     142             :              int&    ndim,
     143             :              int&    idum,
     144             :              double& epsilon,
     145             :              double& sigma) {
     146             : 
     147             :     // Read everything from input file
     148             :     char buffer1[256];
     149             :     std::string tempstr;
     150          18 :     parse("temperature",tempstr);
     151           9 :     if( tempstr!="NVE" ) {
     152           9 :       Tools::convert(tempstr,temperature);
     153             :     }
     154          18 :     parse("tstep",tstep);
     155             :     std::string frictionstr;
     156          18 :     parse("friction",frictionstr);
     157           9 :     if( tempstr!="NVE" ) {
     158           9 :       if(frictionstr=="off") {
     159           0 :         error("Specify friction for thermostat");
     160             :       }
     161           9 :       Tools::convert(frictionstr,friction);
     162             :     }
     163           9 :     parse("forcecutoff",forcecutoff);
     164           9 :     parse("listcutoff",listcutoff);
     165           9 :     parse("nstep",nstep);
     166           9 :     parse("idum",idum);
     167           9 :     parse("epsilon",epsilon);
     168           9 :     parse("sigma",sigma);
     169             : 
     170             :     // Read in stuff with sanity checks
     171          18 :     parse("inputfile",inputfile);
     172           9 :     if(inputfile.length()==0) {
     173           0 :       error("Specify input file");
     174             :     }
     175          18 :     parse("outputfile",outputfile);
     176           9 :     if(outputfile.length()==0) {
     177           0 :       error("Specify output file");
     178             :     }
     179             :     std::string nconfstr;
     180          18 :     parse("nconfig",nconfstr);
     181           9 :     std::sscanf(nconfstr.c_str(),"%100d %255s",&nconfig,buffer1);
     182             :     trajfile=buffer1;
     183           9 :     if(trajfile.length()==0) {
     184           0 :       error("Specify traj file");
     185             :     }
     186             :     std::string nstatstr;
     187          18 :     parse("nstat",nstatstr);
     188           9 :     std::sscanf(nstatstr.c_str(),"%100d %255s",&nstat,buffer1);
     189             :     statfile=buffer1;
     190           9 :     if(statfile.length()==0) {
     191           0 :       error("Specify stat file");
     192             :     }
     193           9 :     parse("ndim",ndim);
     194           9 :     if(ndim<1 || ndim>3) {
     195           0 :       error("ndim should be 1,2 or 3");
     196             :     }
     197             :     std::string w;
     198           9 :     parse("wrapatoms",w);
     199           9 :     wrapatoms=false;
     200           9 :     if(w.length()>0 && (w[0]=='T' || w[0]=='t')) {
     201           2 :       wrapatoms=true;
     202             :     }
     203           9 :   }
     204             : 
     205           9 :   void read_natoms(const std::string & inputfile,int & natoms) {
     206             : // read the number of atoms in file "input.xyz"
     207           9 :     FILE* fp=std::fopen(inputfile.c_str(),"r");
     208           9 :     if(!fp) {
     209           0 :       error(std::string("file ") + inputfile + std::string(" not found"));
     210             :     }
     211             : 
     212             : // call fclose when fp goes out of scope
     213             :     std::unique_ptr<FILE,decltype(deleter)> fp_deleter(fp,deleter);
     214             : 
     215           9 :     int ret=std::fscanf(fp,"%1000d",&natoms);
     216           9 :     if(ret==0) {
     217           0 :       plumed_error() <<"Error reading number of atoms from file "<<inputfile;
     218             :     }
     219           9 :   }
     220             : 
     221           9 :   void read_positions(const std::string& inputfile,int natoms,std::vector<Vector>& positions,double cell[3]) {
     222             : // read positions and cell from a file called inputfile
     223             : // natoms (input variable) and number of atoms in the file should be consistent
     224           9 :     FILE* fp=std::fopen(inputfile.c_str(),"r");
     225           9 :     if(!fp) {
     226           0 :       error(std::string("file ") + inputfile + std::string(" not found"));
     227             :     }
     228             : // call fclose when fp goes out of scope
     229             :     std::unique_ptr<FILE,decltype(deleter)> fp_deleter(fp,deleter);
     230             : 
     231             :     char buffer[256];
     232             :     char atomname[256];
     233             :     char* cret=fgets(buffer,256,fp);
     234           9 :     if(cret==nullptr) {
     235           0 :       plumed_error() <<"Error reading buffer from file "<<inputfile;
     236             :     }
     237           9 :     int ret=std::fscanf(fp,"%1000lf %1000lf %1000lf",&cell[0],&cell[1],&cell[2]);
     238           9 :     if(ret==0) {
     239           0 :       plumed_error() <<"Error reading cell line from file "<<inputfile;
     240             :     }
     241         880 :     for(int i=0; i<natoms; i++) {
     242         871 :       ret=std::fscanf(fp,"%255s %1000lf %1000lf %1000lf",atomname,&positions[i][0],&positions[i][1],&positions[i][2]);
     243             : // note: atomname is read but not used
     244         871 :       if(ret==0) {
     245           0 :         plumed_error() <<"Error reading atom line from file "<<inputfile;
     246             :       }
     247             :     }
     248           9 :   }
     249             : 
     250           9 :   void randomize_velocities(const int natoms,const int ndim,const double temperature,const std::vector<double>&masses,std::vector<Vector>& velocities,Random&random) {
     251             : // randomize the velocities according to the temperature
     252         880 :     for(int iatom=0; iatom<natoms; iatom++)
     253        3477 :       for(int i=0; i<ndim; i++) {
     254        2606 :         velocities[iatom][i]=std::sqrt(temperature/masses[iatom])*random.Gaussian();
     255             :       }
     256           9 :   }
     257             : 
     258    10165978 :   void pbc(const double cell[3],const Vector & vin,Vector & vout) {
     259             : // apply periodic boundary condition to a vector
     260    40663912 :     for(int i=0; i<3; i++) {
     261    30497934 :       vout[i]=vin[i]-std::floor(vin[i]/cell[i]+0.5)*cell[i];
     262             :     }
     263    10165978 :   }
     264             : 
     265        3750 :   void check_list(const int natoms,const std::vector<Vector>& positions,const std::vector<Vector>&positions0,const double listcutoff,
     266             :                   const double forcecutoff,bool & recompute) {
     267             : // check if the neighbour list have to be recomputed
     268        3750 :     recompute=false;
     269        3750 :     auto delta2=(0.5*(listcutoff-forcecutoff))*(0.5*(listcutoff-forcecutoff));
     270             : // if ANY atom moved more than half of the skin thickness, recompute is set to .true.
     271      206750 :     for(int iatom=0; iatom<natoms; iatom++) {
     272      203000 :       if(modulo2(positions[iatom]-positions0[iatom])>delta2) {
     273         736 :         recompute=true;
     274             :       }
     275             :     }
     276        3750 :   }
     277             : 
     278             : 
     279         437 :   void compute_list(const int natoms,const std::vector<Vector>& positions,const double cell[3],const double listcutoff,
     280             :                     std::vector<std::vector<int> >& list) {
     281         437 :     double listcutoff2=listcutoff*listcutoff; // squared list cutoff
     282         437 :     list.assign(natoms,std::vector<int>());
     283         437 :     #   pragma omp parallel for num_threads(OpenMP::getNumThreads()) schedule(static,1)
     284             :     for(int iatom=0; iatom<natoms-1; iatom++) {
     285             :       for(int jatom=iatom+1; jatom<natoms; jatom++) {
     286             :         auto distance=positions[iatom]-positions[jatom];
     287             :         Vector distance_pbc; // minimum-image distance of the two atoms
     288             :         pbc(cell,distance,distance_pbc);
     289             : // if the interparticle distance is larger than the cutoff, skip
     290             :         if(modulo2(distance_pbc)>listcutoff2) {
     291             :           continue;
     292             :         }
     293             :         list[iatom].push_back(jatom);
     294             :       }
     295             :     }
     296         437 :   }
     297             : 
     298        3759 :   void compute_forces(const int natoms,double epsilon, double sigma,const std::vector<Vector>& positions,const double cell[3],
     299             :                       double forcecutoff,const std::vector<std::vector<int> >& list,std::vector<Vector>& forces,double & engconf) {
     300        3759 :     double forcecutoff2=forcecutoff*forcecutoff; // squared force cutoff
     301        3759 :     engconf=0.0;
     302             :     double engconf_tmp=0.0; // separate variable for reduction (gcc 4.8 does not make reductions on references)
     303      207630 :     for(int i=0; i<natoms; i++)
     304      815484 :       for(int k=0; k<3; k++) {
     305      611613 :         forces[i][k]=0.0;
     306             :       }
     307        3759 :     double engcorrection=4.0*epsilon*(1.0/std::pow(forcecutoff2/(sigma*sigma),6.0)-1.0/std::pow(forcecutoff2/(sigma*sigma),3)); // energy necessary shift the potential avoiding discontinuities
     308        3759 :     #   pragma omp parallel num_threads(OpenMP::getNumThreads())
     309             :     {
     310             :       std::vector<Vector> omp_forces(forces.size());
     311             :       #pragma omp for reduction(+:engconf_tmp) schedule(static,1) nowait
     312             :       for(int iatom=0; iatom<natoms-1; iatom++) {
     313             :         for(int jlist=0; jlist<list[iatom].size(); jlist++) {
     314             :           const int jatom=list[iatom][jlist];
     315             :           auto distance=positions[iatom]-positions[jatom];
     316             :           Vector distance_pbc;    // minimum-image distance of the two atoms
     317             :           pbc(cell,distance,distance_pbc);
     318             :           auto distance_pbc2=modulo2(distance_pbc);   // squared minimum-image distance
     319             : // if the interparticle distance is larger than the cutoff, skip
     320             :           if(distance_pbc2>forcecutoff2) {
     321             :             continue;
     322             :           }
     323             :           auto distance_pbcm2=sigma*sigma/distance_pbc2;
     324             :           auto distance_pbcm6=distance_pbcm2*distance_pbcm2*distance_pbcm2;
     325             :           auto distance_pbcm8=distance_pbcm6*distance_pbcm2;
     326             :           auto distance_pbcm12=distance_pbcm6*distance_pbcm6;
     327             :           auto distance_pbcm14=distance_pbcm12*distance_pbcm2;
     328             :           engconf_tmp+=4.0*epsilon*(distance_pbcm12 - distance_pbcm6) - engcorrection;
     329             :           auto f=24.0*distance_pbc*(2.0*distance_pbcm14-distance_pbcm8)*epsilon/sigma;
     330             :           omp_forces[iatom]+=f;
     331             :           omp_forces[jatom]-=f;
     332             :         }
     333             :       }
     334             :       #     pragma omp critical
     335             :       for(unsigned i=0; i<omp_forces.size(); i++) {
     336             :         forces[i]+=omp_forces[i];
     337             :       }
     338             :     }
     339             : 
     340        3759 :     engconf=engconf_tmp;
     341             : 
     342        3759 :   }
     343             : 
     344        3750 :   void compute_engkin(const int natoms,const std::vector<double>& masses,const std::vector<Vector>& velocities,double & engkin) {
     345             : // calculate the kinetic energy from the velocities
     346        3750 :     engkin=0.0;
     347      206750 :     for(int iatom=0; iatom<natoms; iatom++) {
     348      203000 :       engkin+=0.5*masses[iatom]*modulo2(velocities[iatom]);
     349             :     }
     350        3750 :   }
     351             : 
     352             : 
     353        7500 :   void thermostat(const int natoms,const int ndim,const std::vector<double>& masses,const double dt,const double friction,
     354             :                   const double temperature,std::vector<Vector>& velocities,double & engint,Random & random) {
     355             : // Langevin thermostat, implemented as described in Bussi and Parrinello, Phys. Rev. E (2007)
     356             : // it is a linear combination of old velocities and new, randomly chosen, velocity,
     357             : // with proper coefficients
     358        7500 :     double c1=std::exp(-friction*dt);
     359      413500 :     for(int iatom=0; iatom<natoms; iatom++) {
     360      406000 :       double c2=std::sqrt((1.0-c1*c1)*temperature/masses[iatom]);
     361     1596000 :       for(int i=0; i<ndim; i++) {
     362     1190000 :         engint+=0.5*masses[iatom]*velocities[iatom][i]*velocities[iatom][i];
     363     1190000 :         velocities[iatom][i]=c1*velocities[iatom][i]+c2*random.Gaussian();
     364     1190000 :         engint-=0.5*masses[iatom]*velocities[iatom][i]*velocities[iatom][i];
     365             :       }
     366             :     }
     367        7500 :   }
     368             : 
     369          42 :   void write_positions(const std::string& trajfile,int natoms,const std::vector<Vector>& positions,const double cell[3],const bool wrapatoms) {
     370             : // write positions on file trajfile
     371             : // positions are appended at the end of the file
     372          42 :     Vector pos;
     373             :     FILE*fp;
     374          42 :     if(write_positions_first) {
     375           9 :       fp=std::fopen(trajfile.c_str(),"w");
     376           9 :       write_positions_first=false;
     377             :     } else {
     378          33 :       fp=std::fopen(trajfile.c_str(),"a");
     379             :     }
     380          42 :     plumed_assert(fp);
     381             : // call fclose when fp goes out of scope
     382             :     std::unique_ptr<FILE,decltype(deleter)> fp_deleter(fp,deleter);
     383             :     std::fprintf(fp,"%d\n",natoms);
     384          42 :     std::fprintf(fp,"%f %f %f\n",cell[0],cell[1],cell[2]);
     385        4376 :     for(int iatom=0; iatom<natoms; iatom++) {
     386             : // usually, it is better not to apply pbc here, so that diffusion
     387             : // is more easily calculated from a trajectory file:
     388        4334 :       if(wrapatoms) {
     389        1080 :         pbc(cell,positions[iatom],pos);
     390             :       } else {
     391        3254 :         pos=positions[iatom];
     392             :       }
     393        4334 :       std::fprintf(fp,"Ar %10.7f %10.7f %10.7f\n",pos[0],pos[1],pos[2]);
     394             :     }
     395          42 :   }
     396             : 
     397           9 :   void write_final_positions(const std::string& outputfile,int natoms,const std::vector<Vector>& positions,const double cell[3],const bool wrapatoms) {
     398             : // write positions on file outputfile
     399           9 :     Vector pos;
     400             :     FILE*fp;
     401           9 :     fp=std::fopen(outputfile.c_str(),"w");
     402           9 :     plumed_assert(fp);
     403             : // call fclose when fp goes out of scope
     404             :     std::unique_ptr<FILE,decltype(deleter)> fp_deleter(fp,deleter);
     405             :     std::fprintf(fp,"%d\n",natoms);
     406           9 :     std::fprintf(fp,"%f %f %f\n",cell[0],cell[1],cell[2]);
     407         880 :     for(int iatom=0; iatom<natoms; iatom++) {
     408             : // usually, it is better not to apply pbc here, so that diffusion
     409             : // is more easily calculated from a trajectory file:
     410         871 :       if(wrapatoms) {
     411         216 :         pbc(cell,positions[iatom],pos);
     412             :       } else {
     413         655 :         pos=positions[iatom];
     414             :       }
     415         871 :       std::fprintf(fp,"Ar %10.7f %10.7f %10.7f\n",pos[0],pos[1],pos[2]);
     416             :     }
     417           9 :   }
     418             : 
     419             : 
     420         177 :   void write_statistics(const std::string & statfile,const int istep,const double tstep,
     421             :                         const int natoms,const int ndim,const double engkin,const double engconf,const double engint) {
     422             : // write statistics on file statfile
     423         177 :     if(write_statistics_first) {
     424             : // first time this routine is called, open the file
     425           9 :       write_statistics_fp=std::fopen(statfile.c_str(),"w");
     426             :       write_statistics_fp_deleter.reset(write_statistics_fp);
     427           9 :       write_statistics_first=false;
     428             :     }
     429         177 :     if(istep-write_statistics_last_time_reopened>100) {
     430             : // every 100 steps, reopen the file to flush the buffer
     431             :       write_statistics_fp_deleter.reset(nullptr); // close file
     432          14 :       write_statistics_fp=std::fopen(statfile.c_str(),"a");
     433             :       write_statistics_fp_deleter.reset(write_statistics_fp);
     434          14 :       write_statistics_last_time_reopened=istep;
     435             :     }
     436         177 :     std::fprintf(write_statistics_fp,"%d %f %f %f %f %f\n",istep,istep*tstep,2.0*engkin/double(ndim*natoms),engconf,engkin+engconf,engkin+engconf+engint);
     437         177 :   }
     438             : 
     439             : 
     440             : 
     441           9 :   int main(FILE* in,FILE*out,PLMD::Communicator& pc) override {
     442             :     int            natoms;       // number of atoms
     443             :     std::vector<Vector> positions;    // atomic positions
     444             :     std::vector<Vector> velocities;   // velocities
     445             :     std::vector<double> masses;       // masses
     446             :     std::vector<Vector> forces;       // forces
     447             :     double         cell[3];      // cell size
     448             :     double         cell9[3][3];  // cell size
     449             : 
     450             : // neighbour list variables
     451             : // see Allen and Tildesey book for details
     452             :     std::vector< std::vector<int> >  list; // neighbour list
     453             :     std::vector<Vector> positions0;   // reference atomic positions, i.e. positions when the neighbour list
     454             : 
     455             : // input parameters
     456             : // all of them have a reasonable default value, set in read_input()
     457             :     double      tstep;             // simulation timestep
     458             :     double      temperature;       // temperature
     459             :     double      friction;          // friction for Langevin dynamics (for NVE, use 0)
     460             :     double      listcutoff;        // cutoff for neighbour list
     461             :     double      forcecutoff;       // cutoff for forces
     462             :     int         nstep;             // number of steps
     463             :     int         nconfig;           // stride for output of configurations
     464             :     int         nstat;             // stride for output of statistics
     465             :     int         ndim;              // dimensionality of the system (1, 2, or 3)
     466             :     int         idum;              // seed
     467             :     int         plumedWantsToStop; // stop flag
     468             :     bool        wrapatoms;         // if true, atomic coordinates are written wrapped in minimal cell
     469             :     std::string inputfile;         // name of file with starting configuration (xyz)
     470             :     std::string outputfile;        // name of file with final configuration (xyz)
     471             :     std::string trajfile;          // name of the trajectory file (xyz)
     472             :     std::string statfile;          // name of the file with statistics
     473             : 
     474             :     double      epsilon, sigma;    // LJ parameters
     475             : 
     476             :     double engkin;                 // kinetic energy
     477             :     double engconf;                // configurational energy
     478             :     double engint;                 // integral for conserved energy in Langevin dynamics
     479             : 
     480             :     bool recompute_list;           // control if the neighbour list have to be recomputed
     481             : 
     482           9 :     Random random;                 // random numbers stream
     483             : 
     484           9 :     std::unique_ptr<PlumedMain> plumed;
     485             : 
     486             : // Commenting the next line it is possible to switch-off plumed
     487           9 :     plumed=Tools::make_unique<PLMD::PlumedMain>();
     488             : 
     489           9 :     if(plumed) {
     490           9 :       plumed->cmd("setRealPrecision",int(sizeof(double)));
     491             :     }
     492             : 
     493           9 :     read_input(temperature,tstep,friction,forcecutoff,
     494             :                listcutoff,nstep,nconfig,nstat,
     495             :                wrapatoms,inputfile,outputfile,trajfile,statfile,
     496             :                ndim,idum,epsilon,sigma);
     497             : 
     498             : // number of atoms is read from file inputfile
     499           9 :     read_natoms(inputfile,natoms);
     500             : 
     501             : // write the parameters in output so they can be checked
     502             :     std::fprintf(out,"%s %s\n","Starting configuration           :",inputfile.c_str());
     503             :     std::fprintf(out,"%s %s\n","Final configuration              :",outputfile.c_str());
     504           9 :     std::fprintf(out,"%s %d\n","Number of atoms                  :",natoms);
     505           9 :     std::fprintf(out,"%s %f\n","Temperature                      :",temperature);
     506           9 :     std::fprintf(out,"%s %f\n","Time step                        :",tstep);
     507           9 :     std::fprintf(out,"%s %f\n","Friction                         :",friction);
     508           9 :     std::fprintf(out,"%s %f\n","Cutoff for forces                :",forcecutoff);
     509           9 :     std::fprintf(out,"%s %f\n","Cutoff for neighbour list        :",listcutoff);
     510           9 :     std::fprintf(out,"%s %d\n","Number of steps                  :",nstep);
     511           9 :     std::fprintf(out,"%s %d\n","Stride for trajectory            :",nconfig);
     512             :     std::fprintf(out,"%s %s\n","Trajectory file                  :",trajfile.c_str());
     513           9 :     std::fprintf(out,"%s %d\n","Stride for statistics            :",nstat);
     514             :     std::fprintf(out,"%s %s\n","Statistics file                  :",statfile.c_str());
     515           9 :     std::fprintf(out,"%s %d\n","Dimensionality                   :",ndim);
     516           9 :     std::fprintf(out,"%s %d\n","Seed                             :",idum);
     517           9 :     std::fprintf(out,"%s %s\n","Are atoms wrapped on output?     :",(wrapatoms?"T":"F"));
     518           9 :     std::fprintf(out,"%s %f\n","Epsilon                          :",epsilon);
     519           9 :     std::fprintf(out,"%s %f\n","Sigma                            :",sigma);
     520             : 
     521             : // Setting the seed
     522           9 :     random.setSeed(idum);
     523             : 
     524             : // allocation of dynamical arrays
     525           9 :     positions.resize(natoms);
     526           9 :     positions0.resize(natoms);
     527           9 :     velocities.resize(natoms);
     528           9 :     forces.resize(natoms);
     529           9 :     masses.resize(natoms);
     530           9 :     list.resize(natoms);
     531             : 
     532             : // masses are hard-coded to 1
     533         880 :     for(int i=0; i<natoms; ++i) {
     534         871 :       masses[i]=1.0;
     535             :     }
     536             : 
     537             : // energy integral initialized to 0
     538           9 :     engint=0.0;
     539             : 
     540             : // positions are read from file inputfile
     541           9 :     read_positions(inputfile,natoms,positions,cell);
     542             : 
     543             : // velocities are randomized according to temperature
     544           9 :     randomize_velocities(natoms,ndim,temperature,masses,velocities,random);
     545             : 
     546           9 :     if(plumed) {
     547           9 :       plumed->cmd("setNatoms",natoms);
     548           9 :       plumed->cmd("setNoVirial");
     549           9 :       plumed->cmd("setMDEngine","simpleMD");
     550           9 :       plumed->cmd("setTimestep",tstep);
     551           9 :       plumed->cmd("setPlumedDat","plumed.dat");
     552           9 :       int pversion=0;
     553           9 :       plumed->cmd("getApiVersion",&pversion);
     554             : // setting kbT is only implemented with api>1
     555             : // even if not necessary in principle in SimpleMD (which is part of plumed)
     556             : // we leave the check here as a reference
     557           9 :       if(pversion>1) {
     558           9 :         plumed->cmd("setKbT",temperature);
     559             :       }
     560           9 :       plumed->cmd("init");
     561             :     }
     562             : 
     563             : // neighbour list are computed, and reference positions are saved
     564           9 :     compute_list(natoms,positions,cell,listcutoff,list);
     565             : 
     566             :     int list_size=0;
     567         880 :     for(int i=0; i<list.size(); i++) {
     568         871 :       list_size+=list[i].size();
     569             :     }
     570             :     std::fprintf(out,"List size: %d\n",list_size);
     571         880 :     for(int iatom=0; iatom<natoms; ++iatom) {
     572         871 :       positions0[iatom]=positions[iatom];
     573             :     }
     574             : 
     575             : // forces are computed before starting md
     576           9 :     compute_forces(natoms,epsilon,sigma,positions,cell,forcecutoff,list,forces,engconf);
     577             : 
     578             : // remove forces if ndim<3
     579           9 :     if(ndim<3)
     580           8 :       for(int iatom=0; iatom<natoms; ++iatom)
     581          14 :         for(int k=ndim; k<3; ++k) {
     582           7 :           forces[iatom][k]=0.0;
     583             :         }
     584             : 
     585             : // here is the main md loop
     586             : // Langevin thermostat is applied before and after a velocity-Verlet integrator
     587             : // the overall structure is:
     588             : //   thermostat
     589             : //   update velocities
     590             : //   update positions
     591             : //   (eventually recompute neighbour list)
     592             : //   compute forces
     593             : //   update velocities
     594             : //   thermostat
     595             : //   (eventually dump output informations)
     596        3759 :     for(int istep=0; istep<nstep; istep++) {
     597        3750 :       thermostat(natoms,ndim,masses,0.5*tstep,friction,temperature,velocities,engint,random);
     598             : 
     599      206750 :       for(int iatom=0; iatom<natoms; iatom++) {
     600      203000 :         velocities[iatom]+=forces[iatom]*0.5*tstep/masses[iatom];
     601             :       }
     602             : 
     603      206750 :       for(int iatom=0; iatom<natoms; iatom++) {
     604      203000 :         positions[iatom]+=velocities[iatom]*tstep;
     605             :       }
     606             : 
     607             : // a check is performed to decide whether to recalculate the neighbour list
     608        3750 :       check_list(natoms,positions,positions0,listcutoff,forcecutoff,recompute_list);
     609        3750 :       if(recompute_list) {
     610         428 :         compute_list(natoms,positions,cell,listcutoff,list);
     611       44329 :         for(int iatom=0; iatom<natoms; ++iatom) {
     612       43901 :           positions0[iatom]=positions[iatom];
     613             :         }
     614             :         int list_size=0;
     615       44329 :         for(int i=0; i<list.size(); i++) {
     616       43901 :           list_size+=list[i].size();
     617             :         }
     618             :         std::fprintf(out,"List size: %d\n",list_size);
     619             :       }
     620             : 
     621        3750 :       compute_forces(natoms,epsilon,sigma,positions,cell,forcecutoff,list,forces,engconf);
     622             : 
     623        3750 :       if(plumed) {
     624        3750 :         plumedWantsToStop=0;
     625       15000 :         for(int i=0; i<3; i++)
     626       45000 :           for(int k=0; k<3; k++) {
     627       33750 :             cell9[i][k]=0.0;
     628             :           }
     629       15000 :         for(int i=0; i<3; i++) {
     630       11250 :           cell9[i][i]=cell[i];
     631             :         }
     632        3750 :         plumed->cmd("setStep",istep+1);
     633        3750 :         plumed->cmd("setMasses",&masses[0]);
     634        3750 :         plumed->cmd("setForces",&forces[0][0]);
     635        3750 :         plumed->cmd("setEnergy",engconf);
     636        3750 :         plumed->cmd("setPositions",&positions[0][0]);
     637        3750 :         plumed->cmd("setBox",&cell9[0][0]);
     638        3750 :         plumed->cmd("setStopFlag",&plumedWantsToStop);
     639        3750 :         plumed->cmd("calc");
     640        3750 :         if(plumedWantsToStop) {
     641           0 :           nstep=istep;
     642             :         }
     643             :       }
     644             : // remove forces if ndim<3
     645        3750 :       if(ndim<3)
     646       16000 :         for(int iatom=0; iatom<natoms; ++iatom)
     647       28000 :           for(int k=ndim; k<3; ++k) {
     648       14000 :             forces[iatom][k]=0.0;
     649             :           }
     650             : 
     651      206750 :       for(int iatom=0; iatom<natoms; iatom++) {
     652      203000 :         velocities[iatom]+=forces[iatom]*0.5*tstep/masses[iatom];
     653             :       }
     654             : 
     655        3750 :       thermostat(natoms,ndim,masses,0.5*tstep,friction,temperature,velocities,engint,random);
     656             : 
     657             : // kinetic energy is calculated
     658        3750 :       compute_engkin(natoms,masses,velocities,engkin);
     659             : 
     660             : // eventually, write positions and statistics
     661        3750 :       if((istep+1)%nconfig==0) {
     662          42 :         write_positions(trajfile,natoms,positions,cell,wrapatoms);
     663             :       }
     664        3750 :       if((istep+1)%nstat==0) {
     665         177 :         write_statistics(statfile,istep+1,tstep,natoms,ndim,engkin,engconf,engint);
     666             :       }
     667             : 
     668             :     }
     669             : 
     670             : // call final plumed jobs
     671           9 :     plumed->cmd("runFinalJobs");
     672             : 
     673             : // write final positions
     674           9 :     write_final_positions(outputfile,natoms,positions,cell,wrapatoms);
     675             : 
     676           9 :     return 0;
     677          18 :   }
     678             : 
     679             : 
     680             : };
     681             : 
     682       16268 : PLUMED_REGISTER_CLTOOL(SimpleMD,"simplemd")
     683             : 
     684             : }
     685             : }
     686             : 
     687             : 
     688             : 
     689             : 

Generated by: LCOV version 1.16