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 "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 (see \cite Hovan2019).
34 :
35 : 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).
36 : The input file could be a colvar file generated with plumed driver on a trajectory containing the frames.
37 :
38 : The metric for the path collective variables takes the following form:
39 :
40 : \f[
41 : R[X - X_i] = \sum_{j=1}^M c_j^2 (x_j - x_{i,j})^2\,.
42 : \f]
43 :
44 : Here, the coefficients \f$c_j\f$ determine the relative weights of the collective variables \f$c_j\f$ in the metric.
45 : 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.
46 :
47 : \par Examples
48 :
49 : This command calculates the PCVs using the values from the file COLVAR_TRAJ and the provided values for the lambda and the coefficients.
50 : 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.
51 :
52 : \plumedfile
53 : FUNCPATHGENERAL ...
54 : LABEL=path
55 : LAMBDA=12.2
56 : REFERENCE=COLVAR_TRAJ
57 : COEFFICIENTS=0.3536,0.3536,0.3536,0.3536,0.7071
58 : ARG=d1,d2,d,t,drmsd
59 : ... FUNCPATHGENERAL
60 : \endplumedfile
61 :
62 : The command below is a variation of the previous one, specifying a subset of the collective variables and using a neighbor list.
63 : The columns are zero-indexed.
64 : The neighbor list will include the 10 closest frames and will be recalculated every 20 steps.
65 :
66 : \plumedfile
67 : FUNCPATHGENERAL ...
68 : LABEL=path
69 : LAMBDA=5.0
70 : REFERENCE=COLVAR_TRAJ
71 : COLUMNS=2,3,4
72 : COEFFICIENTS=0.3536,0.3536,0.3536
73 : ARG=d2,d,t
74 : NEIGH_SIZE=10
75 : NEIGH_STRIDE=20
76 : ... FUNCPATHGENERAL
77 : \endplumedfile
78 :
79 : */
80 : //+ENDPLUMEDOC
81 :
82 : class FuncPathGeneral : public Function {
83 : double lambda;
84 : int neigh_size;
85 : double neigh_stride;
86 :
87 : std::vector<double> coefficients;
88 : std::vector< std::vector<double> > path_cv_values;
89 :
90 : // For faster calculation
91 : std::vector<double> expdists;
92 :
93 : // For calculating derivatives
94 : std::vector< std::vector<double> > numerators;
95 : std::vector<double> s_path_ders;
96 : std::vector<double> z_path_ders;
97 :
98 : // For handling periodicity
99 : std::vector<double> domains;
100 :
101 : std::string reference;
102 : std::vector<int> columns;
103 :
104 : std::vector< std::pair<int,double> > neighpair;
105 : std::vector <Value*> allArguments;
106 :
107 : // Methods
108 : void loadReference();
109 :
110 : struct pairordering {
111 : bool operator ()(std::pair<int, double> const& a, std::pair<int, double> const& b) {
112 0 : return (a).second < (b).second;
113 : }
114 : };
115 :
116 : public:
117 : explicit FuncPathGeneral(const ActionOptions&);
118 : // Active methods:
119 : virtual void calculate();
120 : virtual void prepare();
121 : static void registerKeywords(Keywords& keys);
122 : };
123 :
124 10421 : PLUMED_REGISTER_ACTION(FuncPathGeneral, "FUNCPATHGENERAL")
125 :
126 1 : void FuncPathGeneral::loadReference() {
127 1 : IFile input;
128 1 : input.open(reference);
129 1 : if (!input)
130 0 : plumed_merror("Could not open the reference file!");
131 18 : while (input)
132 : {
133 : std::vector<std::string> strings;
134 17 : Tools::getParsedLine(input, strings);
135 17 : if (strings.empty())
136 : continue;
137 : std::vector<double> colvarLine;
138 : double value;
139 16 : int max = columns.empty() ? strings.size() : columns.size();
140 128 : for (int i = 0; i < max; ++i)
141 : {
142 112 : int col = columns.empty() ? i : columns[i];
143 : // If no columns have been entered, ignore the first (time) and take the rest
144 112 : if (columns.empty() && i == 0)
145 16 : continue;
146 :
147 96 : Tools::convert(strings[col], value);
148 96 : colvarLine.push_back(value);
149 : }
150 16 : path_cv_values.push_back(colvarLine);
151 17 : }
152 1 : }
153 :
154 2 : void FuncPathGeneral::registerKeywords(Keywords& keys) {
155 2 : Function::registerKeywords(keys);
156 2 : keys.use("ARG");
157 4 : keys.add("compulsory", "LAMBDA", "Lambda parameter required for smoothing");
158 4 : keys.add("compulsory", "COEFFICIENTS", "Coefficients to be assigned to the CVs");
159 4 : keys.add("compulsory", "REFERENCE", "Colvar file needed to provide the CV milestones");
160 4 : keys.add("optional", "COLUMNS", "List of columns in the reference colvar file specifying the CVs");
161 4 : keys.add("optional", "NEIGH_SIZE", "Size of the neighbor list");
162 4 : keys.add("optional", "NEIGH_STRIDE", "How often the neighbor list needs to be calculated in time units");
163 2 : componentsAreNotOptional(keys);
164 4 : keys.addOutputComponent("s", "default", "Position on the path");
165 4 : keys.addOutputComponent("z", "default", "Distance from the path");
166 2 : }
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 : }
|