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 the paper from the bibliography below.
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 : $$
43 : R[X - X_i] = \sum_{j=1}^M c_j^2 (x_j - x_{i,j})^2\,.
44 : $$
45 :
46 : Here, the coefficients $c_j$ determine the relative weights of the collective variables $c_j$ 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 : ## 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 : ```plumed
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 : ```
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 : ```plumed
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 : ```
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 : }
134 18 : while (input) {
135 : std::vector<std::string> strings;
136 17 : Tools::getParsedLine(input, strings);
137 17 : if (strings.empty()) {
138 : continue;
139 : }
140 : std::vector<double> colvarLine;
141 : double value;
142 16 : int max = columns.empty() ? strings.size() : columns.size();
143 128 : for (int i = 0; i < max; ++i) {
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 :
150 96 : Tools::convert(strings[col], value);
151 96 : colvarLine.push_back(value);
152 : }
153 16 : path_cv_values.push_back(colvarLine);
154 17 : }
155 1 : }
156 :
157 3 : void FuncPathGeneral::registerKeywords(Keywords& keys) {
158 3 : Function::registerKeywords(keys);
159 3 : keys.add("compulsory", "LAMBDA", "Lambda parameter required for smoothing");
160 3 : keys.add("compulsory", "COEFFICIENTS", "Coefficients to be assigned to the CVs");
161 3 : keys.add("compulsory", "REFERENCE", "Colvar file needed to provide the CV milestones");
162 3 : keys.add("optional", "COLUMNS", "List of columns in the reference colvar file specifying the CVs");
163 3 : keys.add("optional", "NEIGH_SIZE", "Size of the neighbor list");
164 3 : keys.add("optional", "NEIGH_STRIDE", "How often the neighbor list needs to be calculated in time units");
165 6 : keys.addOutputComponent("s", "default", "scalar","Position on the path");
166 6 : keys.addOutputComponent("z", "default", "scalar","Distance from the path");
167 3 : keys.addDOI("10.1021/acs.jctc.8b00563");
168 3 : }
169 :
170 1 : FuncPathGeneral::FuncPathGeneral(const ActionOptions&ao):
171 : Action(ao),
172 : Function(ao),
173 1 : neigh_size(-1),
174 1 : neigh_stride(-1.) {
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 : }
186 1 : if (!columns.empty()) {
187 0 : if (columns.size() != coefficients.size()) {
188 0 : plumed_merror("The numbers of coefficients and columns are different!");
189 : }
190 : }
191 1 : log.printf(" Consistency check completed! Your path cvs look good!\n");
192 :
193 : // Load the reference colvar file
194 1 : loadReference();
195 :
196 : // Do some neighbour printout
197 1 : if (neigh_stride > 0. || neigh_size > 0) {
198 0 : if (static_cast<unsigned>(neigh_size) > path_cv_values.size()) {
199 0 : log.printf(" List size required ( %d ) is too large: resizing to the maximum number of arg required: %d \n", neigh_size, getNumberOfArguments());
200 0 : neigh_size = path_cv_values.size();
201 : }
202 0 : log.printf(" Neighbour list enabled: \n");
203 0 : log.printf(" size : %d elements\n", neigh_size);
204 0 : log.printf(" stride : %f time \n", neigh_stride);
205 : } else {
206 1 : log.printf(" Neighbour list NOT enabled \n");
207 : }
208 :
209 1 : addComponentWithDerivatives("s");
210 1 : componentIsNotPeriodic("s");
211 1 : addComponentWithDerivatives("z");
212 2 : componentIsNotPeriodic("z");
213 :
214 : // Initialise vectors
215 1 : std::vector<double> temp (coefficients.size());
216 17 : for (unsigned i = 0; i < path_cv_values.size(); ++i) {
217 16 : numerators.push_back(temp);
218 16 : expdists.push_back(0.);
219 16 : s_path_ders.push_back(0.);
220 16 : z_path_ders.push_back(0.);
221 : }
222 :
223 : // Store the arguments
224 7 : for (unsigned i=0; i<getNumberOfArguments(); i++) {
225 6 : allArguments.push_back(getPntrToArgument(i));
226 : }
227 :
228 : // Get periodic domains, negative for not periodic, stores half the domain length (maximum difference)
229 7 : for (unsigned i = 0; i < allArguments.size(); ++i) {
230 6 : if (allArguments[i]->isPeriodic()) {
231 : double min_lim, max_lim;
232 0 : allArguments[i]->getDomain(min_lim, max_lim);
233 0 : domains.push_back((max_lim - min_lim) / 2);
234 : } else {
235 6 : domains.push_back(-1.);
236 : }
237 : }
238 1 : }
239 :
240 : // Calculator
241 175001 : void FuncPathGeneral::calculate() {
242 : double s_path = 0.;
243 : double partition = 0.;
244 : double tmp, value, diff, expdist, s_der, z_der;
245 : int ii;
246 :
247 : typedef std::vector< std::pair< int,double> >::iterator pairiter;
248 :
249 2975001 : for (pairiter it = neighpair.begin(); it != neighpair.end(); ++it) {
250 2800000 : (*it).second = 0.;
251 : }
252 :
253 175001 : if (neighpair.empty()) {
254 : // Resize at the first step
255 1 : neighpair.resize(path_cv_values.size());
256 17 : for (unsigned i = 0; i < path_cv_values.size(); ++i) {
257 16 : neighpair[i].first = i;
258 : }
259 : }
260 :
261 175001 : Value* val_s_path=getPntrToComponent("s");
262 175001 : Value* val_z_path=getPntrToComponent("z");
263 :
264 1225007 : for(unsigned j = 0; j < allArguments.size(); ++j) {
265 1050006 : value = allArguments[j]->get();
266 17850102 : for (pairiter it = neighpair.begin(); it != neighpair.end(); ++it) {
267 16800096 : diff = (value - path_cv_values[(*it).first][j]);
268 16800096 : if (domains[j] > 0) {
269 0 : if (diff > domains[j]) {
270 0 : diff -= 2 * domains[j];
271 : }
272 0 : if (diff < -domains[j]) {
273 0 : diff += 2 * domains[j];
274 : }
275 : }
276 33600192 : (*it).second += Tools::fastpow(coefficients[j] * diff, 2);
277 33600192 : numerators[(*it).first][j] = 2 * Tools::fastpow(coefficients[j], 2) * diff;
278 : }
279 : }
280 :
281 2975017 : for (pairiter it = neighpair.begin(); it != neighpair.end(); ++it) {
282 2800016 : expdist = std::exp(-lambda * (*it).second);
283 2800016 : expdists[(*it).first] = expdist;
284 2800016 : s_path += ((*it).first + 1) * expdist;
285 2800016 : partition += expdist;
286 : }
287 :
288 175001 : if(partition==0.0) {
289 : partition=std::numeric_limits<double>::min();
290 : }
291 :
292 175001 : s_path /= partition;
293 : val_s_path->set(s_path);
294 175001 : val_z_path->set(-(1. / lambda) * std::log(partition));
295 :
296 : // Derivatives
297 2975017 : for (pairiter it = neighpair.begin(); it != neighpair.end(); ++it) {
298 2800016 : ii = (*it).first;
299 2800016 : tmp = lambda * expdists[ii] * (s_path - (ii + 1)) / partition;
300 2800016 : s_path_ders[ii] = tmp;
301 2800016 : z_path_ders[ii] = expdists[ii] / partition;
302 : }
303 1225007 : for (unsigned i = 0; i < coefficients.size(); ++i) {
304 : s_der = 0.;
305 : z_der = 0.;
306 17850102 : for (pairiter it = neighpair.begin(); it != neighpair.end(); ++it) {
307 16800096 : ii = (*it).first;
308 16800096 : s_der += s_path_ders[ii] * numerators[ii][i];
309 16800096 : z_der += z_path_ders[ii] * numerators[ii][i];
310 : }
311 : setDerivative(val_s_path, i, s_der);
312 : setDerivative(val_z_path, i, z_der);
313 : }
314 175001 : }
315 :
316 : // Prepare the required arguments
317 175001 : void FuncPathGeneral::prepare() {
318 : // Neighbour list: rank and activate the chain for the next step
319 :
320 : // Neighbour list: if neigh_size < 0 never sort and keep the full vector
321 : // Neighbour list: if neigh_size > 0
322 : // if the size is full -> sort the vector and decide the dependencies for next step
323 : // if the size is not full -> check if next step will need the full dependency otherwise keep these dependencies
324 :
325 175001 : if (neigh_size > 0) {
326 0 : if (neighpair.size() == path_cv_values.size()) {
327 : // The complete round has been done: need to sort, shorten and give it a go
328 : // Sort the values
329 0 : std::sort(neighpair.begin(), neighpair.end(), pairordering());
330 : // Resize the effective list
331 0 : neighpair.resize(neigh_size);
332 0 : log.printf(" NEIGHBOUR LIST NOW INCLUDES INDICES: ");
333 0 : for (int i = 0; i < neigh_size; ++i) {
334 0 : log.printf(" %i ",neighpair[i].first);
335 : }
336 0 : log.printf(" \n");
337 : } else {
338 0 : if (int(getStep()) % int(neigh_stride / getTimeStep()) == 0) {
339 0 : log.printf(" Time %f : recalculating full neighbour list \n", getStep() * getTimeStep());
340 0 : neighpair.resize(path_cv_values.size());
341 0 : for (unsigned i = 0; i < path_cv_values.size(); ++i) {
342 0 : neighpair[i].first = i;
343 : }
344 : }
345 : }
346 : }
347 :
348 175001 : requestArguments(allArguments);
349 175001 : }
350 :
351 : }
352 : }
|