1 /* -------------------------------------------------------------------------- *
2 * Simbody(tm): SimTKmath *
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) 2006-12 Stanford University and the Authors. *
10 * Authors: Michael Sherman *
11 * Contributors: *
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 /** @file
25 * This is the private (library side) implementation of the Simmath
26 * Integrator family of classes.
27 */
28
29 #include "SimTKcommon.h"
30 #include "simmath/Integrator.h"
31
32 #include "IntegratorRep.h"
33
34 #include <exception>
35 #include <limits>
36 #include <iostream>
37
38 using std::cout; using std::endl;
39
40 namespace SimTK {
41
42 //////////////////////////////////
43 // IMPLEMENTATION OF INTEGRATOR //
44 //////////////////////////////////
45
~Integrator()46 Integrator::~Integrator() {
47 delete rep; rep=0;
48 }
49
resetAllStatistics()50 void Integrator::resetAllStatistics() {
51 updRep().resetIntegratorStatistics();
52 updRep().resetMethodStatistics();
53 }
54
initialize(const State & initState)55 void Integrator::initialize(const State& initState) {
56 updRep().initialize(initState);
57 }
58
reinitialize(Stage g,bool shouldTerminate)59 void Integrator::reinitialize(Stage g, bool shouldTerminate) {
60 updRep().reinitialize(g,shouldTerminate);
61 }
62
63 Integrator::SuccessfulStepStatus
stepTo(Real reportTime,Real advanceLimit)64 Integrator::stepTo(Real reportTime, Real advanceLimit) {
65 return updRep().stepTo(reportTime, advanceLimit);
66 }
67
68 Integrator::SuccessfulStepStatus
stepBy(Real interval,Real advanceIntervalLimit)69 Integrator::stepBy(Real interval, Real advanceIntervalLimit) {
70 const Real t = getRep().getState().getTime();
71 return updRep().stepTo(t + interval, t + advanceIntervalLimit);
72 }
73
isSimulationOver() const74 bool Integrator::isSimulationOver() const {
75 return getRep().isSimulationOver();
76 }
77
getTerminationReason() const78 Integrator::TerminationReason Integrator::getTerminationReason() const {
79 return getRep().getTerminationReason();
80 }
81
82 /*static*/ String Integrator::
getSuccessfulStepStatusString(SuccessfulStepStatus stat)83 getSuccessfulStepStatusString(SuccessfulStepStatus stat) {
84 switch(stat) {
85 case ReachedReportTime: return "ReachedReportTime";
86 case ReachedEventTrigger: return "ReachedEventTrigger";
87 case ReachedScheduledEvent: return "ReachedScheduledEvent";
88 case TimeHasAdvanced: return "TimeHasAdvanced";
89 case ReachedStepLimit: return "ReachedStepLimit";
90 case EndOfSimulation: return "EndOfSimulation";
91 case StartOfContinuousInterval: return "StartOfContinuousInterval";
92 case InvalidSuccessfulStepStatus: return "InvalidSuccessfulStepStatus";
93 default: return String("UNKNOWN SUCCESSFUL STEP STATUS ")
94 + String((int)stat);
95 }
96 }
97
98 /*static*/ String Integrator::
getTerminationReasonString(TerminationReason reason)99 getTerminationReasonString(TerminationReason reason) {
100 switch(reason) {
101 case ReachedFinalTime: return "ReachedFinalTime";
102 case AnUnrecoverableErrorOccurred: return "AnUnrecoverableErrorOccurred";
103 case EventHandlerRequestedTermination: return "EventHandlerRequestedTermination";
104 case InvalidTerminationReason: return "InvalidTerminationReason";
105 default: return String("UNKNOWN TERMINATION REASON ")
106 + String((int)reason);
107 }
108 }
109
110 // The following methods are callable only when the Integrator has just
111 // returned a step that ended with an event triggering.
112
getEventWindow() const113 Vec2 Integrator::getEventWindow() const {
114 if (getRep().getStepCommunicationStatus() != IntegratorRep::StepHasBeenReturnedWithEvent) {
115 SimTK_THROW2(CantAskForEventInfoWhenNoEventTriggered, "getEventWindow",
116 getRep().getState().getTime());
117 //NOTREACHED
118 }
119 assert(getRep().getEventWindowLow()==getRep().getState().getTime());
120 assert(getRep().getEventWindowHigh()==getRep().getAdvancedTime());
121 return Vec2(getRep().getEventWindowLow(), getRep().getEventWindowHigh());
122 }
123
124 const Array_<EventId>&
getTriggeredEvents() const125 Integrator::getTriggeredEvents() const {
126 if (getRep().getStepCommunicationStatus() != IntegratorRep::StepHasBeenReturnedWithEvent) {
127 SimTK_THROW2(CantAskForEventInfoWhenNoEventTriggered, "getTriggeredEvents",
128 getRep().getState().getTime());
129 //NOTREACHED
130 }
131 return getRep().getTriggeredEvents();
132 }
133
134 const Array_<Real>&
getEstimatedEventTimes() const135 Integrator::getEstimatedEventTimes() const {
136 if (getRep().getStepCommunicationStatus() != IntegratorRep::StepHasBeenReturnedWithEvent) {
137 SimTK_THROW2(CantAskForEventInfoWhenNoEventTriggered, "getEstimatedEventTimes",
138 getRep().getState().getTime());
139 //NOTREACHED
140 }
141 return getRep().getEstimatedEventTimes();
142 }
143
144 const Array_<Event::Trigger>&
getEventTransitionsSeen() const145 Integrator::getEventTransitionsSeen() const {
146 if (getRep().getStepCommunicationStatus() != IntegratorRep::StepHasBeenReturnedWithEvent) {
147 SimTK_THROW2(CantAskForEventInfoWhenNoEventTriggered, "getEventTransitionsSeen",
148 getRep().getState().getTime());
149 //NOTREACHED
150 }
151 return getRep().getEventTransitionsSeen();
152 }
153
getState() const154 const State& Integrator::getState() const {
155 return getRep().getState();
156 }
157
isStateInterpolated() const158 bool Integrator::isStateInterpolated() const {
159 return getRep().isStateInterpolated();
160 }
161
getAdvancedState() const162 const State& Integrator::getAdvancedState() const {
163 return getRep().getAdvancedState();
164 }
165
updAdvancedState()166 State& Integrator::updAdvancedState() {
167 return updRep().updAdvancedState();
168 }
169
170
getAccuracyInUse() const171 Real Integrator::getAccuracyInUse() const {
172 return getRep().getAccuracyInUse();
173 }
174
getConstraintToleranceInUse() const175 Real Integrator::getConstraintToleranceInUse() const {
176 return getRep().getConstraintToleranceInUse();
177 }
178
getActualInitialStepSizeTaken() const179 Real Integrator::getActualInitialStepSizeTaken() const {
180 return getRep().getActualInitialStepSizeTaken();
181 }
getPreviousStepSizeTaken() const182 Real Integrator::getPreviousStepSizeTaken() const {
183 return getRep().getPreviousStepSizeTaken();
184 }
getPredictedNextStepSize() const185 Real Integrator::getPredictedNextStepSize() const {
186 return getRep().getPredictedNextStepSize();
187 }
getNumStepsAttempted() const188 int Integrator::getNumStepsAttempted() const {
189 return getRep().getNumStepsAttempted();
190 }
getNumStepsTaken() const191 int Integrator::getNumStepsTaken() const {
192 return getRep().getNumStepsTaken();
193 }
getNumRealizations() const194 int Integrator::getNumRealizations() const {
195 return getRep().getNumRealizations();
196 }
getNumQProjections() const197 int Integrator::getNumQProjections() const {
198 return getRep().getNumQProjections();
199 }
getNumUProjections() const200 int Integrator::getNumUProjections() const {
201 return getRep().getNumUProjections();
202 }
getNumProjections() const203 int Integrator::getNumProjections() const {
204 return getRep().getNumQProjections()
205 + getRep().getNumUProjections();
206 }
getNumErrorTestFailures() const207 int Integrator::getNumErrorTestFailures() const {
208 return getRep().getNumErrorTestFailures();
209 }
getNumConvergenceTestFailures() const210 int Integrator::getNumConvergenceTestFailures() const {
211 return getRep().getNumConvergenceTestFailures();
212 }
getNumRealizationFailures() const213 int Integrator::getNumRealizationFailures() const {
214 return getRep().getNumRealizationFailures();
215 }
getNumQProjectionFailures() const216 int Integrator::getNumQProjectionFailures() const {
217 return getRep().getNumQProjectionFailures();
218 }
getNumUProjectionFailures() const219 int Integrator::getNumUProjectionFailures() const {
220 return getRep().getNumUProjectionFailures();
221 }
getNumProjectionFailures() const222 int Integrator::getNumProjectionFailures() const {
223 return getRep().getNumQProjectionFailures()
224 + getRep().getNumUProjectionFailures();
225 }
getNumConvergentIterations() const226 int Integrator::getNumConvergentIterations() const {
227 return getRep().getNumConvergentIterations();
228 }
getNumDivergentIterations() const229 int Integrator::getNumDivergentIterations() const {
230 return getRep().getNumDivergentIterations();
231 }
getNumIterations() const232 int Integrator::getNumIterations() const {
233 return getRep().getNumIterations();
234 }
235
setFinalTime(Real tFinal)236 void Integrator::setFinalTime(Real tFinal) {
237 assert(tFinal == -1. || (0. <= tFinal));
238 updRep().userFinalTime = tFinal;
239 }
240
setInternalStepLimit(int nSteps)241 void Integrator::setInternalStepLimit(int nSteps) {
242 updRep().userInternalStepLimit = nSteps > 0 ? nSteps : -1;
243 }
244
setInitialStepSize(Real z)245 void Integrator::setInitialStepSize(Real z) {
246 assert(z == -1. || z > 0.);
247 assert(getRep().userMinStepSize==-1. || z >= getRep().userMinStepSize);
248 assert(getRep().userMaxStepSize==-1. || z <= getRep().userMaxStepSize);
249 updRep().userInitStepSize = z;
250 }
setMinimumStepSize(Real z)251 void Integrator::setMinimumStepSize(Real z) {
252 assert(z == -1. || z > 0.);
253 assert(getRep().userInitStepSize==-1. || z <= getRep().userInitStepSize);
254 assert(getRep().userMaxStepSize ==-1. || z <= getRep().userMaxStepSize);
255 updRep().userMinStepSize = z;
256 }
setMaximumStepSize(Real z)257 void Integrator::setMaximumStepSize(Real z) {
258 assert(z == -1. || z > 0.);
259 assert(getRep().userInitStepSize==-1. || z >= getRep().userInitStepSize);
260 assert(getRep().userMinStepSize ==-1. || z >= getRep().userMinStepSize);
261 updRep().userMaxStepSize = z;
262 }
setFixedStepSize(Real stepSize)263 void Integrator::setFixedStepSize(Real stepSize) {
264 updRep().userInitStepSize = stepSize;
265 updRep().userMinStepSize = stepSize;
266 updRep().userMaxStepSize = stepSize;
267 }
setAccuracy(Real accuracy)268 void Integrator::setAccuracy(Real accuracy) {
269 assert(accuracy == -1. || (0. < accuracy && accuracy < 1.));
270 updRep().userAccuracy = accuracy;
271 }
setConstraintTolerance(Real consTol)272 void Integrator::setConstraintTolerance(Real consTol) {
273 assert(consTol == -1. || (0. < consTol && consTol <= 1.));
274 updRep().userConsTol=consTol;
275 }
setUseInfinityNorm(bool useInfinityNorm)276 void Integrator::setUseInfinityNorm(bool useInfinityNorm) {
277 updRep().userUseInfinityNorm = useInfinityNorm ? 1 : 0;
278 }
isInfinityNormInUse() const279 bool Integrator::isInfinityNormInUse() const
280 { return getRep().userUseInfinityNorm == 1; }
281
setForceFullNewton(bool forceFullNewton)282 void Integrator::setForceFullNewton(bool forceFullNewton) {
283 updRep().userForceFullNewton = forceFullNewton ? 1 : 0;
284 }
setReturnEveryInternalStep(bool shouldReturn)285 void Integrator::setReturnEveryInternalStep(bool shouldReturn) {
286 updRep().userReturnEveryInternalStep = shouldReturn ? 1 : 0;
287 }
setProjectEveryStep(bool forceProject)288 void Integrator::setProjectEveryStep(bool forceProject) {
289 updRep().userProjectEveryStep = forceProject ? 1 : 0;
290 }
setAllowInterpolation(bool shouldInterpolate)291 void Integrator::setAllowInterpolation(bool shouldInterpolate) {
292 updRep().userAllowInterpolation = shouldInterpolate ? 1 : 0;
293 }
setProjectInterpolatedStates(bool shouldProject)294 void Integrator::setProjectInterpolatedStates(bool shouldProject) {
295 updRep().userProjectInterpolatedStates = shouldProject ? 1 : 0;
296 }
297
methodHasErrorControl() const298 bool Integrator::methodHasErrorControl() const {
299 return getRep().methodHasErrorControl();
300 }
301
getMethodName() const302 const char* Integrator::getMethodName() const {
303 return getRep().getMethodName();
304 }
305
getMethodMinOrder() const306 int Integrator::getMethodMinOrder() const {
307 return getRep().getMethodMinOrder();
308 }
309
getMethodMaxOrder() const310 int Integrator::getMethodMaxOrder() const {
311 return getRep().getMethodMaxOrder();
312 }
313
314 //////////////////////////////////////
315 // IMPLEMENTATION OF INTEGRATOR REP //
316 //////////////////////////////////////
317
IntegratorRep(Integrator * handle,const System & system)318 IntegratorRep::IntegratorRep
319 (Integrator* handle,
320 const System& system)
321 : myHandle(handle), sys(system)
322 {
323 invalidateIntegratorInternalState();
324 initializeUserStuff();
325 resetIntegratorStatistics();
326 resetMethodStatistics();
327 }
328
329 //------------------------------------------------------------------------------
330 // INITIALIZE
331 //------------------------------------------------------------------------------
332
initialize(const State & initState)333 void IntegratorRep::initialize(const State& initState) {
334 try
335 { invalidateIntegratorInternalState();
336
337 // Copy the supplied initial state into the integrator.
338 updAdvancedState() = initState;
339
340 // Freeze the number and kinds of state variables.
341 getSystem().realizeModel(updAdvancedState());
342
343 // Freeze problem dimensions; at this point the state represents an
344 // instance of a physically realizable system.
345 getSystem().realize(getAdvancedState(), Stage::Instance);
346
347 // Some Systems need to get control whenever time is advanced
348 // successfully (and irreversibly) so that they can do discrete updates.
349 systemHasTimeAdvancedEvents = getSystem().hasTimeAdvancedEvents();
350
351 // Allocate integrator-local data structures now that we know the sizes.
352 const int ny = getAdvancedState().getNY();
353 const int nc = getAdvancedState().getNYErr();
354 const int ne = getAdvancedState().getNEventTriggers();
355 timeScaleInUse = getSystem().getDefaultTimeScale();
356
357 // Set accuracy and consTol to their user-requested values or
358 // to the appropriate defaults.
359 setAccuracyAndTolerancesFromUserRequests();
360
361 // Now we can ask the System for information about its event triggers.
362 // (Event trigger info is Instance stage information.) Note that the event
363 // localization windows here are expressed in terms of the system timescale
364 // -- be sure to multiply by timescale before using.
365 getSystem().calcEventTriggerInfo(getAdvancedState(), eventTriggerInfo);
366
367 // Realize Time stage, apply prescribed motions, and attempt to project q's
368 // and u's onto the
369 // position and velocity constraint manifolds to drive constraint errors
370 // below accuracy*unitTolerance for each constraint. We'll allow project()
371 // to throw an exception if it fails since we can't recover from here.
372 // However, we won't set the LocalOnly option which means project() is
373 // allowed to thrash around wildly to attempt to find *some* solution.
374 // Also force repeated updates of iteration matrix to maximize chances of
375 // finding a solution; we're not in a hurry here.
376 realizeAndProjectKinematicsWithThrow(updAdvancedState(),
377 ProjectOptions::ForceProjection, ProjectOptions::ForceFullNewton);
378
379 // Inform any state-using System elements (especially Measures) that we
380 // are starting a simulation and give them a chance to initialize their own
381 // continuous (z) variables and discrete variables.
382
383 // Handler is allowed to throw an exception if it fails since we don't
384 // have a way to recover.
385 HandleEventsOptions handleOpts(getConstraintToleranceInUse());
386 HandleEventsResults results;
387 getSystem().handleEvents(updAdvancedState(),
388 Event::Cause::Initialization,
389 Array_<EventId>(),
390 handleOpts, results);
391 SimTK_ERRCHK_ALWAYS(
392 results.getExitStatus()!=HandleEventsResults::ShouldTerminate,
393 "Integrator::initialize()",
394 "An initialization event handler requested termination.");
395
396 // Now evaluate the state through the Acceleration stage to calculate
397 // the initial state derivatives.
398 realizeStateDerivatives(getAdvancedState());
399
400 // Now that we have valid update values for auto-update discrete variables,
401 // use them to reinitialize the discrete state variables. This one time
402 // only, the value swapped in from the discrete variable here is not yet
403 // suitable for use as an update value, during time integration since it
404 // has never been realized to Instance stage. So we'll force a
405 // re-evaluation.
406 updAdvancedState().autoUpdateDiscreteVariables();
407 getAdvancedState().invalidateAllCacheAtOrAbove(Stage::Instance);
408 // Re-realize to fill in the swapped-in update values.
409 realizeStateDerivatives(getAdvancedState());
410
411 // Record the continuous parts of this now-realized initial state as the
412 // previous state as well (previous state is used when we have to back up
413 // from a failed step attempt).
414 saveStateAndDerivsAsPrevious(getAdvancedState());
415
416 // The initial state is set so it looks like we just *completed* a step to
417 // get here. That way if the first reportTime is zero, this will get
418 // reported.
419 setStepCommunicationStatus(CompletedInternalStepNoEvent);
420 startOfContinuousInterval = true;
421
422 // This will be set to something meaningful when the simulation ends.
423 terminationReason = Integrator::InvalidTerminationReason;
424
425 // Call this virtual method in case the concrete integrator class has
426 // additional initialization needs. Note that it gets the State as we have
427 // adjusted it, NOT the one originally passed to initialize().
428 methodInitialize(getAdvancedState());
429
430 } catch (const std::exception& e) {
431 SimTK_THROW1(Integrator::InitializationFailed, e.what());
432 } /* catch (...) {
433 SimTK_THROW1(Integrator::InitializationFailed, "UNKNOWN EXCEPTION");
434 } */
435 }
436
reinitialize(Stage stage,bool shouldTerminate)437 void IntegratorRep::reinitialize(Stage stage, bool shouldTerminate) {
438 if (stage < Stage::Report) {
439 startOfContinuousInterval = true;
440 setUseInterpolatedState(false);
441 }
442 if (shouldTerminate) {
443 setStepCommunicationStatus(FinalTimeHasBeenReturned);
444 terminationReason = Integrator::EventHandlerRequestedTermination;
445 }
446 methodReinitialize(stage,shouldTerminate);
447 }
448
449 } // namespace SimTK
450
451
452