Line data Source code
1 : /* +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 : Copyright (c) 2020-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 :
23 : #include "Function.h"
24 : #include "core/ActionRegister.h"
25 : #include "tools/IFile.h"
26 : #include <limits>
27 :
28 : namespace PLMD {
29 : namespace function {
30 :
31 : //+PLUMEDOC FUNCTION FUNCPATHGENERAL
32 : /*
33 : This function calculates path collective variables (PCVs) using an arbitrary combination of collective variables.
34 :
35 : The method used to calculate the PCVs that is used in this method is described in \cite Hovan2019.
36 :
37 : This variable computes the progress along a given set of frames that is provided in an input file ("s" component) and the distance from them ("z" component).
38 : The input file could be a colvar file generated with plumed driver on a trajectory containing the frames.
39 :
40 : The metric for the path collective variables takes the following form:
41 :
42 : \f[
43 : R[X - X_i] = \sum_{j=1}^M c_j^2 (x_j - x_{i,j})^2\,.
44 : \f]
45 :
46 : Here, the coefficients \f$c_j\f$ determine the relative weights of the collective variables \f$c_j\f$ in the metric.
47 : A value for the lambda coefficient also needs to be provided, typically chosen in such a way that it ensures a smooth variation of the "s" component.
48 :
49 : \par Examples
50 :
51 : This command calculates the PCVs using the values from the file COLVAR_TRAJ and the provided values for the lambda and the coefficients.
52 : Since the columns in the file were not specified, the first one will be ignored (assumed to correspond to the time) and the rest used.
53 :
54 : \plumedfile
55 : FUNCPATHGENERAL ...
56 : LABEL=path
57 : LAMBDA=12.2
58 : REFERENCE=COLVAR_TRAJ
59 : COEFFICIENTS=0.3536,0.3536,0.3536,0.3536,0.7071
60 : ARG=d1,d2,d,t,drmsd
61 : ... FUNCPATHGENERAL
62 : \endplumedfile
63 :
64 : The command below is a variation of the previous one, specifying a subset of the collective variables and using a neighbor list.
65 : The columns are zero-indexed.
66 : The neighbor list will include the 10 closest frames and will be recalculated every 20 steps.
67 :
68 : \plumedfile
69 : FUNCPATHGENERAL ...
70 : LABEL=path
71 : LAMBDA=5.0
72 : REFERENCE=COLVAR_TRAJ
73 : COLUMNS=2,3,4
74 : COEFFICIENTS=0.3536,0.3536,0.3536
75 : ARG=d2,d,t
76 : NEIGH_SIZE=10
77 : NEIGH_STRIDE=20
78 : ... FUNCPATHGENERAL
79 : \endplumedfile
80 :
81 : */
82 : //+ENDPLUMEDOC
83 :
84 : class FuncPathGeneral : public Function {
85 : double lambda;
86 : int neigh_size;
87 : double neigh_stride;
88 :
89 : std::vector<double> coefficients;
90 : std::vector< std::vector<double> > path_cv_values;
91 :
92 : // For faster calculation
93 : std::vector<double> expdists;
94 :
95 : // For calculating derivatives
96 : std::vector< std::vector<double> > numerators;
97 : std::vector<double> s_path_ders;
98 : std::vector<double> z_path_ders;
99 :
100 : // For handling periodicity
101 : std::vector<double> domains;
102 :
103 : std::string reference;
104 : std::vector<int> columns;
105 :
106 : std::vector< std::pair<int,double> > neighpair;
107 : std::vector <Value*> allArguments;
108 :
109 : // Methods
110 : void loadReference();
111 :
112 : struct pairordering {
113 : bool operator ()(std::pair<int, double> const& a, std::pair<int, double> const& b) {
114 0 : return (a).second < (b).second;
115 : }
116 : };
117 :
118 : public:
119 : explicit FuncPathGeneral(const ActionOptions&);
120 : // Active methods:
121 : virtual void calculate();
122 : virtual void prepare();
123 : static void registerKeywords(Keywords& keys);
124 : };
125 :
126 : PLUMED_REGISTER_ACTION(FuncPathGeneral, "FUNCPATHGENERAL")
127 :
128 1 : void FuncPathGeneral::loadReference() {
129 1 : IFile input;
130 1 : input.open(reference);
131 1 : if (!input)
132 0 : plumed_merror("Could not open the reference file!");
133 18 : while (input)
134 : {
135 : std::vector<std::string> strings;
136 17 : Tools::getParsedLine(input, strings);
137 17 : if (strings.empty())
138 : continue;
139 : std::vector<double> colvarLine;
140 : double value;
141 16 : int max = columns.empty() ? strings.size() : columns.size();
142 128 : for (int i = 0; i < max; ++i)
143 : {
144 112 : int col = columns.empty() ? i : columns[i];
145 : // If no columns have been entered, ignore the first (time) and take the rest
146 112 : if (columns.empty() && i == 0)
147 16 : continue;
148 :
149 96 : Tools::convert(strings[col], value);
150 96 : colvarLine.push_back(value);
151 : }
152 16 : path_cv_values.push_back(colvarLine);
153 17 : }
154 1 : }
155 :
156 3 : void FuncPathGeneral::registerKeywords(Keywords& keys) {
157 3 : Function::registerKeywords(keys);
158 3 : keys.use("ARG");
159 6 : keys.add("compulsory", "LAMBDA", "Lambda parameter required for smoothing");
160 6 : keys.add("compulsory", "COEFFICIENTS", "Coefficients to be assigned to the CVs");
161 6 : keys.add("compulsory", "REFERENCE", "Colvar file needed to provide the CV milestones");
162 6 : keys.add("optional", "COLUMNS", "List of columns in the reference colvar file specifying the CVs");
163 6 : keys.add("optional", "NEIGH_SIZE", "Size of the neighbor list");
164 6 : keys.add("optional", "NEIGH_STRIDE", "How often the neighbor list needs to be calculated in time units");
165 6 : keys.addOutputComponent("s", "default", "Position on the path");
166 6 : keys.addOutputComponent("z", "default", "Distance from the path");
167 3 : }
168 :
169 1 : FuncPathGeneral::FuncPathGeneral(const ActionOptions&ao):
170 : Action(ao),
171 : Function(ao),
172 1 : neigh_size(-1),
173 1 : neigh_stride(-1.)
174 : {
175 1 : parse("LAMBDA", lambda);
176 1 : parse("NEIGH_SIZE", neigh_size);
177 1 : parse("NEIGH_STRIDE", neigh_stride);
178 1 : parse("REFERENCE", reference);
179 1 : parseVector("COEFFICIENTS", coefficients);
180 1 : parseVector("COLUMNS", columns);
181 1 : checkRead();
182 1 : log.printf(" lambda is %f\n", lambda);
183 1 : if (getNumberOfArguments() != coefficients.size())
184 0 : plumed_merror("The numbers of coefficients and CVs are different!");
185 1 : if (!columns.empty()) {
186 0 : if (columns.size() != coefficients.size())
187 0 : plumed_merror("The numbers of coefficients and columns are different!");
188 : }
189 1 : log.printf(" Consistency check completed! Your path cvs look good!\n");
190 :
191 : // Load the reference colvar file
192 1 : loadReference();
193 :
194 : // Do some neighbour printout
195 1 : if (neigh_stride > 0. || neigh_size > 0) {
196 0 : if (static_cast<unsigned>(neigh_size) > path_cv_values.size()) {
197 0 : log.printf(" List size required ( %d ) is too large: resizing to the maximum number of arg required: %d \n", neigh_size, getNumberOfArguments());
198 0 : neigh_size = path_cv_values.size();
199 : }
200 0 : log.printf(" Neighbour list enabled: \n");
201 0 : log.printf(" size : %d elements\n", neigh_size);
202 0 : log.printf(" stride : %f time \n", neigh_stride);
203 : } else {
204 1 : log.printf(" Neighbour list NOT enabled \n");
205 : }
206 :
207 2 : addComponentWithDerivatives("s"); componentIsNotPeriodic("s");
208 3 : addComponentWithDerivatives("z"); componentIsNotPeriodic("z");
209 :
210 : // Initialise vectors
211 1 : std::vector<double> temp (coefficients.size());
212 17 : for (unsigned i = 0; i < path_cv_values.size(); ++i) {
213 16 : numerators.push_back(temp);
214 16 : expdists.push_back(0.);
215 16 : s_path_ders.push_back(0.);
216 16 : z_path_ders.push_back(0.);
217 : }
218 :
219 : // Store the arguments
220 7 : for (unsigned i=0; i<getNumberOfArguments(); i++)
221 6 : allArguments.push_back(getPntrToArgument(i));
222 :
223 : // Get periodic domains, negative for not periodic, stores half the domain length (maximum difference)
224 7 : for (unsigned i = 0; i < allArguments.size(); ++i) {
225 6 : if (allArguments[i]->isPeriodic()) {
226 : double min_lim, max_lim;
227 0 : allArguments[i]->getDomain(min_lim, max_lim);
228 0 : domains.push_back((max_lim - min_lim) / 2);
229 : }
230 : else
231 6 : domains.push_back(-1.);
232 : }
233 1 : }
234 :
235 : // Calculator
236 175001 : void FuncPathGeneral::calculate() {
237 : double s_path = 0.;
238 : double partition = 0.;
239 : double tmp, value, diff, expdist, s_der, z_der;
240 : int ii;
241 :
242 : typedef std::vector< std::pair< int,double> >::iterator pairiter;
243 :
244 2975001 : for (pairiter it = neighpair.begin(); it != neighpair.end(); ++it) {
245 2800000 : (*it).second = 0.;
246 : }
247 :
248 175001 : if (neighpair.empty()) {
249 : // Resize at the first step
250 1 : neighpair.resize(path_cv_values.size());
251 17 : for (unsigned i = 0; i < path_cv_values.size(); ++i)
252 16 : neighpair[i].first = i;
253 : }
254 :
255 175001 : Value* val_s_path=getPntrToComponent("s");
256 175001 : Value* val_z_path=getPntrToComponent("z");
257 :
258 1225007 : for(unsigned j = 0; j < allArguments.size(); ++j) {
259 1050006 : value = allArguments[j]->get();
260 17850102 : for (pairiter it = neighpair.begin(); it != neighpair.end(); ++it) {
261 16800096 : diff = (value - path_cv_values[(*it).first][j]);
262 16800096 : if (domains[j] > 0) {
263 0 : if (diff > domains[j])
264 0 : diff -= 2 * domains[j];
265 0 : if (diff < -domains[j])
266 0 : diff += 2 * domains[j];
267 : }
268 33600192 : (*it).second += Tools::fastpow(coefficients[j] * diff, 2);
269 33600192 : numerators[(*it).first][j] = 2 * Tools::fastpow(coefficients[j], 2) * diff;
270 : }
271 : }
272 :
273 2975017 : for (pairiter it = neighpair.begin(); it != neighpair.end(); ++it) {
274 2800016 : expdist = std::exp(-lambda * (*it).second);
275 2800016 : expdists[(*it).first] = expdist;
276 2800016 : s_path += ((*it).first + 1) * expdist;
277 2800016 : partition += expdist;
278 : }
279 :
280 175001 : if(partition==0.0) partition=std::numeric_limits<double>::min();
281 :
282 175001 : s_path /= partition;
283 : val_s_path->set(s_path);
284 175001 : val_z_path->set(-(1. / lambda) * std::log(partition));
285 :
286 : // Derivatives
287 2975017 : for (pairiter it = neighpair.begin(); it != neighpair.end(); ++it) {
288 2800016 : ii = (*it).first;
289 2800016 : tmp = lambda * expdists[ii] * (s_path - (ii + 1)) / partition;
290 2800016 : s_path_ders[ii] = tmp;
291 2800016 : z_path_ders[ii] = expdists[ii] / partition;
292 : }
293 1225007 : for (unsigned i = 0; i < coefficients.size(); ++i) {
294 : s_der = 0.;
295 : z_der = 0.;
296 17850102 : for (pairiter it = neighpair.begin(); it != neighpair.end(); ++it) {
297 16800096 : ii = (*it).first;
298 16800096 : s_der += s_path_ders[ii] * numerators[ii][i];
299 16800096 : z_der += z_path_ders[ii] * numerators[ii][i];
300 : }
301 : setDerivative(val_s_path, i, s_der);
302 : setDerivative(val_z_path, i, z_der);
303 : }
304 175001 : }
305 :
306 : // Prepare the required arguments
307 175001 : void FuncPathGeneral::prepare() {
308 : // Neighbour list: rank and activate the chain for the next step
309 :
310 : // Neighbour list: if neigh_size < 0 never sort and keep the full vector
311 : // Neighbour list: if neigh_size > 0
312 : // if the size is full -> sort the vector and decide the dependencies for next step
313 : // if the size is not full -> check if next step will need the full dependency otherwise keep these dependencies
314 :
315 175001 : if (neigh_size > 0) {
316 0 : if (neighpair.size() == path_cv_values.size()) {
317 : // The complete round has been done: need to sort, shorten and give it a go
318 : // Sort the values
319 0 : std::sort(neighpair.begin(), neighpair.end(), pairordering());
320 : // Resize the effective list
321 0 : neighpair.resize(neigh_size);
322 0 : log.printf(" NEIGHBOUR LIST NOW INCLUDES INDICES: ");
323 0 : for (int i = 0; i < neigh_size; ++i)
324 0 : log.printf(" %i ",neighpair[i].first);
325 0 : log.printf(" \n");
326 : } else {
327 0 : if (int(getStep()) % int(neigh_stride / getTimeStep()) == 0) {
328 0 : log.printf(" Time %f : recalculating full neighbour list \n", getStep() * getTimeStep());
329 0 : neighpair.resize(path_cv_values.size());
330 0 : for (unsigned i = 0; i < path_cv_values.size(); ++i)
331 0 : neighpair[i].first = i;
332 : }
333 : }
334 : }
335 :
336 175001 : requestArguments(allArguments);
337 175001 : }
338 :
339 : }
340 : }
|