1 /* -------------------------------------------------------------------------- *
2 * Simbody(tm) *
3 * -------------------------------------------------------------------------- *
4 * This is part of the SimTK biosimulation toolkit originating from *
5 * Simbios, the NIH National Center for Physics-Based Simulation of *
6 * Biological Structures at Stanford, funded under the NIH Roadmap for *
7 * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody. *
8 * *
9 * Portions copyright (c) 2009-12 Stanford University and the Authors. *
10 * Authors: Michael Sherman *
11 * Contributors: Christopher Bruns *
12 * *
13 * Licensed under the Apache License, Version 2.0 (the "License"); you may *
14 * not use this file except in compliance with the License. You may obtain a *
15 * copy of the License at http://www.apache.org/licenses/LICENSE-2.0. *
16 * *
17 * Unless required by applicable law or agreed to in writing, software *
18 * distributed under the License is distributed on an "AS IS" BASIS, *
19 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
20 * See the License for the specific language governing permissions and *
21 * limitations under the License. *
22 * -------------------------------------------------------------------------- */
23
24 #include "SimTKcommon.h"
25
26 #include "simbody/internal/common.h"
27 #include "simbody/internal/MobilizedBody.h"
28 #include "simbody/internal/SimbodyMatterSubsystem.h"
29 #include "simbody/internal/Force_Thermostat.h"
30
31 #include "ForceImpl.h"
32
33 namespace SimTK {
34
35 // Implementation class for Force::Thermostat.
36 class Force::ThermostatImpl : public ForceImpl {
37 public:
ThermostatImpl(const SimbodyMatterSubsystem & matter,Real boltzmannsConstant,Real defBathTemp,Real defRelaxationTime,int defNumExcludedDofs)38 ThermostatImpl(const SimbodyMatterSubsystem& matter,
39 Real boltzmannsConstant,
40 Real defBathTemp,
41 Real defRelaxationTime,
42 int defNumExcludedDofs)
43 : matter(matter), kB(boltzmannsConstant),
44 defaultNumChains(DefaultDefaultNumChains),
45 defaultBathTemp(defBathTemp),
46 defaultRelaxationTime(defRelaxationTime),
47 defaultNumExcludedDofs(defNumExcludedDofs) {}
48
clone() const49 ThermostatImpl* clone() const override {return new ThermostatImpl(*this);}
dependsOnlyOnPositions() const50 bool dependsOnlyOnPositions() const override {return false;}
51
52 void calcForce(const State& state, Vector_<SpatialVec>& bodyForces,
53 Vector_<Vec3>& particleForces, Vector& mobilityForces) const override;
54
55 // Temperature does not contribute to potential energy.
calcPotentialEnergy(const State & state) const56 Real calcPotentialEnergy(const State& state) const override {return 0;}
57
58 void realizeTopology(State& state) const override;
59 void realizeModel(State& state) const override;
60 void realizeVelocity(const State& state) const override;
61 void realizeDynamics(const State& state) const override;
62
63 // Get/update the current number of chains.
getNumChains(const State & s) const64 int getNumChains(const State& s) const {
65 assert(dvNumChains.isValid());
66 return Value<int>::downcast(getForceSubsystem().getDiscreteVariable(s, dvNumChains));
67 }
updNumChains(State & s) const68 int& updNumChains(State& s) const {
69 assert(dvNumChains.isValid());
70 return Value<int>::updDowncast(getForceSubsystem().updDiscreteVariable(s, dvNumChains));
71 }
72
73 // Get/update the current number of excluded dofs.
getNumExcludedDofs(const State & s) const74 int getNumExcludedDofs(const State& s) const {
75 assert(dvNumExcludedDofs.isValid());
76 return Value<int>::downcast
77 (getForceSubsystem().getDiscreteVariable(s, dvNumExcludedDofs));
78 }
updNumExcludedDofs(State & s) const79 int& updNumExcludedDofs(State& s) const {
80 assert(dvNumExcludedDofs.isValid());
81 return Value<int>::updDowncast
82 (getForceSubsystem().updDiscreteVariable(s, dvNumExcludedDofs));
83 }
84
85 int getNumThermalDOFs(const State& s) const;
86
87 // Get the auxiliary continuous state index of the 0'th thermostat state variable z.
getZ0Index(const State & s) const88 ZIndex getZ0Index(const State& s) const {
89 assert(cacheZ0Index.isValid());
90 return Value<ZIndex>::downcast(getForceSubsystem().getCacheEntry(s, cacheZ0Index));
91 }
updZ0Index(State & s) const92 ZIndex& updZ0Index(State& s) const {
93 assert(cacheZ0Index.isValid());
94 return Value<ZIndex>::updDowncast(getForceSubsystem().updCacheEntry(s, cacheZ0Index));
95 }
96
97 // Get the current bath temperature (i.e., the desired temperature).
getBathTemp(const State & s) const98 Real getBathTemp(const State& s) const {
99 assert(dvBathTemp.isValid());
100 return Value<Real>::downcast(getForceSubsystem().getDiscreteVariable(s, dvBathTemp));
101 }
updBathTemp(State & s) const102 Real& updBathTemp(State& s) const {
103 assert(dvBathTemp.isValid());
104 return Value<Real>::updDowncast(getForceSubsystem().updDiscreteVariable(s, dvBathTemp));
105 }
106
107 // Get the current bath temperature (i.e., the desired temperature).
getRelaxationTime(const State & s) const108 Real getRelaxationTime(const State& s) const {
109 assert(dvRelaxationTime.isValid());
110 return Value<Real>::downcast(getForceSubsystem().getDiscreteVariable(s, dvRelaxationTime));
111 }
updRelaxationTime(State & s) const112 Real& updRelaxationTime(State& s) const {
113 assert(dvRelaxationTime.isValid());
114 return Value<Real>::updDowncast(getForceSubsystem().updDiscreteVariable(s, dvRelaxationTime));
115 }
116
117 // Get the calculated momentum M*u (after Stage::Velocity).
getMomentum(const State & s) const118 const Vector& getMomentum(const State& s) const {
119 assert(cacheMomentumIndex.isValid());
120 return Value<Vector>::downcast(getForceSubsystem().getCacheEntry(s, cacheMomentumIndex));
121 }
updMomentum(const State & s) const122 Vector& updMomentum(const State& s) const {
123 assert(cacheMomentumIndex.isValid());
124 return Value<Vector>::updDowncast(getForceSubsystem().updCacheEntry(s, cacheMomentumIndex));
125 }
126
127 // Get the calculated system kinetic energy ~u*M*u/2 (after Stage::Velocity).
getKE(const State & s) const128 const Real& getKE(const State& s) const {
129 assert(cacheKEIndex.isValid());
130 return Value<Real>::downcast(getForceSubsystem().getCacheEntry(s, cacheKEIndex));
131 }
updKE(const State & s) const132 Real& updKE(const State& s) const {
133 assert(cacheKEIndex.isValid());
134 return Value<Real>::updDowncast(getForceSubsystem().updCacheEntry(s, cacheKEIndex));
135 }
136
getExternalWork(const State & s) const137 Real getExternalWork(const State& s) const
138 { return getForceSubsystem().getZ(s)[workZIndex]; }
updExternalWork(State & s) const139 Real& updExternalWork(State& s) const
140 { return getForceSubsystem().updZ(s)[workZIndex]; }
141
updWorkZDot(const State & s) const142 Real& updWorkZDot(const State& s) const
143 { return getForceSubsystem().updZDot(s)[workZIndex]; }
144
145 Real calcExternalPower(const State& s) const;
146
147 // Get the current value of one of the thermostat state variables.
getZ(const State & s,int i) const148 Real getZ(const State& s, int i) const {
149 assert(0 <= i && i < 2*getNumChains(s));
150 const ZIndex z0 = getZ0Index(s);
151 return getForceSubsystem().getZ(s)[z0+i];
152 }
updZ(State & s,int i) const153 Real& updZ(State& s, int i) const {
154 assert(0 <= i && i < 2*getNumChains(s));
155 const ZIndex z0 = getZ0Index(s);
156 return getForceSubsystem().updZ(s)[z0+i];
157 }
getZDot(const State & s,int i) const158 Real getZDot(const State& s, int i) const {
159 assert(0 <= i && i < 2*getNumChains(s));
160 const ZIndex z0 = getZ0Index(s);
161 return getForceSubsystem().getZDot(s)[z0+i];
162 }
163 // State is const here because ZDot is a cache entry.
updZDot(const State & s,int i) const164 Real& updZDot(const State& s, int i) const {
165 assert(0 <= i && i < 2*getNumChains(s));
166 const ZIndex z0 = getZ0Index(s);
167 return getForceSubsystem().updZDot(s)[z0+i];
168 }
169
170 static const int DefaultDefaultNumChains = 3;
171 private:
172 const SimbodyMatterSubsystem& matter;
173 const Real kB; // Boltzmann's constant in compatible units
174
175 // Topology-stage "state" variables.
176 int defaultNumChains; // # chains in a new State
177 int defaultNumExcludedDofs; // # non-thermal rigid body dofs
178 Real defaultBathTemp; // bath temperature
179 Real defaultRelaxationTime; // relaxation time
180
181 // These indices are Topology-stage "cache" variables.
182 DiscreteVariableIndex dvNumChains; // integer
183 DiscreteVariableIndex dvNumExcludedDofs; // integer
184 DiscreteVariableIndex dvBathTemp; // Real
185 DiscreteVariableIndex dvRelaxationTime; // Real
186 CacheEntryIndex cacheZ0Index; // ZIndex
187 CacheEntryIndex cacheMomentumIndex; // M*u
188 CacheEntryIndex cacheKEIndex; // ~u*M*u/2
189 ZIndex workZIndex; // power integral
190
191 friend class Force::Thermostat;
192 };
193
194 //-------------------------------- Thermostat ----------------------------------
195 //------------------------------------------------------------------------------
196
197 SimTK_INSERT_DERIVED_HANDLE_DEFINITIONS(Force::Thermostat, Force::ThermostatImpl, Force);
198
Thermostat(GeneralForceSubsystem & forces,const SimbodyMatterSubsystem & matter,Real boltzmannsConstant,Real bathTemperature,Real relaxationTime,int numExcludedDofs)199 Force::Thermostat::Thermostat
200 (GeneralForceSubsystem& forces, const SimbodyMatterSubsystem& matter,
201 Real boltzmannsConstant, Real bathTemperature, Real relaxationTime,
202 int numExcludedDofs)
203 : Force(new ThermostatImpl(matter, boltzmannsConstant, bathTemperature,
204 relaxationTime, numExcludedDofs))
205 {
206 SimTK_APIARGCHECK1_ALWAYS(boltzmannsConstant > 0,
207 "Force::Thermostat","ctor", "Illegal Boltzmann's constant %g.", boltzmannsConstant);
208 SimTK_APIARGCHECK1_ALWAYS(bathTemperature > 0,
209 "Force::Thermostat","ctor", "Illegal bath temperature %g.", bathTemperature);
210 SimTK_APIARGCHECK1_ALWAYS(relaxationTime > 0,
211 "Force::Thermostat","ctor", "Illegal relaxation time %g.", relaxationTime);
212 SimTK_APIARGCHECK1_ALWAYS(0 <= numExcludedDofs && numExcludedDofs <= 6,
213 "Force::Thermostat","ctor",
214 "Illegal number of excluded rigid body dofs %d (must be 0-6).",
215 numExcludedDofs);
216
217 updImpl().setForceSubsystem(forces, forces.adoptForce(*this));
218 }
219
220 Force::Thermostat& Force::Thermostat::
setDefaultNumChains(int numChains)221 setDefaultNumChains(int numChains) {
222 SimTK_APIARGCHECK1_ALWAYS(numChains > 0,
223 "Force::Thermostat","setDefaultNumChains",
224 "Illegal number of chains %d.", numChains);
225
226 getImpl().invalidateTopologyCache();
227 updImpl().defaultNumChains = numChains;
228 return *this;
229 }
230
231 Force::Thermostat& Force::Thermostat::
setDefaultBathTemperature(Real bathTemperature)232 setDefaultBathTemperature(Real bathTemperature) {
233 SimTK_APIARGCHECK1_ALWAYS(bathTemperature > 0,
234 "Force::Thermostat","setDefaultBathTemperature",
235 "Illegal bath temperature %g.", bathTemperature);
236
237 getImpl().invalidateTopologyCache();
238 updImpl().defaultBathTemp = bathTemperature;
239 return *this;
240 }
241
242 Force::Thermostat& Force::Thermostat::
setDefaultRelaxationTime(Real relaxationTime)243 setDefaultRelaxationTime(Real relaxationTime) {
244 SimTK_APIARGCHECK1_ALWAYS(relaxationTime > 0,
245 "Force::Thermostat","setDefaultRelaxationTime",
246 "Illegal bath temperature %g.", relaxationTime);
247
248 getImpl().invalidateTopologyCache();
249 updImpl().defaultRelaxationTime = relaxationTime;
250 return *this;
251 }
252
253 Force::Thermostat& Force::Thermostat::
setDefaultNumExcludedDofs(int numExcludedDofs)254 setDefaultNumExcludedDofs(int numExcludedDofs) {
255 SimTK_APIARGCHECK1_ALWAYS(0 <= numExcludedDofs && numExcludedDofs <= 6,
256 "Force::Thermostat","setDefaultNumExcludedDofs",
257 "Illegal number of excluded rigid body dofs %d (must be 0-6).",
258 numExcludedDofs);
259
260 getImpl().invalidateTopologyCache();
261 updImpl().defaultNumExcludedDofs = numExcludedDofs;
262 return *this;
263 }
264
getDefaultNumChains() const265 int Force::Thermostat::getDefaultNumChains() const {return getImpl().defaultNumChains;}
getDefaultBathTemperature() const266 Real Force::Thermostat::getDefaultBathTemperature() const {return getImpl().defaultBathTemp;}
getDefaultRelaxationTime() const267 Real Force::Thermostat::getDefaultRelaxationTime() const {return getImpl().defaultRelaxationTime;}
getDefaultNumExcludedDofs() const268 int Force::Thermostat::getDefaultNumExcludedDofs() const {return getImpl().defaultNumExcludedDofs;}
getBoltzmannsConstant() const269 Real Force::Thermostat::getBoltzmannsConstant() const {return getImpl().kB;}
270
271 const Force::Thermostat& Force::Thermostat::
setNumChains(State & s,int numChains) const272 setNumChains(State& s, int numChains) const {
273 SimTK_APIARGCHECK1_ALWAYS(numChains > 0,
274 "Force::Thermostat","setNumChains",
275 "Illegal number of chains %d.", numChains);
276
277 getImpl().updNumChains(s) = numChains;
278 return *this;
279 }
280
281 const Force::Thermostat& Force::Thermostat::
setBathTemperature(State & s,Real bathTemperature) const282 setBathTemperature(State& s, Real bathTemperature) const {
283 SimTK_APIARGCHECK1_ALWAYS(bathTemperature > 0,
284 "Force::Thermostat","setBathTemperature",
285 "Illegal bath temperature %g.", bathTemperature);
286
287 getImpl().updBathTemp(s) = bathTemperature;
288 return *this;
289 }
290
291 const Force::Thermostat& Force::Thermostat::
setRelaxationTime(State & s,Real relaxationTime) const292 setRelaxationTime(State& s, Real relaxationTime) const {
293 SimTK_APIARGCHECK1_ALWAYS(relaxationTime > 0,
294 "Force::Thermostat","setRelaxationTime",
295 "Illegal bath temperature %g.", relaxationTime);
296
297 getImpl().updRelaxationTime(s) = relaxationTime;
298 return *this;
299 }
300
301 const Force::Thermostat& Force::Thermostat::
setNumExcludedDofs(State & s,int numExcludedDofs) const302 setNumExcludedDofs(State& s, int numExcludedDofs) const {
303 SimTK_APIARGCHECK1_ALWAYS(0 <= numExcludedDofs && numExcludedDofs <= 6,
304 "Force::Thermostat","setNumExcludedDofs",
305 "Illegal number of excluded rigid body dofs %d (must be 0-6).",
306 numExcludedDofs);
307
308 getImpl().updNumExcludedDofs(s) = numExcludedDofs;
309 return *this;
310 }
311
getNumChains(const State & s) const312 int Force::Thermostat::getNumChains(const State& s) const {return getImpl().getNumChains(s);}
getBathTemperature(const State & s) const313 Real Force::Thermostat::getBathTemperature(const State& s) const {return getImpl().getBathTemp(s);}
getRelaxationTime(const State & s) const314 Real Force::Thermostat::getRelaxationTime(const State& s) const {return getImpl().getRelaxationTime(s);}
getNumExcludedDofs(const State & s) const315 int Force::Thermostat::getNumExcludedDofs(const State& s) const {return getImpl().getNumExcludedDofs(s);}
316
initializeChainState(State & s) const317 void Force::Thermostat::initializeChainState(State& s) const {
318 const ThermostatImpl& impl = getImpl();
319 const int nChains = impl.getNumChains(s);
320 for (int i=0; i < 2*nChains; ++i)
321 impl.updZ(s, i) = 0;
322 }
323
setChainState(State & s,const Vector & z) const324 void Force::Thermostat::setChainState(State& s, const Vector& z) const {
325 const ThermostatImpl& impl = getImpl();
326 const int nChains = impl.getNumChains(s);
327 SimTK_APIARGCHECK2_ALWAYS(z.size() == 2*nChains,
328 "Force::Thermostat", "setChainState",
329 "Number of values supplied (%d) didn't match twice the number of chains %d.",
330 z.size(), nChains);
331 for (int i=0; i < 2*nChains; ++i)
332 impl.updZ(s, i) = z[i];
333 }
334
getChainState(const State & s) const335 Vector Force::Thermostat::getChainState(const State& s) const {
336 const ThermostatImpl& impl = getImpl();
337 const int nChains = impl.getNumChains(s);
338 Vector out(2*nChains);
339 for (int i=0; i < 2*nChains; ++i)
340 out[i] = impl.getZ(s, i);
341 return out;
342 }
343
344 // Cost is a divide and two flops, say about 20 flops.
getCurrentTemperature(const State & s) const345 Real Force::Thermostat::getCurrentTemperature(const State& s) const {
346 const Real ke = getImpl().getKE(s); // Cached value for kinetic energy
347 const Real kB = getImpl().kB; // Boltzmann's constant
348 const int N = getImpl().getNumThermalDOFs(s);
349 return (2*ke) / (N*kB);
350 }
351
getNumThermalDofs(const State & s) const352 int Force::Thermostat::getNumThermalDofs(const State& s) const {
353 return getImpl().getNumThermalDOFs(s);
354 }
355
356 // Bath energy is KEb + PEb where
357 // KEb = 1/2 kT t^2 (N z0^2 + sum(zi^2))
358 // PEb = kT (N s0 + sum(si))
359 // Cost is about 7 flops + 4 flops/chain, say about 25 flops.
calcBathEnergy(const State & state) const360 Real Force::Thermostat::calcBathEnergy(const State& state) const {
361 const ThermostatImpl& impl = getImpl();
362 const int nChains = impl.getNumChains(state);
363 const int N = impl.getNumThermalDOFs(state);
364 const Real kT = impl.kB * impl.getBathTemp(state);
365 const Real t = impl.getRelaxationTime(state);
366
367 Real zsqsum = N * square(impl.getZ(state,0));
368 for (int i=1; i < nChains; ++i)
369 zsqsum += square(impl.getZ(state,i));
370
371 Real ssum = N * impl.getZ(state, nChains);
372 for (int i=1; i < nChains; ++i)
373 ssum += impl.getZ(state, nChains+i);
374
375 const Real KEb = (kT/2) * t*t * zsqsum;
376 const Real PEb = kT * ssum;
377
378 return KEb + PEb;
379 }
380
getExternalPower(const State & state) const381 Real Force::Thermostat::getExternalPower(const State& state) const {
382 return getImpl().calcExternalPower(state);
383 }
384
getExternalWork(const State & state) const385 Real Force::Thermostat::getExternalWork(const State& state) const {
386 return getImpl().getExternalWork(state);
387 }
388
setExternalWork(State & state,Real w) const389 void Force::Thermostat::setExternalWork(State& state, Real w) const {
390 getImpl().updExternalWork(state) = w;
391 }
392
393 // This is the number of thermal dofs. TODO: we're ignoring constraint
394 // redundancy but we shouldn't be! That could result in negative dofs, so we'll
395 // make sure that doesn't happen. But don't expect meaningful results
396 // in that case. Note that it is the acceleration-level constraints that
397 // matter; they remove dofs regardless of whether there is a corresponding
398 // velocity constraint.
getNumThermalDOFs(const State & state) const399 int Force::ThermostatImpl::getNumThermalDOFs(const State& state) const {
400 const int ndofs = state.getNU() - state.getNUDotErr()
401 - getNumExcludedDofs(state);
402 const int N = std::max(1, ndofs);
403 return N;
404 }
405
406 // This force produces only mobility forces, with
407 // f = -z0 * M * u
408 // Conveniently we already calculated the momentum M*u and cached it
409 // in realizeVelocity(). Additional cost here is 2*N flops.
410 void Force::ThermostatImpl::
calcForce(const State & state,Vector_<SpatialVec> &,Vector_<Vec3> &,Vector & mobilityForces) const411 calcForce(const State& state, Vector_<SpatialVec>&, Vector_<Vec3>&,
412 Vector& mobilityForces) const
413 {
414 const Vector& p = getMomentum(state);
415
416 // Generate momentum-weighted forces and apply to mobilities.
417 // This is 2*N flops.
418 mobilityForces -= getZ(state, 0)*p;
419 }
420
421 // All the power generated by this force is external (to or from the
422 // thermal bath). So the power is
423 // p = ~u * f = -z0 * (~u * M * u) = -z0 * (2*KE).
424 // We already calculated and cached KE in realizeVelocity() so power
425 // is practically free here (2 flops).
426 Real Force::ThermostatImpl::
calcExternalPower(const State & state) const427 calcExternalPower(const State& state) const {
428 return -2 * getZ(state, 0) * getKE(state);
429 }
430
431 // Allocate and initialize state variables.
realizeTopology(State & state) const432 void Force::ThermostatImpl::realizeTopology(State& state) const {
433 // Make these writable just here where we need to fill in the Topology "cache"
434 // variables; after this they are const.
435 Force::ThermostatImpl* mutableThis = const_cast<Force::ThermostatImpl*>(this);
436 mutableThis->dvNumChains =
437 getForceSubsystem().allocateDiscreteVariable(state, Stage::Model,
438 new Value<int>(defaultNumChains));
439 mutableThis->dvBathTemp =
440 getForceSubsystem().allocateDiscreteVariable(state, Stage::Instance,
441 new Value<Real>(defaultBathTemp));
442 mutableThis->dvRelaxationTime =
443 getForceSubsystem().allocateDiscreteVariable(state, Stage::Instance,
444 new Value<Real>(defaultRelaxationTime));
445 mutableThis->dvNumExcludedDofs =
446 getForceSubsystem().allocateDiscreteVariable(state, Stage::Model,
447 new Value<int>(defaultNumExcludedDofs));
448
449 // This cache entry holds the auxiliary state index of our first
450 // thermostat state variable. It is valid after realizeModel().
451 mutableThis->cacheZ0Index =
452 getForceSubsystem().allocateCacheEntry(state, Stage::Model,
453 new Value<ZIndex>());
454
455 // This cache entry holds the generalized momentum M*u. The vector
456 // will be allocated to hold nu values.
457 mutableThis->cacheMomentumIndex =
458 getForceSubsystem().allocateCacheEntry(state, Stage::Velocity,
459 new Value<Vector>());
460
461 // This cache entry holds the kinetic energy ~u*M*u/2.
462 mutableThis->cacheKEIndex =
463 getForceSubsystem().allocateCacheEntry(state, Stage::Velocity,
464 new Value<Real>(NaN));
465
466 const Vector workZInit(1, Zero);
467 mutableThis->workZIndex =
468 getForceSubsystem().allocateZ(state, workZInit);
469 }
470
471 // Allocate the chain state variables and bath energy variables.
472 // TODO: this should be done at Instance stage.
realizeModel(State & state) const473 void Force::ThermostatImpl::realizeModel(State& state) const {
474 const int nChains = getNumChains(state);
475 const Vector zInit(2*nChains, Zero);
476 updZ0Index(state) = getForceSubsystem().allocateZ(state, zInit);
477 }
478
479 // Calculate velocity-dependent terms, the internal coordinate
480 // momentum and the kinetic energy. This is the expensive part
481 // at about 125*N flops if all joints are 1 dof.
realizeVelocity(const State & state) const482 void Force::ThermostatImpl::realizeVelocity(const State& state) const {
483 Vector& Mu = updMomentum(state);
484 matter.multiplyByM(state, state.getU(), Mu); // <-- expensive ~123*N flops
485 updKE(state) = (~state.getU() * Mu) / 2; // 2*N flops
486 }
487
488 // Calculate time derivatives of the various state variables.
489 // This is just a fixed cost for the whole system, independent of
490 // size: 3 divides + a few flops per chain, maybe 100 flops total.
realizeDynamics(const State & state) const491 void Force::ThermostatImpl::realizeDynamics(const State& state) const {
492 const Real t = getRelaxationTime(state);
493 const Real oot2 = 1 / square(t);
494 const int m = getNumChains(state);
495
496 // This is the desired kinetic energy per dof.
497 const Real Eb = kB * getBathTemp(state) / 2;
498
499 const int N = getNumThermalDOFs(state);
500
501 // This is the current average kinetic energy per dof.
502 const Real E = getKE(state) / N;
503
504 updZDot(state, 0) = (E/Eb - 1) * oot2;
505
506 int Ndofs = N; // only for z0
507 for (int k=1; k < m; ++k) {
508 const Real zk1 = getZ(state, k-1);
509 const Real zk = getZ(state, k);
510 updZDot(state, k-1) -= zk1 * zk;
511 updZDot(state, k) = Ndofs * square(zk1) - oot2;
512 Ndofs = 1; // z1..m-1 control only 1 dof each
513 }
514
515 // Calculate sdot's for energy calculation.
516 for (int k=0; k < m; ++k)
517 updZDot(state, m+k) = getZ(state, k);
518
519 updWorkZDot(state) = calcExternalPower(state);
520 }
521
522
523 } // namespace SimTK
524
525