dpchsw Subroutine

private subroutine dpchsw(dfmax, iextrm, d1, d2, h, slope, ierr)

Switch Excursion Limiter.

Called by dpchcs to adjust D1 and D2 if necessary to insure that the extremum on this interval is not further than DFMAX from the extreme data value.

Warning

This routine does no validity-checking of arguments.

Arguments

Type IntentOptional Attributes Name
real(kind=wp), intent(in) :: dfmax

maximum allowed difference between F(IEXTRM) and the cubic determined by derivative values D1,D2. (assumes DFMAX>0.)

integer, intent(in) :: iextrm

index of the extreme data value. (assumes IEXTRM = 1 or 2 . Any value /=1 is treated as 2.)

real(kind=wp), intent(inout) :: d1

derivative values at the ends of the interval. (Assumes D1*D2 <= 0.) (output) may be modified if necessary to meet the restriction imposed by DFMAX.

real(kind=wp), intent(inout) :: d2

derivative values at the ends of the interval. (Assumes D1*D2 <= 0.) (output) may be modified if necessary to meet the restriction imposed by DFMAX.

real(kind=wp), intent(in) :: h

interval length. (Assumes H>0.)

real(kind=wp), intent(in) :: slope

data slope on the interval.

integer, intent(out) :: ierr

error flag. should be zero.

  • If IERR=-1, assumption on D1 and D2 is not satisfied.
  • If IERR=-2, quadratic equation locating extremum has negative discriminant (should never occur).

Calls

proc~~dpchsw~~CallsGraph proc~dpchsw pchip_module::dpchsw proc~xermsg pchip_module::xermsg proc~dpchsw->proc~xermsg

Called by

proc~~dpchsw~~CalledByGraph proc~dpchsw pchip_module::dpchsw proc~dpchcs pchip_module::dpchcs proc~dpchcs->proc~dpchsw proc~dpchic pchip_module::dpchic proc~dpchic->proc~dpchcs

Source Code

    subroutine dpchsw (dfmax, iextrm, d1, d2, h, slope, ierr)

    integer,intent(in)  :: iextrm       !! index of the extreme data value.  (assumes
                                        !! IEXTRM = 1 or 2 .  Any value /=1 is treated as 2.)
    integer,intent(out) :: ierr         !! error flag.  should be zero.
                                        !!
                                        !! * If IERR=-1, assumption on D1 and D2 is not satisfied.
                                        !! * If IERR=-2, quadratic equation locating extremum has
                                        !!   negative discriminant (should never occur).
    real(wp),intent(in) :: dfmax        !! maximum allowed difference between F(IEXTRM) and
                                        !! the cubic determined by derivative values D1,D2.  (assumes
                                        !! DFMAX>0.)
    real(wp),intent(inout) :: d1        !! derivative values at the ends of the interval.
                                        !! (Assumes D1*D2 <= 0.)
                                        !! (output) may be modified if necessary to meet the restriction
                                        !! imposed by DFMAX.
    real(wp),intent(inout) :: d2        !! derivative values at the ends of the interval.
                                        !! (Assumes D1*D2 <= 0.)
                                        !! (output) may be modified if necessary to meet the restriction
                                        !! imposed by DFMAX.
    real(wp),intent(in) :: h            !! interval length.  (Assumes  H>0.)
    real(wp),intent(in) :: slope        !! data slope on the interval.

    real(wp) :: cp, hphi, nu, radcal, sigma
    real(wp) :: lambda  !! the ratio of d2 to d1.
    real(wp) :: phi     !! phi is the normalized value of p(x)-f1 at x = xhat = x-hat(rho),
                        !! where  that = (xhat - x1)/h .
                        !! that is, p(xhat)-f1 = d*h*phi,  where d=d1 or d2.
                        !! similarly,  p(xhat)-f2 = d*h*(phi-rho) .
    real(wp) :: rho     !! the ratio of the data slope to the derivative being tested.
    real(wp) :: that    !! that = t-hat(rho) is the normalized location of the extremum.

    real(wp),parameter :: fact = 100.0_wp
    real(wp),parameter :: third = one/three - d1mach4  !! third should be slightly less than 1/3 (original code had 0.33333)
    real(wp),parameter :: small = fact*d1mach4 !! small should be a few orders of magnitude greater than macheps.

    !  do main calculation.

    if (d1 == zero) then

        ! special case -- d1==zero .
        ! if d2 is also zero, this routine should not have been called.
        if (d2 == zero) then
            ! d1 and d2 both zero, or both nonzero and same sign.
            ierr = -1
            call xermsg ('PCHIP', 'dpchsw', 'd1 and/or d2 invalid', ierr, 1)
            return
        end if

        rho = slope/d2
        ! extremum is outside interval when rho >= 1/3 .
        if (rho >= third) then
            ierr = 0
            return
        end if
        that = (two*(three*rho-one)) / (three*(two*rho-one))
        phi = that**2 * ((three*rho-one)/three)

        ! convert to distance from f2 if iextrm/=1 .
        if (iextrm /= 1) phi = phi - rho

        ! test for exceeding limit, and adjust accordingly.
        hphi = h * abs(phi)
        if (hphi*abs(d2) > dfmax) then
            ! at this point, hphi>0, so divide is ok.
            d2 = sign (dfmax/hphi, d2)
        endif

    else

        rho = slope/d1
        lambda = -d2/d1
        if (d2 == zero) then
            ! special case -- d2==zero .
            ! extremum is outside interval when rho >= 1/3 .
            if (rho >= third) then
                ierr = 0
                return
            end if
            cp = two - three*rho
            nu = one - two*rho
            that = one / (three*nu)

        else
            if (lambda <= zero) then
                ! d1 and d2 both zero, or both nonzero and same sign.
                ierr = -1
                call xermsg ('PCHIP', 'dpchsw', 'd1 and/or d2 invalid', ierr, 1)
                return
            end if

            ! normal case -- d1 and d2 both nonzero, opposite signs.

            nu = one - lambda - two*rho
            sigma = one - rho
            cp = nu + sigma
            if (abs(nu) > small) then
                radcal = (nu - (two*rho+one))*nu + sigma**2
                if (radcal < zero) then
                    ! negative value of radical (should never occur).
                    ierr = -2
                    call xermsg ('PCHIP', 'dpchsw', 'negative radical', ierr, 1)
                end if
                that = (cp - sqrt(radcal)) / (three*nu)
            else
                that = one/(two*sigma)
            endif
        endif
        phi = that*((nu*that - cp)*that + one)

        ! convert to distance from f2 if iextrm/=1 .
        if (iextrm /= 1) phi = phi - rho

        ! test for exceeding limit, and adjust accordingly.
        hphi = h * abs(phi)
        if (hphi*abs(d1) > dfmax) then
            ! at this point, hphi>0, so divide is ok.
            d1 = sign (dfmax/hphi, d1)
            d2 = -lambda*d1
        endif

    endif

    ierr = 0

    end subroutine dpchsw