NumericalMethods.h

Go to the documentation of this file.
00001 #ifndef SimTK_SIMBODY_NUMERICAL_METHODS_H_
00002 #define SimTK_SIMBODY_NUMERICAL_METHODS_H_
00003 
00004 /* -------------------------------------------------------------------------- *
00005  *                      SimTK Core: SimTK Simbody(tm)                         *
00006  * -------------------------------------------------------------------------- *
00007  * This is part of the SimTK Core biosimulation toolkit originating from      *
00008  * Simbios, the NIH National Center for Physics-Based Simulation of           *
00009  * Biological Structures at Stanford, funded under the NIH Roadmap for        *
00010  * Medical Research, grant U54 GM072970. See https://simtk.org.               *
00011  *                                                                            *
00012  * Portions copyright (c) 2005-7 Stanford University and the Authors.         *
00013  * Authors: Michael Sherman                                                   *
00014  * Contributors:                                                              *
00015  *                                                                            *
00016  * Permission is hereby granted, free of charge, to any person obtaining a    *
00017  * copy of this software and associated documentation files (the "Software"), *
00018  * to deal in the Software without restriction, including without limitation  *
00019  * the rights to use, copy, modify, merge, publish, distribute, sublicense,   *
00020  * and/or sell copies of the Software, and to permit persons to whom the      *
00021  * Software is furnished to do so, subject to the following conditions:       *
00022  *                                                                            *
00023  * The above copyright notice and this permission notice shall be included in *
00024  * all copies or substantial portions of the Software.                        *
00025  *                                                                            *
00026  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR *
00027  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,   *
00028  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL    *
00029  * THE AUTHORS, CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,    *
00030  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR      *
00031  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE  *
00032  * USE OR OTHER DEALINGS IN THE SOFTWARE.                                     *
00033  * -------------------------------------------------------------------------- */
00034 
00035 #include "simbody/internal/common.h"
00036 #include "SimTKcpodes.h"
00037 
00038 #include <cassert>
00039 
00040 namespace SimTK {
00041 
00146 class MechanicalDAEIntegrator {
00147 public:
00148     explicit MechanicalDAEIntegrator(const MultibodySystem& mb, State& s) 
00149         : mbs(mb), state(s) {
00150         initializeUserStuff();
00151         zeroStats();
00152     }
00153     virtual ~MechanicalDAEIntegrator() { }
00154 
00155     const MultibodySystem& getMultibodySystem() const {return mbs;}
00156     const State&           getState()           const {return state;}
00157     State&                 updState()                 {return state;}
00158 
00159     virtual MechanicalDAEIntegrator* clone()       const = 0;
00160 
00161     virtual bool initialize() = 0;
00162     virtual bool step(const Real& tout) = 0;
00163 
00164     virtual Real getConstraintTolerance() const = 0;
00165 
00166     void setStopTime(const Real& tstop) {
00167         assert(tstop == -1. || (0. <= tstop));
00168         userStopTime = tstop;
00169     }
00170 
00171     void setInitialStepSize(const Real& z) {
00172         assert(z == -1. || z > 0.);
00173         assert(userMinStepSize==-1. || z >= userMinStepSize);
00174         assert(userMaxStepSize==-1. || z <= userMaxStepSize);
00175         userInitStepSize = z;
00176     }
00177     void setMinimumStepSize(const Real& z) { 
00178         assert(z == -1. || z > 0.);
00179         assert(userInitStepSize==-1. || z <= userInitStepSize);
00180         assert(userMaxStepSize ==-1. || z <= userMaxStepSize);
00181         userMinStepSize = z;
00182     }
00183     void setMaximumStepSize(const Real& z) {
00184         assert(z == -1. || z > 0.);
00185         assert(userInitStepSize==-1. || z >= userInitStepSize);
00186         assert(userMinStepSize ==-1. || z >= userMinStepSize);
00187         userMaxStepSize = z;
00188     }
00189     void setAccuracy(const Real& accuracy) {
00190         assert(accuracy == -1. || (0. < accuracy && accuracy < 1.));
00191         userAccuracy = accuracy;
00192     }
00193     void setRelativeTolerance(const Real& relTol) {
00194         assert(relTol  == -1. || (0. < relTol  && relTol  <= 1.));
00195         userRelTol=relTol;
00196     }
00197     void setAbsoluteTolerance(const Real& absTol) {
00198         assert(absTol  == -1. || (0. < absTol  && absTol  <= 1.));
00199         userAbsTol=absTol;
00200     }
00201     void setConstraintTolerance(const Real& consTol) {
00202         assert(consTol == -1. || (0. < consTol && consTol <= 1.));
00203         userConsTol=consTol;
00204     }
00205     void setVelocityConstraintRescale(const Real& rescale) {
00206         assert(rescale == -1 || (0. <= rescale));
00207         userVConsRescale = rescale;
00208     }
00209     void setProjectEveryStep(bool forceProject) {
00210         userProjectEveryStep = forceProject ? 1 : 0;
00211     }
00212 
00213     long getStepsAttempted()     const {return statsStepsAttempted;}
00214     long getStepsTaken()         const {return statsStepsTaken;}
00215     long getErrorTestFailures()  const {return statsErrorTestFailures;}
00216     long getRealizeFailures()    const {return statsRealizationFailures;}
00217     long getProjectionFailures() const {return statsProjectionFailures;}
00218     long getStepSizeChanges()    const {return statsStepSizeChanges;}
00219 
00220 protected:
00221     const MultibodySystem& mbs;
00222     State&                 state;
00223 
00224     // collect user requests
00225     Real userInitStepSize, userMinStepSize, userMaxStepSize;
00226     Real userAccuracy; // use for relTol, absTol, constraintTol
00227     Real userRelTol, userAbsTol, userConsTol, userVConsRescale; // for fussy people
00228     Real userStopTime; // never go past this
00229     long userMaxNumSteps; // that is, in a single call to step(); 0=no limit
00230     int  userProjectEveryStep; // -1 (not supplied), 0(false), 1(true)
00231 
00232     // Mark all user-supplied options "not supplied by user".
00233     void initializeUserStuff() {
00234         userInitStepSize = userMinStepSize = userMaxStepSize = -1.;
00235         userAccuracy = userRelTol = userAbsTol = userConsTol = userVConsRescale = -1.;
00236         userStopTime = -1.;
00237         userMaxNumSteps = -1;
00238         userProjectEveryStep= -1;
00239     }
00240 
00241     // Statistics
00242     long statsStepsAttempted;
00243     long statsStepsTaken;
00244     long statsErrorTestFailures;
00245     long statsRealizationFailures;
00246     long statsProjectionFailures;
00247     long statsStepSizeChanges;
00248 
00249     void zeroStats() {
00250         statsStepsAttempted = statsStepsTaken = statsErrorTestFailures
00251             = statsRealizationFailures = statsProjectionFailures = statsStepSizeChanges = 0;
00252     }
00253 
00254 };
00255 
00256 // This class implements the abstract CPodesSystem interface understood by
00257 // our C++ interface to CPodes.
00258 class CPodesMultibodySystem : public CPodesSystem {
00259 public:
00260     CPodesMultibodySystem(MechanicalDAEIntegrator* p) : mdae(p) 
00261     {
00262     }
00263 
00264     // Override default implementations of these virtual functions.
00265     //   explicitODE()
00266     //   project()
00267 
00268 
00269     // Calculate ydot = f(t,y).
00270     int explicitODE(Real t, const Vector& y, Vector& ydot) const {
00271         mdae->updState().updY() = y;
00272         mdae->updState().updTime() = t;
00273 
00274         try { 
00275             mdae->getMultibodySystem().realize(mdae->getState(), Stage::Acceleration); 
00276         }
00277         catch(...) { return CPodes::RecoverableError; } // assume recoverable
00278         ydot = mdae->getState().getYDot();
00279         return CPodes::Success;
00280     }
00281 
00282     // Calculate yerr = c(t,y).
00283     int constraint(Real t, const Vector& y, Vector& yerr) const {
00284         mdae->updState().updY() = y;
00285         mdae->updState().updTime() = t;
00286 
00287         try { 
00288             mdae->getMultibodySystem().realize(mdae->getState(), Stage::Velocity); 
00289         }
00290         catch(...) { return CPodes::RecoverableError; } // assume recoverable
00291         yerr = mdae->getState().getYErr();
00292         return CPodes::Success;
00293     }
00294 
00295     // Given a state (t,y) not on the constraint manifold, return ycorr
00296     // such that (t,y+ycorr+eps) is on the manifold, with ||eps||_wrms <= epsProj. 
00297     // 'err' passed in as the integrator's current error estimate for state y;
00298     // optionally project it to eliminate the portion normal to the manifold.
00299     int project(Real t, const Vector& y, Vector& ycorr, Real epsProj, 
00300                 Vector& err) const 
00301     {
00302         State& s = mdae->updState();
00303         s.updY() = y;
00304         s.updTime() = t;
00305 
00306         const MultibodySystem& mbs = mdae->getMultibodySystem();
00307 
00308         try {
00309             const Real tol = mdae->getConstraintTolerance();
00310             Vector yUnitWeights, unitTolerances;
00311             mbs.realize(s, Stage::Position);
00312             mbs.calcYUnitWeights(s, yUnitWeights);
00313             mbs.calcYErrUnitTolerances(s, unitTolerances);
00314 
00315             mbs.project(s, tol, yUnitWeights, unitTolerances, err);
00316         }
00317         catch (...) { return CPodes::RecoverableError; } // assume recoverable
00318 
00319         ycorr = s.getY()-y;
00320         return CPodes::Success;
00321     }
00322 
00323     /*
00324     virtual int  quadrature(Real t, const Vector& y, 
00325                             Vector& qout) const;
00326     virtual int  root(Real t, const Vector& y, const Vector& yp,
00327                       Vector& gout) const;
00328     virtual int  weight(const Vector& y, Vector& weights) const;
00329     virtual void errorHandler(int error_code, const char* module,
00330                               const char* function, char* msg) const;
00331     */
00332     
00333     MechanicalDAEIntegrator *mdae;
00334 };
00335 
00336 class OLDCPodesIntegrator : public MechanicalDAEIntegrator {
00337 public:
00338     OLDCPodesIntegrator(const MultibodySystem& mb, State& s) 
00339       : MechanicalDAEIntegrator(mb,s), cpodes(0), sys(0)
00340     {
00341         SimTK_STAGECHECK_GE_ALWAYS(state.getSystemStage(), Stage::Topology,
00342             "CPodesIntegrator::CPodesIntegrator()");
00343         reconstructForNewModel();
00344     }
00345 
00346     // Virtual function implementations
00347     ~OLDCPodesIntegrator() {
00348         delete cpodes;
00349         delete sys;
00350     }
00351     OLDCPodesIntegrator* clone() const {assert(false); return 0;}
00352 
00353     const CPodes& getCPodes() const {assert(cpodes); return *cpodes;}
00354 
00355     bool initialize() {
00356         SimTK_STAGECHECK_GE_ALWAYS(state.getSystemStage(), Stage::Topology,
00357             "CPodesIntegrator::initialize()");
00358         if (state.getSystemStage() < Stage::Model)
00359             reconstructForNewModel();
00360         initializeIntegrationParameters();
00361 
00362         initialized = true;
00363 
00364         mbs.realize(state, Stage::Velocity);
00365 
00366         const int ny = state.getY().size();
00367         const int nc = state.getYErr().size();
00368 
00369         Vector ycorr(ny);
00370         Vector err(ny, Real(0));
00371         if (sys->project(state.getTime(), state.getY(), ycorr, 0./*ignored*/, err)
00372                 != CPodes::Success)
00373             return false;
00374         state.updY() = state.getY() + ycorr;
00375 
00376         mbs.realize(state, Stage::Velocity);
00377 
00378         Vector ydot(ny);
00379         if (sys->explicitODE(state.getTime(), state.getY(), ydot) 
00380                != CPodes::Success)
00381             return false;
00382 
00383         int retval;
00384         if ((retval=cpodes->init(*sys, state.getTime(), state.getY(), ydot,
00385             CPodes::ScalarScalar, relTol, &absTol)) != CPodes::Success) 
00386         {
00387             printf("init returned %d\n", retval);
00388             return false;
00389         }
00390         cpodes->lapackDense(ny);
00391         cpodes->setNonlinConvCoef(0.01); // TODO (default is 0.1)
00392         cpodes->setMaxNumSteps(50000);
00393         cpodes->projDefine();
00394         /*
00395         if (nc) {
00396             cpodes->projInit(CPodes::L2Norm, CPodes::Nonlinear,
00397                     Vector(nc, getConstraintTolerance()));
00398             //cpodes->setProjUpdateErrEst(false);
00399             //cpodes->setProjFrequency(long proj_freq);
00400             //cpodes->setProjTestCnstr(true);
00401             //cpodes->setProjLsetupFreq(long proj_lset_freq);
00402             //cpodes->setProjNonlinConvCoef(.1);
00403             cpodes->lapackDenseProj(nc, ny, 
00404                 //CPodes::ProjectWithSchurComplement
00405                 CPodes::ProjectWithQRPivot
00406                 //CPodes::ProjectWithLU
00407                 );
00408         }
00409         /**/
00410         return true;
00411     }
00412 
00413     bool step(const Real& tout) {
00414         // Re-parametrizing or remodeling requires a new call to initialize().
00415         SimTK_STAGECHECK_GE_ALWAYS(state.getSystemStage(), Stage::Instance,
00416             "CPodesIntegrator::step()");
00417         assert(initialized);
00418 
00419         Real   tret; // ignored
00420         Vector ypout(state.getY().size()); // ignored
00421 
00422         // TODO: deal with stop time
00423         int res = cpodes->step(tout, &tret, state.updY(), ypout, CPodes::Normal);
00424         return res >= 0;
00425     }
00426 
00427     Real getConstraintTolerance() const {
00428         assert(initialized);
00429         return consTol;
00430     }
00431 
00432     Real getPredictedNextStep() const {
00433         assert(initialized);
00434         Real hnext;
00435         (void)cpodes->getCurrentStep(&hnext);
00436         return hnext;
00437     }
00438 private:
00439     void initializeIntegrationParameters() {
00440         mbs.realize(state, Stage::Instance);
00441 
00442         initializeStepSizes();
00443         initializeTolerances();  
00444 
00445         if (userStopTime != -1.) 
00446             cpodes->setStopTime(userStopTime);
00447         if (userMaxNumSteps != -1) 
00448             cpodes->setMaxNumSteps(userMaxNumSteps);
00449         if (userProjectEveryStep != -1)
00450             if (userProjectEveryStep==1)
00451                 cpodes->setProjFrequency(1); // every step
00452     }
00453 
00454     void initializeStepSizes() {
00455         if (userInitStepSize != -1)
00456             cpodes->setInitStep(userInitStepSize);
00457         if (userMinStepSize != -1)
00458             cpodes->setMinStep(userMinStepSize);
00459         if (userMaxStepSize != -1)
00460             cpodes->setMaxStep(userMaxStepSize);
00461     }
00462 
00463     void initializeTolerances() {
00464         accuracy     = (userAccuracy     != -1. ? userAccuracy     : 1e-3);
00465         relTol       = (userRelTol       != -1. ? userRelTol       : accuracy); 
00466         absTol       = (userAbsTol       != -1. ? userAbsTol       : accuracy); 
00467         consTol      = (userConsTol      != -1. ? userConsTol      : accuracy); 
00468         vconsRescale = (userVConsRescale != -1. ? userVConsRescale : 1.); 
00469     }
00470 private:
00471     void reconstructForNewModel() {
00472         initialized = false;
00473         delete cpodes;
00474         delete sys;
00475         mbs.realize(state, Stage::Model);
00476         accuracy=relTol=absTol=consTol=vconsRescale
00477             = CNT<Real>::getNaN();
00478 
00479         cpodes = new CPodes(CPodes::ExplicitODE, CPodes::BDF, CPodes::Newton);
00480         //cpodes = new CPodes(CPodes::ExplicitODE, CPodes::BDF, CPodes::Functional);
00481         sys = new CPodesMultibodySystem(this);
00482     }
00483 
00484     CPodes*                cpodes;
00485     CPodesMultibodySystem* sys;
00486     Real accuracy, relTol, absTol, consTol, vconsRescale;
00487     bool initialized;
00488 };
00489 
00490 class OLDExplicitEuler : public MechanicalDAEIntegrator {
00491 public:
00492     OLDExplicitEuler(const MultibodySystem& mb, State& s) 
00493       : MechanicalDAEIntegrator(mb,s) 
00494     {
00495         SimTK_STAGECHECK_GE_ALWAYS(state.getSystemStage(), Stage::Topology,
00496             "ExplicitEuler::ExplicitEuler()");
00497         reconstructForNewModel();
00498     }
00499 
00500     OLDExplicitEuler* clone() const {return new OLDExplicitEuler(*this);}
00501 
00502     Real getConstraintTolerance() const {
00503         assert(initialized);
00504         return consTol;
00505     }
00506 
00507     Real getPredictedNextStep() const {
00508         assert(initialized);
00509         return maxStepSize;
00510     }
00511 
00512     bool initialize() {
00513         SimTK_STAGECHECK_GE_ALWAYS(state.getSystemStage(), Stage::Topology,
00514             "OLDExplicitEuler::initialize()");
00515         if (state.getSystemStage() < Stage::Model)
00516             reconstructForNewModel();
00517         initializeIntegrationParameters();
00518 
00519         try { 
00520             Vector yUnitWeights, unitTolerances;
00521             mbs.realize(state, Stage::Position);
00522             mbs.calcYUnitWeights(state, yUnitWeights);
00523             mbs.calcYErrUnitTolerances(state, unitTolerances);
00524             Vector dummyErrest; //TODO
00525             mbs.project(state, consTol, yUnitWeights, unitTolerances,
00526                         dummyErrest);
00527         }
00528         catch (...) { ++statsProjectionFailures; return false; }
00529 
00530         try { mbs.realize(state, Stage::Acceleration); }
00531         catch (...) { ++statsRealizationFailures; return false; }
00532 
00533         initialized = true;
00534         return true;
00535     }
00536 
00537     bool step(const Real& tOut) {
00538         // Re-parametrizing or remodeling requires a new call to initialize().
00539         SimTK_STAGECHECK_GE_ALWAYS(state.getSystemStage(), Stage::Instance,
00540             "OLDExplicitEuler::step()");
00541 
00542         assert(initialized && tOut >= state.getTime());
00543         const Real tMax = std::min(tOut, stopTime);
00544 
00545         bool wasLastStep;
00546         long stepsTaken = 0;
00547         do {
00548             if (maxNumSteps && stepsTaken >= maxNumSteps)
00549                 return false; // too much work
00550 
00551             mbs.realize(state, Stage::Acceleration);
00552             t0    = state.getTime();
00553             ydot0 = state.getYDot(); // save so we can restart
00554 
00555             // Take the biggest step we can get up to maxStep
00556             Real hTry = std::min(maxStepSize, tMax-t0);
00557             for (;;) {
00558                 ++statsStepsAttempted;
00559                 wasLastStep = false;
00560                 Real nextT = t0+hTry;
00561                 if (tMax-nextT < minStepSize)
00562                     nextT = tMax, hTry = tMax-t0, wasLastStep=true;
00563 
00564                 state.updTime() = nextT;
00565                 state.updY()    = state.getY() + hTry*ydot0;
00566 
00567                 bool projectOK = false, realizeOK = false;
00568                 try {
00569                     projectOK = true;
00570                     Vector yUnitWeights, unitTolerances;
00571                     mbs.realize(state, Stage::Position);
00572                     mbs.calcYUnitWeights(state, yUnitWeights);
00573                     mbs.calcYErrUnitTolerances(state, unitTolerances);
00574                     bool needProjection = true;
00575                     if (!projectEveryStep) {
00576                         mbs.realize(state, Stage::Velocity); // so we can get UErrs
00577                         const Real err = 
00578                             mbs.calcWeightedRMSNorm(state.getYErr(), unitTolerances);
00579                         if (err < 0.9*consTol)
00580                             needProjection = false; // far from being violated
00581                     }
00582                     if (needProjection) {
00583                         Vector dummyErrest; //TODO
00584                         mbs.project(state, consTol, yUnitWeights, unitTolerances,
00585                                     dummyErrest);
00586                     }
00587                 }
00588                 catch (...) { projectOK=false; }
00589 
00590                 if (projectOK) {
00591                     try {
00592                         realizeOK = true;
00593                         mbs.realize(state, Stage::Acceleration);
00594                     } catch (...) { realizeOK = false; }
00595                     if (!realizeOK)  ++statsRealizationFailures;
00596                 } else ++statsProjectionFailures;
00597 
00598                 if (projectOK && realizeOK)
00599                     break; // took a step of size hTry.
00600 
00601                 // Failed
00602 
00603                 if (hTry <= minStepSize)
00604                     return false; // can't proceed
00605                 // Reduce step size and try again
00606                 hTry = std::max(minStepSize, hTry/2);
00607             }
00608 
00609             // Took a successful step of size hTry.
00610             ++stepsTaken;
00611             ++statsStepsTaken;
00612         } while (!wasLastStep);
00613 
00614         // We reached tMax successfully.
00615         return true;
00616     }
00617 private:
00618 
00619     
00620     void initializeIntegrationParameters() {
00621         mbs.realize(state, Stage::Instance);
00622 
00623         initializeStepSizes();
00624         initializeTolerances();
00625         if (userStopTime != -1.) stopTime = userStopTime;
00626         else stopTime = CNT<Real>::getInfinity();
00627         if (userMaxNumSteps != -1) 
00628             maxNumSteps = userMaxNumSteps; // 0 means infinity
00629         else maxNumSteps = 0;
00630         if (userProjectEveryStep != -1)
00631             projectEveryStep = (userProjectEveryStep==1);
00632     }
00633 
00634     void initializeTolerances() {
00635         accuracy     = (userAccuracy     != -1. ? userAccuracy     : 1e-3);
00636         relTol       = (userRelTol       != -1. ? userRelTol       : accuracy); 
00637         absTol       = (userAbsTol       != -1. ? userAbsTol       : accuracy); 
00638         consTol      = (userConsTol      != -1. ? userConsTol      : accuracy); 
00639         vconsRescale = (userVConsRescale != -1. ? userVConsRescale : 1.); 
00640     }
00641 
00642     void initializeStepSizes() {
00643         if (userMaxStepSize != -1.) { // got max
00644             maxStepSize = userMaxStepSize;
00645             if (userInitStepSize != -1.) { // got max & init
00646                 initStepSize = userInitStepSize;
00647                 if (userMinStepSize != -1.)
00648                     minStepSize = userMinStepSize; // got min,max,init
00649                 else minStepSize = std::min(maxStepSize/3, initStepSize);
00650             } else { // got max, not init
00651                 initStepSize = maxStepSize;
00652                 if (userMinStepSize != -1.)
00653                     minStepSize = userMinStepSize; // max & min, not init
00654                 else minStepSize = maxStepSize/3;
00655             }
00656         } else { // didn't get max
00657             if (userInitStepSize != -1.) { // got init, not max
00658                 initStepSize = userInitStepSize;
00659                 maxStepSize = initStepSize;
00660                 minStepSize = (userMinStepSize != -1. ? userMinStepSize : maxStepSize/3);
00661             } else { // didn't get init or max
00662                 if (userMinStepSize != -1.) { // got only min
00663                     minStepSize = userMinStepSize;
00664                     maxStepSize = std::max(mbs.calcTimescale(state)/10., minStepSize);
00665                     initStepSize = maxStepSize;
00666                 } else { // didn't get anything
00667                     maxStepSize = initStepSize = mbs.calcTimescale(state)/10.;
00668                     minStepSize = maxStepSize/3;
00669                 }
00670             }
00671         }
00672     }
00673 
00674     void reconstructForNewModel() {
00675         mbs.realize(state, Stage::Model);
00676         initStepSize=minStepSize=maxStepSize
00677             =accuracy=relTol=absTol=consTol=vconsRescale
00678             =stopTime=CNT<Real>::getNaN();
00679         maxNumSteps = -1;
00680         projectEveryStep = false;
00681         ydot0.resize(state.getY().size());
00682         initialized = false;
00683     }
00684 
00685     Real   initStepSize, minStepSize, maxStepSize;
00686     Real   accuracy, relTol, absTol, consTol, vconsRescale;
00687     Real   stopTime;
00688     long   maxNumSteps;
00689     bool   projectEveryStep;
00690     Real   t0;
00691     Vector ydot0;
00692 
00693     bool initialized;
00694 };
00695 
00696 
00697 class OLDRungeKuttaMerson : public MechanicalDAEIntegrator {
00698 public:
00699     OLDRungeKuttaMerson(const MultibodySystem& mb, State& s, bool noProject=false) 
00700       : MechanicalDAEIntegrator(mb,s) 
00701     {
00702         SimTK_STAGECHECK_GE_ALWAYS(state.getSystemStage(), Stage::Topology,
00703             "OLDRungeKuttaMerson::OLDRungeKuttaMerson()");
00704         reconstructForNewModel();
00705         if (noProject) suppressProject = true;
00706     }
00707 
00708     OLDRungeKuttaMerson* clone() const {return new OLDRungeKuttaMerson(*this);}
00709 
00710     Real getConstraintTolerance() const {
00711         assert(initialized);
00712         return consTol;
00713     }
00714     Real getPredictedNextStep() const {
00715         assert(initialized);
00716         return predictedNextStep;
00717     }
00718 
00719     bool initialize() {
00720         SimTK_STAGECHECK_GE_ALWAYS(state.getSystemStage(), Stage::Topology,
00721             "OLDRungeKuttaMerson::initialize()");
00722         if (state.getSystemStage() < Stage::Model)
00723             reconstructForNewModel();
00724 
00725         initializeIntegrationParameters();
00726 
00727         if (!evaluateAndProject(true))
00728             return false;
00729         
00730         initialized = true;
00731         return true;
00732     }
00733 
00735     // TODO: this is not preserving the old state properly. Can't just save continuous states!
00737     bool step(const Real& tOut) {
00738         // Re-parametrizing or remodeling requires a new call to initialize().
00739         SimTK_STAGECHECK_GE_ALWAYS(state.getSystemStage(), Stage::Instance,
00740             "OLDRungeKuttaMerson::step()");
00741 
00742         assert(initialized && tOut >= state.getTime());
00743         const Real tMax = std::min(tOut, stopTime);
00744 
00745         // Duck out now if there isn't enough time left to take a step.
00746         if (tMax-state.getTime() < minStepSize) {
00747             ++statsStepsAttempted;
00748             state.updTime() = tMax;
00749 
00750             if (!evaluateAndProject())
00751                 return false;
00752 
00753             ++statsStepsTaken;
00754             return true;
00755         }
00756 
00757         bool wasLastStep;
00758         long stepsTaken = 0;
00759 
00760         // hWanted tracks the step size we would like to have taken if the
00761         // end of the step interval had not intervened.
00762         Real hWanted = std::min(predictedNextStep, maxStepSize);
00763         do {
00764             if (maxNumSteps && stepsTaken >= maxNumSteps)
00765                 return false; // too much work
00766 
00767             // Keep track of whether we had to use an excessively small step size
00768             // in order to stop at tOut. In that case we don't want to use the
00769             // shrunken step to predict the next one.
00770             bool trialHWasLimitedByTOut = false;
00771 
00772             wasLastStep = false;
00773 
00774             t0 = state.getTime(); // save these so we can restart
00775             y0 = state.getY();
00776 
00777             // Near tStop we'll shrink or stretch as appropriate. If we shrink
00778             // substantially we'll make note of that and not attempt to 
00779             // grow the step size if we succeed with this tiny thing.
00780             Real trialH = hWanted;
00781             if (t0 + 1.05*trialH > tMax) {
00782                 const Real hAdj = tMax - t0;
00783                 trialHWasLimitedByTOut = hAdj < 0.9*trialH;
00784                 trialH = hAdj;
00785                 wasLastStep = true;
00786             }
00787 
00788             mbs.realize(state, Stage::Acceleration);
00789             ydot0 = state.getYDot();    // save for faster restart
00790 
00791             // Now we're going to attempt to take as big a step as we can
00792             // take (up to hNext). We'll keep shrinking trialH until something
00793             // works or we die.
00794             Real scalarError;
00795             bool RKstepOK;
00796             for (;;) {
00797                 ++statsStepsAttempted;
00798 
00799                 RKstepOK = 
00800                     takeAnRK4MStep(t0, y0, ydot0, trialH,
00801                                    ytmp[0], ytmp[1], ytmp[2], 
00802                                    ynew, errEst);
00803 
00804                 if (!RKstepOK) ++statsRealizationFailures;
00805 
00806                 if (RKstepOK) {
00807                     Vector wts;
00808                     mbs.calcYUnitWeights(state, wts);
00809                     scalarError = mbs.calcWeightedRMSNorm(errEst, wts) / accuracy;
00810                     //scalarError = mbs.calcYErrorNorm(state, errEst) / accuracy;
00811 
00812                     if (scalarError <= 1.) {
00813                         // Passed error test; see if we can evaluate at ynew.
00814                         state.updTime() = t0+trialH;
00815                         state.updY()    = ynew;
00816 
00817                         if (evaluateAndProject()) {
00818                             // Accept this step (we'll pick up projection changes here).
00819                             t0 = state.getTime(); y0 = state.getY();
00820 
00821                             // Adjust step size. Restrict growth to 5x, shrinking to 0.1x.
00822                             // We won't grow at all if the step we just executed was
00823                             // artificially shrunk due, e.g., to us being near tMax.
00824                             // We'll still allow shrinkage in that case, although that
00825                             // is unlikely to be indicated.
00826                             // This is a 4th order method so 1/4 is the correct exponent.
00827                             // Note the 0.9 safety factor applied here -- we always
00828                             // underpredict the step size by 10% to avoid thrashing.
00829 
00830                             Real optimalStepScale = 
00831                                 scalarError > 0 ? 0.9/pow(scalarError, 0.25) : 5.;
00832                             optimalStepScale = 
00833                                 std::min(std::max(optimalStepScale, 0.1), 5.);
00834 
00835                             // Inject a little stability here -- don't grow the step
00836                             // by less than 20% or shrink by less than 10%.
00837                             if (0.9 < optimalStepScale && optimalStepScale < 1.2)
00838                                 optimalStepScale = 1.;
00839 
00840                             // Change the value of hWanted unless we just succeeded with
00841                             // an unnaturally small end-of-interval step.
00842                             if (!(trialHWasLimitedByTOut && optimalStepScale >= 1)) {
00843                                 hWanted = optimalStepScale * trialH;
00844                                 hWanted = std::min(std::max(hWanted, minStepSize), maxStepSize);
00845                                 if (hWanted != trialH)
00846                                     ++statsStepSizeChanges;
00847                             }
00848                             predictedNextStep = hWanted;
00849                             break; // done with this step
00850                         }
00851                         ++statsRealizationFailures;
00852                         ++statsProjectionFailures;
00853                     } else 
00854                         ++statsErrorTestFailures;
00855                 }
00856 
00857                 // Step did not succeed.
00858                 if (trialH <= minStepSize )
00859                     return false;
00860 
00861                 Real optimalStepScale;
00862                 if (RKstepOK) { 
00863                     optimalStepScale = scalarError > 1. ? 0.9/pow(scalarError, 0.25) : 1.;
00864                     optimalStepScale = 
00865                         std::max(optimalStepScale, 0.1);
00866                 } else
00867                     optimalStepScale = 0.5;
00868 
00869                 hWanted = optimalStepScale * trialH;
00870                 hWanted = std::min(std::max(hWanted, minStepSize), maxStepSize);
00871                 if (hWanted != trialH)
00872                     ++statsStepSizeChanges;
00873 
00874                 trialH = hWanted;
00875                 trialHWasLimitedByTOut = false;
00876                 wasLastStep = false;
00877             }
00878             // Completed a step at h=trialH, hNext is set properly
00879             ++stepsTaken;
00880             ++statsStepsTaken;
00881         } while (!wasLastStep);
00882 
00883         return true;
00884     }
00885 
00886 private:
00887     bool f(const Real& t, const Vector& y, Vector& yd) const {
00888         state.updY() = y;
00889         state.updTime() = t;
00890 
00891         try { mbs.realize(state, Stage::Acceleration); }
00892         catch(...) { return false; }
00893         yd = state.getYDot();
00894         return true;
00895     }
00896 
00897     bool takeAnRK4MStep(const Real& t0, const Vector& y0, const Vector& yd0,
00898                         const Real& h, 
00899                         Vector& ytmp, Vector& yd1, Vector& yd2,
00900                         Vector& y, Vector& errEst)
00901     {
00902         ytmp = y0 + (h/3)*yd0;
00903         if (!f(t0+h/3, ytmp, yd1)) return false;
00904 
00905         ytmp = y0 + (h/6)*(yd0+yd1);
00906         if (!f(t0+h/3, ytmp, yd1)) return false;
00907 
00908         ytmp = y0 + (h/8)*(yd0 + 3.*yd1);
00909         if (!f(t0+h/2, ytmp, yd2)) return false;
00910 
00911         ytmp = y0 + (h/2)*(yd0 - 3.*yd1 + 4.*yd2);
00912         if (!f(t0+h,   ytmp, yd1)) return false;
00913 
00914         // final
00915         y = y0 + (h/6)*(yd0 + 4.*yd2 + yd1);
00916 
00917         for (int i=0; i<y.size(); ++i)
00918             errEst[i] = 0.2*std::fabs(y[i]-ytmp[i]);
00919 
00920         return true;
00921     }
00922 
00923     // Realize through Position stage, project, then realize
00924     // through Acceleration stage.
00925     bool evaluateAndProject(bool forceProject=false) {
00926         bool projectOK = false, realizeOK = false;
00927         try {
00928             projectOK = true;
00929             Vector yUnitWeights, unitTolerances;
00930             mbs.realize(state, Stage::Position);
00931             mbs.calcYUnitWeights(state, yUnitWeights);
00932             mbs.calcYErrUnitTolerances(state, unitTolerances);
00933             bool needProjection = true;
00934             if (!projectEveryStep) {
00935                 mbs.realize(state, Stage::Velocity); // so we can get UErrs
00936                 const Real err = 
00937                     mbs.calcWeightedRMSNorm(state.getYErr(), unitTolerances);
00938                 if (err < 0.9*consTol)
00939                     needProjection = false; // far from being violated
00940             }
00941             if ((needProjection && !suppressProject) || forceProject) {
00942                 Vector dummyErrest; //TODO
00943                 mbs.project(state, consTol, yUnitWeights, unitTolerances,
00944                             dummyErrest);
00945             }
00946         }
00947         catch (...) { projectOK=false; }
00948 
00949         if (projectOK) {
00950             try {
00951                 realizeOK = true;
00952                 mbs.realize(state, Stage::Acceleration);
00953             } catch (...) { realizeOK = false; }
00954             if (!realizeOK)  ++statsRealizationFailures;
00955         } else ++statsProjectionFailures;
00956 
00957         return projectOK && realizeOK;
00958     }
00959 
00960 private:    
00961     void initializeIntegrationParameters() {
00962         mbs.realize(state, Stage::Instance);
00963 
00964         initializeStepSizes();
00965         predictedNextStep = initStepSize;
00966         initializeTolerances();        
00967         if (userStopTime != -1.) stopTime = userStopTime;
00968         else stopTime = CNT<Real>::getInfinity();
00969         if (userMaxNumSteps != -1) 
00970             maxNumSteps = userMaxNumSteps; // 0 means infinity
00971         else maxNumSteps = 0;
00972         if (userProjectEveryStep != -1)
00973             projectEveryStep = (userProjectEveryStep==1);
00974     }
00975 
00976     void initializeTolerances() {
00977         accuracy     = (userAccuracy     != -1. ? userAccuracy     : 1e-3);
00978         relTol       = (userRelTol       != -1. ? userRelTol       : accuracy); 
00979         absTol       = (userAbsTol       != -1. ? userAbsTol       : accuracy); 
00980         consTol      = (userConsTol      != -1. ? userConsTol      : accuracy); 
00981         vconsRescale = (userVConsRescale != -1. ? userVConsRescale : 1.); 
00982     }
00983 
00984     void initializeStepSizes() {
00985         const Real MinStep = std::pow(Eps, Real(0.75)); // e.g., 1e-12 in double
00986         if (userMaxStepSize != -1.) { // got max
00987             maxStepSize = userMaxStepSize;
00988             if (userInitStepSize != -1.) { // got max & init
00989                 initStepSize = userInitStepSize;
00990                 if (userMinStepSize != -1.)
00991                     minStepSize = userMinStepSize; // got min,max,init
00992                 else minStepSize = std::min(MinStep, initStepSize);
00993             } else { // got max, not init
00994                 if (userMinStepSize != -1.) {
00995                     minStepSize = userMinStepSize; // max & min, not init
00996                     initStepSize = std::max(minStepSize, mbs.calcTimescale(state)/10.);
00997                 } else {
00998                     // got max only
00999                     initStepSize = std::min(mbs.calcTimescale(state)/10., maxStepSize);
01000                     minStepSize  = std::min(MinStep, initStepSize);
01001                 }
01002             }
01003         } else { // didn't get max
01004             maxStepSize = CNT<Real>::getInfinity();
01005             if (userInitStepSize != -1.) { // got init, not max
01006                 initStepSize = userInitStepSize;
01007                 if (userMinStepSize != -1.) minStepSize = userMinStepSize;
01008                 else minStepSize = std::min(MinStep, initStepSize);
01009             } else { // didn't get init or max
01010                 if (userMinStepSize != -1.) { // got only min
01011                     minStepSize = userMinStepSize;
01012                     initStepSize = std::max(mbs.calcTimescale(state)/10., minStepSize);
01013                 } else { // didn't get anything
01014                     initStepSize = mbs.calcTimescale(state)/10.;
01015                     minStepSize  = std::min(MinStep, initStepSize);
01016                 }
01017             }
01018         }
01019     }
01020 
01021     static const int NTemps = 4;
01022 
01023     void reconstructForNewModel() {
01024         mbs.realize(state, Stage::Model);
01025         initStepSize=minStepSize=maxStepSize
01026             =accuracy=relTol=absTol=consTol=vconsRescale
01027             =stopTime=predictedNextStep=CNT<Real>::getNaN();
01028         maxNumSteps = -1;
01029         projectEveryStep = false;
01030         suppressProject = false;
01031         y0.resize(state.getY().size());
01032         ydot0.resize(state.getY().size());
01033         errEst.resize(state.getY().size());
01034         ynew.resize(state.getY().size());
01035         for (int i=0; i<NTemps; ++i)
01036             ytmp[i].resize(state.getY().size());
01037         initialized = false;
01038     }
01039 
01040     Real   initStepSize, minStepSize, maxStepSize;
01041     Real   accuracy, relTol, absTol, consTol, vconsRescale;
01042     Real   stopTime;
01043     long   maxNumSteps;
01044     bool   projectEveryStep;
01045     bool   suppressProject;
01046     Real   t0;
01047     Vector y0, ydot0, errEst, ynew;
01048     Vector ytmp[NTemps];
01049     Real predictedNextStep;
01050 
01051     bool initialized;
01052 };
01053 
01054 } // namespace SimTK
01055 
01056 #endif // SimTK_SIMBODY_NUMERICAL_METHODS_H_

Generated on Fri Sep 26 07:44:15 2008 for SimTKcore by  doxygen 1.5.6