"platforms/cuda/src/kernels/customGBValueN2.cu" did not exist on "e918cb6fc04c86568ba56e13d61d74642f45f063"
Commit 5a06df78 authored by tic20's avatar tic20
Browse files
parents 8dd60914 a9223eea
......@@ -163,12 +163,12 @@ void testRandomSeed() {
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
integrator.step(20);
State state1 = context.getState(State::Positions);
context.reinitialize();
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
integrator.step(20);
State state2 = context.getState(State::Positions);
// Try twice with a different random seed.
......@@ -177,12 +177,12 @@ void testRandomSeed() {
context.reinitialize();
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
integrator.step(20);
State state3 = context.getState(State::Positions);
context.reinitialize();
context.setPositions(positions);
context.setVelocities(velocities);
integrator.step(10);
integrator.step(20);
State state4 = context.getState(State::Positions);
// Compare the results.
......
......@@ -34,7 +34,7 @@
#include "openmm/HarmonicBondForce.h"
#include "openmm/NonbondedForce.h"
#include "openmm/System.h"
#include "openmm/BAOABLangevinIntegrator.h"
#include "openmm/LangevinMiddleIntegrator.h"
#include "SimTKOpenMMRealType.h"
#include "sfmt/SFMT.h"
#include <iostream>
......@@ -49,7 +49,7 @@ void testSingleBond() {
System system;
system.addParticle(2.0);
system.addParticle(2.0);
BAOABLangevinIntegrator integrator(0, 0.1, 0.01);
LangevinMiddleIntegrator integrator(0, 0.1, 0.01);
HarmonicBondForce* forceField = new HarmonicBondForce();
forceField->addBond(0, 1, 1.5, 1);
system.addForce(forceField);
......@@ -93,7 +93,7 @@ void testTemperature() {
const double temp = 100.0;
System system;
system.setDefaultPeriodicBoxVectors(Vec3(5, 0, 0), Vec3(0, 5, 0), Vec3(0, 0, 5));
BAOABLangevinIntegrator integrator(temp, 3.0, 0.01);
LangevinMiddleIntegrator integrator(temp, 3.0, 0.01);
NonbondedForce* forceField = new NonbondedForce();
forceField->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
for (int i = 0; i < numParticles; ++i) {
......@@ -130,7 +130,7 @@ void testConstraints() {
const int numConstraints = 5;
const double temp = 100.0;
System system;
BAOABLangevinIntegrator integrator(temp, 2.0, 0.01);
LangevinMiddleIntegrator integrator(temp, 2.0, 0.01);
integrator.setConstraintTolerance(1e-5);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
......@@ -181,7 +181,7 @@ void testConstrainedMasslessParticles() {
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(1, 0, 0);
BAOABLangevinIntegrator integrator(300.0, 2.0, 0.01);
LangevinMiddleIntegrator integrator(300.0, 2.0, 0.01);
bool failed = false;
try {
// This should throw an exception.
......@@ -208,7 +208,7 @@ void testRandomSeed() {
const int numParticles = 8;
const double temp = 100.0;
System system;
BAOABLangevinIntegrator integrator(temp, 2.0, 0.01);
LangevinMiddleIntegrator integrator(temp, 2.0, 0.01);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.addParticle(2.0);
......
......@@ -354,6 +354,44 @@ void testPeriodic() {
ASSERT_EQUAL_TOL(2*ONE_4PI_EPS0*(1.0)*(1.0+krf*1.0-crf), state.getPotentialEnergy(), TOL);
}
void testPeriodicExceptions() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
VerletIntegrator integrator(0.01);
NonbondedForce* nonbonded = new NonbondedForce();
nonbonded->addParticle(1.0, 1, 0);
nonbonded->addParticle(1.0, 1, 0);
nonbonded->addException(0, 1, 1.0, 1.0, 0.0);
nonbonded->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
const double cutoff = 2.0;
nonbonded->setCutoffDistance(cutoff);
system.setDefaultPeriodicBoxVectors(Vec3(4, 0, 0), Vec3(0, 4, 0), Vec3(0, 0, 4));
system.addForce(nonbonded);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(3, 0, 0);
context.setPositions(positions);
State state = context.getState(State::Forces | State::Energy);
vector<Vec3> forces = state.getForces();
double force = ONE_4PI_EPS0/(3*3);
ASSERT_EQUAL_VEC(Vec3(-force, 0, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(force, 0, 0), forces[1], TOL);
ASSERT_EQUAL_TOL(ONE_4PI_EPS0/3, state.getPotentialEnergy(), TOL);
// Now make exceptions periodic and see if it changes correctly.
nonbonded->setExceptionsUsePeriodicBoundaryConditions(true);
context.reinitialize(true);
state = context.getState(State::Forces | State::Energy);
forces = state.getForces();
force = ONE_4PI_EPS0/(1*1);
ASSERT_EQUAL_VEC(Vec3(force, 0, 0), forces[0], TOL);
ASSERT_EQUAL_VEC(Vec3(-force, 0, 0), forces[1], TOL);
ASSERT_EQUAL_TOL(ONE_4PI_EPS0/1, state.getPotentialEnergy(), TOL);
}
void testTriclinic() {
System system;
system.addParticle(1.0);
......@@ -809,6 +847,7 @@ int main(int argc, char* argv[]) {
testCutoff();
testCutoff14();
testPeriodic();
testPeriodicExceptions();
testTriclinic();
testLargeSystem();
testDispersionCorrection();
......
/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
* This is part of the OpenMM molecular simulation toolkit originating from *
* Simbios, the NIH National Center for Physics-Based Simulation of *
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2019 Stanford University and the Authors. *
* Authors: Andreas Krämer and Andrew C. Simmonett *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/Context.h"
#include "openmm/CustomExternalForce.h"
#include "openmm/HarmonicBondForce.h"
#include "openmm/NonbondedForce.h"
#include "openmm/System.h"
#include "openmm/NoseHooverIntegrator.h"
#include "openmm/VirtualSite.h"
#include "SimTKOpenMMRealType.h"
#include "sfmt/SFMT.h"
#include <iostream>
#include <vector>
using namespace OpenMM;
using namespace std;
const double TOL = 1e-5;
void testVVSingleBond() {
System system;
system.addParticle(2.0);
system.addParticle(2.0);
NoseHooverIntegrator integrator(0.01);
HarmonicBondForce* forceField = new HarmonicBondForce();
forceField->addBond(0, 1, 1.5, 1);
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(1, 0, 0);
context.setPositions(positions);
// This is simply a harmonic oscillator, so compare it to the analytical solution.
const double freq = 1.0;
State state = context.getState(State::Energy);
const double initialEnergy = state.getKineticEnergy()+state.getPotentialEnergy();
for (int i = 0; i < 1000; ++i) {
state = context.getState(State::Positions | State::Velocities | State::Energy);
double time = state.getTime();
double expectedDist = 1.5+0.5*std::cos(freq*time);
ASSERT_EQUAL_VEC(Vec3(-0.5*expectedDist, 0, 0), state.getPositions()[0], 0.02);
ASSERT_EQUAL_VEC(Vec3(0.5*expectedDist, 0, 0), state.getPositions()[1], 0.02);
double expectedSpeed = -0.5*freq*std::sin(freq*time);
ASSERT_EQUAL_VEC(Vec3(-0.5*expectedSpeed, 0, 0), state.getVelocities()[0], 0.02);
ASSERT_EQUAL_VEC(Vec3(0.5*expectedSpeed, 0, 0), state.getVelocities()[1], 0.02);
double energy = state.getKineticEnergy()+state.getPotentialEnergy();
ASSERT_EQUAL_TOL(initialEnergy, energy, 0.01);
integrator.step(1);
}
ASSERT_EQUAL_TOL(10.0, context.getState(0).getTime(), 1e-5);
}
void testVVConstraints() {
const int numParticles = 8;
const int numConstraints = 5;
System system;
NoseHooverIntegrator integrator(0.001);
integrator.setConstraintTolerance(1e-5);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.addParticle(i%2 == 0 ? 5.0 : 10.0);
forceField->addParticle((i%2 == 0 ? 0.2 : -0.2), 0.5, 5.0);
}
system.addConstraint(0, 1, 1.0);
system.addConstraint(1, 2, 1.0);
system.addConstraint(2, 3, 1.0);
system.addConstraint(4, 5, 1.0);
system.addConstraint(6, 7, 1.0);
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(numParticles);
vector<Vec3> velocities(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int i = 0; i < numParticles; ++i) {
positions[i] = Vec3(i/2, (i+1)/2, 0);
velocities[i] = Vec3(genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5);
}
context.setPositions(positions);
context.setVelocities(velocities);
// Simulate it and see whether the constraints remain satisfied.
double initialEnergy = 0.0;
for (int i = 0; i < 1000; ++i) {
State state = context.getState(State::Positions | State::Energy | State::Velocities | State::Forces);
for (int j = 0; j < numConstraints; ++j) {
int particle1, particle2;
double distance;
system.getConstraintParameters(j, particle1, particle2, distance);
Vec3 p1 = state.getPositions()[particle1];
Vec3 p2 = state.getPositions()[particle2];
double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2]));
ASSERT_EQUAL_TOL(distance, dist, 1e-4);
}
double energy = state.getPotentialEnergy()+state.getKineticEnergy();
if (i == 1)
initialEnergy = energy;
else if (i > 1)
ASSERT_EQUAL_TOL(initialEnergy, energy, 0.01);
integrator.step(1);
}
}
void testVVConstrainedClusters() {
const int numParticles = 7;
System system;
NoseHooverIntegrator integrator(0.001);
integrator.setConstraintTolerance(1e-5);
NonbondedForce* forceField = new NonbondedForce();
for (int i = 0; i < numParticles; ++i) {
system.addParticle(i > 1 ? 1.0 : 10.0);
forceField->addParticle((i%2 == 0 ? 0.2 : -0.2), 0.5, 5.0);
}
system.addConstraint(0, 1, 1.0);
system.addConstraint(0, 2, 1.0);
system.addConstraint(0, 3, 1.0);
system.addConstraint(0, 4, 1.0);
system.addConstraint(1, 5, 1.0);
system.addConstraint(1, 6, 1.0);
system.addConstraint(2, 3, sqrt(2.0));
system.addConstraint(2, 4, sqrt(2.0));
system.addConstraint(3, 4, sqrt(2.0));
system.addConstraint(5, 6, sqrt(2.0));
system.addForce(forceField);
Context context(system, integrator, platform);
vector<Vec3> positions(numParticles);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(1, 0, 0);
positions[2] = Vec3(-1, 0, 0);
positions[3] = Vec3(0, 1, 0);
positions[4] = Vec3(0, 0, 1);
positions[5] = Vec3(2, 0, 0);
positions[6] = Vec3(1, 1, 0);
vector<Vec3> velocities(numParticles);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int i = 0; i < numParticles; ++i)
velocities[i] = Vec3(genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5, genrand_real2(sfmt)-0.5);
context.setPositions(positions);
context.setVelocities(velocities);
// Simulate it and see whether the constraints remain satisfied.
double initialEnergy = 0.0;
for (int i = 0; i < 1000; ++i) {
State state = context.getState(State::Positions | State::Energy | State::Velocities | State::Forces);
for (int j = 0; j < system.getNumConstraints(); ++j) {
int particle1, particle2;
double distance;
system.getConstraintParameters(j, particle1, particle2, distance);
Vec3 p1 = state.getPositions()[particle1];
Vec3 p2 = state.getPositions()[particle2];
double dist = std::sqrt((p1[0]-p2[0])*(p1[0]-p2[0])+(p1[1]-p2[1])*(p1[1]-p2[1])+(p1[2]-p2[2])*(p1[2]-p2[2]));
ASSERT_EQUAL_TOL(distance, dist, 2e-5);
}
double energy = state.getPotentialEnergy()+state.getKineticEnergy();
if (i == 1)
initialEnergy = energy;
else if (i > 1)
ASSERT_EQUAL_TOL(initialEnergy, energy, 0.01);
integrator.step(1);
}
}
void testVVConstrainedMasslessParticles() {
System system;
system.addParticle(0.0);
system.addParticle(1.0);
system.addConstraint(0, 1, 1.5);
vector<Vec3> positions(2);
positions[0] = Vec3(-1, 0, 0);
positions[1] = Vec3(1, 0, 0);
NoseHooverIntegrator integrator(0.01);
bool failed = false;
try {
// This should throw an exception.
Context context(system, integrator, platform);
}
catch (exception& ex) {
failed = true;
}
ASSERT(failed);
// Now make both particles massless, which should work.
system.setParticleMass(1, 0.0);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(300.0);
integrator.step(1);
State state = context.getState(State::Velocities);
ASSERT_EQUAL(0.0, state.getVelocities()[0][0]);
}
/**
* Make sure that virtual sites are updated correctly
*/
void testThreeParticleVirtualSite() {
System system;
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(1.0);
system.addParticle(0.0);
system.setVirtualSite(3, new ThreeParticleAverageSite(0, 1, 2, 0.2, 0.3, 0.5));
CustomExternalForce* forceField = new CustomExternalForce("-a*x");
system.addForce(forceField);
forceField->addPerParticleParameter("a");
vector<double> params(1);
params[0] = 0.1;
forceField->addParticle(0, params);
params[0] = 0.2;
forceField->addParticle(1, params);
params[0] = 0.3;
forceField->addParticle(2, params);
params[0] = 0.4;
forceField->addParticle(3, params);
NoseHooverIntegrator integrator(0.002);
Context context(system, integrator, platform);
vector<Vec3> positions(4);
positions[0] = Vec3(0, 0, 0);
positions[1] = Vec3(1, 0, 0);
positions[2] = Vec3(0, 1, 0);
context.setPositions(positions);
context.applyConstraints(0.0001);
for (int i = 0; i < 1000; i++) {
State state = context.getState(State::Positions | State::Forces);
const vector<Vec3>& pos = state.getPositions();
ASSERT_EQUAL_VEC(pos[0]*0.2+pos[1]*0.3+pos[2]*0.5, pos[3], 1e-5);
ASSERT_EQUAL_VEC(Vec3(0.1+0.4*0.2, 0, 0), state.getForces()[0], 1e-5);
ASSERT_EQUAL_VEC(Vec3(0.2+0.4*0.3, 0, 0), state.getForces()[1], 1e-5);
ASSERT_EQUAL_VEC(Vec3(0.3+0.4*0.5, 0, 0), state.getForces()[2], 1e-5);
integrator.step(1);
}
}
void runPlatformTests();
int main(int argc, char* argv[]) {
try {
initializeTests(argc, argv);
testVVSingleBond();
testVVConstraints();
testVVConstrainedClusters();
testVVConstrainedMasslessParticles();
testThreeParticleVirtualSite();
runPlatformTests();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
}
/* -------------------------------------------------------------------------- *
* OpenMM *
* -------------------------------------------------------------------------- *
* This is part of the OpenMM molecular simulation toolkit originating from *
* Simbios, the NIH National Center for Physics-Based Simulation of *
* Biological Structures at Stanford, funded under the NIH Roadmap for *
* Medical Research, grant U54 GM072970. See https://simtk.org. *
* *
* Portions copyright (c) 2019 Stanford University and the Authors. *
* Authors: Andreas Krämer and Andrew C. Simmonett *
* Contributors: *
* *
* Permission is hereby granted, free of charge, to any person obtaining a *
* copy of this software and associated documentation files (the "Software"), *
* to deal in the Software without restriction, including without limitation *
* the rights to use, copy, modify, merge, publish, distribute, sublicense, *
* and/or sell copies of the Software, and to permit persons to whom the *
* Software is furnished to do so, subject to the following conditions: *
* *
* The above copyright notice and this permission notice shall be included in *
* all copies or substantial portions of the Software. *
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, *
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL *
* THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE *
* USE OR OTHER DEALINGS IN THE SOFTWARE. *
* -------------------------------------------------------------------------- */
#include "openmm/internal/AssertionUtilities.h"
#include "openmm/NoseHooverChain.h"
#include "openmm/NoseHooverIntegrator.h"
#include "openmm/Context.h"
#include "openmm/State.h"
#include "openmm/HarmonicBondForce.h"
#include "openmm/NonbondedForce.h"
#include "openmm/CustomExternalForce.h"
#include "openmm/System.h"
#include "SimTKOpenMMRealType.h"
#include "sfmt/SFMT.h"
#include <iostream>
#include <sstream>
#include <iomanip>
#include <vector>
#include <algorithm>
#include <numeric>
using namespace OpenMM;
using namespace std;
void testHarmonicOscillator() {
const double mass = 1.0;
double temperature = 300;
double frequency = 1;
double mts = 1, ys = 1, chain_length = 3;
System system;
system.addParticle(mass);
vector<Vec3> positions(1);
positions[0] = Vec3(0.5,0.5,0.5);
vector<Vec3> velocities(1);
velocities[0] = Vec3(0, 0, 0);
auto harmonic_restraint = new CustomExternalForce("0.5*(x^2+y^2+z^2)");
harmonic_restraint->addParticle(0);
system.addForce(harmonic_restraint);
NoseHooverIntegrator integrator(0.001);
integrator.addThermostat(temperature, frequency, chain_length, mts, ys);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocities(velocities);
double mean_temperature=0;
// equilibration
integrator.step(2000);
for (size_t i=0; i < 2500; i++){
integrator.step(10);
State state = context.getState(State::Energy | State::Positions | State::Velocities);
double kinetic_energy = state.getKineticEnergy();
double temp = kinetic_energy/(0.5*3*BOLTZ);
mean_temperature = (i*mean_temperature + temp)/(i+1);
double PE = state.getPotentialEnergy();
double time = state.getTime();
double energy = kinetic_energy + PE + integrator.computeHeatBathEnergy();
}
ASSERT_EQUAL_TOL(temperature, mean_temperature, 0.02);
}
int makeDimerBox(System& system, std::vector<Vec3>& positions, bool constrain=true, int numMolecules=20, double bondLength=0.1){
double boxLength = 2; // nm
Vec3 a(boxLength, 0.0, 0.0);
Vec3 b(0.0, boxLength, 0.0);
Vec3 c(0.0, 0.0, boxLength);
double mass = 20;
double bondForceConstant = 30000; //0.001;
int numDOF = 0;
NonbondedForce* forceField = new NonbondedForce();
HarmonicBondForce* bondForce = new HarmonicBondForce();
for(int molecule = 0; molecule < numMolecules; ++molecule) {
int particle1 = system.addParticle(mass);
int particle2 = system.addParticle(mass);
forceField->addParticle(0.0, 0.1, 1.0);
forceField->addParticle(0.0, 0.1, 1.0);
forceField->addException(particle1, particle2, 0, 0, 0);
bondForce->addBond(particle1, particle2, bondLength, bondForceConstant);
numDOF += 6;
if (constrain) {
system.addConstraint(particle1, particle2, bondLength);
numDOF -= 1;
}
}
forceField->setCutoffDistance(.99*boxLength/2);
forceField->setSwitchingDistance(.88*boxLength/2);
forceField->setUseSwitchingFunction(true);
forceField->setUseDispersionCorrection(false);
forceField->setNonbondedMethod(NonbondedForce::CutoffPeriodic);
system.addForce(forceField);
system.addForce(bondForce);
system.setDefaultPeriodicBoxVectors(a, b, c);
OpenMM_SFMT::SFMT sfmt;
init_gen_rand(0, sfmt);
for (int i = 0; i < numMolecules; i++) {
while (true) {
Vec3 pos = Vec3(boxLength*genrand_real2(sfmt), boxLength*genrand_real2(sfmt), boxLength*genrand_real2(sfmt));
Vec3 pos1 = pos + Vec3(0,0, bondLength/2);
Vec3 pos2 = pos + Vec3(0,0,-bondLength/2);
double minDist = 2*boxLength;
for (int j = 0; j < i; j++) {
Vec3 delta = pos1-positions[j];
minDist = std::min(minDist, sqrt(delta.dot(delta)));
delta = pos2-positions[j];
minDist = std::min(minDist, sqrt(delta.dot(delta)));
}
if (minDist > 0.15) {
positions[2*i+0] = pos1;
positions[2*i+1] = pos2;
break;
}
}
}
return numDOF;
}
void testDimerBox(bool constrain=true) {
// Check conservation of system + bath energy for a harmonic oscillator
int numMolecules = 20;
double bondLength = 0.1;
double bondLengthSquared = bondLength * bondLength;
System system;
std::vector<Vec3> positions(numMolecules*2);
int numDOF = makeDimerBox(system, positions, constrain, numMolecules, bondLength);
bool simpleConstruct = true;
double temperature = 300; // kelvin
double collisionFrequency = 200; // 1/ps
int numMTS = 3;
int numYS = 3;
int chainLength = 5;
auto integrator = simpleConstruct ? NoseHooverIntegrator(temperature, collisionFrequency, 0.001, chainLength, numMTS, numYS)
: NoseHooverIntegrator(0.001);
if (!simpleConstruct)
integrator.addThermostat(temperature, collisionFrequency, chainLength, numMTS, numYS);
Context context(system, integrator, platform);
context.setPositions(positions);
context.setVelocitiesToTemperature(temperature);
int nSteps = 1500;
double mean_temp = 0.0;
std::vector<double> energies(nSteps);
for (int i = 0; i < nSteps; ++i) {
integrator.step(1);
State state = context.getState(State::Energy | (constrain ? State::Positions : 0));
if (constrain) {
auto positions = state.getPositions();
for(int i = 0; i < numMolecules; ++i) {
Vec3 delta = positions[2*i+1] - positions[2*i];
double dR2 = delta.dot(delta);
ASSERT_EQUAL_TOL(bondLengthSquared, dR2, 1e-4);
}
}
double KE = state.getKineticEnergy();
double PE = state.getPotentialEnergy();
double time = state.getTime();
double instantaneous_temperature = 2 * KE / (BOLTZ * numDOF);
mean_temp = (i*mean_temp + instantaneous_temperature)/(i+1);
double energy = KE + PE + integrator.computeHeatBathEnergy();
energies[i] = energy;
}
double sum = std::accumulate(energies.begin(), energies.end(), 0.0);
double mean = sum / energies.size();
double sq_sum = std::inner_product(energies.begin(), energies.end(), energies.begin(), 0.0);
double std = std::sqrt(sq_sum / energies.size() - mean * mean);
double relative_std = std / mean;
// Check mean temperature
ASSERT_USUALLY_EQUAL_TOL(temperature, mean_temp, 1e-2);
// Check fluctuation of conserved (total bath + system) energy
ASSERT_USUALLY_EQUAL_TOL(relative_std, 0, 5e-4);
}
void testCheckpoints() {
// Create a system with Drude-like particles to be thermostated as a pair, as well as another
// particle to be thermostated independently, to test all integrator features.
double timeStep = 0.001;
NoseHooverIntegrator integrator(timeStep), newIntegrator(timeStep);
System system;
double mass = 1;
system.addParticle(8*mass);
system.addParticle(mass);
system.addParticle(5*mass);
HarmonicBondForce* force = new HarmonicBondForce();
force->addBond(0, 1, 0.1, 50.0);
force->addBond(0, 2, 0.1, 50.0);
system.addForce(force);
double kineticEnergy = 1e6;
double temperature=300, collisionFrequency=1, chainLength=3, numMTS=3, numYS=3;
chainLength = 10;
integrator.addSubsystemThermostat(std::vector<int>{2}, std::vector<std::pair<int,int>>{{0,1}}, temperature, collisionFrequency, temperature, collisionFrequency,
chainLength, numMTS, numYS);
newIntegrator.addSubsystemThermostat(std::vector<int>{2}, std::vector<std::pair<int,int>>{{0,1}}, temperature, collisionFrequency, temperature, collisionFrequency,
chainLength, numMTS, numYS);
Context context(system, integrator, platform);
Context newContext(system, newIntegrator, platform);
std::vector<Vec3> positions(3);
std::vector<Vec3> velocities(3);
positions[1] = {0.1, 0.0, 0.0};
velocities[1] = {0.1,0.2,-0.2};
positions[2] = {-0.1, 0.001, 0.001};
velocities[2] = {-0.1,0.2,-0.2};
context.setPositions(positions);
context.setVelocities(velocities);
// Run a short simulation and checkpoint..
integrator.step(500);
std::stringstream checkpoint;
context.createCheckpoint(checkpoint);
// Now continue the simulation
integrator.step(5);
// And try the same, starting from the checkpoint
newContext.loadCheckpoint(checkpoint);
newIntegrator.step(5);
State state1 = context.getState(State::Positions | State::Velocities);
State state2 = newContext.getState(State::Positions | State::Velocities);
ASSERT_EQUAL_VEC(state1.getPositions()[0], state2.getPositions()[0], 1e-6);
ASSERT_EQUAL_VEC(state1.getPositions()[1], state2.getPositions()[1], 1e-6);
ASSERT_EQUAL_VEC(state1.getVelocities()[0], state2.getVelocities()[0], 1e-6);
ASSERT_EQUAL_VEC(state1.getVelocities()[1], state2.getVelocities()[1], 1e-6);
}
void testAPIChangeNumParticles() {
bool constrain = true;
int numMolecules = 20;
double bondLength = 0.1;
double bondLengthSquared = bondLength * bondLength;
System system;
std::vector<Vec3> positions(numMolecules*2);
int numDOF = makeDimerBox(system, positions, constrain, numMolecules, bondLength);
}
void runPlatformTests();
int main(int argc, char* argv[]) {
try {
initializeTests(argc, argv);
testHarmonicOscillator();
bool constrain;
constrain = false; testDimerBox(constrain);
constrain = true; testDimerBox(constrain);
testCheckpoints();
runPlatformTests();
}
catch(const exception& e) {
cout << "exception: " << e.what() << endl;
return 1;
}
cout << "Done" << endl;
return 0;
}
......@@ -76,7 +76,12 @@ class WrapperGenerator:
'static std::vector<std::string> OpenMM::Platform::loadPluginsFromDirectory',
'Vec3 OpenMM::LocalCoordinatesSite::getOriginWeights',
'Vec3 OpenMM::LocalCoordinatesSite::getXWeights',
'Vec3 OpenMM::LocalCoordinatesSite::getYWeights']
'Vec3 OpenMM::LocalCoordinatesSite::getYWeights',
'std::vector<double> OpenMM::NoseHooverChain::getYoshidaSuzukiWeights',
'const std::vector<int>& OpenMM::NoseHooverIntegrator::getAllThermostatedIndividualParticles',
'const std::vector<std::tuple<int, int, double> >& OpenMM::NoseHooverIntegrator::getAllThermostatedPairs',
'virtual void OpenMM::NoseHooverIntegrator::stateChanged'
]
self.hideClasses = ['Kernel', 'KernelImpl', 'KernelFactory', 'ContextImpl', 'SerializationNode', 'SerializationProxy']
self.nodeByID={}
......@@ -165,6 +170,7 @@ class CHeaderGenerator(WrapperGenerator):
'std::vector< std::string >': 'OpenMM_StringArray',
'std::vector< Vec3 >': 'OpenMM_Vec3Array',
'std::vector< std::pair< int, int > >': 'OpenMM_BondArray',
'const std::vector< std::pair< int, int > >': 'OpenMM_BondArray',
'std::map< std::string, double >': 'OpenMM_ParameterArray',
'std::map< std::string, std::string >': 'OpenMM_PropertyArray',
'std::vector< double >': 'OpenMM_DoubleArray',
......
......@@ -184,7 +184,7 @@ def buildKeywordDictionary(major_version_num=MAJOR_VERSION_NUM,
if not openmm_lib_path:
reportError("Set OPENMM_LIB_PATH to point to the lib directory for OpenMM")
extra_compile_args=[]
extra_compile_args=['-std=c++11']
extra_link_args=[]
if platform.system() == "Windows":
define_macros.append( ('WIN32', None) )
......
......@@ -8,7 +8,7 @@ Structures at Stanford, funded under the NIH Roadmap for Medical Research,
grant U54 GM072970. See https://simtk.org. This code was originally part of
the ParmEd program and was ported for use with OpenMM.
Copyright (c) 2014-2016 the Authors
Copyright (c) 2014-2020 the Authors
Author: Jason M. Swails
Contributors: Jing Huang
......@@ -41,7 +41,7 @@ import sys
import simtk.openmm as mm
from simtk.openmm.vec3 import Vec3
import simtk.unit as u
from simtk.openmm.app import (forcefield as ff, Topology, element)
from simtk.openmm.app import (forcefield as ff, Topology, element, PDBFile)
from simtk.openmm.app.amberprmtopfile import HCT, OBC1, OBC2, GBn, GBn2
from simtk.openmm.app.internal.customgbforces import (GBSAHCTForce,
GBSAOBC1Force, GBSAOBC2Force, GBSAGBnForce, GBSAGBn2Force)
......@@ -164,6 +164,7 @@ class CharmmPsfFile(object):
CMAP_FORCE_GROUP = 5
NONBONDED_FORCE_GROUP = 6
GB_FORCE_GROUP = 6
DRUDE_FORCE_GROUP = 7
@_catchindexerror
def __init__(self, psf_name, periodicBoxVectors=None, unitCellDimensions=None):
......@@ -218,6 +219,7 @@ class CharmmPsfFile(object):
atom_list = AtomList()
if IsDrudePSF:
drudeconsts_list = TrackedList()
PDBFile._loadNameReplacementTables()
for i in xrange(natom):
words = psfsections['NATOM'][1][i].split()
system = words[1]
......@@ -238,6 +240,12 @@ class CharmmPsfFile(object):
charge = conv(words[6], float, 'partial charge')
mass = conv(words[7], float, 'atomic mass')
props = words[8:]
if resname in PDBFile._residueNameReplacements:
resname = PDBFile._residueNameReplacements[resname]
if resname in PDBFile._atomNameReplacements:
atomReplacements = PDBFile._atomNameReplacements[resname]
if name in atomReplacements:
name = atomReplacements[name]
atom = residue_list.add_atom(system, resid, resname, name,
attype, charge, mass, inscode, props)
atom_list.append(atom)
......@@ -466,6 +474,37 @@ class CharmmPsfFile(object):
else:
self.box_vectors = periodicBoxVectors
def _build_exclusion_list(self):
pair_12_set = set()
pair_13_set = set()
pair_14_set = set()
for bond in self.bond_list:
a1, a2 = bond.atom1, bond.atom2
pair = (min(a1.idx, a2.idx), max(a1.idx, a2.idx),)
pair_12_set.add(pair)
for bond in self.bond_list:
a2, a3 = bond.atom1, bond.atom2
for a1 in a2.bond_partners:
pair = (min(a1.idx, a3.idx), max(a1.idx, a3.idx),)
if a1 != a3:
pair_13_set.add(pair)
for a4 in a3.bond_partners:
pair = (min(a2.idx, a4.idx), max(a2.idx, a4.idx),)
if a2 != a4:
pair_13_set.add(pair)
for bond in self.bond_list:
a2, a3 = bond.atom1, bond.atom2
for a1 in a2.bond_partners:
for a4 in a3.bond_partners:
pair = (min(a1.idx, a4.idx), max(a1.idx, a4.idx),)
if a1 != a4:
pair_14_set.add(pair)
# in case there are 3,4,5-member rings
self.pair_12_list = list(sorted(pair_12_set))
self.pair_13_list = list(sorted(pair_13_set - pair_12_set))
self.pair_14_list = list(sorted(pair_14_set - pair_13_set.union(pair_12_set)))
@staticmethod
def _convert(string, type, message):
"""Converts a string to a specific type, making sure to raise
......@@ -1320,31 +1359,29 @@ class CharmmPsfFile(object):
# now, add the actual force to the system
system.addForce(nbtforce)
# build 1-2, 1-3 and 1-4 pairs from connectivity
if verbose:
print('Build exclusion list...')
self._build_exclusion_list()
if verbose:
print('\tNumber of 1-2 pairs: %i' % len(self.pair_12_list))
print('\tNumber of 1-3 pairs: %i' % len(self.pair_13_list))
print('\tNumber of 1-4 pairs: %i' % len(self.pair_14_list))
# Add 1-4 interactions
excluded_atom_pairs = set() # save these pairs so we don't zero them out
sigma_scale = 2**(-1/6)
nbxmod = abs(params.nbxmod)
if nbxmod == 4:
for ia1, ia4 in self.pair_14_list:
force.addException(ia1, ia4, 0.0, 0.1, 0.0)
if nbxmod == 5:
for tor in self.dihedral_parameter_list:
# First check to see if atoms 1 and 4 are already excluded because
# they are 1-2 or 1-3 pairs (would happen in 6-member rings or
# fewer). Then check that they're not already added as exclusions
if tor.atom1 in tor.atom4.bond_partners: continue
if tor.atom1 in tor.atom4.angle_partners: continue
key = min((tor.atom1.idx, tor.atom4.idx),
(tor.atom4.idx, tor.atom1.idx))
if key in excluded_atom_pairs: continue # multiterm...
charge_prod = (tor.atom1.charge * tor.atom4.charge)
epsilon = (sqrt(abs(tor.atom1.type.epsilon_14) * ene_conv *
abs(tor.atom4.type.epsilon_14) * ene_conv))
sigma = (tor.atom1.type.rmin_14 + tor.atom4.type.rmin_14) * (
length_conv * sigma_scale)
force.addException(tor.atom1.idx, tor.atom4.idx,
charge_prod, sigma, epsilon)
excluded_atom_pairs.add(
min((tor.atom1.idx, tor.atom4.idx),
(tor.atom4.idx, tor.atom1.idx))
)
for ia1, ia4 in self.pair_14_list:
atom1 = self.atom_list[ia1]
atom4 = self.atom_list[ia4]
charge_prod = (atom1.charge * atom4.charge)
epsilon = sqrt(abs(atom1.type.epsilon_14 * atom4.type.epsilon_14)) * ene_conv
sigma = (atom1.type.rmin_14 + atom4.type.rmin_14) * (length_conv * sigma_scale)
force.addException(ia1, ia4, charge_prod, sigma, epsilon)
# Add excluded atoms
# Drude and lonepairs will be excluded based on their parent atoms
......@@ -1368,33 +1405,24 @@ class CharmmPsfFile(object):
for i in range(len(excludeterm)):
for j in range(i):
force.addException(excludeterm[j], excludeterm[i], 0.0, 0.1, 0.0)
# Exclude all bonds and angles, as well as the lonepair/Drude attached onto them
for atom in self.atom_list:
if nbxmod > 1:
for atom2 in atom.bond_partners:
if atom2.idx > atom.idx:
for excludeatom in [atom.idx]+parent_exclude_list[atom.idx]:
for excludeatom2 in [atom2.idx]+parent_exclude_list[atom2.idx]:
force.addException(excludeatom, excludeatom2, 0.0, 0.1, 0.0)
if nbxmod > 2:
for atom2 in atom.angle_partners:
if atom2.idx > atom.idx:
for excludeatom in [atom.idx]+parent_exclude_list[atom.idx]:
for excludeatom2 in [atom2.idx]+parent_exclude_list[atom2.idx]:
force.addException(excludeatom, excludeatom2, 0.0, 0.1, 0.0)
if nbxmod > 3:
for atom2 in atom.dihedral_partners:
if atom2.idx <= atom.idx: continue
if ((atom.idx, atom2.idx) in excluded_atom_pairs):
continue
force.addException(atom.idx, atom2.idx, 0.0, 0.1, 0.0)
# Exclude 1-2 and 1-3 pairs as well as the lonepair/Drude attached onto them
if nbxmod > 1:
for ia1, ia2 in self.pair_12_list:
for excludeatom in [ia1]+parent_exclude_list[ia1]:
for excludeatom2 in [ia2]+parent_exclude_list[ia2]:
force.addException(excludeatom, excludeatom2, 0.0, 0.1, 0.0)
if nbxmod > 2:
for ia1, ia3 in self.pair_13_list:
for excludeatom in [ia1]+parent_exclude_list[ia1]:
for excludeatom2 in [ia3]+parent_exclude_list[ia3]:
force.addException(excludeatom, excludeatom2, 0.0, 0.1, 0.0)
system.addForce(force)
# Add Drude particles (Drude force)
if has_drude_particle:
if verbose: print('Adding Drude force and Thole screening...')
drudeforce = mm.DrudeForce()
drudeforce.setForceGroup(7)
drudeforce.setForceGroup(self.DRUDE_FORCE_GROUP)
for pair in self.drudepair_list:
parentatom=pair[0]
drudeatom=pair[1]
......@@ -1423,24 +1451,16 @@ class CharmmPsfFile(object):
particleMap = {}
for i in range(drudeforce.getNumParticles()):
particleMap[drudeforce.getParticleParameters(i)[0]] = i
for bond in self.bond_list:
alpha1 = self.drudeconsts_list[bond.atom1.idx][0]
alpha2 = self.drudeconsts_list[bond.atom2.idx][0]
if abs(alpha1) > TINY and abs(alpha2) > TINY: # both are Drude parent atoms
thole1 = self.drudeconsts_list[bond.atom1.idx][1]
thole2 = self.drudeconsts_list[bond.atom2.idx][1]
drude1 = bond.atom1.idx + 1 # CHARMM psf has hard-coded rule that the Drude is next to its parent
drude2 = bond.atom2.idx + 1
drudeforce.addScreenedPair(particleMap[drude1], particleMap[drude2], thole1+thole2)
for ang in self.angle_list:
alpha1 = self.drudeconsts_list[ang.atom1.idx][0]
alpha2 = self.drudeconsts_list[ang.atom3.idx][0]
# Apply thole screening for 1-2 and 1-3 pairs
for ia1, ia2 in self.pair_12_list + self.pair_13_list:
alpha1 = self.drudeconsts_list[ia1][0]
alpha2 = self.drudeconsts_list[ia2][0]
if abs(alpha1) > TINY and abs(alpha2) > TINY: # both are Drude parent atoms
thole1 = self.drudeconsts_list[ang.atom1.idx][1]
thole2 = self.drudeconsts_list[ang.atom3.idx][1]
drude1 = ang.atom1.idx + 1 # CHARMM psf has hard-coded rule that the Drude is next to its parent
drude2 = ang.atom3.idx + 1
thole1 = self.drudeconsts_list[ia1][1]
thole2 = self.drudeconsts_list[ia2][1]
drude1 = ia1 + 1 # CHARMM psf has hard-coded rule that the Drude is next to its parent
drude2 = ia2 + 1
drudeforce.addScreenedPair(particleMap[drude1], particleMap[drude2], thole1+thole2)
# If we needed a CustomNonbondedForce, map all of the exceptions from
......
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -2983,7 +2983,7 @@
<ExternalBond from="14" />
<ExternalBond from="0" />
</Residue>
<Residue name="HOH">
<Residue name="HOH" rigidWater="false">
<Atom name="H1" type="403" />
<Atom name="H2" type="403" />
<Atom name="O" type="402" />
......
......@@ -1756,7 +1756,7 @@
<ExternalBond from="14" />
<ExternalBond from="0" />
</Residue>
<Residue name="HOH">
<Residue name="HOH" rigidWater="false">
<Atom name="H1" type="248" />
<Atom name="H2" type="248" />
<Atom name="O" type="247" />
......
......@@ -4,7 +4,7 @@
<Type name="381" class="74" element="H" mass="1.008"/>
</AtomTypes>
<Residues>
<Residue name="HOH">
<Residue name="HOH" rigidWater="false">
<Atom name="H1" type="381"/>
<Atom name="H2" type="381"/>
<Atom name="O" type="380"/>
......
......@@ -267,6 +267,8 @@ class ForceField(object):
template = ForceField._TemplateData(resName)
if 'override' in residue.attrib:
template.overrideLevel = int(residue.attrib['override'])
if 'rigidWater' in residue.attrib:
template.rigidWater = (residue.attrib['rigidWater'].lower() == 'true')
atomIndices = template.atomIndices
for ia, atom in enumerate(residue.findall('Atom')):
params = {}
......@@ -597,6 +599,7 @@ class ForceField(object):
self.bonds = []
self.externalBonds = []
self.overrideLevel = 0
self.rigidWater = True
def getAtomIndexByName(self, atom_name):
"""Look up an atom index by atom name, providing a helpful error message if not found."""
......@@ -637,7 +640,7 @@ class ForceField(object):
def areParametersIdentical(self, template2, matchingAtoms, matchingAtoms2):
"""Get whether this template and another one both assign identical atom types and parameters to all atoms.
Parameters
----------
template2: _TemplateData
......@@ -1054,7 +1057,7 @@ class ForceField(object):
return [templates, unique_unmatched_residues]
def createSystem(self, topology, nonbondedMethod=NoCutoff, nonbondedCutoff=1.0*unit.nanometer,
constraints=None, rigidWater=True, removeCMMotion=True, hydrogenMass=None, residueTemplates=dict(),
constraints=None, rigidWater=None, removeCMMotion=True, hydrogenMass=None, residueTemplates=dict(),
ignoreExternalBonds=False, switchDistance=None, flexibleConstraints=False, **args):
"""Construct an OpenMM System representing a Topology with this force field.
......@@ -1070,9 +1073,10 @@ class ForceField(object):
constraints : object=None
Specifies which bonds and angles should be implemented with constraints.
Allowed values are None, HBonds, AllBonds, or HAngles.
rigidWater : boolean=True
rigidWater : boolean=None
If true, water molecules will be fully rigid regardless of the value
passed for the constraints argument
passed for the constraints argument. If None (the default), it uses the
default behavior for this force field's water model.
removeCMMotion : boolean=True
If true, a CMMotionRemover will be added to the System
hydrogenMass : mass=None
......@@ -1112,6 +1116,7 @@ class ForceField(object):
data.atoms = list(topology.atoms())
for atom in data.atoms:
data.excludeAtomWith.append([])
rigidResidue = [False]*topology.getNumResidues()
# Make a list of all bonds
......@@ -1149,6 +1154,13 @@ class ForceField(object):
unmatchedResidues.append(res)
else:
data.recordMatchedAtomParameters(res, template, matches)
if res.name == 'HOH':
# Determine whether this should be a rigid water.
if rigidWater is None and template is not None:
rigidResidue[res.index] = template.rigidWater
elif rigidWater:
rigidResidue[res.index] = True
# Try to apply patches to find matches for any unmatched residues.
......@@ -1272,12 +1284,11 @@ class ForceField(object):
atom1 = data.atoms[bond.atom1]
atom2 = data.atoms[bond.atom2]
bond.isConstrained = atom1.element is elem.hydrogen or atom2.element is elem.hydrogen
if rigidWater:
for bond in data.bonds:
atom1 = data.atoms[bond.atom1]
atom2 = data.atoms[bond.atom2]
if atom1.residue.name == 'HOH' and atom2.residue.name == 'HOH':
bond.isConstrained = True
for bond in data.bonds:
atom1 = data.atoms[bond.atom1]
atom2 = data.atoms[bond.atom2]
if rigidResidue[atom1.residue.index] and rigidResidue[atom2.residue.index]:
bond.isConstrained = True
# Identify angles that should be implemented with constraints
......@@ -1294,14 +1305,13 @@ class ForceField(object):
data.isAngleConstrained.append(numH == 2 or (numH == 1 and atom2.element is elem.oxygen))
else:
data.isAngleConstrained = len(data.angles)*[False]
if rigidWater:
for i in range(len(data.angles)):
angle = data.angles[i]
atom1 = data.atoms[angle[0]]
atom2 = data.atoms[angle[1]]
atom3 = data.atoms[angle[2]]
if atom1.residue.name == 'HOH' and atom2.residue.name == 'HOH' and atom3.residue.name == 'HOH':
data.isAngleConstrained[i] = True
for i in range(len(data.angles)):
angle = data.angles[i]
atom1 = data.atoms[angle[0]]
atom2 = data.atoms[angle[1]]
atom3 = data.atoms[angle[2]]
if rigidResidue[atom1.residue.index] and rigidResidue[atom2.residue.index] and rigidResidue[atom3.residue.index]:
data.isAngleConstrained[i] = True
# Add virtual sites
......@@ -1844,6 +1854,16 @@ def _matchImproper(data, torsion, generator):
(a2, a3) = (a3, a2)
match = (a2, a3, torsion[0], a4, tordef)
break
elif tordef.ordering == 'smirnoff':
# topology atom indexes
a1 = torsion[0]
a2 = torsion[t2[1]]
a3 = torsion[t3[1]]
a4 = torsion[t4[1]]
# enforce exact match
match = (a1, a2, a3, a4, tordef)
break
return match
......@@ -2035,7 +2055,7 @@ class PeriodicTorsionGenerator(object):
def registerImproperTorsion(self, parameters, ordering='default'):
torsion = self.ff._parseTorsion(parameters)
if torsion is not None:
if ordering in ['default', 'charmm', 'amber']:
if ordering in ['default', 'charmm', 'amber', 'smirnoff']:
torsion.ordering = ordering
else:
raise ValueError('Illegal ordering type %s for improper torsion %s' % (ordering, torsion))
......@@ -2119,7 +2139,13 @@ class PeriodicTorsionGenerator(object):
(a1, a2, a3, a4, tordef) = match
for i in range(len(tordef.phase)):
if tordef.k[i] != 0:
force.addTorsion(a1, a2, a3, a4, tordef.periodicity[i], tordef.phase[i], tordef.k[i])
if tordef.ordering == 'smirnoff':
# Add all torsions in trefoil
force.addTorsion(a1, a2, a3, a4, tordef.periodicity[i], tordef.phase[i], tordef.k[i])
force.addTorsion(a1, a3, a4, a2, tordef.periodicity[i], tordef.phase[i], tordef.k[i])
force.addTorsion(a1, a4, a2, a3, tordef.periodicity[i], tordef.phase[i], tordef.k[i])
else:
force.addTorsion(a1, a2, a3, a4, tordef.periodicity[i], tordef.phase[i], tordef.k[i])
parsers["PeriodicTorsionForce"] = PeriodicTorsionGenerator.parseElement
......
......@@ -592,7 +592,8 @@ class GromacsTopFile(object):
top.addBond(atoms[int(fields[0])-1], atoms[int(fields[1])-1])
def createSystem(self, nonbondedMethod=ff.NoCutoff, nonbondedCutoff=1.0*unit.nanometer,
constraints=None, rigidWater=True, implicitSolvent=None, soluteDielectric=1.0, solventDielectric=78.5, ewaldErrorTolerance=0.0005, removeCMMotion=True, hydrogenMass=None):
constraints=None, rigidWater=True, implicitSolvent=None, soluteDielectric=1.0, solventDielectric=78.5,
ewaldErrorTolerance=0.0005, removeCMMotion=True, hydrogenMass=None, switchDistance=None):
"""Construct an OpenMM System representing the topology described by this
top file.
......@@ -627,6 +628,9 @@ class GromacsTopFile(object):
The mass to use for hydrogen atoms bound to heavy atoms. Any mass
added to a hydrogen is subtracted from the heavy atom to keep their
total mass the same.
switchDistance : float=None
The distance at which the potential energy switching function is turned on for
Lennard-Jones interactions. If this is None, no switching function will be used.
Returns
-------
......@@ -1091,6 +1095,9 @@ class GromacsTopFile(object):
nb.setNonbondedMethod(methodMap[nonbondedMethod])
nb.setCutoffDistance(nonbondedCutoff)
nb.setEwaldErrorTolerance(ewaldErrorTolerance)
if switchDistance is not None:
nb.setUseSwitchingFunction(True)
nb.setSwitchingDistance(switchDistance)
if self._defaults[1] in ('1', '3'):
methodMap = {ff.NoCutoff:mm.CustomNonbondedForce.NoCutoff,
ff.CutoffNonPeriodic:mm.CustomNonbondedForce.CutoffNonPeriodic,
......@@ -1100,6 +1107,9 @@ class GromacsTopFile(object):
ff.LJPME:mm.CustomNonbondedForce.CutoffPeriodic}
lj.setNonbondedMethod(methodMap[nonbondedMethod])
lj.setCutoffDistance(nonbondedCutoff)
if switchDistance is not None:
lj.setUseSwitchingFunction(True)
lj.setSwitchingDistance(switchDistance)
if has_nbfix_terms:
if self._defaults[1] != '2':
......@@ -1175,6 +1185,9 @@ class GromacsTopFile(object):
cforce.setCutoffDistance(nonbondedCutoff)
else:
raise ValueError('Unrecognized nonbonded method')
if switchDistance is not None:
cforce.setUseSwitchingFunction(True)
cforce.setSwitchingDistance(switchDistance)
for i in lj_idx_list:
cforce.addParticle((i - 1,)) # adjust for indexing from 0
......
......@@ -1004,7 +1004,7 @@ class Modeller(object):
del context
return actualVariants
def addExtraParticles(self, forcefield):
def addExtraParticles(self, forcefield, ignoreExternalBonds=False):
"""Add missing extra particles to the model that are required by a force
field.
......@@ -1023,6 +1023,10 @@ class Modeller(object):
----------
forcefield : ForceField
the ForceField defining what extra particles should be present
ignoreExternalBonds : boolean=False
If true, ignore external bonds when matching residues to templates.
This is useful when the Topology represents one piece of a larger
molecule, so chains are not terminated properly.
"""
# Create copies of all residue templates that have had all extra points removed.
......@@ -1090,7 +1094,7 @@ class Modeller(object):
signature = _createResidueSignature([atom.element for atom in residue.atoms()])
if signature in forcefield._templateSignatures:
for t in forcefield._templateSignatures[signature]:
if compiled.matchResidueToTemplate(residue, t, bondedToAtom, False) is not None:
if compiled.matchResidueToTemplate(residue, t, bondedToAtom, ignoreExternalBonds) is not None:
matchFound = True
if matchFound:
# Just copy the residue over.
......@@ -1109,7 +1113,7 @@ class Modeller(object):
if signature in forcefield._templateSignatures:
for t in forcefield._templateSignatures[signature]:
if t in templatesNoEP:
matches = compiled.matchResidueToTemplate(residueNoEP, templatesNoEP[t], bondedToAtomNoEP, False)
matches = compiled.matchResidueToTemplate(residueNoEP, templatesNoEP[t], bondedToAtomNoEP, ignoreExternalBonds)
if matches is not None:
template = t;
# Record the corresponding atoms.
......@@ -1255,9 +1259,9 @@ class Modeller(object):
adds whole copies of the pre-equilibrated membrane patch, so the box dimensions will always be
integer multiples of the patch size. That may lead to a larger membrane than what you requested.
This method has built in support for POPC and POPE lipids. You can also build other types of
membranes by providing a pre-equilibrated, solvated membrane patch that can be tiled in the XY
plane to form the membrane.
This method has built in support for POPC, POPE, DLPC, DLPE, DMPC, DOPC and DPPC lipids.
You can also build other types of membranes by providing a pre-equilibrated, solvated membrane patch
that can be tiled in the XY plane to form the membrane.
Parameters
----------
......@@ -1285,7 +1289,7 @@ class Modeller(object):
"""
if 'topology' in dir(lipidType) and 'positions' in dir(lipidType):
patch = lipidType
elif lipidType.upper() in ('POPC', 'POPE'):
elif lipidType.upper() in ('POPC', 'POPE', 'DLPC', 'DLPE', 'DMPC', 'DOPC', 'DPPC'):
patch = PDBFile(os.path.join(os.path.dirname(__file__), 'data', lipidType.upper()+'.pdb'))
else:
raise ValueError('Unsupported lipid type: '+lipidType)
......
......@@ -6,7 +6,7 @@ Simbios, the NIH National Center for Physics-Based Simulation of
Biological Structures at Stanford, funded under the NIH Roadmap for
Medical Research, grant U54 GM072970. See https://simtk.org.
Portions copyright (c) 2015-2019 Stanford University and the Authors.
Portions copyright (c) 2015-2020 Stanford University and the Authors.
Authors: Peter Eastman
Contributors: Jason Swails
......@@ -98,6 +98,16 @@ class PDBxFile(object):
chainIdCol = atomData.getAttributeIndex('auth_asym_id')
if chainIdCol == -1:
chainIdCol = atomData.getAttributeIndex('label_asym_id')
altChainIdCol = -1
else:
altChainIdCol = atomData.getAttributeIndex('label_asym_id')
if altChainIdCol != -1:
# Figure out which column is best to use for chain IDs.
idSet = set(row[chainIdCol] for row in atomData.getRowList())
altIdSet = set(row[altChainIdCol] for row in atomData.getRowList())
if len(altIdSet) > len(idSet):
chainIdCol, altChainIdCol = (altChainIdCol, chainIdCol)
elementCol = atomData.getAttributeIndex('type_symbol')
altIdCol = atomData.getAttributeIndex('label_alt_id')
modelCol = atomData.getAttributeIndex('pdbx_PDB_model_num')
......@@ -105,7 +115,9 @@ class PDBxFile(object):
yCol = atomData.getAttributeIndex('Cartn_y')
zCol = atomData.getAttributeIndex('Cartn_z')
lastChainId = None
lastAltChainId = None
lastResId = None
lastInsertionCode = ''
atomTable = {}
atomsInResidue = set()
models = []
......@@ -123,17 +135,26 @@ class PDBxFile(object):
if modelIndex == 0:
# This row defines a new atom.
if lastChainId != row[chainIdCol]:
if resInsertionCol == -1:
insertionCode = ''
else:
insertionCode = row[resInsertionCol]
if insertionCode in ('.', '?'):
insertionCode = ''
if lastChainId != row[chainIdCol] or (altChainIdCol != -1 and lastAltChainId != row[altChainIdCol]):
# The start of a new chain.
chain = top.addChain(row[chainIdCol])
lastChainId = row[chainIdCol]
lastResId = None
if lastResId != row[resNumCol] or lastChainId != row[chainIdCol] or (lastResId == '.' and row[atomNameCol] in atomsInResidue):
if altChainIdCol != -1:
lastAltChainId = row[altChainIdCol]
if lastResId != row[resNumCol] or lastChainId != row[chainIdCol] or lastInsertionCode != insertionCode or (lastResId == '.' and row[atomNameCol] in atomsInResidue):
# The start of a new residue.
resId = (None if resNumCol == -1 else row[resNumCol])
resIC = ('' if resInsertionCol == -1 else row[resInsertionCol])
resIC = insertionCode
res = top.addResidue(row[resNameCol], chain, resId, resIC)
lastResId = row[resNumCol]
lastInsertionCode = insertionCode
atomsInResidue.clear()
element = None
try:
......@@ -386,6 +407,8 @@ class PDBxFile(object):
raise ValueError('Particle position is NaN')
if any(math.isinf(norm(pos)) for pos in positions):
raise ValueError('Particle position is infinite')
nonHeterogens = PDBFile._standardResidues[:]
nonHeterogens.remove('HOH')
atomIndex = 1
posIndex = 0
for (chainIndex, chain) in enumerate(topology.chains()):
......@@ -401,14 +424,18 @@ class PDBxFile(object):
else:
resId = resIndex + 1
resIC = '.'
if res.name in nonHeterogens:
recordName = "ATOM"
else:
recordName = "HETATM"
for atom in res.atoms():
coords = positions[posIndex]
if atom.element is not None:
symbol = atom.element.symbol
else:
symbol = '?'
line = "ATOM %5d %-3s %-4s . %-4s %s ? %5s %s %10.4f %10.4f %10.4f 0.0 0.0 ? ? ? ? ? . %5s %4s %s %4s %5d"
print(line % (atomIndex, symbol, atom.name, res.name, chainName, resId, resIC, coords[0], coords[1], coords[2],
line = "%s %5d %-3s %-4s . %-4s %s ? %5s %s %10.4f %10.4f %10.4f 0.0 0.0 ? ? ? ? ? . %5s %4s %s %4s %5d"
print(line % (recordName, atomIndex, symbol, atom.name, res.name, chainName, resId, resIC, coords[0], coords[1], coords[2],
resId, res.name, chainName, atom.name, modelIndex), file=file)
posIndex += 1
atomIndex += 1
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment