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, H(x), to a free energy surface using F(x) = -k_B T ln H(x). 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 { 72 15 : return true; 73 : } 74 12 : void prepare() override { 75 12 : activated=true; 76 12 : } 77 23 : void prepareForAveraging() override { 78 23 : ActionWithInputGrid::prepareForAveraging(); 79 23 : activated=false; 80 23 : } 81 : void compute( const unsigned& current, MultiValue& myvals ) const override; 82 : void finishComputations( const std::vector<double>& buffer ) override; 83 0 : bool isPeriodic() override { 84 0 : return false; 85 : } 86 2234 : bool onStep() const override { 87 2234 : return activated; 88 : } 89 : void runFinalJobs() override; 90 : }; 91 : 92 13815 : PLUMED_REGISTER_ACTION(ConvertToFES,"CONVERT_TO_FES") 93 : 94 19 : void ConvertToFES::registerKeywords( Keywords& keys ) { 95 19 : ActionWithInputGrid::registerKeywords( keys ); 96 38 : keys.add("optional","TEMP","the temperature at which you are operating"); 97 38 : keys.addFlag("MINTOZERO",false,"set the minimum in the free energy to be equal to zero"); 98 19 : keys.remove("STRIDE"); 99 19 : keys.remove("KERNEL"); 100 19 : keys.remove("BANDWIDTH"); 101 19 : keys.remove("LOGWEIGHTS"); 102 19 : keys.remove("CLEAR"); 103 19 : keys.remove("NORMALIZATION"); 104 19 : } 105 : 106 15 : ConvertToFES::ConvertToFES(const ActionOptions&ao): 107 : Action(ao), 108 : ActionWithInputGrid(ao), 109 15 : activated(false) { 110 15 : plumed_assert( ingrid->getNumberOfComponents()==1 ); 111 : 112 : // Create a grid 113 30 : auto grid=createGrid( "grid", "COMPONENTS=" + getLabel() + " " + ingrid->getInputString() ); 114 15 : if( ingrid->noDerivatives() ) { 115 7 : grid->setNoDerivatives(); 116 : } 117 : std::vector<double> fspacing; 118 15 : grid->setBounds( ingrid->getMin(), ingrid->getMax(), ingrid->getNbin(), fspacing); 119 15 : setAveragingAction( std::move(grid), true ); 120 : 121 15 : simtemp=0.; 122 15 : parse("TEMP",simtemp); 123 15 : parseFlag("MINTOZERO",mintozero); 124 15 : if(simtemp>0) { 125 15 : simtemp*=plumed.getAtoms().getKBoltzmann(); 126 : } else { 127 0 : simtemp=plumed.getAtoms().getKbT(); 128 : } 129 15 : if( simtemp==0 ) { 130 0 : error("TEMP not set - use keyword TEMP"); 131 : } 132 : 133 : // Now create task list 134 8486 : for(unsigned i=0; i<mygrid->getNumberOfPoints(); ++i) { 135 8471 : addTaskToList(i); 136 : } 137 : // And activate all tasks 138 15 : deactivateAllTasks(); 139 8486 : for(unsigned i=0; i<mygrid->getNumberOfPoints(); ++i) { 140 8471 : taskFlags[i]=1; 141 : } 142 15 : lockContributors(); 143 15 : } 144 : 145 92 : unsigned ConvertToFES::getNumberOfQuantities() const { 146 92 : if( mygrid->noDerivatives() ) { 147 : return 2; 148 : } 149 64 : return 2 + mygrid->getDimension(); 150 : } 151 : 152 14199 : void ConvertToFES::compute( const unsigned& current, MultiValue& myvals ) const { 153 14199 : double val=getFunctionValue( current ); 154 14199 : myvals.setValue(1, -simtemp*std::log(val) ); 155 14199 : if( !mygrid->noDerivatives() && val>0 ) { 156 36998 : for(unsigned i=0; i<mygrid->getDimension(); ++i) { 157 27228 : myvals.setValue( 2+i, -(simtemp/val)*ingrid->getGridElement(current,i+1) ); 158 : } 159 : } 160 14199 : } 161 : 162 23 : void ConvertToFES::finishComputations( const std::vector<double>& buffer ) { 163 23 : ActionWithVessel::finishComputations( buffer ); 164 23 : if(!mintozero) { 165 : return; 166 : } 167 : 168 2 : double optval = mygrid->getGridElement( 0, 0 ); 169 2602 : for(unsigned i=0; i<mygrid->getNumberOfPoints(); ++i) { 170 2600 : double tval = mygrid->getGridElement( i, 0 ); 171 2600 : if( tval<optval || std::isnan(optval) ) { 172 : optval=tval; 173 : } 174 : } 175 2602 : for(unsigned i=0; i<mygrid->getNumberOfPoints(); ++i) { 176 2600 : mygrid->addToGridElement( i, 0, -optval ); 177 : } 178 : } 179 : 180 15 : void ConvertToFES::runFinalJobs() { 181 15 : activated=true; 182 15 : update(); 183 15 : } 184 : 185 : } 186 : }