LCOV - code coverage report
Current view: top level - generic - Read.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 108 124 87.1 %
Date: 2026-11-05 10:19:04 Functions: 10 12 83.3 %

          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 "core/ActionPilot.h"
      23             : #include "core/ActionWithValue.h"
      24             : #include "core/ActionRegister.h"
      25             : #include "core/PlumedMain.h"
      26             : #include "core/ActionSet.h"
      27             : #include "tools/IFile.h"
      28             : #include <memory>
      29             : 
      30             : namespace PLMD {
      31             : namespace generic {
      32             : 
      33             : //+PLUMEDOC GENERIC READ
      34             : /*
      35             : Read quantities from a colvar file.
      36             : 
      37             : This Action can be used with driver to read in a colvar file that was generated during
      38             : an MD simulation. The following example shows how this works.
      39             : 
      40             : ```plumed
      41             : #SETTINGS INPUTFILES=regtest/basic/rt-fametad-1/Input-COLVAR
      42             : sum_abs: READ VALUES=sum_abs FILE=regtest/basic/rt-fametad-1/Input-COLVAR IGNORE_FORCES
      43             : PRINT ARG=sum_abs STRIDE=1 FILE=colvar
      44             : ```
      45             : 
      46             : The input file `Input-COLVAR` is a colvar file that was generated using a [PRINT](PRINT.md)
      47             : command.  The instruction `VALUES=sum_abs` tells PLUMED that we want to read the column headed
      48             : `sum_abs` from that file.  The value outputted from a read command is thus always a scalar.
      49             : 
      50             : The `IGNORE_FORCES` flag tells PLUMED that any forces that are applied on
      51             : this action can be safely ignored.  If you try to run a simulation in which a bias acts upon a
      52             : quantity that is read in from a file using a READ command PLUMED will crash with an error.
      53             : 
      54             : ## Dealing with components
      55             : 
      56             : The following example provides another example where the READ command is used:
      57             : 
      58             : ```plumed
      59             : #SETTINGS INPUTFILES=regtest/basic/rt41/input_colvar
      60             : r1: READ VALUES=p2.X  FILE=regtest/basic/rt41/input_colvar
      61             : r2: READ VALUES=p3.* FILE=regtest/basic/rt41/input_colvar
      62             : PRINT ARG=r1.X,r2.* FILE=colvar
      63             : ```
      64             : 
      65             : Notice that the READ command preseves the part of the name that appears after the ".".  Consequently,
      66             : when we read the value `p2.X` from the input file the output value the action with label `r1` calls the output
      67             : value that contains this information `r1.X`.  The READ command is implemented this way so that you can use the
      68             : wildcard syntax that is illustrated in the command labelled `r2` in the above input.
      69             : 
      70             : ## Reading COLVAR files and trajectories
      71             : 
      72             : The example input below indicates a case where a trajectory is being post processed and where some COLVAR files
      73             : that were generated when the MD simulation was run are read in.
      74             : 
      75             : ```plumed
      76             : #SETTINGS INPUTFILES=regtest/basic/rt19/input_colvar2
      77             : # The distance here is being calculated from the trajectory
      78             : d: DISTANCE ATOMS=1,2
      79             : # This CV is being read in from a file that was output with the same frequency as frames
      80             : # were output from the trajectory. Notice that you can read from zip files
      81             : r1: READ VALUES=rmsd0  FILE=regtest/basic/rt19/input_colvar.gz
      82             : # This CV is being read in from a file that was output with twice as frequency as frames
      83             : r2: READ VALUES=rmsd0  FILE=regtest/basic/rt19/input_colvar2 EVERY=2 IGNORE_TIME
      84             : # We start reading this CV from this file after we have read the first 100 ps of the trajectory
      85             : # and stop reading from it after we have read the first 800 ps of the trajectory
      86             : r3: READ VALUES=rmsd0 FILE=regtest/basic/rt19/input_colvar2 UPDATE_FROM=100 UPDATE_UNTIL=800 IGNORE_TIME
      87             : # This outputs our the three quantities that are determined for every step
      88             : PRINT ARG=d,r1,r2 FILE=colvar
      89             : # This outputs the data that we only have for the 700 ps of the trajectory after the first 100 ps
      90             : PRINT ARG=d,r1,r2,r3 FILE=colvar2 UPDATE_FROM=100 UPDATE_UNTIL=800
      91             : ```
      92             : 
      93             : The driver command to run this script as follows:
      94             : 
      95             : ````
      96             : plumed driver --plumed plumed.dat --trajectory-stride 10 --timestep 0.005 --ixyz trajectory.xyz
      97             : ````
      98             : 
      99             : When you are doing analyses like these, which involve mixing using READ command and analysing a trajectory, PLUMED forces you
     100             : to take care to ensure that everything in the input file was generated from the same step in the input trajectory.  You must
     101             : use the `--trajectory-stride` and `--timestep` commands when using driver above so PLUMED can correctly calculate the simulation
     102             : time and compare it with the time stamps in any colvar files that are being read in using READ commands.
     103             : 
     104             : If you want to turn off these checks either because you are confident that you have set things up correctly or if you are not
     105             : mixing variables that have been calculated from a trajectory with variables that are being read from a file you can use the
     106             : `IGNORE_TIME` flag.  Notice also that you can use the `EVERY` flag to tell PLUMED to ignore parts of the COLVAR file if data
     107             : has been output to that file more frequently that data has been output to the trajectory.
     108             : 
     109             : */
     110             : //+ENDPLUMEDOC
     111             : 
     112             : class Read :
     113             :   public ActionPilot,
     114             :   public ActionWithValue {
     115             : private:
     116             :   bool ignore_time;
     117             :   bool ignore_forces;
     118             :   bool cloned_file;
     119             :   unsigned nlinesPerStep;
     120             :   std::string filename;
     121             : /// Unique pointer with the same scope as ifile.
     122             :   std::unique_ptr<IFile> ifile_ptr;
     123             : /// Pointer to input file.
     124             : /// It is either pointing to the content of ifile_ptr
     125             : /// or to the file it is cloned from.
     126             :   IFile* ifile;
     127             :   std::vector<std::unique_ptr<Value>> readvals;
     128             : public:
     129             :   static void registerKeywords( Keywords& keys );
     130             :   explicit Read(const ActionOptions&);
     131             :   std::string getOutputComponentDescription( const std::string& cname, const Keywords& keys ) const override ;
     132             :   void prepare() override;
     133      921430 :   void apply() override {}
     134             :   void calculate() override;
     135             :   void update() override;
     136             :   std::string getFilename() const;
     137             :   IFile* getFile();
     138             :   unsigned getNumberOfDerivatives() override;
     139             :   void turnOnDerivatives() override;
     140             : };
     141             : 
     142             : PLUMED_REGISTER_ACTION(Read,"READ")
     143             : 
     144          98 : void Read::registerKeywords(Keywords& keys) {
     145          98 :   Action::registerKeywords(keys);
     146          98 :   ActionPilot::registerKeywords(keys);
     147          98 :   ActionWithValue::registerKeywords(keys);
     148          98 :   keys.add("compulsory","STRIDE","1","the frequency with which the file should be read.");
     149          98 :   keys.add("compulsory","EVERY","1","only read every nth line of the colvar file. This should be used if the colvar was written more frequently than the trajectory.");
     150          98 :   keys.add("compulsory","VALUES","the values to read from the file");
     151          98 :   keys.add("compulsory","FILE","the name of the file from which to read these quantities");
     152          98 :   keys.addFlag("IGNORE_TIME",false,"ignore the time in the colvar file. When this flag is not present read will be quite strict "
     153             :                "about the start time of the simulation and the stride between frames");
     154          98 :   keys.addFlag("IGNORE_FORCES",false,"use this flag if the forces added by any bias can be safely ignored.  As an example forces can be "
     155             :                "safely ignored if you are doing post processing that does not involve outputting forces");
     156         196 :   keys.remove("NUMERICAL_DERIVATIVES");
     157          98 :   keys.use("UPDATE_FROM");
     158          98 :   keys.use("UPDATE_UNTIL");
     159          98 :   ActionWithValue::useCustomisableComponents(keys);
     160          98 : }
     161             : 
     162          92 : Read::Read(const ActionOptions&ao):
     163             :   Action(ao),
     164             :   ActionPilot(ao),
     165             :   ActionWithValue(ao),
     166          92 :   ignore_time(false),
     167          92 :   ignore_forces(false),
     168          92 :   nlinesPerStep(1) {
     169             :   // Read the file name from the input line
     170          92 :   parse("FILE",filename);
     171             :   // Check if time is to be ignored
     172          92 :   parseFlag("IGNORE_TIME",ignore_time);
     173             :   // Check if forces are to be ignored
     174          92 :   parseFlag("IGNORE_FORCES",ignore_forces);
     175             :   // Open the file if it is not already opened
     176          92 :   cloned_file=false;
     177          92 :   std::vector<Read*> other_reads=plumed.getActionSet().select<Read*>();
     178         142 :   for(unsigned i=0; i<other_reads.size(); ++i) {
     179          50 :     if( other_reads[i]->getFilename()==filename ) {
     180          47 :       ifile=other_reads[i]->getFile();
     181          47 :       cloned_file=true;
     182             :     }
     183             :   }
     184          92 :   if( !cloned_file ) {
     185          64 :     ifile_ptr=Tools::make_unique<IFile>();
     186          64 :     ifile=ifile_ptr.get();
     187          64 :     if( !ifile->FileExist(filename) ) {
     188           0 :       error("could not find file named " + filename);
     189             :     }
     190          64 :     ifile->link(*this);
     191          64 :     ifile->open(filename);
     192          64 :     ifile->allowIgnoredFields();
     193             :   }
     194          92 :   parse("EVERY",nlinesPerStep);
     195          92 :   if(nlinesPerStep>1) {
     196           2 :     log.printf("  only reading every %uth line of file %s\n",nlinesPerStep,filename.c_str() );
     197             :   } else {
     198          90 :     log.printf("  reading data from file %s\n",filename.c_str() );
     199             :   }
     200             :   // Find out what we are reading
     201          92 :   std::vector<std::string> valread;
     202          92 :   parseVector("VALUES",valread);
     203             : 
     204          92 :   if(nlinesPerStep>1 && cloned_file) {
     205           0 :     error("Opening a file multiple times and using EVERY is not allowed");
     206             :   }
     207             : 
     208             :   std::size_t dot=valread[0].find_first_of('.');
     209          92 :   if( valread[0].find(".")!=std::string::npos ) {
     210          11 :     std::string labelVal=valread[0].substr(0,dot);
     211          11 :     std::string nameVal=valread[0].substr(dot+1);
     212          11 :     if( nameVal=="*" ) {
     213           2 :       if( valread.size()>1 ) {
     214           0 :         error("all values must be from the same Action when using READ");
     215             :       }
     216           2 :       std::vector<std::string> fieldnames;
     217           2 :       ifile->scanFieldList( fieldnames );
     218          16 :       for(unsigned i=0; i<fieldnames.size(); ++i) {
     219          14 :         if( fieldnames[i].substr(0,dot)==labelVal ) {
     220           6 :           readvals.emplace_back(Tools::make_unique<Value>(this, fieldnames[i], false) );
     221          12 :           addComponentWithDerivatives( fieldnames[i].substr(dot+1) );
     222          12 :           if( ifile->FieldExist("min_" + fieldnames[i]) ) {
     223           0 :             componentIsPeriodic( fieldnames[i].substr(dot+1), "-pi","pi" );
     224             :           } else {
     225          12 :             componentIsNotPeriodic( fieldnames[i].substr(dot+1) );
     226             :           }
     227             :         }
     228             :       }
     229           2 :     } else {
     230           9 :       readvals.emplace_back(Tools::make_unique<Value>(this, valread[0], false) );
     231           9 :       addComponentWithDerivatives( nameVal );
     232          18 :       if( ifile->FieldExist("min_" + valread[0]) ) {
     233           0 :         componentIsPeriodic( valread[0].substr(dot+1), "-pi", "pi" );
     234             :       } else {
     235          18 :         componentIsNotPeriodic( valread[0].substr(dot+1) );
     236             :       }
     237          12 :       for(unsigned i=1; i<valread.size(); ++i) {
     238           3 :         if( valread[i].substr(0,dot)!=labelVal ) {
     239           0 :           error("all values must be from the same Action when using READ");
     240             :         };
     241           3 :         readvals.emplace_back(Tools::make_unique<Value>(this, valread[i], false) );
     242           6 :         addComponentWithDerivatives( valread[i].substr(dot+1) );
     243           6 :         if( ifile->FieldExist("min_" + valread[i]) ) {
     244           0 :           componentIsPeriodic( valread[i].substr(dot+1), "-pi", "pi" );
     245             :         } else {
     246           6 :           componentIsNotPeriodic( valread[i].substr(dot+1) );
     247             :         }
     248             :       }
     249             :     }
     250             :   } else {
     251          81 :     if( valread.size()!=1 ) {
     252           0 :       error("all values must be from the same Action when using READ");
     253             :     }
     254          81 :     readvals.emplace_back(Tools::make_unique<Value>(this, valread[0], false) );
     255          81 :     addValueWithDerivatives();
     256         162 :     if( ifile->FieldExist("min_" + valread[0]) ) {
     257          18 :       setPeriodic( "-pi", "pi" );
     258             :     } else {
     259          72 :       setNotPeriodic();
     260             :     }
     261          81 :     log.printf("  reading value %s and storing as %s\n",valread[0].c_str(),getLabel().c_str() );
     262             :   }
     263          92 :   checkRead();
     264         184 : }
     265             : 
     266           4 : std::string Read::getOutputComponentDescription( const std::string& cname, const Keywords& keys ) const {
     267           4 :   plumed_assert( !exists( getLabel() ) );
     268           7 :   for(unsigned i=0; i<readvals.size(); ++i) {
     269           7 :     if( readvals[i]->getName().find( cname )!=std::string::npos ) {
     270          12 :       return "values from the column labelled " + readvals[i]->getName() + " in the file named " + filename;
     271             :     }
     272             :   }
     273           0 :   plumed_error();
     274             :   return "";
     275             : }
     276             : 
     277          50 : std::string Read::getFilename() const {
     278          50 :   return filename;
     279             : }
     280             : 
     281          47 : IFile* Read::getFile() {
     282          47 :   return ifile;
     283             : }
     284             : 
     285           0 : unsigned Read::getNumberOfDerivatives() {
     286           0 :   return 0;
     287             : }
     288             : 
     289          26 : void Read::turnOnDerivatives() {
     290          26 :   if( !ignore_forces )
     291           0 :     error("cannot calculate derivatives for colvars that are read in from a file.  If you are postprocessing and "
     292             :           "these forces do not matter add the flag IGNORE_FORCES to all READ actions");
     293          26 : }
     294             : 
     295      921430 : void Read::prepare() {
     296      921430 :   if( !cloned_file ) {
     297             :     double du_time;
     298      403420 :     if( !ifile->scanField("time",du_time) ) {
     299           0 :       error("Reached end of file " + filename + " before end of trajectory");
     300      201710 :     } else if( std::abs( du_time-getTime() )>getTimeStep() && !ignore_time ) {
     301             :       std::string str_dutime,str_ptime;
     302           0 :       Tools::convert(du_time,str_dutime);
     303           0 :       Tools::convert(getTime(),str_ptime);
     304           0 :       error("mismatched times in colvar files : colvar time=" + str_dutime + " plumed time=" + str_ptime + ". Add IGNORE_TIME to ignore error.");
     305             :     }
     306             :   }
     307      921430 : }
     308             : 
     309      921430 : void Read::calculate() {
     310             :   std::string smin, smax;
     311     2019295 :   for(unsigned i=0; i<readvals.size(); ++i) {
     312             : // .get  returns the raw pointer
     313             : // ->get calls the Value::get() method
     314     1097865 :     ifile->scanField( readvals[i].get() );
     315     1097865 :     getPntrToComponent(i)->set( readvals[i]->get() );
     316     1097865 :     if( readvals[i]->isPeriodic() ) {
     317        3309 :       readvals[i]->getDomain( smin, smax );
     318        3309 :       getPntrToComponent(i)->setDomain( smin, smax );
     319             :     }
     320             :   }
     321      921430 : }
     322             : 
     323      921430 : void Read::update() {
     324      921430 :   if( !cloned_file ) {
     325      403971 :     for(unsigned i=0; i<nlinesPerStep; ++i) {
     326      202261 :       ifile->scanField();
     327             :       double du_time;
     328      404522 :       if( !ifile->scanField("time",du_time) && !plumed.inputsAreActive() ) {
     329          55 :         plumed.stop();
     330             :       }
     331             :     }
     332             :   }
     333      921430 : }
     334             : 
     335             : }
     336             : }

Generated by: LCOV version 1.16