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