Renamed md_openmm into openmm_wrapper, and do_md_openmm into md_openmm.
[gromacs/rigid-bodies.git] / src / kernel / openmm_wrapper.cpp
blobbdf42091723768938148400cb9254d0d76a73035
1 #include <cmath>
2 #include <set>
3 #include <iostream>
4 #include <sstream>
5 #include <fstream>
6 #include <map>
7 #include <vector>
8 #include <cctype>
9 #include <algorithm>
11 using namespace std;
13 #include "OpenMM.h"
15 #include "gmx_fatal.h"
16 #include "typedefs.h"
17 #include "mdrun.h"
18 #include "physics.h"
19 #include "string2.h"
20 #include "gmx_gpu_utils.h"
21 #include "mtop_util.h"
23 #include "openmm_wrapper.h"
25 using namespace OpenMM;
27 #define MEM_ERR_MSG(str) \
28 "The %s-simulation GPU memory test detected errors. As memory errors would cause incorrect " \
29 "simulation results, gromacs has aborted execution.\n Make sure that your GPU's memory is not " \
30 "overclocked and that the device is properly cooled.\n", (str)
32 /** Convert string to type T. */
33 template <class T>
34 static bool from_string(T& t, const string& s, ios_base& (*f)(ios_base&))
36 istringstream iss(s);
37 return !(iss >> f >> t).fail();
40 /**
41 * Split string around a given delimiter.
43 static vector<string> split(const string &s, char delim)
45 vector<string> elems;
46 stringstream ss(s);
47 string item;
48 while(getline(ss, item, delim))
50 if (item.length() != 0)
51 elems.push_back(item);
53 return elems;
56 /**
57 * Split a string "option=value" into "option" and "value" strings.
59 static void split_opt_val(const string &s, string &opt, string &val)
61 size_t eqPos = s.find('=');
62 if (eqPos != string::npos)
64 opt = s.substr(0, eqPos);
65 if (eqPos != s.length()) val = s.substr(eqPos+1);
69 /**
70 * Compare two strings ignoring case.
72 static bool isStringEqNCase(const string s1, const string s2)
74 return (gmx_strncasecmp(s1.c_str(), s2.c_str(), max(s1.length(), s2.length())) == 0);
77 /**
78 * Conver string to upper case.
80 static string toUpper(const string &s)
82 string stmp(s);
83 std::transform(stmp.begin(), stmp.end(), stmp.begin(), static_cast < int(*)(int) > (toupper));
84 return stmp;
87 #define SIZEOF_PLATFORMS 1
88 #define SIZEOF_MEMTESTS 3
89 #define SIZEOF_DEVICEIDS 1
90 #define SIZEOF_FORCE_DEV 2
92 /** Possible options of the mdrun -device parameter. */
93 static const char *devOptStrings[] = { "platform", "deviceid", "memtest", "force-device" };
94 enum devOpt {
95 PLATFORM = 0,
96 DEVICEID = 1,
97 MEMTEST = 2,
98 FORCE_DEVICE = 3
102 * Parse the options string provided to mdrun.
104 class GmxOpenMMPlatformOptions
106 public:
107 GmxOpenMMPlatformOptions(const char *optionString);
108 // GmxOpenMMPlatformOptions(string &optionString);
109 ~GmxOpenMMPlatformOptions() { options.clear(); }
110 string getOptionValue(const string &optName);
111 void remOption(const string &optName) { options.erase(toUpper(optName)); }
112 private:
113 void setOption(const string &opt, const string &val);
114 map<string, string> options;
115 static const char * const platforms[SIZEOF_PLATFORMS];
116 static const char * const memtests[SIZEOF_MEMTESTS];
117 static const char * const deviceid[SIZEOF_DEVICEIDS];
118 static const char * const force_dev[SIZEOF_FORCE_DEV];
121 /* possible values of the parameters that will not be passed to OMM
122 first value will always be the default used in case if the option is not specified */
123 const char * const GmxOpenMMPlatformOptions::platforms[SIZEOF_PLATFORMS] = { "Cuda" /*,"OpenCL"*/ };
124 const char * const GmxOpenMMPlatformOptions::memtests[SIZEOF_MEMTESTS] = { "15", "full", "off" }; /* also valid any positive integer >15 */
125 const char * const GmxOpenMMPlatformOptions::deviceid[SIZEOF_DEVICEIDS] = { "0" }; /* also valid any positive integer */
126 const char * const GmxOpenMMPlatformOptions::force_dev[SIZEOF_FORCE_DEV] = { "no", "yes" };
128 GmxOpenMMPlatformOptions::GmxOpenMMPlatformOptions(const char *optionString)
130 // set default values
131 setOption("platform", platforms[0]);
132 setOption("memtest", memtests[0]);
133 setOption("deviceid", deviceid[0]);
134 setOption("force-device", force_dev[0]);
136 string opt(optionString);
138 // remove all the whitespaces
139 opt.erase(remove_if(opt.begin(), opt.end(), ::isspace), opt.end());
140 // tokenize around ","-s
141 vector<string> tokens = split(opt, ',');
143 for (vector<string>::iterator it = tokens.begin(); it != tokens.end(); ++it)
145 string opt = "", val = "";
146 split_opt_val(*it, opt, val);
148 if (isStringEqNCase(opt, "platform"))
150 setOption(opt, val);
151 continue;
154 if (isStringEqNCase(opt, "memtest"))
156 if (!isStringEqNCase(val, "full") && !isStringEqNCase(val, "off")) /* the value has to be an integer >15 */
158 int secs;
159 if (!from_string<int>(secs, val, std::dec))
161 gmx_fatal(FARGS, "Invalid value for option memtestoption: \"%s\"!\n", val.c_str());
163 if (secs < 15)
165 gmx_fatal(FARGS, "Incorrect value for memtest option (%d). Memtest needs to run for at least 15s!\n", secs);
168 setOption(opt, val);
169 continue;
172 if (isStringEqNCase(opt, "deviceid"))
174 int id;
175 if (!from_string<int>(id, val, std::dec) )
177 gmx_fatal(FARGS, "Invalid device id: \"%s\"!\n", val.c_str());
179 setOption(opt, val);
180 continue;
183 if (isStringEqNCase(opt,"force-device"))
185 if (!isStringEqNCase(val, "yes") && !isStringEqNCase(val, "no"))
187 gmx_fatal(FARGS, "Invalid OpenMM force option: \"%s\"!\n", val.c_str());
189 setOption(opt, val);
190 continue;
193 // if we got till here something went wrong
194 gmx_fatal(FARGS, "Invalid OpenMM platform option: \"%s\"!\n", (*it).c_str());
197 /* cout << "== platform = " << getOptionValue("platform") << endl
198 << "== deviceID = " << getOptionValue("deviceid") << endl
199 << "== memtest = " << getOptionValue("memtest") << endl
200 << "== force = " << getOptionValue("force-device") << endl;
204 string GmxOpenMMPlatformOptions::getOptionValue(const string &optName)
206 if (options.find(toUpper(optName)) != options.end())
208 return options[toUpper(optName)];
210 else
212 return NULL;
216 void GmxOpenMMPlatformOptions::setOption(const string &opt, const string &val)
218 options[toUpper(opt)] = val;
221 class OpenMMData
223 public:
224 System* system;
225 Context* context;
226 Integrator* integrator;
227 bool removeCM;
228 GmxOpenMMPlatformOptions *platformOpt;
231 void run_memtest(FILE* fplog, int devId, const char* pre_post, GmxOpenMMPlatformOptions *opt)
233 // TODO I guess the log entries should go on stdout as well, shouldn't they? [sz]
234 int which_test;
235 int res;
236 const char * test_type = opt->getOptionValue("memtest").c_str();
237 if (!gmx_strcasecmp(test_type, "off"))
239 which_test = 0;
241 else
243 if (!gmx_strcasecmp(test_type, "full"))
245 which_test = 2;
247 else
249 from_string<int>(which_test, test_type, std::dec);
253 switch (which_test)
255 case 0: /* no memtest */
257 sprintf(warn_buf, "%s-simulation GPU memtest skipped. Note, that faulty memory can cause "
258 "incorrect results!\n", pre_post);
259 fprintf(fplog, "%s", warn_buf);
260 warning_note(NULL);
261 break; /* case 0 */
263 case 1: /* quick memtest */
264 fprintf(fplog, "%s-simulation %s GPU memtest in progress...\n", pre_post, test_type);
265 fprintf(stdout, "\n%s-simulation %s GPU memtest in progress...", pre_post, test_type);
266 fflush(fplog); fflush(stdout);
267 if (do_quick_memtest(-1) != 0)
269 gmx_fatal(FARGS,MEM_ERR_MSG(pre_post));
272 else
274 fprintf(fplog, "Memory test completed without errors.\n");
275 fprintf(stdout, "done, no errors detected\n");
277 break; /* case 1 */
279 case 2: /* full memtest */
280 fprintf(fplog, "%s-simulation %s memtest in progress...\n", pre_post, test_type);
281 fprintf(stdout, "\n%s-simulation %s memtest in progress...", pre_post, test_type);
282 fflush(fplog); fflush(stdout);
283 if (do_full_memtest(-1) != 0)
285 gmx_fatal(FARGS, MEM_ERR_MSG(pre_post) );
288 else
290 fprintf(fplog, "Memory test completed without errors.\n");
291 fprintf(stdout, "done, no errors detected\n");
293 break; /* case 2 */
295 default: /* timed memtest */
296 fprintf(fplog, "%s-simulation memtest for ~%ds in progress...\n", pre_post, which_test);
297 fprintf(stdout, "\n%s-simulation memtest for ~%ds in progress...", pre_post, which_test);
298 fflush(fplog); fflush(stdout);
299 if (do_timed_memtest(-1, which_test) != 0)
301 gmx_fatal(FARGS, MEM_ERR_MSG(pre_post));
304 else
306 fprintf(fplog, "Memory test completed without errors.\n");
307 fprintf(stdout, "done, no errors detected.\n");
310 fflush(fplog); fflush(stdout);
313 void checkGmxOptions(t_inputrec *ir, gmx_localtop_t *top)
315 // Abort if unsupported critical options are present
317 /* Integrator */
318 if ( (ir->eI != eiVV) &&
319 (ir->eI != eiVVAK) &&
320 (ir->eI != eiSD1) &&
321 (ir->eI != eiSD2) &&
322 (ir->eI != eiBD)
325 gmx_fatal(FARGS, "** OpenMM Error ** : Unsupported integrator requested."
326 "Available integrators are: md-vv/md-vvak, sd/sd1, and bd. \n");
329 /* Electroctstics */
330 if ((ir->coulombtype != eelPME) && (ir->coulombtype != eelRF) && (ir->coulombtype != eelEWALD))
332 gmx_fatal(FARGS,"** OpenMM Error ** : Unsupported method for electrostatics."
333 "Use NoCutoff (i.e. rcoulomb = rvdw = 0 ),Reaction-Field, Ewald or PME.\n");
336 /* TODO what is this code checking? */
337 if (ir->bPeriodicMols)
338 gmx_fatal(FARGS,"** OpenMM Error ** : Systems with periodic molecules ares not supported\n");
340 if ( (ir->etc != etcNO) && (ir->etc != etcANDERSEN) && (ir->etc != etcANDERSENINTERVAL))
341 gmx_fatal(FARGS,"** OpenMM Error ** : Temperature coupling can be achieved by using either \n\t(1)\t\"md-vv\" or \"md-vvak\" integrators with \"andersen\" or \"andersen-interval\" thermostat, or \n\t(2)\t\"sd\",\"sd1\" or \"bd\" integrators\n");
343 if (ir->opts.ngtc > 1)
344 gmx_fatal(FARGS,"** OpenMM Error ** : Multiple temperature coupling groups are not supported. \n");
346 if (ir->epc != etcNO)
347 gmx_fatal(FARGS,"** OpenMM Error ** : Pressure coupling is not supported. \n");
349 if (ir->opts.annealing[0])
350 gmx_fatal(FARGS,"** OpenMM Error ** : Simulated annealing is not supported. \n");
352 if (ir->eConstrAlg != econtSHAKE)
353 warning_note("** OpenMM Warning ** : Constraints in OpenMM are done by a combination of SHAKE, SETTLE and CCMA. Accuracy is based on the SHAKE tolerance set by the \"shake_tol\" option.");
355 if (ir->nwall != 0)
356 gmx_fatal(FARGS,"** OpenMM Error ** : Walls are not supported.\n");
358 if (ir->ePull != epullNO)
359 gmx_fatal(FARGS,"** OpenMM Error ** : Pulling is not supported.\n");
361 if (top->idef.il[F_DISRES].nr > 0)
362 gmx_fatal(FARGS,"** OpenMM Error ** : Distant restraints are not supported.\n");
364 if (top->idef.il[F_ORIRES].nr > 0)
365 gmx_fatal(FARGS,"** OpenMM Error ** : Orientation restraints are not supported.\n");
367 if (top->idef.il[F_ANGRES].nr > 0)
368 gmx_fatal(FARGS,"** OpenMM Error ** : Angle restraints are not supported.\n");
370 if (top->idef.il[F_DIHRES].nr > 0)
371 gmx_fatal(FARGS,"** OpenMM Error ** : Dihedral restraints are not supported.\n");
373 if (ir->efep != efepNO)
374 gmx_fatal(FARGS,"** OpenMM Error ** : Free energy calculations are not supported.\n");
376 if (ir->opts.ngacc > 1)
377 gmx_fatal(FARGS,"** OpenMM Error ** : Non-equilibrium MD (accelerated groups) are not supported. \n");
379 if (IR_ELEC_FIELD(*ir))
380 gmx_fatal(FARGS,"** OpenMM Error ** : Electric fields are not supported. \n");
382 if (ir->bQMMM)
383 gmx_fatal(FARGS,"** OpenMM Error ** : QMMM calculations are not supported. \n");
385 if (ir->rcoulomb != ir->rvdw)
386 gmx_fatal(FARGS,"** OpenMM Error ** : OpenMM uses a single cutoff for both Coulomb and VdW interactions. Please set rcoulomb equal to rvdw. \n");
391 * Initialize OpenMM, and return a pointer to the OpenMMData object containing the System, Integrator, and Context.
393 void* openmm_init(FILE *fplog, const char *platformOptStr,
394 t_commrec *cr,t_inputrec *ir,
395 gmx_mtop_t *top_global, gmx_localtop_t *top,
396 t_mdatoms *mdatoms, t_forcerec *fr,t_state *state)
398 static bool hasLoadedPlugins = false;
399 string usedPluginDir;
401 try {
402 if (!hasLoadedPlugins)
404 vector<string> loadedPlugins;
405 /* Look for OpenMM plugins at various locations (listed in order of priority):
406 - on the path in OPENMM_PLUGIN_DIR environment variable if this is specified
407 - on the path in the OPENMM_PLUGIN_DIR macro that is set by the build script
408 - at the default location assumed by OpenMM
410 /* env var */
411 char *pluginDir = getenv("OPENMM_PLUGIN_DIR");
412 trim(pluginDir);
413 /* no env var or empty */
414 if (pluginDir != NULL && *pluginDir != '\0')
416 loadedPlugins = Platform::loadPluginsFromDirectory(pluginDir);
417 if (loadedPlugins.size() > 0)
419 hasLoadedPlugins = true;
420 usedPluginDir = pluginDir;
422 else
424 gmx_fatal(FARGS, "The directory provided in the OPENMM_PLUGIN_DIR environment variable "
425 "(%s) does not contain valid OpenMM plugins. Check your OpenMM installation!", pluginDir);
429 /* macro set at build time */
430 #ifdef OPENMM_PLUGIN_DIR
431 if (!hasLoadedPlugins)
433 loadedPlugins = Platform::loadPluginsFromDirectory(OPENMM_PLUGIN_DIR);
434 if (loadedPlugins.size() > 0)
436 hasLoadedPlugins = true;
437 usedPluginDir = OPENMM_PLUGIN_DIR;
440 #endif
441 /* default loocation */
442 if (!hasLoadedPlugins)
444 loadedPlugins = Platform::loadPluginsFromDirectory(Platform::getDefaultPluginsDirectory());
445 if (loadedPlugins.size() > 0)
447 hasLoadedPlugins = true;
448 usedPluginDir = Platform::getDefaultPluginsDirectory();
452 /* if there are still no plugins loaded there won't be any */
453 if (!hasLoadedPlugins)
455 gmx_fatal(FARGS, "No OpenMM plugins were found! You can provide the"
456 " plugin directory in the OPENMM_PLUGIN_DIR environment variable.", pluginDir);
459 fprintf(fplog, "\nPlugins loaded from directory %s:\t", usedPluginDir.c_str());
460 for (int i = 0; i < loadedPlugins.size(); i++)
462 fprintf(fplog, "%s, ", loadedPlugins[i].c_str());
464 fprintf(fplog, "\n");
467 /* parse option string */
468 GmxOpenMMPlatformOptions *opt = new GmxOpenMMPlatformOptions(platformOptStr);
470 /* check wheter Gromacs options compatibility with OpenMM */
471 checkGmxOptions(ir, top);
473 /* check GPU support */
474 char s[1000];
475 is_supported_cuda_gpu(atoi(opt->getOptionValue("deviceid").c_str()), s);
477 // Create the system.
478 const t_idef& idef = top->idef;
479 const int numAtoms = top_global->natoms;
480 const int numConstraints = idef.il[F_CONSTR].nr/3;
481 const int numSettle = idef.il[F_SETTLE].nr/2;
482 const int numBonds = idef.il[F_BONDS].nr/3;
483 const int numAngles = idef.il[F_ANGLES].nr/4;
484 const int numPeriodic = idef.il[F_PDIHS].nr/5;
485 const int numRB = idef.il[F_RBDIHS].nr/5;
486 const int num14 = idef.il[F_LJ14].nr/3;
487 System* sys = new System();
488 if (ir->nstcomm > 0)
489 sys->addForce(new CMMotionRemover(ir->nstcomm));
491 // Set bonded force field terms.
492 const int* bondAtoms = (int*) idef.il[F_BONDS].iatoms;
493 HarmonicBondForce* bondForce = new HarmonicBondForce();
494 sys->addForce(bondForce);
495 int offset = 0;
496 for (int i = 0; i < numBonds; ++i) {
497 int type = bondAtoms[offset++];
498 int atom1 = bondAtoms[offset++];
499 int atom2 = bondAtoms[offset++];
500 bondForce->addBond(atom1, atom2, idef.iparams[type].harmonic.rA, idef.iparams[type].harmonic.krA);
502 const int* angleAtoms = (int*) idef.il[F_ANGLES].iatoms;
503 HarmonicAngleForce* angleForce = new HarmonicAngleForce();
504 sys->addForce(angleForce);
505 offset = 0;
506 for (int i = 0; i < numAngles; ++i) {
507 int type = angleAtoms[offset++];
508 int atom1 = angleAtoms[offset++];
509 int atom2 = angleAtoms[offset++];
510 int atom3 = angleAtoms[offset++];
511 angleForce->addAngle(atom1, atom2, atom3, idef.iparams[type].harmonic.rA*M_PI/180.0, idef.iparams[type].harmonic.krA);
513 const int* periodicAtoms = (int*) idef.il[F_PDIHS].iatoms;
514 PeriodicTorsionForce* periodicForce = new PeriodicTorsionForce();
515 sys->addForce(periodicForce);
516 offset = 0;
517 for (int i = 0; i < numPeriodic; ++i) {
518 int type = periodicAtoms[offset++];
519 int atom1 = periodicAtoms[offset++];
520 int atom2 = periodicAtoms[offset++];
521 int atom3 = periodicAtoms[offset++];
522 int atom4 = periodicAtoms[offset++];
523 periodicForce->addTorsion(atom1, atom2, atom3, atom4, idef.iparams[type].pdihs.mult, idef.iparams[type].pdihs.phiA*M_PI/180.0, idef.iparams[type].pdihs.cpA);
525 const int* rbAtoms = (int*) idef.il[F_RBDIHS].iatoms;
526 RBTorsionForce* rbForce = new RBTorsionForce();
527 sys->addForce(rbForce);
528 offset = 0;
529 for (int i = 0; i < numRB; ++i) {
530 int type = rbAtoms[offset++];
531 int atom1 = rbAtoms[offset++];
532 int atom2 = rbAtoms[offset++];
533 int atom3 = rbAtoms[offset++];
534 int atom4 = rbAtoms[offset++];
535 rbForce->addTorsion(atom1, atom2, atom3, atom4, idef.iparams[type].rbdihs.rbcA[0], idef.iparams[type].rbdihs.rbcA[1],
536 idef.iparams[type].rbdihs.rbcA[2], idef.iparams[type].rbdihs.rbcA[3], idef.iparams[type].rbdihs.rbcA[4], idef.iparams[type].rbdihs.rbcA[5]);
539 // Set nonbonded parameters and masses.
541 int ntypes = fr->ntype;
542 int* types = mdatoms->typeA;
543 real* nbfp = fr->nbfp;
544 real* charges = mdatoms->chargeA;
545 real* masses = mdatoms->massT;
546 NonbondedForce* nonbondedForce = new NonbondedForce();
547 sys->addForce(nonbondedForce);
549 if (ir->rcoulomb == 0)
550 nonbondedForce->setNonbondedMethod(NonbondedForce::NoCutoff);
552 else {
553 switch (ir->coulombtype)
555 case eelRF: // TODO what is the correct condition?
556 if (ir->ePBC == epbcXYZ) {
557 nonbondedForce->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
558 sys->setPeriodicBoxVectors(Vec3(state->box[0][0], 0, 0), Vec3(0, state->box[1][1], 0), Vec3(0, 0, state->box[2][2]));
560 else if (ir->ePBC == epbcNONE)
561 nonbondedForce->setNonbondedMethod(NonbondedForce::CutoffNonPeriodic);
562 else
563 gmx_fatal(FARGS,"** OpenMM Error ** : Only full periodic boundary conditions (pbc = xyz), or none (pbc = no) are supported\n");
564 nonbondedForce->setCutoffDistance(ir->rcoulomb);
565 break;
567 case eelEWALD:
568 if (ir->ewald_geometry == eewg3DC)
569 gmx_fatal(FARGS,"** OpenMM Error ** : Only Ewald 3D geometry is supported\n");
570 if (ir->epsilon_surface != 0)
571 gmx_fatal(FARGS,"** OpenMM Error ** : Dipole correction in Ewald summation is not supported\n");
572 nonbondedForce->setNonbondedMethod(NonbondedForce::Ewald);
573 nonbondedForce->setCutoffDistance(ir->rcoulomb);
574 sys->setPeriodicBoxVectors(Vec3(state->box[0][0], 0, 0), Vec3(0, state->box[1][1], 0), Vec3(0, 0, state->box[2][2]));
575 break;
577 case eelPME:
578 nonbondedForce->setNonbondedMethod(NonbondedForce::PME);
579 nonbondedForce->setCutoffDistance(ir->rcoulomb);
580 sys->setPeriodicBoxVectors(Vec3(state->box[0][0], 0, 0), Vec3(0, state->box[1][1], 0), Vec3(0, 0, state->box[2][2]));
581 break;
583 default:
584 gmx_fatal(FARGS,"Internal error: you should not see this message, it that the"
585 "electrosatics option check failed. Please report this error!");
589 for (int i = 0; i < numAtoms; ++i) {
590 real c6 = nbfp[types[i]*2*ntypes+types[i]*2];
591 real c12 = nbfp[types[i]*2*ntypes+types[i]*2+1];
592 if (c12 <= 0)
593 nonbondedForce->addParticle(charges[i], 1.0, 0.0);
594 else
595 nonbondedForce->addParticle(charges[i], pow(c12/c6, (real) (1.0/6.0)), c6*c6/(4.0*c12));
596 sys->addParticle(masses[i]);
599 // Build a table of all exclusions.
601 vector<set<int> > exclusions(numAtoms);
602 for (int i = 0; i < numAtoms; i++) {
603 int start = top->excls.index[i];
604 int end = top->excls.index[i+1];
605 for (int j = start; j < end; j++)
606 exclusions[i].insert(top->excls.a[j]);
609 // Record the 1-4 interactions, and remove them from the list of exclusions.
611 const int* nb14Atoms = (int*) idef.il[F_LJ14].iatoms;
612 offset = 0;
613 for (int i = 0; i < num14; ++i) {
614 int type = nb14Atoms[offset++];
615 int atom1 = nb14Atoms[offset++];
616 int atom2 = nb14Atoms[offset++];
617 real c6 = idef.iparams[type].lj14.c6A;
618 real c12 = idef.iparams[type].lj14.c12A;
619 real sigma, epsilon;
620 if (c12 <= 0) {
621 epsilon = (real) 0.0;
622 sigma = (real) 1.0;
624 else {
625 epsilon = (real) ((c6*c6)/(4.0*c12));
626 sigma = (real) pow(c12/c6, (real) (1.0/6.0));
628 nonbondedForce->addException(atom1, atom2, fr->fudgeQQ*charges[atom1]*charges[atom2], sigma, epsilon);
629 exclusions[atom1].erase(atom2);
630 exclusions[atom2].erase(atom1);
633 // Record exclusions.
635 for (int i = 0; i < numAtoms; i++) {
636 for (set<int>::const_iterator iter = exclusions[i].begin(); iter != exclusions[i].end(); ++iter) {
637 if (i < *iter)
638 nonbondedForce->addException(i, *iter, 0.0, 1.0, 0.0);
642 // Add GBSA if needed.
643 t_atoms atoms;
644 atoms = gmx_mtop_global_atoms(top_global);
646 if (ir->implicit_solvent == eisGBSA) {
647 GBSAOBCForce* gbsa = new GBSAOBCForce();
648 sys->addForce(gbsa);
649 gbsa->setSoluteDielectric(ir->epsilon_r);
650 gbsa->setSolventDielectric(ir->gb_epsilon_solvent);
651 gbsa->setCutoffDistance(nonbondedForce->getCutoffDistance());
652 if (nonbondedForce->getNonbondedMethod() == NonbondedForce::NoCutoff)
653 gbsa->setNonbondedMethod(GBSAOBCForce::NoCutoff);
654 else if (nonbondedForce->getNonbondedMethod() == NonbondedForce::CutoffNonPeriodic)
655 gbsa->setNonbondedMethod(GBSAOBCForce::CutoffNonPeriodic);
656 else if (nonbondedForce->getNonbondedMethod() == NonbondedForce::CutoffPeriodic)
657 gbsa->setNonbondedMethod(GBSAOBCForce::CutoffPeriodic);
658 else
659 gmx_fatal(FARGS,"** OpenMM Error ** : Only Reaction-Field electrostatics is supported with OBC/GBSA.\n");
661 for (int i = 0; i < numAtoms; ++i)
662 gbsa->addParticle(charges[i],
663 top_global->atomtypes.gb_radius[atoms.atom[i].type],
664 top_global->atomtypes.S_hct[atoms.atom[i].type]);
667 // Set constraints.
669 const int* constraintAtoms = (int*) idef.il[F_CONSTR].iatoms;
670 offset = 0;
671 for (int i = 0; i < numConstraints; ++i) {
672 int type = constraintAtoms[offset++];
673 int atom1 = constraintAtoms[offset++];
674 int atom2 = constraintAtoms[offset++];
675 sys->addConstraint(atom1, atom2, idef.iparams[type].constr.dA);
677 const int* settleAtoms = (int*) idef.il[F_SETTLE].iatoms;
678 offset = 0;
679 for (int i = 0; i < numSettle; ++i) {
680 int type = settleAtoms[offset++];
681 int oxygen = settleAtoms[offset++];
682 sys->addConstraint(oxygen, oxygen+1, idef.iparams[type].settle.doh);
683 sys->addConstraint(oxygen, oxygen+2, idef.iparams[type].settle.doh);
684 sys->addConstraint(oxygen+1, oxygen+2, idef.iparams[type].settle.dhh);
687 // Create an integrator for simulating the system.
689 real friction = (ir->opts.tau_t[0] == 0.0 ? 0.0 : 1.0/ir->opts.tau_t[0]);
690 Integrator* integ;
691 if (ir->eI == eiVV || ir->eI == eiVVAK) {
692 integ = new VerletIntegrator(ir->delta_t);
693 if ( ir->etc == etcANDERSEN) {
694 real collisionFreq = ir->opts.tau_t[0] / 1000; /* tau_t (ps) / 1000 = collisionFreq (fs^-1) */
695 AndersenThermostat* thermostat = new AndersenThermostat(ir->opts.ref_t[0], friction); /* TODO test this */
696 sys->addForce(thermostat);
700 else if (ir->eI == eiBD) {
701 integ = new BrownianIntegrator(ir->opts.ref_t[0], friction, ir->delta_t);
702 static_cast<BrownianIntegrator*>(integ)->setRandomNumberSeed(ir->ld_seed); /* TODO test this */
704 else if (EI_SD(ir->eI)) {
705 integ = new LangevinIntegrator(ir->opts.ref_t[0], friction, ir->delta_t);
706 static_cast<LangevinIntegrator*>(integ)->setRandomNumberSeed(ir->ld_seed); /* TODO test this */
708 else {
709 gmx_fatal(FARGS, "** OpenMM Error ** : Unsupported integrator requested."
710 "Available integrators are: md-vv/md-vvak, sd/sd1, and bd. \n");
713 integ->setConstraintTolerance(ir->shake_tol);
715 // Create a context and initialize it.
716 Context* context = NULL;
717 if (platformOptStr == NULL || platformOptStr == "")
719 context = new Context(*sys, *integ);
721 else
723 // Find which platform is it.
724 for (int i = 0; i < Platform::getNumPlatforms() && context == NULL; i++)
726 if (isStringEqNCase(opt->getOptionValue("platform"), Platform::getPlatform(i).getName()))
728 Platform& platform = Platform::getPlatform(i);
729 // set standard properties
730 platform.setPropertyDefaultValue("CudaDevice", opt->getOptionValue("deviceid"));
731 // TODO add extra properties
732 context = new Context(*sys, *integ, platform);
735 if (context == NULL)
737 gmx_fatal(FARGS, "The requested platform \"%s\" could not be found.\n", opt->getOptionValue("platform").c_str());
741 Platform& platform = context->getPlatform();
742 fprintf(fplog, "Gromacs will use the OpenMM platform: %s\n", platform.getName().c_str());
744 const vector<string>& properties = platform.getPropertyNames();
745 if (debug)
747 for (int i = 0; i < properties.size(); i++)
749 printf(">> %s: %s\n", properties[i].c_str(), platform.getPropertyValue(*context, properties[i]).c_str());
750 fprintf(fplog, ">> %s: %s\n", properties[i].c_str(), platform.getPropertyValue(*context, properties[i]).c_str());
754 int devId;
755 if (!from_string<int>(devId, platform.getPropertyValue(*context, "CudaDevice"), std::dec))
757 gmx_fatal(FARGS, "Internal error: couldn't determine the device selected by OpenMM");
760 /* check GPU compatibility */
761 char gpuname[STRLEN];
762 if (!is_supported_cuda_gpu(-1, gpuname))
764 if (!gmx_strcasecmp(opt->getOptionValue("force-device").c_str(), "yes"))
766 sprintf(warn_buf, "Non-supported GPU selected (#%d, %s), forced continuing.\n"
767 "Note, that the simulation can be slow or it migth even crash.", devId, gpuname);
768 fprintf(fplog, "%s", warn_buf);
769 warning_note(NULL);
771 else
773 gmx_fatal(FARGS, "The selected GPU (#%d, %s) is not supported by Gromacs! "
774 "Most probably you have a low-end GPU which would not perform well, or new hardware that"
775 "has not been tested yet with Gromacs-OpenMM. If you still want to try using this "
776 "device use the force=on option.", devId, gpuname);
779 else
781 fprintf(fplog, "Gromacs will run on the GPU #%d (%s).\n", devId, gpuname);
784 /* do the pre-simulation memtest */
785 run_memtest(fplog, -1, "Pre", opt);
787 vector<Vec3> pos(numAtoms);
788 vector<Vec3> vel(numAtoms);
789 for (int i = 0; i < numAtoms; ++i) {
790 pos[i] = Vec3(state->x[i][0], state->x[i][1], state->x[i][2]);
791 vel[i] = Vec3(state->v[i][0], state->v[i][1], state->v[i][2]);
793 context->setPositions(pos);
794 context->setVelocities(vel);
796 // Return a structure containing the system, integrator, and context.
797 OpenMMData* data = new OpenMMData();
798 data->system = sys;
799 data->integrator = integ;
800 data->context = context;
801 data->removeCM = (ir->nstcomm > 0);
802 data->platformOpt = opt;
803 return data;
805 } catch (std::exception& e) {
806 gmx_fatal(FARGS, "OpenMM exception caught while initializating: %s\n", e.what());
811 * Integrate one step.
813 * @param data the OpenMMData object created by openmm_init().
815 void openmm_take_one_step(void* data)
817 // static int step = 0; printf("----> taking step #%d\n", step++);
818 try {
819 static_cast<OpenMMData*>(data)->integrator->step(1);
820 } catch (std::exception& e) {
821 gmx_fatal(FARGS, "OpenMM exception caught while taking a step: %s\n", e.what());
826 * Integrate n steps.
828 * @param data the OpenMMData object created by openmm_init().
830 void openmm_take_steps(void* data, int nstep)
832 try {
833 static_cast<OpenMMData*>(data)->integrator->step(nstep);
834 } catch (std::exception& e) {
835 gmx_fatal(FARGS, "OpenMM exception caught while taking a step: %s\n", e.what());
840 * Clean up all the data structures used by OpenMM.
842 * @param log file pointer
843 * @param data the OpenMMData object created by openmm_init().
845 void openmm_cleanup(FILE* fplog, void* data)
847 OpenMMData* d = static_cast<OpenMMData*>(data);
848 run_memtest(fplog, -1, "Post", d->platformOpt);
849 delete d->system;
850 delete d->integrator;
851 delete d->context;
852 delete d->platformOpt;
853 delete d;
857 * Copy the current state information (positions, velocities, and forces) into the data structures used
858 * by Gromacs.
860 * @param data the OpenMMData object created by openmm_init().
862 void openmm_copy_state(void *data,
863 t_state *state, double *time,
864 rvec f[], gmx_enerdata_t *enerd,
865 bool includePos, bool includeVel, bool includeForce, bool includeEnergy)
867 int types = 0;
868 if (includePos)
869 types += State::Positions;
870 if (includeVel)
871 types += State::Velocities;
872 if (includeForce)
873 types += State::Forces;
874 if (includeEnergy)
875 types += State::Energy;
876 if (types == 0)
877 return;
878 try {
879 State currentState = static_cast<OpenMMData*>(data)->context->getState(types);
880 int numAtoms = static_cast<OpenMMData*>(data)->system->getNumParticles();
881 if (includePos) {
882 for (int i = 0; i < numAtoms; i++) {
883 Vec3 x = currentState.getPositions()[i];
884 state->x[i][0] = x[0];
885 state->x[i][1] = x[1];
886 state->x[i][2] = x[2];
889 if (includeVel) {
890 for (int i = 0; i < numAtoms; i++) {
891 Vec3 v = currentState.getVelocities()[i];
892 state->v[i][0] = v[0];
893 state->v[i][1] = v[1];
894 state->v[i][2] = v[2];
897 if (includeForce) {
898 for (int i = 0; i < numAtoms; i++) {
899 Vec3 force = currentState.getForces()[i];
900 f[i][0] = force[0];
901 f[i][1] = force[1];
902 f[i][2] = force[2];
905 if (includeEnergy) {
906 int numConstraints = static_cast<OpenMMData*>(data)->system->getNumConstraints();
907 int dof = 3*numAtoms-numConstraints;
908 if (static_cast<OpenMMData*>(data)->removeCM)
909 dof -= 3;
910 enerd->term[F_EPOT] = currentState.getPotentialEnergy();
911 enerd->term[F_EKIN] = currentState.getKineticEnergy();
912 enerd->term[F_ETOT] = enerd->term[F_EPOT] + enerd->term[F_EKIN];
913 enerd->term[F_TEMP] = 2.0*enerd->term[F_EKIN]/dof/BOLTZ;
915 *time = currentState.getTime();
916 } catch (std::exception& e) {
917 gmx_fatal(FARGS, "OpenMM exception caught while retrieving state information: %s\n", e.what());