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