@@ -1082,6 +1082,57 @@ class IntegratorBase {
1082
1082
return true ;
1083
1083
}
1084
1084
1085
+ /* *
1086
+ Stepping function for integrators operating outside of Simulator that
1087
+ advances the continuous state of the `context` *using a single step* to
1088
+ `t_target`. This method is designed for integrator users that do not wish to
1089
+ consider publishing or discontinuous, mid-interval updates. One such
1090
+ example application is that of direct transcription for trajectory
1091
+ optimization, for which the integration process should be _consistent_: it
1092
+ should execute the same sequence of arithmetic operations for all values
1093
+ of the nonlinear programming variables. In keeping with the naming
1094
+ semantics of this function, error controlled integration is not supported
1095
+ (though error estimates will be computed for integrators that support that
1096
+ feature), which is a minimal requirement for "consistency".
1097
+ @param context The context to perform integration on.
1098
+ @param t_target The current or future time to integrate to.
1099
+ @throws std::exception If the integrator has not been initialized or
1100
+ `t_target` is in the past or context is nullptr.
1101
+ @returns `true` if the integrator was able to take a single fixed step to
1102
+ `t_target`.
1103
+ @warning Not thread safe. The integrator holds mutable member variables to
1104
+ avoid allocation inside the integration steps.
1105
+ */
1106
+ [[nodiscard]] bool IntegrateWithSingleFixedStepToTime (
1107
+ Context<T>* context, const T& t_target) const {
1108
+ DRAKE_THROW_UNLESS (context != nullptr );
1109
+ using std::abs ;
1110
+ using std::max;
1111
+
1112
+ const T h = t_target - context->get_time ();
1113
+ if (scalar_predicate<T>::is_bool && h < 0 ) {
1114
+ throw std::logic_error (
1115
+ " IntegrateWithSingleFixedStepToTime() called with "
1116
+ " a negative step size." );
1117
+ }
1118
+
1119
+ if (!DoStepConst (context, h)) return false ;
1120
+
1121
+ if constexpr (scalar_predicate<T>::is_bool) {
1122
+ // Correct any round-off error that has occurred. Formula below requires
1123
+ // that time be non-negative.
1124
+ DRAKE_DEMAND (context->get_time () >= 0 );
1125
+ const double tol =
1126
+ 10 * std::numeric_limits<double >::epsilon () *
1127
+ ExtractDoubleOrThrow (max (1.0 , max (t_target, context_->get_time ())));
1128
+ DRAKE_DEMAND (abs (context->get_time () - t_target) < tol);
1129
+ }
1130
+
1131
+ context->SetTime (t_target);
1132
+
1133
+ return true ;
1134
+ }
1135
+
1085
1136
/* *
1086
1137
@name Integrator statistics methods
1087
1138
@{
@@ -1318,7 +1369,8 @@ class IntegratorBase {
1318
1369
Subclasses should call this function rather than calling
1319
1370
system.EvalTimeDerivatives() directly.
1320
1371
*/
1321
- const ContinuousState<T>& EvalTimeDerivatives (const Context<T>& context) {
1372
+ const ContinuousState<T>& EvalTimeDerivatives (
1373
+ const Context<T>& context) const {
1322
1374
return EvalTimeDerivatives (get_system (), context); // See below.
1323
1375
}
1324
1376
@@ -1330,8 +1382,8 @@ class IntegratorBase {
1330
1382
function evaluations.
1331
1383
*/
1332
1384
template <typename U>
1333
- const ContinuousState<U>& EvalTimeDerivatives (const System<U>& system,
1334
- const Context<U>& context) {
1385
+ const ContinuousState<U>& EvalTimeDerivatives (
1386
+ const System<U>& system, const Context<U>& context) const {
1335
1387
const CacheEntry& entry = system .get_time_derivatives_cache_entry ();
1336
1388
const CacheEntryValue& value = entry.get_cache_entry_value (context);
1337
1389
const int64_t serial_number_before = value.serial_number ();
@@ -1447,7 +1499,6 @@ class IntegratorBase {
1447
1499
method is called during the integration process (via
1448
1500
StepOnceErrorControlledAtMost(), IntegrateNoFurtherThanTime(), and
1449
1501
IntegrateWithSingleFixedStepToTime()).
1450
- @param h The integration step to take.
1451
1502
@returns `true` if successful, `false` if the integrator was unable to take
1452
1503
a single step of size @p h (due to, e.g., an integrator
1453
1504
convergence failure).
@@ -1462,6 +1513,32 @@ class IntegratorBase {
1462
1513
*/
1463
1514
virtual bool DoStep (const T& h) = 0;
1464
1515
1516
+ /* *
1517
+ Derived classes must implement this method to (1) integrate the continuous
1518
+ portion of this system forward by a single step of size @p h and
1519
+ (2) set the error estimate (via get_mutable_error_estimate()). This
1520
+ method is called during the integration process (via
1521
+ StepOnceErrorControlledAtMost(), IntegrateNoFurtherThanTime(), and
1522
+ IntegrateWithSingleFixedStepToTime()).
1523
+ @returns `true` if successful, `false` if the integrator was unable to take
1524
+ a single step of size @p h (due to, e.g., an integrator
1525
+ convergence failure).
1526
+ @post If the time on entry is denoted `t`, the time and state will be
1527
+ advanced to `t+h` if the method returns `true`; otherwise, the
1528
+ time and state should be reset to those at `t`.
1529
+ @warning It is expected that DoStep() will return `true` for some, albeit
1530
+ possibly very small, positive value of @p h. The derived
1531
+ integrator's stepping algorithm can make this guarantee, for
1532
+ example, by switching to an algorithm not subject to convergence
1533
+ failures (e.g., explicit Euler) for very small step sizes.
1534
+ */
1535
+ virtual bool DoStepConst (Context<T>* context, const T& h) const {
1536
+ unused (context);
1537
+ unused (h);
1538
+ throw std::logic_error (
1539
+ " This integrator does not (yet) implement the const DoStep() variant." );
1540
+ }
1541
+
1465
1542
// TODO(russt): Allow subclasses to override the interpolation scheme used, as
1466
1543
// the 'optimal' dense output scheme is only known by the specific integration
1467
1544
// scheme being implemented.
@@ -1628,7 +1705,7 @@ class IntegratorBase {
1628
1705
T smallest_adapted_step_size_taken_{nan ()};
1629
1706
T largest_step_size_taken_{nan ()};
1630
1707
int64_t num_steps_taken_{0 };
1631
- int64_t num_ode_evals_{0 };
1708
+ mutable int64_t num_ode_evals_{0 };
1632
1709
int64_t num_shrinkages_from_error_control_{0 };
1633
1710
int64_t num_shrinkages_from_substep_failures_{0 };
1634
1711
int64_t num_substep_failures_{0 };
0 commit comments