this routine computes the step size, h0, to be attempted on the first step, when the user has not supplied a value for this.
first we check that tout - t0 differs significantly from zero. then an iteration is done to approximate the initial second derivative and this is used to define h from w.r.m.s.norm(h**2 * yddot / 2) = 1. a bias factor of 1/2 is applied to the resulting h. the sign of h0 is inferred from the initial values of tout and t0.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
class(dvode_t), | intent(inout) | :: | me | |||
integer, | intent(in) | :: | n |
size of ode system |
||
real(kind=wp), | intent(in) | :: | t0 |
initial value of independent variable |
||
real(kind=wp), | intent(in) | :: | y0(*) |
vector of initial conditions |
||
real(kind=wp), | intent(in) | :: | ydot(*) |
vector of initial first derivatives |
||
real(kind=wp), | intent(in) | :: | tout |
first output value of independent variable |
||
real(kind=wp), | intent(in) | :: | uround |
machine unit roundoff |
||
real(kind=wp), | intent(in) | :: | ewt(*) |
error weights and tolerance parameters as described in the driver routine |
||
integer, | intent(in) | :: | itol |
error weights and tolerance parameters as described in the driver routine |
||
real(kind=wp), | intent(in) | :: | atol(*) |
error weights and tolerance parameters as described in the driver routine |
||
real(kind=wp), | intent(inout) | :: | y(n) |
work array of length n |
||
real(kind=wp), | intent(inout) | :: | temp(n) |
work array of length n |
||
real(kind=wp), | intent(out) | :: | h0 |
step size to be attempted |
||
integer, | intent(out) | :: | niter |
number of iterations (and of f evaluations) to compute h0 |
||
integer, | intent(out) | :: | ier |
the error flag, returned with the value:
|
subroutine dvhin(me,n,t0,y0,ydot,tout,uround,ewt,itol,& atol,y,temp,h0,niter,ier) class(dvode_t),intent(inout) :: me real(wp),intent(in) :: t0 !! initial value of independent variable real(wp),intent(in) :: y0(*) !! vector of initial conditions real(wp),intent(in) :: ydot(*) !! vector of initial first derivatives real(wp),intent(in) :: tout !! first output value of independent variable real(wp),intent(in) :: uround !! machine unit roundoff real(wp),intent(in) :: ewt(*) !! error weights and tolerance parameters as described in the driver routine real(wp),intent(in) :: atol(*) !! error weights and tolerance parameters as described in the driver routine real(wp),intent(inout) :: y(n) !! work array of length n real(wp),intent(inout) :: temp(n) !! work array of length n integer,intent(in) :: n !! size of ode system integer,intent(in) :: itol !! error weights and tolerance parameters as described in the driver routine real(wp),intent(out) :: h0 !! step size to be attempted integer,intent(out) :: niter !! number of iterations (and of f evaluations) to compute h0 integer,intent(out) :: ier !! the error flag, returned with the value: !! !! * ier = 0 if no trouble occurred, or !! * ier = -1 if tout and t0 are considered too close to proceed. real(wp) :: afi , atoli , delyi , h , hg , hlb , & hnew , hrat , hub , t1 , tdist , & tround , yddnrm integer :: i , iter real(wp),parameter :: half = 0.5_wp real(wp),parameter :: hun = 100.0_wp real(wp),parameter :: pt1 = 0.1_wp real(wp),parameter :: two = 2.0_wp main : block niter = 0 tdist = abs(tout-t0) tround = uround*max(abs(t0),abs(tout)) if ( tdist<two*tround ) then ! error return for tout - t0 too small. -------------------------------- ier = -1 return else ! set a lower bound on h based on the roundoff level in t0 and tout. --- hlb = hun*tround ! set an upper bound on h based on tout-t0 and the initial y and ydot. - hub = pt1*tdist atoli = atol(1) do i = 1 , n if ( itol==2 .or. itol==4 ) atoli = atol(i) delyi = pt1*abs(y0(i)) + atoli afi = abs(ydot(i)) if ( afi*hub>delyi ) hub = delyi/afi enddo ! set initial guess for h as geometric mean of upper and lower bounds. - iter = 0 hg = sqrt(hlb*hub) ! if the bounds have crossed, exit with the mean value. ---------------- if ( hub<hlb ) then h0 = hg exit main endif do ! looping point for iteration. ----------------------------------------- ! estimate the second derivative as a difference quotient in f. -------- h = sign(hg,tout-t0) t1 = t0 + h do i = 1 , n y(i) = y0(i) + h*ydot(i) enddo call me%f(n,t1,y,temp) do i = 1 , n temp(i) = (temp(i)-ydot(i))/h enddo yddnrm = me%dvnorm(n,temp,ewt(1:n)) ! get the corresponding new value of h. -------------------------------- if ( yddnrm*hub*hub>two ) then hnew = sqrt(two/yddnrm) else hnew = sqrt(hg*hub) endif iter = iter + 1 !----------------------------------------------------------------------- ! test the stopping conditions. ! stop if the new and previous h values differ by a factor of < 2. ! stop if four iterations have been done. also, stop with previous h ! if hnew/hg > 2 after first iteration, as this probably means that ! the second derivative value is bad because of cancellation error. !----------------------------------------------------------------------- if ( iter<4 ) then hrat = hnew/hg if ( (hrat<=half) .or. (hrat>=two) ) then if ( (iter>=2) .and. (hnew>two*hg) ) then hnew = hg exit endif hg = hnew cycle endif endif exit end do ! iteration done. apply bounds, bias factor, and sign. then exit. ---- h0 = hnew*half if ( h0<hlb ) h0 = hlb if ( h0>hub ) h0 = hub endif end block main h0 = sign(h0,tout-t0) niter = iter ier = 0 end subroutine dvhin