00001 #ifndef SIMTK_MOLMODEL_VANDERWAALS_FORCE_H_
00002 #define SIMTK_MOLMODEL_VANDERWAALS_FORCE_H_
00003
00004 #include "SimTKsimbody.h"
00005 #include <vector>
00006 #include <iterator>
00007
00008 namespace SimTK {
00009
00010 namespace md {
00011 const Real angstroms = 0.10;
00012 const Real nanometers = 1.0;
00013 const Real square_nanometers = 1.0;
00014
00015 const Real kilocalories_per_mole = 4.184;
00016 const Real kilojoules_per_mole = 1.0;
00017
00018 const Real daltons = 1.0;
00019 }
00020
00021 class VanDerWaalsForce : public Force::Custom::Implementation
00022 {
00023 public:
00024 typedef AtomSubsystem::AtomIndex AtomIndex;
00025
00026
00027 typedef Real length_t;
00028 typedef Real area_t;
00029 typedef Real energy_t;
00030 typedef Real inverse_area_t;
00031 typedef Vec3 location_t;
00032 typedef Vec3 force_t;
00033
00034 class VdwAtom
00035 {
00036 public:
00037 VdwAtom()
00038 : rMin(1.90*md::angstroms), wellDepth(0.1*md::kilocalories_per_mole)
00039 {}
00040
00041 VdwAtom(AtomIndex atomIndex, length_t rMin, energy_t wellDepth)
00042 : atomIndex(atomIndex), rMin(rMin), wellDepth(wellDepth)
00043 {}
00044
00045 AtomIndex atomIndex;
00046 length_t rMin;
00047 energy_t wellDepth;
00048 };
00049
00050
00051 struct WallSphere
00052 {
00053 length_t radius;
00054 location_t center;
00055
00056 length_t vdwRadius;
00057 energy_t wellDepth;
00058 };
00059
00060 VanDerWaalsForce(const AtomSubsystem& atomSubsystem, length_t cutoff = 0.0 * nanometers)
00061 : atomSubsystem(atomSubsystem), cutoff(cutoff), cutoffSquared(cutoff * cutoff)
00062 {}
00063
00064 bool dependsOnlyOnPositions() const {return true;}
00065
00066 void addAtom(AtomSubsystem::AtomIndex atomIndex, length_t rMin, energy_t wellDepth)
00067 {
00068 if (AtomSubsystem::AtomIndex(vdwAtoms.size()) <= atomIndex)
00069 vdwAtoms.resize(atomIndex + 1);
00070
00071 vdwAtoms[atomIndex] = VdwAtom(atomIndex, rMin, wellDepth);
00072 }
00073
00074 void addBoundarySphere(
00075 const location_t& center,
00076 length_t radius,
00077 length_t vdwRadius,
00078 energy_t wellDepth)
00079 {
00080 WallSphere sphere;
00081 sphere.center = center;
00082 sphere.radius = radius;
00083 sphere.vdwRadius = vdwRadius;
00084 sphere.wellDepth = wellDepth;
00085 wallSpheres.push_back(sphere);
00086 }
00087
00088 void decorateAtoms(DecorationSubsystem& decorations, Real scale = 1.0) const {
00089 for (AtomIndex a(0); a < vdwAtoms.size(); ++a)
00090 {
00091 const VdwAtom& vdwAtom = vdwAtoms[a];
00092
00093 if (!vdwAtom.atomIndex.isValid()) continue;
00094
00095 const AtomSubsystem::Atom& atom = atomSubsystem.getAtom(a);
00096
00097 Vec3 color(0.5, 0.5, 0.5);
00098
00099
00100
00101 if (atom.getMass() < 39.5)
00102 ;
00103 else if (atom.getMass() < 40.0)
00104 color = Vec3(0.5, 1, 1);
00105
00106 decorations.addBodyFixedDecoration(
00107 atom.getMobilizedBodyIndex(),
00108 atom.getStationInBodyFrame(),
00109 DecorativeSphere(vdwAtom.rMin * scale).setColor(color)
00110 );
00111 }
00112 }
00113
00114 void decorateBoundingSpheres(DecorationSubsystem& decorations) const {
00115 for (size_t s(0); s < wallSpheres.size(); ++s)
00116 {
00117 Vec3 color(1.0, 1.0, 0.6);
00118
00119 const WallSphere& sphere = wallSpheres[s];
00120
00121 decorations.addBodyFixedDecoration(
00122 atomSubsystem.getMultibodySystem().getMatterSubsystem().getGround().getMobilizedBodyIndex(),
00123 sphere.center,
00124 DecorativeSphere(sphere.radius).setColor(color).setOpacity(0.2)
00125 );
00126 }
00127 }
00128 void calcForce(const State& state, Vector_<SpatialVec>& bodyForces,
00129 Vector_<Vec3>& particleForces, Vector& mobilityForces) const
00130 {
00131
00132 for ( AtomSubsystem::PairIterator pair =
00133 atomSubsystem.pairBegin(state, cutoff);
00134 pair != atomSubsystem.pairEnd();
00135 ++pair)
00136 {
00137 const VdwAtom& vdwAtom1 = vdwAtoms[pair->first];
00138 const VdwAtom& vdwAtom2 = vdwAtoms[pair->second];
00139
00140 if (!vdwAtom1.atomIndex.isValid()) continue;
00141 if (!vdwAtom2.atomIndex.isValid()) continue;
00142
00143 calcForce(vdwAtom1, vdwAtom2, state, bodyForces);
00144 }
00145
00146
00147 for ( std::vector<WallSphere>::const_iterator sphereI = wallSpheres.begin();
00148 sphereI != wallSpheres.end(); ++sphereI) {
00149 for ( std::vector<VdwAtom>::const_iterator atomI = vdwAtoms.begin();
00150 atomI != vdwAtoms.end(); ++atomI) {
00151 if ( ! atomI->atomIndex.isValid() ) continue;
00152 calcForce(*sphereI, *atomI, state, bodyForces);
00153 }
00154 }
00155 }
00156
00157 Real calcPotentialEnergy(const State& state) const {
00158 energy_t energy = 0.0 * md::kilojoules_per_mole;
00159
00160 for ( AtomSubsystem::PairIterator pair =
00161 atomSubsystem.pairBegin(state, cutoff);
00162 pair != atomSubsystem.pairEnd();
00163 ++pair)
00164 {
00165 const VdwAtom& vdwAtom1 = vdwAtoms[pair->first];
00166 const VdwAtom& vdwAtom2 = vdwAtoms[pair->second];
00167
00168 if (!vdwAtom1.atomIndex.isValid()) continue;
00169 if (!vdwAtom2.atomIndex.isValid()) continue;
00170
00171 energy += calcPotentialEnergy(vdwAtom1, vdwAtom2, state);
00172 }
00173
00174
00175 for ( std::vector<WallSphere>::const_iterator sphereI = wallSpheres.begin();
00176 sphereI != wallSpheres.end(); ++sphereI) {
00177 for ( std::vector<VdwAtom>::const_iterator atomI = vdwAtoms.begin();
00178 atomI != vdwAtoms.end(); ++atomI) {
00179 if ( ! atomI->atomIndex.isValid() ) continue;
00180 energy += calcPotentialEnergy(*sphereI, *atomI, state);
00181 }
00182 }
00183
00184 return energy;
00185 }
00186
00187 protected:
00188
00189 struct ForceAndEnergy {
00190 force_t force;
00191 energy_t energy;
00192 };
00193
00194
00195
00196
00197
00198
00199
00200 ForceAndEnergy calcForceAndEnergy(
00201 const location_t& r,
00202 Real dij, Real eij) const
00203 {
00204 ForceAndEnergy forceAndEnergy;
00205
00206 const area_t d2 = r.normSqr();
00207
00208
00209
00210
00211 if ( (cutoff > 0.0) && (d2 > cutoffSquared) )
00212 return forceAndEnergy;
00213
00214
00215 inverse_area_t ood2(1.0 / d2);
00216
00217
00218 const Real ddij2 = dij*dij*ood2;
00219 const Real ddij6 = ddij2*ddij2*ddij2;
00220 const Real ddij12 = ddij6*ddij6;
00221
00222
00223 const energy_t fVdw = 12.0 * eij * (ddij12 - ddij6);
00224 forceAndEnergy.force = fVdw * ood2 * r;
00225
00226
00227 forceAndEnergy.energy = eij * (ddij12 - 2*ddij6);
00228
00229
00230 if (cutoff > 0.0)
00231 {
00232 energy_t truncationOffset = 0.0;
00233 assert(d2 < cutoffSquared);
00234 const Real off2 = dij * dij / cutoffSquared;
00235 const Real off6 = off2 * off2 * off2;
00236 truncationOffset = eij * ( (off6 - 2.0) * off6);
00237 assert(truncationOffset < 0.0);
00238
00239 forceAndEnergy.energy -= truncationOffset;
00240 }
00241
00242 return forceAndEnergy;
00243 }
00244
00245
00246 void calcForce(const VdwAtom& vdwAtom1, const VdwAtom& vdwAtom2, const State& state, Vector_<SpatialVec>& bodyForces) const
00247 {
00248 Real vdwScale = 1.0;
00249
00250 const AtomSubsystem::Atom& atom1 = atomSubsystem.getAtom(vdwAtom1.atomIndex);
00251 const AtomSubsystem::Atom& atom2 = atomSubsystem.getAtom(vdwAtom2.atomIndex);
00252
00253 const Vec3& a1Pos_G = atomSubsystem.getAtomLocationInGround(state, vdwAtom1.atomIndex);
00254 const Vec3& a2Pos_G = atomSubsystem.getAtomLocationInGround(state, vdwAtom2.atomIndex);
00255
00256
00257 const length_t dij = vdwAtom1.rMin + vdwAtom2.rMin;
00258 const energy_t eij = std::sqrt(vdwAtom1.wellDepth * vdwAtom2.wellDepth);
00259
00260 const force_t fj = calcForceAndEnergy(a2Pos_G - a1Pos_G, dij, eij).force * vdwScale;
00261
00262 const Vec3& a1Station_G = atomSubsystem.getAtomBodyStationInGround(state, vdwAtom1.atomIndex);
00263 const Vec3& a2Station_G = atomSubsystem.getAtomBodyStationInGround(state, vdwAtom2.atomIndex);
00264
00265 bodyForces[atom2.getMobilizedBodyIndex()] += SpatialVec( a2Station_G % fj, fj);
00266 bodyForces[atom1.getMobilizedBodyIndex()] -= SpatialVec( a1Station_G % fj, fj);
00267 }
00268
00269 energy_t calcPotentialEnergy(const VdwAtom& vdwAtom1, const VdwAtom& vdwAtom2, const State& state) const
00270 {
00271 Real vdwScale = 1.0;
00272
00273 const AtomSubsystem::Atom& atom1 = atomSubsystem.getAtom(vdwAtom1.atomIndex);
00274 const AtomSubsystem::Atom& atom2 = atomSubsystem.getAtom(vdwAtom2.atomIndex);
00275
00276 const Vec3& a1Pos_G = atomSubsystem.getAtomLocationInGround(state, vdwAtom1.atomIndex);
00277 const Vec3& a2Pos_G = atomSubsystem.getAtomLocationInGround(state, vdwAtom2.atomIndex);
00278
00279
00280 const length_t dij = vdwAtom1.rMin + vdwAtom2.rMin;
00281 const energy_t eij = std::sqrt(vdwAtom1.wellDepth * vdwAtom2.wellDepth);
00282
00283 return calcForceAndEnergy(a2Pos_G - a1Pos_G, dij, eij).energy * vdwScale;
00284 }
00285
00286
00287 void calcForce(const WallSphere& sphere, const VdwAtom& vdwAtom2, const State& state, Vector_<SpatialVec>& bodyForces) const
00288 {
00289 const AtomSubsystem::Atom& atom2 = atomSubsystem.getAtom(vdwAtom2.atomIndex);
00290
00291 const Vec3& a2Pos_G = atomSubsystem.getAtomLocationInGround(state, vdwAtom2.atomIndex);
00292
00293
00294 const length_t dij = sphere.vdwRadius + vdwAtom2.rMin;
00295 const energy_t eij = std::sqrt(sphere.wellDepth * vdwAtom2.wellDepth);
00296
00297 Vec3 dv = a2Pos_G - sphere.center;
00298
00299
00300 Real centerDist = dv.norm();
00301 UnitVec3 centerDir(dv);
00302
00303
00304 Vec3 rA = (centerDist - sphere.radius) * centerDir;
00305
00306 Vec3 rB = (sphere.radius + centerDist) * centerDir;
00307
00308 force_t fj = calcForceAndEnergy(rA, dij, eij).force;
00309 fj += calcForceAndEnergy(rB, dij, eij).force;
00310
00311 const Vec3& a2Station_G = atomSubsystem.getAtomBodyStationInGround(state, vdwAtom2.atomIndex);
00312
00313
00314 bodyForces[atom2.getMobilizedBodyIndex()] += SpatialVec( a2Station_G % fj, fj);
00315 }
00316
00317 energy_t calcPotentialEnergy(const WallSphere& sphere, const VdwAtom& vdwAtom2, const State& state) const
00318 {
00319 const AtomSubsystem::Atom& atom2 = atomSubsystem.getAtom(vdwAtom2.atomIndex);
00320
00321 const Vec3& a2Pos_G = atomSubsystem.getAtomLocationInGround(state, vdwAtom2.atomIndex);
00322
00323
00324 const length_t dij = sphere.vdwRadius + vdwAtom2.rMin;
00325 const energy_t eij = std::sqrt(sphere.wellDepth * vdwAtom2.wellDepth);
00326
00327 Vec3 dv = a2Pos_G - sphere.center;
00328
00329
00330 Real centerDist = dv.norm();
00331 UnitVec3 centerDir(dv);
00332
00333
00334 Vec3 rA = (sphere.radius - centerDist) * centerDir;
00335
00336 Vec3 rB = -(sphere.radius + centerDist) * centerDir;
00337
00338 energy_t energy = calcForceAndEnergy(rA, dij, eij).energy;
00339 energy += calcForceAndEnergy(rB, dij, eij).energy;
00340
00341 return energy;
00342 }
00343
00344 const AtomSubsystem& atomSubsystem;
00345
00346
00347 std::vector<VdwAtom> vdwAtoms;
00348 length_t cutoff;
00349 area_t cutoffSquared;
00350 std::vector<WallSphere> wallSpheres;
00351 };
00352
00353 }
00354
00355 #endif