ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/group/trunk/OOPSE-4/src/utils/interpolation.F90
(Generate patch)

Comparing trunk/OOPSE-4/src/utils/interpolation.F90 (file contents):
Revision 2711 by gezelter, Fri Apr 14 21:49:54 2006 UTC vs.
Revision 2756 by gezelter, Wed May 17 15:37:15 2006 UTC

# Line 43 | Line 43
43   !!
44   !!  Created by Charles F. Vardeman II on 03 Apr 2006.
45   !!
46 < !!  PURPOSE: Generic Spline interpolation routines. These routines
47 < !!           assume that we are on a uniform grid for precomputation of
48 < !!           spline parameters.
46 > !!  PURPOSE: Generic Spline interpolation routines.
47   !!
48   !! @author Charles F. Vardeman II
49 < !! @version $Id: interpolation.F90,v 1.4 2006-04-14 21:49:54 gezelter Exp $
49 > !! @version $Id: interpolation.F90,v 1.8 2006-05-17 15:37:15 gezelter Exp $
50  
51  
52 < module  INTERPOLATION
52 > module interpolation
53    use definitions
54    use status
55    implicit none
56    PRIVATE
57  
60  character(len = statusMsgSize) :: errMSG
61
58    type, public :: cubicSpline
63     private
59       logical :: isUniform = .false.
60 <     integer :: np = 0
60 >     integer :: n = 0
61       real(kind=dp) :: dx_i
62       real (kind=dp), pointer,dimension(:)   :: x => null()
63 <     real (kind=dp), pointer,dimension(:,:) :: c => 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 :: lookup_spline
72 <  public :: lookup_uniform_spline
73 <  public :: lookup_nonuniform_spline
71 >  public :: lookupSpline
72 >  public :: lookupUniformSpline
73 >  public :: lookupNonuniformSpline
74 >  public :: lookupUniformSpline1d
75    
76   contains
77    
78  
79 <  subroutine newSpline(cs, x, y, yp1, ypn, isUniform)
79 >  subroutine newSpline(cs, x, y, isUniform)
80      
82    !************************************************************************
83    !
84    ! newSpline solves for slopes defining a cubic spline.
85    !
86    !  Discussion:
87    !
88    !    A tridiagonal linear system for the unknown slopes S(I) of
89    !    F at x(I), I=1,..., N, is generated and then solved by Gauss
90    !    elimination, with S(I) ending up in cs%C(2,I), for all I.
91    !
92    !  Reference:
93    !
94    !    Carl DeBoor,
95    !    A Practical Guide to Splines,
96    !    Springer Verlag.
97    !
98    !  Parameters:
99    !
100    !    Input, real x(N), the abscissas or X values of
101    !    the data points.  The entries of x are assumed to be
102    !    strictly increasing.
103    !
104    !    Input, real y(I), contains the function value at x(I) for
105    !      I = 1, N.
106    !
107    !    Input, real yp1 contains the slope at x(1)
108    !    Input, real ypn contains the slope at x(N)
109    !
110    !    On output, the slopes at x(I) have been stored in
111    !               cs%C(2,I), for I = 1 to N.
112
81      implicit none
82  
83      type (cubicSpline), intent(inout) :: cs
84      real( kind = DP ), intent(in) :: x(:), y(:)
85 <    real( kind = DP ), intent(in) :: yp1, ypn
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 <    real( kind = DP ) :: g, divdif1, divdif3, dx
120 <    integer :: i, alloc_error, np
89 >    integer :: i, alloc_error, n, k
90  
91      alloc_error = 0
92  
93 <    if (cs%np .ne. 0) then
93 >    if (cs%n .ne. 0) then
94         call handleWarning("interpolation::newSpline", &
95              "cubicSpline struct was already created")
96         call deleteSpline(cs)
# Line 129 | Line 98 | contains
98  
99      ! make sure the sizes match
100  
101 <    np = size(x)
102 <
103 <    if ( size(y) .ne. np ) then
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%np = np
108 >    cs%n = n
109      cs%isUniform = isUniform
110  
111 <    allocate(cs%x(np), stat=alloc_error)
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%c(4,np), stat=alloc_error)
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 <    do i = 1, np
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%c(1,i) = y(i)      
154 <    enddo
153 >       cs%y(i) = y(i)
154 >    end do
155  
156 <    ! Set the first derivative of the function to the second coefficient of
157 <    ! each of the endpoints
156 >    ! Calculate coefficients for the tridiagonal system: store
157 >    ! sub-diagonal in B, diagonal in D, difference quotient in C.
158  
159 <    cs%c(2,1) = yp1
160 <    cs%c(2,np) = ypn
161 <    
165 <    !
166 <    !  Set up the right hand side of the linear system.
167 <    !
159 >    cs%b(1:n-1) = h
160 >    diff_y = diff(y)
161 >    cs%c(1:n-1) = diff_y / h
162  
163 <    do i = 2, cs%np - 1
164 <       cs%c(2,i) = 3.0_DP * ( &
165 <            (x(i) - x(i-1)) * (cs%c(1,i+1) - cs%c(1,i)) / (x(i+1) - x(i)) + &
166 <            (x(i+1) - x(i)) * (cs%c(1,i) - cs%c(1,i-1)) / (x(i) - x(i-1)))
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 <    !
186 <    !  Set the diagonal coefficients.
187 <    !
188 <    cs%c(4,1) = 1.0_DP
189 <    do i = 2, cs%np - 1
190 <       cs%c(4,i) = 2.0_DP * ( x(i+1) - x(i-1) )
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(4,cs%np) = 1.0_DP
207 <    !
208 <    !  Set the off-diagonal coefficients.
209 <    !
210 <    cs%c(3,1) = 0.0_DP
211 <    do i = 2, cs%np
212 <       cs%c(3,i) = x(i) - x(i-1)
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 <    !
216 <    !  Forward elimination.
217 <    !
193 <    do i = 2, cs%np - 1
194 <       g = -cs%c(3,i+1) / cs%c(4,i-1)
195 <       cs%c(4,i) = cs%c(4,i) + g * cs%c(3,i-1)
196 <       cs%c(2,i) = cs%c(2,i) + g * cs%c(2,i-1)
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
198    !
199    !  Back substitution for the interior slopes.
200    !
201    do i = cs%np - 1, 2, -1
202       cs%c(2,i) = ( cs%c(2,i) - cs%c(3,i) * cs%c(2,i+1) ) / cs%c(4,i)
203    end do
204    !
205    !  Now compute the quadratic and cubic coefficients used in the
206    !  piecewise polynomial representation.
207    !
208    do i = 1, cs%np - 1
209       dx = x(i+1) - x(i)
210       divdif1 = ( cs%c(1,i+1) - cs%c(1,i) ) / dx
211       divdif3 = cs%c(2,i) + cs%c(2,i+1) - 2.0_DP * divdif1
212       cs%c(3,i) = ( divdif1 - cs%c(2,i) - divdif3 ) / dx
213       cs%c(4,i) = divdif3 / ( dx * dx )
214    end do
219  
220 <    cs%c(3,cs%np) = 0.0_DP
217 <    cs%c(4,cs%np) = 0.0_DP
220 >    ! Calculate the coefficients defining the spline.
221  
222 <    cs%dx_i = 1.0_DP / dx
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 <    return
227 <  end subroutine newSplineWithoutDerivs
226 >    if (isUniform) then
227 >       cs%dx_i = 1.0_dp / (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
# Line 234 | Line 261 | contains
261         this%c => null()
262      end if
263      
264 <    this%np = 0
264 >    this%n = 0
265      
266    end subroutine deleteSpline
267  
268 <  subroutine lookup_nonuniform_spline(cs, xval, yval)
268 >  subroutine lookupNonuniformSpline(cs, xval, yval)
269      
243    !*************************************************************************
244    !
245    ! lookup_nonuniform_spline evaluates a piecewise cubic Hermite interpolant.
246    !
247    !  Discussion:
248    !
249    !    newSpline must be called first, to set up the
250    !    spline data from the raw function and derivative data.
251    !
252    !  Modified:
253    !
254    !    06 April 1999
255    !
256    !  Reference:
257    !
258    !    Conte and de Boor,
259    !    Algorithm PCUBIC,
260    !    Elementary Numerical Analysis,
261    !    1973, page 234.
262    !
263    !  Parameters:
264    !
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
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%np - 1
281 >    j = cs%n - 1
282  
283 <    do i = 1, cs%np - 2
283 >    do i = 0, cs%n - 2
284  
285         if ( xval < cs%x(i+1) ) then
286            j = i
# Line 287 | Line 292 | contains
292      !  Evaluate the cubic polynomial.
293      !
294      dx = xval - cs%x(j)
295 <
291 <    yval = cs%c(1,j) + dx * ( cs%c(2,j) + dx * ( cs%c(3,j) + dx * cs%c(4,j) ) )
295 >    yval = cs%y(j) + dx*(cs%b(j) + dx*(cs%c(j) + dx*cs%d(j)))
296      
297      return
298 <  end subroutine lookup_nonuniform_spline
298 >  end subroutine lookupNonuniformSpline
299  
300 <  subroutine lookup_uniform_spline(cs, xval, yval)
300 >  subroutine lookupUniformSpline(cs, xval, yval)
301      
298    !*************************************************************************
299    !
300    ! lookup_uniform_spline evaluates a piecewise cubic Hermite interpolant.
301    !
302    !  Discussion:
303    !
304    !    newSpline must be called first, to set up the
305    !    spline data from the raw function and derivative data.
306    !
307    !  Modified:
308    !
309    !    06 April 1999
310    !
311    !  Reference:
312    !
313    !    Conte and de Boor,
314    !    Algorithm PCUBIC,
315    !    Elementary Numerical Analysis,
316    !    1973, page 234.
317    !
318    !  Parameters:
319    !
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
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, int((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 <    j = MAX(1, MIN(cs%np, idint((xval-cs%x(1)) * cs%dx_i) + 1))
321 >  subroutine lookupUniformSpline1d(cs, xval, yval, dydx)
322 >    
323 >    implicit none
324  
325 <    dx = xval - cs%x(j)
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 <    yval = cs%c(1,j) + dx * ( cs%c(2,j) + dx * ( cs%c(3,j) + dx * cs%c(4,j) ) )
334 >
335 >    j = MAX(1, MIN(cs%n-1, int((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.0_dp * cs%c(j) + 3.0_dp * dx * cs%d(j))
341 +      
342      return
343 <  end subroutine lookup_uniform_spline
343 >  end subroutine lookupUniformSpline1d
344  
345 <  subroutine lookup_spline(cs, xval, yval)
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 lookup_uniform_spline(cs, xval, yval)
352 >       call lookupUniformSpline(cs, xval, yval)
353      else
354 <       call lookup_nonuniform_spline(cs, xval, yval)
354 >       call lookupNonuniformSpline(cs, xval, yval)
355      endif
356  
357      return
358 <  end subroutine lookup_spline
358 >  end subroutine lookupSpline
359    
360 < end module INTERPOLATION
360 > end module interpolation

Diff Legend

Removed lines
+ Added lines
< Changed lines
> Changed lines