-
Notifications
You must be signed in to change notification settings - Fork 0
/
splint.f90
168 lines (125 loc) · 4.83 KB
/
splint.f90
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
!> splint evaluates a cubic spline and its first and second
!> derivatives at the abscissas in xi. The spline (which
!> is defined by x, y, and ypp) may have been determined by
!> splift or any other fitting routine that
!> provides second derivatives.
!>
!> The code is based on, and compatible with, the 1978 subroutine
!> written by Rondall E, Jones at Sandia. The linear upward
!> search was replaced by the hunt algorithm from Numerical Recipes.
!>
!> \author Jose Luis Martins
!> \version 5.805
!> \date 9 May 2018
!> \copyright GNU Public License v2
subroutine splint (x,y,ypp,n,xi,yi,ypi,yppi,ni,kerr)
! copyright José Luís Martins, INESC-MN.
implicit none
integer, parameter :: REAL64 = selected_real_kind(12)
integer, parameter :: iowrite = 6
! input
integer, intent(in) :: n !< number of data points that define the spline. n > 1
real(REAL64), intent(in) :: x(n) !< array of abscissas (in increasing order) that define the spline.
real(REAL64), intent(in) :: y(n) !< array of ordinates that define the spline.
real(REAL64), intent(in) :: ypp(n) !< array of second derivatives that define the spline.
integer, intent(in) :: ni !< the number of abscissas at which the spline is to be evaluated. ni > 0
real(REAL64), intent(in) :: xi(ni) !< array of abscissas (in arbitrary order) at which the spline is to be evaluated.
! output
real(REAL64), intent(out) :: yi(ni) !< array of values of the spline (ordinates) at xi
real(REAL64), intent(out) :: ypi(ni) !< array of values of the first derivative of spline at xi
real(REAL64), intent(out) :: yppi(ni) !< array of values of the second derivative of spline at xi
integer, intent(out) :: kerr !< status code. 1: all normal; 2: extrapolations occurred; 3: ni < 1, no interpolation.
! local variables
real(REAL64) :: xx, xxold ! current and previous abscissa to be evaluated, xi(k), xi(k-1)
integer :: i ! i is current index into x array.
integer :: k ! k is index on value of xi being worked on.
integer :: il, ir ! bracket interval for bissection
integer :: jh, inc ! try index and increment for hunt phase
real(REAL64) :: h, h2
real(REAL64) :: xl, xl2, xl3
real(REAL64) :: xr, xr2, xr3
! counter
integer :: j
! constants
real(REAL64), parameter :: ZERO = 0.0_REAL64, UM = 1.0_REAL64
! check input
if (ni <= 0) then
write(iowrite,*) ' in splint, the requested number of ', &
'interpolating points was not positive'
kerr = 3
else
kerr = 1
do k = 1,ni
xx = xi(k)
! checks for extrapolation
if (xx < x(1)) then
kerr = 2
i = 1
elseif (xx > x(n)) then
kerr = 2
i = n-1
else
! use past information
if(k == 1) then
il = 1
ir = n
else
if(xxold < xx) then
il = i
ir = n
else
il = 1
ir = min(n,i+1)
endif
endif
! hunt phase
inc = 1
do j = 1,n+10
jh = il + inc
if(jh > ir) then
exit
else
if(xx > x(jh)) then
il = jh
inc = 2*inc
else
ir = jh
exit
endif
endif
enddo
! bisection search
do j = 1,n+10
i = (il+ir)/2
if(i == il) exit
if(xx == x(i)) exit
if(xx < x(i)) then
ir = i
else
il = i
endif
enddo
endif
! just being paranoid
i = min(i,n-1)
xxold = xx
! interpolate
h = x(i+1) - x(i)
if(h == ZERO) then
write(iowrite,*) ' in splint identical abscissas'
stop
endif
h2 = h*h
xr = (x(i+1)-xx)/h
xr2= xr*xr
xr3= xr*xr2
xl = (xx-x(i))/h
xl2= xl*xl
xl3= xl*xl2
yi(k) = y(i)*xr + y(i+1)*xl - h2*(ypp(i)*(xr-xr3) + ypp(i+1)*(xl-xl3))/6
ypi(k) = (y(i+1)-y(i))/h + h*(ypp(i)*(UM-3*xr2) - ypp(i+1)*(UM-3*xl2))/6
yppi(k) = ypp(i)*xr + ypp(i+1)*xl
enddo
endif
return
end subroutine splint