dvstep Subroutine

private subroutine dvstep(me, y, yh, ldyh, yh1, ewt, savf, vsav, acor, wm, iwm)

dvstep performs one step of the integration of an initial value problem for a system of ordinary differential equations.

dvstep calls subroutine dvnlsd for the solution of the nonlinear system arising in the time step. thus it is independent of the problem jacobian structure and the type of nonlinear system solution method. dvstep returns a completion flag kflag (in common). a return with kflag = -1 or -2 means either abs(h) = hmin or 10 consecutive failures occurred. on a return with kflag negative, the values of tn and the yh array are as of the beginning of the last step, and h is the last step size attempted.

Type Bound

dvode_t

Arguments

Type IntentOptional Attributes Name
class(dvode_t), intent(inout) :: me
real(kind=wp), intent(inout) :: y(*)

an array of length n used for the dependent variable vector.

real(kind=wp), intent(inout) :: yh(ldyh,*)

an ldyh by lmax array containing the dependent variables and their approximate scaled derivatives, where lmax = maxord + 1. yh(i,j+1) contains the approximate j-th derivative of y(i), scaled by h**j/factorial(j) (j = 0,1,...,nq). on entry for the first step, the first two columns of yh must be set from the initial values.

integer, intent(in) :: ldyh

a constant integer >= n, the first dimension of yh. n is the number of odes in the system.

real(kind=wp), intent(inout) :: yh1(*)

a one-dimensional array occupying the same space as yh.

real(kind=wp), intent(in) :: ewt(*)

an array of length n containing multiplicative weights for local error measurements. local errors in y(i) are compared to 1.0/ewt(i) in various error tests.

real(kind=wp) :: savf(*)

an array of working storage, of length n. also used for input of yh(*,maxord+2) when jstart = -1 and maxord < the current order nq.

real(kind=wp) :: vsav(*)

a work array of length n passed to subroutine dvnlsd.

real(kind=wp) :: acor(*)

a work array of length n, used for the accumulated corrections. on a successful return, acor(i) contains the estimated one-step local error in y(i).

real(kind=wp) :: wm(*)

real work array associated with matrix operations in dvnlsd.

integer :: iwm(*)

integer work array associated with matrix operations in dvnlsd.


Calls

proc~~dvstep~~CallsGraph proc~dvstep dvode_module::dvode_t%dvstep proc~daxpy dvode_blas_module::daxpy proc~dvstep->proc~daxpy proc~dcopy dvode_blas_module::dcopy proc~dvstep->proc~dcopy proc~dscal dvode_blas_module::dscal proc~dvstep->proc~dscal proc~dvjust dvode_module::dvode_t%dvjust proc~dvstep->proc~dvjust proc~dvnlsd dvode_module::dvode_t%dvnlsd proc~dvstep->proc~dvnlsd proc~dvset dvode_module::dvode_t%dvset proc~dvstep->proc~dvset proc~dvjust->proc~daxpy proc~dvnlsd->proc~daxpy proc~dvnlsd->proc~dcopy proc~dvnlsd->proc~dscal proc~dvjac dvode_module::dvode_t%dvjac proc~dvnlsd->proc~dvjac proc~dvsol dvode_module::dvode_t%dvsol proc~dvnlsd->proc~dvsol proc~dvjac->proc~dcopy proc~dvjac->proc~dscal proc~dacopy dvode_module::dacopy proc~dvjac->proc~dacopy proc~dgbfa dvode_linpack_module::dgbfa proc~dvjac->proc~dgbfa proc~dgefa dvode_linpack_module::dgefa proc~dvjac->proc~dgefa proc~dgbsl dvode_linpack_module::dgbsl proc~dvsol->proc~dgbsl proc~dgesl dvode_linpack_module::dgesl proc~dvsol->proc~dgesl proc~dacopy->proc~dcopy proc~dgbfa->proc~daxpy proc~dgbfa->proc~dscal proc~idamax dvode_blas_module::idamax proc~dgbfa->proc~idamax proc~dgbsl->proc~daxpy proc~ddot dvode_blas_module::ddot proc~dgbsl->proc~ddot proc~dgefa->proc~daxpy proc~dgefa->proc~dscal proc~dgefa->proc~idamax proc~dgesl->proc~daxpy proc~dgesl->proc~ddot

