Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2016-2018 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 "tools/ConjugateGradient.h" 24 : #include "ActionWithGrid.h" 25 : #include "EvaluateGridFunction.h" 26 : #include "Interpolator.h" 27 : 28 : //+PLUMEDOC GRIDCALC FIND_GRID_MINIMUM 29 : /* 30 : Find the point with the lowest value of the function on the grid 31 : 32 : \par Examples 33 : 34 : */ 35 : //+ENDPLUMEDOC 36 : 37 : //+PLUMEDOC GRIDCALC FIND_GRID_MAXIMUM 38 : /* 39 : Find the point with the highest value of the function on the grid 40 : 41 : \par Examples 42 : 43 : */ 44 : //+ENDPLUMEDOC 45 : 46 : namespace PLMD { 47 : namespace gridtools { 48 : 49 : class FindGridOptimum : public ActionWithGrid { 50 : private: 51 : bool domin; 52 : double cgtol; 53 : std::unique_ptr<Interpolator> function; 54 : public: 55 : static void registerKeywords( Keywords& keys ); 56 : explicit FindGridOptimum(const ActionOptions&ao); 57 0 : void setupOnFirstStep( const bool incalc ) override { 58 0 : plumed_error(); 59 : } 60 : unsigned getNumberOfDerivatives() override ; 61 : void calculate() override ; 62 : std::vector<std::string> getGridCoordinateNames() const override ; 63 : const GridCoordinatesObject& getGridCoordinatesObject() const override ; 64 : double calculateValueAndDerivatives( const std::vector<double>& pp, std::vector<double>& der ); 65 0 : void performTask( const unsigned& current, MultiValue& myvals ) const override { 66 0 : plumed_error(); 67 : } 68 : }; 69 : 70 : PLUMED_REGISTER_ACTION(FindGridOptimum,"FIND_GRID_MAXIMUM") 71 : PLUMED_REGISTER_ACTION(FindGridOptimum,"FIND_GRID_MINIMUM") 72 : 73 8 : void FindGridOptimum::registerKeywords( Keywords& keys ) { 74 8 : ActionWithGrid::registerKeywords( keys ); 75 16 : keys.addInputKeyword("compulsory","ARG","grid","the label for the function on the grid that you would like to find the optimum in"); 76 8 : keys.addFlag("NOINTERPOL",false,"do not interpolate the function when finding the optimum"); 77 8 : keys.add("compulsory","CGTOL","1E-4","the tolerance for the conjugate gradient optimization"); 78 16 : keys.addOutputComponent("optval","default","scalar","the value of the function at the optimum"); 79 16 : keys.addOutputComponent("_opt","default","scalar","the values of the arguments of the function at the optimum can be referenced elsewhere in the input file " 80 : "by using the names of the arguments followed by the string _opt"); 81 8 : } 82 : 83 2 : FindGridOptimum::FindGridOptimum(const ActionOptions&ao): 84 : Action(ao), 85 : ActionWithGrid(ao), 86 2 : cgtol(0) { 87 2 : if( getName()=="FIND_GRID_MAXIMUM" ) { 88 0 : domin=false; 89 2 : } else if( getName()=="FIND_GRID_MINIMUM" ) { 90 2 : domin=true; 91 : } else { 92 0 : plumed_error(); 93 : } 94 : // Create value for this function 95 2 : std::vector<std::string> argn( getGridCoordinateNames() ); 96 2 : std::vector<unsigned> shape(0); 97 5 : for(unsigned i=0; i<argn.size(); ++i) { 98 6 : addComponent( argn[i] + "_opt", shape ); 99 : } 100 2 : addComponent( "optval", shape ); 101 2 : componentIsNotPeriodic( "optval" ); 102 2 : bool nointerpol=false; 103 2 : parseFlag("NOINTERPOL",nointerpol); 104 2 : if( !nointerpol ) { 105 2 : parse("CGTOL",cgtol); 106 4 : function=Tools::make_unique<Interpolator>( getPntrToArgument(0), getGridCoordinatesObject() ); 107 : } 108 2 : } 109 : 110 0 : unsigned FindGridOptimum::getNumberOfDerivatives() { 111 0 : return 0; 112 : } 113 : 114 240 : const GridCoordinatesObject& FindGridOptimum::getGridCoordinatesObject() const { 115 240 : ActionWithGrid* ag=ActionWithGrid::getInputActionWithGrid( getPntrToArgument(0)->getPntrToAction() ); 116 240 : plumed_assert( ag ); 117 240 : return ag->getGridCoordinatesObject(); 118 : } 119 : 120 2 : std::vector<std::string> FindGridOptimum::getGridCoordinateNames() const { 121 2 : ActionWithGrid* ag=ActionWithGrid::getInputActionWithGrid( getPntrToArgument(0)->getPntrToAction() ); 122 2 : plumed_assert( ag ); 123 2 : return ag->getGridCoordinateNames(); 124 : } 125 : 126 236 : double FindGridOptimum::calculateValueAndDerivatives( const std::vector<double>& pp, std::vector<double>& der ) { 127 236 : double val = function->splineInterpolation( pp, der ); 128 : // We normalise the derivatives here and set them so that the linesearch is done over the cell that we know 129 : // in the grid that we know the minimum is inside 130 236 : std::vector<double> spacing( getGridCoordinatesObject().getGridSpacing() ); 131 : double norm = 0; 132 623 : for(unsigned i=0; i<der.size(); ++i) { 133 387 : norm += der[i]*der[i]; 134 : } 135 236 : norm = sqrt(norm); 136 623 : for(unsigned i=0; i<der.size(); ++i) { 137 387 : der[i] = spacing[i]*der[i] / norm; 138 : } 139 236 : if( domin ) { 140 : return val; 141 : } 142 : // If we are looking for a maximum 143 0 : for(unsigned i=0; i<der.size(); ++i) { 144 0 : der[i] = -der[i]; 145 : } 146 0 : return -val; 147 : } 148 : 149 2 : void FindGridOptimum::calculate() { 150 2 : const GridCoordinatesObject& ingrid = getGridCoordinatesObject(); 151 : Value* gval = getPntrToArgument(0); 152 2 : std::vector<double> optargs( gval->getRank() ); 153 2 : std::vector<unsigned> gridind( gval->getRank() ); 154 2 : double optval=gval->get( 0 ); 155 2 : ingrid.getGridPointCoordinates( 0, gridind, optargs ); 156 2 : unsigned nval = gval->getNumberOfValues(); 157 : bool constant=true; 158 2602 : for(unsigned i=0; i<nval; ++i) { 159 2600 : double tval = gval->get( i ); 160 2600 : if( domin && (tval<optval || std::isnan(optval)) ) { 161 : constant=false; 162 : optval=tval; 163 33 : ingrid.getGridPointCoordinates( i, gridind, optargs ); 164 : } 165 2600 : if( !domin && (tval>optval || std::isnan(optval)) ) { 166 : constant=false; 167 : optval=tval; 168 0 : ingrid.getGridPointCoordinates( i, gridind, optargs ); 169 : } 170 : } 171 : // This basically ensures we deal with cases where all points on the grid are infinity as isinf doesn't work on intel compiler 172 2 : if( constant ) { 173 0 : if( domin && gval->get(0)>=gval->get(1) ) { 174 0 : return; 175 0 : } else if( gval->get(0)<=gval->get(1) ) { 176 : return; 177 : } 178 : } 179 2 : if( std::isinf(optval) ) { 180 : return; 181 : } 182 : 183 2 : if( std::isnan(optval) ) { 184 0 : error("all values on grid are nans"); 185 : } 186 : // And do conjugate gradient optimisation (because we can!!) 187 2 : if( cgtol>0 ) { 188 : ConjugateGradient<FindGridOptimum> myminimiser( this ); 189 2 : myminimiser.minimise( cgtol, optargs, &FindGridOptimum::calculateValueAndDerivatives ); 190 : } 191 : // And set the final value 192 5 : for(unsigned j=0; j<optargs.size(); ++j) { 193 3 : getPntrToComponent(j)->set( optargs[j] ); 194 : } 195 2 : std::vector<double> optder( gval->getRank() ); 196 2 : getPntrToComponent(optargs.size())->set( function->splineInterpolation( optargs, optder ) ); 197 : } 198 : 199 : } 200 : }