added gromacs file headers to the OpenMM-related files
[gromacs/rigid-bodies.git] / src / kernel / openmm_wrapper.cpp
blobba78c5b3f54ab0e62ebb15c6ddef20d7ae5b5420
1 /* -*- mode: c; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; c-file-style: "stroustrup"; -*-
3 *
4 * This source code is part of
5 *
6 * G R O M A C S
7 *
8 * GROningen MAchine for Chemical Simulations
9 *
10 * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
11 * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
12 * Copyright (c) 2001-2010, The GROMACS development team,
13 * check out http://www.gromacs.org for more information.
15 * This program is free software; you can redistribute it and/or
16 * modify it under the terms of the GNU General Public License
17 * as published by the Free Software Foundation; either version 2
18 * of the License, or (at your option) any later version.
20 * If you want to redistribute modifications, please consider that
21 * scientific software is very special. Version control is crucial -
22 * bugs must be traceable. We will be happy to consider code for
23 * inclusion in the official distribution, but derived work must not
24 * be called official GROMACS. Details are found in the README & COPYING
25 * files - if they are missing, get the official version at www.gromacs.org.
27 * To help us fund GROMACS development, we humbly ask that you cite
28 * the papers on the package - you can find them in the top README file.
30 * For more info, check our website at http://www.gromacs.org
32 * And Hey:
33 * Gallium Rubidium Oxygen Manganese Argon Carbon Silicon
37 * Note that parts of this source code originate from the Simtk release
38 * of OpenMM accelerated Gromacs, for more details see:
39 * https://simtk.org/project/xml/downloads.xml?group_id=161#package_id600
42 #include <cmath>
43 #include <set>
44 #include <iostream>
45 #include <sstream>
46 #include <fstream>
47 #include <map>
48 #include <vector>
49 #include <cctype>
50 #include <algorithm>
52 using namespace std;
54 #include "OpenMM.h"
56 #include "gmx_fatal.h"
57 #include "typedefs.h"
58 #include "mdrun.h"
59 #include "physics.h"
60 #include "string2.h"
61 #include "gmx_gpu_utils.h"
62 #include "mtop_util.h"
63 #include "warninp.h"
65 #include "openmm_wrapper.h"
67 using namespace OpenMM;
69 /*! \cond */
70 #define MEM_ERR_MSG(str) \
71 "The %s-simulation GPU memory test detected errors. As memory errors would cause incorrect " \
72 "simulation results, gromacs has aborted execution.\n Make sure that your GPU's memory is not " \
73 "overclocked and that the device is properly cooled.\n", (str)
74 /*! \endcond */
76 /*!
77 * \brief Convert string to integer type.
78 * \param[in] s String to convert from.
79 * \param[in] f Basefield format flag that takes any of the following I/O
80 * manipulators: dec, hex, oct.
81 * \param[out] t Destination variable to convert to.
83 template <class T>
84 static bool from_string(T& t, const string& s, ios_base& (*f)(ios_base&))
86 istringstream iss(s);
87 return !(iss >> f >> t).fail();
90 /*!
91 * \brief Split string around a given delimiter.
92 * \param[in] s String to split.
93 * \param[in] delim Delimiter character.
94 * \returns Vector of strings found in \p s.
96 static vector<string> split(const string &s, char delim)
98 vector<string> elems;
99 stringstream ss(s);
100 string item;
101 while (getline(ss, item, delim))
103 if (item.length() != 0)
104 elems.push_back(item);
106 return elems;
110 * \brief Split a string of the form "option=value" into "option" and "value" strings.
111 * This string corresponds to one option and the associated value from the option list
112 * in the mdrun -device argument.
114 * \param[in] s A string containing an "option=value" pair that needs to be split up.
115 * \param[out] opt The name of the option.
116 * \param[out] val Value of the option.
118 static void splitOptionValue(const string &s, string &opt, string &val)
120 size_t eqPos = s.find('=');
121 if (eqPos != string::npos)
123 opt = s.substr(0, eqPos);
124 if (eqPos != s.length()) val = s.substr(eqPos+1);
129 * \brief Compare two strings ignoring case.
130 * This function is in fact a wrapper around the gromacs function gmx_strncasecmp().
131 * \param[in] s1 String.
132 * \param[in] s2 String.
133 * \returns Similarly to the C function strncasecmp(), the return value is an
134 integer less than, equal to, or greater than 0 if \p s1 less than,
135 identical to, or greater than \p s2.
137 static bool isStringEqNCase(const string s1, const string s2)
139 return (gmx_strncasecmp(s1.c_str(), s2.c_str(), max(s1.length(), s2.length())) == 0);
143 * \brief Convert string to upper case.
145 * \param[in] s String to convert to uppercase.
146 * \returns The given string converted to uppercase.
148 static string toUpper(const string &s)
150 string stmp(s);
151 std::transform(stmp.begin(), stmp.end(), stmp.begin(), static_cast < int(*)(int) > (toupper));
152 return stmp;
155 /*!
156 \name Sizes of constant device option arrays GmxOpenMMPlatformOptions#platforms,
157 GmxOpenMMPlatformOptions#memtests, GmxOpenMMPlatformOptions#deviceid,
158 GmxOpenMMPlatformOptions#force_dev. */
159 /* {@ */
160 #define SIZEOF_PLATFORMS 1 // 2
161 #define SIZEOF_MEMTESTS 3
162 #define SIZEOF_DEVICEIDS 1
163 #define SIZEOF_FORCE_DEV 2
164 /* @} */
166 /*! Possible platform options in the mdrun -device option. */
167 static const char *devOptStrings[] = { "platform", "deviceid", "memtest", "force-device" };
169 /*! Enumerated platform options in the mdrun -device option. */
170 enum devOpt
172 PLATFORM = 0,
173 DEVICEID = 1,
174 MEMTEST = 2,
175 FORCE_DEVICE = 3
179 * \brief Class to extract and manage the platform options in the mdrun -device option.
182 class GmxOpenMMPlatformOptions
184 public:
185 GmxOpenMMPlatformOptions(const char *opt);
186 ~GmxOpenMMPlatformOptions() { options.clear(); }
187 string getOptionValue(const string &opt);
188 void remOption(const string &opt);
189 private:
190 void setOption(const string &opt, const string &val);
192 map<string, string> options; /*!< Data structure to store the option (name, value) pairs. */
194 static const char * const platforms[SIZEOF_PLATFORMS]; /*!< Available OpenMM platforms; size #SIZEOF_PLATFORMS */
195 static const char * const memtests[SIZEOF_MEMTESTS]; /*!< Available types of memory tests, also valid
196 any positive integer >=15; size #SIZEOF_MEMTESTS */
197 static const char * const deviceid[SIZEOF_DEVICEIDS]; /*!< Possible values for deviceid option;
198 also valid any positive integer; size #SIZEOF_DEVICEIDS */
199 static const char * const force_dev[SIZEOF_FORCE_DEV]; /*!< Possible values for for force-device option;
200 size #SIZEOF_FORCE_DEV */
203 const char * const GmxOpenMMPlatformOptions::platforms[SIZEOF_PLATFORMS]
204 = {"CUDA"};
205 //= { "Reference", "CUDA" /*,"OpenCL"*/ };
206 const char * const GmxOpenMMPlatformOptions::memtests[SIZEOF_MEMTESTS]
207 = { "15", "full", "off" };
208 const char * const GmxOpenMMPlatformOptions::deviceid[SIZEOF_DEVICEIDS]
209 = { "0" };
210 const char * const GmxOpenMMPlatformOptions::force_dev[SIZEOF_FORCE_DEV]
211 = { "no", "yes" };
214 * \brief Contructor.
215 * Takes the option list, parses it, checks the options and their values for validity.
216 * When certain options are not provided by the user, as default value the first item
217 * of the respective constant array is taken (GmxOpenMMPlatformOptions#platforms,
218 * GmxOpenMMPlatformOptions#memtests, GmxOpenMMPlatformOptions#deviceid,
219 * GmxOpenMMPlatformOptions#force_dev).
220 * \param[in] optionString Option string part of the mdrun -deviceoption parameter.
222 GmxOpenMMPlatformOptions::GmxOpenMMPlatformOptions(const char *optionString)
224 // set default values
225 setOption("platform", platforms[0]);
226 setOption("memtest", memtests[0]);
227 setOption("deviceid", deviceid[0]);
228 setOption("force-device", force_dev[0]);
230 string opt(optionString);
232 // remove all whitespaces
233 opt.erase(remove_if(opt.begin(), opt.end(), ::isspace), opt.end());
234 // tokenize around ","-s
235 vector<string> tokens = split(opt, ',');
237 for (vector<string>::iterator it = tokens.begin(); it != tokens.end(); ++it)
239 string opt = "", val = "";
240 splitOptionValue(*it, opt, val);
242 if (isStringEqNCase(opt, "platform"))
244 /* no check, this will fail if platform does not exist when we try to set it */
245 setOption(opt, val);
246 continue;
249 if (isStringEqNCase(opt, "memtest"))
251 /* the value has to be an integer >15(s) or "full" OR "off" */
252 if (!isStringEqNCase(val, "full") && !isStringEqNCase(val, "off"))
254 int secs;
255 if (!from_string<int>(secs, val, std::dec))
257 gmx_fatal(FARGS, "Invalid value for option memtest option: \"%s\"!", val.c_str());
259 if (secs < 15)
261 gmx_fatal(FARGS, "Incorrect value for memtest option (%d). "
262 "Memtest needs to run for at least 15s!", secs);
265 setOption(opt, val);
266 continue;
269 if (isStringEqNCase(opt, "deviceid"))
271 int id;
272 if (!from_string<int>(id, val, std::dec) )
274 gmx_fatal(FARGS, "Invalid device id: \"%s\"!", val.c_str());
276 setOption(opt, val);
277 continue;
280 if (isStringEqNCase(opt, "force-device"))
282 /* */
283 if (!isStringEqNCase(val, "yes") && !isStringEqNCase(val, "no"))
285 gmx_fatal(FARGS, "Invalid OpenMM force option: \"%s\"!", val.c_str());
287 setOption(opt, val);
288 continue;
291 // if we got till here something went wrong
292 gmx_fatal(FARGS, "Invalid OpenMM platform option: \"%s\"!", (*it).c_str());
298 * \brief Returns the value of an option.
299 * \param[in] opt Name of the option.
301 string GmxOpenMMPlatformOptions::getOptionValue(const string &opt)
303 if (options.find(toUpper(opt)) != options.end())
305 return options[toUpper(opt)];
307 else
309 return NULL;
314 * \brief Setter function - private, only used from contructor.
315 * \param[in] opt Name of the option.
316 * \param[in] val Value for the option.
318 void GmxOpenMMPlatformOptions::setOption(const string &opt, const string &val)
320 options[toUpper(opt)] = val;
324 * \brief Removes an option with its value from the map structure. If the option
325 * does not exist, returns without any action.
326 * \param[in] opt Name of the option.
328 void GmxOpenMMPlatformOptions::remOption(const string &opt)
330 options.erase(toUpper(opt));
335 * \brief Container for OpenMM related data structures that represent the bridge
336 * between the Gromacs data-structures and the OpenMM library and is but it's
337 * only passed through the API functions as void to disable direct access.
339 class OpenMMData
341 public:
342 System* system; /*! The system to simulate. */
343 Context* context; /*! The OpenMM context in which the simulation is carried out. */
344 Integrator* integrator; /*! The integrator used in the simulation. */
345 bool removeCM; /*! If \true remove venter of motion, false otherwise. */
346 GmxOpenMMPlatformOptions *platformOpt; /*! Platform options. */
350 * \brief Runs memtest on the GPU that has alreaby been initialized by OpenMM.
351 * \param[in] fplog Pointer to gromacs log file.
352 * \param[in] devId Device id of the GPU to run the test on. TODO: this can be removed!
353 * \param[in] pre_post Contains either "Pre" or "Post" just to be able to differentiate in
354 * stdout messages/log between memtest carried out before and after simulation.
355 * \param[in] opt Pointer to platform options object.
357 static void runMemtest(FILE* fplog, int devId, const char* pre_post, GmxOpenMMPlatformOptions *opt)
360 char warn_buf[STRLEN];
362 int which_test;
363 int res;
364 const char * test_type = opt->getOptionValue("memtest").c_str();
365 if (!gmx_strcasecmp(test_type, "off"))
367 which_test = 0;
369 else
371 if (!gmx_strcasecmp(test_type, "full"))
373 which_test = 2;
375 else
377 from_string<int>(which_test, test_type, std::dec);
381 switch (which_test)
383 case 0: /* no memtest */
384 sprintf(warn_buf, "%s-simulation GPU memtest skipped. Note, that faulty memory can cause "
385 "incorrect results!", pre_post);
386 fprintf(fplog, "%s\n", warn_buf);
387 gmx_warning(warn_buf);
388 break; /* case 0 */
390 case 1: /* quick memtest */
391 fprintf(fplog, "%s-simulation %s GPU memtest in progress...\n", pre_post, test_type);
392 fprintf(stdout, "\n%s-simulation %s GPU memtest in progress...", pre_post, test_type);
393 fflush(fplog);
394 fflush(stdout);
395 if (do_quick_memtest(-1) != 0)
397 gmx_fatal(FARGS,MEM_ERR_MSG(pre_post));
399 else
401 fprintf(fplog, "Memory test completed without errors.\n");
402 fprintf(stdout, "done, no errors detected\n");
404 break; /* case 1 */
406 case 2: /* full memtest */
407 fprintf(fplog, "%s-simulation %s memtest in progress...\n", pre_post, test_type);
408 fprintf(stdout, "\n%s-simulation %s memtest in progress...", pre_post, test_type);
409 fflush(fplog);
410 fflush(stdout);
411 if (do_full_memtest(-1) != 0)
413 gmx_fatal(FARGS, MEM_ERR_MSG(pre_post) );
416 else
418 fprintf(fplog, "Memory test completed without errors.\n");
419 fprintf(stdout, "done, no errors detected\n");
421 break; /* case 2 */
423 default: /* timed memtest */
424 fprintf(fplog, "%s-simulation memtest for ~%ds in progress...\n", pre_post, which_test);
425 fprintf(stdout, "\n%s-simulation memtest for ~%ds in progress...", pre_post, which_test);
426 fflush(fplog);
427 fflush(stdout);
428 if (do_timed_memtest(-1, which_test) != 0)
430 gmx_fatal(FARGS, MEM_ERR_MSG(pre_post));
433 else
435 fprintf(fplog, "Memory test completed without errors.\n");
436 fprintf(stdout, "done, no errors detected.\n");
439 fflush(fplog);
440 fflush(stdout);
444 * \brief Does gromacs option checking.
446 * Checks the gromacs mdp options for features unsupported in OpenMM, case in which
447 * interrupts the execution. It also warns the user about pecularities of OpenMM
448 * implementations.
449 * \param[in] ir Gromacs structure for input options, \see ::t_inputrec
450 * \param[in] top Gromacs local topology, \see ::gmx_localtop_t
451 * \param[in] state Gromacs state structure \see ::t_state
453 static void checkGmxOptions(t_inputrec *ir, gmx_localtop_t *top, t_state *state)
455 char warn_buf[STRLEN];
457 /* Abort if unsupported critical options are present */
459 /* Integrator */
460 if (ir->eI == eiMD)
462 gmx_warning( "OpenMM does not support leap-frog, will use velocity-verlet integrator.");
465 if ( (ir->eI != eiMD) &&
466 (ir->eI != eiVV) &&
467 (ir->eI != eiVVAK) &&
468 (ir->eI != eiSD1) &&
469 (ir->eI != eiSD2) &&
470 (ir->eI != eiBD) )
472 gmx_fatal(FARGS, "OpenMM supports only the following integrators: md/md-vv/md-vv-avek, sd/sd1, and bd.");
475 /* Electroctstics */
476 if ( (ir->coulombtype != eelPME) &&
477 (ir->coulombtype != eelRF) &&
478 (ir->coulombtype != eelEWALD) &&
479 // no-cutoff
480 ( !(ir->coulombtype == eelCUT && ir->rcoulomb == 0 && ir->rvdw == 0)) )
482 gmx_fatal(FARGS,"OpenMM supports only the following methods for electrostatics: "
483 "NoCutoff (i.e. rcoulomb = rvdw = 0 ),Reaction-Field, Ewald or PME.");
486 if (ir->etc != etcNO &&
487 ir->eI != eiSD1 &&
488 ir->eI != eiSD2 &&
489 ir->eI != eiBD )
491 gmx_warning("OpenMM supports only Andersen thermostat with the md/md-vv/md-vv-avek integrators.");
494 if (ir->opts.ngtc > 1)
495 gmx_fatal(FARGS,"OpenMM does not support multiple temperature coupling groups.");
497 if (ir->epc != etcNO)
498 gmx_fatal(FARGS,"OpenMM does not support pressure coupling.");
500 if (ir->opts.annealing[0])
501 gmx_fatal(FARGS,"OpenMM does not support simulated annealing.");
503 if (ir->eConstrAlg != econtSHAKE)
504 gmx_warning("OpenMM provides contraints as a combination "
505 "of SHAKE, SETTLE and CCMA. Accuracy is based on the SHAKE tolerance set "
506 "by the \"shake_tol\" option.");
508 if (ir->nwall != 0)
509 gmx_fatal(FARGS,"OpenMM does not support walls.");
511 if (ir->ePull != epullNO)
512 gmx_fatal(FARGS,"OpenMM does not support pulling.");
514 /* check for restraints */
515 int i;
516 for (i = 0; i < F_EPOT; i++)
518 if (i != F_CONSTR &&
519 i != F_SETTLE &&
520 i != F_BONDS &&
521 i != F_ANGLES &&
522 i != F_PDIHS &&
523 i != F_RBDIHS &&
524 i != F_LJ14 &&
525 top->idef.il[i].nr > 0)
527 /* TODO fix the message */
528 gmx_fatal(FARGS, "OpenMM does not support (some) of the provided restaint(s).");
532 if (ir->efep != efepNO)
533 gmx_fatal(FARGS,"OpenMM does not support free energy calculations.");
535 if (ir->opts.ngacc > 1)
536 gmx_fatal(FARGS,"OpenMM does not support non-equilibrium MD (accelerated groups).");
538 if (IR_ELEC_FIELD(*ir))
539 gmx_fatal(FARGS,"OpenMM does not support electric fields.");
541 if (ir->bQMMM)
542 gmx_fatal(FARGS,"OpenMM does not support QMMM calculations.");
544 if (ir->rcoulomb != ir->rvdw)
545 gmx_fatal(FARGS,"OpenMM uses a single cutoff for both Coulomb "
546 "and VdW interactions. Please set rcoulomb equal to rvdw.");
548 if (EEL_FULL(ir->coulombtype))
550 if (ir->ewald_geometry == eewg3DC)
551 gmx_fatal(FARGS,"OpenMM supports only Ewald 3D geometry.");
552 if (ir->epsilon_surface != 0)
553 gmx_fatal(FARGS,"OpenMM does not support dipole correction in Ewald summation.");
556 if (TRICLINIC(state->box))
558 gmx_fatal(FARGS,"OpenMM does not support triclinic unit cells.");
565 * \brief Convert Lennard-Jones parameters c12 and c6 to sigma and epsilon.
567 * \param[in] c12
568 * \param[in] c6
569 * \param[out] sigma
570 * \param[out] epsilon
572 static void* convert_c_12_6(double c12, double c6, double *sigma, double *epsilon)
574 if (c12 == 0 && c6 == 0)
576 *epsilon = 0.0;
577 *sigma = 1.0;
579 else if (c12 > 0 && c6 > 0)
581 *epsilon = (c6*c6)/(4.0*c12);
582 *sigma = pow(c12/c6, 1.0/6.0);
584 else
586 gmx_fatal(FARGS,"OpenMM does only supports c6 > 0 and c12 > 0 or both 0.");
591 * \brief Initialize OpenMM, run sanity/consistency checks, and return a pointer to
592 * the OpenMMData.
594 * Various gromacs data structures are passed that contain the parameters, state and
595 * other porperties of the system to simulate. These serve as input for initializing
596 * OpenMM. Besides, a set of misc action are taken:
597 * - OpenMM plugins are loaded;
598 * - platform options in \p platformOptStr are parsed and checked;
599 * - Gromacs parameters are checked for OpenMM support and consistency;
600 * - after the OpenMM is initialized memtest executed in the same GPU context.
602 * \param[in] fplog Gromacs log file handler.
603 * \param[in] platformOptStr Platform option string.
604 * \param[in] ir The Gromacs input parameters, see ::t_inputrec
605 * \param[in] top_global TODO Gromacs whole system toppology, \see ::gmx_mtop_t
606 * \param[in] top TODO Gromacs node local topology, \see gmx_localtop_t
607 * \param[in] mdatoms TODO \see ::t_mdatoms
608 * \param[in] fr TODO \see ::t_forcerec
609 * \param[in] state TODO \see ::t_state
612 void* openmm_init(FILE *fplog, const char *platformOptStr,
613 t_inputrec *ir,
614 gmx_mtop_t *top_global, gmx_localtop_t *top,
615 t_mdatoms *mdatoms, t_forcerec *fr, t_state *state)
618 char warn_buf[STRLEN];
619 static bool hasLoadedPlugins = false;
620 string usedPluginDir;
621 int devId;
625 if (!hasLoadedPlugins)
627 vector<string> loadedPlugins;
628 /* Look for OpenMM plugins at various locations (listed in order of priority):
629 - on the path in OPENMM_PLUGIN_DIR environment variable if this is specified
630 - on the path in the OPENMM_PLUGIN_DIR macro that is set by the build script
631 - at the default location assumed by OpenMM
633 /* env var */
634 char *pluginDir = getenv("OPENMM_PLUGIN_DIR");
635 trim(pluginDir);
636 /* no env var or empty */
637 if (pluginDir != NULL && *pluginDir != '\0')
639 loadedPlugins = Platform::loadPluginsFromDirectory(pluginDir);
640 if (loadedPlugins.size() > 0)
642 hasLoadedPlugins = true;
643 usedPluginDir = pluginDir;
645 else
647 gmx_fatal(FARGS, "The directory provided in the OPENMM_PLUGIN_DIR environment variable "
648 "(%s) does not contain valid OpenMM plugins. Check your OpenMM installation!",
649 pluginDir);
653 /* macro set at build time */
654 #ifdef OPENMM_PLUGIN_DIR
655 if (!hasLoadedPlugins)
657 loadedPlugins = Platform::loadPluginsFromDirectory(OPENMM_PLUGIN_DIR);
658 if (loadedPlugins.size() > 0)
660 hasLoadedPlugins = true;
661 usedPluginDir = OPENMM_PLUGIN_DIR;
664 #endif
665 /* default loocation */
666 if (!hasLoadedPlugins)
668 loadedPlugins = Platform::loadPluginsFromDirectory(Platform::getDefaultPluginsDirectory());
669 if (loadedPlugins.size() > 0)
671 hasLoadedPlugins = true;
672 usedPluginDir = Platform::getDefaultPluginsDirectory();
676 /* if there are still no plugins loaded there won't be any */
677 if (!hasLoadedPlugins)
679 gmx_fatal(FARGS, "No OpenMM plugins were found! You can provide the"
680 " plugin directory in the OPENMM_PLUGIN_DIR environment variable.", pluginDir);
683 fprintf(fplog, "\nPlugins loaded from directory %s:\t", usedPluginDir.c_str());
684 for (int i = 0; i < loadedPlugins.size(); i++)
686 fprintf(fplog, "%s, ", loadedPlugins[i].c_str());
688 fprintf(fplog, "\n");
691 /* parse option string */
692 GmxOpenMMPlatformOptions *opt = new GmxOpenMMPlatformOptions(platformOptStr);
694 /* check wheter Gromacs options compatibility with OpenMM */
695 checkGmxOptions(ir, top, state);
697 // Create the system.
698 const t_idef& idef = top->idef;
699 const int numAtoms = top_global->natoms;
700 const int numConstraints = idef.il[F_CONSTR].nr/3;
701 const int numSettle = idef.il[F_SETTLE].nr/2;
702 const int numBonds = idef.il[F_BONDS].nr/3;
703 const int numAngles = idef.il[F_ANGLES].nr/4;
704 const int numPeriodic = idef.il[F_PDIHS].nr/5;
705 const int numRB = idef.il[F_RBDIHS].nr/5;
706 const int num14 = idef.il[F_LJ14].nr/3;
707 System* sys = new System();
708 if (ir->nstcomm > 0)
709 sys->addForce(new CMMotionRemover(ir->nstcomm));
711 // Set bonded force field terms.
712 const int* bondAtoms = (int*) idef.il[F_BONDS].iatoms;
713 HarmonicBondForce* bondForce = new HarmonicBondForce();
714 sys->addForce(bondForce);
715 int offset = 0;
716 for (int i = 0; i < numBonds; ++i)
718 int type = bondAtoms[offset++];
719 int atom1 = bondAtoms[offset++];
720 int atom2 = bondAtoms[offset++];
721 bondForce->addBond(atom1, atom2,
722 idef.iparams[type].harmonic.rA, idef.iparams[type].harmonic.krA);
724 const int* angleAtoms = (int*) idef.il[F_ANGLES].iatoms;
725 HarmonicAngleForce* angleForce = new HarmonicAngleForce();
726 sys->addForce(angleForce);
727 offset = 0;
728 for (int i = 0; i < numAngles; ++i)
730 int type = angleAtoms[offset++];
731 int atom1 = angleAtoms[offset++];
732 int atom2 = angleAtoms[offset++];
733 int atom3 = angleAtoms[offset++];
734 angleForce->addAngle(atom1, atom2, atom3,
735 idef.iparams[type].harmonic.rA*M_PI/180.0, idef.iparams[type].harmonic.krA);
737 const int* periodicAtoms = (int*) idef.il[F_PDIHS].iatoms;
738 PeriodicTorsionForce* periodicForce = new PeriodicTorsionForce();
739 sys->addForce(periodicForce);
740 offset = 0;
741 for (int i = 0; i < numPeriodic; ++i)
743 int type = periodicAtoms[offset++];
744 int atom1 = periodicAtoms[offset++];
745 int atom2 = periodicAtoms[offset++];
746 int atom3 = periodicAtoms[offset++];
747 int atom4 = periodicAtoms[offset++];
748 periodicForce->addTorsion(atom1, atom2, atom3, atom4,
749 idef.iparams[type].pdihs.mult,
750 idef.iparams[type].pdihs.phiA*M_PI/180.0,
751 idef.iparams[type].pdihs.cpA);
753 const int* rbAtoms = (int*) idef.il[F_RBDIHS].iatoms;
754 RBTorsionForce* rbForce = new RBTorsionForce();
755 sys->addForce(rbForce);
756 offset = 0;
757 for (int i = 0; i < numRB; ++i)
759 int type = rbAtoms[offset++];
760 int atom1 = rbAtoms[offset++];
761 int atom2 = rbAtoms[offset++];
762 int atom3 = rbAtoms[offset++];
763 int atom4 = rbAtoms[offset++];
764 rbForce->addTorsion(atom1, atom2, atom3, atom4,
765 idef.iparams[type].rbdihs.rbcA[0], idef.iparams[type].rbdihs.rbcA[1],
766 idef.iparams[type].rbdihs.rbcA[2], idef.iparams[type].rbdihs.rbcA[3],
767 idef.iparams[type].rbdihs.rbcA[4], idef.iparams[type].rbdihs.rbcA[5]);
770 // Set nonbonded parameters and masses.
771 int ntypes = fr->ntype;
772 int* types = mdatoms->typeA;
773 real* nbfp = fr->nbfp;
774 real* charges = mdatoms->chargeA;
775 real* masses = mdatoms->massT;
776 NonbondedForce* nonbondedForce = new NonbondedForce();
777 sys->addForce(nonbondedForce);
779 switch (ir->ePBC)
781 case epbcNONE:
782 if (ir->rcoulomb == 0)
784 nonbondedForce->setNonbondedMethod(NonbondedForce::NoCutoff);
786 else
788 nonbondedForce->setNonbondedMethod(NonbondedForce::CutoffNonPeriodic);
790 break;
791 case epbcXYZ:
792 switch (ir->coulombtype)
794 case eelRF:
795 nonbondedForce->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
796 break;
798 case eelEWALD:
799 nonbondedForce->setNonbondedMethod(NonbondedForce::Ewald);
800 break;
802 case eelPME:
803 nonbondedForce->setNonbondedMethod(NonbondedForce::PME);
804 break;
806 default:
807 gmx_fatal(FARGS,"Internal error: you should not see this message, it that the"
808 "electrosatics option check failed. Please report this error!");
810 sys->setPeriodicBoxVectors(Vec3(state->box[0][0], 0, 0),
811 Vec3(0, state->box[1][1], 0), Vec3(0, 0, state->box[2][2]));
812 nonbondedForce->setCutoffDistance(ir->rcoulomb);
814 break;
815 default:
816 gmx_fatal(FARGS,"OpenMM supports only full periodic boundary conditions "
817 "(pbc = xyz), or none (pbc = no).");
820 for (int i = 0; i < numAtoms; ++i)
822 double c12 = nbfp[types[i]*2*ntypes+types[i]*2+1];
823 double c6 = nbfp[types[i]*2*ntypes+types[i]*2];
824 double sigma, epsilon;
825 convert_c_12_6(c12, c6, &sigma, &epsilon);
826 nonbondedForce->addParticle(charges[i], sigma, epsilon);
827 sys->addParticle(masses[i]);
830 // Build a table of all exclusions.
831 vector<set<int> > exclusions(numAtoms);
832 for (int i = 0; i < numAtoms; i++)
834 int start = top->excls.index[i];
835 int end = top->excls.index[i+1];
836 for (int j = start; j < end; j++)
837 exclusions[i].insert(top->excls.a[j]);
840 // Record the 1-4 interactions, and remove them from the list of exclusions.
841 const int* nb14Atoms = (int*) idef.il[F_LJ14].iatoms;
842 offset = 0;
843 for (int i = 0; i < num14; ++i)
845 int type = nb14Atoms[offset++];
846 int atom1 = nb14Atoms[offset++];
847 int atom2 = nb14Atoms[offset++];
848 double sigma, epsilon;
849 convert_c_12_6(idef.iparams[type].lj14.c12A,
850 idef.iparams[type].lj14.c6A,
851 &sigma, &epsilon);
852 nonbondedForce->addException(atom1, atom2,
853 fr->fudgeQQ*charges[atom1]*charges[atom2], sigma, epsilon);
854 exclusions[atom1].erase(atom2);
855 exclusions[atom2].erase(atom1);
858 // Record exclusions.
859 for (int i = 0; i < numAtoms; i++)
861 for (set<int>::const_iterator iter = exclusions[i].begin(); iter != exclusions[i].end(); ++iter)
863 if (i < *iter)
865 nonbondedForce->addException(i, *iter, 0.0, 1.0, 0.0);
870 // Add GBSA if needed.
871 if (ir->implicit_solvent == eisGBSA)
873 t_atoms atoms = gmx_mtop_global_atoms(top_global);
874 GBSAOBCForce* gbsa = new GBSAOBCForce();
876 sys->addForce(gbsa);
877 gbsa->setSoluteDielectric(ir->epsilon_r);
878 gbsa->setSolventDielectric(ir->gb_epsilon_solvent);
879 gbsa->setCutoffDistance(nonbondedForce->getCutoffDistance());
880 if (nonbondedForce->getNonbondedMethod() == NonbondedForce::NoCutoff)
881 gbsa->setNonbondedMethod(GBSAOBCForce::NoCutoff);
882 else if (nonbondedForce->getNonbondedMethod() == NonbondedForce::CutoffNonPeriodic)
883 gbsa->setNonbondedMethod(GBSAOBCForce::CutoffNonPeriodic);
884 else if (nonbondedForce->getNonbondedMethod() == NonbondedForce::CutoffPeriodic)
885 gbsa->setNonbondedMethod(GBSAOBCForce::CutoffPeriodic);
886 else
887 gmx_fatal(FARGS,"OpenMM supports only Reaction-Field electrostatics with OBC/GBSA.");
889 for (int i = 0; i < numAtoms; ++i)
891 gbsa->addParticle(charges[i],
892 top_global->atomtypes.gb_radius[atoms.atom[i].type],
893 top_global->atomtypes.S_hct[atoms.atom[i].type]);
895 free_t_atoms(&atoms, FALSE);
898 // Set constraints.
899 const int* constraintAtoms = (int*) idef.il[F_CONSTR].iatoms;
900 offset = 0;
901 for (int i = 0; i < numConstraints; ++i)
903 int type = constraintAtoms[offset++];
904 int atom1 = constraintAtoms[offset++];
905 int atom2 = constraintAtoms[offset++];
906 sys->addConstraint(atom1, atom2, idef.iparams[type].constr.dA);
908 const int* settleAtoms = (int*) idef.il[F_SETTLE].iatoms;
909 offset = 0;
910 for (int i = 0; i < numSettle; ++i)
912 int type = settleAtoms[offset++];
913 int oxygen = settleAtoms[offset++];
914 sys->addConstraint(oxygen, oxygen+1, idef.iparams[type].settle.doh);
915 sys->addConstraint(oxygen, oxygen+2, idef.iparams[type].settle.doh);
916 sys->addConstraint(oxygen+1, oxygen+2, idef.iparams[type].settle.dhh);
919 // Create an integrator for simulating the system.
920 double friction = (ir->opts.tau_t[0] == 0.0 ? 0.0 : 1.0/ir->opts.tau_t[0]);
921 Integrator* integ;
922 if (ir->eI == eiBD)
924 integ = new BrownianIntegrator(ir->opts.ref_t[0], friction, ir->delta_t);
925 static_cast<BrownianIntegrator*>(integ)->setRandomNumberSeed(ir->ld_seed);
927 else if (EI_SD(ir->eI))
929 integ = new LangevinIntegrator(ir->opts.ref_t[0], friction, ir->delta_t);
930 static_cast<LangevinIntegrator*>(integ)->setRandomNumberSeed(ir->ld_seed);
932 else
934 integ = new VerletIntegrator(ir->delta_t);
935 if ( ir->etc != etcNO)
937 real collisionFreq = ir->opts.tau_t[0] / 1000; /* tau_t (ps) / 1000 = collisionFreq (fs^-1) */
938 AndersenThermostat* thermostat = new AndersenThermostat(ir->opts.ref_t[0], friction);
939 sys->addForce(thermostat);
942 integ->setConstraintTolerance(ir->shake_tol);
944 // Create a context and initialize it.
945 Context* context = NULL;
948 OpenMM could automatically select the "best" GPU, however we're not't
949 going to let it do that for now, as the current algorithm is very rudimentary
950 and we anyway support only CUDA.
951 if (platformOptStr == NULL || platformOptStr == "")
953 context = new Context(*sys, *integ);
955 else
958 // Find which platform is it.
959 for (int i = 0; i < Platform::getNumPlatforms() && context == NULL; i++)
961 if (isStringEqNCase(opt->getOptionValue("platform"), Platform::getPlatform(i).getName()))
963 Platform& platform = Platform::getPlatform(i);
964 // set standard properties
965 platform.setPropertyDefaultValue("CudaDevice", opt->getOptionValue("deviceid"));
966 // TODO add extra properties
967 context = new Context(*sys, *integ, platform);
970 if (context == NULL)
972 gmx_fatal(FARGS, "The requested platform \"%s\" could not be found.",
973 opt->getOptionValue("platform").c_str());
977 Platform& platform = context->getPlatform();
978 fprintf(fplog, "Gromacs will use the OpenMM platform: %s\n", platform.getName().c_str());
980 const vector<string>& properties = platform.getPropertyNames();
981 if (debug)
983 for (int i = 0; i < properties.size(); i++)
985 printf(">> %s: %s\n", properties[i].c_str(),
986 platform.getPropertyValue(*context, properties[i]).c_str());
987 fprintf(fplog, ">> %s: %s\n", properties[i].c_str(),
988 platform.getPropertyValue(*context, properties[i]).c_str());
992 /* only for CUDA */
993 if (isStringEqNCase(opt->getOptionValue("platform"), "CUDA"))
995 /* For now this is just to double-check if OpenMM selected the GPU we wanted,
996 but when we'll let OpenMM select the GPU automatically, it will query the devideId.
998 int tmp;
999 if (!from_string<int>(tmp, platform.getPropertyValue(*context, "CudaDevice"), std::dec))
1001 gmx_fatal(FARGS, "Internal error: couldn't determine the device selected by OpenMM");
1002 if (tmp != devId)
1004 gmx_fatal(FARGS, "Internal error: OpenMM is using device #%d"
1005 "while initialized for device #%d", tmp, devId);
1009 /* check GPU compatibility */
1010 char gpuname[STRLEN];
1011 devId = atoi(opt->getOptionValue("deviceid").c_str());
1012 if (!is_supported_cuda_gpu(-1, gpuname))
1014 if (!gmx_strcasecmp(opt->getOptionValue("force-device").c_str(), "yes"))
1016 sprintf(warn_buf, "Non-supported GPU selected (#%d, %s), forced continuing."
1017 "Note, that the simulation can be slow or it migth even crash.",
1018 devId, gpuname);
1019 fprintf(fplog, "%s\n", warn_buf);
1020 gmx_warning(warn_buf);
1022 else
1024 gmx_fatal(FARGS, "The selected GPU (#%d, %s) is not supported by Gromacs! "
1025 "Most probably you have a low-end GPU which would not perform well, "
1026 "or new hardware that has not been tested yet with Gromacs-OpenMM. "
1027 "If you still want to try using the device, use the force=on option.",
1028 devId, gpuname);
1031 else
1033 fprintf(fplog, "Gromacs will run on the GPU #%d (%s).\n", devId, gpuname);
1037 /* only for CUDA */
1038 if (isStringEqNCase(opt->getOptionValue("platform"), "CUDA"))
1040 /* pre-simulation memtest */
1041 runMemtest(fplog, -1, "Pre", opt);
1044 vector<Vec3> pos(numAtoms);
1045 vector<Vec3> vel(numAtoms);
1046 for (int i = 0; i < numAtoms; ++i)
1048 pos[i] = Vec3(state->x[i][0], state->x[i][1], state->x[i][2]);
1049 vel[i] = Vec3(state->v[i][0], state->v[i][1], state->v[i][2]);
1051 context->setPositions(pos);
1052 context->setVelocities(vel);
1054 // Return a structure containing the system, integrator, and context.
1055 OpenMMData* data = new OpenMMData();
1056 data->system = sys;
1057 data->integrator = integ;
1058 data->context = context;
1059 data->removeCM = (ir->nstcomm > 0);
1060 data->platformOpt = opt;
1061 return data;
1064 catch (std::exception& e)
1066 gmx_fatal(FARGS, "OpenMM exception caught while initializating: %s", e.what());
1071 * \brief Integrate one step.
1073 * \param[in] data OpenMMData object created by openmm_init().
1075 void openmm_take_one_step(void* data)
1077 // static int step = 0; printf("----> taking step #%d\n", step++);
1080 static_cast<OpenMMData*>(data)->integrator->step(1);
1082 catch (std::exception& e)
1084 gmx_fatal(FARGS, "OpenMM exception caught while taking a step: %s", e.what());
1089 * \brief Integrate n steps.
1091 * \param[in] data OpenMMData object created by openmm_init().
1093 void openmm_take_steps(void* data, int nstep)
1097 static_cast<OpenMMData*>(data)->integrator->step(nstep);
1099 catch (std::exception& e)
1101 gmx_fatal(FARGS, "OpenMM exception caught while taking a step: %s", e.what());
1106 * \brief Clean up the data structures cretead for OpenMM.
1108 * \param[in] log Log file pointer.
1109 * \param[in] data OpenMMData object created by openmm_init().
1111 void openmm_cleanup(FILE* fplog, void* data)
1113 OpenMMData* d = static_cast<OpenMMData*>(data);
1114 /* only for CUDA */
1115 if (isStringEqNCase(d->platformOpt->getOptionValue("platform"), "CUDA"))
1117 /* post-simulation memtest */
1118 runMemtest(fplog, -1, "Post", d->platformOpt);
1120 delete d->system;
1121 delete d->integrator;
1122 delete d->context;
1123 delete d->platformOpt;
1124 delete d;
1128 * \brief Copy the current state information from OpenMM into the Gromacs data structures.
1130 * This function results in the requested proprties to be copied from the
1131 * GPU to host. As this represents a bottleneck, the frequency of pulling data
1132 * should be minimized.
1134 * \param[in] data OpenMMData object created by openmm_init().
1135 * \param[out] time Simulation time for which the state was created.
1136 * \param[out] state State of the system: coordinates and velocities.
1137 * \param[out] f Forces.
1138 * \param[out] enerd Energies.
1139 * \param[in] includePos True if coordinates are requested.
1140 * \param[in] includeVel True if velocities are requested.
1141 * \param[in] includeForce True if forces are requested.
1142 * \param[in] includeEnergy True if energies are requested.
1144 void openmm_copy_state(void *data,
1145 t_state *state, double *time,
1146 rvec f[], gmx_enerdata_t *enerd,
1147 bool includePos, bool includeVel, bool includeForce, bool includeEnergy)
1149 int types = 0;
1150 if (includePos)
1151 types += State::Positions;
1152 if (includeVel)
1153 types += State::Velocities;
1154 if (includeForce)
1155 types += State::Forces;
1156 if (includeEnergy)
1157 types += State::Energy;
1158 if (types == 0)
1159 return;
1162 State currentState = static_cast<OpenMMData*>(data)->context->getState(types);
1163 int numAtoms = static_cast<OpenMMData*>(data)->system->getNumParticles();
1164 if (includePos)
1166 for (int i = 0; i < numAtoms; i++)
1168 Vec3 x = currentState.getPositions()[i];
1169 state->x[i][0] = x[0];
1170 state->x[i][1] = x[1];
1171 state->x[i][2] = x[2];
1174 if (includeVel)
1176 for (int i = 0; i < numAtoms; i++)
1178 Vec3 v = currentState.getVelocities()[i];
1179 state->v[i][0] = v[0];
1180 state->v[i][1] = v[1];
1181 state->v[i][2] = v[2];
1184 if (includeForce)
1186 for (int i = 0; i < numAtoms; i++)
1188 Vec3 force = currentState.getForces()[i];
1189 f[i][0] = force[0];
1190 f[i][1] = force[1];
1191 f[i][2] = force[2];
1194 if (includeEnergy)
1196 int numConstraints = static_cast<OpenMMData*>(data)->system->getNumConstraints();
1197 int dof = 3*numAtoms-numConstraints;
1198 if (static_cast<OpenMMData*>(data)->removeCM)
1199 dof -= 3;
1200 enerd->term[F_EPOT] = currentState.getPotentialEnergy();
1201 enerd->term[F_EKIN] = currentState.getKineticEnergy();
1202 enerd->term[F_ETOT] = enerd->term[F_EPOT] + enerd->term[F_EKIN];
1203 enerd->term[F_TEMP] = 2.0*enerd->term[F_EKIN]/dof/BOLTZ;
1205 *time = currentState.getTime();
1207 catch (std::exception& e)
1209 gmx_fatal(FARGS, "OpenMM exception caught while retrieving state information: %s", e.what());