splcw Subroutine

private subroutine splcw(me, ndim, xdata, l1xdat, ydata, wdata, ndata, xmin, xmax, nodes, xtrap, coef, ncf, work, nwrk, ierror)

N-dimensional cubic spline coefficient calculation by weighted least squares on arbitrarily located data.

The spline (or its derivatives) may then be evaluated by using function SPLFE (or SPLDE).

A grid of evenly spaced nodes in NDIM space is defined by the arguments XMIN, XMAX and NODES. A linear basis for the class of natural splines on these nodes is formed, and a set of corresponding coefficients is computed in the array COEF. These coefficients are chosen to minimize the weighted sum of squared errors between the spline and the arbitrarily located data values described by the arguments XDATA, YDATA and NDATA. The smoothness of the spline in data sparse areas is controlled by the argument XTRAP.

Note

In order to understand the arguments of this routine, one should realize that the node grid need not bear any particular relation to the data points. In the theory of exact-fit interpolatory splines, the nodes would in fact be data locations, but in this case they serve only to define the class of splines from which the approximating function is chosen. This node grid is a rectangular arrangement of points in NDIM space, with the restriction that along any coordinate direction the nodes are equally spaced. The class of natural splines on this grid of nodes (NDIM-cubic splines whose 2nd derivatives normal to the boundaries are 0) has as many degrees of freedom as the grid has nodes. Thus the smoothness or flexibility of the splines is determined by the choice of the node grid.

Algorithm

An overdetermined system of linear equations is formed -- one equation for each data point plus equations for derivative constraints. This system is solved using subroutine suprls.

Accuracy

If there is exactly one data point in the near vicinity of each node and no extra data, the resulting spline will agree with the data values to machine accuracy. However, if the problem is overdetermined or the sparse data option is utilized, the accuracy is hard to predict. Basically, smooth functions require fewer nodes than rough ones for the same accuracy.

Timing

The execution time is roughly proportional to NDATA*NCOF**2 where NCOF = NODES(1)*...*NODES(NDIM).

Type Bound

splpak_type

Arguments

Type IntentOptional Attributes Name
class(splpak_type), intent(inout) :: me
integer, intent(in) :: ndim

The dimensionality of the problem. The spline is a function of NDIM variables or coordinates and thus a point in the independent variable space is an NDIM vector. NDIM must be >= 1.

real(kind=wp), intent(in) :: xdata(l1xdat,ndata)

A collection of locations for the data values, i.e., points from the independent variable space. This collection is a 2-dimensional array whose 1st dimension indexes the NDIM coordinates of a given point and whose 2nd dimension labels the data point. For example, the data point with label IDATA is located at the point (XDATA(1,IDATA),...,XDATA(NDIM,IDATA)) where the elements of this vector are the values of the NDIM coordinates. The location, number and ordering of the data points is arbitrary. The dimension of XDATA is assumed to be XDATA(L1XDAT,NDATA).

integer, intent(in) :: l1xdat

The length of the 1st dimension of XDATA in the calling program. L1XDAT must be >= NDIM.

Note:

For 1-dimensional problems L1XDAT is usually 1.

real(kind=wp), intent(in) :: ydata(ndata)

A collection of data values corresponding to the points in XDATA. YDATA(IDATA) is the data value associated with the point (XDATA(1,IDATA),...,XDATA(NDIM,IDATA)) in the independent variable space. The spline whose coefficients are computed by this routine approximates these data values in the least squares sense. The dimension is assumed to be YDATA(NDATA).

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

A collection of weights. WDATA(IDATA) is a weight associated with the data point labelled IDATA. It should be non-negative, but may be of any magnitude. The weights have the effect of forcing greater or lesser accuracy at a given point as follows: this routine chooses coefficients to minimize the sum over all data points of the quantity

   (WDATA(IDATA)*(YDATA(IDATA) ! spline value at XDATA(IDATA)))**2.

Thus, if the reliability of a data point is known to be low, the corresponding weight may be made small (relative to the other weights) so that the sum over all data points is affected less by discrepencies at the unreliable point. Data points with zero weight are completely ignored.

