inter_module.f90
Go to the documentation of this file.
1 !> @file inter_mod.f90
2 !!
3 !! The error file code for this file is ***W27***.
4 !! This file contains the module \ref inter_module
5 
6 !>
7 !! @brief Module \ref inter_module with interpolation routines
8 !!
9 !! This routines are used to interpolate the hydrodynamic
10 !! quantaties (e.g., temperature, density,..)
11 !!
12 #include "macros.h"
14  implicit none
15 
16  !
17  ! Public and private fields and methods of the module (everything is public)
18  !
19  public:: &
22  private:: &
24 
25 contains
26 
27 !>
28 !! Linear interpolation in lin-log space.
29 !!
30 !! This routine assumes that the x and y arrays are given
31 !! in the form of {x_i, log(y_i)}.
32 !! An optional argument flin allows to control the return value:
33 !! - if flin is absent: return exp(log(y)) = y;
34 !! - if flin is present: return log(y).
35 !!
36 !! \b Edited:
37 !! - 11.01.14
38 !! .
39 subroutine lininter(n,ref_array,ref_value,array,res,flin)
40  implicit none
41 
42  integer,intent(in) :: n !< array sizes
43  real(r_kind),dimension(n),intent(in) :: ref_array !< array of x-values {x_i}
44  real(r_kind),dimension(n),intent(in) :: array !< array of log(y)-values {log(y_i)}
45  real(r_kind),intent(in) :: ref_value !< value of log(x)
46  real(r_kind),intent(out) :: res !< interpolated value of y
47  logical,intent(in),optional :: flin !< if present, return log(y) instead of y
48  !
49  real(r_kind) :: grad
50  integer :: i,ii
51  logical :: linear
52 
53  ! Default value
54  if (.not. present(flin)) then
55  linear = .false.
56  else
57  linear = flin
58  end if
59 
60  ii = 1
61  do i = 1,n
62  if (ref_value .le. ref_array(i)) exit
63  ii = i+1
64  end do
65 
66  if (ii.eq.1) then
67  res = array(1)
68  else if (ii.gt.n) then
69  res = array(n)
70  else
71  grad = (ref_value-ref_array(ii-1))/(ref_array(ii)-ref_array(ii-1))
72  res = array(ii-1) + grad*(array(ii)-array(ii-1))
73  end if
74 
75  if(linear) then
76  return
77  else
78  res= dexp(res)
79  return
80  end if
81 
82 end subroutine lininter
83 
84 
85 
86 !> Interface for 1D interpolation routines.
87 !!
88 !! This routine is a wrapper for the 1D interpolation routines
89 !! lininter, cubinter, akimainter, makimainter, and pchipinter.
90 !! The interpolation type is controlled by the optional argument
91 !! itype. If itype is absent, \ref interp_mode is used.
92 !! The optional argument flin allows to control the return value:
93 !! - if flin is absent: return exp(log(y)) = y;
94 !! - if flin is present: return log(y).
95 !!
96 !! @author M. Reichert
97 !! @date 01.05.2023
98 subroutine interp1d(n,xp,xb,yp,res,flin,itype)
100  use parameter_class, only: interp_mode
101  integer,intent(in) :: n !< array sizes
102  real(r_kind),dimension(n),intent(in) :: xp !< array of x-values
103  real(r_kind),intent(in) :: xb !< value of x
104  real(r_kind),dimension(n),intent(in) :: yp !< array of log(y)-values
105  real(r_kind),intent(out) :: res !< interpolated value of y
106  logical,intent(in),optional :: flin !< if present, return log(y) instead of y
107  integer,intent(in),optional :: itype!< Type of interpolation (1: linear, 2: cubic)
108  ! Internal variables
109  integer :: interp_type
110  logical :: linear
111 
112  ! Set default values
113  if (.not. present(itype)) then
114  interp_type = interp_mode
115  else
116  interp_type = itype
117  end if
118 
119  if (.not. present(flin)) then
120  linear = .false.
121  else
122  linear = flin
123  end if
124 
125  ! Decide on desired interpolation type
126  if (interp_type .eq. itype_linear) then
127  call lininter(n,xp,xb,yp,res,linear)
128  else if (interp_type .eq. itype_cubic) then
129  call cubinter(n,xp,xb,yp,res,linear)
130  else if (interp_type .eq. itype_akima) then
131  call akimainter(n,xp,xb,yp,res,linear)
132  else if (interp_type .eq. itype_makima) then
133  call makimainter(n,xp,xb,yp,res,linear)
134  else if (interp_type .eq. itype_pchip) then
135  call pchipinter(n,xp,xb,yp,res,linear)
136  else
137  call raise_exception("Interpolation type not known. Got "//&
138  int_to_str(interp_type)//&
139  ". Try to change interp_mode to a valid value.",&
140  "interp1d",270004)
141  end if
142 
143 end subroutine interp1d
144 
145 
146 
147 !>
148 !! Cubic interpolation in log-log space. Here, it is assumed that
149 !!
150 !! the x and y arrays are given in the form of {x_i, log(y_i)},
151 !! but before interpolating the x coordinate, it is transformed
152 !! to a log space: {log(x_i), log(y_i)}.
153 !! An optional argument flin allows to control the return value:
154 !! - if flin is absent: return exp(log(y)) = y;
155 !! - if flin is present: return log(y).
156 !!
157 !! \b Edited:
158 !! - 14.01.14
159 !! .
160 subroutine cubinter (n,xp,xb,yp,res,flin)
161  implicit none
162 
163  integer,intent(in) :: n !< array sizes
164  real(r_kind),dimension(n),intent(in) :: xp !< array of x-values
165  real(r_kind),intent(in) :: xb !< value of x
166  real(r_kind),dimension(n),intent(in) :: yp !< array of log(y)-values
167  real(r_kind),intent(out) :: res !< interpolated value of y
168  logical,intent(in),optional :: flin !< if present, return log(y) instead of y
169  !
170  integer :: ii,k
171  real(r_kind) :: x,x1,x2,x3,x4,x12,x13,x14,x23,x24,x34
172  logical :: linear
173 
174  if(present(flin)) then
175  linear= flin
176  else
177  linear= .false.
178  end if
179 
180  x= xb
181  do ii= 1,n
182  if (xb .le. xp(ii)) exit
183  end do
184  if (x.le.xp(1)) then
185  res= yp(1)
186  else if (x.ge.xp(n)) then
187  res= yp(n)
188  else if(ii.le.2) then ! linear interpolation in lin-log space
189  x1= x - xp(1)
190  x2= x - xp(2)
191  x12= x2-x1
192  res= (yp(1)*x2 - yp(2)*x1)/x12
193  else if(ii.eq.n) then ! linear interpolation in log-log space
194  x1= log(x/xp(n-1))
195  x2= log(x/xp(n))
196  x12= x2-x1
197  res= (yp(n-1)*x2 - yp(n)*x1)/x12
198  else ! cubic interpolation in log-log space
199  k= min(max(ii-2,2),n-3)
200  x= log(x)
201  x1= x - log(xp(k))
202  x2= x - log(xp(k+1))
203  x3= x - log(xp(k+2))
204  x4= x - log(xp(k+3))
205 
206  x12= x2-x1
207  x13= x3-x1
208  x14= x4-x1
209  x23= x3-x2
210  x24= x4-x2
211  x34= x4-x3
212 
213  res= yp(k) *x2*x3*x4/(x12*x13*x14) &
214  - yp(k+1)*x1*x3*x4/(x12*x23*x24) &
215  + yp(k+2)*x1*x2*x4/(x13*x23*x34) &
216  - yp(k+3)*x1*x2*x3/(x14*x24*x34)
217  endif
218 
219  if(linear) then
220  return
221  else
222  res= dexp(res)
223  return
224  end if
225 end subroutine cubinter
226 
227 
228 !> PCHIP interpolation in lin-log space.
229 !!
230 !! The Piecewise Cubic Hermite Interpolating Polynomial (PCHIP)
231 !! interpolation is a method for interpolating data points where
232 !! every section between points is monoton. The interpolation
233 !! avoids overshoots and undershoots and is close to the linear
234 !! interpolation, but having the first derivative continuous.
235 !!
236 !! @see F. N. Fritsch and R. E. Carlson, Monotone Piecewise Cubic
237 !! Interpolation, SIAM Journal on Numerical Analysis, 17(2),
238 !! 1980, pp. 238-246.
239 !! [Steffen 1990](https://ui.adsabs.harvard.edu/abs/1990A%26A...239..443S/abstract)
240 !! @author: M. Reichert
241 !! @date: 01.05.2023
242 subroutine pchipinter(n,xp,xb,yp,res,flin)
243  implicit none
244  integer,intent(in) :: n !< array sizes
245  real(r_kind),dimension(n),intent(in) :: xp !< array of x-values
246  real(r_kind),intent(in) :: xb !< value of x
247  real(r_kind),dimension(n),intent(in) :: yp !< array of log(y)-values
248  real(r_kind),intent(out) :: res !< interpolated value of y
249  logical,intent(in),optional :: flin !< if present, return log(y) instead of y
250  ! Internal variables
251  integer :: ii, i
252  real(r_kind) :: a, b, c, d
253  real(r_kind) :: hi, him1, hip1
254  real(r_kind) :: si, sim1, sip1
255  real(r_kind) :: pi, pip1
256  real(r_kind) :: yi_prime, yip1_prime
257  real(r_kind) :: x1, x2
258  real(r_kind) :: delt
259  logical :: linear
260 
261  if(present(flin)) then
262  linear= flin
263  else
264  linear= .false.
265  end if
266 
267  ! Find the index for the interpolation interval
268  do ii= 1,n
269  if (xb .le. xp(ii)) exit
270  end do
271  ii = ii-1
272 
273  ! Extrapolation, value is smaller than grid
274  if (xb.le.xp(1)) then
275  res= yp(1)
276  ! Extrapolation, value is larger than grid
277  else if (xb.ge.xp(n)) then
278  res= yp(n)
279  ! Value is at left border
280  else if(ii.lt.2) then
281  ! Interpolate linear
282  x1= xb - xp(ii)
283  x2= xb - xp(ii+1)
284  res= (yp(ii)*x2 - yp(ii+1)*x1)/(x2-x1)
285  ! Value is at left border
286  else if(ii.gt.n-2) then
287  ! Interpolate linear
288  x1= xb - xp(ii)
289  x2= xb - xp(ii+1)
290  res= (yp(ii)*x2 - yp(ii+1)*x1)/(x2-x1)
291  else
292  ! Normal case, use pchip interpolation
293  ! Values in interval
294  hi = xp(ii+1)-xp(ii)
295  si = (yp(ii+1)-yp(ii))/hi
296  ! Values to the left
297  him1 = xp(ii)-xp(ii-1)
298  sim1 = (yp(ii)-yp(ii-1))/him1
299  ! Values to the right
300  hip1 = xp(ii+2)-xp(ii+1)
301  sip1 = (yp(ii+2)-yp(ii+1))/hip1
302 
303  pi = (hi*sim1+him1*si)/(hi+him1)
304  pip1 = (hip1*si+hi*sip1)/(hip1+hi)
305 
306  ! Restrict slopes to ensure monotonicity
307  if (si*sim1 .le. 0) then
308  yi_prime = 0
309  elseif ((dabs(pi)>2*dabs(sim1)) .or. (dabs(pi)>2*dabs(si))) then
310  yi_prime = 2*sign(1.d0,si)*min(dabs(sim1),dabs(si))
311  else
312  yi_prime=pi
313  end if
314 
315  if (sip1*si .le. 0) then
316  yip1_prime = 0
317  elseif ((dabs(pip1)>2*dabs(si)) .or. (dabs(pip1)>2*dabs(sip1))) then
318  yip1_prime = 2*sign(1.d0,si)*min(dabs(sip1),dabs(si))
319  else
320  yip1_prime=pip1
321  end if
322 
323  a = (yi_prime+yip1_prime-2*si)/hi**2
324  b = (3*si-2*yi_prime-yip1_prime)/hi
325  c = yi_prime
326  d = yp(ii)
327 
328  delt = xb-xp(ii)
329  ! Calculate interpolated value
330  res = a*delt**3 + b*delt**2 + c*delt + d
331 
332  end if
333 
334  if(linear) then
335  return
336  else
337  res= dexp(res)
338  return
339  end if
340 
341 end subroutine pchipinter
342 
343 
344 
345 !> Interpolation using prescription of Akima
346 !!
347 !! The interpolation is a spline interpolation that tries
348 !! to avoid overshoots.
349 !!
350 !! Here, it is assumed that
351 !! the x and y arrays are given in the form of {x_i, log(y_i)},
352 !! but before interpolating the x coordinate, it is transformed
353 !! to a log space: {log(x_i), log(y_i)}.
354 !! An optional argument flin allows to control the return value:
355 !! - if flin is absent: return exp(log(y)) = y;
356 !! - if flin is present: return log(y).
357 !! .
358 !!
359 !! @see [Akima, Journal of the ACM, 17: 589–602, 1970](https://web.archive.org/web/20201218210437/http://www.leg.ufpr.br/lib/exe/fetch.php/wiki:internas:biblioteca:akima.pdf),
360 !! [Wikipedia: Akima Spline](https://en.wikipedia.org/wiki/Akima_spline)
361 !! @author M. Reichert
362 !! @date 30.04.2023
363 subroutine akimainter(n,xp,xb,yp,res,flin)
364  implicit none
365  integer,intent(in) :: n !< array sizes
366  real(r_kind),dimension(n),intent(in) :: xp !< array of x-values
367  real(r_kind),intent(in) :: xb !< value of x
368  real(r_kind),dimension(n),intent(in) :: yp !< array of log(y)-values
369  real(r_kind),intent(out) :: res !< interpolated value of y
370  logical,intent(in),optional :: flin !< if present, return log(y) instead of y
371  ! Internal variables
372  real(r_kind) :: xval(6), yval(6), mval(5), sval(2)
373  integer :: ii, i
374  real(r_kind) :: a, b, c, d
375  real(r_kind) :: x1,x2
376  real(r_kind) :: denom
377  logical :: linear
378 
379  if(present(flin)) then
380  linear= flin
381  else
382  linear= .false.
383  end if
384 
385  xval = 0.0d0
386  yval = 0.0d0
387  mval = 0.0d0
388  sval = 0.0d0
389 
390 
391  ! Find the index for the interpolation interval
392  do ii= 1,n
393  if (xb .le. xp(ii)) exit
394  end do
395  ii = ii-1
396 
397  ! Extrapolation, value is smaller than grid
398  if (xb.le.xp(1)) then
399  res= yp(1)
400  ! Extrapolation, value is larger than grid
401  else if (xb.ge.xp(n)) then
402  res= yp(n)
403  ! Value is at left border
404  else if(ii.lt.3) then
405  ! Interpolate linear
406  x1= xb - xp(ii)
407  x2= xb - xp(ii+1)
408  res= (yp(ii)*x2 - yp(ii+1)*x1)/(x2-x1)
409  ! Value is at left border
410  else if(ii.gt.n-3) then
411  ! Interpolate linear
412  x1= xb - xp(ii)
413  x2= xb - xp(ii+1)
414  res= (yp(ii)*x2 - yp(ii+1)*x1)/(x2-x1)
415  else
416  ! Normal case, use Akima interpolation
417 
418  ! Save the required values of the grid for the interpolation
419  xval(1:6) = xp(ii-2:ii+3)
420  yval(1:6) = yp(ii-2:ii+3)
421  ! Calculate the derivative
422  mval(1:5) = (yval(2:6) - yval(1:5)) / (xval(2:6) - xval(1:5))
423 
424  ! Get si and si+1
425  i = 3
426  sval(1) = ((dabs(mval(i+1)-mval(i))*mval(i-1) + dabs(mval(i-1)-mval(i-2))*mval(i)))
427  denom = (dabs(mval(i+1)-mval(i)) + dabs(mval(i-1)-mval(i-2)))
428  ! Avoid division by something very small
429  if (denom .gt. 1e-8) then
430  sval(1) = sval(1)/denom
431  else
432  sval(1) = (mval(i-1)+mval(i))/2d0
433  end if
434  sval(2) = ((dabs(mval(i+2)-mval(i+1))*mval(i) + dabs(mval(i)-mval(i-1))*mval(i+1)))
435  denom = (dabs(mval(i+2)-mval(i+1)) + dabs(mval(i)-mval(i-1)))
436  if (denom .gt. 1e-8) then
437  sval(2) = sval(2)/denom
438  else
439  sval(2) = (mval(i)+mval(i+1))/2d0
440  end if
441  ! Get the coefficients
442  a = yval(i)
443  b = sval(1)
444  c = (3.0d0 * mval(i) - 2.0d0 * sval(1) - sval(2)) / (xval(i+1) - xval(i))
445  d = (sval(1) + sval(2) - 2.0d0 * mval(i)) / (xval(i+1) - xval(i))**2
446  ! Calculate the results
447  res = a + b * (xb - xval(i)) + c * (xb - xval(i))**2 + d * (xb - xval(i))**3
448  end if
449 
450  if(linear) then
451  return
452  else
453  res= dexp(res)
454  return
455  end if
456 
457 end subroutine akimainter
458 
459 
460 
461 !> Interpolation using modified prescription of Akima
462 !!
463 !! The interpolation is a spline interpolation that tries
464 !! to avoid overshoots. It is doing this slightly more
465 !! radically than the Akima interpolation, but not as
466 !! strong as the pchip interpolation.
467 !!
468 !! Here, it is assumed that
469 !! the x and y arrays are given in the form of {x_i, log(y_i)},
470 !! but before interpolating the x coordinate, it is transformed
471 !! to a log space: {log(x_i), log(y_i)}.
472 !! An optional argument flin allows to control the return value:
473 !! - if flin is absent: return exp(log(y)) = y;
474 !! - if flin is present: return log(y).
475 !! .
476 !!
477 !! @see https://blogs.mathworks.com/cleve/2019/04/29/makima-piecewise-cubic-interpolation/
478 !! @author M. Reichert
479 !! @date 30.04.2023
480 subroutine makimainter(n,xp,xb,yp,res,flin)
481  implicit none
482  integer,intent(in) :: n !< array sizes
483  real(r_kind),dimension(n),intent(in) :: xp !< array of x-values
484  real(r_kind),intent(in) :: xb !< value of x
485  real(r_kind),dimension(n),intent(in) :: yp !< array of log(y)-values
486  real(r_kind),intent(out) :: res !< interpolated value of y
487  logical,intent(in),optional :: flin !< if present, return log(y) instead of y
488  ! Internal variables
489  real(r_kind) :: xval(6), yval(6), mval(5), sval(2)
490  integer :: ii, i
491  real(r_kind) :: a, b, c, d
492  real(r_kind) :: x1,x2
493  real(r_kind) :: denom
494  logical :: linear
495 
496  if(present(flin)) then
497  linear= flin
498  else
499  linear= .false.
500  end if
501 
502  xval = 0.0d0
503  yval = 0.0d0
504  mval = 0.0d0
505  sval = 0.0d0
506 
507 
508  ! Find the index for the interpolation interval
509  do ii= 1,n
510  if (xb .le. xp(ii)) exit
511  end do
512  ii = ii-1
513 
514  ! Extrapolation, value is smaller than grid
515  if (xb.le.xp(1)) then
516  res= yp(1)
517  ! Extrapolation, value is larger than grid
518  else if (xb.ge.xp(n)) then
519  res= yp(n)
520  ! Value is at left border
521  else if(ii.lt.3) then
522  ! Interpolate linear
523  x1= xb - xp(ii)
524  x2= xb - xp(ii+1)
525  res= (yp(ii)*x2 - yp(ii+1)*x1)/(x2-x1)
526  ! Value is at left border
527  else if(ii.gt.n-3) then
528  ! Interpolate linear
529  x1= xb - xp(ii)
530  x2= xb - xp(ii+1)
531  res= (yp(ii)*x2 - yp(ii+1)*x1)/(x2-x1)
532  else
533  ! Normal case, use Akima interpolation
534 
535  ! Save the required values of the grid for the interpolation
536  xval(1:6) = xp(ii-2:ii+3)
537  yval(1:6) = yp(ii-2:ii+3)
538  ! Calculate the derivative
539  mval(1:5) = (yval(2:6) - yval(1:5)) / (xval(2:6) - xval(1:5))
540 
541  ! Get si and si+1
542  i = 3
543  sval(1) = ((dabs(mval(i+1)-mval(i)) + dabs(mval(i+1)+mval(i))/2d0)*mval(i-1) + &
544  (dabs(mval(i-1)-mval(i-2)) + dabs(mval(i-1)+mval(i-2))/2d0)*mval(i))
545 
546  denom = ((dabs(mval(i+1)-mval(i)) + dabs(mval(i+1)+mval(i))/2d0) + &
547  (dabs(mval(i-1)-mval(i-2)) + dabs(mval(i-1)+mval(i-2))/2d0))
548  ! Avoid division by something very small
549  if (denom .gt. 1e-8) then
550  sval(1) = sval(1)/denom
551  else
552  sval(1) = (mval(i-1)+mval(i))/2d0
553  end if
554  sval(2) = (((dabs(mval(i+2)-mval(i+1)) + dabs(mval(i+2)+mval(i+1))/2d0)*mval(i) + &
555  (dabs(mval(i)-mval(i-1)) + dabs(mval(i)+mval(i-1))/2d0) *mval(i+1)))
556  denom = ((dabs(mval(i+2)-mval(i+1)) + dabs(mval(i+2)+mval(i+1))/2d0) + &
557  (dabs(mval(i)-mval(i-1)) + dabs(mval(i)+mval(i-1))/2d0))
558  if (denom .gt. 1e-8) then
559  sval(2) = sval(2)/denom
560  else
561  sval(2) = (mval(i)+mval(i+1))/2d0
562  end if
563  ! Get the coefficients
564  a = yval(i)
565  b = sval(1)
566  c = (3.0d0 * mval(i) - 2.0d0 * sval(1) - sval(2)) / (xval(i+1) - xval(i))
567  d = (sval(1) + sval(2) - 2.0d0 * mval(i)) / (xval(i+1) - xval(i))**2
568  ! Calculate the results
569  res = a + b * (xb - xval(i)) + c * (xb - xval(i))**2 + d * (xb - xval(i))**3
570  end if
571 
572  if(linear) then
573  return
574  else
575  res= dexp(res)
576  return
577  end if
578 
579 end subroutine makimainter
580 
581 
582 !> Get indices of the grid for given x- and y- values
583 !!
584 !! @author: M. Reichert
585 !! @date: 06.07.22
586 subroutine get_indice_2d(xval,yval,x,y,x_dim,y_dim,indices)
587  implicit none
588  integer,intent(in) :: x_dim, y_dim !< Dimension of x- and y-
589  real(r_kind),intent(in) :: xval,yval !< Function values
590  real(r_kind),dimension(x_dim),intent(in) :: x !< X-grid values
591  real(r_kind),dimension(y_dim),intent(in) :: y !< Y-grid values
592  integer,dimension(2,2),intent(out) :: indices !< Index in the grid
593  ! Internal variables
594  integer :: i !< Loop variables
595 
596  ! Index in x-direction
597  do i=2,x_dim-1
598  if (xval .lt. x(i)) exit
599  end do
600  indices(1,1)=i-1
601  indices(1,2)=i
602 
603  ! Index in y-direction
604  do i=2,y_dim-1
605  if (yval .lt. y(i)) exit
606  end do
607  indices(2,1)=i-1
608  indices(2,2)=i
609 
610 end subroutine get_indice_2d
611 
612 
613 !> Calculate derivatives at all points in a given grid.
614 !!
615 !! These derivatives are necessary for the cubic interpolation.
616 !! The subroutine returns the derivative df/dx, df/dy, and ddf/dxdy.
617 !! They are second order accurate, i.e.,
618 !! \f[ df/dx = \frac{h_1^2 f(x+h_2) + (h_2^2-h_1^2)f(x)-h_2^2 f(x-h_1) }{h_1 h_2 (h_1+h_2)} \f]
619 !!
620 !! @author: M. Reichert
621 !! @date 06.07.22
622 subroutine calc_derivative_2d(f,x,y,dfx,dfy,dfxy,x_dim,y_dim)
623  implicit none
624  integer,intent(in) :: x_dim, y_dim !< Dimension of x- and y-
625  real(r_kind),dimension(x_dim, y_dim),intent(in) :: f !< Function values
626  real(r_kind),dimension(x_dim),intent(in) :: x !< x values
627  real(r_kind),dimension(y_dim),intent(in) :: y !< y values
628  real(r_kind),dimension(x_dim,y_dim),intent(inout) :: dfx !< Derivative with respect to x
629  real(r_kind),dimension(x_dim,y_dim),intent(inout) :: dfy !< Derivative with respect to y
630  real(r_kind),dimension(x_dim,y_dim),intent(inout) :: dfxy !< Derivative with respect to x and y
631  ! Internal variables
632  integer :: i, j !< Loop variables
633  real(r_kind) :: h1,h2 !< steps
634 
635  ! Calculate derivatives for every point
636  do i=1,x_dim
637  do j=1,y_dim
638  ! X-direction
639  if ((i .gt. 1) .and. (i .lt. x_dim) ) then
640  h1 = abs(x(i) -x(i-1))
641  h2 = abs(x(i+1)-x(i))
642  dfx(i,j) = h1**2*f(i+1,j)+(h2**2-h1**2)*f(i,j)-h2**2*f(i-1,j)
643  dfx(i,j) = dfx(i,j) / (h1*h2*(h1+h2))
644  elseif (i .eq. 1) then
645  h2 = abs(x(i+1)-x(i))
646  dfx(i,j) = (f(i+1,j)-f(i,j)) / (h2)
647  elseif (i .eq. x_dim) then
648  h1 = abs(x(i)-x(i-1))
649  dfx(i,j) = (f(i,j)-f(i-1,j)) / (h1)
650  end if
651 
652  ! Y-direction
653  if ((j .gt.1) .and. (j .lt. y_dim)) then
654  h1 = abs(y(j) -y(j-1))
655  h2 = abs(y(j+1)-y(j))
656  dfy(i,j) = h1**2*f(i,j+1)+(h2**2-h1**2)*f(i,j)-h2**2*f(i,j-1)
657  dfy(i,j) = dfy(i,j) / (h1*h2*(h1+h2))
658  elseif (j .eq. 1) then
659  h2 = abs(y(j+1)-y(j))
660  dfy(i,j) = (f(i,j+1)-f(i,j)) / (h2)
661  elseif (j .eq. y_dim) then
662  h1 = abs(y(j)-y(j-1))
663  dfy(i,j) = (f(i,j)-f(i,j-1)) / (h1)
664  end if
665  end do
666  end do
667 
668  ! Mixed partial derivatives
669  do i=1, x_dim
670  do j=1, y_dim
671  if ((j .gt. 1) .and. (j .lt. y_dim) ) then
672  h1 = abs(y(j) -y(j-1))
673  h2 = abs(y(j+1)-y(j))
674  dfxy(i,j) = h1**2*dfx(i,j+1)+(h2**2-h1**2)*dfx(i,j)-h2**2*dfx(i,j-1)
675  dfxy(i,j) = dfxy(i,j) / (h1*h2*(h1+h2))
676  elseif (j .eq. 1) then
677  h2 = abs(y(j+1)-y(j))
678  dfxy(i,j) = (dfx(i,j+1)-dfx(i,j)) / (h2)
679  elseif (j .eq. y_dim) then
680  h1 = abs(y(j)-y(j-1))
681  dfxy(i,j) = (dfx(i,j)-dfx(i,j-1)) / (h1)
682  end if
683  end do
684  end do
685 
686 end subroutine calc_derivative_2d
687 
688 
689 !> Interpolate linearly on 2 Dimensional grid
690 !!
691 !! Bilinear interpolation
692 !!
693 !! @see weak_inter
694 !!
695 !! @author: M. Reichert
696 !! @date: 06.07.22
697 !! .
698 function lininter_2d(xin,yin,x,y,f,x_dim,y_dim,indice,extrapolation) result (wr)
700  implicit none
701  integer,intent(in) :: x_dim, y_dim !< Dimension of x- and y-
702  real(r_kind),intent(in) :: xin,yin !< Function input values
703  real(r_kind),dimension(x_dim),intent(in) :: x !< X values
704  real(r_kind),dimension(y_dim),intent(in) :: y !< Y values
705  real(r_kind),dimension(x_dim,y_dim),intent(in) :: f !< Function and derivatives
706  integer,optional,intent(in) :: extrapolation !< 1: None; 2: constant
707  integer,dimension(2,2),optional,intent(in) :: indice !< Index where to interpolate
708  real(r_kind) :: wr !< Interpolated value (output)
709  ! Internal Variables
710  integer,dimension(2,2) :: ind !< Index where to interpolate
711  real(r_kind) :: xval,yval !< Internal function input values
712  real(r_kind) :: x1, x2, y1, y2 !< Corner values of the grid
713  integer :: extr !< Internal extr. flag
714  real(r_kind),dimension(2,2) :: weights !< Weights for interpolation
715 
716  ! Store variables in internal variable in order to be able to change them
717  xval=xin; yval=yin
718 
719  ! Get the index
720  if (.not. present(indice)) then
721  call get_indice_2d(xval,yval,x,y,x_dim,y_dim,ind)
722  else
723  ind(:,:) = indice(:,:)
724  end if
725 
726  ! Check that an extrapolation value is present
727  if (.not. present(extrapolation)) extr=1
728  if (present(extrapolation)) extr=extrapolation
729 
730  ! Check if extrapolation is necessary
731  if ((xval .lt. x(1)) .or. (yval .lt. y(1))) then
732  select case(extr)
733  case (1)
734  call raise_exception("Value for interpolation was out of bounds.",&
735  "inter_module",270003)
736  case (2)
737  xval= max(x(1),xval)
738  yval= max(y(1),yval)
739  case default
740  call raise_exception("Unknown interpolation type.",&
741  "inter_module",270004)
742  end select
743  elseif ((xval .gt. x(x_dim)) .or. (yval .gt. y(y_dim))) then
744  select case(extr)
745  case (1)
746  call raise_exception("Value for interpolation was out of bounds.",&
747  "inter_module",270003)
748  case (2)
749  xval= min(x(x_dim),xval)
750  yval= min(y(y_dim),yval)
751  case default
752  call raise_exception("Unknown interpolation type.",&
753  "inter_module",270004)
754  end select
755  end if
756 
757  ! Define x and y border points
758  x1 = x(ind(1,1))
759  x2 = x(ind(1,2))
760  y1 = y(ind(2,1))
761  y2 = y(ind(2,2))
762 
763  ! Weights for linear interpolation
764  weights(1,1) = ((x2-xval)*(y2-yval))/((x2-x1)*(y2-y1))
765  weights(1,2) = ((x2-xval)*(yval-y1))/((x2-x1)*(y2-y1))
766  weights(2,1) = ((xval-x1)*(y2-yval))/((x2-x1)*(y2-y1))
767  weights(2,2) = ((xval-x1)*(yval-y1))/((x2-x1)*(y2-y1))
768 
769  ! Get the interpolated value
770  wr = weights(1,1)*f(ind(1,1),ind(2,1)) + &
771  weights(1,2)*f(ind(1,1),ind(2,2)) + &
772  weights(2,1)*f(ind(1,2),ind(2,1)) + &
773  weights(2,2)*f(ind(1,2),ind(2,2))
774 
775  return
776 
777 end function lininter_2d
778 
779 
780 
781 
782 !> Cubic interpolate on 2-Dimensional grid
783 !!
784 !! This interpolation is a bicubic interpolation. For values outside the grid,
785 !! either a constant extrapolation is performed or an error is raised.
786 !!
787 !! @see weak_inter_bilinear, weak_inter, readweak_logft
788 !!
789 !! @author: Moritz Reichert
790 !! @date: 14.03.22
791 !!
792 !! @see [Bicubic interpolation](https://en.wikipedia.org/wiki/Bicubic_interpolation)
793 !! .
794 function cubinter_2d(xin,yin,x,y,f,dfx,dfy,dfxy,x_dim,y_dim,indice,extrapolation) result (wr)
796  implicit none
797  integer,intent(in) :: x_dim, y_dim !< Dimension of x- and y-
798  real(r_kind),intent(in) :: xin,yin !< Function input values
799  real(r_kind),dimension(x_dim),intent(in) :: x !< X values
800  real(r_kind),dimension(y_dim),intent(in) :: y !< Y values
801  real(r_kind),dimension(x_dim,y_dim),intent(in) :: f,dfx,dfy,dfxy !< Function and derivatives
802  integer,optional,intent(in) :: extrapolation !< 1: None; 2: constant
803  integer,dimension(2,2),optional,intent(in) :: indice !< Index where to interpolate
804  real(r_kind) :: wr !< Interpolated value (output)
805  ! Internal Variables
806  real(r_kind) :: xval,yval !< Internal function input values
807  real(r_kind),dimension(16,16) :: a_inv !< Inverse matrix to solve eq.
808  real(r_kind),dimension(16) :: vec,alphas !< Vectors for lin. eq.
809  integer,dimension(2,2) :: ind !< Index where to interpolate
810  real(r_kind),dimension(4,4) :: alphas_r !< Reshaped alpha coefficients
811  real(r_kind) :: x1, x2, y1, y2 !< Corner values of the grid
812  real(r_kind) :: xbar,ybar !< Helper variables
813  integer :: i,j !< Loop variable
814  integer :: extr !< Internal value for extr. flag
815 
816  ! Store variables in internal variable in order to be able to change them
817  xval=xin; yval=yin
818 
819  ! Get the index
820  if (.not. present(indice)) then
821  call get_indice_2d(xval,yval,x,y,x_dim,y_dim,ind)
822  else
823  ind(:,:) = indice(:,:)
824  end if
825 
826  ! Check that an extrapolation value is present
827  if (.not. present(extrapolation)) extr=2
828  if (present(extrapolation)) extr=extrapolation
829 
830  ! Check if extrapolation is necessary
831  if ((xval .lt. x(1)) .or. (yval .lt. y(1))) then
832  select case(extr)
833  case (1)
834  call raise_exception("Value for interpolation was out of bounds.",&
835  "inter_module",270003)
836  case (2)
837  xval= max(x(1),xval)
838  yval= max(y(1),yval)
839  case default
840  call raise_exception("Unknown interpolation type.",&
841  "inter_module",270004)
842  end select
843  elseif ((xval .gt. x(x_dim)) .or. (yval .gt. y(y_dim))) then
844  select case(extr)
845  case (1)
846  call raise_exception("Value for interpolation was out of bounds.",&
847  "inter_module",270003)
848  case (2)
849  xval= min(x(x_dim),xval)
850  yval= min(y(y_dim),yval)
851  case default
852  call raise_exception("Unknown interpolation type.",&
853  "inter_module",270004)
854  end select
855  end if
856 
857  ! Define x and y border points
858  x1 = x(ind(1,1))
859  x2 = x(ind(1,2))
860  y1 = y(ind(2,1))
861  y2 = y(ind(2,2))
862 
863  ! Define xbar and ybar
864  xbar = (xval-x1)/(x2-x1)
865  ybar = (yval-y1)/(y2-y1)
866 
867  ! Define helper inverse matrix to solve equation for determining
868  ! alpha coefficients later
869  a_inv = reshape( (/ 1, 0,-3, 2, 0, 0, 0, 0,-3, 0, 9,-6, 2, 0,-6, 4, &
870  0, 0, 3,-2, 0, 0, 0, 0, 0, 0,-9, 6, 0, 0, 6,-4, &
871  0, 0, 0, 0, 0, 0, 0, 0, 3, 0,-9, 6,-2, 0, 6,-4, &
872  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 9,-6, 0, 0,-6, 4, &
873  0, 1,-2, 1, 0, 0, 0, 0, 0,-3, 6,-3, 0, 2,-4, 2, &
874  0, 0,-1, 1, 0, 0, 0, 0, 0, 0, 3,-3, 0, 0,-2, 2, &
875  0, 0, 0, 0, 0, 0, 0, 0, 0, 3,-6, 3, 0,-2, 4,-2, &
876  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-3, 3, 0, 0, 2,-2, &
877  0, 0, 0, 0, 1, 0,-3, 2,-2, 0, 6,-4, 1, 0,-3, 2, &
878  0, 0, 0, 0, 0, 0, 3,-2, 0, 0,-6, 4, 0, 0, 3,-2, &
879  0, 0, 0, 0, 0, 0, 0, 0,-1, 0, 3,-2, 1, 0,-3, 2, &
880  0, 0, 0, 0, 0, 0, 0, 0, 0, 0,-3, 2, 0, 0, 3,-2, &
881  0, 0, 0, 0, 0, 1,-2, 1, 0,-2, 4,-2, 0, 1,-2, 1, &
882  0, 0, 0, 0, 0, 0,-1, 1, 0, 0, 2,-2, 0, 0,-1, 1, &
883  0, 0, 0, 0, 0, 0, 0, 0, 0,-1, 2,-1, 0, 1,-2, 1, &
884  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,-1, 0, 0,-1, 1 &
885  /), (/16, 16/) ) !< Helper Matrix
886 
887  ! Values for cubic interpolation
888  vec = (/f(ind(1,1),ind(2,1)),f(ind(1,2),ind(2,1)),f(ind(1,1),ind(2,2)),f(ind(1,2),ind(2,2)),&
889  (x2-x1)*dfx(ind(1,1),ind(2,1)),(x2-x1)*dfx(ind(1,2),ind(2,1)),(x2-x1)*dfx(ind(1,1),ind(2,2)),(x2-x1)*dfx(ind(1,2),ind(2,2)),&
890  (y2-y1)*dfy(ind(1,1),ind(2,1)),(y2-y1)*dfy(ind(1,2),ind(2,1)),(y2-y1)*dfy(ind(1,1),ind(2,2)),(y2-y1)*dfy(ind(1,2),ind(2,2)),&
891  (x2-x1)*(y2-y1)*dfxy(ind(1,1),ind(2,1)),(x2-x1)*(y2-y1)*dfxy(ind(1,2),ind(2,1)),&
892  (x2-x1)*(y2-y1)*dfxy(ind(1,1),ind(2,2)),(x2-x1)*(y2-y1)*dfxy(ind(1,2),ind(2,2))/)
893 
894  ! Solve lin. equation to get alpha coefficients
895  alphas = matmul(a_inv,vec)
896  ! Reshape to 4 x 4
897  alphas_r = reshape(alphas, (/4,4/))
898 
899  ! Calculate the interpolation result
900  wr = 0
901  do i=1,4
902  do j=1,4
903  wr = wr + alphas_r(i,j)*xbar**(i-1)*ybar**(j-1)
904  end do
905  end do
906 
907 end function cubinter_2d
908 
909 
910 
911 
912 
913 !>
914 !! The inverse of the 1D interpolation function.
915 !!
916 !! Finds the inverse of cubinter and returns the value of x.
917 !! This is done by using a Newton-Raphson method.
918 !!
919 !! @note The result depends on xb, because x_i does not necessarily have
920 !! to be monotonic. The result is the closest value to xb
921 !!
922 !! @author M. Reichert
923 !!
924 !! @see timestep_module::restrict_timestep
925 !!
926 !! \b Edited:
927 !! - 24.11.15
928 !! - 30.01.23
929 !! .
930 subroutine inverse_interp1d (n,xp,xb,yp,res,flin,itype)
931  use parameter_class, only: interp_mode
932  implicit none
933 
934  integer,intent(in) :: n !< array sizes
935  real(r_kind),dimension(n),intent(in) :: xp !< array of log(x)-values
936  real(r_kind),intent(in) :: xb !< value of x
937  real(r_kind),dimension(n),intent(in) :: yp !< array of y-values
938  real(r_kind),intent(inout) :: res !< interpolated value of y
939  logical,intent(in),optional :: flin !< flag for linear interpolation
940  integer,intent(in),optional :: itype !< interpolation type
941  !
942  integer :: ii
943  integer, parameter :: maxiter=50
944  real(r_kind) :: x1,x2
945  real(r_kind) :: m,y1,y2,xnew,b
946  real(r_kind),parameter :: diff=1d-4
947  real(r_kind),parameter :: tol =1d-10
948  logical :: converged
949  logical :: linear
950  integer :: interp_type
951 
952  ! Set default values
953  if (.not. present(itype)) then
954  interp_type = interp_mode
955  else
956  interp_type = itype
957  end if
958 
959  if (.not. present(flin)) then
960  linear = .false.
961  else
962  linear = flin
963  end if
964 
965  x1 = res
966  x2=x1+diff
967  converged = .false.
968 
969  inner_loop: do ii=1,maxiter
970  ! Calculate slope
971  call interp1d(n,yp,x1,xp,y1,linear,interp_type)
972  call interp1d(n,yp,x2,xp,y2,linear,interp_type)
973  y1 = y1-xb
974  y2 = y2-xb
975  if (x1 .ne. x2) then
976  m = (y1-y2)/(x1-x2)
977  else
978  converged = .false.
979  exit inner_loop
980  endif
981 
982  ! calculate new x value
983  b = y1-m*x1
984 
985  ! Exit if the slope is zero
986  if (m .eq. 0) then
987  converged = .false.
988  ! Give a very large negative number
989  exit
990  end if
991 
992  xnew = -b/m
993 
994  ! Exit if converged
995  if (abs(x1-xnew) .lt. tol) then
996  converged = .true.
997  exit inner_loop
998  end if
999 
1000  ! Reshuffle variables if it is not converged
1001  x1 = xnew
1002  x2 = x1+diff
1003  end do inner_loop
1004 
1005  ! Take NR value if it converged
1006  if (converged) then
1007  res = xnew
1008  else
1009  res = res
1010  end if
1011 
1012  return
1013 end subroutine inverse_interp1d
1014 
1015 end module inter_module
inter_module
Module inter_module with interpolation routines.
Definition: inter_module.f90:13
inter_module::inverse_interp1d
subroutine, public inverse_interp1d(n, xp, xb, yp, res, flin, itype)
The inverse of the 1D interpolation function.
Definition: inter_module.f90:931
error_msg_class
Error handling routines.
Definition: error_msg_class.f90:16
error_msg_class::int_to_str
character(:) function, allocatable, public int_to_str(num)
Converts a given integer to a string.
Definition: error_msg_class.f90:224
inter_module::cubinter
subroutine, private cubinter(n, xp, xb, yp, res, flin)
Cubic interpolation in log-log space. Here, it is assumed that.
Definition: inter_module.f90:161
inter_module::get_indice_2d
subroutine, public get_indice_2d(xval, yval, x, y, x_dim, y_dim, indices)
Get indices of the grid for given x- and y- values.
Definition: inter_module.f90:587
error_msg_class::raise_exception
subroutine, public raise_exception(msg, sub, error_code)
Raise a exception with a given error message.
Definition: error_msg_class.f90:245
inter_module::pchipinter
subroutine, private pchipinter(n, xp, xb, yp, res, flin)
PCHIP interpolation in lin-log space.
Definition: inter_module.f90:243
inter_module::interp1d
subroutine, public interp1d(n, xp, xb, yp, res, flin, itype)
Interface for 1D interpolation routines.
Definition: inter_module.f90:99
inter_module::makimainter
subroutine, private makimainter(n, xp, xb, yp, res, flin)
Interpolation using modified prescription of Akima.
Definition: inter_module.f90:481
parameter_class::interp_mode
integer interp_mode
Mode for interpolation of temperature and density.
Definition: parameter_class.f90:151
r_kind
#define r_kind
Definition: macros.h:46
inter_module::lininter
subroutine, private lininter(n, ref_array, ref_value, array, res, flin)
Linear interpolation in lin-log space.
Definition: inter_module.f90:40
inter_module::cubinter_2d
real(r_kind) function, public cubinter_2d(xin, yin, x, y, f, dfx, dfy, dfxy, x_dim, y_dim, indice, extrapolation)
Cubic interpolate on 2-Dimensional grid.
Definition: inter_module.f90:795
inter_module::akimainter
subroutine, private akimainter(n, xp, xb, yp, res, flin)
Interpolation using prescription of Akima.
Definition: inter_module.f90:364
inter_module::lininter_2d
real(r_kind) function, public lininter_2d(xin, yin, x, y, f, x_dim, y_dim, indice, extrapolation)
Interpolate linearly on 2 Dimensional grid.
Definition: inter_module.f90:699
inter_module::calc_derivative_2d
subroutine, public calc_derivative_2d(f, x, y, dfx, dfy, dfxy, x_dim, y_dim)
Calculate derivatives at all points in a given grid.
Definition: inter_module.f90:623
pi
real(r_kind) function pi()
Further information: http://netlib.org/quadpack/index.html https://orion.math.iastate....
Definition: quadpack_module.f90:193
parameter_class
Contains all runtime parameters as well as phys and math constants.
Definition: parameter_class.f90:24