LCOV - code coverage report
Current view: top level - gridtools - DumpGrid.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 112 114 98.2 %
Date: 2024-10-18 13:59:31 Functions: 7 8 87.5 %

          Line data    Source code
       1             : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
       2             :    Copyright (c) 2016-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/ActionWithArguments.h"
      23             : #include "core/ActionPilot.h"
      24             : #include "core/ActionRegister.h"
      25             : #include "core/PlumedMain.h"
      26             : #include "ActionWithGrid.h"
      27             : #include "tools/OFile.h"
      28             : 
      29             : namespace PLMD {
      30             : namespace gridtools {
      31             : 
      32             : //+PLUMEDOC GRIDANALYSIS DUMPCUBE
      33             : /*
      34             : Output a three dimensional grid using the Gaussian cube file format.
      35             : 
      36             : Suppose you have calculated the value of a function on a three dimensional grid.
      37             : This function might be a \ref HISTOGRAM or it might be a free energy energy surface
      38             : that was calculated from this histogram by using \ref CONVERT_TO_FES.  Alternatively,
      39             : your function might be a phase-field that was calculated using \ref MULTICOLVARDENS.
      40             : Whatever the function is, however, you obviously cannot show it using a typical contour
      41             : plotting program such as gnuplot as you have three input variables.
      42             : 
      43             : Tools like VMD have nice features for plotting these types of three dimensional functions
      44             : but typically you are required to use a Gaussian cube file format to input the data.  This
      45             : action thus allows you to output a function evaluated on a grid to a Gaussian cube file format.
      46             : 
      47             : \par Examples
      48             : 
      49             : The input below can be used to post process a trajectory.  A histogram as a function of the distance
      50             : between atoms 1 and 2, the distance between atom 1 and 3 and the angle between the vector connecting atoms 1 and
      51             : 2 and 1 and 3 is computed using kernel density estimation.  Once all the data contained in the trajectory has been read in and
      52             : all the kernels have been added the resulting histogram is output to a file called histoA1.cube.  This file has the
      53             : Gaussian cube file format.  The histogram can thus be visualized using tools such as VMD.
      54             : 
      55             : \plumedfile
      56             : x1: DISTANCE ATOMS=1,2
      57             : x2: DISTANCE ATOMS=1,3
      58             : x3: ANGLE ATOMS=1,2,3
      59             : 
      60             : hA1: HISTOGRAM ARG=x1,x2,x3 GRID_MIN=0.0,0.0,0.0 GRID_MAX=3.0,3.0,3.0 GRID_BIN=10,10,10 BANDWIDTH=1.0,1.0,1.0
      61             : DUMPCUBE GRID=hA1 FILE=histoA1.cube
      62             : \endplumedfile
      63             : 
      64             : */
      65             : //+ENDPLUMEDOC
      66             : 
      67             : //+PLUMEDOC GRIDANALYSIS DUMPGRID
      68             : /*
      69             : Output the function on the grid to a file with the PLUMED grid format.
      70             : 
      71             : PLUMED provides a number of actions that calculate the values of functions on grids.
      72             : For instance, whenever you calculate a free energy as a function of a collective variable using
      73             : \ref HISTOGRAM and \ref CONVERT_TO_FES you will generally want to output the value of the free energy at a number of points on a
      74             : discrete grid that covers the CV space uniformly.  Alternatively you may want to calculate
      75             : what value some symmetry function takes at different points inside your simulation cell using \ref MULTICOLVARDENS.
      76             : 
      77             : This action allows you to output these functions calculated on a grid using a format that can be read in using gnuplot
      78             : and other such plotting programs.  The file output using this action will have a header that contains some essential
      79             : information about the function plotted and that looks something like this:
      80             : 
      81             : \verbatim
      82             : #! FIELDS x y hA1 dhA1_x dhA1_x
      83             : #! SET normalisation    2.0000
      84             : #! SET min_x 0.0
      85             : #! SET max_x 3.0
      86             : #! SET nbins_x  100
      87             : #! SET periodic_x false
      88             : #! SET min_y 0.0
      89             : #! SET max_y 3.0
      90             : #! SET nbins_y  100
      91             : #! SET periodic_y false
      92             : \endverbatim
      93             : 
      94             : The header shown here tells us that we have grid showing the values that a function with two arguments x and y
      95             : takes at various points in our cell.  The lines beneath the first line then tell us a little bit about these two
      96             : input arguments.
      97             : 
      98             : The remaining lines of the file give us information on the positions of our grid points and the value the function and
      99             : its partial derivatives with respect to x and y.  If the header is as above a list of values of the function that have
     100             : x=0 and 100 values of y between 0.0 and 3.0 will be provided.  This block of data will be followed with a blank line.
     101             : There will then be a second block of values which will all have been evaluated the same value of x and all possible values
     102             : for y.  This block is then followed by a blank line again and this pattern continues until all points of the grid have been covered.
     103             : 
     104             : \par Examples
     105             : 
     106             : The following input monitors two torsional angles during a simulation
     107             : and outputs a continuous histogram as a function of them at the end of the simulation.
     108             : \plumedfile
     109             : TORSION ATOMS=1,2,3,4 LABEL=r1
     110             : TORSION ATOMS=2,3,4,5 LABEL=r2
     111             : HISTOGRAM ...
     112             :   ARG=r1,r2
     113             :   GRID_MIN=-3.14,-3.14
     114             :   GRID_MAX=3.14,3.14
     115             :   GRID_BIN=200,200
     116             :   BANDWIDTH=0.05,0.05
     117             :   LABEL=hh
     118             : ... HISTOGRAM
     119             : 
     120             : DUMPGRID GRID=hh FILE=histo
     121             : \endplumedfile
     122             : 
     123             : The following input monitors two torsional angles during a simulation
     124             : and outputs a discrete histogram as a function of them at the end of the simulation.
     125             : \plumedfile
     126             : TORSION ATOMS=1,2,3,4 LABEL=r1
     127             : TORSION ATOMS=2,3,4,5 LABEL=r2
     128             : HISTOGRAM ...
     129             :   ARG=r1,r2
     130             :   KERNEL=DISCRETE
     131             :   GRID_MIN=-3.14,-3.14
     132             :   GRID_MAX=3.14,3.14
     133             :   GRID_BIN=200,200
     134             :   LABEL=hh
     135             : ... HISTOGRAM
     136             : 
     137             : DUMPGRID GRID=hh FILE=histo
     138             : \endplumedfile
     139             : 
     140             : The following input monitors two torsional angles during a simulation
     141             : and outputs the histogram accumulated thus far every 100000 steps.
     142             : \plumedfile
     143             : TORSION ATOMS=1,2,3,4 LABEL=r1
     144             : TORSION ATOMS=2,3,4,5 LABEL=r2
     145             : HISTOGRAM ...
     146             :   ARG=r1,r2
     147             :   GRID_MIN=-3.14,-3.14
     148             :   GRID_MAX=3.14,3.14
     149             :   GRID_BIN=200,200
     150             :   BANDWIDTH=0.05,0.05
     151             :   LABEL=hh
     152             : ... HISTOGRAM
     153             : 
     154             : DUMPGRID GRID=hh FILE=histo STRIDE=100000
     155             : \endplumedfile
     156             : 
     157             : The following input monitors two torsional angles during a simulation
     158             : and outputs a separate histogram for each 100000 steps worth of trajectory.
     159             : Notice how the CLEAR keyword is used here and how it is not used in the
     160             : previous example.
     161             : 
     162             : \plumedfile
     163             : TORSION ATOMS=1,2,3,4 LABEL=r1
     164             : TORSION ATOMS=2,3,4,5 LABEL=r2
     165             : HISTOGRAM ...
     166             :   ARG=r1,r2 CLEAR=100000
     167             :   GRID_MIN=-3.14,-3.14
     168             :   GRID_MAX=3.14,3.14
     169             :   GRID_BIN=200,200
     170             :   BANDWIDTH=0.05,0.05
     171             :   LABEL=hh
     172             : ... HISTOGRAM
     173             : 
     174             : DUMPGRID GRID=hh FILE=histo STRIDE=100000
     175             : \endplumedfile
     176             : 
     177             : */
     178             : //+ENDPLUMEDOC
     179             : 
     180             : class DumpGrid :
     181             :   public ActionWithArguments,
     182             :   public ActionPilot {
     183             : private:
     184             :   std::string fmt, filename;
     185             :   bool onefile, xyzfile;
     186             : public:
     187             :   static void registerKeywords( Keywords& keys );
     188             :   explicit DumpGrid(const ActionOptions&ao);
     189         132 :   ~DumpGrid() {}
     190         105 :   void calculate() override {}
     191         105 :   void apply() override {}
     192             :   void update() override ;
     193             : };
     194             : 
     195             : PLUMED_REGISTER_ACTION(DumpGrid,"DUMPCUBE")
     196             : PLUMED_REGISTER_ACTION(DumpGrid,"DUMPGRID")
     197             : 
     198          70 : void DumpGrid::registerKeywords( Keywords& keys ) {
     199          70 :   Action::registerKeywords( keys );
     200          70 :   ActionPilot::registerKeywords( keys );
     201          70 :   ActionWithArguments::registerKeywords( keys );
     202         140 :   keys.addInputKeyword("compulsory","ARG","grid","the label for the grid that you would like to output");
     203         140 :   keys.add("optional","GRID","the grid you would like to print (can also use ARG for specifying what is being printed)");
     204         140 :   keys.add("compulsory","STRIDE","0","the frequency with which the grid should be output to the file.  Default of zero means dump at end of calculation");
     205         140 :   keys.add("compulsory","FILE","density","the file on which to write the grid.");
     206         140 :   keys.add("optional","FMT","the format that should be used to output real numbers");
     207         140 :   keys.addFlag("PRINT_XYZ",false,"output coordinates on fibonacci grid to xyz file");
     208         140 :   keys.addFlag("PRINT_ONE_FILE",false,"output grids one after the other in a single file");
     209          70 : }
     210             : 
     211          66 : DumpGrid::DumpGrid(const ActionOptions&ao):
     212             :   Action(ao),
     213             :   ActionWithArguments(ao),
     214             :   ActionPilot(ao),
     215          66 :   fmt("%f")
     216             : {
     217          66 :   if( getNumberOfArguments()==0 ) {
     218           0 :     std::vector<Value*> grids; parseArgumentList("GRID",grids); requestArguments(grids);
     219             :   }
     220          66 :   if( getNumberOfArguments()!=1 ) error("should only be one argument");
     221          66 :   if( getPntrToArgument(0)->getRank()==0 || !getPntrToArgument(0)->hasDerivatives() ) error("input should be a grid");
     222          66 :   if( getName()=="DUMPCUBE" && getPntrToArgument(0)->getRank()!=3 ) error("input should be a three dimensional grid");
     223         132 :   parse("FILE",filename);
     224          66 :   if(filename.length()==0) error("name out output file was not specified");
     225          66 :   ActionWithGrid* ag = ActionWithGrid::getInputActionWithGrid( getPntrToArgument(0)->getPntrToAction() );
     226          66 :   if( !ag ) error( getPntrToArgument(0)->getName() + " is not grid");
     227             : 
     228          66 :   log.printf("  outputting grid with label %s to file named %s",getPntrToArgument(0)->getName().c_str(), filename.c_str() );
     229         132 :   parse("FMT",fmt); log.printf(" with format %s \n", fmt.c_str() );
     230         137 :   if( getName()=="DUMPGRID" ) fmt = " " + fmt; else if( getName()=="DUMPCUBE" ) fmt = fmt + " ";
     231         132 :   parseFlag("PRINT_ONE_FILE", onefile); parseFlag("PRINT_XYZ",xyzfile);
     232          66 :   if( xyzfile ) {
     233           4 :     if( getName()=="DUMPCUBE" ) error("PRINT_XYZ flag not compatible with DUMPCUBE");
     234           8 :     if( ag->getGridCoordinatesObject().getGridType()=="flat" ) error("can only use PRINT_XYZ option for fibonacci grids");
     235           4 :     log.printf("  outputting grid to xyzfile\n");
     236             :   }
     237          66 :   if( onefile ) log.printf("  printing all grids on a single file \n");
     238          64 :   else log.printf("  printing all grids on separate files \n");
     239          66 : }
     240             : 
     241         137 : void DumpGrid::update() {
     242         137 :   OFile ofile; ofile.link(*this);
     243         137 :   if( onefile ) {
     244          10 :     ofile.enforceRestart();
     245          10 :     ofile.open( filename );
     246             :   } else {
     247         127 :     ofile.setBackupString("analysis");
     248         127 :     ofile.open( filename );
     249             :   }
     250             : 
     251         137 :   ActionWithGrid* ag = ActionWithGrid::getInputActionWithGrid( getPntrToArgument(0)->getPntrToAction() );
     252         137 :   plumed_assert( ag ); const GridCoordinatesObject & mygrid = ag->getGridCoordinatesObject();
     253             : 
     254         137 :   if( getName()=="DUMPCUBE" ) {
     255          11 :     double lunit=1.0; std::vector<std::string> argnames( ag->getGridCoordinateNames() );
     256             : 
     257          11 :     ofile.printf("PLUMED CUBE FILE\n");
     258          11 :     ofile.printf("OUTER LOOP: X, MIDDLE LOOP: Y, INNER LOOP: Z\n");
     259             :     // Number of atoms followed by position of origin (origin set so that center of grid is in center of cell)
     260          11 :     bool isdists=true; std::vector<double> extent(3); std::vector<std::string> min( mygrid.getMin() ), max( mygrid.getMax() );
     261          44 :     for(unsigned j=0; j<3; ++j) {
     262          33 :       if( argnames[j].find(".")!=std::string::npos ) {
     263           3 :         std::size_t dot = argnames[j].find(".");
     264           3 :         std::string name = argnames[j].substr(dot+1);
     265           6 :         if( name!="x" && name!="y" && name!="z" ) isdists=false;
     266             :       } else isdists=false;
     267             : 
     268          33 :       double mind, maxd; Tools::convert( min[j], mind ); Tools::convert( max[j], maxd );
     269          33 :       if( mygrid.isPeriodic(j) ) extent[j]=maxd-mind;
     270          30 :       else { extent[j]=maxd-mind+mygrid.getGridSpacing()[j]; }
     271             :     }
     272          11 :     if( isdists ) {
     273           1 :       if( plumed.usingNaturalUnits() ) lunit = 1.0/0.5292;
     274           0 :       else lunit = plumed.getUnits().getLength()/.05929;
     275             :     }
     276          22 :     std::string ostr = "%d " + fmt + fmt + fmt + "\n"; Value* gval=getPntrToArgument(0);
     277          11 :     ofile.printf(ostr.c_str(),1,-0.5*lunit*extent[0],-0.5*lunit*extent[1],-0.5*lunit*extent[2]);
     278          11 :     ofile.printf(ostr.c_str(),mygrid.getNbin(true)[0],lunit*mygrid.getGridSpacing()[0],0.0,0.0);  // Number of bins in each direction followed by
     279          11 :     ofile.printf(ostr.c_str(),mygrid.getNbin(true)[1],0.0,lunit*mygrid.getGridSpacing()[1],0.0);  // shape of voxel
     280          22 :     ofile.printf(ostr.c_str(),mygrid.getNbin(true)[2],0.0,0.0,lunit*mygrid.getGridSpacing()[2]);
     281          11 :     ofile.printf(ostr.c_str(),1,0.0,0.0,0.0); // Fake atom otherwise VMD doesn't work
     282          11 :     std::vector<unsigned> pp(3); std::vector<unsigned> nbin( mygrid.getNbin(true) );
     283         135 :     for(pp[0]=0; pp[0]<nbin[0]; ++pp[0]) {
     284        1530 :       for(pp[1]=0; pp[1]<nbin[1]; ++pp[1]) {
     285       20204 :         for(pp[2]=0; pp[2]<nbin[2]; ++pp[2]) {
     286       18798 :           unsigned ival=pp[pp.size()-1];
     287       56394 :           for(unsigned i=pp.size()-1; i>0; --i) ival=ival*nbin[i-1]+pp[i-1];
     288       18798 :           ofile.printf(fmt.c_str(), gval->get(ival) );
     289       18798 :           if(pp[2]%6==5) ofile.printf("\n");
     290             :         }
     291        1406 :         ofile.printf("\n");
     292             :       }
     293             :     }
     294         148 :   } else if( xyzfile ) {
     295          13 :     std::vector<double> coords(3);
     296             :     Value* myarg = getPntrToArgument(0);
     297          13 :     unsigned nvals = myarg->getNumberOfValues(); ofile.printf("%d\n\n", nvals);
     298        1797 :     for(unsigned i=0; i<nvals; ++i) {
     299        1784 :       double val = myarg->get(i); mygrid.getGridPointCoordinates( i, coords );
     300        1784 :       ofile.printf("X");
     301        7136 :       for(unsigned j=0; j<3; ++j) ofile.printf((" " + fmt).c_str(), val*coords[j] );
     302        1784 :       ofile.printf("\n");
     303             :     }
     304             :   } else {
     305             :     Value* gval=getPntrToArgument(0);
     306         113 :     std::vector<double> xx( gval->getRank() );
     307         113 :     std::vector<unsigned> ind( gval->getRank() );
     308         113 :     std::vector<std::string> argn( ag->getGridCoordinateNames() );
     309         226 :     if( mygrid.getGridType()=="fibonacci" ) {
     310          12 :       ofile.addConstantField("nbins");
     311             :     } else {
     312         214 :       plumed_assert( mygrid.getGridType()=="flat" );
     313         229 :       for(unsigned i=0; i<gval->getRank(); ++i) {
     314         244 :         ofile.addConstantField("min_" + argn[i] );
     315         244 :         ofile.addConstantField("max_" + argn[i] );
     316         244 :         ofile.addConstantField("nbins_" + argn[i] );
     317         244 :         ofile.addConstantField("periodic_" + argn[i] );
     318             :       }
     319             :     }
     320             : 
     321      213260 :     for(unsigned i=0; i<gval->getNumberOfValues(); ++i) {
     322             :       // Retrieve and print the grid coordinates
     323      213147 :       mygrid.getGridPointCoordinates( i, ind, xx );
     324      213147 :       if(i>0 && gval->getRank()==2 && ind[gval->getRank()-2]==0) ofile.printf("\n");
     325      213147 :       ofile.fmtField(fmt);
     326      426294 :       if( mygrid.getGridType()=="fibonacci" ) {
     327        1200 :         ofile.printField("nbins", static_cast<int>(gval->getNumberOfValues()) );
     328             :       } else {
     329      682787 :         for(unsigned j=0; j<gval->getRank(); ++j) {
     330      940480 :           ofile.printField("min_" + argn[j], mygrid.getMin()[j] );
     331      940480 :           ofile.printField("max_" + argn[j], mygrid.getMax()[j] );
     332      940480 :           ofile.printField("nbins_" + argn[j], static_cast<int>(mygrid.getNbin(false)[j]) );
     333      650466 :           if( mygrid.isPeriodic(j) ) ofile.printField("periodic_" + argn[j], "true" );
     334      580028 :           else         ofile.printField("periodic_" + argn[j], "false" );
     335             :         }
     336             :       }
     337             :       // Print the grid coordinates
     338      685187 :       for(unsigned j=0; j<gval->getRank(); ++j) { ofile.fmtField(fmt); ofile.printField(argn[j],xx[j]); }
     339             :       // Print value
     340      213147 :       ofile.fmtField(fmt); ofile.printField( gval->getName(), gval->get(i) );
     341             :       // Print the derivatives
     342      685187 :       for(unsigned j=0; j<gval->getRank(); ++j) { ofile.fmtField(fmt); ofile.printField( "d" + gval->getName() + "_" + argn[j], gval->getGridDerivative(i,j) ); }
     343      213147 :       ofile.printField();
     344             :     }
     345         113 :   }
     346         137 : }
     347             : 
     348             : 
     349             : }
     350             : }

Generated by: LCOV version 1.16