1 #ifndef SimTK_SIMMATH_INTEGRATOR_REP_H_
2 #define SimTK_SIMMATH_INTEGRATOR_REP_H_
3 
4 /* -------------------------------------------------------------------------- *
5  *                        Simbody(tm): SimTKmath                              *
6  * -------------------------------------------------------------------------- *
7  * This is part of the SimTK biosimulation toolkit originating from           *
8  * Simbios, the NIH National Center for Physics-Based Simulation of           *
9  * Biological Structures at Stanford, funded under the NIH Roadmap for        *
10  * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody.  *
11  *                                                                            *
12  * Portions copyright (c) 2006-12 Stanford University and the Authors.        *
13  * Authors: Michael Sherman                                                   *
14  * Contributors:                                                              *
15  *                                                                            *
16  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
17  * not use this file except in compliance with the License. You may obtain a  *
18  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
19  *                                                                            *
20  * Unless required by applicable law or agreed to in writing, software        *
21  * distributed under the License is distributed on an "AS IS" BASIS,          *
22  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
23  * See the License for the specific language governing permissions and        *
24  * limitations under the License.                                             *
25  * -------------------------------------------------------------------------- */
26 
27 /** @file
28  * This is the declaration of the abstract IntegratorRep class which
29  * represents the implementation of the Integrator class, and DAESystemRep
30  * which implements the Integrator::DAESystem class.
31  */
32 
33 #include "SimTKcommon.h"
34 #include "simmath/Integrator.h"
35 
36 #include <exception>
37 #include <limits>
38 #include <algorithm>
39 #include <vector>
40 
41 namespace SimTK {
42 
43     ///////////////////////////
44     // INTEGRATOR EXCEPTIONS //
45     ///////////////////////////
46 
47 class Integrator::InitializationFailed : public Exception::Base {
48 public:
InitializationFailed(const char * fn,int ln,const char * msg)49     InitializationFailed(const char* fn, int ln, const char* msg) : Base(fn,ln) {
50         setMessage("Integrator initialization failed apparently because:\n"
51                    + String(msg));
52     }
53 };
54 
55 
56 class Integrator::StepSizeTooSmall : public Exception::Base {
57 public:
StepSizeTooSmall(const char * fn,int ln,Real t,Real h,Real hmin)58     StepSizeTooSmall(const char* fn, int ln, Real t, Real h, Real hmin) : Base(fn,ln) {
59         setMessage("At time=" + String(t) +
60                    " the integrator failed to take a step with step size "
61                   + String(h) + " which was already at or below the minimum allowed size "
62                   + String(hmin));
63     }
64 };
65 
66 class Integrator::StepFailed : public Exception::Base {
67 public:
StepFailed(const char * fn,int ln,Real t,const char * msg)68     StepFailed(const char* fn, int ln, Real t, const char* msg) : Base(fn,ln) {
69         setMessage("Integrator step failed at time " + String(t) + " apparently because:\n"
70                    + String(msg));
71     }
72 };
73 
74 class Integrator::CantAskForEventInfoWhenNoEventTriggered : public Exception::Base {
75 public:
CantAskForEventInfoWhenNoEventTriggered(const char * fn,int ln,const char * methodname,Real t)76     CantAskForEventInfoWhenNoEventTriggered
77        (const char* fn, int ln, const char* methodname, Real t) : Base(fn,ln)
78     {
79         setMessage("Method Integrator::" + String(methodname)
80                    + "() was called at time " + String(t)
81                    + " but no event had triggered.");
82     }
83 };
84 
85 //
86 // Specification of the stepTo() method below:
87 //
88 // On entry we expect advancedState to be valid. Time may have already
89 // been advanced past the indicated reportTime in which case we'll
90 // be able to return an interpolated result immediately. We will never
91 // advance past scheduledEventTime or finalTime.
92 //
93 // The Integrator has a simple state machine to deal with the fact that the
94 // computation is interruptable due to the need for returning control
95 // to the caller (usually the time stepper) for various reasons. We enter
96 // this computation either due to a call to the routine stepTo(), or from
97 // within stepTo() after taking an internal step that didn't require a return.
98 // Here's a sketch:
99 //
100 //          advance (or report interpolated value)
101 //           ------
102 //          |      ^
103 //          v      |no
104 //   (TimeAdvanced)--return?->(StepReturned)--final?->(ReturnedFinalTime)
105 //        ^                                 |no
106 //        <-------advance-------------------<
107 //
108 // In words, we take a step, we report that step if necessary (returning
109 // control), then take another step unless we just reported one at the
110 // final time. The state transition table below gives more detail about
111 // what exactly gets reported. "->Xyz" below means return control
112 // to caller of stepTo() with status Xyz, "INTERP t" means "interpolate
113 // back to time t".
114 //
115 // There are additional states below used to distinguish between triggered
116 // event conditions (which take some special handling) and the others.
117 // The only difference between state StepReturned and StepReturnedEvent
118 // is that some event-info query methods are allowed in the latter state.
119 //
120 // Current State     Condition         Action           Next State
121 // ----------------  ----------------  --------------  ----------------
122 // Uninitialized          any          throw error            X
123 // ReturnedFinalTime      any          throw error            X
124 //
125 // StepReturned or   t_adv>=t_final    ->Done         ReturnedFinalTime
126 // StepReturnedEvent    OTHERWISE      ADVANCE       TimeAdvanced[Event]
127 //
128 // TimeAdvancedEvent t_report<t_low    INTERP t_report  (unchanged)
129 //                                     ->Report
130 //
131 //                      OTHERWISE      INTERP t_low
132 //                  (event triggered)  ->Triggered    StepReturnedEvent
133 //
134 // TimeAdvanced      t_report<t_adv    INTERP t_report
135 //                                     ->Report          (unchanged)
136 //
137 //                         else
138 //                   t_adv==t_sched    ->Scheduled      StepReturned
139 //
140 //                         else
141 //                   user wants control
142 //                     at end of step  ->EndOfStep      StepReturned
143 //
144 //                         else
145 //                   t_adv==t_report   ->Report         StepReturned
146 //
147 //                         else
148 //                   t_adv>=t_final    ->Final          StepReturned
149 //
150 //                         else
151 //                   hit step limit    ->StepLimit      StepReturned
152 //
153 //                      OTHERWISE      ADVANCE        TimeAdvanced[Event]
154 // ----------------  ----------------  --------------  ----------------
155 //
156 // Notes:
157 // * for the above conditions to be sufficient, the integrator must
158 //   ensure that the interior of the interval (t_low,t_high) to
159 //   which an event trigger has been localized DOES NOT contain
160 //   t_report, t_sched, or t_final. It is OK if t_report is exactly
161 //   t_low, and it is OK if t_report, t_sched, or t_final is exactly
162 //   t_high.
163 // * the intent of this state diagram is to make sure that the
164 //   most significant end-of-step reason is reported, but that each
165 //   step is reported at most once. Note that when the return status
166 //   is "Done", the final trajectory point has *already* been reported.
167 //   Simultaneous occurrences must be anticipated and handled by the caller
168 //   (time stepper).
169 // * note that an interpolated report doesn't count as reporting the
170 //   step since we haven't reached t_advanced. But the interpolation
171 //   to t_low does since t_advanced==t_high in that case and there
172 //   is nothing in between t_low and t_high. Also, a report that
173 //   coincides with the end of an internal step counts as a step report.
174 
175 class IntegratorRep {
176 public:
177     IntegratorRep(Integrator* handle,
178                   const System&);
~IntegratorRep()179     virtual ~IntegratorRep() { }
180     // no default constructor, no copy or copy assign
181 
182     // The System must be successfully realized to Stage::Instance before this
183     // call. At this point the integrator can query the System about the
184     // problem size and allocate appropriately-sized internal data structures.
185     void initialize(const State&);
186     // This is for use after return from an event handler.
187     void reinitialize(Stage, bool shouldTerminate);
188     // We also give the concrete integration method a chance to initialize
189     // itself after the generic initialization is done.
methodInitialize(const State &)190     virtual void methodInitialize(const State&) { }
191     // This is for use after return from an event handler.
methodReinitialize(Stage stage,bool shouldTerminate)192     virtual void methodReinitialize(Stage stage, bool shouldTerminate) { }
193 
194     // The integrator has already been initialized. Take as many internal steps
195     // as needed to get up to or past reportTime, but don't advance past the
196     // next scheduled event time or the simulation final time.
197     virtual Integrator::SuccessfulStepStatus
198         stepTo(Real reportTime, Real scheduledEventTime) = 0;
199 
isSimulationOver()200     bool isSimulationOver() const {
201         return stepCommunicationStatus == FinalTimeHasBeenReturned;
202     }
203 
204     // Get the reason the simulation ended. This should only be invoked if
205     // isSimulationOver() returns true.
getTerminationReason()206     Integrator::TerminationReason getTerminationReason() const {
207         assert (isSimulationOver());
208         return terminationReason;
209     }
210 
211     // This represents the Integrator's finite state machine for dealing with
212     // communication of internal steps to the caller. After initialization, the
213     // computation will be in state CompletedInternalStepNoEvent, but with
214     // t_prev=t_advanced=t_initial. We will process this as though we had just
215     // taken a step so that various conditions will result in the first
216     // stepTo() call returning immediately. Note: event handling and subsequent
217     // partial reinitialization of the integrator MUST NOT change the
218     // integrator's communication state.
219     enum StepCommunicationStatus {
220         // Time has advanced from t_prev to t_advanced; no event triggered. We
221         // have not yet returned to the caller with time t_advanced, although
222         // we may have returned at interpolated report times
223         // t_prev < t_report < t_advanced.
224         CompletedInternalStepNoEvent,
225 
226         // Time has advanced from t_prev to t_advanced with an event trigger
227         // localized to the interval (t_low,t_high] where t_high==t_advanced.
228         // We have not yet returned to the caller with time t_low, although we
229         // may have returned at interpolated report times
230         // t_prev < t_report < t_low.
231         CompletedInternalStepWithEvent,
232 
233         // We have already returned control to the caller at t_advanced with
234         // the strongest stopping reason given; no more returns are allowed for
235         // this step.
236         StepHasBeenReturnedNoEvent,
237 
238         // We have already returned control to the caller at t_low with
239         // "EventTriggered" as the stopping reason; no more returns are allowed
240         // for this step.
241         StepHasBeenReturnedWithEvent,
242 
243         // Any call to stepTo() when the integrator is already in this state is
244         // a fatal error.
245         FinalTimeHasBeenReturned,
246 
247         InvalidStepCommunicationStatus = -1
248     };
249 
getAdvancedState()250     const State& getAdvancedState() const {return advancedState;}
getAdvancedTime()251     Real         getAdvancedTime()  const {return advancedState.getTime();}
252 
getState()253     const State& getState() const
254     {   return useInterpolatedState ? interpolatedState : advancedState; }
isStateInterpolated()255     bool isStateInterpolated() const {return useInterpolatedState;}
256 
getAccuracyInUse()257     Real getAccuracyInUse() const {return accuracyInUse;}
getConstraintToleranceInUse()258     Real getConstraintToleranceInUse() const {return consTol;}
getTimeScaleInUse()259     Real getTimeScaleInUse() const {return timeScaleInUse;}
260 
261     // What was the size of the first successful step after the last initialize() call?
262     virtual Real getActualInitialStepSizeTaken() const = 0;
263 
264     // What was the size of the most recent successful step?
265     virtual Real getPreviousStepSizeTaken() const = 0;
266 
267     // What step size will be attempted first on the next step() call?
268     virtual Real getPredictedNextStepSize() const = 0;
269 
270     virtual int getNumStepsAttempted() const = 0;
271     virtual int getNumStepsTaken() const = 0;
272     virtual int getNumErrorTestFailures() const = 0;
273     virtual int getNumConvergenceTestFailures() const = 0;
274     virtual int getNumConvergentIterations() const = 0;
275     virtual int getNumDivergentIterations() const = 0;
276     virtual int getNumIterations() const = 0;
277 
resetMethodStatistics()278     virtual void resetMethodStatistics() {
279     }
280 
281     // Cubic Hermite interpolation. See Hairer, et al. Solving ODEs I, 2nd rev.
282     // ed., pg 190. Given (t0,y0,y0'),(t1,y1,y1') with y0 and y1 at least 3rd
283     // order accurate, we can obtain a 3rd order accurate interpolation yt for
284     // a time t0 < t < t1 using Hermite interpolation. Let
285     // f0=y0', f1=y1', h=t1-t0, d=(t-t0)/h (0<=d<=1). Then the interpolating
286     // function is:
287     //   u(d)=y(t0+dh)=(1-d)y0 + dy1 + d(d-1)[(1-2d)(y1-y0) + (d-1)hf0 + dhf1]
288     // Rearrange so we only have to through each array once:
289     //   u(d)=cy0*y0 + cy1*y1 + cf0*f0 + cf1*f1
290     //   cy0= 1 - d^2(3 - 2*d)
291     //   cy1=     d^2(3 - 2*d) = (1-cy0)
292     //   cf0=d*(d-1)^2*h = h*d*(d-1) * (d-1)
293     //   cf1=d^2*(d-1)*h = h*d*(d-1) * d
294     //
295     // Note: you can't get a 3rd order estimate of the derivative ft, only
296     // the state yt.
297     //
298     // It is OK if yt is the same object as y0 or y1.
interpolateOrder3(const Real & t0,const Vector & y0,const Vector & f0,const Real & t1,const Vector & y1,const Vector & f1,const Real & t,Vector & yt)299     static void interpolateOrder3
300        (const Real& t0, const Vector& y0, const Vector& f0,
301         const Real& t1, const Vector& y1, const Vector& f1,
302         const Real& t, Vector& yt) {
303         assert(t0 < t1);
304         assert(t0 <= t && t <= t1);
305         assert(f0.size()==y0.size() && y1.size()==y0.size()
306             && f1.size()==y0.size());
307 
308         const Real h = t1-t0, d=(t-t0)/h;
309         const Real cy1 = d*d*(3-2*d), cy0 = 1-cy1;
310         const Real hdd1 = h*d*(d-1), cf1=hdd1*d, cf0=cf1-hdd1;
311 
312         yt = cy0*y0 + cy1*y1 + cf0*f0 + cf1*f1; // + O(h^4)
313     }
314 
315     // We have bracketed a zero crossing for some function f(t)
316     // between (tLow,fLow) and (tHigh,fHigh), not including
317     // the end points. That means tHigh > tLow, and
318     // sign(fLow) != sign(fHigh) (sign(x) returns -1, 0, 1).
319     // We want to estimate time tRoot with
320     // tLow<tRoot<tHigh such that f(tRoot) is zero.
321     // For a nicely behaved continuous function this is just
322     // the secant method:
323     //      x = fHigh/(fHigh-fLow)  (0 <= x <= 1)
324     //      tRoot = tHigh - x*(tHigh-tLow)
325     // However, if the function appears to be discontinuous we'll
326     // simply bisect the interval (x==0.5 above). We decide it is
327     // discontinuous if either end point is exactly zero, which
328     // would occur with a boolean function, for example. Also, if
329     // the time interval is already at or below the smallest allowable
330     // localization window, we'll bisect.
331     //
332     // One further twist, taken from CVODES, is to allow the caller
333     // to provide a bias (> 0) which will bias our returned tRoot
334     // further into the lower (bias<1) or upper (bias>1) half-interval.
335     // If bias==1 this is the pure secant method. Bias has no effect
336     // on functions deemed to be discontinuous; we'll always bisect
337     // the interval in that case.
338     //
339     // Finally, we won't return tRoot very close to either end of
340     // the interval. Instead we define a buffer zone at either end
341     // of width 10% of the interval, and push tRoot away from the
342     // edges if it gets any closer. And in any case we'll require
343     // at least 1/2 minWindow from either end, even if 10% of the
344     // interval is smaller than that.
345     //
346     // Note that "minWindow" here is *not* the desired localization window
347     // based on user accuracy requirements, but the (typically much smaller)
348     // smallest allowable localization window based on numerical roundoff
349     // considerations.
350 
estimateRootTime(Real tLow,Real fLow,Real tHigh,Real fHigh,Real bias,Real minWindow)351     static Real estimateRootTime(Real tLow, Real fLow, Real tHigh, Real fHigh,
352                                  Real bias, Real minWindow)
353     {
354         assert(tLow < tHigh);
355         assert(sign(fLow) != sign(fHigh));
356         assert(bias > 0);
357         assert(minWindow > 0);
358 
359         const Real h = tHigh-tLow;
360 
361         if (fLow==0 || fHigh==0 || h <= minWindow) {
362             // bisecting
363             return tLow + h/2;
364         }
365 
366         // Use secant method.
367 
368         const Real x = fHigh/(fHigh-bias*fLow);
369         Real tRoot = tHigh - x*h;
370 
371         // If tRoot is too close to either end point we'll assume bad behavior
372         // and guess a value 10% of the interval away from the end.
373 
374         const Real BufferZone = std::max(Real(0.1*h), minWindow/2);
375         tRoot = std::max(tRoot, tLow  + BufferZone);
376         tRoot = std::min(tRoot, tHigh - BufferZone);
377 
378         return tRoot;
379     }
380 
381     // Here we look at a pair of event trigger function values across an
382     // interval and decide if there are any events triggering. If so we return
383     // that event as an "event candidate". Optionally, pass in the current list
384     // of event candidates and we'll only look at those (that is, the list can
385     // only be narrowed). Don't use the same array for the current list and new
386     // list.
387     //
388     // For purposes of this method, events are specified by their indices in
389     // the array of trigger functions, NOT by their event IDs.
findEventCandidates(int nEvents,const Array_<SystemEventTriggerIndex> * viableCandidates,const Array_<Event::Trigger> * viableCandidateTransitions,Real tLow,const Vector & eLow,Real tHigh,const Vector & eHigh,Real bias,Real minWindow,Array_<SystemEventTriggerIndex> & candidates,Array_<Real> & timeEstimates,Array_<Event::Trigger> & transitions,Real & earliestTimeEst,Real & narrowestWindow)390     void findEventCandidates
391        (int nEvents,
392         const Array_<SystemEventTriggerIndex>*  viableCandidates,
393         const Array_<Event::Trigger>*           viableCandidateTransitions,
394         Real    tLow,   const Vector&   eLow,
395         Real    tHigh,  const Vector&   eHigh,
396         Real    bias,   Real            minWindow,
397         Array_<SystemEventTriggerIndex>&        candidates,
398         Array_<Real>&                           timeEstimates,
399         Array_<Event::Trigger>&                 transitions,
400         Real&                                   earliestTimeEst,
401         Real&                                   narrowestWindow) const
402     {
403         int nCandidates;
404         if (viableCandidates) {
405             nCandidates = (int)viableCandidates->size();
406             assert(viableCandidateTransitions && (int)viableCandidateTransitions->size()==nCandidates);
407         } else {
408             assert(!viableCandidateTransitions);
409             nCandidates = nEvents;
410         }
411 
412         candidates.clear();
413         timeEstimates.clear();
414         transitions.clear();
415         earliestTimeEst = narrowestWindow = Infinity;
416         for (int i=0; i<nCandidates; ++i) {
417             const SystemEventTriggerIndex e = viableCandidates ? (*viableCandidates)[i]
418                                                                  : SystemEventTriggerIndex(i);
419             Event::Trigger transitionSeen =
420                 Event::maskTransition(
421                     Event::classifyTransition(sign(eLow[e]), sign(eHigh[e])),
422                     eventTriggerInfo[e].calcTransitionMask());
423 
424             if (transitionSeen != Event::NoEventTrigger) {
425                 // Replace the transition we just saw with the appropriate one for
426                 // reporting purposes. For example, if the event trigger only wants
427                 // negative-to-positive transitions but we just saw negative-to-zero
428                 // we'll report that as negative-to-positive.
429                 transitionSeen = eventTriggerInfo[e].calcTransitionToReport(transitionSeen);
430                 candidates.push_back(e);
431                 narrowestWindow = std::max(
432                     std::min(narrowestWindow,
433                              accuracyInUse*timeScaleInUse*eventTriggerInfo[e].getRequiredLocalizationTimeWindow()),
434                     minWindow);
435 
436                 // Set estimated event trigger time for the viable candidates.
437                 timeEstimates.push_back(estimateRootTime(tLow, eLow[e], tHigh, eHigh[e],
438                                                          bias, minWindow));
439                 transitions.push_back(transitionSeen);
440                 earliestTimeEst = std::min(earliestTimeEst, timeEstimates.back());
441             }
442         }
443     }
444 
445     /// Given a list of events, specified by their indices in the list of trigger functions,
446     /// convert them to the corresponding event IDs.
findEventIds(const Array_<SystemEventTriggerIndex> & indices,Array_<EventId> & ids)447     void findEventIds(const Array_<SystemEventTriggerIndex>& indices, Array_<EventId>& ids) {
448         for (int i = 0; i < (int)indices.size(); ++i)
449             ids.push_back(eventTriggerInfo[indices[i]].getEventId());
450     }
451 
452     // Calculate the error norm using RMS or Inf norm, and report which y
453     // was dominant.
calcErrorNorm(const State & s,const Vector & yErrEst,int & worstY)454     Real calcErrorNorm(const State& s, const Vector& yErrEst,
455                        int& worstY) const {
456         const int nq=s.getNQ(), nu=s.getNU(), nz=s.getNZ();
457         int worstQ, worstU, worstZ;
458         Real qNorm, uNorm, zNorm, maxNorm;
459         if (userUseInfinityNorm == 1) {
460             qNorm = calcWeightedInfNormQ(s, s.getUWeights(), yErrEst(0,nq),
461                                          worstQ);
462             uNorm = calcWeightedInfNorm(getPreviousUScale(), yErrEst(nq,nu),
463                                         worstU);
464             zNorm = calcWeightedInfNorm(getPreviousZScale(), yErrEst(nq+nu,nz),
465                                         worstZ);
466         } else {
467             qNorm = calcWeightedRMSNormQ(s, s.getUWeights(), yErrEst(0,nq),
468                                          worstQ);
469             uNorm = calcWeightedRMSNorm(getPreviousUScale(), yErrEst(nq,nu),
470                                         worstU);
471             zNorm = calcWeightedRMSNorm(getPreviousZScale(), yErrEst(nq+nu,nz),
472                                         worstZ);
473         }
474 
475         // Find the largest of the three norms and report the corresponding
476         // worst offender within q, u, or z.
477         if (qNorm >= uNorm) {
478             if (qNorm >= zNorm)
479                  {maxNorm = qNorm; worstY = worstQ;}        // q>=u && q>=z
480             else {maxNorm = zNorm; worstY = nq+nu+worstZ;}  // z>q>=u
481         } else { // qNorm < uNorm
482             if (uNorm >= zNorm)
483                  {maxNorm = uNorm; worstY = nq + worstU;}   // u>q && u>=z
484             else {maxNorm = zNorm; worstY = nq+nu+worstZ;}  // z>u>q
485         }
486 
487         return maxNorm;
488     }
489 
490 
491     // Given a proposed absolute change dq to generalized coordinates q, scale
492     // it to produce fq, such that fq_i is the fraction of q_i's "unit change"
493     // represented by dq_i. A suitable norm of fq can then be compared directly
494     // with the relative accuracy requirement.
495     //
496     // Because q's are not independent of u's (qdot=N(q)*u), the "unit change"
497     // of q is related to the unit change of u. We want fq=Wq*dq, but we
498     // determine Wq from Wu via Wq = N*Wu*pinv(N). Wq is block diagonal while
499     // Wu is diagonal. State must be realized to Position stage.
scaleDQ(const State & state,const Vector & Wu,const Vector & dq,Vector & dqw)500     void scaleDQ(const State& state, const Vector& Wu,
501                  const Vector& dq, Vector& dqw) const // in/out
502     {
503         const System& system = getSystem();
504         const int nq = state.getNQ();
505         const int nu = state.getNU();
506         assert(dq.size() == nq);
507         assert(Wu.size() == nu);
508         dqw.resize(nq);
509         if (nq==0) return;
510         Vector du(nu);
511         system.multiplyByNPInv(state, dq, du);
512         du.rowScaleInPlace(Wu);
513         system.multiplyByN(state, du, dqw);
514     }
515     // Calculate |Wq*dq|_RMS=|N*Wu*pinv(N)*dq|_RMS
calcWeightedRMSNormQ(const State & state,const Vector & Wu,const Vector & dq,int & worstQ)516     Real calcWeightedRMSNormQ(const State& state, const Vector& Wu,
517                               const Vector& dq, int& worstQ) const
518     {
519         const int nq = state.getNQ();
520         Vector dqw(nq);
521         scaleDQ(state, Wu, dq, dqw);
522         return dqw.normRMS(&worstQ);
523     }
524     // Calculate |Wq*dq|_Inf=|N*Wu*pinv(N)*dq|_Inf
calcWeightedInfNormQ(const State & state,const Vector & Wu,const Vector & dq,int & worstQ)525     Real calcWeightedInfNormQ(const State& state, const Vector& Wu,
526                               const Vector& dq, int& worstQ) const
527     {
528         const int nq = state.getNQ();
529         Vector dqw(nq);
530         scaleDQ(state, Wu, dq, dqw);
531         return dqw.normInf(&worstQ);
532     }
533 
534     // TODO: these utilities don't really belong here
calcWeightedRMSNorm(const Vector & weights,const Vector & values,int & worstOne)535     static Real calcWeightedRMSNorm(const Vector& weights, const Vector& values,
536                                     int& worstOne) {
537         return values.weightedNormRMS(weights, &worstOne);
538     }
539 
calcWeightedInfNorm(const Vector & weights,const Vector & values,int & worstOne)540     static Real calcWeightedInfNorm(const Vector& weights, const Vector& values,
541                                     int& worstOne) {
542         return values.weightedNormInf(weights, &worstOne);
543     }
544 
545     virtual const char* getMethodName() const = 0;
546     virtual int getMethodMinOrder() const = 0;
547     virtual int getMethodMaxOrder() const = 0;
548     virtual bool methodHasErrorControl() const = 0;
549 
550 protected:
getSystem()551     const System& getSystem() const {return sys;}
552 
553     // This is information we extract from the System during initialization or
554     // reinitialization and save here.
getDynamicSystemHasTimeAdvancedEvents()555     bool getDynamicSystemHasTimeAdvancedEvents()      const {return systemHasTimeAdvancedEvents;}
getDynamicSystemTimescale()556     Real getDynamicSystemTimescale()                  const {return timeScaleInUse;}
557     const Array_<EventTriggerInfo>&
getDynamicSystemEventTriggerInfo()558         getDynamicSystemEventTriggerInfo()            const {return eventTriggerInfo;}
559 
getInterpolatedState()560     const State& getInterpolatedState() const {return interpolatedState;}
updInterpolatedState()561     State&       updInterpolatedState()       {return interpolatedState;}
562 
updAdvancedState()563     State& updAdvancedState() {return advancedState;}
564 
setAdvancedState(const Real & t,const Vector & y)565     void setAdvancedState(const Real& t, const Vector& y) {
566         advancedState.updY() = y;
567         advancedState.updTime() = t;
568     }
569 
setTriggeredEvents(Real tlo,Real thi,const Array_<EventId> & eventIds,const Array_<Real> & estEventTimes,const Array_<Event::Trigger> & transitionsSeen)570     void setTriggeredEvents(Real tlo, Real thi,
571                             const Array_<EventId>&  eventIds,
572                             const Array_<Real>& estEventTimes,
573                             const Array_<Event::Trigger>& transitionsSeen)
574     {
575         assert(tPrev <= tlo && tlo < thi && thi <= advancedState.getTime());
576         tLow = tlo;
577         tHigh = thi;
578 
579         const int n = eventIds.size();
580         assert(n > 0 && estEventTimes.size()==n && transitionsSeen.size()==n);
581         triggeredEvents.resize(n); estimatedEventTimes.resize(n); eventTransitionsSeen.resize(n);
582         Array_<int> eventOrder; // will be a permutation of 0:n-1
583         calcEventOrder(eventIds, estEventTimes, eventOrder);
584         for (int i=0; i<(int)eventOrder.size(); ++i) {
585             const int ipos = eventOrder[i];
586             triggeredEvents[i] = eventIds[ipos];
587 
588             assert(tlo < estEventTimes[ipos] && estEventTimes[ipos] <= thi);
589             estimatedEventTimes[i] = estEventTimes[ipos];
590 
591             // TODO: assert that the transition is one of the allowed ones for this event
592             eventTransitionsSeen[i] = transitionsSeen[ipos];
593         }
594     }
595 
getEventWindowLow()596     Real getEventWindowLow()  const {return tLow;}
getEventWindowHigh()597     Real getEventWindowHigh() const {return tHigh;}
598 
getTriggeredEvents()599     const Array_<EventId>&  getTriggeredEvents()  const {return triggeredEvents;}
getEstimatedEventTimes()600     const Array_<Real>& getEstimatedEventTimes()  const {return estimatedEventTimes;}
601     const Array_<Event::Trigger>&
getEventTransitionsSeen()602                        getEventTransitionsSeen() const {return eventTransitionsSeen;}
603 
604     // This determines which state will be returned by getState().
setUseInterpolatedState(bool shouldUse)605     void setUseInterpolatedState(bool shouldUse) {
606         useInterpolatedState = shouldUse;
607     }
608 
setStepCommunicationStatus(StepCommunicationStatus scs)609     void setStepCommunicationStatus(StepCommunicationStatus scs) {
610         stepCommunicationStatus = scs;
611     }
612 
getStepCommunicationStatus()613     StepCommunicationStatus getStepCommunicationStatus() const {
614         return stepCommunicationStatus;
615     }
616 
getPreviousTime()617     const Real&   getPreviousTime()          const {return tPrev;}
618 
619     // q,u,z are views into y.
getPreviousY()620     const Vector& getPreviousY()            const {return yPrev;}
getPreviousQ()621     const Vector& getPreviousQ()            const {return qPrev;}
getPreviousU()622     const Vector& getPreviousU()            const {return uPrev;}
getPreviousZ()623     const Vector& getPreviousZ()            const {return zPrev;}
624 
getPreviousUScale()625     const Vector& getPreviousUScale()       const {return uScalePrev;}
getPreviousZScale()626     const Vector& getPreviousZScale()       const {return zScalePrev;}
627 
628     // qdot,udot,zdot are views into ydot.
getPreviousYDot()629     const Vector& getPreviousYDot()         const {return ydotPrev;}
getPreviousQDot()630     const Vector& getPreviousQDot()         const {return qdotPrev;}
getPreviousUDot()631     const Vector& getPreviousUDot()         const {return udotPrev;}
getPreviousZDot()632     const Vector& getPreviousZDot()         const {return zdotPrev;}
633 
getPreviousQDotDot()634     const Vector& getPreviousQDotDot()      const {return qdotdotPrev;}
635 
getPreviousEventTriggers()636     const Vector& getPreviousEventTriggers() const {return triggersPrev;}
637 
updEventTriggerInfo()638     Array_<EventTriggerInfo>& updEventTriggerInfo() {return eventTriggerInfo;}
639 
640     // Given an array of state variables v (either u or z) and corresponding
641     // weights w (1/absolute scale), return relative scale min(wi,1/|vi|) for
642     // each variable i. That is, we choose the current value of vi as its
643     // scale when it is large enough, otherwise use absolute scale.
calcRelativeScaling(const Vector & v,const Vector & w,Vector & vScale)644     static void calcRelativeScaling(const Vector& v, const Vector& w,
645                                     Vector& vScale)
646     {
647         const int nv = v.size();
648         assert(w.size() == nv);
649         vScale.resize(nv);
650         for (int i=0; i<nv; ++i) {
651             const Real vi = std::abs(v[i]);
652             const Real wi = w[i];
653             vScale[i] = vi*wi > 1 ? 1/vi : wi;
654         }
655     }
656 
657 
658     // We're about to take a step. Record the current time and state as
659     // the previous ones. We calculate the scaling for
660     // u and z here which may include relative scaling based on their current
661     // values. This scaling is frozen during a step attempt.
saveTimeAndStateAsPrevious(const State & s)662     void saveTimeAndStateAsPrevious(const State& s) {
663         const int nq = s.getNQ(), nu = s.getNU(), nz = s.getNZ();
664 
665         tPrev        = s.getTime();
666 
667         yPrev        = s.getY();
668         qPrev.viewAssign(yPrev(0,     nq));
669         uPrev.viewAssign(yPrev(nq,    nu));
670         zPrev.viewAssign(yPrev(nq+nu, nz));
671 
672         calcRelativeScaling(s.getU(), s.getUWeights(), uScalePrev);
673         calcRelativeScaling(s.getZ(), s.getZWeights(), zScalePrev);
674     }
675 
676     // We have evaluated state derivatives and event witness function values
677     // at the start of a new step; record
678     // as previous values to make restarts easy. Must have called
679     // saveTimeAndStateAsPrevious() on this same state already. State must
680     // be realized through Acceleration stage.
saveStateDerivsAsPrevious(const State & s)681     void saveStateDerivsAsPrevious(const State& s) {
682         const int nq = s.getNQ(), nu = s.getNU(), nz = s.getNZ();
683 
684         ydotPrev     = s.getYDot();
685         qdotPrev.viewAssign(ydotPrev(0,     nq));
686         udotPrev.viewAssign(ydotPrev(nq,    nu));
687         zdotPrev.viewAssign(ydotPrev(nq+nu, nz));
688 
689         qdotdotPrev  = s.getQDotDot();
690         triggersPrev = s.getEventTriggers();
691     }
692 
693     // State must already have been evaluated through Stage::Acceleration
694     // or this will throw a stage violation.
saveStateAndDerivsAsPrevious(const State & s)695     void saveStateAndDerivsAsPrevious(const State& s) {
696         saveTimeAndStateAsPrevious(s);
697         saveStateDerivsAsPrevious(s);
698     }
699 
700     // collect user requests
701     Real userInitStepSize, userMinStepSize, userMaxStepSize;
702     Real userAccuracy; // also use for constraintTol
703     Real userConsTol; // for fussy people
704     Real userFinalTime; // never go past this
705     int  userInternalStepLimit; // that is, in a single call to step(); 0=no limit
706 
707     // three-state booleans
708     int  userUseInfinityNorm;           // -1 (not supplied), 0(false), 1(true)
709     int  userReturnEveryInternalStep;   //      "
710     int  userProjectEveryStep;          //      "
711     int  userAllowInterpolation;        //      "
712     int  userProjectInterpolatedStates; //      "
713     int  userForceFullNewton;           //      "
714 
715     // Mark all user-supplied options "not supplied by user".
initializeUserStuff()716     void initializeUserStuff() {
717         userInitStepSize = userMinStepSize = userMaxStepSize = -1.;
718         userAccuracy = userConsTol = -1.;
719         userFinalTime = -1.;
720         userInternalStepLimit = -1;
721 
722         // booleans
723         userUseInfinityNorm = userReturnEveryInternalStep =
724             userProjectEveryStep = userAllowInterpolation =
725             userProjectInterpolatedStates = userForceFullNewton = -1;
726 
727         accuracyInUse = NaN;
728         consTol  = NaN;
729     }
730 
731     // Required accuracy and constraint tolerance.
732     // These are obtained during initialization from the user requests above, and
733     // given default values if the user was agnostic.
734 
735     Real consTol;   // fraction of the constraint unit tolerance to be applied to each constraint
736 
setAccuracyAndTolerancesFromUserRequests()737     void setAccuracyAndTolerancesFromUserRequests() {
738         accuracyInUse = (userAccuracy != -1 ? userAccuracy : Real(1e-3));
739         consTol       = (userConsTol  != -1 ? userConsTol  : accuracyInUse/10);
740     }
741 
742     // If this is set to true, the next call to stepTo() will return immediately
743     // with result code StartOfContinuousInterval. This is set by initialize()
744     // and by reinitialize() after an event handler call that modified the
745     // state.
746     bool startOfContinuousInterval;
747 
748     // The reason the simulation ended.  If isSimulationOver() returns false,
749     // the value of this field is meaningless.
750     Integrator::TerminationReason terminationReason;
751 
752     // Realize the supplied state through Stage::Acceleration
753     // and bump statistics appropriately. Throws
754     // an exception if it fails, with failure statistics bumped.
realizeStateDerivatives(const State & s)755     void realizeStateDerivatives(const State& s) const {
756         if (s.getSystemStage() < Stage::Acceleration) {
757             ++statsRealizations; ++statsRealizationFailures;
758             getSystem().realize(s, Stage::Acceleration);
759             --statsRealizationFailures;
760         }
761     }
762 
763     // State should have had its q's prescribed and realized through Position
764     // stage. This will attempt to project q's and the q part of the yErrEst
765     // (if yErrEst is not length zero). Returns false if we fail which you
766     // can consider a convergence failure for the step. This is intended for
767     // use during integration.
768     // Stats are properly updated. State is realized through Position stage
769     // on successful return.
770     bool localProjectQAndQErrEstNoThrow(State& s, Vector& yErrEst,
771         bool& anyChanges, Real projectionLimit=Infinity)
772     {
773         ProjectOptions options;
774         options.setRequiredAccuracy(getConstraintToleranceInUse());
775         options.setProjectionLimit(projectionLimit);
776         options.setOption(ProjectOptions::LocalOnly);
777         options.setOption(ProjectOptions::DontThrow);
778         if (userProjectEveryStep==1)
779             options.setOption(ProjectOptions::ForceProjection);
780         if (userUseInfinityNorm==1)
781             options.setOption(ProjectOptions::UseInfinityNorm);
782         if (userForceFullNewton==1)
783             options.setOption(ProjectOptions::ForceFullNewton);
784 
785         anyChanges = false;
786         ProjectResults results;
787         // Nothing happens here if position constraints were already satisfied
788         // unless we set the ForceProjection option above.
789         if (yErrEst.size()) {
790             VectorView qErrEst = yErrEst(0, s.getNQ());
791             getSystem().projectQ(s, qErrEst, options, results);
792         } else {
793             getSystem().projectQ(s, yErrEst, options, results);
794         }
795         if (results.getExitStatus() != ProjectResults::Succeeded) {
796             ++statsQProjectionFailures;
797             return false;
798         }
799         anyChanges = results.getAnyChangeMade();
800         if (anyChanges)
801             ++statsQProjections;
802 
803         return true;
804     }
805 
806     // State should have had its q's and u's prescribed and realized through
807     // Velocity stage. This will attempt to project u's and the u part of the
808     // yErrEst (if yErrEst is not length zero). Returns false if we fail which
809     // you can consider a convergence failure for the step. This is intended for
810     // use during integration.
811     // Stats are properly updated. State is realized through Velocity stage
812     // on successful return.
813     bool localProjectUAndUErrEstNoThrow(State& s, Vector& yErrEst,
814         bool& anyChanges, Real projectionLimit=Infinity)
815     {
816         ProjectOptions options;
817         options.setRequiredAccuracy(getConstraintToleranceInUse());
818         options.setProjectionLimit(projectionLimit);
819         options.setOption(ProjectOptions::LocalOnly);
820         options.setOption(ProjectOptions::DontThrow);
821         if (userProjectEveryStep==1)
822             options.setOption(ProjectOptions::ForceProjection);
823         if (userUseInfinityNorm==1)
824             options.setOption(ProjectOptions::UseInfinityNorm);
825         if (userForceFullNewton==1)
826             options.setOption(ProjectOptions::ForceFullNewton);
827 
828         anyChanges = false;
829         ProjectResults results;
830         // Nothing happens here if velocity constraints were already satisfied
831         // unless we set the ForceProjection option above.
832         if (yErrEst.size()) {
833             VectorView uErrEst = yErrEst(s.getNQ(), s.getNU());
834             getSystem().projectU(s, uErrEst, options, results);
835         } else {
836             getSystem().projectU(s, yErrEst, options, results);
837         }
838         if (results.getExitStatus() != ProjectResults::Succeeded) {
839             ++statsUProjectionFailures;
840             return false;
841         }
842         anyChanges = results.getAnyChangeMade();
843         if (anyChanges)
844             ++statsUProjections;
845 
846         return true;
847     }
848 
849     // Given a state which has just had t and y updated, realize it through
850     // velocity stage taking care of prescribed motion, projection, and
851     // throwing an exception if anything goes wrong. This is not for use
852     // during normal integration but good for initialization and generation
853     // of interpolated states.
854     // Extra options to consider are whether to restrict projection to the
855     // local neighborhood, and whether to unconditionally force projection.
856     // Stats are properly updated. State is realized through Velocity stage
857     // on return.
858     void realizeAndProjectKinematicsWithThrow(State& s,
859         ProjectOptions::Option xtraOption1=ProjectOptions::None,
860         ProjectOptions::Option xtraOption2=ProjectOptions::None,
861         ProjectOptions::Option xtraOption3=ProjectOptions::None)
862     {
863         const System& system = getSystem();
864         ProjectOptions options;
865         options.setRequiredAccuracy(getConstraintToleranceInUse());
866         options.setOption(xtraOption1);
867         options.setOption(xtraOption2);
868         options.setOption(xtraOption3);
869         if (userProjectEveryStep==1)
870             options.setOption(ProjectOptions::ForceProjection);
871         if (userUseInfinityNorm==1)
872             options.setOption(ProjectOptions::UseInfinityNorm);
873         if (userForceFullNewton==1)
874             options.setOption(ProjectOptions::ForceFullNewton);
875 
876         system.realize(s, Stage::Time);
877         system.prescribeQ(s);
878         system.realize(s, Stage::Position);
879 
880         Vector dummy; // no error estimate to project
881         ProjectResults results;
882         ++statsQProjectionFailures; // assume failure, then fix if no throw
883         system.projectQ(s, dummy, options, results);
884         --statsQProjectionFailures; // false alarm -- it succeeded
885         if (results.getAnyChangeMade())
886             ++statsQProjections;
887 
888         system.prescribeU(s);
889         system.realize(s, Stage::Velocity);
890 
891         results.clear();
892         ++statsUProjectionFailures; // assume failure, then fix if no throw
893         system.projectU(s, dummy, options, results);
894         --statsUProjectionFailures; // false alarm -- it succeeded
895         if (results.getAnyChangeMade())
896             ++statsUProjections;
897     }
898 
899     // Set the advanced state and then evaluate state derivatives. Throws an
900     // exception if it fails. Updates stats.
setAdvancedStateAndRealizeDerivatives(const Real & t,const Vector & y)901     void setAdvancedStateAndRealizeDerivatives(const Real& t, const Vector& y)
902     {
903         const System& system = getSystem();
904         State& advanced = updAdvancedState();
905 
906         setAdvancedState(t,y);
907 
908         system.realize(advanced, Stage::Time);
909         system.prescribeQ(advanced); // set q_p
910         system.realize(advanced, Stage::Position);
911         system.prescribeU(advanced); // set u_p
912 
913         // Now realize Velocity, Dynamics, and Acceleration stages.
914         realizeStateDerivatives(getAdvancedState());
915     }
916 
917     // Set the advanced state and then evaluate constraint errors. Throws an
918     // exception if it fails. Never counts as a realization because
919     // we only need to realize kinematics.
setAdvancedStateAndRealizeKinematics(const Real & t,const Vector & y)920     void setAdvancedStateAndRealizeKinematics(const Real& t, const Vector& y)
921     {
922         const System& system = getSystem();
923         State& advanced = updAdvancedState();
924 
925         setAdvancedState(t,y);
926 
927         system.realize(advanced, Stage::Time);
928         system.prescribeQ(advanced); // set q_p
929         system.realize(advanced, Stage::Position);
930         system.prescribeU(advanced); // set u_p
931 
932         // Now realize remaining kinematics.
933         system.realize(advanced, Stage::Velocity);
934     }
935 
936 
resetIntegratorStatistics()937     void resetIntegratorStatistics() {
938         statsRealizations = 0;
939         statsQProjections = statsUProjections = 0;
940         statsRealizationFailures = 0;
941         statsQProjectionFailures = statsUProjectionFailures = 0;
942     }
943 
getNumRealizations()944     int getNumRealizations() const {return statsRealizations;}
getNumQProjections()945     int getNumQProjections() const {return statsQProjections;}
getNumUProjections()946     int getNumUProjections() const {return statsUProjections;}
947 
getNumRealizationFailures()948     int getNumRealizationFailures() const {return statsRealizationFailures;}
getNumQProjectionFailures()949     int getNumQProjectionFailures() const {return statsQProjectionFailures;}
getNumUProjectionFailures()950     int getNumUProjectionFailures() const {return statsUProjectionFailures;}
951 
952 private:
953     class EventSorter {
954     public:
EventSorter()955         EventSorter() : ordinal(-1), id(-1), estTime(NaN) { }
EventSorter(int ord,int eventId,Real estEventTime)956         EventSorter(int ord, int eventId, Real estEventTime)
957           : ordinal(ord), id(eventId), estTime(estEventTime) { }
958         bool operator<(const EventSorter& r) const {
959             if (estTime < r.estTime) return true;
960             if (estTime > r.estTime) return false;
961             return id < r.id;
962         }
963 
964         int  ordinal; // original position in the eventIds array
965         int  id;
966         Real estTime;
967     };
968 
calcEventOrder(const Array_<EventId> & eventIds,const Array_<Real> & estEventTimes,Array_<int> & eventOrder)969     void calcEventOrder(const Array_<EventId>&  eventIds,
970                         const Array_<Real>& estEventTimes,
971                         Array_<int>&        eventOrder)
972     {
973         const unsigned n = eventIds.size();
974         assert(estEventTimes.size()==n);
975         eventOrder.resize(n);
976         if (n==0)
977             return;
978 
979         if (n==1) {
980             eventOrder[0] = 0;
981             return;
982         }
983 
984         // otherwise sort
985         Array_<EventSorter> events(n);
986         for (unsigned i=0; i<n; ++i)
987             events[i] = EventSorter(i, eventIds[i], estEventTimes[i]);
988         std::sort(events.begin(), events.end());
989         for (unsigned i=0; i<n; ++i)
990             eventOrder[i] = events[i].ordinal;
991     }
992 
993 private:
994     Integrator* myHandle;
995     friend class Integrator;
996 
997     const System& sys;
998 
999 
1000 protected:
1001     // realization and projection stats are shared by all integrators;
1002     // others are left to the individual integration methods
1003     mutable int statsQProjectionFailures, statsUProjectionFailures;
1004     mutable int statsQProjections, statsUProjections;
1005     mutable int statsRealizations;
1006     mutable int statsRealizationFailures;
1007 private:
1008 
1009         // SYSTEM INFORMATION
1010         // Information extracted from the System describing properties we need
1011         // to know about BEFORE taking a time step. These properties are always
1012         // frozen across an integration step, but may be updated as discrete
1013         // updates during time stepping.
1014 
1015     // Some Systems need to get control whenever time is advanced
1016     // successfully (and irreversibly) so that they can do discrete updates.
1017     // This is Stage::Model information.
1018     bool systemHasTimeAdvancedEvents;
1019 
1020 
1021     // Event trigger functions specify which sign transitions should be
1022     // monitored: rising, falling, to/from zero, or combinations of those.
1023     // They also provide a localization window width.
1024     // This is Stage::Instance information.
1025 
1026     Array_<EventTriggerInfo> eventTriggerInfo;
1027 
1028     // A unitless fraction.
1029     Real accuracyInUse;
1030 
1031     // The time scale is an estimate as to what is the smallest length of time
1032     // on which we can expect any significant changes to occur. This can be
1033     // used to estimate an initial step size, and it also establishes the units
1034     // in which event trigger localization windows are defined (that is, the
1035     // window is given as some fraction of the time scale).
1036     // This is Stage::Instance information.
1037     Real timeScaleInUse;
1038 
1039         // INTEGRATOR INTERNAL STATE
1040         // Persists between stepTo() calls.
1041         // A concrete integrator is free to have more state.
1042 
1043     StepCommunicationStatus stepCommunicationStatus;
1044 
1045     // We save both the step size we have to take next and the one
1046     // we wish we could take. Once we're done with whatever's causing
1047     // us to come up short (like event isolation), we may be able to
1048     // jump right back up to the ideal one.
1049     Real    nextStepSizeToTry;  // This is what we'll actually try first.
1050     Real    idealNextStepSize;  // But if accuracy were the only concern
1051                                 //   we would try this (>=nextStepSizeToTry).
1052 
1053     // This is how far the integrator has advanced the system
1054     // trajectory. We might allow an interpolated state a
1055     // little earlier than this, but otherwise there is no
1056     // going back from this point.
1057     State   advancedState;
1058 
1059     // When stepCommunicationStatus indicates that an event has
1060     // triggered, the following arrays are valid. Events are sorted
1061     // by estimated occurrence time within the window; identical-time
1062     // events are sorted in ascending order of event Id.
1063 
1064     // These are the events that the integrator has algorithmically
1065     // determined are now triggered. You may not get the same result
1066     // simply comparing the trigger function values at tLow and tHigh.
1067     Array_<EventId>  triggeredEvents;
1068 
1069     // These are the estimated times corresponding to the triggeredEvents.
1070     // They are in ascending order although there may be duplicates.
1071     Array_<Real> estimatedEventTimes;
1072 
1073     // Which transition was seen for each triggered event (this is
1074     // only a single transition, not an OR-ed together set). This is
1075     // the integrator's algorithmic determination of the transition to
1076     // be reported -- you might not get the same answer just looking
1077     // at the event trigger functions at tLow and tHigh.
1078     Array_<Event::Trigger> eventTransitionsSeen;
1079 
1080     // When we have successfully localized a triggering event into
1081     // the time interval (tLow,tHigh] we record the bounds here.
1082     Real    tLow, tHigh;
1083 
1084     State   interpolatedState;    // might be unused
1085     bool    useInterpolatedState;
1086 
1087     // Use these to record the continuous part of the previous
1088     // accepted state. We use these in combination with the
1089     // continuous contents of advancedState to fill in the
1090     // interpolated state.
1091     Real    tPrev;
1092     Vector  yPrev;
1093 
1094     // These combine weightings from the Prev state with the possible
1095     // requirement of relative accuracy using the current values of u and z.
1096     // The result is min( wxi, 1/|xi|) for the relative ones where wxi is
1097     // the weight (1/absolute scale) and xi is either ui or zi.
1098     Vector  uScalePrev;
1099     Vector  zScalePrev;
1100 
1101     // Save continuous derivatives and event function values
1102     // from previous accepted state also so we don't have to
1103     // recalculate for restarts.
1104     Vector  ydotPrev;
1105     Vector  qdotdotPrev;
1106     Vector  triggersPrev;
1107 
1108     // These are views into yPrev and ydotPrev.
1109     Vector qPrev, uPrev, zPrev;
1110     Vector qdotPrev, udotPrev, zdotPrev;
1111 
1112     // We'll leave the various arrays above sized as they are and full
1113     // of garbage. They'll be resized when first assigned to something
1114     // meaningful.
invalidateIntegratorInternalState()1115     void invalidateIntegratorInternalState() {
1116         stepCommunicationStatus = InvalidStepCommunicationStatus;
1117         nextStepSizeToTry       = NaN;
1118         idealNextStepSize       = NaN;
1119         tLow = tHigh            = NaN;
1120         useInterpolatedState    = false;
1121         tPrev                   = NaN;
1122     }
1123 
1124     // suppress
1125     IntegratorRep(const IntegratorRep&);
1126     IntegratorRep& operator=(const IntegratorRep&);
1127 };
1128 
1129 } // namespace SimTK
1130 
1131 #endif // SimTK_SIMMATH_INTEGRATOR_REP_H_
1132 
1133 
1134