Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 : Copyright (c) 2016-2021 The VES code team 3 : (see the PEOPLE-VES file at the root of this folder for a list of names) 4 : 5 : See http://www.ves-code.org for more information. 6 : 7 : This file is part of VES code module. 8 : 9 : The VES code module 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 : The VES code module 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 the VES code module. If not, see <http://www.gnu.org/licenses/>. 21 : +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ */ 22 : 23 : #include "TargetDistribution.h" 24 : #include "VesTools.h" 25 : 26 : 27 : #include "core/ActionRegister.h" 28 : #include "core/ActionSet.h" 29 : #include "core/PlumedMain.h" 30 : #include "tools/Grid.h" 31 : 32 : #include "GridIntegrationWeights.h" 33 : 34 : 35 : namespace PLMD { 36 : 37 : class Action; 38 : 39 : namespace ves { 40 : 41 : //+PLUMEDOC VES_TARGETDIST TD_PRODUCT_COMBINATION 42 : /* 43 : Target distribution given by product combination of distributions (static or dynamic). 44 : 45 : Employ a target distribution that is a product combination of the other 46 : distributions, defined as 47 : 48 : $$ 49 : p(\mathbf{s}) = 50 : \frac{\prod_{i} p_{i}(\mathbf{s})} 51 : {\int d \mathbf{s} \prod_{i} p_{i}(\mathbf{s})} 52 : $$ 53 : 54 : where the distributions $p_{i}(\mathbf{s})$ are in full dimensional space 55 : of the arguments used. 56 : 57 : Note the difference between this target distribution and the one defined in 58 : [TD_PRODUCT_DISTRIBUTION](TD_PRODUCT_DISTRIBUTION.md). Here we have a non-separable distribution given 59 : as a product of distribution $p_{i}(\mathbf{s})$ which are in full dimensional 60 : space of the arguments used. 61 : 62 : The labels of the distributions $p_{i}(\mathbf{s})$ to be used in the 63 : product combination are given in the DISTRIBUTIONS keyword. 64 : 65 : The target distribution resulting from the product combination will be 66 : automatically normalized. Therefore, the product combination needs to 67 : be a proper distribution that is non-negative and that can be normalized. The 68 : code will perform checks to make sure that this is indeed the case. 69 : 70 : The product combination will be a dynamic target distribution if one or more 71 : of the distributions used is a dynamic distribution. Otherwise it will be a 72 : static distribution. 73 : 74 : ## Examples 75 : 76 : In the following example the overall interval on which the 77 : target distribution is defined is from 0.23 to 0.8. 78 : We employ a product combination of a well-tempered 79 : distribution and a uniform distribution that decays to 80 : zero at 0.6. This results in a target distribution that 81 : is well-tempered from 0.23 to 0.6 and then decays to zero. 82 : In other words, we cut off the tail of the well-tempered 83 : distribution at 0.6 84 : 85 : ```plumed 86 : td_welltemp: TD_WELLTEMPERED BIASFACTOR=5 87 : td_uniform: TD_UNIFORM MINIMA=0.23 MAXIMA=0.6 SIGMA_MAXIMA=0.05 88 : td_combination: TD_PRODUCT_COMBINATION DISTRIBUTIONS=td_uniform,td_welltemp 89 : ``` 90 : 91 : In the following example the overall interval on which the 92 : target distribution is defined is from -4 to 4. 93 : We employ a product of a Gaussian distribution with two centers 94 : and distribution that is uniform on the interval -3 to 3 and 95 : then smoothly decays to zero outside that interval. 96 : The overall effect will then be to cut off the tails of the 97 : Gaussian distribution 98 : 99 : ```plumed 100 : TD_GAUSSIAN ... 101 : CENTER1=-2.9 SIGMA1=1.0 102 : CENTER2=+2.9 SIGMA2=0.4 103 : LABEL=td_gauss 104 : ... TD_GAUSSIAN 105 : 106 : TD_UNIFORM ... 107 : MINIMA=-3.0 SIGMA_MINIMA=0.20 108 : MAXIMA=+3.0 SIGMA_MAXIMA=0.15 109 : LABEL=td_uni 110 : ... TD_UNIFORM 111 : 112 : td_pc: TD_PRODUCT_COMBINATION DISTRIBUTIONS=td_gauss,td_uni 113 : ``` 114 : 115 : */ 116 : //+ENDPLUMEDOC 117 : 118 : class VesBias; 119 : 120 : class TD_ProductCombination: public TargetDistribution { 121 : private: 122 : std::vector<TargetDistribution*> distribution_pntrs_; 123 : std::vector<Grid*> grid_pntrs_; 124 : unsigned int ndist_; 125 : void setupAdditionalGrids(const std::vector<Value*>&, const std::vector<std::string>&, const std::vector<std::string>&, const std::vector<unsigned int>&) override; 126 : public: 127 : static void registerKeywords(Keywords&); 128 : explicit TD_ProductCombination(const ActionOptions& ao); 129 : void updateGrid() override; 130 : double getValue(const std::vector<double>&) const override; 131 : // 132 : void linkVesBias(VesBias*) override; 133 : void linkAction(Action*) override; 134 : // 135 : void linkBiasGrid(Grid*) override; 136 : void linkBiasWithoutCutoffGrid(Grid*) override; 137 : void linkFesGrid(Grid*) override; 138 : // 139 : }; 140 : 141 : 142 : PLUMED_REGISTER_ACTION(TD_ProductCombination,"TD_PRODUCT_COMBINATION") 143 : 144 : 145 8 : void TD_ProductCombination::registerKeywords(Keywords& keys) { 146 8 : TargetDistribution::registerKeywords(keys); 147 8 : keys.add("compulsory","DISTRIBUTIONS","The labels of the target distribution actions to be used in the product combination."); 148 8 : keys.use("WELLTEMPERED_FACTOR"); 149 8 : keys.use("SHIFT_TO_ZERO"); 150 8 : } 151 : 152 : 153 4 : TD_ProductCombination::TD_ProductCombination(const ActionOptions& ao): 154 : PLUMED_VES_TARGETDISTRIBUTION_INIT(ao), 155 8 : distribution_pntrs_(0), 156 4 : grid_pntrs_(0), 157 4 : ndist_(0) { 158 4 : std::vector<std::string> targetdist_labels; 159 4 : parseVector("DISTRIBUTIONS",targetdist_labels); 160 : 161 4 : std::string error_msg = ""; 162 8 : distribution_pntrs_ = VesTools::getPointersFromLabels<TargetDistribution*>(targetdist_labels,plumed.getActionSet(),error_msg); 163 4 : if(error_msg.size()>0) { 164 0 : plumed_merror("Error in keyword DISTRIBUTIONS of "+getName()+": "+error_msg); 165 : } 166 : 167 12 : for(unsigned int i=0; i<distribution_pntrs_.size(); i++) { 168 8 : if(distribution_pntrs_[i]->isDynamic()) { 169 : setDynamic(); 170 : } 171 8 : if(distribution_pntrs_[i]->fesGridNeeded()) { 172 : setFesGridNeeded(); 173 : } 174 16 : if(distribution_pntrs_[i]->biasGridNeeded()) { 175 : setBiasGridNeeded(); 176 : } 177 : } 178 : 179 4 : ndist_ = distribution_pntrs_.size(); 180 4 : grid_pntrs_.assign(ndist_,NULL); 181 4 : if(ndist_==0) { 182 0 : plumed_merror(getName()+ ": no distributions are given."); 183 : } 184 4 : if(ndist_==1) { 185 0 : plumed_merror(getName()+ ": giving only one distribution does not make sense."); 186 : } 187 : // 188 4 : checkRead(); 189 4 : } 190 : 191 : 192 0 : double TD_ProductCombination::getValue(const std::vector<double>& argument) const { 193 0 : plumed_merror("getValue not implemented for TD_ProductCombination"); 194 : return 0.0; 195 : } 196 : 197 : 198 4 : void TD_ProductCombination::setupAdditionalGrids(const std::vector<Value*>& arguments, const std::vector<std::string>& min, const std::vector<std::string>& max, const std::vector<unsigned int>& nbins) { 199 12 : for(unsigned int i=0; i<ndist_; i++) { 200 8 : distribution_pntrs_[i]->setupGrids(arguments,min,max,nbins); 201 8 : if(distribution_pntrs_[i]->getDimension()!=this->getDimension()) { 202 0 : plumed_merror(getName() + ": all target distribution must have the same dimension"); 203 : } 204 8 : grid_pntrs_[i]=distribution_pntrs_[i]->getTargetDistGridPntr(); 205 : } 206 4 : } 207 : 208 : 209 14 : void TD_ProductCombination::updateGrid() { 210 42 : for(unsigned int i=0; i<ndist_; i++) { 211 28 : distribution_pntrs_[i]->updateTargetDist(); 212 : } 213 28 : std::vector<double> integration_weights = GridIntegrationWeights::getIntegrationWeights(getTargetDistGridPntr()); 214 : double norm = 0.0; 215 11717 : for(Grid::index_t l=0; l<targetDistGrid().getSize(); l++) { 216 : double value = 1.0; 217 35109 : for(unsigned int i=0; i<ndist_; i++) { 218 23406 : value *= grid_pntrs_[i]->getValue(l); 219 : } 220 11703 : if(value<0.0 && !isTargetDistGridShiftedToZero()) { 221 0 : plumed_merror(getName()+": The target distribution function gives negative values. You should change the definition of the target distribution to avoid this. You can also use the SHIFT_TO_ZERO keyword to avoid this problem."); 222 : } 223 11703 : norm += integration_weights[l]*value; 224 11703 : targetDistGrid().setValue(l,value); 225 11703 : logTargetDistGrid().setValue(l,-std::log(value)); 226 : } 227 : 228 14 : if(norm>0.0) { 229 14 : targetDistGrid().scaleAllValuesAndDerivatives(1.0/norm); 230 0 : } else if(!isTargetDistGridShiftedToZero()) { 231 0 : plumed_merror(getName()+": The target distribution function cannot be normalized properly. You should change the definition of the target distribution to avoid this. You can also use the SHIFT_TO_ZERO keyword to avoid this problem."); 232 : } 233 14 : logTargetDistGrid().setMinToZero(); 234 14 : } 235 : 236 : 237 1 : void TD_ProductCombination::linkVesBias(VesBias* vesbias_pntr_in) { 238 1 : TargetDistribution::linkVesBias(vesbias_pntr_in); 239 3 : for(unsigned int i=0; i<ndist_; i++) { 240 2 : distribution_pntrs_[i]->linkVesBias(vesbias_pntr_in); 241 : } 242 1 : } 243 : 244 : 245 0 : void TD_ProductCombination::linkAction(Action* action_pntr_in) { 246 0 : TargetDistribution::linkAction(action_pntr_in); 247 0 : for(unsigned int i=0; i<ndist_; i++) { 248 0 : distribution_pntrs_[i]->linkAction(action_pntr_in); 249 : } 250 0 : } 251 : 252 : 253 0 : void TD_ProductCombination::linkBiasGrid(Grid* bias_grid_pntr_in) { 254 0 : TargetDistribution::linkBiasGrid(bias_grid_pntr_in); 255 0 : for(unsigned int i=0; i<ndist_; i++) { 256 0 : distribution_pntrs_[i]->linkBiasGrid(bias_grid_pntr_in); 257 : } 258 0 : } 259 : 260 : 261 0 : void TD_ProductCombination::linkBiasWithoutCutoffGrid(Grid* bias_withoutcutoff_grid_pntr_in) { 262 0 : TargetDistribution::linkBiasWithoutCutoffGrid(bias_withoutcutoff_grid_pntr_in); 263 0 : for(unsigned int i=0; i<ndist_; i++) { 264 0 : distribution_pntrs_[i]->linkBiasWithoutCutoffGrid(bias_withoutcutoff_grid_pntr_in); 265 : } 266 0 : } 267 : 268 : 269 1 : void TD_ProductCombination::linkFesGrid(Grid* fes_grid_pntr_in) { 270 1 : TargetDistribution::linkFesGrid(fes_grid_pntr_in); 271 3 : for(unsigned int i=0; i<ndist_; i++) { 272 2 : distribution_pntrs_[i]->linkFesGrid(fes_grid_pntr_in); 273 : } 274 1 : } 275 : 276 : 277 : } 278 : }