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