dpchsp Subroutine

public subroutine dpchsp(ic, vc, n, x, f, d, incfd, wk, nwk, ierr)

Piecewise Cubic Hermite Spline

Computes the Hermite representation of the cubic spline interpolant to the data given in X and F satisfying the boundary conditions specified by IC and VC.

To facilitate two-dimensional applications, includes an increment between successive values of the F- and D-arrays.

The resulting piecewise cubic Hermite function may be evaluated by dpchfe or dpchfd.

References

  • Carl de Boor, A Practical Guide to Splines, Springer-Verlag, New York, 1978, pp. 53-59.

Note

This is a modified version of C. de Boor's cubic spline routine CUBSPL.

Arguments

Type IntentOptional Attributes Name
integer, intent(in) :: ic(2)

integer array of length 2 specifying desired boundary conditions:

  • IC(1) = IBEG, desired condition at beginning of data.
  • IC(2) = IEND, desired condition at end of data.

Valid values for IBEG:

  • IBEG = 0 to set D(1) so that the third derivative is continuous at X(2). This is the "not a knot" condition provided by de Boor's cubic spline routine CUBSPL. This is the default boundary condition.
  • IBEG = 1 if first derivative at X(1) is given in VC(1).
  • IBEG = 2 if second derivative at X(1) is given in VC(1).
  • IBEG = 3 to use the 3-point difference formula for D(1). (Reverts to the default b.c. if N<3).
  • IBEG = 4 to use the 4-point difference formula for D(1). (Reverts to the default b.c. if N<4).

NOTES:

  1. An error return is taken if IBEG is out of range.
  2. For the "natural" boundary condition, use IBEG=2 and VC(1)=0.

IEND may take on the same values as IBEG, but applied to derivative at X(N). In case IEND = 1 or 2, the value is given in VC(2).

NOTES:

  1. An error return is taken if IEND is out of range.
  2. For the "natural" boundary condition, use IEND=2 and VC(2)=0.
real(kind=wp), intent(in) :: vc(2)

array of length 2 specifying desired boundary values, as indicated above.

  • VC(1) need be set only if IC(1) = 1 or 2 .
  • VC(2) need be set only if IC(2) = 1 or 2 .
integer, intent(in) :: n

number of data points. (Error return if N<2).

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

array of independent variable values. The elements of X must be strictly increasing: X(I-1) < X(I), I = 2(1)N. (Error return if not.)

real(kind=wp), intent(in) :: f(incfd,*)

array of dependent variable values to be interpolated. F(1+(I-1)*INCFD) is value corresponding to X(I).

real(kind=wp), intent(out) :: d(incfd,*)

array of derivative values at the data points. These values will determine the cubic spline interpolant with the requested boundary conditions. The value corresponding to X(I) is stored in D(1+(I-1)*INCFD), I=1(1)N. No other entries in D are changed.

integer, intent(in) :: incfd

increment between successive values in F and D. This argument is provided primarily for 2-D applications. (Error return if INCFD<1).

real(kind=wp), intent(inout) :: wk(2,*)

array of working storage.

integer, intent(in) :: nwk

length of work array. (Error return if NWK<2*N).

integer, intent(out) :: ierr

error flag.

  • Normal return:
    • IERR = 0 (no errors).
  • "Recoverable" errors:

    • IERR = -1 if N<2 .
    • IERR = -2 if INCFD<1 .
    • IERR = -3 if the X-array is not strictly increasing.
    • IERR = -4 if IBEG<0 or IBEG>4 .
    • IERR = -5 if IEND<0 of IEND>4 .
    • IERR = -6 if both of the above are true.
    • IERR = -7 if NWK is too small.

    NOTE: The above errors are checked in the order listed, and following arguments have NOT been validated. (The D-array has not been changed in any of these cases.)

    • IERR = -8 in case of trouble solving the linear system for the interior derivative values. (The D-array may have been changed in this case.) ( Do NOT use it! )

Calls

proc~~dpchsp~~CallsGraph proc~dpchsp pchip_module::dpchsp proc~dpchdf pchip_module::dpchdf proc~dpchsp->proc~dpchdf proc~xermsg pchip_module::xermsg proc~dpchsp->proc~xermsg proc~dpchdf->proc~xermsg