Note:

If WDATA(1) is < 0, the other elements of WDATA are not referenced, and all weights are assumed to be unity.

The dimension is assumed to be WDATA(NDATA) unless WDATA(1) < 0., in which case the dimension is assumed to be 1.

integer, intent(in) :: ndata

The number of data points mentioned in the above arguments.

real(kind=wp), intent(in) :: xmin(ndim)

A vector describing the lower extreme corner of the node grid. A set of evenly spaced nodes is formed along each coordinate axis and XMIN(IDIM) is the location of the first node along the IDIM axis. The dimension is assumed to be XMIN(NDIM).

real(kind=wp), intent(in) :: xmax(ndim)

A vector describing the upper extreme corner of the node grid. A set of evenly spaced nodes is formed along each coordinate axis and XMAX(IDIM) is the location of the last node along the IDIM axis. The dimension is assumed to be XMAX(NDIM).

integer, intent(in) :: nodes(ndim)

A vector of integers describing the number of nodes along each axis. NODES(IDIM) is the number of nodes (counting endpoints) along the IDIM axis and determines the flexibility of the spline in that coordinate direction. NODES(IDIM) must be >= 4, but may be as large as the arrays COEF and WORK allow. The dimension is assumed to be NODES(NDIM).

Note:

The node grid is completely defined by the arguments XMIN, XMAX and NODES. The spacing of this grid in the IDIM coordinate direction is:

   DX(IDIM) = (XMAX(IDIM)-XMIN(IDIM)) / (NODES(IDIM)-1).

A node in this grid may be indexed by an NDIM vector of integers (IN(1),...,IN(NDIM)) where 1 <= IN(IDIM) <= NODES(IDIM). The location of such a node may be represented by an NDIM vector (X(1),...,X(NDIM)) where X(IDIM) = XMIN(IDIM) + (IN(IDIM)-1) * DX(IDIM).

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

A parameter to control extrapolation to data sparse areas. The region described by XMIN and XMAX is divided into rectangles, the number of which is determined by NODES, and any rectangle containing a disproportionately small number of data points is considered to be data sparse (rectangle is used here to mean NDIM-dimensional rectangle). If XTRAP is nonzero the least squares problem is augmented with derivative constraints in the data sparse areas to prevent the matrix from becoming poorly conditioned. XTRAP serves as a weight for these constraints, and thus may be used to control smoothness in data sparse areas. Experience indicates that unity is a good first guess for this parameter.

Note:

If XTRAP is zero, substantial portions of the routine will be skipped, but a singular matrix can result if large portions of the region are without data.

real(kind=wp), intent(out) :: coef(ncf)

The array of coefficients computed by this routine. Each coefficient corresponds to a particular basis function which in turn corresponds to a node in the node grid. This correspondence between the node grid and the array COEF is as if COEF were an NDIM-dimensional Fortran array with dimensions NODES(1),...,NODES(NDIM), i.e., to store the array linearly, the leftmost indices are incremented most frequently. Hence the length of the COEF array must equal or exceed the total number of nodes, which is NODES(1)*...*NODES(NDIM). The computed array COEF may be used with function SPLFE (or SPLDE) to evaluate the spline (or its derivatives) at an arbitrary point in NDIM space. The dimension is assumed to be COEF(NCF).

integer, intent(in) :: ncf

The length of the array COEF in the calling program. If NCF is < NODES(1)*...*NODES(NDIM), a fatal error is diagnosed.

real(kind=wp) :: work(nwrk)

A workspace array for solving the least squares matrix generated by this routine. Its required size is a function of the total number of nodes in the node grid. This total, NCOL = NODES(1)*...*NODES(NDIM), is also the number of columns in the least squares matrix. The length of the array WORK must equal or exceed NCOL*(NCOL+1).

integer, intent(in) :: nwrk

The length of the array WORK in the calling program. If NCOL = NODES(1)*...*NODES(NDIM) is the total number of nodes, then a fatal error is diagnosed if NWRK is less than NCOL*(NCOL+1).