Called by

proc~~dvstep~~CalledByGraph proc~dvstep dvode_module::dvode_t%dvstep proc~dvode dvode_module::dvode_t%dvode proc~dvode->proc~dvstep

Source Code

   subroutine dvstep(me,y,yh,ldyh,yh1,ewt,savf,vsav,acor,wm,iwm)

      class(dvode_t),intent(inout) :: me
      integer,intent(in) :: ldyh !! a constant integer >= n, the first dimension of yh.
                                 !! n is the number of odes in the system.
      real(wp),intent(inout) :: y(*) !! an array of length n used for the dependent variable vector.
      real(wp),intent(inout) :: yh(ldyh,*) !! an ldyh by lmax array containing the dependent variables
                                           !! and their approximate scaled derivatives, where
                                           !! lmax = maxord + 1.  yh(i,j+1) contains the approximate
                                           !! j-th derivative of y(i), scaled by h**j/factorial(j)
                                           !! (j = 0,1,...,nq).  on entry for the first step, the first
                                           !! two columns of yh must be set from the initial values.
      real(wp),intent(inout) :: yh1(*) !! a one-dimensional array occupying the same space as yh.
      real(wp),intent(in) :: ewt(*) !! an array of length n containing multiplicative weights
                                    !! for local error measurements.  local errors in y(i) are
                                    !! compared to 1.0/ewt(i) in various error tests.
      real(wp) :: savf(*) !! an array of working storage, of length n.
                          !! also used for input of yh(*,maxord+2) when jstart = -1
                          !! and maxord < the current order nq.
      real(wp) :: vsav(*) !! a work array of length n passed to subroutine [[dvnlsd]].
      real(wp) :: acor(*) !! a work array of length n, used for the accumulated
                          !! corrections.  on a successful return, acor(i) contains
                          !! the estimated one-step local error in y(i).
      real(wp) :: wm(*) !! real work array associated with matrix
                        !! operations in [[dvnlsd]].
      integer :: iwm(*) !! integer work array associated with matrix
                        !! operations in [[dvnlsd]].

      real(wp) :: cnquot , ddn , dsm , dup , etaqp1 , &
                  flotl , r , told
      integer :: i , i1 , i2 , iback , j , jb , ncf , nflag

      integer,parameter :: kfc = -3
      integer,parameter :: kfh = -7
      integer,parameter :: mxncf = 10
      real(wp),parameter :: one = 1.0_wp
      real(wp),parameter :: addon = 1.0e-6_wp
      real(wp),parameter :: bias1 = 6.0_wp
      real(wp),parameter :: bias2 = 6.0_wp
      real(wp),parameter :: bias3 = 10.0_wp
      real(wp),parameter :: etacf = 0.25_wp
      real(wp),parameter :: etamin = 0.1_wp
      real(wp),parameter :: etamxf = 0.2_wp
      real(wp),parameter :: etamx1 = 10000.0_wp
      real(wp),parameter :: etamx2 = 10.0_wp
      real(wp),parameter :: etamx3 = 10.0_wp
      real(wp),parameter :: onepsm = 1.00001_wp
      real(wp),parameter :: thresh = 1.5_wp

      me%dat%kflag = 0
      told = me%dat%tn
      ncf = 0
      me%dat%jcur = 0
      nflag = 0

      if ( me%dat%jstart<=0 ) then
         if ( me%dat%jstart==-1 ) then
            !-----------------------------------------------------------------------
            ! the following block handles preliminaries needed when jstart = -1.
            ! if n was reduced, zero out part of yh to avoid undefined references.
            ! if maxord was reduced to a value less than the tentative order newq,
            ! then nq is set to maxord, and a new h ratio eta is chosen.
            ! otherwise, we take the same preliminary actions as for jstart > 0.
            ! in any case, nqwait is reset to l = nq + 1 to prevent further
            ! changes in order for that many steps.
            ! the new h ratio eta is limited by the input h if kuth = 1,
            ! by hmin if kuth = 0, and by hmxi in any case.
            ! finally, the history array yh is rescaled.
            !-----------------------------------------------------------------------
            me%dat%lmax = me%dat%maxord + 1
            if ( me%dat%n/=ldyh ) then
               i1 = 1 + (me%dat%newq+1)*ldyh
               i2 = (me%dat%maxord+1)*ldyh
               if ( i1<=i2 ) then
                  do i = i1 , i2
                     yh1(i) = zero
                  enddo
               endif
            endif
            if ( me%dat%newq>me%dat%maxord ) then
               flotl = real(me%dat%lmax,wp)
               if ( me%dat%maxord<me%dat%nq-1 ) then
                  ddn = me%dvnorm(me%dat%n,savf(1:me%dat%n),ewt(1:me%dat%n))/me%dat%tq(1)
                  me%dat%eta = one/((bias1*ddn)**(one/flotl)+addon)
               endif
               if ( me%dat%maxord==me%dat%nq .and. me%dat%newq==me%dat%nq+1 ) me%dat%eta = me%etaq
               if ( me%dat%maxord==me%dat%nq-1 .and. me%dat%newq==me%dat%nq+1 ) then
                  me%dat%eta = me%etaqm1
                  call me%dvjust(yh,ldyh,-1)
               endif
               if ( me%dat%maxord==me%dat%nq-1 .and. me%dat%newq==me%dat%nq ) then
                  ddn = me%dvnorm(me%dat%n,savf(1:me%dat%n),ewt(1:me%dat%n))/me%dat%tq(1)
                  me%dat%eta = one/((bias1*ddn)**(one/flotl)+addon)
                  call me%dvjust(yh,ldyh,-1)
               endif
               me%dat%eta = min(me%dat%eta,one)
               me%dat%nq = me%dat%maxord
               me%dat%l = me%dat%lmax
            endif
            if ( me%dat%kuth==1 ) me%dat%eta = min(me%dat%eta,abs(me%dat%h/me%dat%hscal))
            if ( me%dat%kuth==0 ) me%dat%eta = max(me%dat%eta,me%dat%hmin/abs(me%dat%hscal))
            me%dat%eta = me%dat%eta/max(one,abs(me%dat%hscal)*me%dat%hmxi*me%dat%eta)
            me%dat%newh = 1
            me%dat%nqwait = me%dat%l
            if ( me%dat%newq>me%dat%maxord ) goto 300
         else
            !-----------------------------------------------------------------------
            ! on the first call, the order is set to 1, and other variables are
            ! initialized.  etamax is the maximum ratio by which h can be increased
            ! in a single step.  it is normally 10, but is larger during the
            ! first step to compensate for the small initial h.  if a failure
            ! occurs (in corrector convergence or error test), etamax is set to 1
            ! for the next increase.
            !-----------------------------------------------------------------------
            me%dat%lmax = me%dat%maxord + 1
            me%dat%nq = 1
            me%dat%l = 2
            me%dat%nqnyh = me%dat%nq*ldyh
            me%dat%tau(1) = me%dat%h
            me%dat%prl1 = one
            me%dat%rc = zero
            me%dat%etamax = etamx1
            me%dat%nqwait = 2
            me%dat%hscal = me%dat%h
            goto 400
         end if

      else if ( me%dat%kuth==1 ) then
         !-----------------------------------------------------------------------
         ! take preliminary actions on a normal continuation step (jstart>0).
         ! if the driver changed h, then eta must be reset and newh set to 1.
         ! if a change of order was dictated on the previous step, then
         ! it is done here and appropriate adjustments in the history are made.
         ! on an order decrease, the history array is adjusted by dvjust.
         ! on an order increase, the history array is augmented by a column.
         ! on a change of step size h, the history array yh is rescaled.
         !-----------------------------------------------------------------------
         me%dat%eta = min(me%dat%eta,me%dat%h/me%dat%hscal)
         me%dat%newh = 1
      endif

      if ( me%dat%newh==0 ) goto 400

      if ( me%dat%newq<me%dat%nq ) then
         call me%dvjust(yh,ldyh,-1)
         me%dat%nq = me%dat%newq
         me%dat%l = me%dat%nq + 1
         me%dat%nqwait = me%dat%l
      else if ( me%dat%newq>me%dat%nq ) then
         call me%dvjust(yh,ldyh,1)
         me%dat%nq = me%dat%newq
         me%dat%l = me%dat%nq + 1
         me%dat%nqwait = me%dat%l
      endif

      ! rescale the history array for a change in h by a factor of eta. ------
 300  r = one
      do j = 2 , me%dat%l
         r = r*me%dat%eta
         call dscal(me%dat%n,r,yh(1,j),1)
      enddo
      me%dat%h = me%dat%hscal*me%dat%eta
      me%dat%hscal = me%dat%h
      me%dat%rc = me%dat%rc*me%dat%eta
      me%dat%nqnyh = me%dat%nq*ldyh

      !-----------------------------------------------------------------------
      ! this section computes the predicted values by effectively
      ! multiplying the yh array by the pascal triangle matrix.
      ! dvset is called to calculate all integration coefficients.
      ! rc is the ratio of new to old values of the coefficient h/el(2)=h/l1.
      !-----------------------------------------------------------------------
 400  me%dat%tn = me%dat%tn + me%dat%h
      i1 = me%dat%nqnyh + 1
      do jb = 1 , me%dat%nq
         i1 = i1 - ldyh
         do i = i1 , me%dat%nqnyh
            yh1(i) = yh1(i) + yh1(i+ldyh)
         enddo
      enddo
      call me%dvset()
      me%dat%rl1 = one/me%dat%el(2)
      me%dat%rc = me%dat%rc*(me%dat%rl1/me%dat%prl1)
      me%dat%prl1 = me%dat%rl1

      ! call the nonlinear system solver. ------------------------------------
      call me%dvnlsd(y,yh,ldyh,vsav,savf,ewt,acor,iwm,wm,nflag)

      if ( nflag==0 ) then
         !-----------------------------------------------------------------------
         ! the corrector has converged (nflag = 0).  the local error test is
         ! made and control passes to statement 500 if it fails.
         !-----------------------------------------------------------------------
         dsm = me%dat%acnrm/me%dat%tq(2)
         if ( dsm>one ) then
            !-----------------------------------------------------------------------
            ! the error test failed.  kflag keeps track of multiple failures.
            ! restore tn and the yh array to their previous values, and prepare
            ! to try the step again.  compute the optimum step size for the
            ! same order.  after repeated failures, h is forced to decrease
            ! more rapidly.
            !-----------------------------------------------------------------------
            me%dat%kflag = me%dat%kflag - 1
            me%dat%netf = me%dat%netf + 1
            nflag = -2
            me%dat%tn = told
            i1 = me%dat%nqnyh + 1
            do jb = 1 , me%dat%nq
               i1 = i1 - ldyh
               do i = i1 , me%dat%nqnyh
                  yh1(i) = yh1(i) - yh1(i+ldyh)
               enddo
            enddo
            if ( abs(me%dat%h)<=me%dat%hmin*onepsm ) then
               !-----------------------------------------------------------------------
               ! all returns are made through this section.
               ! on a successful return, etamax is reset and acor is scaled.
               !-----------------------------------------------------------------------
               me%dat%kflag = -1
               me%dat%jstart = 1
               return
            else
               me%dat%etamax = one
               if ( me%dat%kflag>kfc ) then
                  ! compute ratio of new h to current h at the current order. ------------
                  flotl = real(me%dat%l,wp)
                  me%dat%eta = one/((bias2*dsm)**(one/flotl)+addon)
                  me%dat%eta = max(me%dat%eta,me%dat%hmin/abs(me%dat%h),etamin)
                  if ( (me%dat%kflag<=-2) .and. (me%dat%eta>etamxf) ) me%dat%eta = etamxf
                  goto 300
                  !-----------------------------------------------------------------------
                  ! control reaches this section if 3 or more consecutive failures
                  ! have occurred.  it is assumed that the elements of the yh array
                  ! have accumulated errors of the wrong order.  the order is reduced
                  ! by one, if possible.  then h is reduced by a factor of 0.1 and
                  ! the step is retried.  after a total of 7 consecutive failures,
                  ! an exit is taken with kflag = -1.
                  !-----------------------------------------------------------------------
               else if ( me%dat%kflag==kfh ) then
                  me%dat%kflag = -1
                  me%dat%jstart = 1
                  return
               else if ( me%dat%nq==1 ) then
                  me%dat%eta = max(etamin,me%dat%hmin/abs(me%dat%h))
                  me%dat%h = me%dat%h*me%dat%eta
                  me%dat%hscal = me%dat%h
                  me%dat%tau(1) = me%dat%h
                  call me%f(me%dat%n,me%dat%tn,y(1:me%dat%n),savf(1:me%dat%n))
                  me%dat%nfe = me%dat%nfe + 1
                  do i = 1 , me%dat%n
                     yh(i,2) = me%dat%h*savf(i)
                  enddo
                  me%dat%nqwait = 10
                  goto 400
               else
                  me%dat%eta = max(etamin,me%dat%hmin/abs(me%dat%h))
                  call me%dvjust(yh,ldyh,-1)
                  me%dat%l = me%dat%nq
                  me%dat%nq = me%dat%nq - 1
                  me%dat%nqwait = me%dat%l
                  goto 300
               endif
            endif
         else
            !-----------------------------------------------------------------------
            ! after a successful step, update the yh and tau arrays and decrement
            ! nqwait.  if nqwait is then 1 and nq < maxord, then acor is saved
            ! for use in a possible order increase on the next step.
            ! if etamax = 1 (a failure occurred this step), keep nqwait >= 2.
            !-----------------------------------------------------------------------
            me%dat%kflag = 0
            me%dat%nst = me%dat%nst + 1
            me%dat%hu = me%dat%h
            me%dat%nqu = me%dat%nq
            do iback = 1 , me%dat%nq
               i = me%dat%l - iback
               me%dat%tau(i+1) = me%dat%tau(i)
            enddo
            me%dat%tau(1) = me%dat%h
            do j = 1 , me%dat%l
               call daxpy(me%dat%n,me%dat%el(j),acor,1,yh(1,j),1)
            enddo
            me%dat%nqwait = me%dat%nqwait - 1
            if ( (me%dat%l/=me%dat%lmax) .and. (me%dat%nqwait==1) ) then
               call dcopy(me%dat%n,acor,1,yh(1,me%dat%lmax),1)
               me%dat%conp = me%dat%tq(5)
            endif
            if ( me%dat%etamax/=one ) then
               !-----------------------------------------------------------------------
               ! if nqwait = 0, an increase or decrease in order by one is considered.
               ! factors etaq, etaqm1, etaqp1 are computed by which h could
               ! be multiplied at order q, q-1, or q+1, respectively.
               ! the largest of these is determined, and the new order and
               ! step size set accordingly.
               ! a change of h or nq is made only if h increases by at least a
               ! factor of thresh.  if an order change is considered and rejected,
               ! then nqwait is set to 2 (reconsider it after 2 steps).
               !-----------------------------------------------------------------------
               ! compute ratio of new h to current h at the current order. ------------
               flotl = real(me%dat%l,wp)
               me%etaq = one/((bias2*dsm)**(one/flotl)+addon)
               if ( me%dat%nqwait==0 ) then
                  me%dat%nqwait = 2
                  me%etaqm1 = zero
                  if ( me%dat%nq/=1 ) then
                     ! compute ratio of new h to current h at the current order less one. ---
                     ddn = me%dvnorm(me%dat%n,yh(1,me%dat%l),ewt(1:me%dat%n))/me%dat%tq(1)
                     me%etaqm1 = one/((bias1*ddn)**(one/(flotl-one))+addon)
                  endif
                  etaqp1 = zero
                  if ( me%dat%l/=me%dat%lmax ) then
                     ! compute ratio of new h to current h at current order plus one. -------
                     cnquot = (me%dat%tq(5)/me%dat%conp)*(me%dat%h/me%dat%tau(2))**me%dat%l
                     do i = 1 , me%dat%n
                        savf(i) = acor(i) - cnquot*yh(i,me%dat%lmax)
                     enddo
                     dup = me%dvnorm(me%dat%n,savf(1:me%dat%n),ewt(1:me%dat%n))/me%dat%tq(3)
                     etaqp1 = one/((bias3*dup)**(one/(flotl+one))+addon)
                  endif
                  if ( me%etaq<etaqp1 ) then
                     if ( etaqp1<=me%etaqm1 ) goto 420
                     me%dat%eta = etaqp1
                     me%dat%newq = me%dat%nq + 1
                     call dcopy(me%dat%n,acor,1,yh(1,me%dat%lmax),1)
                     goto 450
                  else if ( me%etaq<me%etaqm1 ) then
                     goto 420
                  endif
               endif
               me%dat%eta = me%etaq
               me%dat%newq = me%dat%nq
               goto 450
            else
               if ( me%dat%nqwait<2 ) me%dat%nqwait = 2
               me%dat%newq = me%dat%nq
               me%dat%newh = 0
               me%dat%eta = one
               me%dat%hnew = me%dat%h
               goto 500
            endif
 420        me%dat%eta = me%etaqm1
            me%dat%newq = me%dat%nq - 1
         endif

         ! test tentative new h against thresh, etamax, and hmxi, then exit. ----
 450     if ( me%dat%eta<thresh .or. me%dat%etamax==one ) then
            me%dat%newq = me%dat%nq
            me%dat%newh = 0
            me%dat%eta = one
            me%dat%hnew = me%dat%h
         else
            me%dat%eta = min(me%dat%eta,me%dat%etamax)
            me%dat%eta = me%dat%eta/max(one,abs(me%dat%h)*me%dat%hmxi*me%dat%eta)
            me%dat%newh = 1
            me%dat%hnew = me%dat%h*me%dat%eta
         endif
      else
         !-----------------------------------------------------------------------
         ! the vnls routine failed to achieve convergence (nflag /= 0).
         ! the yh array is retracted to its values before prediction.
         ! the step size h is reduced and the step is retried, if possible.
         ! otherwise, an error exit is taken.
         !-----------------------------------------------------------------------
         ncf = ncf + 1
         me%dat%ncfn = me%dat%ncfn + 1
         me%dat%etamax = one
         me%dat%tn = told
         i1 = me%dat%nqnyh + 1
         do jb = 1 , me%dat%nq
            i1 = i1 - ldyh
            do i = i1 , me%dat%nqnyh
               yh1(i) = yh1(i) - yh1(i+ldyh)
            enddo
         enddo
         if ( nflag<-1 ) then
            if ( nflag==-2 ) me%dat%kflag = -3
            if ( nflag==-3 ) me%dat%kflag = -4
            me%dat%jstart = 1
            return
         else if ( abs(me%dat%h)<=me%dat%hmin*onepsm ) then
            me%dat%kflag = -2
            me%dat%jstart = 1
            return
         else if ( ncf==mxncf ) then
            me%dat%kflag = -2
            me%dat%jstart = 1
            return
         else
            me%dat%eta = etacf
            me%dat%eta = max(me%dat%eta,me%dat%hmin/abs(me%dat%h))
            nflag = -1
            goto 300
         endif
      endif

 500  me%dat%etamax = etamx3
      if ( me%dat%nst<=10 ) me%dat%etamax = etamx2
      r = one/me%dat%tq(2)
      call dscal(me%dat%n,r,acor,1)

      me%dat%jstart = 1

   end subroutine dvstep