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

Generated by: LCOV version 1.16