integer, intent(out) :: ierror

An error flag with the following meanings:

  • 0 No error.
  • 101 NDIM is < 1.
  • 102 NODES(IDIM) is < 4 for some IDIM.
  • 103 XMIN(IDIM) = XMAX(IDIM) for some IDIM.
  • 104 NCF (size of COEF) is < NODES(1)*...*NODES(NDIM).
  • 105 NDATA is < 1.
  • 106 NWRK (size of WORK) is too small.
  • 107 suprls failure (usually insufficient data) -- ordinarily occurs only if XTRAP is zero or WDATA contains all zeros.

Calls

proc~~splcw~~CallsGraph proc~splcw splpak_module::splpak_type%splcw proc~bascmp splpak_module::splpak_type%bascmp proc~splcw->proc~bascmp proc~cfaerr splpak_module::cfaerr proc~splcw->proc~cfaerr proc~destroy_splpak splpak_module::splpak_type%destroy_splpak proc~splcw->proc~destroy_splpak proc~suprls splpak_module::splpak_type%suprls proc~splcw->proc~suprls proc~suprls->proc~cfaerr

Called by

proc~~splcw~~CalledByGraph proc~splcw splpak_module::splpak_type%splcw proc~splcc splpak_module::splpak_type%splcc proc~splcc->proc~splcw

Source Code

