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 : #include "core/ActionRegister.h"
27 : #include "core/ActionSet.h"
28 : #include "core/PlumedMain.h"
29 : #include "tools/Grid.h"
30 :
31 : #include <limits>
32 :
33 :
34 : namespace PLMD {
35 :
36 : class Action;
37 :
38 : namespace ves {
39 :
40 : //+PLUMEDOC VES_TARGETDIST TD_LINEAR_COMBINATION
41 : /*
42 : Target distribution given by linear combination of distributions (static or dynamic).
43 :
44 : Employ a target distribution that is a linear combination of the other
45 : distributions, defined as
46 :
47 : $$
48 : p(\mathbf{s}) = \sum_{i} w_{i} \, p_{i}(\mathbf{s})
49 : $$
50 :
51 : where the weights $w_{i}$ are normalized to 1, $\sum_{i}w_{i}=1$.
52 :
53 : The labels of the distributions $p_{i}(\mathbf{s})$ to be used in the
54 : linear combination are given in the DISTRIBUTIONS keyword.
55 :
56 : The weights $w_{i}$ can be given using
57 : the WEIGHTS keyword. The distributions are weighted equally if no weights are given.
58 :
59 : It is assumed that all the distributions $p_{i}(\mathbf{s})$ are normalized.
60 : If that is not the case for some reason should you
61 : normalize each distribution separately by using the NORMALIZE
62 : keyword when defining them in the input file (i.e. before the
63 : TD_LINEAR_COMBINATION action).
64 : Note that normalizing the overall
65 : linear combination will generally lead to different results than normalizing
66 : each distribution separately.
67 :
68 : The linear combination will be a dynamic target distribution if one or more
69 : of the distributions used is a dynamic distribution, otherwise it will be a
70 : static distribution.
71 :
72 : ## Examples
73 :
74 : Here we employ a linear combination of a uniform and a Gaussian distribution.
75 : No weights are given so the two distributions will be weighted equally.
76 :
77 : ```plumed
78 : td_uni: TD_UNIFORM
79 :
80 : td_gauss: TD_GAUSSIAN CENTER1=-2.0 SIGMA1=0.5
81 :
82 : td_comb: TD_LINEAR_COMBINATION DISTRIBUTIONS=td_uni,td_gauss
83 : ```
84 :
85 : Here we employ a linear combination of a uniform and two Gaussian distribution.
86 : The weights are automatically normalized to 1 such that giving
87 : WEIGHTS=1.0,1.0,2.0 as we do here is equal to giving WEIGHTS=0.25,0.25,0.50.
88 :
89 : ```plumed
90 : td_uni: TD_UNIFORM
91 :
92 : td_gauss1: TD_GAUSSIAN CENTER1=-2.0,-2.0 SIGMA1=0.5,0.3
93 :
94 : td_gauss2: TD_GAUSSIAN CENTER1=+2.0,+2.0 SIGMA1=0.3,0.5
95 :
96 : TD_LINEAR_COMBINATION ...
97 : DISTRIBUTIONS=td_uni,td_gauss1,td_gauss2
98 : WEIGHTS=1.0,1.0,2.0
99 : LABEL=td_comb
100 : ... TD_LINEAR_COMBINATION
101 : ```
102 :
103 : In the above example the two Gaussian kernels are given using two separate
104 : DISTRIBUTION keywords. As the [TD_GAUSSIAN](TD_GAUSSIAN.md) target distribution allows multiple
105 : centers is it also possible to use just one DISTRIBUTION keyword for the two
106 : Gaussian kernels. This is shown in the following example which will give the
107 : exact same result as the one above as the weights have been appropriately
108 : adjusted
109 :
110 : ```plumed
111 : td_uni: TD_UNIFORM
112 :
113 : TD_GAUSSIAN ...
114 : CENTER1=-2.0,-2.0 SIGMA1=0.5,0.3
115 : CENTER2=+2.0,+2.0 SIGMA2=0.3,0.5
116 : WEIGHTS=1.0,2.0
117 : LABEL=td_gauss
118 : ... TD_GAUSSIAN
119 :
120 : TD_LINEAR_COMBINATION ...
121 : DISTRIBUTIONS=td_uni,td_gauss
122 : WEIGHTS=0.25,0.75
123 : LABEL=td_comb
124 : ... TD_LINEAR_COMBINATION
125 : ```
126 :
127 : */
128 : //+ENDPLUMEDOC
129 :
130 : class VesBias;
131 :
132 : class TD_LinearCombination: public TargetDistribution {
133 : private:
134 : std::vector<TargetDistribution*> distribution_pntrs_;
135 : std::vector<Grid*> grid_pntrs_;
136 : std::vector<double> weights_;
137 : unsigned int ndist_;
138 : void setupAdditionalGrids(const std::vector<Value*>&, const std::vector<std::string>&, const std::vector<std::string>&, const std::vector<unsigned int>&) override;
139 : public:
140 : static void registerKeywords(Keywords&);
141 : explicit TD_LinearCombination(const ActionOptions& ao);
142 : void updateGrid() override;
143 : double getValue(const std::vector<double>&) const override;
144 : //
145 : void linkVesBias(VesBias*) override;
146 : void linkAction(Action*) override;
147 : //
148 : void linkBiasGrid(Grid*) override;
149 : void linkBiasWithoutCutoffGrid(Grid*) override;
150 : void linkFesGrid(Grid*) override;
151 : //
152 : };
153 :
154 :
155 : PLUMED_REGISTER_ACTION(TD_LinearCombination,"TD_LINEAR_COMBINATION")
156 :
157 :
158 16 : void TD_LinearCombination::registerKeywords(Keywords& keys) {
159 16 : TargetDistribution::registerKeywords(keys);
160 16 : keys.add("compulsory","DISTRIBUTIONS","The labels of the target distribution actions to be used in the linear combination.");
161 16 : keys.add("optional","WEIGHTS","The weights of target distributions. Have to be as many as the number of target distribution labels given in DISTRIBUTIONS. If no weights are given the distributions are weighted equally. The weights are automatically normalized to 1.");
162 16 : keys.use("WELLTEMPERED_FACTOR");
163 16 : keys.use("NORMALIZE");
164 16 : }
165 :
166 :
167 12 : TD_LinearCombination::TD_LinearCombination(const ActionOptions& ao):
168 : PLUMED_VES_TARGETDISTRIBUTION_INIT(ao),
169 24 : distribution_pntrs_(0),
170 12 : grid_pntrs_(0),
171 12 : weights_(0),
172 12 : ndist_(0) {
173 12 : std::vector<std::string> targetdist_labels;
174 12 : parseVector("DISTRIBUTIONS",targetdist_labels);
175 :
176 12 : std::string error_msg = "";
177 24 : distribution_pntrs_ = VesTools::getPointersFromLabels<TargetDistribution*>(targetdist_labels,plumed.getActionSet(),error_msg);
178 12 : if(error_msg.size()>0) {
179 0 : plumed_merror("Error in keyword DISTRIBUTIONS of "+getName()+": "+error_msg);
180 : }
181 :
182 40 : for(unsigned int i=0; i<distribution_pntrs_.size(); i++) {
183 28 : if(distribution_pntrs_[i]->isDynamic()) {
184 : setDynamic();
185 : }
186 28 : if(distribution_pntrs_[i]->fesGridNeeded()) {
187 : setFesGridNeeded();
188 : }
189 28 : if(distribution_pntrs_[i]->biasGridNeeded()) {
190 : setBiasGridNeeded();
191 : }
192 : }
193 :
194 12 : ndist_ = distribution_pntrs_.size();
195 12 : grid_pntrs_.assign(ndist_,NULL);
196 12 : if(ndist_==0) {
197 0 : plumed_merror(getName()+ ": no distributions are given.");
198 : }
199 12 : if(ndist_==1) {
200 0 : plumed_merror(getName()+ ": giving only one distribution does not make sense.");
201 : }
202 : //
203 24 : parseVector("WEIGHTS",weights_);
204 12 : if(weights_.size()==0) {
205 4 : weights_.assign(distribution_pntrs_.size(),1.0);
206 : }
207 12 : if(distribution_pntrs_.size()!=weights_.size()) {
208 0 : plumed_merror(getName()+ ": there has to be as many weights given in WEIGHTS as the number of target distribution labels given in DISTRIBUTIONS");
209 : }
210 : //
211 : double sum_weights=0.0;
212 40 : for(unsigned int i=0; i<weights_.size(); i++) {
213 28 : sum_weights+=weights_[i];
214 : }
215 40 : for(unsigned int i=0; i<weights_.size(); i++) {
216 28 : weights_[i]/=sum_weights;
217 : }
218 12 : checkRead();
219 12 : }
220 :
221 :
222 0 : double TD_LinearCombination::getValue(const std::vector<double>& argument) const {
223 0 : plumed_merror("getValue not implemented for TD_LinearCombination");
224 : return 0.0;
225 : }
226 :
227 :
228 12 : void TD_LinearCombination::setupAdditionalGrids(const std::vector<Value*>& arguments, const std::vector<std::string>& min, const std::vector<std::string>& max, const std::vector<unsigned int>& nbins) {
229 40 : for(unsigned int i=0; i<ndist_; i++) {
230 28 : distribution_pntrs_[i]->setupGrids(arguments,min,max,nbins);
231 28 : if(distribution_pntrs_[i]->getDimension()!=this->getDimension()) {
232 0 : plumed_merror(getName() + ": all target distribution must have the same dimension");
233 : }
234 28 : grid_pntrs_[i]=distribution_pntrs_[i]->getTargetDistGridPntr();
235 : }
236 12 : }
237 :
238 :
239 22 : void TD_LinearCombination::updateGrid() {
240 70 : for(unsigned int i=0; i<ndist_; i++) {
241 48 : distribution_pntrs_[i]->updateTargetDist();
242 : }
243 162033 : for(Grid::index_t l=0; l<targetDistGrid().getSize(); l++) {
244 : double value = 0.0;
245 506837 : for(unsigned int i=0; i<ndist_; i++) {
246 344826 : value += weights_[i]*grid_pntrs_[i]->getValue(l);
247 : }
248 162011 : if(value==0.0) {
249 : value=std::numeric_limits<double>::denorm_min();
250 : }
251 162011 : targetDistGrid().setValue(l,value);
252 162011 : logTargetDistGrid().setValue(l,-std::log(value));
253 : }
254 22 : logTargetDistGrid().setMinToZero();
255 22 : }
256 :
257 :
258 1 : void TD_LinearCombination::linkVesBias(VesBias* vesbias_pntr_in) {
259 1 : TargetDistribution::linkVesBias(vesbias_pntr_in);
260 3 : for(unsigned int i=0; i<ndist_; i++) {
261 2 : distribution_pntrs_[i]->linkVesBias(vesbias_pntr_in);
262 : }
263 1 : }
264 :
265 :
266 0 : void TD_LinearCombination::linkAction(Action* action_pntr_in) {
267 0 : TargetDistribution::linkAction(action_pntr_in);
268 0 : for(unsigned int i=0; i<ndist_; i++) {
269 0 : distribution_pntrs_[i]->linkAction(action_pntr_in);
270 : }
271 0 : }
272 :
273 :
274 0 : void TD_LinearCombination::linkBiasGrid(Grid* bias_grid_pntr_in) {
275 0 : TargetDistribution::linkBiasGrid(bias_grid_pntr_in);
276 0 : for(unsigned int i=0; i<ndist_; i++) {
277 0 : distribution_pntrs_[i]->linkBiasGrid(bias_grid_pntr_in);
278 : }
279 0 : }
280 :
281 :
282 0 : void TD_LinearCombination::linkBiasWithoutCutoffGrid(Grid* bias_withoutcutoff_grid_pntr_in) {
283 0 : TargetDistribution::linkBiasWithoutCutoffGrid(bias_withoutcutoff_grid_pntr_in);
284 0 : for(unsigned int i=0; i<ndist_; i++) {
285 0 : distribution_pntrs_[i]->linkBiasWithoutCutoffGrid(bias_withoutcutoff_grid_pntr_in);
286 : }
287 0 : }
288 :
289 :
290 1 : void TD_LinearCombination::linkFesGrid(Grid* fes_grid_pntr_in) {
291 1 : TargetDistribution::linkFesGrid(fes_grid_pntr_in);
292 3 : for(unsigned int i=0; i<ndist_; i++) {
293 2 : distribution_pntrs_[i]->linkFesGrid(fes_grid_pntr_in);
294 : }
295 1 : }
296 :
297 :
298 : }
299 : }
|