Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2015-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/Atoms.h" 25 : #include "ActionWithInputGrid.h" 26 : 27 : //+PLUMEDOC GRIDANALYSIS CONVERT_TO_FES 28 : /* 29 : Convert a histogram, \f$H(x)\f$, to a free energy surface using \f$F(x) = -k_B T \ln H(x)\f$. 30 : 31 : This action allows you to take a free energy surface that was calculated using the \ref HISTOGRAM 32 : action and to convert it to a free energy surface. This transformation performed by doing: 33 : 34 : \f[ 35 : F(x) = -k_B T \ln H(x) 36 : \f] 37 : 38 : The free energy calculated on a grid is output by this action and can be printed using \ref DUMPGRID 39 : 40 : \par Examples 41 : 42 : This is a typical example showing how CONVERT_TO_FES might be used when post processing a trajectory. 43 : The input below calculates the free energy as a function of the distance between atom 1 and atom 2. 44 : This is done by accumulating a histogram as a function of this distance using kernel density estimation 45 : and the HISTOGRAM action. All the data within this trajectory is used in the construction of this 46 : HISTOGRAM. Finally, once all the data has been read in, the histogram is converted to a free energy 47 : using the formula above and the free energy is output to a file called fes.dat 48 : 49 : \plumedfile 50 : x: DISTANCE ATOMS=1,2 51 : hA1: HISTOGRAM ARG=x GRID_MIN=0.0 GRID_MAX=3.0 GRID_BIN=100 BANDWIDTH=0.1 52 : ff: CONVERT_TO_FES GRID=hA1 TEMP=300 53 : DUMPGRID GRID=ff FILE=fes.dat 54 : \endplumedfile 55 : 56 : */ 57 : //+ENDPLUMEDOC 58 : 59 : namespace PLMD { 60 : namespace gridtools { 61 : 62 : class ConvertToFES : public ActionWithInputGrid { 63 : private: 64 : double simtemp; 65 : bool activated; 66 : bool mintozero; 67 : public: 68 : static void registerKeywords( Keywords& keys ); 69 : explicit ConvertToFES(const ActionOptions&ao); 70 : unsigned getNumberOfQuantities() const override; 71 15 : bool ignoreNormalization() const override { return true; } 72 12 : void prepare() override { activated=true; } 73 23 : void prepareForAveraging() override { ActionWithInputGrid::prepareForAveraging(); activated=false; } 74 : void compute( const unsigned& current, MultiValue& myvals ) const override; 75 : void finishComputations( const std::vector<double>& buffer ) override; 76 0 : bool isPeriodic() override { return false; } 77 2234 : bool onStep() const override { return activated; } 78 : void runFinalJobs() override; 79 : }; 80 : 81 10449 : PLUMED_REGISTER_ACTION(ConvertToFES,"CONVERT_TO_FES") 82 : 83 16 : void ConvertToFES::registerKeywords( Keywords& keys ) { 84 16 : ActionWithInputGrid::registerKeywords( keys ); 85 32 : keys.add("optional","TEMP","the temperature at which you are operating"); 86 32 : keys.addFlag("MINTOZERO",false,"set the minimum in the free energy to be equal to zero"); 87 48 : keys.remove("STRIDE"); keys.remove("KERNEL"); keys.remove("BANDWIDTH"); 88 48 : keys.remove("LOGWEIGHTS"); keys.remove("CLEAR"); keys.remove("NORMALIZATION"); 89 16 : } 90 : 91 15 : ConvertToFES::ConvertToFES(const ActionOptions&ao): 92 : Action(ao), 93 : ActionWithInputGrid(ao), 94 15 : activated(false) 95 : { 96 15 : plumed_assert( ingrid->getNumberOfComponents()==1 ); 97 : 98 : // Create a grid 99 47 : auto grid=createGrid( "grid", "COMPONENTS=" + getLabel() + " " + ingrid->getInputString() ); 100 15 : if( ingrid->noDerivatives() ) grid->setNoDerivatives(); 101 : std::vector<double> fspacing; 102 15 : grid->setBounds( ingrid->getMin(), ingrid->getMax(), ingrid->getNbin(), fspacing); 103 15 : setAveragingAction( std::move(grid), true ); 104 : 105 30 : simtemp=0.; parse("TEMP",simtemp); parseFlag("MINTOZERO",mintozero); 106 15 : if(simtemp>0) simtemp*=plumed.getAtoms().getKBoltzmann(); 107 0 : else simtemp=plumed.getAtoms().getKbT(); 108 15 : if( simtemp==0 ) error("TEMP not set - use keyword TEMP"); 109 : 110 : // Now create task list 111 8486 : for(unsigned i=0; i<mygrid->getNumberOfPoints(); ++i) addTaskToList(i); 112 : // And activate all tasks 113 15 : deactivateAllTasks(); 114 8486 : for(unsigned i=0; i<mygrid->getNumberOfPoints(); ++i) taskFlags[i]=1; 115 15 : lockContributors(); 116 15 : } 117 : 118 92 : unsigned ConvertToFES::getNumberOfQuantities() const { 119 92 : if( mygrid->noDerivatives() ) return 2; 120 64 : return 2 + mygrid->getDimension(); 121 : } 122 : 123 14199 : void ConvertToFES::compute( const unsigned& current, MultiValue& myvals ) const { 124 14199 : double val=getFunctionValue( current ); myvals.setValue(1, -simtemp*std::log(val) ); 125 14199 : if( !mygrid->noDerivatives() && val>0 ) { 126 36998 : for(unsigned i=0; i<mygrid->getDimension(); ++i) myvals.setValue( 2+i, -(simtemp/val)*ingrid->getGridElement(current,i+1) ); 127 : } 128 14199 : } 129 : 130 23 : void ConvertToFES::finishComputations( const std::vector<double>& buffer ) { 131 23 : ActionWithVessel::finishComputations( buffer ); 132 23 : if(!mintozero) return; 133 : 134 2 : double optval = mygrid->getGridElement( 0, 0 ); 135 2602 : for(unsigned i=0; i<mygrid->getNumberOfPoints(); ++i) { 136 2600 : double tval = mygrid->getGridElement( i, 0 ); 137 2600 : if( tval<optval || std::isnan(optval) ) { optval=tval; } 138 : } 139 2602 : for(unsigned i=0; i<mygrid->getNumberOfPoints(); ++i) mygrid->addToGridElement( i, 0, -optval ); 140 : } 141 : 142 15 : void ConvertToFES::runFinalJobs() { 143 15 : activated=true; update(); 144 15 : } 145 : 146 : } 147 : }