ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/OOPSE-4/src/utils/interpolation.F90
Revision: 2722
Committed: Thu Apr 20 18:24:24 2006 UTC (18 years, 2 months ago) by gezelter
File size: 10477 byte(s)
Log Message:
Complete rewrite of spline code and everything that uses it.

File Contents

# Content
1 !!
2 !! Copyright (c) 2006 The University of Notre Dame. All Rights Reserved.
3 !!
4 !! The University of Notre Dame grants you ("Licensee") a
5 !! non-exclusive, royalty free, license to use, modify and
6 !! redistribute this software in source and binary code form, provided
7 !! that the following conditions are met:
8 !!
9 !! 1. Acknowledgement of the program authors must be made in any
10 !! publication of scientific results based in part on use of the
11 !! program. An acceptable form of acknowledgement is citation of
12 !! the article in which the program was described (Matthew
13 !! A. Meineke, Charles F. Vardeman II, Teng Lin, Christopher
14 !! J. Fennell and J. Daniel Gezelter, "OOPSE: An Object-Oriented
15 !! Parallel Simulation Engine for Molecular Dynamics,"
16 !! J. Comput. Chem. 26, pp. 252-271 (2005))
17 !!
18 !! 2. Redistributions of source code must retain the above copyright
19 !! notice, this list of conditions and the following disclaimer.
20 !!
21 !! 3. Redistributions in binary form must reproduce the above copyright
22 !! notice, this list of conditions and the following disclaimer in the
23 !! documentation and/or other materials provided with the
24 !! distribution.
25 !!
26 !! This software is provided "AS IS," without a warranty of any
27 !! kind. All express or implied conditions, representations and
28 !! warranties, including any implied warranty of merchantability,
29 !! fitness for a particular purpose or non-infringement, are hereby
30 !! excluded. The University of Notre Dame and its licensors shall not
31 !! be liable for any damages suffered by licensee as a result of
32 !! using, modifying or distributing the software or its
33 !! derivatives. In no event will the University of Notre Dame or its
34 !! licensors be liable for any lost revenue, profit or data, or for
35 !! direct, indirect, special, consequential, incidental or punitive
36 !! damages, however caused and regardless of the theory of liability,
37 !! arising out of the use of or inability to use software, even if the
38 !! University of Notre Dame has been advised of the possibility of
39 !! such damages.
40 !!
41 !!
42 !! interpolation.F90
43 !!
44 !! Created by Charles F. Vardeman II on 03 Apr 2006.
45 !!
46 !! PURPOSE: Generic Spline interpolation routines.
47 !!
48 !! @author Charles F. Vardeman II
49 !! @version $Id: interpolation.F90,v 1.7 2006-04-20 18:24:24 gezelter Exp $
50
51
52 module interpolation
53 use definitions
54 use status
55 implicit none
56 PRIVATE
57
58 type, public :: cubicSpline
59 logical :: isUniform = .false.
60 integer :: n = 0
61 real(kind=dp) :: dx_i
62 real (kind=dp), pointer,dimension(:) :: x => null()
63 real (kind=dp), pointer,dimension(:) :: y => null()
64 real (kind=dp), pointer,dimension(:) :: b => null()
65 real (kind=dp), pointer,dimension(:) :: c => null()
66 real (kind=dp), pointer,dimension(:) :: d => null()
67 end type cubicSpline
68
69 public :: newSpline
70 public :: deleteSpline
71 public :: lookupSpline
72 public :: lookupUniformSpline
73 public :: lookupNonuniformSpline
74 public :: lookupUniformSpline1d
75
76 contains
77
78
79 subroutine newSpline(cs, x, y, isUniform)
80
81 implicit none
82
83 type (cubicSpline), intent(inout) :: cs
84 real( kind = DP ), intent(in) :: x(:), y(:)
85 real( kind = DP ) :: fp1, fpn, p
86 REAL( KIND = DP), DIMENSION(size(x)-1) :: diff_y, H
87
88 logical, intent(in) :: isUniform
89 integer :: i, alloc_error, n, k
90
91 alloc_error = 0
92
93 if (cs%n .ne. 0) then
94 call handleWarning("interpolation::newSpline", &
95 "cubicSpline struct was already created")
96 call deleteSpline(cs)
97 end if
98
99 ! make sure the sizes match
100
101 n = size(x)
102
103 if ( size(y) .ne. size(x) ) then
104 call handleError("interpolation::newSpline", &
105 "Array size mismatch")
106 end if
107
108 cs%n = n
109 cs%isUniform = isUniform
110
111 allocate(cs%x(n), stat=alloc_error)
112 if(alloc_error .ne. 0) then
113 call handleError("interpolation::newSpline", &
114 "Error in allocating storage for x")
115 endif
116
117 allocate(cs%y(n), stat=alloc_error)
118 if(alloc_error .ne. 0) then
119 call handleError("interpolation::newSpline", &
120 "Error in allocating storage for y")
121 endif
122
123 allocate(cs%b(n), stat=alloc_error)
124 if(alloc_error .ne. 0) then
125 call handleError("interpolation::newSpline", &
126 "Error in allocating storage for b")
127 endif
128
129 allocate(cs%c(n), stat=alloc_error)
130 if(alloc_error .ne. 0) then
131 call handleError("interpolation::newSpline", &
132 "Error in allocating storage for c")
133 endif
134
135 allocate(cs%d(n), stat=alloc_error)
136 if(alloc_error .ne. 0) then
137 call handleError("interpolation::newSpline", &
138 "Error in allocating storage for d")
139 endif
140
141 ! make sure we are monotinically increasing in x:
142
143 h = diff(x)
144 if (any(h <= 0)) then
145 call handleError("interpolation::newSpline", &
146 "Negative dx interval found")
147 end if
148
149 ! load x and y values into the cubicSpline structure:
150
151 do i = 1, n
152 cs%x(i) = x(i)
153 cs%y(i) = y(i)
154 end do
155
156 ! Calculate coefficients for the tridiagonal system: store
157 ! sub-diagonal in B, diagonal in D, difference quotient in C.
158
159 cs%b(1:n-1) = h
160 diff_y = diff(y)
161 cs%c(1:n-1) = diff_y / h
162
163 if (n == 2) then
164 ! Assume the derivatives at both endpoints are zero
165 ! another assumption could be made to have a linear interpolant
166 ! between the two points. In that case, the b coefficients
167 ! below would be diff_y(1)/h(1) and the c and d coefficients would
168 ! both be zero.
169 cs%b(1) = 0.0_dp
170 cs%c(1) = -3.0_dp * (diff_y(1)/h(1))**2
171 cs%d(1) = -2.0_dp * (diff_y(1)/h(1))**3
172 cs%b(2) = cs%b(1)
173 cs%c(2) = 0.0_dp
174 cs%d(2) = 0.0_dp
175 cs%dx_i = 1.0_dp / h(1)
176 return
177 end if
178
179 cs%d(1) = 2.0_dp * cs%b(1)
180 do i = 2, n-1
181 cs%d(i) = 2.0_dp * (cs%b(i) + cs%b(i-1))
182 end do
183 cs%d(n) = 2.0_dp * cs%b(n-1)
184
185 ! Calculate estimates for the end slopes using polynomials
186 ! that interpolate the data nearest the end.
187
188 fp1 = cs%c(1) - cs%b(1)*(cs%c(2) - cs%c(1))/(cs%b(1) + cs%b(2))
189 if (n > 3) then
190 fp1 = fp1 + cs%b(1)*((cs%b(1) + cs%b(2))*(cs%c(3) - cs%c(2))/ &
191 (cs%b(2) + cs%b(3)) - cs%c(2) + cs%c(1))/(x(4) - x(1))
192 end if
193
194 fpn = cs%c(n-1) + cs%b(n-1)*(cs%c(n-1) - cs%c(n-2))/(cs%b(n-2) + cs%b(n-1))
195 if (n > 3) then
196 fpn = fpn + cs%b(n-1)*(cs%c(n-1) - cs%c(n-2) - (cs%b(n-2) + cs%b(n-1))* &
197 (cs%c(n-2) - cs%c(n-3))/(cs%b(n-2) + cs%b(n-3)))/(x(n) - x(n-3))
198 end if
199
200 ! Calculate the right hand side and store it in C.
201
202 cs%c(n) = 3.0_dp * (fpn - cs%c(n-1))
203 do i = n-1,2,-1
204 cs%c(i) = 3.0_dp * (cs%c(i) - cs%c(i-1))
205 end do
206 cs%c(1) = 3.0_dp * (cs%c(1) - fp1)
207
208 ! Solve the tridiagonal system.
209
210 do k = 2, n
211 p = cs%b(k-1) / cs%d(k-1)
212 cs%d(k) = cs%d(k) - p*cs%b(k-1)
213 cs%c(k) = cs%c(k) - p*cs%c(k-1)
214 end do
215 cs%c(n) = cs%c(n) / cs%d(n)
216 do k = n-1, 1, -1
217 cs%c(k) = (cs%c(k) - cs%b(k) * cs%c(k+1)) / cs%d(k)
218 end do
219
220 ! Calculate the coefficients defining the spline.
221
222 cs%d(1:n-1) = diff(cs%c) / (3.0_dp * h)
223 cs%b(1:n-1) = diff_y / h - h * (cs%c(1:n-1) + h * cs%d(1:n-1))
224 cs%b(n) = cs%b(n-1) + h(n-1) * (2.0_dp*cs%c(n-1) + h(n-1)*3.0_dp*cs%d(n-1))
225
226 if (isUniform) then
227 cs%dx_i = 1.0d0 / (x(2) - x(1))
228 endif
229
230 return
231
232 contains
233
234 function diff(v)
235 ! Auxiliary function to compute the forward difference
236 ! of data stored in a vector v.
237
238 implicit none
239 real (kind = dp), dimension(:), intent(in) :: v
240 real (kind = dp), dimension(size(v)-1) :: diff
241
242 integer :: n
243
244 n = size(v)
245 diff = v(2:n) - v(1:n-1)
246 return
247 end function diff
248
249 end subroutine newSpline
250
251 subroutine deleteSpline(this)
252
253 type(cubicSpline) :: this
254
255 if(associated(this%x)) then
256 deallocate(this%x)
257 this%x => null()
258 end if
259 if(associated(this%c)) then
260 deallocate(this%c)
261 this%c => null()
262 end if
263
264 this%n = 0
265
266 end subroutine deleteSpline
267
268 subroutine lookupNonuniformSpline(cs, xval, yval)
269
270 implicit none
271
272 type (cubicSpline), intent(in) :: cs
273 real( kind = DP ), intent(in) :: xval
274 real( kind = DP ), intent(out) :: yval
275 real( kind = DP ) :: dx
276 integer :: i, j
277 !
278 ! Find the interval J = [ cs%x(J), cs%x(J+1) ] that contains
279 ! or is nearest to xval.
280 !
281 j = cs%n - 1
282
283 do i = 0, cs%n - 2
284
285 if ( xval < cs%x(i+1) ) then
286 j = i
287 exit
288 end if
289
290 end do
291 !
292 ! Evaluate the cubic polynomial.
293 !
294 dx = xval - cs%x(j)
295 yval = cs%y(j) + dx*(cs%b(j) + dx*(cs%c(j) + dx*cs%d(j)))
296
297 return
298 end subroutine lookupNonuniformSpline
299
300 subroutine lookupUniformSpline(cs, xval, yval)
301
302 implicit none
303
304 type (cubicSpline), intent(in) :: cs
305 real( kind = DP ), intent(in) :: xval
306 real( kind = DP ), intent(out) :: yval
307 real( kind = DP ) :: dx
308 integer :: i, j
309 !
310 ! Find the interval J = [ cs%x(J), cs%x(J+1) ] that contains
311 ! or is nearest to xval.
312
313 j = MAX(1, MIN(cs%n-1, idint((xval-cs%x(1)) * cs%dx_i) + 1))
314
315 dx = xval - cs%x(j)
316 yval = cs%y(j) + dx*(cs%b(j) + dx*(cs%c(j) + dx*cs%d(j)))
317
318 return
319 end subroutine lookupUniformSpline
320
321 subroutine lookupUniformSpline1d(cs, xval, yval, dydx)
322
323 implicit none
324
325 type (cubicSpline), intent(in) :: cs
326 real( kind = DP ), intent(in) :: xval
327 real( kind = DP ), intent(out) :: yval, dydx
328 real( kind = DP ) :: dx
329 integer :: i, j
330
331 ! Find the interval J = [ cs%x(J), cs%x(J+1) ] that contains
332 ! or is nearest to xval.
333
334
335 j = MAX(1, MIN(cs%n-1, idint((xval-cs%x(1)) * cs%dx_i) + 1))
336
337 dx = xval - cs%x(j)
338 yval = cs%y(j) + dx*(cs%b(j) + dx*(cs%c(j) + dx*cs%d(j)))
339
340 dydx = cs%b(j) + dx*(2.0d0 * cs%c(j) + 3.0d0 * dx * cs%d(j))
341
342 return
343 end subroutine lookupUniformSpline1d
344
345 subroutine lookupSpline(cs, xval, yval)
346
347 type (cubicSpline), intent(in) :: cs
348 real( kind = DP ), intent(inout) :: xval
349 real( kind = DP ), intent(inout) :: yval
350
351 if (cs%isUniform) then
352 call lookupUniformSpline(cs, xval, yval)
353 else
354 call lookupNonuniformSpline(cs, xval, yval)
355 endif
356
357 return
358 end subroutine lookupSpline
359
360 end module interpolation