-
Notifications
You must be signed in to change notification settings - Fork 1.2k
/
radau_integrator.h
326 lines (276 loc) · 13.9 KB
/
radau_integrator.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
#pragma once
#include <memory>
#include <vector>
#include "drake/common/autodiff.h"
#include "drake/common/drake_copyable.h"
#include "drake/systems/analysis/bogacki_shampine3_integrator.h"
#include "drake/systems/analysis/implicit_integrator.h"
#include "drake/systems/analysis/runge_kutta2_integrator.h"
namespace drake {
namespace systems {
/**
* A selectable order (third- or first-order), fully implicit integrator with
* error estimation.
*
* @tparam_nonsymbolic_scalar
* @tparam num_stages The number of stages used in this integrator, which must
* be either 1 or 2. Set this to 1 for the integrator to be implicit
* Euler and 2 for it to Radau3 (default).
*
* A two-stage Radau IIa (see [Hairer, 1996], Ch. 5) method is used for
* propagating the state forward, by default. The state can also be propagated
* using a single-stage method, in which case it is equivalent to an implicit
* Euler method, by setting num_stages=1. Regardless of the order of propagating
* state, the local (truncation) error is estimated through the implicit
* trapezoid rule.
*
* Radau IIa methods are known to be L-Stable, meaning both that
* applying it at a fixed integration step to the "test" equation `y(t) = eᵏᵗ`
* yields zero (for `k < 0` and `t → ∞`) *and* that it is also A-Stable.
* A-Stability, in turn, means that the method can integrate the linear constant
* coefficient system `dx/dt = Ax` at any step size without the solution
* becoming unstable (growing without bound). The practical effect of
* L-Stability is that the integrator tends to be stable for any given step size
* on an arbitrary system of ordinary differential equations. Note that the
* implicit trapezoid rule used for error estimation is "only" A-Stable; whether
* this lesser stability has some practical effect on the efficiency of this
* integrator is currently unknown. See [Lambert, 1991], Ch. 6 for an
* approachable discussion on stiff differential equations and L- and
* A-Stability.
*
* This implementation uses Newton-Raphson (NR). General implementation
* details were taken from [Hairer, 1996] Ch. 8.
*
* - [Hairer, 1996] E. Hairer and G. Wanner. Solving Ordinary Differential
* Equations II (Stiff and Differential-Algebraic Problems).
* Springer, 1996.
* - [Lambert, 1991] J. D. Lambert. Numerical Methods for Ordinary Differential
* Equations. John Wiley & Sons, 1991.
*
* @see ImplicitIntegrator class documentation for information about implicit
* integration methods in general.
* @see Radau3Integrator and Radau1Integrator alises for third- and first-order
* templates with num_stages already specified.
* @note This integrator uses the integrator accuracy setting, even when run
* in fixed-step mode, to limit the error in the underlying Newton-Raphson
* process. See IntegratorBase::set_target_accuracy() for more info.
* @ingroup integrators
*/
template <typename T, int num_stages = 2>
class RadauIntegrator final : public ImplicitIntegrator<T> {
static_assert(num_stages == 1 || num_stages == 2,
"Only 1-stage and 2-stage Radau are supported.");
public:
DRAKE_NO_COPY_NO_MOVE_NO_ASSIGN(RadauIntegrator)
explicit RadauIntegrator(const System<T>& system,
Context<T>* context = nullptr);
~RadauIntegrator() final = default;
bool supports_error_estimation() const final { return true; }
/// This integrator uses embedded second order methods to compute estimates of
/// the local truncation error. The order of the asymptotic difference between
/// the third-order Radau method and an embedded second order method is O(h³).
/// The order of the asymptotic difference between the first-order Radau
/// method and an embedded second order method is O(h²).
int get_error_estimate_order() const final {
if (num_stages == 2) {
return 3;
} else {
DRAKE_DEMAND(num_stages == 1);
return 2;
}
}
private:
int64_t do_get_num_newton_raphson_iterations() const final {
return num_nr_iterations_;
}
int64_t do_get_num_error_estimator_derivative_evaluations() const final {
return num_err_est_function_evaluations_;
}
int64_t do_get_num_error_estimator_derivative_evaluations_for_jacobian()
const final {
return num_err_est_jacobian_function_evaluations_;
}
int64_t do_get_num_error_estimator_newton_raphson_iterations()
const final {
return num_err_est_nr_iterations_;
}
int64_t do_get_num_error_estimator_jacobian_evaluations() const final {
return num_err_est_jacobian_reforms_;
}
int64_t do_get_num_error_estimator_iteration_matrix_factorizations()
const final {
return num_err_est_iter_factorizations_;
}
// Computes the solution xtplus (i.e., the continuous state at t0 + h) from
// the continuous state at t0 (xt0) and the current Newton-Raphson
// iterate (Z).
void ComputeSolutionFromIterate(
const VectorX<T>& xt0, const VectorX<T>& Z, VectorX<T>* xtplus) const;
// Updates the error estimate from the propagated solution and the embedded
// solution.
void ComputeAndSetErrorEstimate(
const VectorX<T>& xtplus_prop, const VectorX<T>& xtplus_embed);
// Steps Radau forward by h, if possible.
// @param t0 the initial time.
// @param h the integration step size to attempt.
// @param xt0 the continuous state at time t0.
// @param[out] xtplus_radau contains the Radau integrator solution on return.
// @param[out] xtplus_itr contains the implicit trapezoid solution on return.
// @returns `true` if the integration was successful at the requested step
// size.
// @pre The time and state in the system's context (stored by the integrator)
// are set to (t0, xt0) on entry.
// @post The time and state of the system's context (stored by the integrator)
// will be set to t0+h and `xtplus_radau` on successful exit (indicated
// by this function returning `true`) and will be indeterminate on
// unsuccessful exit (indicated by this function returning `false`).
bool AttemptStepPaired(const T& t0, const T& h, const VectorX<T>& xt0,
VectorX<T>* xtplus_radau, VectorX<T>* xtplus_itr);
// Computes F(Z) used in [Hairer, 1996], (IV.8.4). This method evaluates
// the time derivatives of the system given the current iterate Z.
// @param t0 the initial time.
// @param h the integration step size to attempt.
// @param xt0 the continuous state at time t0.
// @param Z the current iterate, of dimension state_dim * num_stages.
// @post the state of the internal context will be set to (t0, xt0) on return.
// @return a (state_dim * num_stages)-dimensional vector.
const VectorX<T>& ComputeFofZ(
const T& t0, const T& h, const VectorX<T>& xt0, const VectorX<T>& Z);
void DoInitialize() final;
void DoResetCachedJacobianRelatedMatrices() final {
iteration_matrix_radau_ = {};
iteration_matrix_implicit_trapezoid_ = {};
}
void DoResetImplicitIntegratorStatistics() final;
// Takes a given step of the requested size, if possible.
// @param h the integration step size to attempt.
// @returns `true` if successful.
// @post the time and continuous state will be advanced only if `true` is
// returned (if `false` is returned, the time and state will be reset
// to their values on entry).
bool DoImplicitIntegratorStep(const T& h) final;
// Computes the next continuous state (at t0 + h) using the Radau method,
// assuming that the method is able to converge at that step size.
// @param t0 the initial time.
// @param h the integration step size to attempt.
// @param xt0 the continuous state at time t0.
// @param[out] xtplus the value for x(t+h) on return.
// @param trial the attempt for this approach (1-4). StepRadau() uses more
// computationally expensive methods as the trial numbers increase.
// @post the internal context will be in an indeterminate state on returning
// `false`.
// @returns `true` if the method was successfully able to take an integration
// step of size `h`.
bool StepRadau(const T& t0, const T& h, const VectorX<T>& xt0,
VectorX<T>* xtplus, int trial = 1);
// Computes the next continuous state (at t0 + h) using the implicit trapezoid
// method, assuming that the method is able to converge at that step size.
// @param t0 the initial time.
// @param h the integration step size to attempt.
// @param xt0 the continuous state at time t0.
// @param dx0 the time derivatives computed at time and state (t0, xt0).
// @param xtplus_radau the Radau solution for x(t+h).
// @param[out] xtplus the value for x(t+h) on return.
// @returns `true` if the method was successfully able to take an integration
// step of size `h`.
bool StepImplicitTrapezoid(const T& t0, const T& h, const VectorX<T>& xt0,
const VectorX<T>& dx0, const VectorX<T>& xtplus_radau,
VectorX<T>* xtplus);
// Computes the tensor product between two matrices. Given
// A = | a11 ... a1m |
// | ... ... |
// | an1 ... anm |
// and some matrix B, the tensor product is:
// A ⊗ B = | a11B ... a1mB |
// | ... ... |
// | an1B ... anmB |
static MatrixX<T> CalcTensorProduct(const MatrixX<T>& A, const MatrixX<T>& B);
static void ComputeImplicitTrapezoidIterationMatrix(const MatrixX<T>& J,
const T& h,
typename ImplicitIntegrator<T>::IterationMatrix* iteration_matrix);
// Function for computing the iteration matrix for the Radau method. This
// is the matrix in [Hairer, 1996] (IV.8.4) on p.119.
static void ComputeRadauIterationMatrix(const MatrixX<T>& J, const T& h,
const MatrixX<double>& A,
typename ImplicitIntegrator<T>::IterationMatrix* iteration_matrix);
// Does all of the real work for the implicit trapezoid method.
bool StepImplicitTrapezoidDetail(const T& t0, const T& h,
const VectorX<T>& xt0, const std::function<VectorX<T>()>& g,
const VectorX<T>& xtplus_radau, VectorX<T>* xtplus, int trial = 1);
// The num_stages-dimensional (constant) vector of time-scaling coefficients
// common to Runge-Kutta-type integrators.
std::vector<double> c_;
// The num_stages x num_stages-dimensional (constant) matrix of stage-scaling
// coefficients that are standard with Runge-Kutta-type integrators.
MatrixX<double> A_;
// The iteration matrix for the Radau method.
typename ImplicitIntegrator<T>::IterationMatrix iteration_matrix_radau_;
// The iteration matrix for the implicit trapezoid method.
typename ImplicitIntegrator<T>::IterationMatrix
iteration_matrix_implicit_trapezoid_;
// The (constant) tensor product between A_ and an identity matrix. This
// product is computed only at initialization.
MatrixX<T> A_tp_eye_;
// The num_stages-dimensional (constant) solution propagation coefficients
// (that also scales the stages) common to Runge-Kutta-type integrators.
std::vector<double> b_;
// The num_stages-dimensional (constant) scaling coefficients for Z (IV.8.2b)
// in [Hairer, 1996].
std::vector<double> d_;
// A num_stages * |xc|-dimensional vector of the current iterate for the
// Newton-Raphson process.
VectorX<T> Z_;
// The num_stages dimensional vector of derivative evaluations at every stage.
VectorX<T> F_of_Z_;
// Vector used in error estimate calculations.
VectorX<T> err_est_vec_;
// The continuous state update vector used during Newton-Raphson.
std::unique_ptr<ContinuousState<T>> dx_state_;
// Continuous state at the beginning of an integration step (stored to avoid
// heap allocations).
VectorX<T> xt0_;
// Time-derivative of continuous state (stored to avoid heap allocations).
VectorX<T> xdot_;
// "Propagated" solution computed by the integrator (stored to avoid heap
// allocations)- this is the solution that will be propagated forward in time.
VectorX<T> xtplus_prop_;
// "Error estimate" solution computed by the embedded method (stored to avoid
// allocations)- this is the solution that will be used in concert with the
// propagated solution to compute the error estimate.
VectorX<T> xtplus_embed_;
// 3/2 Bogacki-Shampine integrator used for propagation and error estimation
// when the step size becomes smaller than the working minimum step size.
std::unique_ptr<BogackiShampine3Integrator<T>> bs3_;
// Second order Runge-Kutta integrator used for error estimation when the
// step size becomes smaller than the working minimum step size.
std::unique_ptr<RungeKutta2Integrator<T>> rk2_;
// Statistics specific to this integrator.
int64_t num_nr_iterations_{0};
// Implicit trapezoid specific statistics.
int64_t num_err_est_jacobian_reforms_{0};
int64_t num_err_est_jacobian_function_evaluations_{0};
int64_t num_err_est_iter_factorizations_{0};
int64_t num_err_est_function_evaluations_{0};
int64_t num_err_est_nr_iterations_{0};
};
/** A third-order fully implicit integrator with error estimation.
See RadauIntegrator with `num_stages == 2` for details.
@tparam_nonsymbolic_scalar */
template <typename T>
using Radau3Integrator = RadauIntegrator<T, 2>;
/** A first-order fully implicit integrator with error estimation.
See RadauIntegrator with `num_stages == 1` for details.
@tparam_nonsymbolic_scalar */
template <typename T>
using Radau1Integrator = RadauIntegrator<T, 1>;
} // namespace systems
} // namespace drake
// Declare class template initializations for double and AutoDiffXd.
// Note: We don't use the macros in drake/common/default_scalars.h because
// those macros are designed for functions with only one template argument, and
// we need to instantiate both scalar types for both the Radau1 and Radau3
// integrators, which have num_stages set 1 and 2, respectively.
extern template class drake::systems::RadauIntegrator<double, 1>;
extern template class drake::systems::RadauIntegrator<drake::AutoDiffXd, 1>;
extern template class drake::systems::RadauIntegrator<double, 2>;
extern template class drake::systems::RadauIntegrator<drake::AutoDiffXd, 2>;