subroutine splcw(me,ndim,xdata,l1xdat,ydata,wdata,ndata,xmin,xmax, &
                 nodes,xtrap,coef,ncf,work,nwrk,ierror)

    class(splpak_type),intent(inout) :: me
    integer,intent(in) :: ndim !! The dimensionality of the problem.  The
                               !! spline is a function of `NDIM` variables or
                               !! coordinates and thus a point in the
                               !! independent variable space is an `NDIM` vector.
                               !! `NDIM` must be `>= 1`.
    integer,intent(in) :: l1xdat !! The length of the 1st dimension of `XDATA` in
                                 !! the calling program.  `L1XDAT` must be `>= NDIM`.
                                 !!
                                 !!#### Note:
                                 !! For 1-dimensional problems `L1XDAT` is usually 1.
    integer,intent(in) :: ncf !! The length of the array `COEF` in the calling
                              !! program.  If `NCF` is `< NODES(1)*...*NODES(NDIM)`,
                              !! a fatal error is diagnosed.
    integer,intent(in) :: nwrk !! The length of the array `WORK` in the calling
                               !! program.  If
                               !! `NCOL = NODES(1)*...*NODES(NDIM)` is the total
                               !! number of nodes, then a fatal error is
                               !! diagnosed if `NWRK` is less than
                               !! `NCOL*(NCOL+1)`.
    integer,intent(in) :: ndata !! The number of data points mentioned in the
                                !! above arguments.
    real(wp),intent(in) :: xdata(l1xdat,ndata) !! A collection of locations for the data
                                               !! values, i.e., points from the independent
                                               !! variable space.  This collection is a
                                               !! 2-dimensional array whose 1st dimension
                                               !! indexes the `NDIM` coordinates of a given point
                                               !! and whose 2nd dimension labels the data
                                               !! point.  For example, the data point with
                                               !! label `IDATA` is located at the point
                                               !! `(XDATA(1,IDATA),...,XDATA(NDIM,IDATA))` where
                                               !! the elements of this vector are the values of
                                               !! the `NDIM` coordinates.  The location, number
                                               !! and ordering of the data points is arbitrary.
                                               !! The dimension of `XDATA` is assumed to be
                                               !! `XDATA(L1XDAT,NDATA)`.
    real(wp),intent(in) :: ydata(ndata) !! A collection of data values corresponding to
                                        !! the points in `XDATA`.  `YDATA(IDATA)` is the
                                        !! data value associated with the point
                                        !! `(XDATA(1,IDATA),...,XDATA(NDIM,IDATA))` in the
                                        !! independent variable space.  The spline whose
                                        !! coefficients are computed by this routine
                                        !! approximates these data values in the least
                                        !! squares sense.  The dimension is assumed to be
                                        !! `YDATA(NDATA)`.
    real(wp),intent(in) :: wdata(:) !! A collection of weights.  `WDATA(IDATA)` is a
                                    !! weight associated with the data point
                                    !! labelled `IDATA`.  It should be non-negative,
                                    !! but may be of any magnitude.  The weights
                                    !! have the effect of forcing greater or lesser
                                    !! accuracy at a given point as follows: this
                                    !! routine chooses coefficients to minimize the
                                    !! sum over all data points of the quantity
                                    !!```fortran
                                    !!   (WDATA(IDATA)*(YDATA(IDATA) ! spline value at XDATA(IDATA)))**2.
                                    !!```
                                    !! Thus, if the reliability
                                    !! of a data point is known to be low, the
                                    !! corresponding weight may be made small
                                    !! (relative to the other weights) so that the
                                    !! sum over all data points is affected less by
                                    !! discrepencies at the unreliable point.  Data
                                    !! points with zero weight are completely
                                    !! ignored.
                                    !!
                                    !!#### Note:
                                    !! If `WDATA(1)` is `< 0`, the other
                                    !! elements of `WDATA` are not
                                    !! referenced, and all weights are
                                    !! assumed to be unity.
                                    !!
                                    !! The dimension is assumed to be `WDATA(NDATA)`
                                    !! unless `WDATA(1) < 0.`, in which case the
                                    !! dimension is assumed to be 1.
    real(wp),intent(in) :: xmin(ndim) !! A vector describing the lower extreme corner
                                      !! of the node grid.  A set of evenly spaced
                                      !! nodes is formed along each coordinate axis
                                      !! and `XMIN(IDIM)` is the location of the first
                                      !! node along the `IDIM` axis.  The dimension is
                                      !! assumed to be `XMIN(NDIM)`.
    real(wp),intent(in) :: xmax(ndim) !! A vector describing the upper extreme corner
                                      !! of the node grid.  A set of evenly spaced
                                      !! nodes is formed along each coordinate axis
                                      !! and `XMAX(IDIM)` is the location of the last
                                      !! node along the `IDIM` axis.  The dimension is
                                      !! assumed to be `XMAX(NDIM)`.
    real(wp),intent(in) :: xtrap !! A parameter to control extrapolation to data
                                 !! sparse areas.  The region described by `XMIN`
                                 !! and `XMAX` is divided into rectangles, the
                                 !! number of which is determined by `NODES`, and
                                 !! any rectangle containing a disproportionately
                                 !! small number of data points is considered to
                                 !! be data sparse (rectangle is used here to
                                 !! mean `NDIM`-dimensional rectangle).  If `XTRAP`
                                 !! is nonzero the least squares problem is
                                 !! augmented with derivative constraints in the
                                 !! data sparse areas to prevent the matrix from
                                 !! becoming poorly conditioned.  `XTRAP` serves as
                                 !! a weight for these constraints, and thus may
                                 !! be used to control smoothness in data sparse
                                 !! areas.  Experience indicates that unity is a
                                 !! good first guess for this parameter.
                                 !!
                                 !!#### Note:
                                 !! If `XTRAP` is zero, substantial
                                 !! portions of the routine will be
                                 !! skipped, but a singular matrix
                                 !! can result if large portions of
                                 !! the region are without data.
    integer,intent(in) :: nodes(ndim) !! A vector of integers describing the number of
                                      !! nodes along each axis.  `NODES(IDIM)` is the
                                      !! number of nodes (counting endpoints) along
                                      !! the `IDIM` axis and determines the flexibility
                                      !! of the spline in that coordinate direction.
                                      !! `NODES(IDIM)` must be `>= 4`, but may be as
                                      !! large as the arrays `COEF` and `WORK` allow.
                                      !! The dimension is assumed to be `NODES(NDIM)`.
                                      !!
                                      !!#### Note:
                                      !! The node grid is completely defined by
                                      !! the arguments `XMIN`, `XMAX` and `NODES`.
                                      !! The spacing of this grid in the `IDIM`
                                      !! coordinate direction is:
                                      !!```fortran
                                      !!   DX(IDIM) = (XMAX(IDIM)-XMIN(IDIM)) / (NODES(IDIM)-1).
                                      !!```
                                      !! A node in this grid may be indexed by
                                      !! an `NDIM` vector of integers
                                      !! `(IN(1),...,IN(NDIM))` where
                                      !! `1 <= IN(IDIM) <= NODES(IDIM)`.
                                      !! The location of such a node may be
                                      !! represented by an `NDIM` vector
                                      !! `(X(1),...,X(NDIM))` where
                                      !! `X(IDIM) = XMIN(IDIM) + (IN(IDIM)-1) * DX(IDIM)`.
    real(wp) :: work(nwrk) !! A workspace array for solving the least
                           !! squares matrix generated by this routine.
                           !! Its required size is a function of the total
                           !! number of nodes in the node grid.  This
                           !! total, `NCOL = NODES(1)*...*NODES(NDIM)`, is
                           !! also the number of columns in the least
                           !! squares matrix.  The length of the array `WORK`
                           !! must equal or exceed `NCOL*(NCOL+1)`.
    real(wp),intent(out) :: coef(ncf) !! The array of coefficients computed by this
                                      !! routine.  Each coefficient corresponds to a
                                      !! particular basis function which in turn
                                      !! corresponds to a node in the node grid.  This
                                      !! correspondence between the node grid and the
                                      !! array `COEF` is as if `COEF` were an
                                      !! `NDIM`-dimensional Fortran array with
                                      !! dimensions `NODES(1),...,NODES(NDIM)`, i.e., to
                                      !! store the array linearly, the leftmost
                                      !! indices are incremented most frequently.
                                      !! Hence the length of the `COEF` array must equal
                                      !! or exceed the total number of nodes, which is
                                      !! `NODES(1)*...*NODES(NDIM)`.  The computed array
                                      !! `COEF` may be used with function [[SPLFE]]
                                      !! (or [[SPLDE]]) to evaluate the spline (or its
                                      !! derivatives) at an arbitrary point in `NDIM`
                                      !! space.  The dimension is assumed to be `COEF(NCF)`.
    integer,intent(out) :: ierror !! An error flag with the following meanings:
                                  !!
                                  !! * `  0`  No error.
                                  !! * `101`  `NDIM` is < 1.
                                  !! * `102`  `NODES(IDIM)` is < 4 for some `IDIM`.
                                  !! * `103`  `XMIN(IDIM) = XMAX(IDIM)` for some `IDIM`.
                                  !! * `104`  `NCF` (size of `COEF`) is `< NODES(1)*...*NODES(NDIM)`.
                                  !! * `105`  `NDATA` is `< 1`.
                                  !! * `106`  `NWRK` (size of `WORK`) is too small.
                                  !! * `107`  [[suprls]] failure (usually insufficient
                                  !!   data) -- ordinarily occurs only if
                                  !!   `XTRAP` is zero or `WDATA` contains all
                                  !!   zeros.

    real(wp),dimension(:),allocatable :: x
    integer,dimension(:),allocatable :: nderiv,in,inmx
    real(wp) :: xrng,swght,rowwt,rhs,basm,reserr,totlwt,&
                bump,wtprrc,expect,dcwght
    integer :: ncol,idim,nod,nwrk1,mdata,nwlft,irow,idata,&
               icol,it,lserr,iin,nrect,idimc,idm,jdm,inidim
    logical :: boundary

    real(wp),parameter :: spcrit = 0.75_wp
        !! SPCRIT is used to determine data sparseness as follows -
        !! the weights assigned to all data points are totaled into the
        !! variable TOTLWT. (If no weights are entered, it is set to
        !! NDATA.)  Each node of the node network is assigned a
        !! rectangle (in which it is contained) and the weights of all
        !! data points which fall in that rectangle are totaled.  If that
        !! total is less than SPCRIT*EXPECT (EXPECT is defined below),
        !! then the node is ascertained to be in a data sparse location.
        !! EXPECT is that fraction of TOTLWT that would be expected by
        !! comparing the area of the rectangle with the total area under
        !! consideration.

    ! size the arrays:
    call me%destroy(ndim)
    allocate(x(ndim))
    allocate(nderiv(ndim))
    allocate(in(ndim))
    allocate(inmx(ndim))

    ierror = 0
    me%mdim = ndim
    if (me%mdim<1) then
        ierror = 101
        call cfaerr(ierror, &
            ' splcc or splcw - NDIM is less than 1')
        return
    end if

    ncol = 1
    do idim = 1,me%mdim
        nod = nodes(idim)
        if (nod<4) then
            ierror = 102
            call cfaerr(ierror, &
                ' splcc or splcw - NODES(IDIM) is less than 4 for some IDIM')
            return
        end if

        !  Number of columns in least squares matrix = number of coefficients =
        !  product of nodes over all dimensions.
        ncol = ncol*nod
        xrng = xmax(idim) - xmin(idim)
        if (xrng==0.0_wp) then
            ierror = 103
            call cfaerr(ierror, &
                ' splcc or splcw - XMIN(IDIM) equals XMAX(IDIM) for some IDIM')
            return
        end if

        !  DX(IDIM) is the node spacing along the IDIM coordinate.
        me%dx(idim) = xrng/real(nod-1,wp)
        me%dxin(idim) = 1.0_wp/me%dx(idim)
        nderiv(idim) = 0
    end do
    if (ncol>ncf) then
        ierror = 104
        call cfaerr(ierror, &
            ' splcc or splcw - NCF (size of COEF) is too small')
        return
    end if
    nwrk1 = 1
    mdata = ndata
    if (mdata<1) then
        ierror = 105
        call cfaerr(ierror, &
            ' splcc or splcw - Ndata Is less than 1')
        return
    end if

    !  SWGHT is a local variable = XTRAP, and can be considered a smoothing
    !  weight for data sparse areas.  If SWGHT == 0, no smoothing
    !  computations are performed.
    swght = xtrap

    !  Set aside workspace for counting data points.
    if (swght/=0.0_wp) nwrk1 = ncol + 1

    !  NWLFT is the length of the remaining workspace.
    nwlft = nwrk - nwrk1 + 1
    if (nwlft<1) then
        ierror = 106
        call cfaerr(ierror, &
            ' splcc or splcw - NWRK (size of WORK) is too small')
        return
    end if
    irow = 0

    !  ROWWT is used to weight rows of the least squares matrix.
    rowwt = 1.0_wp

    !  Loop through all data points, computing a row for each.
    do idata = 1,mdata

        !  WDATA(1)<0 means weights have not been entered.  In that case,
        !  ROWWT is left equal to  1. for all points.  Otherwise ROWWT is
        !  equal to WDATA(IDATA).
        !
        !  Every element of the row, as well as the corresponding right hand
        !  side, is multiplied by ROWWT.
        if (wdata(1)>=0.0_wp) then
            rowwt = wdata(idata)
            !  Data points with 0 weight are ignored.
            if (rowwt==0.0_wp) cycle
        end if
        irow = irow + 1

        !  One row of the least squares matrix corresponds to each data
        !  point.  The right hand for that row will correspond to the
        !  function value YDATA at that point.
        rhs = rowwt*ydata(idata)
        do idim = 1,me%mdim
            x(idim) = xdata(idim,idata)
        end do

        !  The COEF array serves as a row of least squares matrix.
        !  Its value is zero except for columns corresponding to functions
        !  which are nonzero at X.
        do icol = 1,ncol
            coef(icol) = 0.0_wp
        end do

        !  Compute the indices of basis functions which are nonzero at X.
        !  IBMN is in the range 0 to nodes-2 and IBMX is in range 1
        !  to NODES-1.
        do idim = 1,me%mdim
            nod = nodes(idim)
            it = me%dxin(idim)* (x(idim)-xmin(idim))
            me%ibmn(idim) = min(max(it-1,0),nod-2)
            me%ib(idim) = me%ibmn(idim)
            me%ibmx(idim) = max(min(it+2,nod-1),1)
        end do

        basis_index : do
            !  Begining of basis index loop - traverse all indices corresponding
            !  to basis functions which are nonzero at X.  The indices are in
            !  IB and are passed through common to BASCMP.
            call me%bascmp(x,nderiv,xmin,nodes,icol,basm)

            !  BASCMP computes ICOL and BASM where BASM is the value at X of
            !  the N-dimensional basis function corresponding to column ICOL.
            coef(icol) = rowwt*basm

            !  Increment the basis indices.
            do idim = 1,me%mdim
                me%ib(idim) = me%ib(idim) + 1
                if (me%ib(idim)<=me%ibmx(idim)) cycle basis_index
                me%ib(idim) = me%ibmn(idim)
            end do
            exit basis_index !  End of basis index loop.
        end do basis_index

        !  Send a row of the least squares matrix to the reduction routine.
        call me%suprls(irow,coef,ncol,rhs,work(nwrk1),nwlft,coef,reserr,lserr)
        if (lserr/=0) then
            ierror = 107
            call cfaerr(ierror, ' splcc or splcw - suprls failure '//&
                                '(this usually indicates insufficient input data)')
        end if
    end do

    !  Row computations for all data points are now complete.
    !
    !  If SWGHT==0, the least squares matrix is complete and no
    !  smoothing rows are computed.

    if (swght/=0.0_wp) then

        !  Initialize smoothing computations for data sparse areas.
        !  Derivative constraints will always have zero right hand side.
        rhs = 0.0_wp
        nrect = 1

        !  Initialize the node indices and compute number of rectangles
        !  formed by the node network.
        do idim = 1,me%mdim
            in(idim) = 0
            inmx(idim) = nodes(idim) - 1
            nrect = nrect*inmx(idim)
        end do

        !  Every node is assigned an element of the workspace (set aside
        !  previously) in which data points are counted.
        do iin = 1,ncol
            work(iin) = 0.0_wp
        end do

        !  Assign each data point to a node, total the assignments for
        !  each node, and save in the workspace.
        totlwt = 0.0_wp
        do idata = 1,mdata

            ! BUMP is the weight associated with the data point.
            bump = 1.0_wp
            if (wdata(1)>=0.0_wp) bump = wdata(idata)
            if (bump==0.0_wp) cycle

            ! Find the nearest node.
            iin = 0
            do idimc = 1,me%mdim
                idim = me%mdim + 1 - idimc
                inidim = int(me%dxin(idim)* (xdata(idim,idata)-xmin(idim))+0.5_wp)
                ! Points not in range (+ or - 1/2 node spacing) are not counted.
                if (inidim<0 .or. inidim>inmx(idim)) cycle
                ! Compute linear address of node in workspace by Horner's method.
                iin = (inmx(idim)+1)*iin + inidim
            end do

            ! Bump counter for that node.
            work(iin+1) = work(iin+1) + bump
            totlwt = totlwt + bump
        end do

        ! Compute the expected weight per rectangle.
        wtprrc = totlwt/real(nrect,wp)

        !  IN contains indices of the node (previously initialized).
        !  IIN will be the linear address of the node in the workspace.
        iin = 0

        !  Loop through all nodes, computing derivative constraint rows
        !  for those in data sparse locations.
        !
        !  Begining of node index loop - traverse all node indices.
        !  The indices are in IN.
        node_index : do
            iin = iin + 1
            expect = wtprrc

            !  Rectangles at edge of network are smaller and hence less weight
            !  should be expected.
            do idim = 1,me%mdim
                if (in(idim)==0 .or. in(idim)==inmx(idim)) expect = 0.5_wp*expect
            end do

            !  The expected weight minus the actual weight serves to define
            !  data sparseness and is also used to weight the derivative
            !  constraint rows.
            !
            !  There is no constraint if not data sparse.
            if (work(iin)<spcrit*expect) then

                dcwght = expect - work(iin)
                do idim = 1,me%mdim
                    inidim = in(idim)

                    !  Compute the location of the node.
                    x(idim) = xmin(idim) + real(inidim,wp)*me%dx(idim)

                    !  Compute the indices of the basis functions which are non-zero
                    !  at the node.
                    me%ibmn(idim) = inidim - 1
                    me%ibmx(idim) = inidim + 1

                    !  Distinguish the boundaries.
                    if (inidim==0) me%ibmn(idim) = 0
                    if (inidim==inmx(idim)) me%ibmx(idim) = inmx(idim)

                    !  Initialize the basis indices.
                    me%ib(idim) = me%ibmn(idim)
                end do

                !  Multiply by the extrapolation parameter (this acts as a
                !  smoothing weight).
                dcwght = swght*dcwght

                !  The COEF array serves as a row of the least squares matrix.
                !  Its value is zero except for columns corresponding to functions
                !  which are non-zero at the node.
                do icol = 1,ncol
                    coef(icol) = 0.0_wp
                end do

                !  The 2nd derivative of a function of MDIM variables may be thought
                !  of as a symmetric MDIM x MDIM matrix of 2nd order partial
                !  derivatives.  Traverse the upper triangle of this matrix and,
                !  for each element, compute a row of the least squares matrix.

                do idm = 1,me%mdim
                    do jdm = idm,me%mdim
                        do idim = 1,me%mdim
                            nderiv(idim) = 0
                        end do

                        boundary = .true.
                        !  Off-diagonal elements appear twice by symmetry, so the corresponding
                        !  row is weighted by a factor of 2.
                        rowwt = 2.0_wp*dcwght
                        if (jdm==idm) then
                            !  Diagonal.
                            rowwt = dcwght
                            nderiv(jdm) = 2
                            if (in(idm)/=0 .and. in(idm)/=inmx(idm)) then
                                boundary = .false.
                            end if
                        end if
                        if (boundary) then
                            !  Node is at boundary.
                            !
                            !  Normal 2nd derivative constraint at boundary is not appropriate for
                            !  natural splines (2nd derivative 0 by definition).  Substitute
                            !  a 1st derivative constraint.
                            nderiv(idm) = 1
                            nderiv(jdm) = 1
                        end if
                        irow = irow + 1

                        basis : do
                            !  Begining of basis index loop - traverse all indices corresponding
                            !  to basis functions which are non-zero at X.
                            !  The indices are in IB and are passed through common to BASCMP.
                            call me%bascmp(x,nderiv,xmin,nodes,icol,basm)

                            !  BASCMP computes ICOL and BASM where BASM is the value at X of the
                            !  N-dimensional basis function corresponding to column ICOL.
                            coef(icol) = rowwt*basm

                            !  Increment the basis indices.
                            do idim = 1,me%mdim
                                me%ib(idim) = me%ib(idim) + 1
                                if (me%ib(idim)<=me%ibmx(idim)) cycle basis
                                me%ib(idim) = me%ibmn(idim)
                            end do

                            !  End of basis index loop.
                            exit basis
                        end do basis

                        !  Send row of least squares matrix to reduction routine.
                        call me%suprls(irow,coef,ncol,rhs,work(nwrk1),nwlft,coef,reserr,lserr)
                        if (lserr/=0) then
                            ierror = 107
                            call cfaerr(ierror, &
                                ' splcc or splcw - suprls failure '//&
                                '(this usually indicates insufficient input data)')
                        end if
                    end do
                end do

            end if

            !  Increment node indices.
            do idim = 1,me%mdim
                in(idim) = in(idim) + 1
                if (in(idim)<=inmx(idim)) cycle node_index
                in(idim) = 0
            end do

            exit node_index !  End of node index loop.

        end do node_index

    end if

    !  Call for least squares solution in COEF array.
    irow = 0
    call me%suprls(irow,coef,ncol,rhs,work(nwrk1),nwlft,coef,reserr,lserr)
    if (lserr/=0) then
        ierror = 107
        call cfaerr(ierror, &
            ' splcc or splcw - suprls failure '//&
            '(this usually indicates insufficient input data)')
    end if

end subroutine splcw