1 // -*- c++ -*-
2 
3 // This file is part of the Collective Variables module (Colvars).
4 // The original version of Colvars and its updates are located at:
5 // https://github.com/Colvars/colvars
6 // Please update all Colvars source files before making any changes.
7 // If you wish to distribute your changes, please submit them to the
8 // Colvars repository at GitHub.
9 
10 #include <algorithm>
11 
12 #include "colvarmodule.h"
13 #include "colvarvalue.h"
14 #include "colvar.h"
15 #include "colvarcomp.h"
16 
17 
18 
cvc()19 colvar::cvc::cvc()
20 {
21   description = "uninitialized colvar component";
22   b_try_scalable = true;
23   sup_coeff = 1.0;
24   sup_np = 1;
25   period = 0.0;
26   wrap_center = 0.0;
27   width = 0.0;
28   init_dependencies();
29 }
30 
31 
cvc(std::string const & conf)32 colvar::cvc::cvc(std::string const &conf)
33 {
34   description = "uninitialized colvar component";
35   b_try_scalable = true;
36   sup_coeff = 1.0;
37   sup_np = 1;
38   period = 0.0;
39   wrap_center = 0.0;
40   width = 0.0;
41   init_dependencies();
42   init(conf);
43 }
44 
45 
init(std::string const & conf)46 int colvar::cvc::init(std::string const &conf)
47 {
48   if (cvm::debug())
49     cvm::log("Initializing cvc base object.\n");
50 
51   std::string const old_name(name);
52 
53   if (name.size() > 0) {
54     cvm::log("Updating configuration for component \""+name+"\"\n");
55   }
56 
57   if (get_keyval(conf, "name", name, name)) {
58     if (name.size() > 0) {
59       description = "cvc \"" + name + "\" of type " + function_type;
60     } else {
61       description = "unnamed cvc";
62     }
63     if ((name != old_name) && (old_name.size() > 0)) {
64       cvm::error("Error: cannot rename component \""+old_name+
65                  "\" after initialization (new name = \""+name+"\")",
66                  INPUT_ERROR);
67       name = old_name;
68     }
69   }
70 
71   get_keyval(conf, "componentCoeff", sup_coeff, sup_coeff);
72   get_keyval(conf, "componentExp", sup_np, sup_np);
73   // TODO these could be condensed into get_keyval()
74   register_param("componentCoeff", reinterpret_cast<void *>(&sup_coeff));
75   register_param("componentExp", reinterpret_cast<void *>(&sup_np));
76 
77   get_keyval(conf, "period", period, period);
78   get_keyval(conf, "wrapAround", wrap_center, wrap_center);
79   // TODO when init() is called after all constructors, check periodic flag
80   register_param("period", reinterpret_cast<void *>(&period));
81   register_param("wrapAround", reinterpret_cast<void *>(&wrap_center));
82 
83   get_keyval_feature(this, conf, "debugGradients",
84                      f_cvc_debug_gradient, false, parse_silent);
85 
86   bool b_no_PBC = !is_enabled(f_cvc_pbc_minimum_image); // Enabled by default
87   get_keyval(conf, "forceNoPBC", b_no_PBC, b_no_PBC);
88   if (b_no_PBC) {
89     disable(f_cvc_pbc_minimum_image);
90   } else {
91     enable(f_cvc_pbc_minimum_image);
92   }
93 
94   // Attempt scalable calculations when in parallel? (By default yes, if available)
95   get_keyval(conf, "scalable", b_try_scalable, b_try_scalable);
96 
97   if (cvm::debug())
98     cvm::log("Done initializing cvc base object.\n");
99 
100   return cvm::get_error();
101 }
102 
103 
init_total_force_params(std::string const & conf)104 int colvar::cvc::init_total_force_params(std::string const &conf)
105 {
106   if (cvm::get_error()) return COLVARS_ERROR;
107 
108   if (get_keyval_feature(this, conf, "oneSiteSystemForce",
109                          f_cvc_one_site_total_force, is_enabled(f_cvc_one_site_total_force))) {
110     cvm::log("Warning: keyword \"oneSiteSystemForce\" is deprecated: "
111              "please use \"oneSiteTotalForce\" instead.\n");
112   }
113   if (get_keyval_feature(this, conf, "oneSiteTotalForce",
114                          f_cvc_one_site_total_force, is_enabled(f_cvc_one_site_total_force))) {
115     cvm::log("Computing total force on group 1 only\n");
116   }
117 
118   if (! is_enabled(f_cvc_one_site_total_force)) {
119     // check whether any of the other atom groups is dummy
120     std::vector<cvm::atom_group *>::iterator agi = atom_groups.begin();
121     agi++;
122     for ( ; agi != atom_groups.end(); agi++) {
123       if ((*agi)->b_dummy) {
124         provide(f_cvc_inv_gradient, false);
125         provide(f_cvc_Jacobian, false);
126       }
127     }
128   }
129 
130   return COLVARS_OK;
131 }
132 
133 
parse_group(std::string const & conf,char const * group_key,bool optional)134 cvm::atom_group *colvar::cvc::parse_group(std::string const &conf,
135                                           char const *group_key,
136                                           bool optional)
137 {
138   cvm::atom_group *group = NULL;
139   std::string group_conf;
140 
141   if (key_lookup(conf, group_key, &group_conf)) {
142     group = new cvm::atom_group(group_key);
143 
144     if (b_try_scalable) {
145       if (is_available(f_cvc_scalable_com)
146           && is_enabled(f_cvc_com_based)
147           && !is_enabled(f_cvc_debug_gradient)) {
148         enable(f_cvc_scalable_com);
149         enable(f_cvc_scalable);
150         // The CVC makes the feature available;
151         // the atom group will enable it unless it needs to compute a rotational fit
152         group->provide(f_ag_scalable_com);
153       }
154 
155       // TODO check for other types of parallelism here
156     }
157 
158     if (group_conf.size() == 0) {
159       cvm::error("Error: atom group \""+group->key+
160                  "\" is set, but has no definition.\n",
161                  INPUT_ERROR);
162       return group;
163     }
164 
165     cvm::increase_depth();
166     if (group->parse(group_conf) == COLVARS_OK) {
167       register_atom_group(group);
168     }
169     group->check_keywords(group_conf, group_key);
170     if (cvm::get_error()) {
171       cvm::error("Error parsing definition for atom group \""+
172                  std::string(group_key)+"\".", INPUT_ERROR);
173     }
174     cvm::decrease_depth();
175 
176   } else {
177     if (! optional) {
178       cvm::error("Error: definition for atom group \""+
179                  std::string(group_key)+"\" not found.\n");
180     }
181   }
182 
183   return group;
184 }
185 
186 
init_dependencies()187 int colvar::cvc::init_dependencies() {
188   size_t i;
189   // Initialize static array once and for all
190   if (features().size() == 0) {
191     for (i = 0; i < colvardeps::f_cvc_ntot; i++) {
192       modify_features().push_back(new feature);
193     }
194 
195     init_feature(f_cvc_active, "active", f_type_dynamic);
196 //     The dependency below may become useful if we use dynamic atom groups
197 //     require_feature_children(f_cvc_active, f_ag_active);
198 
199     init_feature(f_cvc_scalar, "scalar", f_type_static);
200 
201     init_feature(f_cvc_periodic, "periodic", f_type_static);
202 
203     init_feature(f_cvc_width, "defined_width", f_type_static);
204 
205     init_feature(f_cvc_lower_boundary, "defined_lower_boundary", f_type_static);
206 
207     init_feature(f_cvc_upper_boundary, "defined_upper_boundary", f_type_static);
208 
209     init_feature(f_cvc_gradient, "gradient", f_type_dynamic);
210 
211     init_feature(f_cvc_explicit_gradient, "explicit_gradient", f_type_static);
212     require_feature_children(f_cvc_explicit_gradient, f_ag_explicit_gradient);
213 
214     init_feature(f_cvc_inv_gradient, "inverse_gradient", f_type_dynamic);
215     require_feature_self(f_cvc_inv_gradient, f_cvc_gradient);
216 
217     init_feature(f_cvc_debug_gradient, "debug_gradient", f_type_user);
218     require_feature_self(f_cvc_debug_gradient, f_cvc_gradient);
219     require_feature_self(f_cvc_debug_gradient, f_cvc_explicit_gradient);
220 
221     init_feature(f_cvc_Jacobian, "Jacobian_derivative", f_type_dynamic);
222     require_feature_self(f_cvc_Jacobian, f_cvc_inv_gradient);
223 
224     // Compute total force on first site only to avoid unwanted
225     // coupling to other colvars (see e.g. Ciccotti et al., 2005)
226     init_feature(f_cvc_one_site_total_force, "total_force_from_one_group", f_type_user);
227     require_feature_self(f_cvc_one_site_total_force, f_cvc_com_based);
228 
229     init_feature(f_cvc_com_based, "function_of_centers_of_mass", f_type_static);
230 
231     init_feature(f_cvc_pbc_minimum_image, "use_minimum-image_with_PBCs", f_type_user);
232 
233     init_feature(f_cvc_scalable, "scalable_calculation", f_type_static);
234     require_feature_self(f_cvc_scalable, f_cvc_scalable_com);
235 
236     init_feature(f_cvc_scalable_com, "scalable_calculation_of_centers_of_mass", f_type_static);
237     require_feature_self(f_cvc_scalable_com, f_cvc_com_based);
238 
239 
240     // TODO only enable this when f_ag_scalable can be turned on for a pre-initialized group
241     // require_feature_children(f_cvc_scalable, f_ag_scalable);
242     // require_feature_children(f_cvc_scalable_com, f_ag_scalable_com);
243 
244     // check that everything is initialized
245     for (i = 0; i < colvardeps::f_cvc_ntot; i++) {
246       if (is_not_set(i)) {
247         cvm::error("Uninitialized feature " + cvm::to_str(i) + " in " + description);
248       }
249     }
250   }
251 
252   // Initialize feature_states for each instance
253   // default as available, not enabled
254   // except dynamic features which default as unavailable
255   feature_states.reserve(f_cvc_ntot);
256   for (i = 0; i < colvardeps::f_cvc_ntot; i++) {
257     bool avail = is_dynamic(i) ? false : true;
258     feature_states.push_back(feature_state(avail, false));
259   }
260 
261   // Features that are implemented by all cvcs by default
262   // Each cvc specifies what other features are available
263   feature_states[f_cvc_active].available = true;
264   feature_states[f_cvc_gradient].available = true;
265 
266   // CVCs are enabled from the start - get disabled based on flags
267   enable(f_cvc_active);
268   // feature_states[f_cvc_active].enabled = true;
269 
270   // Explicit gradients are implemented in mosts CVCs. Exceptions must be specified explicitly.
271   // feature_states[f_cvc_explicit_gradient].enabled = true;
272   enable(f_cvc_explicit_gradient);
273 
274   // Use minimum-image distances by default
275   // feature_states[f_cvc_pbc_minimum_image].enabled = true;
276   enable(f_cvc_pbc_minimum_image);
277 
278   // Features that are implemented by default if their requirements are
279   feature_states[f_cvc_one_site_total_force].available = true;
280 
281   // Features That are implemented only for certain simulation engine configurations
282   feature_states[f_cvc_scalable_com].available = (cvm::proxy->scalable_group_coms() == COLVARS_OK);
283   feature_states[f_cvc_scalable].available = feature_states[f_cvc_scalable_com].available;
284 
285   return COLVARS_OK;
286 }
287 
288 
setup()289 int colvar::cvc::setup()
290 {
291   description = "cvc " + name;
292   return COLVARS_OK;
293 }
294 
295 
~cvc()296 colvar::cvc::~cvc()
297 {
298   free_children_deps();
299   remove_all_children();
300   for (size_t i = 0; i < atom_groups.size(); i++) {
301     if (atom_groups[i] != NULL) delete atom_groups[i];
302   }
303 }
304 
305 
init_as_distance()306 void colvar::cvc::init_as_distance()
307 {
308   x.type(colvarvalue::type_scalar);
309   enable(f_cvc_lower_boundary);
310   lower_boundary.type(colvarvalue::type_scalar);
311   lower_boundary.real_value = 0.0;
312   register_param("lowerBoundary", reinterpret_cast<void *>(&lower_boundary));
313 }
314 
315 
init_as_angle()316 void colvar::cvc::init_as_angle()
317 {
318   x.type(colvarvalue::type_scalar);
319   init_scalar_boundaries(0.0, 180);
320 }
321 
322 
init_scalar_boundaries(cvm::real lb,cvm::real ub)323 void colvar::cvc::init_scalar_boundaries(cvm::real lb, cvm::real ub)
324 {
325   enable(f_cvc_lower_boundary);
326   lower_boundary.type(colvarvalue::type_scalar);
327   lower_boundary.real_value = lb;
328   enable(f_cvc_upper_boundary);
329   upper_boundary.type(colvarvalue::type_scalar);
330   upper_boundary.real_value = ub;
331   register_param("lowerBoundary", reinterpret_cast<void *>(&lower_boundary));
332   register_param("upperBoundary", reinterpret_cast<void *>(&upper_boundary));
333 }
334 
335 
register_atom_group(cvm::atom_group * ag)336 void colvar::cvc::register_atom_group(cvm::atom_group *ag)
337 {
338   atom_groups.push_back(ag);
339   add_child(ag);
340 }
341 
342 
get_param_grad(std::string const & param_name)343 colvarvalue const *colvar::cvc::get_param_grad(std::string const &param_name)
344 {
345   colvarvalue const *ptr =
346     reinterpret_cast<colvarvalue const *>(get_param_grad_ptr(param_name));
347   return ptr != NULL ? ptr : NULL;
348 }
349 
350 
set_param(std::string const & param_name,void const * new_value)351 int colvar::cvc::set_param(std::string const &param_name,
352                            void const *new_value)
353 {
354   if (param_map.count(param_name) > 0) {
355 
356     // TODO When we can use C++11, make this a proper function map
357     if (param_name.compare("componentCoeff") == 0) {
358       sup_coeff = *(reinterpret_cast<cvm::real const *>(new_value));
359     }
360     if (param_name.compare("componentExp") == 0) {
361       sup_np = *(reinterpret_cast<int const *>(new_value));
362     }
363     if (is_enabled(f_cvc_periodic)) {
364       if (param_name.compare("period") == 0) {
365         period = *(reinterpret_cast<cvm::real const *>(new_value));
366       }
367       if (param_name.compare("wrapAround") == 0) {
368         wrap_center = *(reinterpret_cast<cvm::real const *>(new_value));
369       }
370     }
371   }
372 
373   return colvarparams::set_param(param_name, new_value);
374 }
375 
376 
read_data()377 void colvar::cvc::read_data()
378 {
379   size_t ig;
380   for (ig = 0; ig < atom_groups.size(); ig++) {
381     cvm::atom_group &atoms = *(atom_groups[ig]);
382     atoms.reset_atoms_data();
383     atoms.read_positions();
384     atoms.calc_required_properties();
385     // each atom group will take care of its own fitting_group, if defined
386   }
387 
388 ////  Don't try to get atom velocities, as no back-end currently implements it
389 //   if (tasks[task_output_velocity] && !tasks[task_fdiff_velocity]) {
390 //     for (i = 0; i < cvcs.size(); i++) {
391 //       for (ig = 0; ig < cvcs[i]->atom_groups.size(); ig++) {
392 //         cvcs[i]->atom_groups[ig]->read_velocities();
393 //       }
394 //     }
395 //   }
396 }
397 
398 
get_atom_lists()399 std::vector<std::vector<int> > colvar::cvc::get_atom_lists()
400 {
401   std::vector<std::vector<int> > lists;
402 
403   std::vector<cvm::atom_group *>::iterator agi = atom_groups.begin();
404   for ( ; agi != atom_groups.end(); ++agi) {
405     (*agi)->create_sorted_ids();
406     lists.push_back((*agi)->sorted_ids());
407     if ((*agi)->is_enabled(f_ag_fitting_group) && (*agi)->is_enabled(f_ag_fit_gradients)) {
408       cvm::atom_group &fg = *((*agi)->fitting_group);
409       fg.create_sorted_ids();
410       lists.push_back(fg.sorted_ids());
411     }
412   }
413   return lists;
414 }
415 
416 
collect_gradients(std::vector<int> const & atom_ids,std::vector<cvm::rvector> & atomic_gradients)417 void colvar::cvc::collect_gradients(std::vector<int> const &atom_ids, std::vector<cvm::rvector> &atomic_gradients)
418 {
419   // Coefficient: d(a * x^n) = a * n * x^(n-1) * dx
420   cvm::real coeff = sup_coeff * cvm::real(sup_np) *
421     cvm::integer_power(value().real_value, sup_np-1);
422 
423   for (size_t j = 0; j < atom_groups.size(); j++) {
424 
425     cvm::atom_group &ag = *(atom_groups[j]);
426 
427     // If necessary, apply inverse rotation to get atomic
428     // gradient in the laboratory frame
429     if (ag.is_enabled(f_ag_rotate)) {
430       cvm::rotation const rot_inv = ag.rot.inverse();
431 
432       for (size_t k = 0; k < ag.size(); k++) {
433         size_t a = std::lower_bound(atom_ids.begin(), atom_ids.end(),
434                                     ag[k].id) - atom_ids.begin();
435         atomic_gradients[a] += coeff * rot_inv.rotate(ag[k].grad);
436       }
437 
438     } else {
439 
440       for (size_t k = 0; k < ag.size(); k++) {
441         size_t a = std::lower_bound(atom_ids.begin(), atom_ids.end(),
442                                     ag[k].id) - atom_ids.begin();
443         atomic_gradients[a] += coeff * ag[k].grad;
444       }
445     }
446     if (ag.is_enabled(f_ag_fitting_group) && ag.is_enabled(f_ag_fit_gradients)) {
447       cvm::atom_group const &fg = *(ag.fitting_group);
448       for (size_t k = 0; k < fg.size(); k++) {
449         size_t a = std::lower_bound(atom_ids.begin(), atom_ids.end(),
450                                     fg[k].id) - atom_ids.begin();
451         // fit gradients are in the unrotated (simulation) frame
452         atomic_gradients[a] += coeff * fg.fit_gradients[k];
453       }
454     }
455   }
456 }
457 
458 
calc_force_invgrads()459 void colvar::cvc::calc_force_invgrads()
460 {
461   cvm::error("Error: calculation of inverse gradients is not implemented "
462              "for colvar components of type \""+function_type+"\".\n",
463              COLVARS_NOT_IMPLEMENTED);
464 }
465 
466 
calc_Jacobian_derivative()467 void colvar::cvc::calc_Jacobian_derivative()
468 {
469   cvm::error("Error: calculation of inverse gradients is not implemented "
470              "for colvar components of type \""+function_type+"\".\n",
471              COLVARS_NOT_IMPLEMENTED);
472 }
473 
474 
calc_fit_gradients()475 void colvar::cvc::calc_fit_gradients()
476 {
477   for (size_t ig = 0; ig < atom_groups.size(); ig++) {
478     atom_groups[ig]->calc_fit_gradients();
479   }
480 }
481 
482 
debug_gradients()483 void colvar::cvc::debug_gradients()
484 {
485   // this function should work for any scalar cvc:
486   // the only difference will be the name of the atom group (here, "group")
487   // NOTE: this assumes that groups for this cvc are non-overlapping,
488   // since atom coordinates are modified only within the current group
489 
490   cvm::log("Debugging gradients for " + description);
491 
492   for (size_t ig = 0; ig < atom_groups.size(); ig++) {
493     cvm::atom_group *group = atom_groups[ig];
494     if (group->b_dummy) continue;
495 
496     cvm::rotation const rot_0 = group->rot;
497     cvm::rotation const rot_inv = group->rot.inverse();
498 
499     cvm::real x_0 = x.real_value;
500     if ((x.type() == colvarvalue::type_vector) && (x.size() == 1)) x_0 = x[0];
501 
502     // cvm::log("gradients     = "+cvm::to_str (gradients)+"\n");
503 
504     cvm::atom_group *group_for_fit = group->fitting_group ? group->fitting_group : group;
505     cvm::atom_pos fit_gradient_sum, gradient_sum;
506 
507     // print the values of the fit gradients
508     if (group->is_enabled(f_ag_center) || group->is_enabled(f_ag_rotate)) {
509       if (group->is_enabled(f_ag_fit_gradients)) {
510         size_t j;
511 
512         // fit_gradients are in the simulation frame: we should print them in the rotated frame
513         cvm::log("Fit gradients:\n");
514         for (j = 0; j < group_for_fit->fit_gradients.size(); j++) {
515           cvm::log((group->fitting_group ? std::string("refPosGroup") : group->key) +
516                   "[" + cvm::to_str(j) + "] = " +
517                   (group->is_enabled(f_ag_rotate) ?
518                     cvm::to_str(rot_0.rotate(group_for_fit->fit_gradients[j])) :
519                     cvm::to_str(group_for_fit->fit_gradients[j])));
520         }
521       }
522     }
523 
524     // debug the gradients
525     for (size_t ia = 0; ia < group->size(); ia++) {
526 
527       // tests are best conducted in the unrotated (simulation) frame
528       cvm::rvector const atom_grad = (group->is_enabled(f_ag_rotate) ?
529                                       rot_inv.rotate((*group)[ia].grad) :
530                                       (*group)[ia].grad);
531       gradient_sum += atom_grad;
532 
533       for (size_t id = 0; id < 3; id++) {
534         // (re)read original positions
535         group->read_positions();
536         // change one coordinate
537         (*group)[ia].pos[id] += cvm::debug_gradients_step_size;
538         group->calc_required_properties();
539         calc_value();
540         cvm::real x_1 = x.real_value;
541         if ((x.type() == colvarvalue::type_vector) && (x.size() == 1)) x_1 = x[0];
542         cvm::log("Atom "+cvm::to_str(ia)+", component "+cvm::to_str(id)+":\n");
543         cvm::log("dx(actual) = "+cvm::to_str(x_1 - x_0,
544                               21, 14)+"\n");
545         cvm::real const dx_pred = (group->fit_gradients.size()) ?
546           (cvm::debug_gradients_step_size * (atom_grad[id] + group->fit_gradients[ia][id])) :
547           (cvm::debug_gradients_step_size * atom_grad[id]);
548         cvm::log("dx(interp) = "+cvm::to_str(dx_pred,
549                               21, 14)+"\n");
550         cvm::log("|dx(actual) - dx(interp)|/|dx(actual)| = "+
551                   cvm::to_str(cvm::fabs(x_1 - x_0 - dx_pred) /
552                               cvm::fabs(x_1 - x_0), 12, 5)+"\n");
553       }
554     }
555 
556     if ((group->is_enabled(f_ag_fit_gradients)) && (group->fitting_group != NULL)) {
557       cvm::atom_group *ref_group = group->fitting_group;
558       group->read_positions();
559       group->calc_required_properties();
560 
561       for (size_t ia = 0; ia < ref_group->size(); ia++) {
562 
563         // fit gradients are in the unrotated (simulation) frame
564         cvm::rvector const atom_grad = ref_group->fit_gradients[ia];
565         fit_gradient_sum += atom_grad;
566 
567         for (size_t id = 0; id < 3; id++) {
568           // (re)read original positions
569           group->read_positions();
570           ref_group->read_positions();
571           // change one coordinate
572           (*ref_group)[ia].pos[id] += cvm::debug_gradients_step_size;
573           group->calc_required_properties();
574           calc_value();
575 
576           cvm::real const x_1 = x.real_value;
577           cvm::log("refPosGroup atom "+cvm::to_str(ia)+", component "+cvm::to_str (id)+":\n");
578           cvm::log("dx(actual) = "+cvm::to_str (x_1 - x_0,
579                                 21, 14)+"\n");
580 
581           cvm::real const dx_pred = cvm::debug_gradients_step_size * atom_grad[id];
582 
583           cvm::log("dx(interp) = "+cvm::to_str (dx_pred,
584                                 21, 14)+"\n");
585           cvm::log ("|dx(actual) - dx(interp)|/|dx(actual)| = "+
586                     cvm::to_str(cvm::fabs (x_1 - x_0 - dx_pred) /
587                                 cvm::fabs (x_1 - x_0),
588                                 12, 5)+
589                     ".\n");
590         }
591       }
592     }
593 
594     cvm::log("Gradient sum: " +  cvm::to_str(gradient_sum) +
595           "  Fit gradient sum: " + cvm::to_str(fit_gradient_sum) +
596           "  Total " + cvm::to_str(gradient_sum + fit_gradient_sum));
597   }
598   return;
599 }
600 
601 
dist2(colvarvalue const & x1,colvarvalue const & x2) const602 cvm::real colvar::cvc::dist2(colvarvalue const &x1,
603                              colvarvalue const &x2) const
604 {
605   return x1.dist2(x2);
606 }
607 
608 
dist2_lgrad(colvarvalue const & x1,colvarvalue const & x2) const609 colvarvalue colvar::cvc::dist2_lgrad(colvarvalue const &x1,
610                                      colvarvalue const &x2) const
611 {
612   return x1.dist2_grad(x2);
613 }
614 
615 
dist2_rgrad(colvarvalue const & x1,colvarvalue const & x2) const616 colvarvalue colvar::cvc::dist2_rgrad(colvarvalue const &x1,
617                                      colvarvalue const &x2) const
618 {
619   return x2.dist2_grad(x1);
620 }
621 
622 
wrap(colvarvalue &) const623 void colvar::cvc::wrap(colvarvalue & /* x_unwrapped */) const
624 {
625   return;
626 }
627 
628 
629 
630 // Static members
631 
632 std::vector<colvardeps::feature *> colvar::cvc::cvc_features;
633