1 /* -------------------------------------------------------------------------- *
2  *                        Simbody(tm): SimTKmath                              *
3  * -------------------------------------------------------------------------- *
4  * This is part of the SimTK biosimulation toolkit originating from           *
5  * Simbios, the NIH National Center for Physics-Based Simulation of           *
6  * Biological Structures at Stanford, funded under the NIH Roadmap for        *
7  * Medical Research, grant U54 GM072970. See https://simtk.org/home/simbody.  *
8  *                                                                            *
9  * Portions copyright (c) 2013 Stanford University and the Authors.           *
10  * Authors: Michael Sherman                                                   *
11  * Contributors:                                                              *
12  *                                                                            *
13  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
14  * not use this file except in compliance with the License. You may obtain a  *
15  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
16  *                                                                            *
17  * Unless required by applicable law or agreed to in writing, software        *
18  * distributed under the License is distributed on an "AS IS" BASIS,          *
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
20  * See the License for the specific language governing permissions and        *
21  * limitations under the License.                                             *
22  * -------------------------------------------------------------------------- */
23 
24 #include "simmath/SemiExplicitEuler2Integrator.h"
25 
26 #include "IntegratorRep.h"
27 #include "SemiExplicitEuler2IntegratorRep.h"
28 
29 using namespace SimTK;
30 
31 //==============================================================================
32 //                   SEMI EXPLICIT EULER 2 INTEGRATOR
33 //==============================================================================
34 
SemiExplicitEuler2Integrator(const System & sys)35 SemiExplicitEuler2Integrator::SemiExplicitEuler2Integrator
36    (const System& sys) {
37     rep = new SemiExplicitEuler2IntegratorRep(this, sys);
38 }
39 
40 
41 //==============================================================================
42 //                   SEMI EXPLICIT EULER 2 INTEGRATOR REP
43 //==============================================================================
SemiExplicitEuler2IntegratorRep(Integrator * handle,const System & sys)44 SemiExplicitEuler2IntegratorRep::SemiExplicitEuler2IntegratorRep
45    (Integrator* handle, const System& sys)
46 :   AbstractIntegratorRep(handle, sys, 1, 1, "SemiExplicitEuler2",  true)
47 {
48 }
49 
50 //==============================================================================
51 //                         CREATE INTERPOLATED STATE
52 //==============================================================================
53 // Create an interpolated state at time t, which is between tPrev and tCurrent.
54 // If we haven't yet delivered an interpolated state in this interval, we have
55 // to initialize its discrete part from the advanced state.
56 //
57 // TODO: Note that this is a first-order interpolation across the *whole* step,
58 // even though this integrator takes two smaller first-order substeps. It would
59 // be better to record the midstep values and perform a piecewise-linear
60 // interpolation across the half steps so that the interpolated values match
61 // the underlying steps. Alternately, the midpoint value could be used to
62 // perform a second-order interpolation here; I'm not sure whether that would
63 // be better.
createInterpolatedState(Real t)64 void SemiExplicitEuler2IntegratorRep::createInterpolatedState(Real t) {
65     const System& system   = getSystem();
66     const State&  advanced = getAdvancedState();
67     State&        interp   = updInterpolatedState();
68     interp = advanced; // pick up discrete stuff.
69     const Real weight1 = (getAdvancedTime()-t) /
70                          (getAdvancedTime()-getPreviousTime());
71     const Real weight2 = 1-weight1;
72     interp.updY() = weight1*getPreviousY()+weight2*getAdvancedState().getY();
73     interp.updTime() = t;
74 
75     if (userProjectInterpolatedStates == 0) {
76         system.realize(interp, Stage::Time);
77         system.prescribeQ(interp);
78         system.realize(interp, Stage::Position);
79         system.prescribeU(interp);
80         system.realize(interp, Stage::Velocity);
81         return;
82     }
83 
84     // We may need to project onto constraint manifold. Allow project()
85     // to throw an exception if it fails since there is no way to recover here.
86     realizeAndProjectKinematicsWithThrow(interp, ProjectOptions::LocalOnly);
87 }
88 
89 
90 //==============================================================================
91 //                  BACK UP ADVANCED STATE BY INTERPOLATION
92 //==============================================================================
93 // Interpolate the advanced state back to an earlier part of the interval,
94 // forgetting about the rest of the interval. This is necessary, for
95 // example after we have localized an event trigger to an interval tLow:tHigh
96 // where tHigh < tAdvanced.
97 //
98 // See comment above re this being a first-order *whole step* interpolation.
99 void SemiExplicitEuler2IntegratorRep::
backUpAdvancedStateByInterpolation(Real t)100 backUpAdvancedStateByInterpolation(Real t) {
101     const System& system   = getSystem();
102     State& advanced = updAdvancedState();
103     const Real t0 = getPreviousTime(), t1 = advanced.getTime();
104     const Vector& y0 = getPreviousY();
105     const Vector& y1 = advanced.getY();
106 
107     assert(t0 <= t && t <= t1);
108     assert(t1 > t0);
109 
110     const Real weight0 = (t1-t) / (t1-t0);
111     const Real weight1 = 1-weight0;
112     advanced.updY() = weight0*y0 + weight1*y1;
113     advanced.updTime() = t;
114 
115     // Ignore any user request not to project interpolated states here -- this
116     // is the actual advanced state which will be propagated through the
117     // rest of the trajectory so we can't allow it not to satisfy the
118     // constraints!
119     // But it is OK if it just *barely* satisfies the constraints so we
120     // won't get carried away if the user isn't being finicky about it.
121 
122     // Project position constraints if warranted. Allow project()
123     // to throw an exception if it fails since there is no way to recover here.
124 
125     realizeAndProjectKinematicsWithThrow(advanced, ProjectOptions::LocalOnly);
126 }
127 
128 
129 
130 //==============================================================================
131 //                            ATTEMPT DAE STEP
132 //==============================================================================
133 // Note that SemiExplicitEuler overrides the entire DAE step because it can't
134 // use the default ODE-then-DAE structure. Instead the constraint projections
135 // are interwoven here.
attemptDAEStep(Real t1,Vector & yErrEst,int & errOrder,int & numIterations)136 bool SemiExplicitEuler2IntegratorRep::attemptDAEStep
137    (Real t1, Vector& yErrEst, int& errOrder, int& numIterations)
138 {
139     const System& system   = getSystem();
140     State& advanced = updAdvancedState();
141     Vector dummyErrEst; // for when we don't want the error estimate projected
142 
143     const int nq = advanced.getNQ();
144     const int nu = advanced.getNU();
145     const int nz = advanced.getNZ();
146 
147     // We'll need to work with these variable separately.
148     VectorView qErrEst  = yErrEst(    0, nq);
149     VectorView uErrEst  = yErrEst(   nq, nu);
150     VectorView zErrEst  = yErrEst(nq+nu, nz);
151 
152     statsStepsAttempted++;
153     errOrder = 2;
154 
155     const Real    t0        = getPreviousTime();       // nicer names
156     const Vector& q0        = getPreviousQ();
157     const Vector& u0        = getPreviousU();
158     const Vector& z0        = getPreviousZ();
159     const Vector& qdot0     = getPreviousQDot();
160     const Vector& udot0     = getPreviousUDot();
161     const Vector& zdot0     = getPreviousZDot();
162     const Vector& qdotdot0  = getPreviousQDotDot();
163 
164     const Real h = t1-t0, hHalf = h/2, tHalf = t0 + hHalf;
165 
166     // -------------------------------------------------------------------------
167     // First calculate the big step, borrowing advanced for the calculations.
168     advanced.updZ() = m_zBig = getPreviousZ() + h * getPreviousZDot();
169     advanced.updU() = getPreviousU() + h * getPreviousUDot();
170 
171     // Note that changing time does not invalidate position kinematics.
172     advanced.updTime() = t1;
173     system.realize(advanced, Stage::Position); // old q, new t=t1
174     system.prescribeU(advanced);
175 
176     // Update qdotBig = N(q_t0)*u_t1 from now-advanced u.
177     system.multiplyByN(advanced, advanced.getU(), m_qdotTmp);
178     advanced.updQ() = getPreviousQ() + h * m_qdotTmp;
179     system.prescribeQ(advanced); // at t1
180     m_qBig = advanced.getQ();
181     system.realize(advanced, Stage::Position); // new q, new t=t1
182     system.prescribeU(advanced); // update prescribed u in case q-dependent
183     m_uBig = advanced.getU();
184     // -------------------------------------------------------------------------
185 
186     // At this point yBig=(qBig,uBig,zBig) has been calculated.
187 
188     // -------------------------------------------------------------------------
189     // Now take two half steps, working directly in advanced.
190     advanced.updZ() = getPreviousZ() + hHalf * getPreviousZDot();
191     advanced.updU() = getPreviousU() + hHalf * getPreviousUDot();
192     advanced.updQ() = getPreviousQ(); // back to old q
193     advanced.updTime() = tHalf;
194     system.realize(advanced, Stage::Position); // old q, new t=tHalf
195     system.prescribeU(advanced);
196 
197     // Update qdot_tHalf = N(q_t0)*u_tHalf from now-advanced u.
198     system.multiplyByN(advanced, advanced.getU(), m_qdotTmp);
199     advanced.updQ() += hHalf * m_qdotTmp;
200     system.prescribeQ(advanced);
201     system.realize(advanced, Stage::Position); // new q, new t
202     system.prescribeU(advanced); // update prescribed u if q-dependent
203     system.realize(advanced, Stage::Velocity);
204 
205     realizeStateDerivatives(advanced); // get udot,zdot at tHalf
206     // Get these references now -- as soon as we change u or z they
207     // will be invalid.
208     const Vector& zdotHalf = advanced.getZDot();
209     const Vector& udotHalf = advanced.getUDot();
210 
211     // Second half-step.
212     advanced.updZ() += hHalf * zdotHalf;
213     advanced.updU() += hHalf * udotHalf;
214 
215     advanced.updTime() = t1; // position kinematics unchanged
216     system.realize(advanced, Stage::Position); // old q=qHalf, new t=t1
217     system.prescribeU(advanced);
218 
219     // Update qdot_t1 = N(q_tHalf)*u_t1 from now-advanced u.
220     system.multiplyByN(advanced, advanced.getU(), m_qdotTmp);
221     advanced.updQ() += hHalf * m_qdotTmp;
222     system.prescribeQ(advanced);
223     system.realize(advanced, Stage::Position); // new q=q1, new t=t1
224     system.prescribeU(advanced); // update prescribed u in case q-dependent
225     // -------------------------------------------------------------------------
226     // Now estimate the error and use local extrapolation to improve the
227     // final solution.
228     qErrEst = advanced.getQ() - m_qBig;
229     uErrEst = advanced.getU() - m_uBig;
230     zErrEst = advanced.getZ() - m_zBig;
231 
232     // Local extrapolation. CAUSES STABILITY PROBLEMS! Don't do it!
233     //advanced.updZ() += zErrEst; // Solution is now second-order.
234     //advanced.updU() += uErrEst;
235     //advanced.updQ() += qErrEst;
236     //system.realize(advanced, Stage::Position);
237     //system.prescribeU(advanced); // update prescribed u in case q-dependent
238 
239     // Consider position constraint projection. Note that we have to do this
240     // projection prior to calculating prescribed u's since the prescription
241     // can depend on q's. Prevent project() from throwing an exception since
242     // failure here may be recoverable.
243     bool anyChanges;
244     if (!localProjectQAndQErrEstNoThrow(advanced, dummyErrEst, anyChanges))
245         return false; // convergence failure for this step
246 
247     // q's satisfy the position constraint manifold. Now work on u's.
248 
249     system.prescribeU(advanced);
250     system.realize(advanced, Stage::Velocity);
251 
252     // Now try velocity constraint projection. Nothing will happen if
253     // velocity constraints are already satisfied unless user has set the
254     // ForceProjection option.
255 
256     if (!localProjectUAndUErrEstNoThrow(advanced, dummyErrEst, anyChanges))
257         return false; // convergence failure for this step
258 
259     numIterations = 1;
260     return true;
261 }
262 
263