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