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

Generated by: LCOV version 1.15