Dense output: compute the state at a time within the last accepted step,
using the continuous extension of the method (if it has one, see
rklib_properties dense).
This is meant to be called from the report function during an integration
(to get the state at times in between the reported points), or after an integration
(to get the state within the final step). t must be in the interval of the last
accepted step: [t_prev, t], where t is the last reported time and t_prev
is the one before it (see dense_interval).
The dense output must have been enabled when the integrator was initialized (see initialize_variable_step), otherwise an error is raised.
This must not be called from the derivative function f. If it is, an
error is raised (RKLIB_ERROR_DENSE_NOT_AVAILABLE during a step, or RKLIB_ERROR_DENSE_RECURSIVE
while computing the extra function evaluations needed by some methods for the dense output).
If the user has stopped the integration (see rk_class_stop), nothing is computed
(no function evaluations are done), and x is returned as zero (with istatus, if present,
set to RKLIB_ERROR_USER_STOPPED). This is also the case if the user stops the
integration in f while the extra function evaluations for the dense output are computed.
If istatus is present, it returns the status of this call (RKLIB_ERROR_NONE if x was computed),
regardless of any previous errors.
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| class(rk_class), | intent(inout) | :: | me | |||
| real(kind=wp), | intent(in) | :: | t |
time at which to compute the state |
||
| real(kind=wp), | intent(out), | dimension(me%n) | :: | x |
state at |
|
| integer, | intent(out), | optional | :: | istatus |
status of this call |
recursive subroutine interpolate(me,t,x,istatus) class(rk_class),intent(inout) :: me real(wp),intent(in) :: t !! time at which to compute the state real(wp),dimension(me%n),intent(out) :: x !! state at `t` integer,intent(out),optional :: istatus !! status of this call real(wp) :: theta !! normalized time within the step [0,1] real(wp) :: theta_tol !! tolerance for the `theta` range check integer :: j !! counter integer :: degree !! degree of the interpolating polynomial if (present(istatus)) istatus = RKLIB_ERROR_NONE if (me%dense_computing) then ! called from `f` while computing the extra stages for the dense output call fail(RKLIB_ERROR_DENSE_RECURSIVE) return end if if (me%stopped) then call user_stopped() return end if if (.not. allocated(me%dense_b)) then call fail(RKLIB_ERROR_NO_DENSE_OUTPUT) return end if if (.not. me%dense_enabled) then call fail(RKLIB_ERROR_DENSE_NOT_ENABLED) return end if if (.not. me%dense_available) then ! no step has been taken yet (e.g., the first reported point), ! or called from `f` during a step call fail(RKLIB_ERROR_DENSE_NOT_AVAILABLE) return end if ! allow for roundoff in the times of the step endpoints: theta_tol = 10*spacing(max(abs(me%dense_t),abs(me%dense_t+me%dense_h))) / abs(me%dense_h) theta = (t - me%dense_t) / me%dense_h if (theta < -theta_tol .or. theta > 1.0_wp + theta_tol) then call fail(RKLIB_ERROR_DENSE_OUT_OF_RANGE) return end if if (.not. me%dense_computed) then ! compute the polynomial coefficients for this step (only done once per step) me%dense_computing = .true. call me%dense_extra_stages() me%dense_computing = .false. if (me%stopped) then ! the user stopped the integration in `f` call user_stopped() return end if me%dense_r = me%dense_h * matmul(me%funcs(:,1:size(me%dense_b,1)), me%dense_b) me%dense_computed = .true. end if ! evaluate the interpolant (from the inside out): degree = size(me%dense_r,2) x = me%dense_r(:,degree) select case (me%dense_form) case (dense_form_polynomial) ! x0 + theta*(r1 + theta*(r2 + theta*(r3 + ...))) do j = degree-1, 1, -1 x = me%dense_r(:,j) + theta*x end do case (dense_form_dopri) ! x0 + theta*(r1 + theta1*(r2 + theta*(r3 + theta1*(r4 + ...)))) do j = degree-1, 1, -1 if (mod(j,2)==1) then x = me%dense_r(:,j) + (1.0_wp-theta)*x else x = me%dense_r(:,j) + theta*x end if end do end select x = me%dense_x + theta*x contains subroutine fail(error_code) !! the interpolation failed integer,intent(in) :: error_code x = zero call me%raise_exception(error_code) if (present(istatus)) istatus = error_code end subroutine fail subroutine user_stopped() !! the user has stopped the integration (not an error, and !! the status has already been set by [[rk_class_stop]]) x = zero if (present(istatus)) istatus = RKLIB_ERROR_USER_STOPPED end subroutine user_stopped end subroutine interpolate