LCOV - code coverage report
Current view: top level - gridtools - InterpolateGrid.cpp (source / functions) Hit Total Coverage
Test: plumed test coverage Lines: 139 151 92.1 %
Date: 2026-11-05 10:19:04 Functions: 11 12 91.7 %

          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/ActionRegister.h"
      23             : #include "core/PlumedMain.h"
      24             : #include "core/ParallelTaskManager.h"
      25             : #include "EvaluateGridFunction.h"
      26             : #include "ActionWithGrid.h"
      27             : 
      28             : //+PLUMEDOC GRIDANALYSIS INTERPOLATE_GRID
      29             : /*
      30             : Interpolate a smooth function stored on a grid onto a grid with a smaller grid spacing.
      31             : 
      32             : This action takes a function evaluated on a grid as input and can be used to interpolate the values of that
      33             : function on to a finer grained grid.  By default the interpolation within this algorithm is done using splines.
      34             : 
      35             : ## Examples
      36             : 
      37             : The input below can be used to post process a trajectory.  It calculates a [HISTOGRAM](HISTOGRAM.md) as a function the
      38             : distance between atoms 1 and 2 using kernel density estimation.  During the calculation the values of the kernels
      39             : are evaluated at 100 points on a uniform grid between 0.0 and 3.0.  Prior to outputting this function at the end of the
      40             : simulation this function is interpolated onto a finer grid of 200 points between 0.0 and 3.0.
      41             : 
      42             : ```plumed
      43             : x: DISTANCE ATOMS=1,2
      44             : hA1: HISTOGRAM ARG=x GRID_MIN=0.0 GRID_MAX=3.0 GRID_BIN=100 BANDWIDTH=0.1
      45             : ii: INTERPOLATE_GRID ARG=hA1 GRID_BIN=200
      46             : DUMPGRID ARG=ii FILE=histo.dat
      47             : ```
      48             : 
      49             : When specifying the parameters of the interpolating grid you can use GRID_BIN to specify the number of grid points
      50             : or GRID_SPACING to specify the spacing between grid points as shown below:
      51             : 
      52             : ```plumed
      53             : x: DISTANCE ATOMS=1,2
      54             : hA1: HISTOGRAM ARG=x GRID_MIN=0.0 GRID_MAX=3.0 GRID_BIN=100 BANDWIDTH=0.1
      55             : ii: INTERPOLATE_GRID ARG=hA1 GRID_SPACING=0.05 INTERPOLATION_TYPE=floor
      56             : DUMPGRID ARG=ii FILE=histo.dat
      57             : ```
      58             : 
      59             : In the above input spline interpolation is not used. Instead, if you evaluating the function at value $x$, which
      60             : is $x_n < x < x_{n+1}$ where $x_n$ and $x_{n+1}$ are two points where the values for the function on the input grid
      61             : are $f(x_n)$ and $f(x_{n+1})$ then $f(x)=f(x_n)$. If you want $f(x)=f(x_{n+1})$ you use `INTERPOLATION_TYPE=ceiling`.
      62             : Alternatively, you can use linear interpolation as has been done in the following input.
      63             : 
      64             : ```plumed
      65             : x: DISTANCE ATOMS=1,2
      66             : hA1: HISTOGRAM ARG=x GRID_MIN=0.0 GRID_MAX=3.0 GRID_BIN=100 BANDWIDTH=0.1
      67             : ii: INTERPOLATE_GRID ARG=hA1 GRID_MIN=0.015 GRID_MAX=2.985 GRID_BIN=99 INTERPOLATION_TYPE=linear
      68             : DUMPGRID ARG=ii FILE=histo.dat
      69             : ```
      70             : 
      71             : Notice that the way we have specified `GRID_MIN`, `GRID_MAX` and `GRID_BIN` in the above input ensures that function is evaluated
      72             : at the $n-1$ mid points that lie between the points on the input grid.
      73             : 
      74             : */
      75             : //+ENDPLUMEDOC
      76             : 
      77             : namespace PLMD {
      78             : namespace gridtools {
      79             : 
      80             : class InterpolateGrid : public ActionWithGrid {
      81             : public:
      82             :   using input_type = EvaluateGridFunction;
      83             :   using PTM = ParallelTaskManager<InterpolateGrid>;
      84             :   typedef typename PTM::ParallelActionsInput ParallelActionsInput;
      85             :   typedef typename PTM::ParallelActionsOutput ParallelActionsOutput;
      86             : private:
      87             :   bool firststep;
      88             : /// The parallel task manager
      89             :   PTM taskmanager;
      90             :   std::vector<std::size_t> nbin;
      91             :   std::vector<std::string> gmin, gmax;
      92             :   std::vector<double> gspacing;
      93             :   GridCoordinatesObject output_grid;
      94             : public:
      95             :   static void registerKeywords( Keywords& keys );
      96             :   explicit InterpolateGrid(const ActionOptions&ao);
      97             :   unsigned getNumberOfDerivatives() override ;
      98             :   const GridCoordinatesObject& getGridCoordinatesObject() const override ;
      99             :   std::vector<std::string> getGridCoordinateNames() const override ;
     100             :   void calculate() override ;
     101             :   void getArgumentInputData( std::vector<double>& inputdata ) const override ;
     102             :   // Calculate the value of the function at a grid point
     103             :   static void performTask( std::size_t task_index,
     104             :                            const EvaluateGridFunction& actiondata,
     105             :                            ParallelActionsInput& input,
     106             :                            ParallelActionsOutput& output );
     107             :   void applyNonZeroRankForces( std::vector<double>& outforces ) override ;
     108             :   static int getNumberOfValuesPerTask( std::size_t task_index,
     109             :                                        const EvaluateGridFunction& actiondata );
     110             :   static void getForceIndices( std::size_t task_index,
     111             :                                std::size_t colno,
     112             :                                std::size_t ntotal_force,
     113             :                                const EvaluateGridFunction& actiondata,
     114             :                                const ParallelActionsInput& input,
     115             :                                ForceIndexHolder force_indices );
     116             : };
     117             : 
     118             : PLUMED_REGISTER_ACTION(InterpolateGrid,"INTERPOLATE_GRID")
     119             : 
     120          43 : void InterpolateGrid::registerKeywords( Keywords& keys ) {
     121          43 :   ActionWithGrid::registerKeywords( keys );
     122          43 :   keys.add("optional","GRID_BIN","the number of bins for the grid");
     123          86 :   keys.addInputKeyword("compulsory","ARG","grid","the label for function on the grid that you would like to interpolate");
     124          43 :   keys.add("optional","GRID_SPACING","the approximate grid spacing (to be used as an alternative or together with GRID_BIN)");
     125          43 :   keys.add("compulsory","GRID_MIN","auto","the lower bounds for the grid. By default this action uses the lower bound for the input grid");
     126          43 :   keys.add("compulsory","GRID_MAX","auto","the upper bounds for the grid. By default this action uses the upper bound for the input grid");
     127          43 :   EvaluateGridFunction::registerKeywords( keys );
     128          43 :   keys.remove("ZERO_OUTSIDE_GRID_RANGE");
     129          86 :   keys.setValueDescription("grid","the function evaluated onto the interpolated grid");
     130          43 :   PTM::registerKeywords( keys );
     131          43 : }
     132             : 
     133          23 : InterpolateGrid::InterpolateGrid(const ActionOptions&ao):
     134             :   Action(ao),
     135             :   ActionWithGrid(ao),
     136          23 :   firststep(true),
     137          23 :   taskmanager(this) {
     138          23 :   if( getNumberOfArguments()!=1 ) {
     139           0 :     error("should only be one argument to this action");
     140             :   }
     141          23 :   if( getPntrToArgument(0)->getRank()==0 || !getPntrToArgument(0)->hasDerivatives() ) {
     142           0 :     error("input to this action should be a grid");
     143             :   }
     144             : 
     145          23 :   parseVector("GRID_BIN",nbin);
     146          46 :   parseVector("GRID_SPACING",gspacing);
     147          23 :   unsigned dimension = getPntrToArgument(0)->getRank();
     148          23 :   gmin.resize( dimension );
     149          23 :   gmax.resize( dimension );
     150          23 :   parseVector("GRID_MIN",gmin);
     151          46 :   parseVector("GRID_MAX",gmax);
     152          23 :   if( nbin.size()!=dimension && gspacing.size()!=dimension ) {
     153           0 :     error("MIDPOINTS, GRID_BIN or GRID_SPACING must be set");
     154             :   }
     155          23 :   if( nbin.size()==dimension ) {
     156          22 :     log.printf("  number of bins in grid %zu", nbin[0]);
     157          23 :     for(unsigned i=1; i<nbin.size(); ++i) {
     158           1 :       log.printf(", %zu", nbin[i]);
     159             :     }
     160          22 :     log.printf("\n");
     161           1 :   } else if( gspacing.size()==dimension ) {
     162           1 :     log.printf("  spacing for bins in grid %f", gspacing[0]);
     163           1 :     for(unsigned i=1; i<gspacing.size(); ++i) {
     164           0 :       log.printf(", %f", gspacing[i]);
     165             :     }
     166           1 :     log.printf("\n");
     167             :   }
     168             :   // Create the input grid
     169          23 :   function::FunctionOptions foptions;
     170          23 :   EvaluateGridFunction::read( taskmanager.getActionInput(), this, foptions );
     171             :   // Need this for creation of tasks
     172          46 :   output_grid.setup( "flat", taskmanager.getActionInput().getPbc(), 0, 0.0 );
     173             : 
     174             :   // Now add a value
     175          23 :   std::vector<std::size_t> shape( getPntrToArgument(0)->getShape() );
     176          23 :   addValueWithDerivatives( shape );
     177             : 
     178          23 :   if( getPntrToArgument(0)->isPeriodic() ) {
     179             :     std::string min, max;
     180           0 :     getPntrToArgument(0)->getDomain( min, max );
     181           0 :     setPeriodic( min, max );
     182             :   } else {
     183          23 :     setNotPeriodic();
     184             :   }
     185             :   // Setup the task manager
     186          23 :   if( taskmanager.getActionInput().interpolation_type==EvaluateGridFunction::spline ) {
     187           2 :     taskmanager.setupParallelTaskManager( 0, 0 );
     188          21 :   } else if( taskmanager.getActionInput().interpolation_type==EvaluateGridFunction::linear ) {
     189           5 :     taskmanager.setupParallelTaskManager( 1+dimension, getPntrToArgument(0)->getNumberOfStoredValues() );
     190          16 :   } else if( taskmanager.getActionInput().interpolation_type==EvaluateGridFunction::floor || taskmanager.getActionInput().interpolation_type==EvaluateGridFunction::ceiling ) {
     191          16 :     taskmanager.setupParallelTaskManager( 1, getPntrToArgument(0)->getNumberOfStoredValues() );
     192             :   } else {
     193           0 :     error("interpolation type not defined");
     194             :   }
     195          46 : }
     196             : 
     197        1913 : unsigned InterpolateGrid::getNumberOfDerivatives() {
     198        1913 :   return getPntrToArgument(0)->getRank();
     199             : }
     200             : 
     201         343 : const GridCoordinatesObject& InterpolateGrid::getGridCoordinatesObject() const {
     202         343 :   return output_grid;
     203             : }
     204             : 
     205           3 : std::vector<std::string> InterpolateGrid::getGridCoordinateNames() const {
     206           3 :   ActionWithGrid* ag = ActionWithGrid::getInputActionWithGrid( getPntrToArgument(0)->getPntrToAction() );
     207           3 :   plumed_assert( ag );
     208           3 :   return ag->getGridCoordinateNames();
     209             : }
     210             : 
     211        1763 : void InterpolateGrid::calculate() {
     212        1763 :   if( firststep ) {
     213          23 :     ActionWithGrid* ag=ActionWithGrid::getInputActionWithGrid( getPntrToArgument(0)->getPntrToAction() );
     214          23 :     plumed_assert( ag );
     215          23 :     const GridCoordinatesObject& mygrid = ag->getGridCoordinatesObject();
     216          47 :     for(unsigned i=0; i<gmin.size(); ++i) {
     217          24 :       if( gmin[i]=="auto" ) {
     218          36 :         gmin[i] = mygrid.getMin()[i];
     219             :       }
     220          24 :       if( gmax[i]=="auto" ) {
     221          36 :         gmax[i] = mygrid.getMax()[i];
     222             :       }
     223             :     }
     224          23 :     output_grid.setBounds( gmin, gmax, nbin, gspacing );
     225          23 :     getPntrToComponent(0)->setShape( output_grid.getNbin(true) );
     226          23 :     if( taskmanager.getActionInput().interpolation_type==EvaluateGridFunction::linear ) {
     227           5 :       taskmanager.setupParallelTaskManager( 1+getPntrToComponent(0)->getRank(), getPntrToArgument(0)->getNumberOfStoredValues() );
     228          18 :     } else if( taskmanager.getActionInput().interpolation_type==EvaluateGridFunction::floor || taskmanager.getActionInput().interpolation_type==EvaluateGridFunction::ceiling ) {
     229          16 :       taskmanager.setupParallelTaskManager( 1, getPntrToArgument(0)->getNumberOfStoredValues() );
     230           2 :     } else if( taskmanager.getActionInput().interpolation_type!=EvaluateGridFunction::spline ) {
     231           0 :       error("interpolation type not defined");
     232             :     }
     233          23 :     firststep=false;
     234             :   }
     235        1763 :   taskmanager.runAllTasks();
     236        1763 : }
     237             : 
     238        1763 : void InterpolateGrid::getArgumentInputData( std::vector<double>& inputdata ) const {
     239        1763 :   std::size_t ndim = output_grid.getDimension();
     240        1763 :   std::size_t nstored = getConstPntrToComponent(0)->getNumberOfStoredValues();
     241        1763 :   std::vector<double> pos( ndim );
     242        1763 :   if( inputdata.size()!=nstored*ndim ) {
     243          23 :     inputdata.resize( ndim*nstored );
     244             :   }
     245             : 
     246       15684 :   for(unsigned i=0; i<nstored; ++i) {
     247       13921 :     output_grid.getGridPointCoordinates( i, pos );
     248       34242 :     for(unsigned j=0; j<ndim; ++j) {
     249       20321 :       inputdata[ i*ndim + j ] = pos[j];
     250             :     }
     251             :   }
     252        1763 : }
     253             : 
     254       15917 : void InterpolateGrid::performTask( std::size_t task_index,
     255             :                                    const EvaluateGridFunction& actiondata,
     256             :                                    ParallelActionsInput& input,
     257             :                                    ParallelActionsOutput& output ) {
     258       15917 :   auto funcout = function::FunctionOutput::create( 1,
     259             :                  output.values.data(),
     260       15917 :                  input.ranks[0],
     261             :                  output.values.data()+1 );
     262       31834 :   EvaluateGridFunction::calc( actiondata,
     263             :                               false,
     264       15917 :                               View<const double>(input.inputdata + task_index*input.ranks[0],input.ranks[0]),
     265             :                               funcout );
     266             : 
     267       15917 :   if( actiondata.interpolation_type==EvaluateGridFunction::linear ) {
     268        6840 :     View<const double> args(input.inputdata + task_index*input.ranks[0],input.ranks[0]);
     269             :     const GridCoordinatesObject & gridobject = actiondata.getGridObject();
     270             :     unsigned dimension = gridobject.getDimension();
     271        6840 :     std::vector<double> xfloor(dimension);
     272        6840 :     std::vector<unsigned> indices(dimension), nindices(dimension), ind(dimension);
     273        6840 :     gridobject.getIndices( args, indices );
     274        6840 :     unsigned nn=gridobject.getIndex(indices);
     275        6840 :     gridobject.getGridPointCoordinates( nn, nindices, xfloor );
     276        6840 :     output.derivatives[0] = 0;
     277       20080 :     for(unsigned i=0; i<dimension; ++i) {
     278             :       int x0=1;
     279       13240 :       if(nindices[i]==indices[i]) {
     280             :         x0=0;
     281             :       }
     282       13240 :       double ddx=gridobject.getGridSpacing()[i];
     283       13240 :       double X = fabs((args[i]-xfloor[i])/ddx-(double)x0);
     284       13240 :       output.derivatives[0] += (1-X);
     285       13240 :       output.derivatives[1+i] = X;
     286             :     }
     287        9077 :   } else if( actiondata.interpolation_type==EvaluateGridFunction::floor ) {
     288          61 :     output.derivatives[0] = 1;
     289        9016 :   } else if( actiondata.interpolation_type==EvaluateGridFunction::ceiling ) {
     290        8816 :     output.derivatives[0] = 1;
     291             :   }
     292       15917 : }
     293             : 
     294         471 : void InterpolateGrid::applyNonZeroRankForces( std::vector<double>& outforces ) {
     295         471 :   taskmanager.applyForces( outforces );
     296         471 : }
     297             : 
     298        1996 : int InterpolateGrid::getNumberOfValuesPerTask( std::size_t task_index,
     299             :     const EvaluateGridFunction& actiondata ) {
     300        1996 :   return 1;
     301             : }
     302             : 
     303        1996 : void InterpolateGrid::getForceIndices( std::size_t task_index,
     304             :                                        std::size_t colno,
     305             :                                        std::size_t ntotal_force,
     306             :                                        const EvaluateGridFunction& actiondata,
     307             :                                        const ParallelActionsInput& input,
     308             :                                        ForceIndexHolder force_indices ) {
     309             : 
     310             :   const GridCoordinatesObject & gridobject = actiondata.getGridObject();
     311             :   unsigned dimension = gridobject.getDimension();
     312        1996 :   std::vector<unsigned> indices(dimension);
     313        1996 :   gridobject.getIndices( View<const double>(input.inputdata + task_index*input.ranks[0],input.ranks[0]), indices );
     314        1996 :   force_indices.threadsafe_derivatives_end[0] = 0;
     315        1996 :   if( actiondata.interpolation_type==EvaluateGridFunction::linear ) {
     316         140 :     force_indices.tot_indices[0] = 1 + dimension;
     317         140 :     force_indices.indices[0][0] = gridobject.getIndex(indices);
     318         140 :     std::vector<unsigned> ind( dimension );
     319         280 :     for(unsigned i=0; i<dimension; ++i) {
     320         280 :       for(unsigned j=0; j<dimension; ++j) {
     321         140 :         ind[j] = indices[j];
     322             :       }
     323         140 :       if( gridobject.isPeriodic(i) && (ind[i]+1)==gridobject.getNbin(false)[i] ) {
     324           0 :         ind[i]=0;
     325             :       } else {
     326         140 :         ind[i] = ind[i] + 1;
     327             :       }
     328         140 :       force_indices.indices[0][1+i] = gridobject.getIndex(ind);
     329             :     }
     330        1856 :   } else if( actiondata.interpolation_type==EvaluateGridFunction::floor ) {
     331           0 :     force_indices.indices[0][0] = gridobject.getIndex(indices);
     332           0 :     force_indices.tot_indices[0] = 1;
     333        1856 :   } else if( actiondata.interpolation_type==EvaluateGridFunction::ceiling ) {
     334        3712 :     for(unsigned i=0; i<dimension; ++i) {
     335        2320 :       if( gridobject.isPeriodic(i) && (indices[i]+1)==gridobject.getNbin(false)[i] ) {
     336         464 :         indices[i]=0;
     337             :       } else {
     338        1392 :         indices[i] = indices[i] + 1;
     339             :       }
     340             :     }
     341        1856 :     force_indices.indices[0][0] = gridobject.getIndex(indices);
     342        1856 :     force_indices.tot_indices[0] = 1;
     343             :   } else {
     344           0 :     plumed_merror("cannot calculate derivatives for this type of interpolation");
     345             :   }
     346        1996 : }
     347             : 
     348             : }
     349             : }

Generated by: LCOV version 1.16