HelloSodiumChloride.cpp 8.9 KB
Newer Older
Michael Sherman's avatar
Michael Sherman committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/* -----------------------------------------------------------------------------
 *                OpenMM(tm) HelloSodiumChloride example (May 2009)
 * -----------------------------------------------------------------------------
 * This is a complete, self-contained "hello world" example demonstrating 
 * GPU-accelerated constant energy simulation of a very simple system with just 
 * nonbonded forces, consisting of several sodium (Na+) and chloride (Cl-) ions. 
 * A multi-frame PDB file is written to stdout which can be read by VMD or other 
 * visualization tool to produce an animation of the resulting trajectory.
 *
 * Pay particular attention to the handling of units in this example. Incorrect
 * handling of units is a very common error; this example shows how you can
 * continue to work with Amber-style units of Angstroms and kCals while correctly
 * communicating with OpenMM in nanometers and kJoules.
 * -------------------------------------------------------------------------- */

// Suppress irrelevant warnings from Microsoft's compiler.
#ifdef _MSC_VER
    #pragma warning(disable:4996)   // sprintf is unsafe 
    #pragma warning(disable:4251)   // no dll interface for some classes
#endif
21

22
23
#include "OpenMM.h"

Michael Sherman's avatar
Michael Sherman committed
24
#include <cstdio>
25
26
#include <string>

27
28
29
using OpenMM::Vec3; // so we can just say "Vec3" below

// -----------------------------------------------------------------------------
Michael Sherman's avatar
Michael Sherman committed
30
31
32
33
//                   MODELING AND SIMULATION PARAMETERS
// -----------------------------------------------------------------------------
const double StepSizeInFs        = 2;       // integration step size (fs)
const double ReportIntervalInFs  = 10;      // how often to generate PDB frame (fs)
34
const double SimulationTimeInPs  = 100;     // total simulation time (ps)
Michael Sherman's avatar
Michael Sherman committed
35
36

static void simulateNaCl();
37
38
static void writePDB(const OpenMM::OpenMMContext&); // PDB file writer; see below.

Michael Sherman's avatar
Michael Sherman committed
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
// -----------------------------------------------------------------------------
//                                MAIN PROGRAM
// -----------------------------------------------------------------------------
int main() {
    // ALWAYS enclose all OpenMM calls with a try/catch block to make sure that
    // usage and runtime errors are caught and reported.
    try {
        // Load all available OpenMM plugins from their default location.
        OpenMM::Platform::loadPluginsFromDirectory
           (OpenMM::Platform::getDefaultPluginsDirectory());

        simulateNaCl();

        return 0; // Normal return from main.
    }

    // Catch and report usage and runtime errors detected by OpenMM and fail.
    catch(const std::exception& e) {
        printf("EXCEPTION: %s\n", e.what());
        return 1;
    }
60
61
}

Michael Sherman's avatar
Michael Sherman committed
62
63
// -----------------------------------------------------------------------------
//                          ATOM AND FORCE FIELD DATA
64
// -----------------------------------------------------------------------------
65
66
67
68
69
70
// This is not part of OpenMM; just a struct we can use to collect
// atom parameters for this example. Normally atom parameters would
// come from the force field's parameterization file.
// We're going to use data in Angstrom and Kilocalorie units and
// show how to safely convert to OpenMM's internal unit system
// which uses nanometers and kilojoules.
71
72
73
74
75
76
77
78
79
80
81
82
struct AtomInfo { 
    const char* pdb; 
    double      mass, charge, vdwRadiusInAng, vdwEnergyInKcal;
    Vec3        initPosInAngstroms;
} atoms[] = {
    // pdb   mass   charge   vdwRadius  vdwEnergy   initPos
    {" NA ", 22.99,   1,     1.8680,    0.00277,    Vec3(8,0,0)},
    {" CL ", 35.45,  -1,     2.4700,    0.1000,     Vec3(-8,0,0)},
    {" NA ", 22.99,   1,     1.8680,    0.00277,    Vec3(0,9,0)},
    {" CL ", 35.45,  -1,     2.4700,    0.1000,     Vec3(0,-9,0)},
    {" NA ", 22.99,   1,     1.8680,    0.00277,    Vec3(0,0,-10)},
    {" CL ", 35.45,  -1,     2.4700,    0.1000,     Vec3( 0,0,10)},
83
84
85
    {""} // end of list
};

Michael Sherman's avatar
Michael Sherman committed
86
87
88
89
90
91
92
93
94
95
// Add missing scalar product operators for OpenMM::Vec3.
Vec3 operator*(const Vec3& v, double r) {return Vec3(v[0]*r, v[1]*r, v[2]*r);}
Vec3 operator*(double r, const Vec3& v) {return Vec3(r*v[0], r*v[1], r*v[2]);}
// This is the conversion factor that takes you from a van der Waals radius
// (defined as 1/2 the minimum energy separation) to the related Lennard Jones 
// "sigma" parameter (defined as the zero crossing separation).
static const double SigmaPerVdwRadius = 2*std::pow(2., -1./6.);

