V3FIT
mkspline.f
1  subroutine mkspline(x,nx,fspl,ibcxmin,bcxmin,ibcxmax,bcxmax,
2  > ilinx,ier)
3 C
4 C make a 2-coefficient 1d spline
5 C
6 C only 2 coefficients, the data and its 2nd derivative, are needed to
7 C fully specify a spline. See e.g. Numerical Recipies in Fortran-77
8 C (2nd edition) chapter 3, section on cubic splines.
9 C
10 C input:
11  integer nx ! no. of data points
12  real x(nx) ! x axis data, strict ascending order
13 C
14 C input/output:
15  real fspl(2,nx) ! f(1,*): data in; f(2,*): coeffs out
16 C
17 C f(1,j) = f(x(j)) on input (unchanged on output)
18 C f(2,j) = f''(x(j)) (of interpolating spline) (on output).
19 C
20 C ...boundary conditions...
21 C
22 C input:
23 C
24  integer ibcxmin ! b.c. flag @ x=xmin=x(1)
25  real bcxmin ! b.c. data @xmin
26 C
27  integer ibcxmax ! b.c. flag @ x=xmax=x(nx)
28  real bcxcmax ! b.c. data @xmax
29 C
30 C ibcxmin=-1 -- periodic boundary condition
31 C (bcxmin,ibcxmax,bcxmax are ignored)
32 C
33 C the output spline s satisfies
34 C s'(x(1))=s'(x(nx)) ..and.. s''(x(1))=s''(x(nx))
35 C
36 C if non-periodic boundary conditions are used, then the xmin and xmax
37 C boundary conditions can be specified independently:
38 C
39 C ibcxmin (ibcxmax) = 0 -- this specifies a "not a knot" boundary
40 C condition, see "cubsplb.for". This is a common way
41 C for inferring a "good" spline boundary condition
42 C automatically from data in the vicinity of the
43 C boundary. ... bcxmin (bcxmax) are ignored.
44 C
45 C ibcxmin (ibcxmax) = 1 -- boundary condition is to have s'(x(1))
46 C ( s'(x(nx)) ) match the passed value bcxmin (bcxmax).
47 C
48 C ibcxmin (ibcxmax) = 2 -- boundary condition is to have s''(x(1))
49 C ( s''(x(nx)) ) match the passed value bcxmin (bcxmax).
50 C
51 C ibcxmin (ibcxmax) = 3 -- boundary condition is to have s'(x(1))=0.0
52 C ( s'(x(nx))=0.0 )
53 C
54 C ibcxmin (ibcxmax) = 4 -- boundary condition is to have s''(x(1))=0.0
55 C ( s''(x(nx))=0.0 )
56 C
57 C ibcxmin (ibcxmax) = 5 -- boundary condition is to have s'(x(1))
58 C ( s'(x(nx)) ) match the 1st divided difference
59 C e.g. at x(1): d(1)/h(1), where
60 C d(j)=f(1,j+1)-f(1,j)
61 C h(j)=x(j+1)-x(j)
62 C
63 C ibcxmin (ibcxmax) = 6 -- BC is to have s''(x(1)) ( s''(x(nx)) )
64 C match the 2nd divided difference
65 C e.g. at x(1):
66 C e(1) = [d(2)/h(2) - d(1)/h(1)]/(0.5*(h(1)+h(2)))
67 C
68 C ibcxmin (ibcxmax) = 7 -- BC is to have s'''(x(1)) ( s'''(x(nx)) )
69 C match the 3rd divided difference
70 C e.g. at x(1): [e(2)-e(1)]/(0.33333*(h(1)+h(2)+h(3)))
71 C
72 C output:
73 C
74  integer ilinx ! =1: hint, x axis is ~evenly spaced
75 C
76 C let dx[avg] = (x(nx)-x(1))/(nx-1)
77 C let dx[j] = x(j+1)-x(j), for all j satisfying 1.le.j.lt.nx
78 C
79 C if for all such j, abs(dx[j]-dx[avg]).le.(1.0e-3*dx[avg]) then
80 C ilinx=1 is returned, indicating the data is (at least nearly)
81 C evenly spaced. Even spacing is useful, for speed of zone lookup,
82 C when evaluating a spline.
83 C
84 C if the even spacing condition is not satisfied, ilinx=2 is returned.
85 C
86  integer ier ! exit code, 0=OK
87 C
88 C an error code is returned if the x axis is not strict ascending,
89 C or if nx.lt.4, or if an invalid boundary condition specification was
90 C input.
91 C
92 C------------------------------------
93 C
94 C this routine calls traditional 4 coefficient spline software, and
95 C translates the result to 2 coefficient form.
96 C
97 C this could be done more efficiently but we decided out of conservatism
98 C to use the traditional software.
99 C
100 C------------------------------------
101 C workspaces -- f90 dynamically allocated
102 C
103  real, dimension(:,:), allocatable :: fspl4 ! traditional 4-spline
104  real, dimension(:), allocatable :: wk ! cspline workspace
105 C
106 C------------------------------------
107 C
108  allocate(fspl4(4,nx),wk(nx))
109 C
110 C make the traditional call
111 C
112  do i=1,nx
113  fspl4(1,i)=fspl(1,i)
114  fspl(2,i)=0.0 ! for now
115  enddo
116 C
117  inwk=nx
118 C
119 C boundary conditions imposed by cspline...
120 C
121  call cspline(x,nx,fspl4,ibcxmin,bcxmin,ibcxmax,bcxmax,
122  > wk,inwk,ilinx,ier)
123 C
124  if(ier.eq.0) then
125 C
126 C copy the output -- careful of end point.
127 C
128  do i=1,nx-1
129  fspl(2,i)=2.0*fspl4(3,i)
130  enddo
131  fspl(2,nx)=2.0*fspl4(3,nx-1) +
132  > (x(nx)-x(nx-1))*6.0*fspl4(4,nx-1)
133  endif
134 C
135  deallocate(fspl4,wk)
136 C
137  return
138  end