MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_eos_interp.t
Go to the documentation of this file.
1!=============================================================================
2!> Interpolation kernels for the EoS tables (pure math; no EoS state).
3!>
4!> Every routine operates on an eos_table_container passed by the caller:
5!> bicubic_lookup / bilinear_lookup -- dispatch on tc%is_uniform and return
6!> the interpolated value (monotone bicubic PCHIP, or bilinear).
7!> interp_pchip_interleaved(_nu) -- evaluate several quantities sharing
8!> one (nH, eint/nH) grid in a single cache-friendly pass.
9!> find_index_guard -- O(1) bracket lookup on a non-uniform
10!> axis (falls back to binary search if no guard array is built).
11!> Uniform grids use an affine index; non-uniform (_nu) grids use explicit
12!> node arrays. The PCHIP kernel is monotone (no overshoot on each 1D slice).
13!=============================================================================
16 implicit none
17 private
18
22 public :: precompute_step_inv
23
24contains
25
26 !> The single monotone cubic Hermite (PCHIP-style) interval kernel for this
27 !> module. Interpolates on [p1,p2] (t in [0,1]) from 4 uniformly spaced
28 !> samples p0..p3: secant slopes d0,d1,d2; interior derivatives by harmonic
29 !> mean with a monotonicity sign-clamp and a Hyman magnitude clamp (no
30 !> overshoot on the interval). EVERY PCHIP slice in this module -- the
31 !> uniform/non-uniform bicubic routines and both interleaved evaluators --
32 !> routes through here, so the limiter exists in exactly one place.
33 pure double precision function pchip_interval(p0, p1, p2, p3, t) result(v)
34 double precision, intent(in) :: p0, p1, p2, p3, t
35 double precision :: d0, d1, d2, m1, m2
36 double precision :: tt, ttt, h00, h10, h01, h11
37 double precision :: s, a1, a2, lim
38
39 d0 = p1 - p0
40 d1 = p2 - p1
41 d2 = p3 - p2
42
43 if (d1 == 0.0d0) then
44 m1 = 0.0d0
45 m2 = 0.0d0
46 else
47 if (d0*d1 <= 0.0d0) then
48 m1 = 0.0d0
49 else
50 m1 = 2.0d0*d0*d1 / (d0 + d1)
51 end if
52 if (d1*d2 <= 0.0d0) then
53 m2 = 0.0d0
54 else
55 m2 = 2.0d0*d1*d2 / (d1 + d2)
56 end if
57 s = sign(1.0d0, d1)
58 a1 = s*m1
59 a2 = s*m2
60 if (a1 < 0.0d0) a1 = 0.0d0
61 if (a2 < 0.0d0) a2 = 0.0d0
62 lim = 3.0d0*abs(d1)
63 if (a1 > lim) a1 = lim
64 if (a2 > lim) a2 = lim
65 m1 = s*a1
66 m2 = s*a2
67 end if
68
69 tt = t*t
70 ttt = tt*t
71 h00 = 2.0d0*ttt - 3.0d0*tt + 1.0d0
72 h10 = ttt - 2.0d0*tt + t
73 h01 = -2.0d0*ttt + 3.0d0*tt
74 h11 = ttt - tt
75
76 v = h00*p1 + h10*m1 + h01*p2 + h11*m2
77 end function pchip_interval
78
79 !> Fast adaptive index lookup. Returns the smallest 1-based ii such that
80 !> nodes(ii) >= val. Falls back to binary search if the guard array is
81 !> not built (M = 0). Equivalent in result to find_index_bsearch but O(1)
82 !> when the guard is present.
83 pure integer function find_index_guard(nodes, n, val, guard, M, scale_M) result(ix)
85 double precision, intent(in) :: nodes(n), val, scale_m
86 integer, intent(in) :: n, m
87 integer, intent(in) :: guard(m)
88 integer :: k
89 if (m > 0) then
90 k = 1 + min(m-1, max(0, int(floor((val - nodes(1)) * scale_m))))
91 ix = guard(k)
92 do while (ix < n .and. nodes(ix) < val)
93 ix = ix + 1
94 end do
95 else
96 ix = find_index_bsearch(nodes(1:n), val)
97 end if
98 end function find_index_guard
99
100 pure double precision function interp_clamped_bilinear_table(vary, varx, table, nx, ny, vmin_y, vmax_y, vmin_x, vmax_x) result(z)
101 double precision, intent(in) :: vary, varx
102 integer, intent(in) :: nx, ny
103 double precision, intent(in) :: table(ny, nx)
104 double precision, intent(in) :: vmin_y, vmax_y, vmin_x, vmax_x
105
106 double precision :: fx, fy, tx, ty, rx, ry, xstep_inv, ystep_inv
107 integer :: ix, iy, ix1, iy1
108
109 xstep_inv = dble(nx-1) / (vmax_x - vmin_x)
110 ystep_inv = dble(ny-1) / (vmax_y - vmin_y)
111
112 !> fractional positions
113 fx = (varx - vmin_x) * xstep_inv
114 fy = (vary - vmin_y) * ystep_inv
115
116 !> clamp to [0, nx-1] and [0, ny-1]
117 rx = max(0.0d0, min(fx, dble(nx-1)))
118 ry = max(0.0d0, min(fy, dble(ny-1)))
119
120 ix = int(rx)
121 iy = int(ry)
122
123 ix1 = min(ix+1, nx-1)
124 iy1 = min(iy+1, ny-1)
125
126 tx = rx - dble(ix)
127 ty = ry - dble(iy)
128
129 z = (1.0d0-ty) * ((1.0d0-tx)*table(iy+1, ix+1) + tx*table(iy+1, ix1+1)) &
130 + ty * ((1.0d0-tx)*table(iy1+1,ix+1) + tx*table(iy1+1,ix1+1))
132
133 pure double precision function interp_clamped_monotone_bicubic_table( &
134 vary, varx, table, nx, ny, vmin_y, vmax_y, vmin_x, vmax_x) result(z)
135
136 !> Monotone bicubic (practical tensor-product variant):
137 !> - Do a *monotone* cubic Hermite (PCHIP-style) interpolation in x on 4 points
138 !> for each of 4 surrounding y-rows (j-1..j+2) -> gives 4 intermediate values.
139 !> - Then do the same monotone cubic Hermite interpolation in y on those 4 values.
140 !>
141 !> This construction is monotone along x-lines and y-lines (no overshoot on each 1D slice)
142 !> https://jacobwilliams.github.io/PCHIP/
143 !> https://docs.scipy.org/doc/scipy/reference/generated/scipy.interpolate.PchipInterpolator.html
144
145 double precision, intent(in) :: vary, varx
146 integer, intent(in) :: nx, ny
147 double precision, intent(in) :: table(ny, nx)
148 double precision, intent(in) :: vmin_y, vmax_y, vmin_x, vmax_x
149
150 double precision :: fx, fy, rx, ry, tx, ty, xstep_inv, ystep_inv
151 integer :: ix, iy
152
153 double precision :: g0, g1, g2, g3
154
155 xstep_inv = dble(nx-1) / (vmax_x - vmin_x)
156 ystep_inv = dble(ny-1) / (vmax_y - vmin_y)
157
158 fx = (varx - vmin_x) * xstep_inv
159 fy = (vary - vmin_y) * ystep_inv
160
161 rx = max(0.0d0, min(fx, dble(nx-1)))
162 ry = max(0.0d0, min(fy, dble(ny-1)))
163
164 ix = int(rx)
165 iy = int(ry)
166
167 tx = rx - dble(ix)
168 ty = ry - dble(iy)
169
170 !> Four rows around iy: (iy-1, iy, iy+1, iy+2)
171 g0 = x_interp_row(iy-1, ix, tx)
172 g1 = x_interp_row(iy , ix, tx)
173 g2 = x_interp_row(iy+1, ix, tx)
174 g3 = x_interp_row(iy+2, ix, tx)
175
176 !> Now monotone cubic in y between g1 and g2 with neighbors g0,g3
177 z = y_interp_from4(g0, g1, g2, g3, ty)
178
179 contains
180
181 pure integer function clampi(i, lo, hi) result(o)
182 integer, intent(in) :: i, lo, hi
183 o = max(lo, min(hi, i))
184 end function clampi
185
186 pure double precision function x_interp_row(j, i, t) result(v)
187 !> Interpolate in x for fixed row j at cell starting index i (0-based),
188 !> using points i-1, i, i+1, i+2 with clamped indexing.
189 integer, intent(in) :: j, i
190 double precision, intent(in) :: t
191 integer :: i0, i1, i2, i3, jj
192 double precision :: p0, p1, p2, p3
193
194 jj = clampi(j, 0, ny-1)
195 i0 = clampi(i-1, 0, nx-1)
196 i1 = clampi(i , 0, nx-1)
197 i2 = clampi(i+1, 0, nx-1)
198 i3 = clampi(i+2, 0, nx-1)
199
200 p0 = table(jj+1, i0+1)
201 p1 = table(jj+1, i1+1)
202 p2 = table(jj+1, i2+1)
203 p3 = table(jj+1, i3+1)
204
205 v = pchip_interval(p0, p1, p2, p3, t)
206 end function x_interp_row
207
208 pure double precision function y_interp_from4(v0, v1, v2, v3, t) result(v)
209 !> Interpolate in y between v1 and v2 using surrounding v0..v3, t in [0,1]
210 double precision, intent(in) :: v0, v1, v2, v3, t
211 v = pchip_interval(v0, v1, v2, v3, t)
212 end function y_interp_from4
213
214 end function interp_clamped_monotone_bicubic_table
215
216 !> Dispatch wrapper: bicubic lookup that branches on the table's
217 !> is_uniform flag. Hides the uniform-vs-adaptive distinction from
218 !> call sites -- they pass (var1, var2, eos%<table>) and get the
219 !> interpolated value, regardless of grid type.
220 pure double precision function bicubic_lookup(var1, var2, tc) result(z)
221 double precision, intent(in) :: var1, var2
222 type(eos_table_container), intent(in) :: tc
223 if (tc%is_uniform) then
224 !> Existing uniform routine signature has (vary, varx, ...) where
225 !> vary is the FIRST table axis (var1). Caller var1 -> vary,
226 !> caller var2 -> varx. The existing argument ordering passes
227 !> dim1, dim2 and the var1 / var2 bounds; the routine's internal
228 !> "y/x" labelling is consistent with that ordering for square
229 !> tables (which all our tables are).
230 z = interp_clamped_monotone_bicubic_table(var1, var2, tc%table, &
231 tc%dim1, tc%dim2, &
232 tc%var1_min, tc%var1_max, tc%var2_min, tc%var2_max)
233 else
234 z = interp_clamped_monotone_bicubic_table_nu(var1, var2, tc%table, &
235 tc%dim1, tc%dim2, tc%var1_nodes, tc%var2_nodes, &
236 tc%guard_1, tc%guard_M_1, tc%guard_scale_1, &
237 tc%guard_2, tc%guard_M_2, tc%guard_scale_2)
238 end if
239 end function bicubic_lookup
240
241 !> Dispatch wrapper: bilinear lookup that branches on is_uniform.
242 pure double precision function bilinear_lookup(var1, var2, tc) result(z)
243 double precision, intent(in) :: var1, var2
244 type(eos_table_container), intent(in) :: tc
245 if (tc%is_uniform) then
246 z = interp_clamped_bilinear_table(var1, var2, tc%table, &
247 tc%dim1, tc%dim2, &
248 tc%var1_min, tc%var1_max, tc%var2_min, tc%var2_max)
249 else
250 z = interp_clamped_bilinear_table_nu(var1, var2, tc%table, &
251 tc%dim1, tc%dim2, tc%var1_nodes, tc%var2_nodes, &
252 tc%guard_1, tc%guard_M_1, tc%guard_scale_1, &
253 tc%guard_2, tc%guard_M_2, tc%guard_scale_2)
254 end if
255 end function bilinear_lookup
256
257 !> Non-uniform-grid bilinear lookup. Same semantics as
258 !> interp_clamped_bilinear_table but with explicit per-axis node arrays.
259 !> Use binary search to locate the enclosing cell, then compute the
260 !> local fractional coordinate from the actual node positions. Falls
261 !> back to the boundary node value outside the grid (clamped).
262 !>
263 !> Convention (matches struct fields var1_nodes / var2_nodes):
264 !> var1 has dim1 nodes, var2 has dim2 nodes.
265 !> Table is stored as table(dim1, dim2) -- Fortran column-major,
266 !> matching how generate_lte_tables.py writes it (data.T.tofile).
267 !> Element (i1, i2) of the table corresponds to node positions
268 !> (var1_nodes(i1), var2_nodes(i2)).
269 pure double precision function interp_clamped_bilinear_table_nu( &
270 var1, var2, table, dim1, dim2, var1_nodes, var2_nodes, &
271 guard_1, M_1, scale_1, guard_2, M_2, scale_2) result(z)
272 double precision, intent(in) :: var1, var2
273 integer, intent(in) :: dim1, dim2
274 double precision, intent(in) :: table(dim1, dim2)
275 double precision, intent(in) :: var1_nodes(dim1), var2_nodes(dim2)
276 integer, intent(in) :: m_1, m_2
277 double precision, intent(in) :: scale_1, scale_2
278 integer, intent(in) :: guard_1(m_1), guard_2(m_2)
279
280 integer :: i1, i2, i1p, i2p, ii
281 double precision :: t1, t2
282
283 !> Axis-1 cell location
284 if (var1 <= var1_nodes(1)) then
285 i1 = 0; t1 = 0.0d0
286 else if (var1 >= var1_nodes(dim1)) then
287 i1 = dim1 - 2; t1 = 1.0d0
288 else
289 ii = find_index_guard(var1_nodes, dim1, var1, guard_1, m_1, scale_1)
290 i1 = max(0, min(ii - 2, dim1 - 2)) !> 0-based cell index
291 t1 = (var1 - var1_nodes(i1+1)) / (var1_nodes(i1+2) - var1_nodes(i1+1))
292 t1 = max(0.0d0, min(t1, 1.0d0))
293 end if
294
295 !> Axis-2 cell location
296 if (var2 <= var2_nodes(1)) then
297 i2 = 0; t2 = 0.0d0
298 else if (var2 >= var2_nodes(dim2)) then
299 i2 = dim2 - 2; t2 = 1.0d0
300 else
301 ii = find_index_guard(var2_nodes, dim2, var2, guard_2, m_2, scale_2)
302 i2 = max(0, min(ii - 2, dim2 - 2))
303 t2 = (var2 - var2_nodes(i2+1)) / (var2_nodes(i2+2) - var2_nodes(i2+1))
304 t2 = max(0.0d0, min(t2, 1.0d0))
305 end if
306
307 i1p = min(i1+1, dim1-1)
308 i2p = min(i2+1, dim2-1)
309
310 z = (1.0d0-t2) * ((1.0d0-t1)*table(i1+1, i2+1) + t1*table(i1p+1, i2+1)) &
311 + t2 * ((1.0d0-t1)*table(i1+1, i2p+1) + t1*table(i1p+1, i2p+1))
312 end function interp_clamped_bilinear_table_nu
313
314 !> Non-uniform-grid monotone bicubic lookup. Same semantics as
315 !> interp_clamped_monotone_bicubic_table but with explicit per-axis
316 !> node arrays. The PCHIP kernel itself is unchanged: it uses the
317 !> "uniform" secant-slope formulation operating on the local 4-tuple
318 !> of values (this is the standard pragmatic approach for adaptive
319 !> grids with rectangular node structure -- strictly suboptimal vs.
320 !> proper non-uniform PCHIP but empirically near-optimal for grids
321 !> designed by curvature equidistribution).
322 pure double precision function interp_clamped_monotone_bicubic_table_nu( &
323 var1, var2, table, dim1, dim2, var1_nodes, var2_nodes, &
324 guard_1, M_1, scale_1, guard_2, M_2, scale_2) result(z)
325 double precision, intent(in) :: var1, var2
326 integer, intent(in) :: dim1, dim2
327 double precision, intent(in) :: table(dim1, dim2)
328 double precision, intent(in) :: var1_nodes(dim1), var2_nodes(dim2)
329 integer, intent(in) :: m_1, m_2
330 double precision, intent(in) :: scale_1, scale_2
331 integer, intent(in) :: guard_1(m_1), guard_2(m_2)
332
333 integer :: i1, i2, ii
334 double precision :: t1, t2
335 double precision :: g0, g1, g2, g3
336
337 !> Axis-1 cell location
338 if (var1 <= var1_nodes(1)) then
339 i1 = 0; t1 = 0.0d0
340 else if (var1 >= var1_nodes(dim1)) then
341 i1 = dim1 - 2; t1 = 1.0d0
342 else
343 ii = find_index_guard(var1_nodes, dim1, var1, guard_1, m_1, scale_1)
344 i1 = max(0, min(ii - 2, dim1 - 2))
345 t1 = (var1 - var1_nodes(i1+1)) / (var1_nodes(i1+2) - var1_nodes(i1+1))
346 t1 = max(0.0d0, min(t1, 1.0d0))
347 end if
348
349 !> Axis-2 cell location
350 if (var2 <= var2_nodes(1)) then
351 i2 = 0; t2 = 0.0d0
352 else if (var2 >= var2_nodes(dim2)) then
353 i2 = dim2 - 2; t2 = 1.0d0
354 else
355 ii = find_index_guard(var2_nodes, dim2, var2, guard_2, m_2, scale_2)
356 i2 = max(0, min(ii - 2, dim2 - 2))
357 t2 = (var2 - var2_nodes(i2+1)) / (var2_nodes(i2+2) - var2_nodes(i2+1))
358 t2 = max(0.0d0, min(t2, 1.0d0))
359 end if
360
361 !> Four columns around i2: (i2-1, i2, i2+1, i2+2), interpolate
362 !> in axis-1 along each fixed-i2 column, then pchip in axis-2.
363 g0 = ax1_interp_col_nu(i2-1, i1, t1)
364 g1 = ax1_interp_col_nu(i2 , i1, t1)
365 g2 = ax1_interp_col_nu(i2+1, i1, t1)
366 g3 = ax1_interp_col_nu(i2+2, i1, t1)
367
368 z = pchip_interval(g0, g1, g2, g3, t2)
369
370 contains
371
372 pure integer function clampi_nu(i, lo, hi) result(o)
373 integer, intent(in) :: i, lo, hi
374 o = max(lo, min(hi, i))
375 end function clampi_nu
376
377 pure double precision function ax1_interp_col_nu(j2, i1_cell, t) result(v)
378 !> Interpolate in axis-1 at fixed axis-2 column j2 (0-based,
379 !> clamped) using axis-1 cell starting index i1_cell (0-based),
380 !> with the 4-point stencil i1_cell-1, i1_cell, i1_cell+1, i1_cell+2.
381 integer, intent(in) :: j2, i1_cell
382 double precision, intent(in) :: t
383 integer :: a0, a1, a2, a3, b
384 double precision :: p0, p1, p2, p3
385
386 b = clampi_nu(j2, 0, dim2-1)
387 a0 = clampi_nu(i1_cell-1, 0, dim1-1)
388 a1 = clampi_nu(i1_cell , 0, dim1-1)
389 a2 = clampi_nu(i1_cell+1, 0, dim1-1)
390 a3 = clampi_nu(i1_cell+2, 0, dim1-1)
391
392 p0 = table(a0+1, b+1)
393 p1 = table(a1+1, b+1)
394 p2 = table(a2+1, b+1)
395 p3 = table(a3+1, b+1)
396
397 v = pchip_interval(p0, p1, p2, p3, t)
398 end function ax1_interp_col_nu
399
400 end function interp_clamped_monotone_bicubic_table_nu
401
402 !> Interleaved PCHIP: evaluate N quantities at the same (vary, varx) point.
403 !> Table layout: table_il(nq, ny, nx) where nq quantities share the same grid.
404 !> All nq values at each grid point are contiguous in memory (cache-optimal).
405 subroutine interp_pchip_interleaved(vary, varx, table_il, nq, nx, ny, &
406 vmin_y, vmax_y, vmin_x, vmax_x, results)
407 double precision, intent(in) :: vary, varx
408 integer, intent(in) :: nq, nx, ny
409 double precision, intent(in) :: table_il(nq, ny, nx)
410 double precision, intent(in) :: vmin_y, vmax_y, vmin_x, vmax_x
411 double precision, intent(out) :: results(nq)
412
413 double precision :: fx, fy, rx, ry, tx, ty, xstep_inv, ystep_inv
414 integer :: ix, iy, q
415 integer :: i0, i1, i2, i3, j0, j1, j2, j3
416 double precision :: g0(nq), g1(nq), g2(nq), g3(nq)
417 double precision :: p0, p1, p2, p3
418
419 xstep_inv = dble(nx-1) / (vmax_x - vmin_x)
420 ystep_inv = dble(ny-1) / (vmax_y - vmin_y)
421
422 fx = (varx - vmin_x) * xstep_inv
423 fy = (vary - vmin_y) * ystep_inv
424
425 rx = max(0.0d0, min(fx, dble(nx-1)))
426 ry = max(0.0d0, min(fy, dble(ny-1)))
427
428 ix = int(rx); iy = int(ry)
429 tx = rx - dble(ix); ty = ry - dble(iy)
430
431 ! Clamped column indices (shared across all rows and quantities)
432 i0 = max(0, min(nx-1, ix-1))
433 i1 = max(0, min(nx-1, ix))
434 i2 = max(0, min(nx-1, ix+1))
435 i3 = max(0, min(nx-1, ix+2))
436
437 ! For each of 4 y-rows, interpolate in x for ALL quantities
438 do j0 = 0, 3
439 j1 = max(0, min(ny-1, iy - 1 + j0))
440 do q = 1, nq
441 p0 = table_il(q, j1+1, i0+1)
442 p1 = table_il(q, j1+1, i1+1)
443 p2 = table_il(q, j1+1, i2+1)
444 p3 = table_il(q, j1+1, i3+1)
445 select case(j0)
446 case(0); g0(q) = pchip_interval(p0, p1, p2, p3, tx)
447 case(1); g1(q) = pchip_interval(p0, p1, p2, p3, tx)
448 case(2); g2(q) = pchip_interval(p0, p1, p2, p3, tx)
449 case(3); g3(q) = pchip_interval(p0, p1, p2, p3, tx)
450 end select
451 end do
452 end do
453
454 ! Final PCHIP interpolation in y for each quantity
455 do q = 1, nq
456 results(q) = pchip_interval(g0(q), g1(q), g2(q), g3(q), ty)
457 end do
458
459 end subroutine interp_pchip_interleaved
460
461 !> Adaptive-grid sibling of interp_pchip_interleaved. Replaces the
462 !> affine index calculation `(x - x_min) * step_inv` with binary
463 !> searches on the explicit node arrays var{1,2}_nodes (Q axis-1
464 !> binary searches: O(log_2 N) comparisons each, total ~16 cmps for
465 !> N = 256). The PCHIP kernel itself is unchanged: it operates on
466 !> the local 4-tuple of values regardless of grid spacing. The
467 !> stride-1 access pattern over the q-slot dimension is preserved,
468 !> so cache behaviour matches the uniform variant for the inner kernel.
469 subroutine interp_pchip_interleaved_nu(vary, varx, table_il, nq, nx, ny, &
470 var1_nodes, var2_nodes, &
471 guard_1, M_1, scale_1, guard_2, M_2, scale_2, results)
472 double precision, intent(in) :: vary, varx
473 integer, intent(in) :: nq, nx, ny
474 double precision, intent(in) :: table_il(nq, ny, nx)
475 double precision, intent(in) :: var1_nodes(ny), var2_nodes(nx)
476 integer, intent(in) :: m_1, m_2
477 double precision, intent(in) :: scale_1, scale_2
478 integer, intent(in) :: guard_1(m_1), guard_2(m_2)
479 double precision, intent(out) :: results(nq)
480
481 double precision :: tx, ty
482 integer :: ix, iy, q, ii
483 integer :: i0, i1, i2, i3, j0, j1, j2, j3
484 double precision :: g0(nq), g1(nq), g2(nq), g3(nq)
485 double precision :: p0, p1, p2, p3
486
487 ! Axis-1 (vary) cell index + local fractional coordinate
488 if (vary <= var1_nodes(1)) then
489 iy = 0; ty = 0.0d0
490 else if (vary >= var1_nodes(ny)) then
491 iy = ny - 2; ty = 1.0d0
492 else
493 ii = find_index_guard(var1_nodes, ny, vary, guard_1, m_1, scale_1)
494 iy = max(0, min(ii - 2, ny - 2))
495 ty = (vary - var1_nodes(iy+1)) &
496 / (var1_nodes(iy+2) - var1_nodes(iy+1))
497 ty = max(0.0d0, min(ty, 1.0d0))
498 end if
499
500 ! Axis-2 (varx) cell index + local fractional coordinate
501 if (varx <= var2_nodes(1)) then
502 ix = 0; tx = 0.0d0
503 else if (varx >= var2_nodes(nx)) then
504 ix = nx - 2; tx = 1.0d0
505 else
506 ii = find_index_guard(var2_nodes, nx, varx, guard_2, m_2, scale_2)
507 ix = max(0, min(ii - 2, nx - 2))
508 tx = (varx - var2_nodes(ix+1)) &
509 / (var2_nodes(ix+2) - var2_nodes(ix+1))
510 tx = max(0.0d0, min(tx, 1.0d0))
511 end if
512
513 i0 = max(0, min(nx-1, ix-1))
514 i1 = max(0, min(nx-1, ix))
515 i2 = max(0, min(nx-1, ix+1))
516 i3 = max(0, min(nx-1, ix+2))
517
518 ! Same kernel as the uniform variant -- only the index calculation differs
519 do j0 = 0, 3
520 j1 = max(0, min(ny-1, iy - 1 + j0))
521 do q = 1, nq
522 p0 = table_il(q, j1+1, i0+1)
523 p1 = table_il(q, j1+1, i1+1)
524 p2 = table_il(q, j1+1, i2+1)
525 p3 = table_il(q, j1+1, i3+1)
526 select case(j0)
527 case(0); g0(q) = pchip_interval(p0, p1, p2, p3, tx)
528 case(1); g1(q) = pchip_interval(p0, p1, p2, p3, tx)
529 case(2); g2(q) = pchip_interval(p0, p1, p2, p3, tx)
530 case(3); g3(q) = pchip_interval(p0, p1, p2, p3, tx)
531 end select
532 end do
533 end do
534
535 do q = 1, nq
536 results(q) = pchip_interval(g0(q), g1(q), g2(q), g3(q), ty)
537 end do
538
539 end subroutine interp_pchip_interleaved_nu
540
541 subroutine precompute_step_inv(tc)
542 type(eos_table_container), intent(inout) :: tc
543 if (allocated(tc%table)) then
544 tc%step_inv_1 = dble(tc%dim1-1) / (tc%var1_max - tc%var1_min)
545 tc%step_inv_2 = dble(tc%dim2-1) / (tc%var2_max - tc%var2_min)
546 end if
547 end subroutine precompute_step_inv
548
549end module mod_eos_interp
550!> Needs a line after to pass the preprocessor
EoS state container – the single thermodynamic authority for AMRVAC.
Interpolation kernels for the EoS tables (pure math; no EoS state).
pure double precision function, public interp_clamped_bilinear_table(vary, varx, table, nx, ny, vmin_y, vmax_y, vmin_x, vmax_x)
subroutine, public interp_pchip_interleaved_nu(vary, varx, table_il, nq, nx, ny, var1_nodes, var2_nodes, guard_1, m_1, scale_1, guard_2, m_2, scale_2, results)
Adaptive-grid sibling of interp_pchip_interleaved. Replaces the affine index calculation (x - x_min) ...
pure double precision function, public bilinear_lookup(var1, var2, tc)
Dispatch wrapper: bilinear lookup that branches on is_uniform.
subroutine, public interp_pchip_interleaved(vary, varx, table_il, nq, nx, ny, vmin_y, vmax_y, vmin_x, vmax_x, results)
Interleaved PCHIP: evaluate N quantities at the same (vary, varx) point. Table layout: table_il(nq,...
pure integer function, public find_index_guard(nodes, n, val, guard, m, scale_m)
Fast adaptive index lookup. Returns the smallest 1-based ii such that nodes(ii) >= val....
pure double precision function, public bicubic_lookup(var1, var2, tc)
Dispatch wrapper: bicubic lookup that branches on the table's is_uniform flag. Hides the uniform-vs-a...
subroutine, public precompute_step_inv(tc)
A Fortran 90 module for creating 1D and 2D lookup tables. These tables can be used to efficiently int...
pure integer function, public find_index_bsearch(list, val)
Binary search of sorted list for the smallest ix such that list(ix) >= val. On failure,...