NAMD
colvarproxy_namd.C
Go to the documentation of this file.
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 <errno.h>
11 
12 #include "common.h"
13 #include "fstream_namd.h"
14 #include "Debug.h"
15 #include "BackEnd.h"
16 #include "InfoStream.h"
17 #include "Node.h"
18 #include "Molecule.h"
19 #include "GridForceGrid.h"
20 #include "GridForceGrid.inl"
21 #include "PDB.h"
22 #include "PDBData.h"
23 #include "ReductionMgr.h"
24 #include "ScriptTcl.h"
25 #include "NamdState.h"
26 #include "Controller.h"
27 #include "PatchData.h"
28 
29 #ifdef NAMD_TCL
30 #include <tcl.h>
31 #endif
32 
33 // For replica exchange
34 #include "converse.h"
35 #include "DataExchanger.h"
36 
37 #include "colvarmodule.h"
38 #include "colvar.h"
39 #include "colvarbias.h"
40 #include "colvaratoms.h"
41 #include "colvarproxy.h"
42 #include "colvarproxy_namd.h"
43 #include "colvarscript.h"
44 
45 
47 {
48  engine_name_ = "NAMD";
49 #if CMK_SMP && USE_CKLOOP
50  charm_lock_state = CmiCreateLock();
51 #endif
52 
53  version_int = get_version_from_string(COLVARPROXY_VERSION);
54 #if CMK_TRACE_ENABLED
55  if ( 0 == CkMyPe() ) {
56  traceRegisterUserEvent("GM COLVAR item", GLOBAL_MASTER_CKLOOP_CALC_ITEM);
57  traceRegisterUserEvent("GM COLVAR bias", GLOBAL_MASTER_CKLOOP_CALC_BIASES );
58  traceRegisterUserEvent("GM COLVAR scripted bias", GLOBAL_MASTER_CKLOOP_CALC_SCRIPTED_BIASES );
59  }
60 #endif
61  first_timestep = true;
62  requestTotalForce(total_force_requested);
63 
64  boltzmann_ = 0.001987191;
65 
66  angstrom_value_ = 1.;
67 
68  // initialize pointers to NAMD configuration data
70 
71  if (cvm::debug())
72  iout << "Info: initializing the colvars proxy object.\n" << endi;
73 
74  // find the configuration file, if provided
75  StringList *config = Node::Object()->configList->find("colvarsConfig");
76 
77  // find the input state file
78  StringList *input_restart = Node::Object()->configList->find("colvarsInput");
79  colvarproxy_io::set_input_prefix(input_restart ? input_restart->data : "");
80 
82  set_integration_timestep(simparams->dt);
83 
85 
86  // both fields are taken from data structures already available
87  updated_masses_ = updated_charges_ = true;
88 
89  // Take the output prefixes from the NAMD input
90  colvarproxy_io::set_output_prefix(std::string(simparams->outputFilename));
91  colvarproxy_io::set_restart_output_prefix(std::string(simparams->restartFilename));
92  colvarproxy_io::set_default_restart_frequency(simparams->restartFrequency);
93 
94  if (simparams->accelMDOn) {
95  accelMDOn = true;
96  } else {
97  accelMDOn = false;
98  }
99  amd_weight_factor = 1.0;
100 
101  // check if it is possible to save output configuration
102  if ((!output_prefix_str.size()) && (!restart_output_prefix_str.size())) {
103  error("Error: neither the final output state file or "
104  "the output restart file could be defined, exiting.\n");
105  }
106 
107  init_atoms_map();
108 
109  // initialize module: this object will be the communication proxy
110  colvars = new colvarmodule(this);
111 
112  cvm::log("Using NAMD interface, version "+
113  cvm::to_str(COLVARPROXY_VERSION)+".\n");
114  colvars->cite_feature("NAMD engine");
115  colvars->cite_feature("Colvars-NAMD interface");
116 
117  errno = 0;
118  for ( ; config; config = config->next ) {
119  add_config("configfile", config->data);
120  }
121 
122  // Trigger immediate initialization of the module
123  colvarproxy::parse_module_config();
125  colvars->update_engine_parameters();
126  colvars->setup_input();
127  colvars->setup_output();
128 
129  // save to Node for Tcl script access
130  Node::Object()->colvars = colvars;
131 
132 #ifdef NAMD_TCL
133  have_scripts = true;
134 #else
135  have_scripts = false;
136 #endif
137 
138  if (simparams->firstTimestep != 0) {
139  colvars->set_initial_step(static_cast<cvm::step_number>(simparams->firstTimestep));
140  }
141 
142 #if !defined (NAMD_UNIFIED_REDUCTION)
144 #endif
145 
146  #if defined(NODEGROUP_FORCE_REGISTER) && !defined(NAMD_UNIFIED_REDUCTION)
147  CProxy_PatchData cpdata(CkpvAccess(BOCclass_group).patchData);
148  PatchData *patchData = cpdata.ckLocalBranch();
149  nodeReduction = patchData->reduction;
150  #endif
151 
152  if (cvm::debug())
153  iout << "Info: done initializing the colvars proxy object.\n" << endi;
154 }
155 
156 
158 {
159 #if CMK_SMP && USE_CKLOOP
160  CmiDestroyLock(charm_lock_state);
161 #endif
162 #if !defined (NAMD_UNIFIED_REDUCTION)
163  delete reduction;
164 #endif
165 }
166 
167 
169 {
170  int error_code = COLVARS_OK;
171  if (simparams->rescaleFreq > 0) {
172  error_code |= set_target_temperature(simparams->rescaleTemp);
173  } else if (simparams->reassignFreq > 0) {
174  error_code |= set_target_temperature(simparams->reassignTemp);
175  } else if (simparams->langevinOn) {
176  error_code |= set_target_temperature(simparams->langevinTemp);
177  } else if (simparams->tCoupleOn) {
178  error_code |= set_target_temperature(simparams->tCoupleTemp);
179  } else if (simparams->loweAndersenOn) {
180  error_code |= set_target_temperature(simparams->loweAndersenTemp);
181  } else if (simparams->stochRescaleOn) {
182  error_code |= set_target_temperature(simparams->stochRescaleTemp);
183  } else {
184  error_code |= set_target_temperature(0.0);
185  }
186  return error_code;
187 }
188 
189 
190 
192 {
193  size_t const n_all_atoms = Node::Object()->molecule->numAtoms;
194  atoms_map.assign(n_all_atoms, -1);
195 }
196 
197 
200 {
201  if (atoms_map.size() != Node::Object()->molecule->numAtoms) {
202  init_atoms_map();
203  }
204 
205  for (AtomIDList::const_iterator a_i = begin; a_i != end; a_i++) {
206 
207  if (atoms_map[*a_i] >= 0) continue;
208 
209  for (size_t i = 0; i < atoms_ids.size(); i++) {
210  if (atoms_ids[i] == *a_i) {
211  atoms_map[*a_i] = i;
212  break;
213  }
214  }
215 
216  if (atoms_map[*a_i] < 0) {
217  // this atom is probably managed by another GlobalMaster:
218  // add it here anyway to avoid having to test for array boundaries at each step
219  int const index = add_atom_slot(*a_i);
220  atoms_map[*a_i] = index;
221  modifyRequestedAtoms().add(*a_i);
222  update_atom_properties(index);
223  }
224  }
225 
226  if (cvm::debug()) {
227  cvm::log("atoms_map = "+cvm::to_str(atoms_map)+".\n");
228  }
229 
230  return COLVARS_OK;
231 }
232 
233 
235 {
236  int error_code = colvarproxy::setup();
237 
238  if (colvars->size() == 0) {
239  // Module is empty, nothing to do
240  return COLVARS_OK;
241  }
242 
243  log("Updating NAMD interface:\n");
244 
245  errno = 0;
246 
247  if (simparams->wrapAll) {
248  log("Warning: enabling wrapAll can lead to inconsistent results "
249  "for Colvars calculations: please disable wrapAll, "
250  "as is the default option in NAMD.\n");
251  }
252 
253  log("updating atomic data ("+cvm::to_str(atoms_ids.size())+" atoms).\n");
254 
255  size_t i;
256  for (i = 0; i < atoms_ids.size(); i++) {
258 
259  // zero out mutable arrays
260  atoms_positions[i] = cvm::rvector(0.0, 0.0, 0.0);
261  atoms_total_forces[i] = cvm::rvector(0.0, 0.0, 0.0);
262  atoms_new_colvar_forces[i] = cvm::rvector(0.0, 0.0, 0.0);
263  }
264 
265  size_t n_group_atoms = 0;
266  for (int ig = 0; ig < modifyRequestedGroups().size(); ig++) {
267  n_group_atoms += modifyRequestedGroups()[ig].size();
268  }
269 
270  log("updating group data ("+cvm::to_str(atom_groups_ids.size())+
271  " scalable groups, "+
272  cvm::to_str(n_group_atoms)+" atoms in total).\n");
273 
274  // Note: groupMassBegin, groupMassEnd may be used here, but they won't work for charges
275  for (int ig = 0; ig < modifyRequestedGroups().size(); ig++) {
276 
277  // update mass and charge
279 
280  atom_groups_coms[ig] = cvm::rvector(0.0, 0.0, 0.0);
281  atom_groups_total_forces[ig] = cvm::rvector(0.0, 0.0, 0.0);
282  atom_groups_new_colvar_forces[ig] = cvm::rvector(0.0, 0.0, 0.0);
283  }
284 
285 #if NAMD_VERSION_NUMBER >= 34471681
286  log("updating grid object data ("+cvm::to_str(volmaps_ids.size())+
287  " grid objects in total).\n");
288  for (int imap = 0; imap < modifyGridObjForces().size(); imap++) {
289  volmaps_new_colvar_forces[imap] = 0.0;
290  }
291 #endif
292 
293  size_t const new_features_hash =
294  std::hash<std::string>{}(colvars->feature_report(0));
295  if (new_features_hash != features_hash) {
296  // Nag only once, there may be many run commands
297  log(std::string("\n")+colvars->feature_report(0)+std::string("\n"));
298  features_hash = new_features_hash;
299  }
300 
302  log("updating target temperature (T = "+
303  cvm::to_str(target_temperature())+" K).\n");
304 
305  // Note: not needed currently, but may be in the future if NAMD allows
306  // redefining the timestep
307  set_integration_timestep(simparams->dt);
308 
309  return error_code;
310 }
311 
312 
314 {
315  if (cvm::debug()) {
316  cvm::log("colvarproxy_namd::reset()\n");
317  }
318 
319  int error_code = COLVARS_OK;
320 
321  // Unrequest all positions, total forces, etc from NAMD
325 
326  modifyRequestedGroups().clear();
328 
329 #if NAMD_VERSION_NUMBER >= 34471681
332 #endif
333 
334  requestTotalForce(false);
335 
336  atoms_map.clear();
337 
338  // Clear internal atomic data
339  error_code |= colvarproxy::reset();
340 
341  return error_code;
342 }
343 
344 
346 {
347  errno = 0;
348 
349  if (first_timestep) {
350 
351  // First run after the proxy is constructed
352 
354  colvars->update_engine_parameters();
355  colvars->setup_input();
356  colvars->setup_output();
357  // Controller is only available after full startup phase, so now
359 
360  first_timestep = false;
361 
362  } else {
363 
364  // Use the time step number inherited from GlobalMaster
365  if ( step - previous_NAMD_step == 1 ) {
366  colvars->it++;
367  b_simulation_continuing = false;
368  } else {
369 
370  // Cases covered by this condition:
371  // - run 0
372  // - beginning of a new run statement
373  // The internal counter is not incremented, and the objects are made
374  // aware of this via the following flag
375  b_simulation_continuing = true;
376 
377  // Update NAMD output and restart prefixes
378  colvarproxy_io::set_output_prefix(std::string(simparams->outputFilename));
379  colvarproxy_io::set_restart_output_prefix(std::string(simparams->restartFilename));
380  colvarproxy_io::set_default_restart_frequency(simparams->restartFrequency);
381  colvars->setup_output();
382 
383  }
384  }
385 
388 
389  {
390  Vector const a = lattice->a();
391  Vector const b = lattice->b();
392  Vector const c = lattice->c();
393  unit_cell_x.set(a.x, a.y, a.z);
394  unit_cell_y.set(b.x, b.y, c.z);
395  unit_cell_z.set(c.x, c.y, c.z);
396  }
397 
398  if (!lattice->a_p() && !lattice->b_p() && !lattice->c_p()) {
399  boundaries_type = boundaries_non_periodic;
400  reset_pbc_lattice();
401  } else if (lattice->a_p() && lattice->b_p() && lattice->c_p()) {
402  if (lattice->orthogonal()) {
403  boundaries_type = boundaries_pbc_ortho;
404  } else {
405  boundaries_type = boundaries_pbc_triclinic;
406  }
407  colvarproxy_system::update_pbc_lattice();
408  } else {
409  boundaries_type = boundaries_unsupported;
410  }
411 
412  if (cvm::debug()) {
413  cvm::log(std::string(cvm::line_marker)+
414  "colvarproxy_namd, step no. "+cvm::to_str(colvars->it)+"\n"+
415  "Updating atomic data arrays.\n");
416  }
417 
418  // must delete the forces applied at the previous step: we can do
419  // that because they have already been used and copied to other
420  // memory locations
423 
424  // If new atomic positions or forces have been requested by other
425  // GlobalMaster objects, add these to the atom map as well
426  size_t const n_all_atoms = Node::Object()->molecule->numAtoms;
427  if ( (atoms_map.size() != n_all_atoms) ||
428  (int(atoms_ids.size()) < (getAtomIdEnd() - getAtomIdBegin())) ||
429  (int(atoms_ids.size()) < (getForceIdEnd() - getForceIdBegin())) ) {
432  }
433 
434  // prepare local arrays
435  for (size_t i = 0; i < atoms_ids.size(); i++) {
436  atoms_positions[i] = cvm::rvector(0.0, 0.0, 0.0);
437  atoms_total_forces[i] = cvm::rvector(0.0, 0.0, 0.0);
438  atoms_new_colvar_forces[i] = cvm::rvector(0.0, 0.0, 0.0);
439  }
440 
441  for (size_t i = 0; i < atom_groups_ids.size(); i++) {
442  atom_groups_total_forces[i] = cvm::rvector(0.0, 0.0, 0.0);
443  atom_groups_new_colvar_forces[i] = cvm::rvector(0.0, 0.0, 0.0);
444  }
445 
446 #if NAMD_VERSION_NUMBER >= 34471681
447  for (int imap = 0; imap < volmaps_ids.size(); imap++) {
448  volmaps_new_colvar_forces[imap] = 0.0;
449  }
450 #endif
451 
452  {
453  if (cvm::debug()) {
454  cvm::log("Updating positions arrays.\n");
455  }
456  size_t n_positions = 0;
460 
461  for ( ; a_i != a_e; ++a_i, ++p_i ) {
462  atoms_positions[atoms_map[*a_i]] = cvm::rvector((*p_i).x, (*p_i).y, (*p_i).z);
463  n_positions++;
464  }
465 
466  // The following had to be relaxed because some atoms may be forced without their position being requested
467  // if (n_positions < atoms_ids.size()) {
468  // cvm::error("Error: did not receive the positions of all atoms.\n", COLVARS_BUG_ERROR);
469  // }
470  }
471 
472  if (total_force_requested && cvm::step_relative() > 0) {
473 
474  // sort the force arrays the previous step
475  // (but only do so if there *is* a previous step!)
476 
477  {
478  if (cvm::debug()) {
479  cvm::log("Updating total forces arrays.\n");
480  }
481  size_t n_total_forces = 0;
485 
486  for ( ; a_i != a_e; ++a_i, ++f_i ) {
487  atoms_total_forces[atoms_map[*a_i]] = cvm::rvector((*f_i).x, (*f_i).y, (*f_i).z);
488  n_total_forces++;
489  }
490 
491  if ( (! b_simulation_continuing) &&
492  (n_total_forces < atoms_ids.size()) ) {
493  cvm::error("Error: total forces were requested, but total forces "
494  "were not received for all atoms.\n"
495  "The most probable cause is combination of energy "
496  "minimization with a biasing method that requires MD (e.g. ABF).\n"
497  "Always run minimization and ABF separately.", COLVARS_INPUT_ERROR);
498  }
499  }
500 
501  {
502  if (cvm::debug()) {
503  cvm::log("Updating group total forces arrays.\n");
504  }
507  size_t i = 0;
508  if ( (! b_simulation_continuing) &&
509  ((f_e - f_i) != ((int) atom_groups_ids.size())) ) {
510  cvm::error("Error: total forces were requested for scalable groups, "
511  "but they are not in the same number from the number of groups.\n"
512  "The most probable cause is combination of energy "
513  "minimization with a biasing method that requires MD (e.g. ABF).\n"
514  "Always run minimization and ABF separately.", COLVARS_INPUT_ERROR);
515  }
516  for ( ; f_i != f_e; f_i++, i++) {
517  atom_groups_total_forces[i] = cvm::rvector((*f_i).x, (*f_i).y, (*f_i).z);
518  }
519  }
520  }
521 
522  {
523  if (cvm::debug()) {
524  cvm::log("Updating group positions arrays.\n");
525  }
526  // update group data (only coms available so far)
527  size_t ig;
528  // note: getGroupMassBegin() could be used here, but masses and charges
529  // have already been calculated from the last call to setup()
531  for (ig = 0; gp_i != getGroupPositionEnd(); gp_i++, ig++) {
532  atom_groups_coms[ig] = cvm::rvector(gp_i->x, gp_i->y, gp_i->z);
533  }
534  }
535 
536 #if NAMD_VERSION_NUMBER >= 34471681
537  {
538  if (cvm::debug()) {
539  log("Updating grid objects.\n");
540  }
541  // Using a simple nested loop: there probably won't be so many maps that
542  // this becomes performance-limiting
545  for ( ; gov_i != getGridObjValueEnd(); goi_i++, gov_i++) {
546  for (size_t imap = 0; imap < volmaps_ids.size(); imap++) {
547  if (volmaps_ids[imap] == *goi_i) {
548  volmaps_values[imap] = *gov_i;
549  break;
550  }
551  }
552  }
553  }
554 #endif
555 
556  if (cvm::debug()) {
557  print_input_atomic_data();
558  }
559 
560  // call the collective variable module
561  if (colvars->calc() != COLVARS_OK) {
562  cvm::error("Error in the collective variables module.\n", COLVARS_ERROR);
563  }
564 
565  if (cvm::debug()) {
566  print_output_atomic_data();
567  }
568 
569  // communicate all forces to the MD integrator
570  for (size_t i = 0; i < atoms_ids.size(); i++) {
571  cvm::rvector const &f = atoms_new_colvar_forces[i];
572  modifyForcedAtoms().add(atoms_ids[i]);
573  modifyAppliedForces().add(Vector(f.x, f.y, f.z));
574  }
575 
576  if (atom_groups_new_colvar_forces.size() > 0) {
579  for (int ig = 0; gf_i != modifyGroupForces().end(); gf_i++, ig++) {
580  cvm::rvector const &f = atom_groups_new_colvar_forces[ig];
581  *gf_i = Vector(f.x, f.y, f.z);
582  }
583  }
584 
585 #if NAMD_VERSION_NUMBER >= 34471681
586  if (volmaps_new_colvar_forces.size() > 0) {
591  for ( ; goi_i != getGridObjIndexEnd(); goi_i++, gof_i++) {
592  for (size_t imap = 0; imap < volmaps_ids.size(); imap++) {
593  if (volmaps_ids[imap] == *goi_i) {
594  *gof_i = volmaps_new_colvar_forces[imap];
595  break;
596  }
597  }
598  }
599  }
600 #endif
601 
602  // send MISC energy
603  #if defined(NODEGROUP_FORCE_REGISTER) && !defined(NAMD_UNIFIED_REDUCTION)
605  reduction->submit();
606  }
607  #else
608  #if !defined(NAMD_UNIFIED_REDUCTION)
609  reduction->submit();
610  #else
611  // submitReduction();
612  #endif
613  #endif
614 
615  // NAMD does not destruct GlobalMaster objects, so we must remember
616  // to write all output files at the end of a run
617  if (step == simparams->N) {
618  post_run();
619  }
620 }
621 
623  // This aMD factor is from previous step!
624  amd_weight_factor = std::exp(controller->accelMDdV / (target_temperature() * boltzmann()));
625 }
626 
627 
628 // Callback functions
629 
631 {
632 #ifdef NAMD_TCL
633  // Store pointer to NAMD's Tcl interpreter
634  set_tcl_interp(Node::Object()->getScript()->interp);
635 #else
636  colvarproxy::init_tcl_pointers(); // Create dedicated interpreter
637 #endif
638 }
639 
641 {
642  return colvarproxy::tcl_run_force_callback();
643 }
644 
646  std::string const &name,
647  std::vector<const colvarvalue *> const &cvc_values,
648  colvarvalue &value)
649 {
650  return colvarproxy::tcl_run_colvar_callback(name, cvc_values, value);
651 }
652 
654  std::string const &name,
655  std::vector<const colvarvalue *> const &cvc_values,
656  std::vector<cvm::matrix2d<cvm::real> > &gradient)
657 {
658  return colvarproxy::tcl_run_colvar_gradient_callback(name, cvc_values,
659  gradient);
660 }
661 
662 
663 void colvarproxy_namd::add_energy(cvm::real energy)
664 {
665  #if defined(NODEGROUP_FORCE_REGISTER) && !defined(NAMD_UNIFIED_REDUCTION)
667  nodeReduction->item(REDUCTION_MISC_ENERGY) += energy;
668  } else {
669  reduction->item(REDUCTION_MISC_ENERGY) += energy;
670  }
671  #else
672  #if !defined(NAMD_UNIFIED_REDUCTION)
673  reduction->item(REDUCTION_MISC_ENERGY) += energy;
674  #else
676  #endif
677  #endif
678 }
679 
681 {
682  if (cvm::debug()) {
683  cvm::log("colvarproxy_namd::request_total_force()\n");
684  }
685  total_force_requested = yesno;
686  requestTotalForce(total_force_requested);
687  if (cvm::debug()) {
688  cvm::log("colvarproxy_namd::request_total_force() end\n");
689  }
690 }
691 
692 
693 void colvarproxy_namd::log(std::string const &message)
694 {
695  std::istringstream is(message);
696  std::string line;
697  while (std::getline(is, line))
698  iout << "colvars: " << line << "\n";
699  iout << endi;
700 }
701 
702 
703 void colvarproxy_namd::error(std::string const &message)
704 {
705  log(message);
706  switch (cvm::get_error()) {
707  case COLVARS_FILE_ERROR:
708  errno = EIO; break;
709  case COLVARS_NOT_IMPLEMENTED:
710  errno = ENOSYS; break;
711  case COLVARS_MEMORY_ERROR:
712  errno = ENOMEM; break;
713  }
714  char const *msg = "Error in the collective variables module "
715  "(see above for details)";
716  if (errno) {
717  NAMD_err(msg);
718  } else {
719  NAMD_die(msg);
720  }
721 }
722 
723 
725 {
726  // NAMD's internal numbering starts from zero
727  int const aid = (atom_number-1);
728 
729  if (cvm::debug())
730  cvm::log("Adding atom "+cvm::to_str(atom_number)+
731  " for collective variables calculation.\n");
732 
733  if ( (aid < 0) || (aid >= Node::Object()->molecule->numAtoms) ) {
734  cvm::error("Error: invalid atom number specified, "+
735  cvm::to_str(atom_number)+"\n", COLVARS_INPUT_ERROR);
736  return COLVARS_INPUT_ERROR;
737  }
738 
739  return aid;
740 }
741 
742 
744 {
745  return COLVARS_OK;
746 }
747 
748 
749 int colvarproxy_namd::init_atom(int atom_number)
750 {
751  // save time by checking first whether this atom has been requested before
752  // (this is more common than a non-valid atom number)
753  int aid = (atom_number-1);
754 
755  for (size_t i = 0; i < atoms_ids.size(); i++) {
756  if (atoms_ids[i] == aid) {
757  // this atom id was already recorded
758  atoms_refcount[i] += 1;
759  return i;
760  }
761  }
762 
763  aid = check_atom_id(atom_number);
764 
765  if (aid < 0) {
766  return COLVARS_INPUT_ERROR;
767  }
768 
769  int const index = add_atom_slot(aid);
770  atoms_map[aid] = index;
771  modifyRequestedAtoms().add(aid);
772  update_atom_properties(index);
773  return index;
774 }
775 
776 
777 int colvarproxy_namd::check_atom_id(cvm::residue_id const &residue,
778  std::string const &atom_name,
779  std::string const &segment_id)
780 {
781  int const aid =
782  (segment_id.size() ?
783  Node::Object()->molecule->get_atom_from_name(segment_id.c_str(),
784  residue,
785  atom_name.c_str()) :
787  residue,
788  atom_name.c_str()));
789 
790  if (aid < 0) {
791  // get_atom_from_name() has returned an error value
792  cvm::error("Error: could not find atom \""+
793  atom_name+"\" in residue "+
794  cvm::to_str(residue)+
795  ( (segment_id != "MAIN") ?
796  (", segment \""+segment_id+"\"") :
797  ("") )+
798  "\n", COLVARS_INPUT_ERROR);
799  return COLVARS_INPUT_ERROR;
800  }
801 
802  return aid;
803 }
804 
805 
806 
810 int colvarproxy_namd::init_atom(cvm::residue_id const &residue,
811  std::string const &atom_name,
812  std::string const &segment_id)
813 {
814  int const aid = check_atom_id(residue, atom_name, segment_id);
815 
816  for (size_t i = 0; i < atoms_ids.size(); i++) {
817  if (atoms_ids[i] == aid) {
818  // this atom id was already recorded
819  atoms_refcount[i] += 1;
820  return i;
821  }
822  }
823 
824  if (cvm::debug())
825  cvm::log("Adding atom \""+
826  atom_name+"\" in residue "+
827  cvm::to_str(residue)+
828  " (index "+cvm::to_str(aid)+
829  ") for collective variables calculation.\n");
830 
831  int const index = add_atom_slot(aid);
832  atoms_map[aid] = index;
833  modifyRequestedAtoms().add(aid);
834  update_atom_properties(index);
835  return index;
836 }
837 
838 
840 {
841  colvarproxy::clear_atom(index);
842  // TODO remove it from GlobalMaster arrays?
843 }
844 
845 
847 {
848  Molecule *mol = Node::Object()->molecule;
849  // update mass
850  double const mass = mol->atommass(atoms_ids[index]);
851  if (mass <= 0.001) {
852  this->log("Warning: near-zero mass for atom "+
853  cvm::to_str(atoms_ids[index]+1)+
854  "; expect unstable dynamics if you apply forces to it.\n");
855  }
856  atoms_masses[index] = mass;
857  // update charge
858  atoms_charges[index] = mol->atomcharge(atoms_ids[index]);
859 }
860 
861 
862 cvm::rvector colvarproxy_namd::position_distance(cvm::atom_pos const &pos1,
863  cvm::atom_pos const &pos2)
864  const
865 {
866  Position const p1(pos1.x, pos1.y, pos1.z);
867  Position const p2(pos2.x, pos2.y, pos2.z);
868  // return p2 - p1
869  Vector const d = this->lattice->delta(p2, p1);
870  return cvm::rvector(d.x, d.y, d.z);
871 }
872 
873 
874 
883 };
884 
885 
886 e_pdb_field pdb_field_str2enum(std::string const &pdb_field_str)
887 {
888  e_pdb_field pdb_field = e_pdb_none;
889 
890  if (colvarparse::to_lower_cppstr(pdb_field_str) ==
891  colvarparse::to_lower_cppstr("O")) {
892  pdb_field = e_pdb_occ;
893  }
894 
895  if (colvarparse::to_lower_cppstr(pdb_field_str) ==
896  colvarparse::to_lower_cppstr("B")) {
897  pdb_field = e_pdb_beta;
898  }
899 
900  if (colvarparse::to_lower_cppstr(pdb_field_str) ==
901  colvarparse::to_lower_cppstr("X")) {
902  pdb_field = e_pdb_x;
903  }
904 
905  if (colvarparse::to_lower_cppstr(pdb_field_str) ==
906  colvarparse::to_lower_cppstr("Y")) {
907  pdb_field = e_pdb_y;
908  }
909 
910  if (colvarparse::to_lower_cppstr(pdb_field_str) ==
911  colvarparse::to_lower_cppstr("Z")) {
912  pdb_field = e_pdb_z;
913  }
914 
915  if (pdb_field == e_pdb_none) {
916  cvm::error("Error: unsupported PDB field, \""+
917  pdb_field_str+"\".\n", COLVARS_INPUT_ERROR);
918  }
919 
920  return pdb_field;
921 }
922 
923 
924 int colvarproxy_namd::load_coords_pdb(char const *pdb_filename,
925  std::vector<cvm::atom_pos> &pos,
926  const std::vector<int> &indices,
927  std::string const &pdb_field_str,
928  double const pdb_field_value)
929 {
930  if (pdb_field_str.size() == 0 && indices.size() == 0) {
931  cvm::error("Bug alert: either PDB field should be defined or list of "
932  "atom IDs should be available when loading atom coordinates!\n", COLVARS_BUG_ERROR);
933  }
934 
935  e_pdb_field pdb_field_index;
936  bool const use_pdb_field = (pdb_field_str.size() > 0);
937  if (use_pdb_field) {
938  pdb_field_index = pdb_field_str2enum(pdb_field_str);
939  }
940 
941  // next index to be looked up in PDB file (if list is supplied)
942  std::vector<int>::const_iterator current_index = indices.begin();
943 
944  PDB *pdb = new PDB(pdb_filename);
945  size_t const pdb_natoms = pdb->num_atoms();
946 
947  if (pos.size() != pdb_natoms) {
948 
949  bool const pos_allocated = (pos.size() > 0);
950 
951  size_t ipos = 0, ipdb = 0;
952  for ( ; ipdb < pdb_natoms; ipdb++) {
953 
954  if (use_pdb_field) {
955  // PDB field mode: skip atoms with wrong value in PDB field
956  double atom_pdb_field_value = 0.0;
957 
958  switch (pdb_field_index) {
959  case e_pdb_occ:
960  atom_pdb_field_value = (pdb->atom(ipdb))->occupancy();
961  break;
962  case e_pdb_beta:
963  atom_pdb_field_value = (pdb->atom(ipdb))->temperaturefactor();
964  break;
965  case e_pdb_x:
966  atom_pdb_field_value = (pdb->atom(ipdb))->xcoor();
967  break;
968  case e_pdb_y:
969  atom_pdb_field_value = (pdb->atom(ipdb))->ycoor();
970  break;
971  case e_pdb_z:
972  atom_pdb_field_value = (pdb->atom(ipdb))->zcoor();
973  break;
974  default:
975  break;
976  }
977 
978  if ( (pdb_field_value) &&
979  (atom_pdb_field_value != pdb_field_value) ) {
980  continue;
981  } else if (atom_pdb_field_value == 0.0) {
982  continue;
983  }
984 
985  } else {
986  // Atom ID mode: use predefined atom IDs from the atom group
987  if (((int) ipdb) != *current_index) {
988  // Skip atoms not in the list
989  continue;
990  } else {
991  current_index++;
992  }
993  }
994 
995  if (!pos_allocated) {
996  pos.push_back(cvm::atom_pos(0.0, 0.0, 0.0));
997  } else if (ipos >= pos.size()) {
998  cvm::error("Error: the PDB file \""+
999  std::string(pdb_filename)+
1000  "\" contains coordinates for "
1001  "more atoms than needed.\n", COLVARS_BUG_ERROR);
1002  }
1003 
1004  pos[ipos] = cvm::atom_pos((pdb->atom(ipdb))->xcoor(),
1005  (pdb->atom(ipdb))->ycoor(),
1006  (pdb->atom(ipdb))->zcoor());
1007  ipos++;
1008  if (!use_pdb_field && current_index == indices.end())
1009  break;
1010  }
1011 
1012  if (ipos < pos.size() || (!use_pdb_field && current_index != indices.end())) {
1013  size_t n_requested = use_pdb_field ? pos.size() : indices.size();
1014  cvm::error("Error: number of matching records in the PDB file \""+
1015  std::string(pdb_filename)+"\" ("+cvm::to_str(ipos)+
1016  ") does not match the number of requested coordinates ("+
1017  cvm::to_str(n_requested)+").\n", COLVARS_INPUT_ERROR);
1018  return COLVARS_ERROR;
1019  }
1020  } else {
1021 
1022  // when the PDB contains exactly the number of atoms of the array,
1023  // ignore the fields and just read coordinates
1024  for (size_t ia = 0; ia < pos.size(); ia++) {
1025  pos[ia] = cvm::atom_pos((pdb->atom(ia))->xcoor(),
1026  (pdb->atom(ia))->ycoor(),
1027  (pdb->atom(ia))->zcoor());
1028  }
1029  }
1030 
1031  delete pdb;
1032  return COLVARS_OK;
1033 }
1034 
1035 
1036 int colvarproxy_namd::load_atoms_pdb(char const *pdb_filename,
1037  cvm::atom_group &atoms,
1038  std::string const &pdb_field_str,
1039  double const pdb_field_value)
1040 {
1041  if (pdb_field_str.size() == 0)
1042  cvm::error("Error: must define which PDB field to use "
1043  "in order to define atoms from a PDB file.\n", COLVARS_INPUT_ERROR);
1044 
1045  PDB *pdb = new PDB(pdb_filename);
1046  size_t const pdb_natoms = pdb->num_atoms();
1047 
1048  e_pdb_field pdb_field_index = pdb_field_str2enum(pdb_field_str);
1049 
1050  for (size_t ipdb = 0; ipdb < pdb_natoms; ipdb++) {
1051 
1052  double atom_pdb_field_value = 0.0;
1053 
1054  switch (pdb_field_index) {
1055  case e_pdb_occ:
1056  atom_pdb_field_value = (pdb->atom(ipdb))->occupancy();
1057  break;
1058  case e_pdb_beta:
1059  atom_pdb_field_value = (pdb->atom(ipdb))->temperaturefactor();
1060  break;
1061  case e_pdb_x:
1062  atom_pdb_field_value = (pdb->atom(ipdb))->xcoor();
1063  break;
1064  case e_pdb_y:
1065  atom_pdb_field_value = (pdb->atom(ipdb))->ycoor();
1066  break;
1067  case e_pdb_z:
1068  atom_pdb_field_value = (pdb->atom(ipdb))->zcoor();
1069  break;
1070  default:
1071  break;
1072  }
1073 
1074  if ( (pdb_field_value) &&
1075  (atom_pdb_field_value != pdb_field_value) ) {
1076  continue;
1077  } else if (atom_pdb_field_value == 0.0) {
1078  continue;
1079  }
1080 
1081  if (atoms.is_enabled(colvardeps::f_ag_scalable)) {
1082  atoms.add_atom_id(ipdb);
1083  } else {
1084  atoms.add_atom(cvm::atom(ipdb+1));
1085  }
1086  }
1087 
1088  delete pdb;
1089  return (cvm::get_error() ? COLVARS_ERROR : COLVARS_OK);
1090 }
1091 
1092 
1093 std::ostream & colvarproxy_namd::output_stream(std::string const &output_name,
1094  std::string const description)
1095 {
1096  if (cvm::debug()) {
1097  cvm::log("Using colvarproxy_namd::output_stream()\n");
1098  }
1099 
1100  if (!io_available()) {
1101  cvm::error("Error: trying to access an output file/channel "
1102  "from the wrong thread.\n", COLVARS_BUG_ERROR);
1103  return *output_stream_error_;
1104  }
1105 
1106  if (output_streams_.count(output_name) > 0) {
1107  return *(output_streams_[output_name]);
1108  }
1109 
1110  backup_file(output_name.c_str());
1111 
1112  output_streams_[output_name] = new ofstream_namd(output_name.c_str(), std::ios::binary);
1113  if (! output_streams_[output_name]->good()) {
1114  cvm::error("Error: cannot write to "+description+" \""+output_name+"\".\n",
1115  COLVARS_FILE_ERROR);
1116  }
1117 
1118  return *(output_streams_[output_name]);
1119 }
1120 
1121 
1122 int colvarproxy_namd::flush_output_stream(std::string const &output_name)
1123 {
1124  if (!io_available()) {
1125  return COLVARS_OK;
1126  }
1127 
1128  if (output_streams_.count(output_name) > 0) {
1129  (reinterpret_cast<ofstream_namd *>(output_streams_[output_name]))->flush();
1130  return COLVARS_OK;
1131  }
1132 
1133  return cvm::error("Error: trying to flush an output file/channel "
1134  "that wasn't open.\n", COLVARS_BUG_ERROR);
1135 }
1136 
1137 
1139 {
1140  if (!io_available()) {
1141  return COLVARS_OK;
1142  }
1143 
1144  for (std::map<std::string, std::ostream *>::iterator osi = output_streams_.begin();
1145  osi != output_streams_.end();
1146  osi++) {
1147  (reinterpret_cast<ofstream_namd *>(osi->second))->flush();
1148  }
1149 
1150  return COLVARS_OK;
1151 }
1152 
1153 
1154 int colvarproxy_namd::close_output_stream(std::string const &output_name)
1155 {
1156  if (!io_available()) {
1157  return cvm::error("Error: trying to access an output file/channel "
1158  "from the wrong thread.\n", COLVARS_BUG_ERROR);
1159  }
1160 
1161  if (output_streams_.count(output_name) > 0) {
1162  (reinterpret_cast<ofstream_namd *>(output_streams_[output_name]))->close();
1163  delete output_streams_[output_name];
1164  output_streams_.erase(output_name);
1165  }
1166 
1167  return COLVARS_OK;
1168 }
1169 
1170 
1172 {
1173  if (! io_available()) {
1174  return COLVARS_OK;
1175  }
1176 
1177  for (std::map<std::string, std::ostream *>::iterator osi = output_streams_.begin();
1178  osi != output_streams_.end();
1179  osi++) {
1180  (reinterpret_cast<ofstream_namd *>(osi->second))->close();
1181  }
1182  output_streams_.clear();
1183 
1184  return COLVARS_OK;
1185 }
1186 
1187 
1188 int colvarproxy_namd::backup_file(char const *filename)
1189 {
1190  if (std::string(filename).rfind(std::string(".colvars.state")) != std::string::npos) {
1191  NAMD_backup_file(filename, ".old");
1192  } else {
1193  NAMD_backup_file(filename, ".BAK");
1194  }
1195  return COLVARS_OK;
1196 }
1197 
1198 
1199 int colvarproxy_namd::init_atom_group(std::vector<int> const &atoms_ids)
1200 {
1201  if (cvm::debug())
1202  cvm::log("Requesting from NAMD a group of size "+cvm::to_str(atoms_ids.size())+
1203  " for collective variables calculation.\n");
1204 
1205  colvars->cite_feature("Scalable center-of-mass computation (NAMD)");
1206 
1207  // Note: modifyRequestedGroups is supposed to be in sync with the colvarproxy arrays,
1208  // and to stay that way during a simulation
1209 
1210  // compare this new group to those already allocated inside GlobalMaster
1211  int ig;
1212  for (ig = 0; ig < modifyRequestedGroups().size(); ig++) {
1213  AtomIDList const &namd_group = modifyRequestedGroups()[ig];
1214  bool b_match = true;
1215 
1216  if (namd_group.size() != ((int) atoms_ids.size())) {
1217  b_match = false;
1218  } else {
1219  int ia;
1220  for (ia = 0; ia < namd_group.size(); ia++) {
1221  int const aid = atoms_ids[ia];
1222  if (namd_group[ia] != aid) {
1223  b_match = false;
1224  break;
1225  }
1226  }
1227  }
1228 
1229  if (b_match) {
1230  if (cvm::debug())
1231  cvm::log("Group was already added.\n");
1232  // this group already exists
1233  atom_groups_refcount[ig] += 1;
1234  return ig;
1235  }
1236  }
1237 
1238  // add this group (note: the argument of add_atom_group_slot() is redundant for NAMD, and provided only for consistency)
1239  size_t const index = add_atom_group_slot(atom_groups_ids.size());
1240  modifyRequestedGroups().resize(atom_groups_ids.size());
1241  // the following is done in calculate()
1242  // modifyGroupForces().resize(atom_groups_ids.size());
1243  AtomIDList &namd_group = modifyRequestedGroups()[index];
1244  namd_group.resize(atoms_ids.size());
1245  int const n_all_atoms = Node::Object()->molecule->numAtoms;
1246  for (size_t ia = 0; ia < atoms_ids.size(); ia++) {
1247  int const aid = atoms_ids[ia];
1248  if (cvm::debug())
1249  cvm::log("Adding atom "+cvm::to_str(aid+1)+
1250  " for collective variables calculation.\n");
1251  if ( (aid < 0) || (aid >= n_all_atoms) ) {
1252  cvm::error("Error: invalid atom number specified, "+
1253  cvm::to_str(aid+1)+"\n", COLVARS_INPUT_ERROR);
1254  return -1;
1255  }
1256  namd_group[ia] = aid;
1257  }
1258 
1259  update_group_properties(index);
1260 
1261  if (cvm::debug()) {
1262  cvm::log("Group has index "+cvm::to_str(index)+"\n");
1263  cvm::log("modifyRequestedGroups length = "+cvm::to_str(modifyRequestedGroups().size())+
1264  ", modifyGroupForces length = "+cvm::to_str(modifyGroupForces().size())+"\n");
1265  }
1266 
1267  return index;
1268 }
1269 
1270 
1272 {
1273  // do nothing, keep the NAMD arrays in sync with the colvarproxy ones
1274  colvarproxy::clear_atom_group(index);
1275 }
1276 
1277 
1279 {
1280  AtomIDList const &namd_group = modifyRequestedGroups()[index];
1281  if (cvm::debug()) {
1282  cvm::log("Re-calculating total mass and charge for scalable group no. "+cvm::to_str(index+1)+" ("+
1283  cvm::to_str(namd_group.size())+" atoms).\n");
1284  }
1285 
1286  cvm::real total_mass = 0.0;
1287  cvm::real total_charge = 0.0;
1288  for (int i = 0; i < namd_group.size(); i++) {
1289  total_mass += Node::Object()->molecule->atommass(namd_group[i]);
1290  total_charge += Node::Object()->molecule->atomcharge(namd_group[i]);
1291  }
1292  atom_groups_masses[index] = total_mass;
1293  atom_groups_charges[index] = total_charge;
1294 
1295  if (cvm::debug()) {
1296  cvm::log("total mass = "+cvm::to_str(total_mass)+
1297  ", total charge = "+cvm::to_str(total_charge)+"\n");
1298  }
1299 
1300  return COLVARS_OK;
1301 }
1302 
1303 
1304 
1305 int colvarproxy_namd::set_unit_system(std::string const &units_in, bool /*check_only*/)
1306 {
1307  if (units_in != "real") {
1308  cvm::error("Error: Specified unit system \"" + units_in + "\" is unsupported in NAMD. Supported units are \"real\" (A, kcal/mol).\n");
1309  return COLVARS_ERROR;
1310  }
1311  return COLVARS_OK;
1312 }
1313 
1314 
1315 #if NAMD_VERSION_NUMBER >= 34471681
1316 
1317 
1319 {
1320  return COLVARS_OK;
1321 }
1322 
1323 
1325 {
1326  for (size_t i = 0; i < volmaps_ids.size(); i++) {
1327  if (volmaps_ids[i] == volmap_id) {
1328  // this map has already been requested
1329  volmaps_refcount[i] += 1;
1330  return i;
1331  }
1332  }
1333 
1334  int error_code = check_volmap_by_id(volmap_id);
1335  int index = -1;
1336  if (error_code == COLVARS_OK) {
1337  index = add_volmap_slot(volmap_id);
1338  modifyRequestedGridObjects().add(volmap_id);
1339  }
1340 
1341  return (error_code == COLVARS_OK) ? index : -1;
1342 }
1343 
1344 
1345 int colvarproxy_namd::init_volmap_by_name(char const *volmap_name)
1346 {
1347  if (volmap_name == NULL) {
1348  return cvm::error("Error: no grid object name provided.", COLVARS_INPUT_ERROR);
1349  }
1350 
1351  int error_code = COLVARS_OK;
1352 
1353  error_code |= check_volmap_by_name(volmap_name);
1354 
1355  int index = -1;
1356  if (error_code == COLVARS_OK) {
1357 
1358  int volmap_id = simparams->mgridforcelist.index_for_key(volmap_name);
1359 
1360  // Check that the scale factor is correctly set to zero
1361  Molecule *mol = Node::Object()->molecule;
1362  GridforceGrid const *grid = mol->get_gridfrc_grid(volmap_id);
1363  Vector const gfScale = grid->get_scale();
1364  if ((gfScale.x != 0.0) || (gfScale.y != 0.0) || (gfScale.z != 0.0)) {
1365  error_code |= cvm::error("Error: GridForce map \""+
1366  std::string(volmap_name)+
1367  "\" has non-zero scale factors.\n",
1368  COLVARS_INPUT_ERROR);
1369  }
1370 
1371  for (size_t i = 0; i < volmaps_ids.size(); i++) {
1372  if (volmaps_ids[i] == volmap_id) {
1373  // this map has already been requested
1374  volmaps_refcount[i] += 1;
1375  return i;
1376  }
1377  }
1378 
1379  index = add_volmap_slot(volmap_id);
1380  modifyRequestedGridObjects().add(volmap_id);
1381  }
1382 
1383  return (error_code == COLVARS_OK) ? index : -1;
1384 }
1385 
1386 
1388 {
1389  Molecule *mol = Node::Object()->molecule;
1390  if ((volmap_id < 0) || (volmap_id >= mol->numGridforceGrids)) {
1391  return cvm::error("Error: invalid numeric ID ("+cvm::to_str(volmap_id)+
1392  ") for map.\n", COLVARS_INPUT_ERROR);
1393  }
1394  colvars->cite_feature("GridForces volumetric map implementation for NAMD");
1395  return COLVARS_OK;
1396 }
1397 
1398 
1399 int colvarproxy_namd::check_volmap_by_name(char const *volmap_name)
1400 {
1401  if (volmap_name == NULL) {
1402  return cvm::error("Error: no grid object name provided.", COLVARS_INPUT_ERROR);
1403  }
1404  int volmap_id = simparams->mgridforcelist.index_for_key(volmap_name);
1405  if (volmap_id < 0) {
1406  return cvm::error("Error: invalid map name \""+std::string(volmap_name)+
1407  "\".\n", COLVARS_INPUT_ERROR);
1408  }
1409  colvars->cite_feature("GridForces volumetric map implementation for NAMD");
1410  return COLVARS_OK;
1411 }
1412 
1413 
1415 {
1416  // TODO remove from GlobalMaster
1417  colvarproxy::clear_volmap(index);
1418 }
1419 
1420 
1421 int colvarproxy_namd::get_volmap_id_from_name(char const *volmap_name)
1422 {
1423  int const volmap_id =
1424  simparams->mgridforcelist.index_for_key(volmap_name);
1425  if (volmap_id < 0) {
1426  // Print error
1427  check_volmap_by_name(volmap_name);
1428  }
1429  return volmap_id;
1430 }
1431 
1432 
1433 template<class T, int flags>
1435  cvm::atom_iter atom_begin,
1436  cvm::atom_iter atom_end,
1437  cvm::real *value,
1438  cvm::real *atom_field)
1439 {
1440  float V = 0.0f;
1441  Vector dV(0.0);
1442  int i = 0;
1443  cvm::atom_iter ai = atom_begin;
1444  for ( ; ai != atom_end; ai++, i++) {
1445  if (g->compute_VdV(Position(ai->pos.x, ai->pos.y, ai->pos.z), V, dV)) {
1446  // out-of-bounds atom
1447  V = 0.0f;
1448  dV = 0.0;
1449  } else {
1450  if (flags & volmap_flag_use_atom_field) {
1451  *value += V * atom_field[i];
1452  if (flags & volmap_flag_gradients) {
1453  ai->grad += atom_field[i] * cvm::rvector(dV.x, dV.y, dV.z);
1454  }
1455  } else {
1456  *value += V;
1457  if (flags & volmap_flag_gradients) {
1458  ai->grad += cvm::rvector(dV.x, dV.y, dV.z);
1459  }
1460  }
1461  }
1462  }
1463 }
1464 
1465 
1466 template<class T>
1468  T const *g,
1469  cvm::atom_iter atom_begin,
1470  cvm::atom_iter atom_end,
1471  cvm::real *value,
1472  cvm::real *atom_field)
1473 {
1474  if (flags & volmap_flag_use_atom_field) {
1475  int const new_flags = volmap_flag_use_atom_field | volmap_flag_gradients;
1476  GridForceGridLoop<T, new_flags>(g, atom_begin, atom_end,
1477  value, atom_field);
1478  } else {
1479  int const new_flags = volmap_flag_gradients;
1480  GridForceGridLoop<T, new_flags>(g, atom_begin, atom_end,
1481  value, atom_field);
1482  }
1483 }
1484 
1485 
1487  int volmap_id,
1488  cvm::atom_iter atom_begin,
1489  cvm::atom_iter atom_end,
1490  cvm::real *value,
1491  cvm::real *atom_field)
1492 {
1493  Molecule *mol = Node::Object()->molecule;
1494  GridforceGrid *grid = mol->get_gridfrc_grid(volmap_id);
1495  // Inheritance is not possible with GridForceGrid's design
1497  GridforceFullMainGrid *g = dynamic_cast<GridforceFullMainGrid *>(grid);
1498  getGridForceGridValue<GridforceFullMainGrid>(flags, g, atom_begin, atom_end,
1499  value, atom_field);
1500  } else if (grid->get_grid_type() == GridforceGrid::GridforceGridTypeLite) {
1501  GridforceLiteGrid *g = dynamic_cast<GridforceLiteGrid *>(grid);
1502  getGridForceGridValue<GridforceLiteGrid>(flags, g, atom_begin, atom_end,
1503  value, atom_field);
1504  }
1505  return COLVARS_OK;
1506 }
1507 
1508 #endif
1509 
1510 #if CMK_SMP && USE_CKLOOP // SMP only
1511 
1512 colvarproxy::smp_mode_t colvarproxy_namd::get_smp_mode() const {
1513  return smp_mode;
1514 }
1515 
1516 int colvarproxy_namd::set_smp_mode(smp_mode_t mode) {
1517  smp_mode = mode;
1518  return COLVARS_OK;
1519 }
1520 
1521 
1522 int colvarproxy_namd::smp_loop(int n_items, std::function<int (int)> const &worker)
1523 {
1524  auto cmkWorker = [&](int start, int end, void * /* result */) {
1525 #if CMK_TRACE_ENABLED
1526  double before = CmiWallTimer();
1527 #endif
1528  for (int i = start; i <= end; i++) {
1529  worker(i);
1530  }
1531 #if CMK_TRACE_ENABLED
1532  traceUserBracketEvent(GLOBAL_MASTER_CKLOOP_CALC_ITEM, before, CmiWallTimer());
1533 #endif
1534  };
1535  const int numChunks = smp_num_threads() > n_items ?
1536  n_items :
1537  smp_num_threads();
1538  cvm::increase_depth();
1539  CkLoop_Parallelize(numChunks, 0, n_items - 1, cmkWorker, nullptr, CKLOOP_NONE, nullptr);
1540  cvm::decrease_depth();
1541  // CkLoop does not support bitwise-OR reduction, so we just return the global error flag
1542  return cvm::get_error();
1543 }
1544 
1545 
1546 void calc_cv_biases_smp(int first, int last, void *result, int paramNum, void *param)
1547 {
1548  colvarproxy_namd *proxy = (colvarproxy_namd *) param;
1549  colvarmodule *cv = proxy->colvars;
1550 #if CMK_TRACE_ENABLED
1551  double before = CmiWallTimer();
1552 #endif
1553  cvm::increase_depth();
1554  for (int i = first; i <= last; i++) {
1555  colvarbias *b = (*(cv->biases_active()))[i];
1556  if (cvm::debug()) {
1557  cvm::log("["+cvm::to_str(proxy->smp_thread_id())+"/"+cvm::to_str(proxy->smp_num_threads())+
1558  "]: calc_cv_biases_smp(), first = "+cvm::to_str(first)+
1559  ", last = "+cvm::to_str(last)+", bias = "+
1560  b->name+"\n");
1561  }
1562  b->update();
1563  }
1564  cvm::decrease_depth();
1565 #if CMK_TRACE_ENABLED
1566  traceUserBracketEvent(GLOBAL_MASTER_CKLOOP_CALC_BIASES,before,CmiWallTimer());
1567 #endif
1568 }
1569 
1570 
1571 int colvarproxy_namd::smp_biases_loop()
1572 {
1573  colvarmodule *cv = this->colvars;
1574  const int numChunks = smp_num_threads() > cv->variables_active_smp()->size() ?
1575  cv->variables_active_smp()->size() :
1576  smp_num_threads();
1577  CkLoop_Parallelize(calc_cv_biases_smp, 1, this,
1578  numChunks, 0, cv->biases_active()->size()-1);
1579  return cvm::get_error();
1580 }
1581 
1582 
1583 void calc_cv_scripted_forces(int paramNum, void *param)
1584 {
1585  colvarproxy_namd *proxy = (colvarproxy_namd *) param;
1586  colvarmodule *cv = proxy->colvars;
1587 #if CMK_TRACE_ENABLED
1588  double before = CmiWallTimer();
1589 #endif
1590  if (cvm::debug()) {
1591  cvm::log("["+cvm::to_str(proxy->smp_thread_id())+"/"+cvm::to_str(proxy->smp_num_threads())+
1592  "]: calc_cv_scripted_forces()\n");
1593  }
1594  cv->calc_scripted_forces();
1595 #if CMK_TRACE_ENABLED
1596  traceUserBracketEvent(GLOBAL_MASTER_CKLOOP_CALC_SCRIPTED_BIASES,before,CmiWallTimer());
1597 #endif
1598 }
1599 
1600 
1601 int colvarproxy_namd::smp_biases_script_loop()
1602 {
1603  colvarmodule *cv = this->colvars;
1604  CkLoop_Parallelize(calc_cv_biases_smp, 1, this,
1605  cv->biases_active()->size(), 0, cv->biases_active()->size()-1,
1606  1, NULL, CKLOOP_NONE,
1607  calc_cv_scripted_forces, 1, this);
1608  return cvm::get_error();
1609 }
1610 
1611 #endif // #if CMK_SMP && USE_CKLOOP
1612 
1613 
1615 #if CMK_HAS_PARTITION
1616  return COLVARS_OK;
1617 #else
1618  return COLVARS_NOT_IMPLEMENTED;
1619 #endif
1620 }
1621 
1622 
1624  return CmiMyPartition();
1625 }
1626 
1627 
1629  return CmiNumPartitions();
1630 }
1631 
1632 
1634  replica_barrier();
1635 }
1636 
1637 
1638 int colvarproxy_namd::replica_comm_recv(char* msg_data, int buf_len,
1639  int src_rep) {
1640  DataMessage *recvMsg = NULL;
1641  replica_recv(&recvMsg, src_rep, CkMyPe());
1642  CmiAssert(recvMsg != NULL);
1643  int retval = recvMsg->size;
1644  if (buf_len >= retval) {
1645  memcpy(msg_data,recvMsg->data,retval);
1646  } else {
1647  retval = 0;
1648  }
1649  CmiFree(recvMsg);
1650  return retval;
1651 }
1652 
1653 
1654 int colvarproxy_namd::replica_comm_send(char* msg_data, int msg_len,
1655  int dest_rep) {
1656  replica_send(msg_data, msg_len, dest_rep, CkMyPe());
1657  return msg_len;
1658 }
1659 
1660 
1663  // This test is only valid for NAMD3
1664  if (freq % simparams->computeEnergies) {
1665  cvm::error("computeEnergies must be a divisor of lambda-dynamics period (" + cvm::to_str(freq) + ").\n");
1666  return COLVARS_INPUT_ERROR;
1667  }
1668  if (!simparams->alchOn) {
1669  cvm::error("alchOn must be enabled for lambda-dynamics.\n");
1670  return COLVARS_INPUT_ERROR;
1671  }
1672  if (!simparams->alchThermIntOn) {
1673  cvm::error("alchType must be set to TI for lambda-dynamics.\n");
1674  return COLVARS_INPUT_ERROR;
1675  }
1676  return COLVARS_OK;
1677 }
1678 
1679 
1681 int colvarproxy_namd::get_alch_lambda(cvm::real* lambda) {
1682  *lambda = simparams->getCurrentLambda(step);
1683  return COLVARS_OK;
1684 }
1685 
1686 
1689  if (simparams->alchLambdaFreq > 0) {
1690  cvm::error("Cannot set lambda from Colvars because alchLambdaFreq is enabled. "
1691  "Either remove biasing forces and extended Lagrangian dynamics on the alchemical coordinate, "
1692  "or disable alchLambdaFreq.\n");
1693  return COLVARS_INPUT_ERROR;
1694  } else {
1695  simparams->alchLambda = cached_alch_lambda;
1696  }
1697  return COLVARS_OK;
1698 }
1699 
1700 
1702 int colvarproxy_namd::get_dE_dlambda(cvm::real* dE_dlambda) {
1703  // Force data at step zero is garbage in NAMD3, zero in NAMD2
1704  if (cvm::step_relative() > 0) {
1705  *dE_dlambda = controller->getTIderivative();
1706  } else {
1707  *dE_dlambda = 0.0;
1708  }
1709  return COLVARS_OK;
1710 }
static Node * Object()
Definition: Node.h:86
Real atomcharge(int anum) const
Definition: Molecule.h:1124
int check_volmaps_available() override
std::ostream & output_stream(std::string const &output_name, std::string const description) override
const Controller & getController() const
Definition: NamdState.h:51
void clear_atom_group(int index) override
ForceList & modifyAppliedForces()
Definition: GlobalMaster.C:202
int get_volmap_id_from_name(char const *volmap_name) override
AtomIDList & modifyRequestedAtoms()
Definition: GlobalMaster.C:167
void clear_volmap(int index) override
GridforceGrid * get_gridfrc_grid(int gridnum) const
Definition: Molecule.h:1368
BigRealList & modifyGridObjForces()
Definition: GlobalMaster.C:219
Definition: PDB.h:36
void getGridForceGridValue(int flags, T const *grid, cvm::atom_iter atom_begin, cvm::atom_iter atom_end, cvm::real *value, cvm::real *atom_field)
Abstraction of the two types of NAMD volumetric maps.
int size(void) const
Definition: ResizeArray.h:131
SimParameters * simparams
Pointer to the NAMD simulation input object.
NAMD_HOST_DEVICE Vector c() const
Definition: Lattice.h:270
Random random
NAMD-style PRNG object.
void NAMD_err(const char *err_msg)
Definition: common.C:170
BigReal getTIderivative(void) const
Definition: Controller.h:121
int init_atom_group(std::vector< int > const &atoms_ids) override
int compute_volmap(int flags, int volmap_id, cvm::atom_iter atom_begin, cvm::atom_iter atom_end, cvm::real *value, cvm::real *atom_field) override
NAMD_HOST_DEVICE int c_p() const
Definition: Lattice.h:291
int set_unit_system(std::string const &units_in, bool check_only) override
#define GLOBAL_MASTER_CKLOOP_CALC_ITEM
void error(std::string const &message) override
int replica_comm_send(char *msg_data, int msg_len, int dest_rep) override
int init_atom(int atom_number) override
AtomIDList::const_iterator getForceIdEnd()
Definition: GlobalMaster.C:300
AtomIDList::const_iterator getAtomIdBegin()
Definition: GlobalMaster.C:230
PositionList::const_iterator getGroupPositionEnd()
Definition: GlobalMaster.C:246
Communication between colvars and NAMD (implementation of colvarproxy)
Controller const * controller
Pointer to Controller object.
int init_volmap_by_id(int volmap_id) override
Definition: Vector.h:72
SimParameters * simParameters
Definition: Node.h:181
int check_volmap_by_name(char const *volmap_name) override
void clear_atom(int index) override
cvm::real amd_weight_factor
e_pdb_field pdb_field_str2enum(std::string const &pdb_field_str)
BigReal reassignTemp
void replica_send(const char *sndbuf, int sendcount, int destPart, int destPE)
int get_alch_lambda(cvm::real *lambda)
Get value of alchemical lambda parameter from back-end.
std::ostream & endi(std::ostream &s)
Definition: InfoStream.C:54
BigReal z
Definition: Vector.h:74
void GridForceGridLoop(T const *g, cvm::atom_iter atom_begin, cvm::atom_iter atom_end, cvm::real *value, cvm::real *atom_field)
Implementation of inner loop; allows for atom list computation and use.
ForceList::const_iterator getGroupTotalForceBegin()
Definition: GlobalMaster.C:250
cvm::step_number previous_NAMD_step
int numGridforceGrids
Definition: Molecule.h:626
SubmitReduction * willSubmit(int setID, int size=-1)
Definition: ReductionMgr.C:368
int update_target_temperature()
Get the target temperature from the NAMD thermostats supported so far.
static int debug
Definition: parm.C:36
static ReductionMgr * Object(void)
Definition: ReductionMgr.h:290
#define iout
Definition: InfoStream.h:51
NAMD_HOST_DEVICE int orthogonal() const
Definition: Lattice.h:273
NodeReduction * reduction
Definition: PatchData.h:139
int num_atoms(void)
Definition: PDB.C:323
void clear()
Definition: ResizeArray.h:91
int add(const Elem &elem)
Definition: ResizeArray.h:101
PositionList::const_iterator getAtomPositionBegin()
Definition: GlobalMaster.C:238
BigReal alchLambda
Molecule stores the structural information for the system.
Definition: Molecule.h:174
NAMD_HOST_DEVICE int b_p() const
Definition: Lattice.h:290
const ResizeArray< AtomIDList > & requestedGroups()
Definition: GlobalMaster.C:189
void request_total_force(bool yesno) override
char outputFilename[NAMD_FILENAME_BUFFER_SIZE]
int setup() override
AtomIDList::const_iterator getForceIdBegin()
Definition: GlobalMaster.C:295
void resize(int i)
Definition: ResizeArray.h:84
int init_volmap_by_name(const char *volmap_name) override
void setall(const Elem &elem)
Definition: ResizeArray.h:94
int check_atom_id(int atom_number) override
#define COLVARPROXY_VERSION
int run_colvar_gradient_callback(std::string const &name, std::vector< const colvarvalue *> const &cvcs, std::vector< cvm::matrix2d< cvm::real > > &gradient) override
#define GLOBAL_MASTER_CKLOOP_CALC_SCRIPTED_BIASES
std::vector< int > atoms_map
Array of atom indices (relative to the colvarproxy arrays), usedfor faster copy of atomic data...
void replica_recv(DataMessage **precvMsg, int srcPart, int srcPE)
int check_atom_name_selections_available() override
Definition: Random.h:37
int index_for_key(const char *key)
void log(std::string const &message) override
e_pdb_field
int request_alch_energy_freq(int const freq)
Request energy computation every freq steps.
IntList::const_iterator getGridObjIndexBegin()
Definition: GlobalMaster.C:258
int get_atom_from_name(const char *segid, int resid, const char *aname) const
Definition: Molecule.C:126
bool accelMDOn
Used to submit restraint energy as MISC.
const AtomID * const_iterator
Definition: ResizeArray.h:38
int backup_file(char const *filename) override
PDBAtom * atom(int place)
Definition: PDB.C:393
BigReal rescaleTemp
int load_atoms_pdb(char const *filename, cvm::atom_group &atoms, std::string const &pdb_field, double const pdb_field_value) override
void init_atoms_map()
Allocate an atoms map with the same size as the NAMD topology.
int flush_output_streams() override
NamdState * state
Definition: Node.h:184
friend class cvm::atom
IntList & modifyRequestedGridObjects()
Definition: GlobalMaster.C:213
BigReal accelMDdV
Definition: Controller.h:116
int reset() override
#define GLOBAL_MASTER_CKLOOP_CALC_BIASES
void replica_comm_barrier() override
BigReal langevinTemp
int send_alch_lambda(void)
Set value of alchemical lambda parameter in back-end.
BigReal x
Definition: Vector.h:74
int check_replicas_enabled() override
int close_output_stream(std::string const &output_name) override
void replica_barrier()
int replica_index() override
NAMD_HOST_DEVICE int a_p() const
Definition: Lattice.h:289
AtomIDList & modifyForcedAtoms()
Definition: GlobalMaster.C:197
int numAtoms
Definition: Molecule.h:586
ResizeArray< AtomIDList > & modifyRequestedGroups()
Definition: GlobalMaster.C:224
int update_group_properties(int index)
void NAMD_die(const char *err_msg)
Definition: common.C:147
int flush_output_stream(std::string const &output_name) override
ForceList & modifyGroupForces()
Definition: GlobalMaster.C:207
Real atommass(int anum) const
Definition: Molecule.h:1114
ConfigList * configList
Definition: Node.h:182
NAMD_HOST_DEVICE Vector b() const
Definition: Lattice.h:269
MGridforceParamsList mgridforcelist
void init_tcl_pointers() override
void update_atom_properties(int index)
void NAMD_backup_file(const char *filename, const char *extension)
Definition: common.C:235
virtual Vector get_scale(void) const =0
char restartFilename[NAMD_FILENAME_BUFFER_SIZE]
Bool set(const char *s)
Definition: Vector.h:261
BigRealList::const_iterator getGridObjValueBegin()
Definition: GlobalMaster.C:266
cvm::rvector position_distance(cvm::atom_pos const &pos1, cvm::atom_pos const &pos2) const
int check_volmap_by_id(int volmap_id) override
IntList::const_iterator getGridObjIndexEnd()
Definition: GlobalMaster.C:262
const Lattice * lattice
Definition: GlobalMaster.h:147
void add_energy(cvm::real energy) override
int get_dE_dlambda(cvm::real *dE_dlambda)
Get energy derivative with respect to lambda.
int close_output_streams() override
unsigned int randomSeed
void requestTotalForce(bool yesno=true)
Definition: GlobalMaster.h:135
char data[1]
Definition: DataExchanger.h:23
StringList * next
Definition: ConfigList.h:49
iterator begin(void)
Definition: ResizeArray.h:36
AtomIDList::const_iterator getAtomIdEnd()
Definition: GlobalMaster.C:234
BigReal getCurrentLambda(const int) const
char * data
Definition: ConfigList.h:48
iterator end(void)
Definition: ResizeArray.h:37
BigReal y
Definition: Vector.h:74
colvarmodule * colvars
Definition: Node.h:187
BigReal tCoupleTemp
int run_colvar_callback(std::string const &name, std::vector< const colvarvalue *> const &cvcs, colvarvalue &value) override
void addReductionEnergy(int reductionTag, BigReal energy)
Definition: GlobalMaster.C:142
GridforceGridType get_grid_type(void)
Definition: GridForceGrid.h:64
int num_replicas() override
const IntList & requestedGridObjs()
Definition: GlobalMaster.C:193
int run_force_callback() override
int replica_comm_recv(char *msg_data, int buf_len, int src_rep) override
StringList * find(const char *name) const
Definition: ConfigList.C:341
NAMD_HOST_DEVICE Vector a() const
Definition: Lattice.h:268
int update_atoms_map(AtomIDList::const_iterator begin, AtomIDList::const_iterator end)
PositionList::const_iterator getGroupPositionBegin()
Definition: GlobalMaster.C:242
Molecule * molecule
Definition: Node.h:179
ForceList::const_iterator getTotalForce()
Definition: GlobalMaster.C:305
BigReal stochRescaleTemp
int load_coords_pdb(char const *filename, std::vector< cvm::atom_pos > &pos, const std::vector< int > &indices, std::string const &pdb_field, double const pdb_field_value) override
BigRealList::const_iterator getGridObjValueEnd()
Definition: GlobalMaster.C:270
NAMD_HOST_DEVICE Vector delta(const Position &pos1, const Position &pos2) const
Definition: Lattice.h:149
BigReal loweAndersenTemp
ForceList::const_iterator getGroupTotalForceEnd()
Definition: GlobalMaster.C:254