Source Code

    subroutine dpchsp (ic, vc, n, x, f, d, incfd, wk, nwk, ierr)

    integer,intent(in)     :: ic(2)     !! integer array of length 2 specifying desired
                                        !! boundary conditions:
                                        !!
                                        !! * IC(1) = IBEG, desired condition at beginning of data.
                                        !! * IC(2) = IEND, desired condition at end of data.
                                        !!
                                        !! Valid values for IBEG:
                                        !!
                                        !! * IBEG = 0  to set D(1) so that the third derivative is
                                        !!     continuous at X(2).  This is the "not a knot" condition
                                        !!     provided by de Boor's cubic spline routine CUBSPL.
                                        !!     **This is the default boundary condition.**
                                        !! * IBEG = 1  if first derivative at X(1) is given in VC(1).
                                        !! * IBEG = 2  if second derivative at X(1) is given in VC(1).
                                        !! * IBEG = 3  to use the 3-point difference formula for D(1).
                                        !!            (Reverts to the default b.c. if N<3).
                                        !! * IBEG = 4  to use the 4-point difference formula for D(1).
                                        !!            (Reverts to the default b.c. if N<4).
                                        !!
                                        !! NOTES:
                                        !!
                                        !! 1. An error return is taken if IBEG is out of range.
                                        !! 2. For the "natural" boundary condition, use IBEG=2 and
                                        !!    VC(1)=0.
                                        !!
                                        !! IEND may take on the same values as IBEG, but applied to
                                        !! derivative at X(N).  In case IEND = 1 or 2, the value is
                                        !! given in VC(2).
                                        !!
                                        !! NOTES:
                                        !!
                                        !!  1. An error return is taken if IEND is out of range.
                                        !!  2. For the "natural" boundary condition, use IEND=2 and
                                        !!     VC(2)=0.
    integer,intent(in)     :: n         !! number of data points.  (Error return if N<2).
    integer,intent(in)     :: incfd     !! increment between successive values in F and D.
                                        !! This argument is provided primarily for 2-D applications.
                                        !! (Error return if  INCFD<1).
    integer,intent(in)     :: nwk       !! length of work array. (Error return if NWK<2*N).
    integer,intent(out)    :: ierr      !! error flag.
                                        !!
                                        !! * Normal return:
                                        !!     * IERR = 0  (no errors).
                                        !! * "Recoverable" errors:
                                        !!     * IERR = -1  if N<2 .
                                        !!     * IERR = -2  if INCFD<1 .
                                        !!     * IERR = -3  if the X-array is not strictly increasing.
                                        !!     * IERR = -4  if IBEG<0 or IBEG>4 .
                                        !!     * IERR = -5  if IEND<0 of IEND>4 .
                                        !!     * IERR = -6  if both of the above are true.
                                        !!     * IERR = -7  if NWK is too small.
                                        !!
                                        !!      NOTE:  The above errors are checked in the order listed,
                                        !!          and following arguments have **NOT** been validated.
                                        !!    (The D-array has not been changed in any of these cases.)
                                        !!
                                        !!     * IERR = -8  in case of trouble solving the linear system
                                        !!                for the interior derivative values.
                                        !!    (The D-array may have been changed in this case.)
                                        !!    (             Do **NOT** use it!                )
    real(wp),intent(in)    :: vc(2)     !! array of length 2 specifying desired boundary
                                        !! values, as indicated above.
                                        !!
                                        !! * VC(1) need be set only if IC(1) = 1 or 2 .
                                        !! * VC(2) need be set only if IC(2) = 1 or 2 .
    real(wp),intent(in)    :: x(*)      !! array of independent variable values.  The
                                        !! elements of X must be strictly increasing:
                                        !! `X(I-1) < X(I),  I = 2(1)N`.
                                        !! (Error return if not.)
    real(wp),intent(in)    :: f(incfd,*)    !! array of dependent variable values to be
                                            !! interpolated.  F(1+(I-1)*INCFD) is value
                                            !! corresponding to X(I).
    real(wp),intent(out)   :: d(incfd,*)    !! array of derivative values at the data
                                            !! points.  These values will determine the cubic spline
                                            !! interpolant with the requested boundary conditions.
                                            !! The value corresponding to X(I) is stored in
                                            !! `D(1+(I-1)*INCFD),  I=1(1)N`.
                                            !! No other entries in D are changed.
    real(wp),intent(inout) :: wk(2,*)       !! array of working storage.

    integer :: ibeg, iend, index, j, nm1
    real(wp) :: g, stemp(3), xtemp(4)
    logical :: forward

    !  validity-check arguments.
    if ( n<2 ) then
        ierr = -1
        call xermsg ('PCHIP', 'dpchsp', 'number of data points less than two', ierr, 1)
        return
    end if
    if ( incfd<1 ) then
        ierr = -2
        call xermsg ('PCHIP', 'dpchsp', 'increment less than one', ierr, 1)
        return
    end if
    do j = 2, n
        if ( x(j)<=x(j-1) ) then
            ! x-array not strictly increasing.
            ierr = -3
            call xermsg ('PCHIP', 'dpchsp', 'x-array not strictly increasing', ierr, 1)
            return
        end if
    end do

    ibeg = ic(1)
    iend = ic(2)
    ierr = 0
    if ( (ibeg<0).or.(ibeg>4) ) ierr = ierr - 1
    if ( (iend<0).or.(iend>4) ) ierr = ierr - 2
    if ( ierr<0 ) then
        ! ic out of range return.
        ierr = ierr - 3
        call xermsg ('PCHIP', 'dpchsp', 'ic out of range', ierr, 1)
        return
    end if

    !  function definition is ok -- go on.

    if ( nwk < 2*n ) then
        ! nwk too small return.
        ierr = -7
        call xermsg ('PCHIP', 'dpchsp', 'work array too small', ierr, 1)
        return
    end if

    ! compute first differences of x sequence and store in wk(1,.). also,
    ! compute first divided difference of data and store in wk(2,.).
    do j=2,n
        wk(1,j) = x(j) - x(j-1)
        wk(2,j) = (f(1,j) - f(1,j-1))/wk(1,j)
    end do

    ! set to default boundary conditions if n is too small.

    if ( ibeg>n ) ibeg = 0
    if ( iend>n ) iend = 0

    ! set up for boundary conditions.

    if ( (ibeg==1).or.(ibeg==2) ) then
        d(1,1) = vc(1)
    else if (ibeg > 2) then
        ! pick up first ibeg points, in reverse order.
        do j = 1, ibeg
            index = ibeg-j+1
            ! index runs from ibeg down to 1.
            xtemp(j) = x(index)
            if (j < ibeg)  stemp(j) = wk(2,index)
        end do

        d(1,1) = dpchdf (ibeg, xtemp, stemp, ierr)

        if (ierr /= 0) then
                ! error return from dpchdf.
                ! *** this case should never occur ***
                ierr = -9
                call xermsg ('PCHIP', 'dpchsp', 'error return from dpchdf', ierr, 1)
        end if
        ibeg = 1
    endif

    if ( (iend==1).or.(iend==2) ) then
        d(1,n) = vc(2)
    else if (iend > 2) then
        ! pick up last iend points.
        do j = 1, iend
            index = n-iend+j
            ! index runs from n+1-iend up to n.
            xtemp(j) = x(index)
            if (j < iend)  stemp(j) = wk(2,index+1)
        end do

        d(1,n) = dpchdf (iend, xtemp, stemp, ierr)

        if (ierr /= 0) then
                ! error return from dpchdf.
                ! *** this case should never occur ***
                ierr = -9
                call xermsg ('PCHIP', 'dpchsp', 'error return from dpchdf', ierr, 1)
        end if
        iend = 1
    endif

    ! --------------------( begin coding from cubspl )--------------------
    !
    !  **** a tridiagonal linear system for the unknown slopes s(j) of
    !  f  at x(j), j=1,...,n, is generated and then solved by gauss elim-
    !  ination, with s(j) ending up in d(1,j), all j.
    !     wk(1,.) and wk(2,.) are used for temporary storage.
    !
    !  construct first equation from first boundary condition, of the form
    !             wk(2,1)*s(1) + wk(1,1)*s(2) = d(1,1)

    if (ibeg == 0) then
        if (n == 2) then
            ! no condition at left end and n = 2.
            wk(2,1) = one
            wk(1,1) = one
            d(1,1) = two*wk(2,2)
        else
            ! not-a-knot condition at left end and n > 2.
            wk(2,1) = wk(1,3)
            wk(1,1) = wk(1,2) + wk(1,3)
            d(1,1) =((wk(1,2) + two*wk(1,1))*wk(2,2)*wk(1,3) + wk(1,2)**2*wk(2,3)) / wk(1,1)
        endif
    else if (ibeg == 1) then
        ! slope prescribed at left end.
        wk(2,1) = one
        wk(1,1) = zero
    else
        ! second derivative prescribed at left end.
        wk(2,1) = two
        wk(1,1) = one
        d(1,1) = three*wk(2,2) - half*wk(1,2)*d(1,1)
    endif

    ! if there are interior knots, generate the corresponding equations and
    ! carry out the forward pass of gauss elimination, after which the j-th
    ! equation reads `wk(2,j)*s(j) + wk(1,j)*s(j+1) = d(1,j)`.

    nm1 = n-1
    if (nm1 > 1) then
        do j=2,nm1
            if (wk(2,j-1) == zero) then
                ! singular system.
                ! *** theoretically, this can only occur if successive x-values   ***
                ! *** are equal, which should already have been caught (ierr=-3). ***
                ierr = -8
                call xermsg ('PCHIP', 'dpchsp', 'singular linear system', ierr, 1)
                return
            end if
            g = -wk(1,j+1)/wk(2,j-1)
            d(1,j) = g*d(1,j-1) + three*(wk(1,j)*wk(2,j+1) + wk(1,j+1)*wk(2,j))
            wk(2,j) = g*wk(1,j-1) + two*(wk(1,j) + wk(1,j+1))
        end do
    endif

    ! construct last equation from second boundary condition, of the form
    !       (-g*wk(2,n-1))*s(n-1) + wk(2,n)*s(n) = d(1,n)
    !
    ! if slope is prescribed at right end, one can go directly to back-
    ! substitution, since arrays happen to be set up just right for it
    ! at this point.
    if (iend /= 1) then

        forward = .true.

        if (iend == 0) then
            if (n==2 .and. ibeg==0) then
                ! not-a-knot at right endpoint and at left endpoint and n = 2.
                d(1,2) = wk(2,2)
                forward = .false.
            else if ((n==2) .or. (n==3 .and. ibeg==0)) then
                ! either (n=3 and not-a-knot also at left) or (n=2 and *not*
                ! not-a-knot at left end point).
                d(1,n) = two*wk(2,n)
                wk(2,n) = one
                if (wk(2,n-1) == zero) then
                    ! singular system.
                    ! *** theoretically, this can only occur if successive x-values   ***
                    ! *** are equal, which should already have been caught (ierr=-3). ***
                    ierr = -8
                    call xermsg ('PCHIP', 'dpchsp', 'singular linear system', ierr, 1)
                    return
                end if
                g = -one/wk(2,n-1)
            else
                ! not-a-knot and n >= 3, and either n>3 or  also not-a-
                ! knot at left end point.
                g = wk(1,n-1) + wk(1,n)
                ! do not need to check following denominators (x-differences).
                d(1,n) = ((wk(1,n)+two*g)*wk(2,n)*wk(1,n-1) &
                            + wk(1,n)**2*(f(1,n-1)-f(1,n-2))/wk(1,n-1))/g
                if (wk(2,n-1) == zero) then
                    ! singular system.
                    ! *** theoretically, this can only occur if successive x-values   ***
                    ! *** are equal, which should already have been caught (ierr=-3). ***
                    ierr = -8
                    call xermsg ('PCHIP', 'dpchsp', 'singular linear system', ierr, 1)
                    return
                end if
                g = -g/wk(2,n-1)
                wk(2,n) = wk(1,n-1)
            endif
        else
            ! second derivative prescribed at right endpoint.
            d(1,n) = three*wk(2,n) + half*wk(1,n)*d(1,n)
            wk(2,n) = two
            if (wk(2,n-1) == zero) then
                ! singular system.
                ! *** theoretically, this can only occur if successive x-values   ***
                ! *** are equal, which should already have been caught (ierr=-3). ***
                ierr = -8
                call xermsg ('PCHIP', 'dpchsp', 'singular linear system', ierr, 1)
                return
            end if
            g = -one/wk(2,n-1)
        endif

        ! complete forward pass of gauss elimination.
        if (forward) then
            wk(2,n) = g*wk(1,n-1) + wk(2,n)
            if (wk(2,n) == zero) then
                ! singular system.
                ! *** theoretically, this can only occur if successive x-values   ***
                ! *** are equal, which should already have been caught (ierr=-3). ***
                ierr = -8
                call xermsg ('PCHIP', 'dpchsp', 'singular linear system', ierr, 1)
                return
            end if
            d(1,n) = (g*d(1,n-1) + d(1,n))/wk(2,n)
        end if

    end if

    ! carry out back substitution
    do j=nm1,1,-1
        if (wk(2,j) == zero) then
                ! singular system.
                ! *** theoretically, this can only occur if successive x-values   ***
                ! *** are equal, which should already have been caught (ierr=-3). ***
                ierr = -8
                call xermsg ('PCHIP', 'dpchsp', 'singular linear system', ierr, 1)
                return
        end if
        d(1,j) = (d(1,j) - wk(1,j)*d(1,j+1))/wk(2,j)
    end do

    end subroutine dpchsp