Fix in openmm_wrapper which makes it work with MSVC.
[gromacs/rigid-bodies.git] / src / kernel / openmm_wrapper.cpp
blobd0fa372e97393dd0a997ad92b9c2a5df81ff0f99
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 void print();
190 private:
191 void setOption(const string &opt, const string &val);
193 map<string, string> options; /*!< Data structure to store the option (name, value) pairs. */
195 static const char * const platforms[SIZEOF_PLATFORMS]; /*!< Available OpenMM platforms; size #SIZEOF_PLATFORMS */
196 static const char * const memtests[SIZEOF_MEMTESTS]; /*!< Available types of memory tests, also valid
197 any positive integer >=15; size #SIZEOF_MEMTESTS */
198 static const char * const deviceid[SIZEOF_DEVICEIDS]; /*!< Possible values for deviceid option;
199 also valid any positive integer; size #SIZEOF_DEVICEIDS */
200 static const char * const force_dev[SIZEOF_FORCE_DEV]; /*!< Possible values for for force-device option;
201 size #SIZEOF_FORCE_DEV */
204 const char * const GmxOpenMMPlatformOptions::platforms[SIZEOF_PLATFORMS]
205 = {"CUDA"};
206 //= { "Reference", "CUDA" /*,"OpenCL"*/ };
207 const char * const GmxOpenMMPlatformOptions::memtests[SIZEOF_MEMTESTS]
208 = { "15", "full", "off" };
209 const char * const GmxOpenMMPlatformOptions::deviceid[SIZEOF_DEVICEIDS]
210 = { "0" };
211 const char * const GmxOpenMMPlatformOptions::force_dev[SIZEOF_FORCE_DEV]
212 = { "no", "yes" };
215 * \brief Contructor.
216 * Takes the option list, parses it, checks the options and their values for validity.
217 * When certain options are not provided by the user, as default value the first item
218 * of the respective constant array is taken (GmxOpenMMPlatformOptions#platforms,
219 * GmxOpenMMPlatformOptions#memtests, GmxOpenMMPlatformOptions#deviceid,
220 * GmxOpenMMPlatformOptions#force_dev).
221 * \param[in] optionString Option string part of the mdrun -deviceoption parameter.
223 GmxOpenMMPlatformOptions::GmxOpenMMPlatformOptions(const char *optionString)
225 // set default values
226 setOption("platform", platforms[0]);
227 setOption("memtest", memtests[0]);
228 setOption("deviceid", deviceid[0]);
229 setOption("force-device", force_dev[0]);
231 string opt(optionString);
233 // remove all whitespaces
234 opt.erase(remove_if(opt.begin(), opt.end(), ::isspace), opt.end());
235 // tokenize around ","-s
236 vector<string> tokens = split(opt, ',');
238 for (vector<string>::iterator it = tokens.begin(); it != tokens.end(); ++it)
240 string opt = "", val = "";
241 splitOptionValue(*it, opt, val);
243 if (isStringEqNCase(opt, "platform"))
245 /* no check, this will fail if platform does not exist when we try to set it */
246 setOption(opt, val);
247 continue;
250 if (isStringEqNCase(opt, "memtest"))
252 /* the value has to be an integer >15(s) or "full" OR "off" */
253 if (!isStringEqNCase(val, "full") && !isStringEqNCase(val, "off"))
255 int secs;
256 if (!from_string<int>(secs, val, std::dec))
258 gmx_fatal(FARGS, "Invalid value for option memtest option: \"%s\"!", val.c_str());
260 if (secs < 15)
262 gmx_fatal(FARGS, "Incorrect value for memtest option (%d). "
263 "Memtest needs to run for at least 15s!", secs);
266 setOption(opt, val);
267 continue;
270 if (isStringEqNCase(opt, "deviceid"))
272 int id;
273 if (!from_string<int>(id, val, std::dec) )
275 gmx_fatal(FARGS, "Invalid device id: \"%s\"!", val.c_str());
277 setOption(opt, val);
278 continue;
281 if (isStringEqNCase(opt, "force-device"))
283 /* */
284 if (!isStringEqNCase(val, "yes") && !isStringEqNCase(val, "no"))
286 gmx_fatal(FARGS, "Invalid OpenMM force option: \"%s\"!", val.c_str());
288 setOption(opt, val);
289 continue;
292 // if we got till here something went wrong
293 gmx_fatal(FARGS, "Invalid OpenMM platform option: \"%s\"!", (*it).c_str());
299 * \brief Returns the value of an option.
300 * \param[in] opt Name of the option.
302 string GmxOpenMMPlatformOptions::getOptionValue(const string &opt)
304 map<string, string> :: const_iterator it = options.find(toUpper(opt));
305 if (it != options.end())
307 // cout << "@@@>> " << it->first << " : " << it->second << endl;
308 return it->second;
310 else
312 return NULL;
317 * \brief Setter function - private, only used from contructor.
318 * \param[in] opt Name of the option.
319 * \param[in] val Value for the option.
321 void GmxOpenMMPlatformOptions::setOption(const string &opt, const string &val)
323 options[toUpper(opt)] = val;
327 * \brief Removes an option with its value from the map structure. If the option
328 * does not exist, returns without any action.
329 * \param[in] opt Name of the option.
331 void GmxOpenMMPlatformOptions::remOption(const string &opt)
333 options.erase(toUpper(opt));
337 * \brief Print option-value pairs to a file (debugging function).
339 void GmxOpenMMPlatformOptions::print()
341 cout << ">> Platform options: " << endl
342 << ">> platform = " << getOptionValue("platform") << endl
343 << ">> deviceID = " << getOptionValue("deviceid") << endl
344 << ">> memtest = " << getOptionValue("memtest") << endl
345 << ">> force-device = " << getOptionValue("force-device") << endl;
349 * \brief Container for OpenMM related data structures that represent the bridge
350 * between the Gromacs data-structures and the OpenMM library and is but it's
351 * only passed through the API functions as void to disable direct access.
353 class OpenMMData
355 public:
356 System* system; /*! The system to simulate. */
357 Context* context; /*! The OpenMM context in which the simulation is carried out. */
358 Integrator* integrator; /*! The integrator used in the simulation. */
359 bool removeCM; /*! If \true remove venter of motion, false otherwise. */
360 GmxOpenMMPlatformOptions *platformOpt; /*! Platform options. */
364 * \brief Runs memtest on the GPU that has alreaby been initialized by OpenMM.
365 * \param[in] fplog Pointer to gromacs log file.
366 * \param[in] devId Device id of the GPU to run the test on. TODO: this can be removed!
367 * \param[in] pre_post Contains either "Pre" or "Post" just to be able to differentiate in
368 * stdout messages/log between memtest carried out before and after simulation.
369 * \param[in] opt Pointer to platform options object.
371 static void runMemtest(FILE* fplog, int devId, const char* pre_post, GmxOpenMMPlatformOptions *opt)
373 char strout_buf[STRLEN];
374 int which_test;
375 int res = 0;
376 string s = opt->getOptionValue("memtest");
377 const char * test_type =
378 s.c_str();
379 /* NOTE: thie code below for some misterious reason does NOT work
380 with MSVC, but the above "fix" seems to solve the problem - not sure why though */
381 // opt->getOptionValue("memtest").c_str();
384 if (!gmx_strcasecmp(test_type, "off"))
386 which_test = 0;
388 else
390 if (!gmx_strcasecmp(test_type, "full"))
392 which_test = 2;
394 else
396 from_string<int>(which_test, test_type, std::dec);
400 if (which_test < 0)
402 gmx_fatal(FARGS, "Amount of seconds for memetest is negative (%d). ", which_test);
405 switch (which_test)
407 case 0: /* no memtest */
408 sprintf(strout_buf, "%s-simulation GPU memtest skipped. Note, that faulty memory can cause "
409 "incorrect results!", pre_post);
410 fprintf(fplog, "%s\n", strout_buf);
411 gmx_warning(strout_buf);
412 break; /* case 0 */
414 case 1: /* quick memtest */
415 fprintf(fplog, "%s-simulation %s GPU memtest in progress...\n", pre_post, test_type);
416 fprintf(stdout, "\n%s-simulation %s GPU memtest in progress...", pre_post, test_type);
417 fflush(fplog);
418 fflush(stdout);
419 res = do_quick_memtest(-1);
420 break; /* case 1 */
422 case 2: /* full memtest */
423 fprintf(fplog, "%s-simulation %s memtest in progress...\n", pre_post, test_type);
424 fprintf(stdout, "\n%s-simulation %s memtest in progress...", pre_post, test_type);
425 fflush(fplog);
426 fflush(stdout);
427 res = do_full_memtest(-1);
428 break; /* case 2 */
430 default: /* timed memtest */
431 fprintf(fplog, "%s-simulation ~%ds memtest in progress...\n", pre_post, which_test);
432 fprintf(stdout, "\n%s-simulation ~%ds memtest in progress...", pre_post, which_test);
433 fflush(fplog);
434 fflush(stdout);
435 res = do_timed_memtest(-1, which_test);
438 if (which_test != 0 && res != 0)
440 gmx_fatal(FARGS, MEM_ERR_MSG(pre_post));
442 else
444 fprintf(fplog, "Memory test completed without errors.\n");
445 fflush(fplog);
446 fprintf(stdout, "done, no errors detected\n");
447 fflush(stdout);
452 * \brief Does gromacs option checking.
454 * Checks the gromacs mdp options for features unsupported in OpenMM, case in which
455 * interrupts the execution. It also warns the user about pecularities of OpenMM
456 * implementations.
457 * \param[in] ir Gromacs structure for input options, \see ::t_inputrec
458 * \param[in] top Gromacs local topology, \see ::gmx_localtop_t
459 * \param[in] state Gromacs state structure \see ::t_state
461 static void checkGmxOptions(t_inputrec *ir, gmx_localtop_t *top, t_state *state)
463 char warn_buf[STRLEN];
465 /* Abort if unsupported critical options are present */
467 /* Integrator */
468 if (ir->eI == eiMD)
470 gmx_warning( "OpenMM does not support leap-frog, will use velocity-verlet integrator.");
473 if ( (ir->eI != eiMD) &&
474 (ir->eI != eiVV) &&
475 (ir->eI != eiVVAK) &&
476 (ir->eI != eiSD1) &&
477 (ir->eI != eiSD2) &&
478 (ir->eI != eiBD) )
480 gmx_fatal(FARGS, "OpenMM supports only the following integrators: md/md-vv/md-vv-avek, sd/sd1, and bd.");
483 /* Electroctstics */
484 if ( (ir->coulombtype != eelPME) &&
485 (ir->coulombtype != eelRF) &&
486 (ir->coulombtype != eelEWALD) &&
487 // no-cutoff
488 ( !(ir->coulombtype == eelCUT && ir->rcoulomb == 0 && ir->rvdw == 0)) )
490 gmx_fatal(FARGS,"OpenMM supports only the following methods for electrostatics: "
491 "NoCutoff (i.e. rcoulomb = rvdw = 0 ),Reaction-Field, Ewald or PME.");
494 if (ir->etc != etcNO &&
495 ir->eI != eiSD1 &&
496 ir->eI != eiSD2 &&
497 ir->eI != eiBD )
499 gmx_warning("OpenMM supports only Andersen thermostat with the md/md-vv/md-vv-avek integrators.");
502 if (ir->opts.ngtc > 1)
503 gmx_fatal(FARGS,"OpenMM does not support multiple temperature coupling groups.");
505 if (ir->epc != etcNO)
506 gmx_fatal(FARGS,"OpenMM does not support pressure coupling.");
508 if (ir->opts.annealing[0])
509 gmx_fatal(FARGS,"OpenMM does not support simulated annealing.");
511 if (top->idef.il[F_CONSTR].nr > 0 && ir->eConstrAlg != econtSHAKE)
512 gmx_warning("OpenMM provides contraints as a combination "
513 "of SHAKE, SETTLE and CCMA. Accuracy is based on the SHAKE tolerance set "
514 "by the \"shake_tol\" option.");
516 if (ir->nwall != 0)
517 gmx_fatal(FARGS,"OpenMM does not support walls.");
519 if (ir->ePull != epullNO)
520 gmx_fatal(FARGS,"OpenMM does not support pulling.");
522 /* check for restraints */
523 int i;
524 for (i = 0; i < F_EPOT; i++)
526 if (i != F_CONSTR &&
527 i != F_SETTLE &&
528 i != F_BONDS &&
529 i != F_ANGLES &&
530 i != F_PDIHS &&
531 i != F_RBDIHS &&
532 i != F_LJ14 &&
533 top->idef.il[i].nr > 0)
535 /* TODO fix the message */
536 gmx_fatal(FARGS, "OpenMM does not support (some) of the provided restaint(s).");
540 if (ir->efep != efepNO)
541 gmx_fatal(FARGS,"OpenMM does not support free energy calculations.");
543 if (ir->opts.ngacc > 1)
544 gmx_fatal(FARGS,"OpenMM does not support non-equilibrium MD (accelerated groups).");
546 if (IR_ELEC_FIELD(*ir))
547 gmx_fatal(FARGS,"OpenMM does not support electric fields.");
549 if (ir->bQMMM)
550 gmx_fatal(FARGS,"OpenMM does not support QMMM calculations.");
552 if (ir->rcoulomb != ir->rvdw)
553 gmx_fatal(FARGS,"OpenMM uses a single cutoff for both Coulomb "
554 "and VdW interactions. Please set rcoulomb equal to rvdw.");
556 if (EEL_FULL(ir->coulombtype))
558 if (ir->ewald_geometry == eewg3DC)
559 gmx_fatal(FARGS,"OpenMM supports only Ewald 3D geometry.");
560 if (ir->epsilon_surface != 0)
561 gmx_fatal(FARGS,"OpenMM does not support dipole correction in Ewald summation.");
564 if (TRICLINIC(state->box))
566 gmx_fatal(FARGS,"OpenMM does not support triclinic unit cells.");
573 * \brief Convert Lennard-Jones parameters c12 and c6 to sigma and epsilon.
575 * \param[in] c12
576 * \param[in] c6
577 * \param[out] sigma
578 * \param[out] epsilon
580 static void convert_c_12_6(double c12, double c6, double *sigma, double *epsilon)
582 if (c12 == 0 && c6 == 0)
584 *epsilon = 0.0;
585 *sigma = 1.0;
587 else if (c12 > 0 && c6 > 0)
589 *epsilon = (c6*c6)/(4.0*c12);
590 *sigma = pow(c12/c6, 1.0/6.0);
592 else
594 gmx_fatal(FARGS,"OpenMM does only supports c6 > 0 and c12 > 0 or both 0.");
599 * \brief Initialize OpenMM, run sanity/consistency checks, and return a pointer to
600 * the OpenMMData.
602 * Various gromacs data structures are passed that contain the parameters, state and
603 * other porperties of the system to simulate. These serve as input for initializing
604 * OpenMM. Besides, a set of misc action are taken:
605 * - OpenMM plugins are loaded;
606 * - platform options in \p platformOptStr are parsed and checked;
607 * - Gromacs parameters are checked for OpenMM support and consistency;
608 * - after the OpenMM is initialized memtest executed in the same GPU context.
610 * \param[in] fplog Gromacs log file handler.
611 * \param[in] platformOptStr Platform option string.
612 * \param[in] ir The Gromacs input parameters, see ::t_inputrec
613 * \param[in] top_global Gromacs whole system toppology, \see ::gmx_mtop_t
614 * \param[in] top Gromacs node local topology, \see gmx_localtop_t
615 * \param[in] mdatoms Gromacs atom parameters, \see ::t_mdatoms
616 * \param[in] fr \see ::t_forcerec
617 * \param[in] state Gromacs systems state, \see ::t_state
620 void* openmm_init(FILE *fplog, const char *platformOptStr,
621 t_inputrec *ir,
622 gmx_mtop_t *top_global, gmx_localtop_t *top,
623 t_mdatoms *mdatoms, t_forcerec *fr, t_state *state)
626 char warn_buf[STRLEN];
627 static bool hasLoadedPlugins = false;
628 string usedPluginDir;
629 int devId;
633 if (!hasLoadedPlugins)
635 vector<string> loadedPlugins;
636 /* Look for OpenMM plugins at various locations (listed in order of priority):
637 - on the path in OPENMM_PLUGIN_DIR environment variable if this is specified
638 - on the path in the OPENMM_PLUGIN_DIR macro that is set by the build script
639 - at the default location assumed by OpenMM
641 /* env var */
642 char *pluginDir = getenv("OPENMM_PLUGIN_DIR");
643 trim(pluginDir);
644 /* no env var or empty */
645 if (pluginDir != NULL && *pluginDir != '\0')
647 loadedPlugins = Platform::loadPluginsFromDirectory(pluginDir);
648 if (loadedPlugins.size() > 0)
650 hasLoadedPlugins = true;
651 usedPluginDir = pluginDir;
653 else
655 gmx_fatal(FARGS, "The directory provided in the OPENMM_PLUGIN_DIR environment variable "
656 "(%s) does not contain valid OpenMM plugins. Check your OpenMM installation!",
657 pluginDir);
661 /* macro set at build time */
662 #ifdef OPENMM_PLUGIN_DIR
663 if (!hasLoadedPlugins)
665 loadedPlugins = Platform::loadPluginsFromDirectory(OPENMM_PLUGIN_DIR);
666 if (loadedPlugins.size() > 0)
668 hasLoadedPlugins = true;
669 usedPluginDir = OPENMM_PLUGIN_DIR;
672 #endif
673 /* default loocation */
674 if (!hasLoadedPlugins)
676 loadedPlugins = Platform::loadPluginsFromDirectory(Platform::getDefaultPluginsDirectory());
677 if (loadedPlugins.size() > 0)
679 hasLoadedPlugins = true;
680 usedPluginDir = Platform::getDefaultPluginsDirectory();
684 /* if there are still no plugins loaded there won't be any */
685 if (!hasLoadedPlugins)
687 gmx_fatal(FARGS, "No OpenMM plugins were found! You can provide the"
688 " plugin directory in the OPENMM_PLUGIN_DIR environment variable.", pluginDir);
691 fprintf(fplog, "\nPlugins loaded from directory %s:\t", usedPluginDir.c_str());
692 for (int i = 0; i < loadedPlugins.size(); i++)
694 fprintf(fplog, "%s, ", loadedPlugins[i].c_str());
696 fprintf(fplog, "\n");
699 /* parse option string */
700 GmxOpenMMPlatformOptions *opt = new GmxOpenMMPlatformOptions(platformOptStr);
702 if (debug)
704 opt->print();
707 /* check wheter Gromacs options compatibility with OpenMM */
708 checkGmxOptions(ir, top, state);
710 // Create the system.
711 const t_idef& idef = top->idef;
712 const int numAtoms = top_global->natoms;
713 const int numConstraints = idef.il[F_CONSTR].nr/3;
714 const int numSettle = idef.il[F_SETTLE].nr/2;
715 const int numBonds = idef.il[F_BONDS].nr/3;
716 const int numUB = idef.il[F_UREY_BRADLEY].nr/3;
717 const int numAngles = idef.il[F_ANGLES].nr/4;
718 const int numPeriodic = idef.il[F_PDIHS].nr/5;
719 const int numRB = idef.il[F_RBDIHS].nr/5;
720 const int num14 = idef.il[F_LJ14].nr/3;
721 System* sys = new System();
722 if (ir->nstcomm > 0)
723 sys->addForce(new CMMotionRemover(ir->nstcomm));
725 // Set bonded force field terms.
726 const int* bondAtoms = (int*) idef.il[F_BONDS].iatoms;
727 HarmonicBondForce* bondForce = new HarmonicBondForce();
728 sys->addForce(bondForce);
729 int offset = 0;
730 for (int i = 0; i < numBonds; ++i)
732 int type = bondAtoms[offset++];
733 int atom1 = bondAtoms[offset++];
734 int atom2 = bondAtoms[offset++];
735 bondForce->addBond(atom1, atom2,
736 idef.iparams[type].harmonic.rA, idef.iparams[type].harmonic.krA);
738 // Urey-Bradley includes both the angle and bond potential for 1-3 interactions
739 const int* ubAtoms = (int*) idef.il[F_UREY_BRADLEY].iatoms;
740 HarmonicBondForce* ubBondForce = new HarmonicBondForce();
741 HarmonicAngleForce* ubAngleForce = new HarmonicAngleForce();
742 sys->addForce(ubBondForce);
743 sys->addForce(ubAngleForce);
744 offset = 0;
745 for (int i = 0; i < numUB; ++i)
747 int type = ubAtoms[offset++];
748 int atom1 = ubAtoms[offset++];
749 int atom2 = ubAtoms[offset++];
750 int atom3 = ubAtoms[offset++];
751 ubBondForce->addBond(atom1, atom3,
752 idef.iparams[type].u_b.r13, idef.iparams[type].u_b.kUB);
753 ubAngleForce->addAngle(atom1, atom2, atom3,
754 idef.iparams[type].u_b.theta*M_PI/180.0, idef.iparams[type].u_b.ktheta);
756 const int* angleAtoms = (int*) idef.il[F_ANGLES].iatoms;
757 HarmonicAngleForce* angleForce = new HarmonicAngleForce();
758 sys->addForce(angleForce);
759 offset = 0;
760 for (int i = 0; i < numAngles; ++i)
762 int type = angleAtoms[offset++];
763 int atom1 = angleAtoms[offset++];
764 int atom2 = angleAtoms[offset++];
765 int atom3 = angleAtoms[offset++];
766 angleForce->addAngle(atom1, atom2, atom3,
767 idef.iparams[type].harmonic.rA*M_PI/180.0, idef.iparams[type].harmonic.krA);
769 const int* periodicAtoms = (int*) idef.il[F_PDIHS].iatoms;
770 PeriodicTorsionForce* periodicForce = new PeriodicTorsionForce();
771 sys->addForce(periodicForce);
772 offset = 0;
773 for (int i = 0; i < numPeriodic; ++i)
775 int type = periodicAtoms[offset++];
776 int atom1 = periodicAtoms[offset++];
777 int atom2 = periodicAtoms[offset++];
778 int atom3 = periodicAtoms[offset++];
779 int atom4 = periodicAtoms[offset++];
780 periodicForce->addTorsion(atom1, atom2, atom3, atom4,
781 idef.iparams[type].pdihs.mult,
782 idef.iparams[type].pdihs.phiA*M_PI/180.0,
783 idef.iparams[type].pdihs.cpA);
785 const int* rbAtoms = (int*) idef.il[F_RBDIHS].iatoms;
786 RBTorsionForce* rbForce = new RBTorsionForce();
787 sys->addForce(rbForce);
788 offset = 0;
789 for (int i = 0; i < numRB; ++i)
791 int type = rbAtoms[offset++];
792 int atom1 = rbAtoms[offset++];
793 int atom2 = rbAtoms[offset++];
794 int atom3 = rbAtoms[offset++];
795 int atom4 = rbAtoms[offset++];
796 rbForce->addTorsion(atom1, atom2, atom3, atom4,
797 idef.iparams[type].rbdihs.rbcA[0], idef.iparams[type].rbdihs.rbcA[1],
798 idef.iparams[type].rbdihs.rbcA[2], idef.iparams[type].rbdihs.rbcA[3],
799 idef.iparams[type].rbdihs.rbcA[4], idef.iparams[type].rbdihs.rbcA[5]);
802 // Set nonbonded parameters and masses.
803 int ntypes = fr->ntype;
804 int* types = mdatoms->typeA;
805 real* nbfp = fr->nbfp;
806 real* charges = mdatoms->chargeA;
807 real* masses = mdatoms->massT;
808 NonbondedForce* nonbondedForce = new NonbondedForce();
809 sys->addForce(nonbondedForce);
811 switch (ir->ePBC)
813 case epbcNONE:
814 if (ir->rcoulomb == 0)
816 nonbondedForce->setNonbondedMethod(NonbondedForce::NoCutoff);
818 else
820 nonbondedForce->setNonbondedMethod(NonbondedForce::CutoffNonPeriodic);
822 break;
823 case epbcXYZ:
824 switch (ir->coulombtype)
826 case eelRF:
827 nonbondedForce->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
828 break;
830 case eelEWALD:
831 nonbondedForce->setNonbondedMethod(NonbondedForce::Ewald);
832 break;
834 case eelPME:
835 nonbondedForce->setNonbondedMethod(NonbondedForce::PME);
836 break;
838 default:
839 gmx_fatal(FARGS,"Internal error: you should not see this message, it that the"
840 "electrosatics option check failed. Please report this error!");
842 sys->setPeriodicBoxVectors(Vec3(state->box[0][0], 0, 0),
843 Vec3(0, state->box[1][1], 0), Vec3(0, 0, state->box[2][2]));
844 nonbondedForce->setCutoffDistance(ir->rcoulomb);
846 break;
847 default:
848 gmx_fatal(FARGS,"OpenMM supports only full periodic boundary conditions "
849 "(pbc = xyz), or none (pbc = no).");
852 for (int i = 0; i < numAtoms; ++i)
854 double c12 = nbfp[types[i]*2*ntypes+types[i]*2+1];
855 double c6 = nbfp[types[i]*2*ntypes+types[i]*2];
856 double sigma, epsilon;
857 convert_c_12_6(c12, c6, &sigma, &epsilon);
858 nonbondedForce->addParticle(charges[i], sigma, epsilon);
859 sys->addParticle(masses[i]);
862 // Build a table of all exclusions.
863 vector<set<int> > exclusions(numAtoms);
864 for (int i = 0; i < numAtoms; i++)
866 int start = top->excls.index[i];
867 int end = top->excls.index[i+1];
868 for (int j = start; j < end; j++)
869 exclusions[i].insert(top->excls.a[j]);
872 // Record the 1-4 interactions, and remove them from the list of exclusions.
873 const int* nb14Atoms = (int*) idef.il[F_LJ14].iatoms;
874 offset = 0;
875 for (int i = 0; i < num14; ++i)
877 int type = nb14Atoms[offset++];
878 int atom1 = nb14Atoms[offset++];
879 int atom2 = nb14Atoms[offset++];
880 double sigma, epsilon;
881 convert_c_12_6(idef.iparams[type].lj14.c12A,
882 idef.iparams[type].lj14.c6A,
883 &sigma, &epsilon);
884 nonbondedForce->addException(atom1, atom2,
885 fr->fudgeQQ*charges[atom1]*charges[atom2], sigma, epsilon);
886 exclusions[atom1].erase(atom2);
887 exclusions[atom2].erase(atom1);
890 // Record exclusions.
891 for (int i = 0; i < numAtoms; i++)
893 for (set<int>::const_iterator iter = exclusions[i].begin(); iter != exclusions[i].end(); ++iter)
895 if (i < *iter)
897 nonbondedForce->addException(i, *iter, 0.0, 1.0, 0.0);
902 // Add GBSA if needed.
903 if (ir->implicit_solvent == eisGBSA)
905 t_atoms atoms = gmx_mtop_global_atoms(top_global);
906 GBSAOBCForce* gbsa = new GBSAOBCForce();
908 sys->addForce(gbsa);
909 gbsa->setSoluteDielectric(ir->epsilon_r);
910 gbsa->setSolventDielectric(ir->gb_epsilon_solvent);
911 gbsa->setCutoffDistance(nonbondedForce->getCutoffDistance());
912 if (nonbondedForce->getNonbondedMethod() == NonbondedForce::NoCutoff)
913 gbsa->setNonbondedMethod(GBSAOBCForce::NoCutoff);
914 else if (nonbondedForce->getNonbondedMethod() == NonbondedForce::CutoffNonPeriodic)
915 gbsa->setNonbondedMethod(GBSAOBCForce::CutoffNonPeriodic);
916 else if (nonbondedForce->getNonbondedMethod() == NonbondedForce::CutoffPeriodic)
917 gbsa->setNonbondedMethod(GBSAOBCForce::CutoffPeriodic);
918 else
919 gmx_fatal(FARGS,"OpenMM supports only Reaction-Field electrostatics with OBC/GBSA.");
921 for (int i = 0; i < numAtoms; ++i)
923 gbsa->addParticle(charges[i],
924 top_global->atomtypes.gb_radius[atoms.atom[i].type],
925 top_global->atomtypes.S_hct[atoms.atom[i].type]);
927 free_t_atoms(&atoms, FALSE);
930 // Set constraints.
931 const int* constraintAtoms = (int*) idef.il[F_CONSTR].iatoms;
932 offset = 0;
933 for (int i = 0; i < numConstraints; ++i)
935 int type = constraintAtoms[offset++];
936 int atom1 = constraintAtoms[offset++];
937 int atom2 = constraintAtoms[offset++];
938 sys->addConstraint(atom1, atom2, idef.iparams[type].constr.dA);
940 const int* settleAtoms = (int*) idef.il[F_SETTLE].iatoms;
941 offset = 0;
942 for (int i = 0; i < numSettle; ++i)
944 int type = settleAtoms[offset++];
945 int oxygen = settleAtoms[offset++];
946 sys->addConstraint(oxygen, oxygen+1, idef.iparams[type].settle.doh);
947 sys->addConstraint(oxygen, oxygen+2, idef.iparams[type].settle.doh);
948 sys->addConstraint(oxygen+1, oxygen+2, idef.iparams[type].settle.dhh);
951 // Create an integrator for simulating the system.
952 double friction = (ir->opts.tau_t[0] == 0.0 ? 0.0 : 1.0/ir->opts.tau_t[0]);
953 Integrator* integ;
954 if (ir->eI == eiBD)
956 integ = new BrownianIntegrator(ir->opts.ref_t[0], friction, ir->delta_t);
957 static_cast<BrownianIntegrator*>(integ)->setRandomNumberSeed(ir->ld_seed);
959 else if (EI_SD(ir->eI))
961 integ = new LangevinIntegrator(ir->opts.ref_t[0], friction, ir->delta_t);
962 static_cast<LangevinIntegrator*>(integ)->setRandomNumberSeed(ir->ld_seed);
964 else
966 integ = new VerletIntegrator(ir->delta_t);
967 if ( ir->etc != etcNO)
969 real collisionFreq = ir->opts.tau_t[0] / 1000; /* tau_t (ps) / 1000 = collisionFreq (fs^-1) */
970 AndersenThermostat* thermostat = new AndersenThermostat(ir->opts.ref_t[0], friction);
971 sys->addForce(thermostat);
974 integ->setConstraintTolerance(ir->shake_tol);
976 // Create a context and initialize it.
977 Context* context = NULL;
980 OpenMM could automatically select the "best" GPU, however we're not't
981 going to let it do that for now, as the current algorithm is very rudimentary
982 and we anyway support only CUDA.
983 if (platformOptStr == NULL || platformOptStr == "")
985 context = new Context(*sys, *integ);
987 else
990 // Find which platform is it.
991 for (int i = 0; i < Platform::getNumPlatforms() && context == NULL; i++)
993 if (isStringEqNCase(opt->getOptionValue("platform"), Platform::getPlatform(i).getName()))
995 Platform& platform = Platform::getPlatform(i);
996 // set standard properties
997 platform.setPropertyDefaultValue("CudaDevice", opt->getOptionValue("deviceid"));
998 // TODO add extra properties
999 context = new Context(*sys, *integ, platform);
1002 if (context == NULL)
1004 gmx_fatal(FARGS, "The requested platform \"%s\" could not be found.",
1005 opt->getOptionValue("platform").c_str());
1009 Platform& platform = context->getPlatform();
1010 fprintf(fplog, "Gromacs will use the OpenMM platform: %s\n", platform.getName().c_str());
1012 const vector<string>& properties = platform.getPropertyNames();
1013 if (debug)
1015 for (int i = 0; i < properties.size(); i++)
1017 printf(">> %s: %s\n", properties[i].c_str(),
1018 platform.getPropertyValue(*context, properties[i]).c_str());
1019 fprintf(fplog, ">> %s: %s\n", properties[i].c_str(),
1020 platform.getPropertyValue(*context, properties[i]).c_str());
1024 /* only for CUDA */
1025 if (isStringEqNCase(opt->getOptionValue("platform"), "CUDA"))
1027 /* For now this is just to double-check if OpenMM selected the GPU we wanted,
1028 but when we'll let OpenMM select the GPU automatically, it will query the devideId.
1030 int tmp;
1031 if (!from_string<int>(tmp, platform.getPropertyValue(*context, "CudaDevice"), std::dec))
1033 gmx_fatal(FARGS, "Internal error: couldn't determine the device selected by OpenMM");
1034 if (tmp != devId)
1036 gmx_fatal(FARGS, "Internal error: OpenMM is using device #%d"
1037 "while initialized for device #%d", tmp, devId);
1041 /* check GPU compatibility */
1042 char gpuname[STRLEN];
1043 devId = atoi(opt->getOptionValue("deviceid").c_str());
1044 if (!is_supported_cuda_gpu(-1, gpuname))
1046 if (!gmx_strcasecmp(opt->getOptionValue("force-device").c_str(), "yes"))
1048 sprintf(warn_buf, "Non-supported GPU selected (#%d, %s), forced continuing."
1049 "Note, that the simulation can be slow or it migth even crash.",
1050 devId, gpuname);
1051 fprintf(fplog, "%s\n", warn_buf);
1052 gmx_warning(warn_buf);
1054 else
1056 gmx_fatal(FARGS, "The selected GPU (#%d, %s) is not supported by Gromacs! "
1057 "Most probably you have a low-end GPU which would not perform well, "
1058 "or new hardware that has not been tested yet with Gromacs-OpenMM. "
1059 "If you still want to try using the device, use the force-device=yes option.",
1060 devId, gpuname);
1063 else
1065 fprintf(fplog, "Gromacs will run on the GPU #%d (%s).\n", devId, gpuname);
1069 /* only for CUDA */
1070 if (isStringEqNCase(opt->getOptionValue("platform"), "CUDA"))
1072 /* pre-simulation memtest */
1073 runMemtest(fplog, -1, "Pre", opt);
1076 vector<Vec3> pos(numAtoms);
1077 vector<Vec3> vel(numAtoms);
1078 for (int i = 0; i < numAtoms; ++i)
1080 pos[i] = Vec3(state->x[i][0], state->x[i][1], state->x[i][2]);
1081 vel[i] = Vec3(state->v[i][0], state->v[i][1], state->v[i][2]);
1083 context->setPositions(pos);
1084 context->setVelocities(vel);
1086 // Return a structure containing the system, integrator, and context.
1087 OpenMMData* data = new OpenMMData();
1088 data->system = sys;
1089 data->integrator = integ;
1090 data->context = context;
1091 data->removeCM = (ir->nstcomm > 0);
1092 data->platformOpt = opt;
1093 return data;
1096 catch (std::exception& e)
1098 gmx_fatal(FARGS, "OpenMM exception caught while initializating: %s", e.what());
1103 * \brief Integrate one step.
1105 * \param[in] data OpenMMData object created by openmm_init().
1107 void openmm_take_one_step(void* data)
1109 // static int step = 0; printf("----> taking step #%d\n", step++);
1112 static_cast<OpenMMData*>(data)->integrator->step(1);
1114 catch (std::exception& e)
1116 gmx_fatal(FARGS, "OpenMM exception caught while taking a step: %s", e.what());
1121 * \brief Integrate n steps.
1123 * \param[in] data OpenMMData object created by openmm_init().
1125 void openmm_take_steps(void* data, int nstep)
1129 static_cast<OpenMMData*>(data)->integrator->step(nstep);
1131 catch (std::exception& e)
1133 gmx_fatal(FARGS, "OpenMM exception caught while taking a step: %s", e.what());
1138 * \brief Clean up the data structures cretead for OpenMM.
1140 * \param[in] log Log file pointer.
1141 * \param[in] data OpenMMData object created by openmm_init().
1143 void openmm_cleanup(FILE* fplog, void* data)
1145 OpenMMData* d = static_cast<OpenMMData*>(data);
1146 /* only for CUDA */
1147 if (isStringEqNCase(d->platformOpt->getOptionValue("platform"), "CUDA"))
1149 /* post-simulation memtest */
1150 runMemtest(fplog, -1, "Post", d->platformOpt);
1152 delete d->system;
1153 delete d->integrator;
1154 delete d->context;
1155 delete d->platformOpt;
1156 delete d;
1160 * \brief Copy the current state information from OpenMM into the Gromacs data structures.
1162 * This function results in the requested proprties to be copied from the
1163 * GPU to host. As this represents a bottleneck, the frequency of pulling data
1164 * should be minimized.
1166 * \param[in] data OpenMMData object created by openmm_init().
1167 * \param[out] time Simulation time for which the state was created.
1168 * \param[out] state State of the system: coordinates and velocities.
1169 * \param[out] f Forces.
1170 * \param[out] enerd Energies.
1171 * \param[in] includePos True if coordinates are requested.
1172 * \param[in] includeVel True if velocities are requested.
1173 * \param[in] includeForce True if forces are requested.
1174 * \param[in] includeEnergy True if energies are requested.
1176 void openmm_copy_state(void *data,
1177 t_state *state, double *time,
1178 rvec f[], gmx_enerdata_t *enerd,
1179 bool includePos, bool includeVel, bool includeForce, bool includeEnergy)
1181 int types = 0;
1182 if (includePos)
1183 types += State::Positions;
1184 if (includeVel)
1185 types += State::Velocities;
1186 if (includeForce)
1187 types += State::Forces;
1188 if (includeEnergy)
1189 types += State::Energy;
1190 if (types == 0)
1191 return;
1194 State currentState = static_cast<OpenMMData*>(data)->context->getState(types);
1195 int numAtoms = static_cast<OpenMMData*>(data)->system->getNumParticles();
1196 if (includePos)
1198 for (int i = 0; i < numAtoms; i++)
1200 Vec3 x = currentState.getPositions()[i];
1201 state->x[i][0] = x[0];
1202 state->x[i][1] = x[1];
1203 state->x[i][2] = x[2];
1206 if (includeVel)
1208 for (int i = 0; i < numAtoms; i++)
1210 Vec3 v = currentState.getVelocities()[i];
1211 state->v[i][0] = v[0];
1212 state->v[i][1] = v[1];
1213 state->v[i][2] = v[2];
1216 if (includeForce)
1218 for (int i = 0; i < numAtoms; i++)
1220 Vec3 force = currentState.getForces()[i];
1221 f[i][0] = force[0];
1222 f[i][1] = force[1];
1223 f[i][2] = force[2];
1226 if (includeEnergy)
1228 int numConstraints = static_cast<OpenMMData*>(data)->system->getNumConstraints();
1229 int dof = 3*numAtoms-numConstraints;
1230 if (static_cast<OpenMMData*>(data)->removeCM)
1231 dof -= 3;
1232 enerd->term[F_EPOT] = currentState.getPotentialEnergy();
1233 enerd->term[F_EKIN] = currentState.getKineticEnergy();
1234 enerd->term[F_ETOT] = enerd->term[F_EPOT] + enerd->term[F_EKIN];
1235 enerd->term[F_TEMP] = 2.0*enerd->term[F_EKIN]/dof/BOLTZ;
1237 *time = currentState.getTime();
1239 catch (std::exception& e)
1241 gmx_fatal(FARGS, "OpenMM exception caught while retrieving state information: %s", e.what());