// -----------------------------------------------------------------------------
//                              NaCl SIMULATION
96
97
// -----------------------------------------------------------------------------
static void simulateNaCl() {
Michael Sherman's avatar
Michael Sherman committed
98
99
100
101
    // -------------------------------------------------------------------------
    // Create a System and Force objects within the System. Retain a reference
    // to each force object so we can fill in the forces. Note: OpenMM owns
    // the objects and will take care of deleting them; don't do it yourself!
102
103
104
    // -------------------------------------------------------------------------
    OpenMM::System system;
    OpenMM::NonbondedForce* nonbond = new OpenMM::NonbondedForce();
105
106
    system.addForce(nonbond);

107
108
109
110
    nonbond->setNonbondedMethod(OpenMM::NonbondedForce::CutoffPeriodic);
    nonbond->setCutoffDistance(2);
    nonbond->setPeriodicBoxVectors(Vec3(5,0,0), Vec3(0,5,0), Vec3(0,0,5));

Michael Sherman's avatar
Michael Sherman committed
111
112
113
114
115
    // -------------------------------------------------------------------------
    // Specify the atoms and their properties:
    //  (1) System needs to know the masses.
    //  (2) NonbondedForce needs charges,van der Waals properties (in MD units!).
    //  (3) Collect default positions for initializing the simulation later.
116
117
118
119
    // -------------------------------------------------------------------------
    std::vector<Vec3> initialPositions;
    for (int n=0; *atoms[n].pdb; ++n) {
        const AtomInfo& atom = atoms[n];
120
121
        system.addParticle(atom.mass);
        nonbond->addParticle(atom.charge,
Michael Sherman's avatar
Michael Sherman committed
122
123
                             atom.vdwRadiusInAng  * OpenMM::NmPerAngstrom 
                                                  * SigmaPerVdwRadius,
124
                             atom.vdwEnergyInKcal * OpenMM::KJPerKcal);
Michael Sherman's avatar
Michael Sherman committed
125
126
        initialPositions.push_back(atoms[n].initPosInAngstroms 
                                                  * OpenMM::NmPerAngstrom);
127
128
    }

Michael Sherman's avatar
Michael Sherman committed
129
130
131
132
133
134
135
136
    // -------------------------------------------------------------------------
    // Choose an Integrator for advancing time, and a Context connecting the
    // System with the Integrator for simulation. Let the Context choose the
    // best available Platform. Initialize the configuration from the default
    // positions we collected above. Initial velocities will be zero.
    // -------------------------------------------------------------------------
    OpenMM::VerletIntegrator integrator(StepSizeInFs * OpenMM::PsPerFs);
    OpenMM::OpenMMContext    context(system, integrator);
137
138
    context.setPositions(initialPositions);

Michael Sherman's avatar
Michael Sherman committed
139
140
141
142
143
    // -------------------------------------------------------------------------
    // Run the simulation:
    //  (1) Write the first line of the PDB file and the initial configuration.
    //  (2) Run silently entirely within OpenMM between reporting intervals.
    //  (3) Write a PDB frame when the time comes.
144
    // -------------------------------------------------------------------------
145
146
    printf( "REMARK  Using OpenMM platform %s\n", context.getPlatform().getName().c_str() );
    writePDB(context);
147

148
    const int NumSilentSteps = (int)(ReportIntervalInFs / StepSizeInFs + 0.5);
149
150
    do {
        integrator.step(NumSilentSteps);
151
        writePDB(context);
152
    } while (context.getTime() < SimulationTimeInPs);
153
154
}

Michael Sherman's avatar
Michael Sherman committed
155
156
// -----------------------------------------------------------------------------
//                               PDB FILE WRITER
157
// -----------------------------------------------------------------------------
158
static void
159
writePDB(const OpenMM::OpenMMContext& context) {
160
    // Caution: at the moment asking for energy requires use of slow reference calculation.
161
162
163
164
165
    const OpenMM::State state = context.getState(  OpenMM::State::Positions 
                                                 | OpenMM::State::Velocities 
                                                 | OpenMM::State::Energy);
    const double energy = state.getPotentialEnergy() + state.getKineticEnergy();
    const std::vector<Vec3>& positions = state.getPositions();
166
167
168
169
170
    static int modelFrameNumber = 0; // numbering for MODEL records in pdb output

    // write out in PDB format -- printf is so much more compact than formatted cout
    modelFrameNumber++;
    printf("MODEL     %d\n", modelFrameNumber);
171
172
    printf("REMARK 250 time=%.3f picoseconds; Energy = %.3f kilojoules/mole\n", 
           state.getTime(), energy);
173
    for (unsigned i=0; i < positions.size(); ++i) {
174
175
176
        const Vec3 pos = positions[i] * OpenMM::AngstromsPerNm;
        printf("ATOM  %5d %4s SLT     1    %8.3f%8.3f%8.3f  1.00  0.00            \n", 
            i+1, atoms[i].pdb, pos[0], pos[1], pos[2]);
177
    }
178
179
180
    printf("ENDMDL\n");
}