MFC
Exascale flow solver
Loading...
Searching...
No Matches
m_derived_variables.fpp.f90
Go to the documentation of this file.
1# 1 "/home/runner/work/MFC/MFC/src/post_process/m_derived_variables.fpp"
2!>
3!! @file
4!! @brief Contains module m_derived_variables
5
6!> @brief Computes derived flow quantities (sound speed, vorticity, Schlieren, etc.) from conservative and primitive variables
7
9
10 use m_derived_types !< definitions of the derived types
11
12 use m_global_parameters !< global parameters for the code
13
14 use m_mpi_proxy !< message passing interface (mpi) module proxy
15
16 use m_helper_basic !< functions to compare floating point numbers
17
19
20 implicit none
21
22 private; public :: s_initialize_derived_variables_module, &
33
34 real(wp), allocatable, dimension(:, :, :) :: gm_rho_sf !<
35 !! Gradient magnitude (gm) of the density for each cell of the computational
36 !! sub-domain. This variable is employed in the calculation of the numerical
37 !! Schlieren function.
38
39 !> @name Finite-difference (fd) coefficients in x-, y- and z-coordinate directions.
40 !! Note that because sufficient boundary information is available for all the
41 !! active coordinate directions, the centered family of the finite-difference
42 !! schemes is used.
43 !> @{
44 real(wp), allocatable, dimension(:, :), public :: fd_coeff_x
45 real(wp), allocatable, dimension(:, :), public :: fd_coeff_y
46 real(wp), allocatable, dimension(:, :), public :: fd_coeff_z
47 !> @}
48
49 integer, private :: flg !<
50 !! Flagging (flg) variable used to annotate the dimensionality of the dataset
51 !! that is undergoing the post-process. A flag value of 1 indicates that the
52 !! dataset is 3D, while a flag value of 0 indicates that it is not. This flg
53 !! variable is necessary to avoid cycling through the third dimension of the
54 !! flow variable(s) when the simulation is not 3D and the size of the buffer
55 !! is non-zero. Note that a similar procedure does not have to be applied to
56 !! the second dimension since in 1D, the buffer size is always zero.
57
58contains
59
60 !> Computation of parameters, allocation procedures, and/or
61 !! any other tasks needed to properly setup the module
63
64 ! Allocating the gradient magnitude of the density variable provided
65 ! that numerical Schlieren function is outputted during post-process
66 if (schlieren_wrt) then
67 allocate (gm_rho_sf(-offset_x%beg:m + offset_x%end, &
68 -offset_y%beg:n + offset_y%end, &
69 -offset_z%beg:p + offset_z%end))
70 end if
71
72 ! Allocating the variables which will store the coefficients of the
73 ! centered family of finite-difference schemes. Note that sufficient
74 ! space is allocated so that the coefficients up to any chosen order
75 ! of accuracy may be bookkept. However, if higher than fourth-order
76 ! accuracy coefficients are wanted, the formulae required to compute
77 ! these coefficients will have to be implemented in the subroutine
78 ! s_compute_finite_difference_coefficients.
79
80 ! Allocating centered finite-difference coefficients in x-direction
81 if (omega_wrt(2) .or. omega_wrt(3) .or. schlieren_wrt .or. liutex_wrt) then
82 allocate (fd_coeff_x(-fd_number:fd_number, &
83 -offset_x%beg:m + offset_x%end))
84 end if
85
86 ! Allocating centered finite-difference coefficients in y-direction
87 if (omega_wrt(1) .or. omega_wrt(3) .or. liutex_wrt &
88 .or. &
89 (n > 0 .and. schlieren_wrt)) then
90 allocate (fd_coeff_y(-fd_number:fd_number, &
91 -offset_y%beg:n + offset_y%end))
92 end if
93
94 ! Allocating centered finite-difference coefficients in z-direction
95 if (omega_wrt(1) .or. omega_wrt(2) .or. liutex_wrt &
96 .or. &
97 (p > 0 .and. schlieren_wrt)) then
98 allocate (fd_coeff_z(-fd_number:fd_number, &
99 -offset_z%beg:p + offset_z%end))
100 end if
101
102 ! Annotating the dimensionality of the dataset undergoing the post-
103 ! process. A flag value of 1 indicates that the dataset is 3D, while
104 ! a flag value of 0 indicates that it is not.
105 if (p > 0) then
106 flg = 1
107 else
108 flg = 0
109 end if
110
112
113 !> This subroutine receives as input the specific heat ratio
114 !! function, gamma_sf, and derives from it the specific heat
115 !! ratio. The latter is stored in the derived flow quantity
116 !! storage variable, q_sf.
117 !! @param q_sf Specific heat ratio
119
120 real(wp), &
121 dimension(-offset_x%beg:m + offset_x%end, & -offset_y%beg:n + offset_y%end, & -offset_z%beg:p + offset_z%end), &
122 intent(inout) :: q_sf
123
124 integer :: i, j, k !< Generic loop iterators
125
126 ! Computing specific heat ratio from specific heat ratio function
127 do k = -offset_z%beg, p + offset_z%end
128 do j = -offset_y%beg, n + offset_y%end
129 do i = -offset_x%beg, m + offset_x%end
130 q_sf(i, j, k) = 1._wp + 1._wp/gamma_sf(i, j, k)
131 end do
132 end do
133 end do
134
135 end subroutine s_derive_specific_heat_ratio
136
137 !> This subroutine admits as inputs the specific heat ratio
138 !! function and the liquid stiffness function, gamma_sf and
139 !! pi_inf_sf, respectively. These are used to calculate the
140 !! values of the liquid stiffness, which are stored in the
141 !! derived flow quantity storage variable, q_sf.
142 !! @param q_sf Liquid stiffness
143 subroutine s_derive_liquid_stiffness(q_sf)
144
145 real(wp), &
146 dimension(-offset_x%beg:m + offset_x%end, & -offset_y%beg:n + offset_y%end, & -offset_z%beg:p + offset_z%end), &
147 intent(inout) :: q_sf
148
149 integer :: i, j, k !< Generic loop iterators
150
151 ! Calculating the values of the liquid stiffness from those of the
152 ! specific heat ratio function and the liquid stiffness function
153 do k = -offset_z%beg, p + offset_z%end
154 do j = -offset_y%beg, n + offset_y%end
155 do i = -offset_x%beg, m + offset_x%end
156 q_sf(i, j, k) = pi_inf_sf(i, j, k)/(gamma_sf(i, j, k) + 1._wp)
157 end do
158 end do
159 end do
160
161 end subroutine s_derive_liquid_stiffness
162
163 !> This subroutine admits as inputs the primitive variables,
164 !! the density, the specific heat ratio function and liquid
165 !! stiffness function. It then computes from those variables
166 !! the values of the speed of sound, which are stored in the
167 !! derived flow quantity storage variable, q_sf.
168 !! @param q_prim_vf Primitive variables
169 !! @param q_sf Speed of sound
170 subroutine s_derive_sound_speed(q_prim_vf, q_sf)
171
172 type(scalar_field), &
173 dimension(sys_size), &
174 intent(in) :: q_prim_vf
175
176 real(wp), &
177 dimension(-offset_x%beg:m + offset_x%end, & -offset_y%beg:n + offset_y%end, & -offset_z%beg:p + offset_z%end), &
178 intent(inout) :: q_sf
179
180 integer :: i, j, k !< Generic loop iterators
181
182 ! Fluid bulk modulus for alternate sound speed
183 real(wp) :: blkmod1, blkmod2
184
185 ! Computing speed of sound values from those of pressure, density,
186 ! specific heat ratio function and the liquid stiffness function
187 do k = -offset_z%beg, p + offset_z%end
188 do j = -offset_y%beg, n + offset_y%end
189 do i = -offset_x%beg, m + offset_x%end
190
191 ! Compute mixture sound speed
192 if (alt_soundspeed .neqv. .true.) then
193 q_sf(i, j, k) = (((gamma_sf(i, j, k) + 1._wp)* &
194 q_prim_vf(e_idx)%sf(i, j, k) + &
195 pi_inf_sf(i, j, k))/(gamma_sf(i, j, k)* &
196 rho_sf(i, j, k)))
197 else
198 blkmod1 = ((gammas(1) + 1._wp)*q_prim_vf(e_idx)%sf(i, j, k) + &
199 pi_infs(1))/gammas(1)
200 blkmod2 = ((gammas(2) + 1._wp)*q_prim_vf(e_idx)%sf(i, j, k) + &
201 pi_infs(2))/gammas(2)
202 q_sf(i, j, k) = (1._wp/(rho_sf(i, j, k)*(q_prim_vf(adv_idx%beg)%sf(i, j, k)/blkmod1 + &
203 (1._wp - q_prim_vf(adv_idx%beg)%sf(i, j, k))/blkmod2)))
204 end if
205
206 if (mixture_err .and. q_sf(i, j, k) < 0._wp) then
207 q_sf(i, j, k) = 1.e-16_wp
208 else
209 q_sf(i, j, k) = sqrt(q_sf(i, j, k))
210 end if
211 end do
212 end do
213 end do
214
215 end subroutine s_derive_sound_speed
216
217 !> This subroutine derives the flux_limiter at cell boundary
218 !! i+1/2. This is an approximation because the velocity used
219 !! to determine the upwind direction is the velocity at the
220 !! cell center i instead of the contact velocity at the cell
221 !! boundary from the Riemann solver.
222 !! @param i Component indicator
223 !! @param q_prim_vf Primitive variables
224 !! @param q_sf Flux limiter
225 subroutine s_derive_flux_limiter(i, q_prim_vf, q_sf)
226
227 integer, intent(in) :: i
228
229 type(scalar_field), dimension(sys_size), intent(in) :: q_prim_vf
230
231 real(wp), dimension(-offset_x%beg:m + offset_x%end, & -offset_y%beg:n + offset_y%end, & -offset_z%beg:p + offset_z%end), &
232 intent(inout) :: q_sf
233
234 real(wp) :: top, bottom, slope !< Flux limiter calcs
235 integer :: j, k, l !< Generic loop iterators
236
237 do l = -offset_z%beg, p + offset_z%end
238 do k = -offset_y%beg, n + offset_y%end
239 do j = -offset_x%beg, m + offset_x%end
240 if (i == 1) then
241 if (q_prim_vf(cont_idx%end + i)%sf(j, k, l) >= 0._wp) then
242 top = q_prim_vf(adv_idx%beg)%sf(j, k, l) - &
243 q_prim_vf(adv_idx%beg)%sf(j - 1, k, l)
244 bottom = q_prim_vf(adv_idx%beg)%sf(j + 1, k, l) - &
245 q_prim_vf(adv_idx%beg)%sf(j, k, l)
246 else
247 top = q_prim_vf(adv_idx%beg)%sf(j + 2, k, l) - &
248 q_prim_vf(adv_idx%beg)%sf(j + 1, k, l)
249 bottom = q_prim_vf(adv_idx%beg)%sf(j + 1, k, l) - &
250 q_prim_vf(adv_idx%beg)%sf(j, k, l)
251 end if
252 elseif (i == 2) then
253 if (q_prim_vf(cont_idx%end + i)%sf(j, k, l) >= 0._wp) then
254 top = q_prim_vf(adv_idx%beg)%sf(j, k, l) - &
255 q_prim_vf(adv_idx%beg)%sf(j, k - 1, l)
256 bottom = q_prim_vf(adv_idx%beg)%sf(j, k + 1, l) - &
257 q_prim_vf(adv_idx%beg)%sf(j, k, l)
258 else
259 top = q_prim_vf(adv_idx%beg)%sf(j, k + 2, l) - &
260 q_prim_vf(adv_idx%beg)%sf(j, k + 1, l)
261 bottom = q_prim_vf(adv_idx%beg)%sf(j, k + 1, l) - &
262 q_prim_vf(adv_idx%beg)%sf(j, k, l)
263 end if
264 else
265 if (q_prim_vf(cont_idx%end + i)%sf(j, k, l) >= 0._wp) then
266 top = q_prim_vf(adv_idx%beg)%sf(j, k, l) - &
267 q_prim_vf(adv_idx%beg)%sf(j, k, l - 1)
268 bottom = q_prim_vf(adv_idx%beg)%sf(j, k, l + 1) - &
269 q_prim_vf(adv_idx%beg)%sf(j, k, l)
270 else
271 top = q_prim_vf(adv_idx%beg)%sf(j, k, l + 2) - &
272 q_prim_vf(adv_idx%beg)%sf(j, k, l + 1)
273 bottom = q_prim_vf(adv_idx%beg)%sf(j, k, l + 1) - &
274 q_prim_vf(adv_idx%beg)%sf(j, k, l)
275 end if
276 end if
277
278 if (abs(top) < 1.e-8_wp) top = 0._wp
279 if (abs(bottom) < 1.e-8_wp) bottom = 0._wp
280
281 if (f_approx_equal(top, bottom)) then
282 slope = 1._wp
283 ! ELSEIF((top == 0._wp .AND. bottom /= 0._wp) &
284 ! .OR. &
285 ! (bottom == 0._wp .AND. top /= 0._wp)) THEN
286 ! slope = 0._wp
287 else
288 slope = (top*bottom)/(bottom**2._wp + 1.e-16_wp)
289 end if
290
291 ! Flux limiter function
292 if (flux_lim == 1) then ! MINMOD (MM)
293 q_sf(j, k, l) = max(0._wp, min(1._wp, slope))
294 elseif (flux_lim == 2) then ! MUSCL (MC)
295 q_sf(j, k, l) = max(0._wp, min(2._wp*slope, 5.e-1_wp*(1._wp + slope), 2._wp))
296 elseif (flux_lim == 3) then ! OSPRE (OP)
297 q_sf(j, k, l) = (15.e-1_wp*(slope**2._wp + slope))/(slope**2._wp + slope + 1._wp)
298 elseif (flux_lim == 4) then ! SUPERBEE (SB)
299 q_sf(j, k, l) = max(0._wp, min(1._wp, 2._wp*slope), min(slope, 2._wp))
300 elseif (flux_lim == 5) then ! SWEBY (SW) (beta = 1.5)
301 q_sf(j, k, l) = max(0._wp, min(15.e-1_wp*slope, 1._wp), min(slope, 15.e-1_wp))
302 elseif (flux_lim == 6) then ! VAN ALBADA (VA)
303 q_sf(j, k, l) = (slope**2._wp + slope)/(slope**2._wp + 1._wp)
304 elseif (flux_lim == 7) then ! VAN LEER (VL)
305 q_sf(j, k, l) = (abs(slope) + slope)/(1._wp + abs(slope))
306 end if
307 end do
308 end do
309 end do
310 end subroutine s_derive_flux_limiter
311
312 !> Computes the solution to the linear system Ax=b w/ sol = x
313 !! @param A Input matrix
314 !! @param b right-hane-side
315 !! @param sol Solution
316 !! @param ndim Problem size
317 subroutine s_solve_linear_system(A, b, sol, ndim)
318
319 integer, intent(in) :: ndim
320 real(wp), dimension(ndim, ndim), intent(inout) :: a
321 real(wp), dimension(ndim), intent(inout) :: b
322 real(wp), dimension(ndim), intent(out) :: sol
323
324 !EXTERNAL DGESV
326 integer :: i, j, k
327
328 ! Solve linear system using own linear solver (Thomson/Darter/Comet/Stampede)
329 ! Forward elimination
330 do i = 1, ndim
331 ! Pivoting
332 j = i - 1 + maxloc(abs(a(i:ndim, i)), 1)
333 sol = a(i, :)
334 a(i, :) = a(j, :)
335 a(j, :) = sol
336 sol(1) = b(i)
337 b(i) = b(j)
338 b(j) = sol(1)
339 ! Elimination
340 b(i) = b(i)/a(i, i)
341 a(i, :) = a(i, :)/a(i, i)
342 do k = i + 1, ndim
343 b(k) = b(k) - a(k, i)*b(i)
344 a(k, :) = a(k, :) - a(k, i)*a(i, :)
345 end do
346 end do
347
348 ! Backward substitution
349 do i = ndim, 1, -1
350 sol(i) = b(i)
351 do k = i + 1, ndim
352 sol(i) = sol(i) - a(i, k)*sol(k)
353 end do
354 end do
355
356 end subroutine s_solve_linear_system
357
358 !> This subroutine receives as inputs the indicator of the
359 !! component of the vorticity that should be outputted and
360 !! the primitive variables. From those inputs, it proceeds
361 !! to calculate values of the desired vorticity component,
362 !! which are subsequently stored in derived flow quantity
363 !! storage variable, q_sf.
364 !! @param i Vorticity component indicator
365 !! @param q_prim_vf Primitive variables
366 !! @param q_sf Vorticity component
367 subroutine s_derive_vorticity_component(i, q_prim_vf, q_sf)
368
369 integer, intent(in) :: i
370
371 type(scalar_field), &
372 dimension(sys_size), &
373 intent(in) :: q_prim_vf
374
375 real(wp), &
376 dimension(-offset_x%beg:m + offset_x%end, & -offset_y%beg:n + offset_y%end, & -offset_z%beg:p + offset_z%end), &
377 intent(inout) :: q_sf
378
379 integer :: j, k, l, r !< Generic loop iterators
380
381 ! Computing the vorticity component in the x-coordinate direction
382 if (i == 1) then
383 do l = -offset_z%beg, p + offset_z%end
384 do k = -offset_y%beg, n + offset_y%end
385 do j = -offset_x%beg, m + offset_x%end
386
387 q_sf(j, k, l) = 0._wp
388
389 do r = -fd_number, fd_number
390 if (grid_geometry == 3) then
391 q_sf(j, k, l) = &
392 q_sf(j, k, l) + 1._wp/y_cc(k)* &
393 (fd_coeff_y(r, k)*y_cc(r + k)* &
394 q_prim_vf(mom_idx%end)%sf(j, r + k, l) &
395 - fd_coeff_z(r, l)* &
396 q_prim_vf(mom_idx%beg + 1)%sf(j, k, r + l))
397 else
398 q_sf(j, k, l) = &
399 q_sf(j, k, l) + fd_coeff_y(r, k)* &
400 q_prim_vf(mom_idx%end)%sf(j, r + k, l) &
401 - fd_coeff_z(r, l)* &
402 q_prim_vf(mom_idx%beg + 1)%sf(j, k, r + l)
403 end if
404 end do
405
406 end do
407 end do
408 end do
409
410 ! Computing the vorticity component in the y-coordinate direction
411 elseif (i == 2) then
412 do l = -offset_z%beg, p + offset_z%end
413 do k = -offset_y%beg, n + offset_y%end
414 do j = -offset_x%beg, m + offset_x%end
415
416 q_sf(j, k, l) = 0._wp
417
418 do r = -fd_number, fd_number
419 if (grid_geometry == 3) then
420 q_sf(j, k, l) = &
421 q_sf(j, k, l) + fd_coeff_z(r, l)/y_cc(k)* &
422 q_prim_vf(mom_idx%beg)%sf(j, k, r + l) &
423 - fd_coeff_x(r, j)* &
424 q_prim_vf(mom_idx%end)%sf(r + j, k, l)
425 else
426 q_sf(j, k, l) = &
427 q_sf(j, k, l) + fd_coeff_z(r, l)* &
428 q_prim_vf(mom_idx%beg)%sf(j, k, r + l) &
429 - fd_coeff_x(r, j)* &
430 q_prim_vf(mom_idx%end)%sf(r + j, k, l)
431 end if
432 end do
433
434 end do
435 end do
436 end do
437
438 ! Computing the vorticity component in the z-coordinate direction
439 else
440 do l = -offset_z%beg, p + offset_z%end
441 do k = -offset_y%beg, n + offset_y%end
442 do j = -offset_x%beg, m + offset_x%end
443
444 q_sf(j, k, l) = 0._wp
445
446 do r = -fd_number, fd_number
447 q_sf(j, k, l) = &
448 q_sf(j, k, l) + fd_coeff_x(r, j)* &
449 q_prim_vf(mom_idx%beg + 1)%sf(r + j, k, l) &
450 - fd_coeff_y(r, k)* &
451 q_prim_vf(mom_idx%beg)%sf(j, r + k, l)
452 end do
453
454 end do
455 end do
456 end do
457 end if
458
459 end subroutine s_derive_vorticity_component
460
461 !> This subroutine gets as inputs the primitive variables. From those
462 !! inputs, it proceeds to calculate the value of the Q_M
463 !! function, which are subsequently stored in the derived flow
464 !! quantity storage variable, q_sf.
465 !! @param q_prim_vf Primitive variables
466 !! @param q_sf Q_M
467 subroutine s_derive_qm(q_prim_vf, q_sf)
468 type(scalar_field), &
469 dimension(sys_size), &
470 intent(in) :: q_prim_vf
471
472 real(wp), &
473 dimension(-offset_x%beg:m + offset_x%end, & -offset_y%beg:n + offset_y%end, & -offset_z%beg:p + offset_z%end), &
474 intent(inout) :: q_sf
475
476 real(wp), &
477 dimension(1:3, 1:3) :: q_jacobian_sf, s, s2, o, o2
478
479 real(wp) :: trs, q, iis
480 integer :: j, k, l, r, jj, kk !< Generic loop iterators
481
482 do l = -offset_z%beg, p + offset_z%end
483 do k = -offset_y%beg, n + offset_y%end
484 do j = -offset_x%beg, m + offset_x%end
485
486 ! Get velocity gradient tensor
487 q_jacobian_sf(:, :) = 0._wp
488
489 do r = -fd_number, fd_number
490 do jj = 1, 3
491 ! d()/dx
492 q_jacobian_sf(jj, 1) = &
493 q_jacobian_sf(jj, 1) + &
494 fd_coeff_x(r, j)* &
495 q_prim_vf(mom_idx%beg + jj - 1)%sf(r + j, k, l)
496 ! d()/dy
497 q_jacobian_sf(jj, 2) = &
498 q_jacobian_sf(jj, 2) + &
499 fd_coeff_y(r, k)* &
500 q_prim_vf(mom_idx%beg + jj - 1)%sf(j, r + k, l)
501 ! d()/dz
502 q_jacobian_sf(jj, 3) = &
503 q_jacobian_sf(jj, 3) + &
504 fd_coeff_z(r, l)* &
505 q_prim_vf(mom_idx%beg + jj - 1)%sf(j, k, r + l)
506 end do
507 end do
508
509 ! Decompose J into asymmetric matrix, S, and a skew-symmetric matrix, O
510 do jj = 1, 3
511 do kk = 1, 3
512 s(jj, kk) = 0.5_wp* &
513 (q_jacobian_sf(jj, kk) + q_jacobian_sf(kk, jj))
514 o(jj, kk) = 0.5_wp* &
515 (q_jacobian_sf(jj, kk) - q_jacobian_sf(kk, jj))
516 end do
517 end do
518
519 ! Compute S2 = S*S'
520 do jj = 1, 3
521 do kk = 1, 3
522 o2(jj, kk) = o(jj, 1)*o(kk, 1) + &
523 o(jj, 2)*o(kk, 2) + &
524 o(jj, 3)*o(kk, 3)
525 s2(jj, kk) = s(jj, 1)*s(kk, 1) + &
526 s(jj, 2)*s(kk, 2) + &
527 s(jj, 3)*s(kk, 3)
528 end do
529 end do
530
531 ! Compute Q
532 q = 0.5_wp*((o2(1, 1) + o2(2, 2) + o2(3, 3)) - &
533 (s2(1, 1) + s2(2, 2) + s2(3, 3)))
534 trs = s(1, 1) + s(2, 2) + s(3, 3)
535 iis = 0.5_wp*((s(1, 1) + s(2, 2) + s(3, 3))**2 - &
536 (s2(1, 1) + s2(2, 2) + s2(3, 3)))
537 q_sf(j, k, l) = q + iis
538
539 end do
540 end do
541 end do
542
543 end subroutine s_derive_qm
544
545 !> This subroutine gets as inputs the primitive variables. From those
546 !! inputs, it proceeds to calculate the Liutex vector and its
547 !! magnitude based on Xu et al. (2019).
548 !! @param q_prim_vf Primitive variables
549 impure subroutine s_derive_liutex(q_prim_vf, liutex_mag, liutex_axis)
550 integer, parameter :: nm = 3
551 type(scalar_field), &
552 dimension(sys_size), &
553 intent(in) :: q_prim_vf
554
555 real(wp), &
556 dimension(-offset_x%beg:m + offset_x%end, & -offset_y%beg:n + offset_y%end, & -offset_z%beg:p + offset_z%end), &
557 intent(out) :: liutex_mag !< Liutex magnitude
558
559 real(wp), &
560 dimension(-offset_x%beg:m + offset_x%end, & -offset_y%beg:n + offset_y%end, & -offset_z%beg:p + offset_z%end, nm), &
561 intent(out) :: liutex_axis !< Liutex rigid rotation axis
562
563 character, parameter :: ivl = 'N' !< compute left eigenvectors
564 character, parameter :: ivr = 'V' !< compute right eigenvectors
565 real(wp), dimension(nm, nm) :: vgt !< velocity gradient tensor
566 real(wp), dimension(nm) :: lr, li !< real and imaginary parts of eigenvalues
567 real(wp), dimension(nm, nm) :: vl, vr !< left and right eigenvectors
568 integer, parameter :: lwork = 4*nm !< size of work array (4*nm recommended)
569 real(wp), dimension(lwork) :: work !< work array
570 integer :: info
571
572 real(wp), dimension(nm) :: eigvec !< real eigenvector
573 real(wp) :: eigvec_mag !< magnitude of real eigenvector
574 real(wp) :: omega_proj !< projection of vorticity on real eigenvector
575 real(wp) :: lci !< imaginary part of complex eigenvalue
576 real(wp) :: alpha
577
578 integer :: j, k, l, r, i !< Generic loop iterators
579 integer :: idx
580
581 do l = -offset_z%beg, p + offset_z%end
582 do k = -offset_y%beg, n + offset_y%end
583 do j = -offset_x%beg, m + offset_x%end
584
585 ! Get velocity gradient tensor (VGT)
586 vgt(:, :) = 0._wp
587
588 do r = -fd_number, fd_number
589 do i = 1, 3
590 ! d()/dx
591 vgt(i, 1) = &
592 vgt(i, 1) + &
593 fd_coeff_x(r, j)* &
594 q_prim_vf(mom_idx%beg + i - 1)%sf(r + j, k, l)
595 ! d()/dy
596 vgt(i, 2) = &
597 vgt(i, 2) + &
598 fd_coeff_y(r, k)* &
599 q_prim_vf(mom_idx%beg + i - 1)%sf(j, r + k, l)
600 ! d()/dz
601 vgt(i, 3) = &
602 vgt(i, 3) + &
603 fd_coeff_z(r, l)* &
604 q_prim_vf(mom_idx%beg + i - 1)%sf(j, k, r + l)
605 end do
606 end do
607
608 ! Call appropriate LAPACK routine based on precision
609#ifdef MFC_SINGLE_PRECISION
610 call sgeev(ivl, ivr, nm, vgt, nm, lr, li, vl, nm, vr, nm, work, lwork, info)
611#else
612 call dgeev(ivl, ivr, nm, vgt, nm, lr, li, vl, nm, vr, nm, work, lwork, info)
613#endif
614
615 ! Find real eigenvector
616 idx = 1
617 do r = 2, 3
618 if (abs(li(r)) < abs(li(idx))) then
619 idx = r
620 end if
621 end do
622 eigvec = vr(:, idx)
623
624 ! Normalize real eigenvector if it is effectively non-zero
625 eigvec_mag = sqrt(eigvec(1)**2._wp &
626 + eigvec(2)**2._wp &
627 + eigvec(3)**2._wp)
628 if (eigvec_mag > sgm_eps) then
629 eigvec = eigvec/eigvec_mag
630 else
631 eigvec = 0._wp
632 end if
633
634 ! Compute vorticity projected on the eigenvector
635 omega_proj = (vgt(3, 2) - vgt(2, 3))*eigvec(1) &
636 + (vgt(1, 3) - vgt(3, 1))*eigvec(2) &
637 + (vgt(2, 1) - vgt(1, 2))*eigvec(3)
638
639 ! As eigenvector can have +/- signs, we can choose the sign
640 ! so that omega_proj is positive
641 if (omega_proj < 0._wp) then
642 eigvec = -eigvec
643 omega_proj = -omega_proj
644 end if
645
646 ! Find imaginary part of complex eigenvalue
647 lci = li(mod(idx, 3) + 1)
648
649 ! Compute Liutex magnitude
650 alpha = omega_proj**2._wp - 4._wp*lci**2._wp ! (2*alpha)^2
651 if (alpha > 0._wp) then
652 liutex_mag(j, k, l) = omega_proj - sqrt(alpha)
653 else
654 liutex_mag(j, k, l) = omega_proj
655 end if
656
657 ! Compute Liutex axis
658 liutex_axis(j, k, l, 1) = eigvec(1)
659 liutex_axis(j, k, l, 2) = eigvec(2)
660 liutex_axis(j, k, l, 3) = eigvec(3)
661
662 end do
663 end do
664 end do
665
666 end subroutine s_derive_liutex
667
668 !> This subroutine gets as inputs the conservative variables
669 !! and density. From those inputs, it proceeds to calculate
670 !! the values of the numerical Schlieren function, which are
671 !! subsequently stored in the derived flow quantity storage
672 !! variable, q_sf.
673 !! @param q_cons_vf Conservative variables
674 !! @param q_sf Numerical Schlieren function
675 impure subroutine s_derive_numerical_schlieren_function(q_cons_vf, q_sf)
676
677 type(scalar_field), &
678 dimension(sys_size), &
679 intent(in) :: q_cons_vf
680
681 real(wp), &
682 dimension(-offset_x%beg:m + offset_x%end, & -offset_y%beg:n + offset_y%end, & -offset_z%beg:p + offset_z%end), &
683 intent(inout) :: q_sf
684
685 real(wp) :: drho_dx, drho_dy, drho_dz !<
686 !! Spatial derivatives of the density in the x-, y- and z-directions
687
688 real(wp), dimension(2) :: gm_rho_max !<
689 !! Maximum value of the gradient magnitude (gm) of the density field
690 !! in entire computational domain and not just the local sub-domain.
691 !! The first position in the variable contains the maximum value and
692 !! the second contains the rank of the processor on which it occurred.
693
694 integer :: i, j, k, l !< Generic loop iterators
695
696 ! Computing Gradient Magnitude of Density
697
698 ! Contributions from the x- and y-coordinate directions
699 do l = -offset_z%beg, p + offset_z%end
700 do k = -offset_y%beg, n + offset_y%end
701 do j = -offset_x%beg, m + offset_x%end
702
703 drho_dx = 0._wp
704 drho_dy = 0._wp
705
706 do i = -fd_number, fd_number
707 drho_dx = drho_dx + fd_coeff_x(i, j)*rho_sf(i + j, k, l)
708 drho_dy = drho_dy + fd_coeff_y(i, k)*rho_sf(j, i + k, l)
709 end do
710
711 gm_rho_sf(j, k, l) = drho_dx*drho_dx + drho_dy*drho_dy
712
713 end do
714 end do
715 end do
716
717 ! Contribution from the z-coordinate direction
718 if (p > 0) then
719 do l = -offset_z%beg, p + offset_z%end
720 do k = -offset_y%beg, n + offset_y%end
721 do j = -offset_x%beg, m + offset_x%end
722
723 drho_dz = 0._wp
724
725 do i = -fd_number, fd_number
726 if (grid_geometry == 3) then
727 drho_dz = drho_dz + fd_coeff_z(i, l)/y_cc(k)* &
728 rho_sf(j, k, i + l)
729 else
730 drho_dz = drho_dz + fd_coeff_z(i, l)* &
731 rho_sf(j, k, i + l)
732 end if
733 end do
734
735 gm_rho_sf(j, k, l) = gm_rho_sf(j, k, l) &
736 + drho_dz*drho_dz
737
738 end do
739 end do
740 end do
741 end if
742
743 ! Up until now, only the dot product of the gradient of the density
744 ! field has been calculated and stored in the gradient magnitude of
745 ! density variable. So now we proceed to take the square-root as to
746 ! complete the desired calculation.
747 gm_rho_sf = sqrt(gm_rho_sf)
748
749 ! Determining the local maximum of the gradient magnitude of density
750 ! and bookkeeping the result, along with rank of the local processor
751 gm_rho_max = (/maxval(gm_rho_sf), real(proc_rank, wp)/)
752
753 ! Comparing the local maximum gradient magnitude of the density on
754 ! this processor to the those computed on the remaining processors.
755 ! This allows for the global maximum to be computed and the rank of
756 ! the processor on which it has occurred to be recorded.
757 if (num_procs > 1) call s_mpi_reduce_maxloc(gm_rho_max)
758
759 ! Computing Numerical Schlieren Function
760
761 ! The form of the numerical Schlieren function depends on the choice
762 ! of the multicomponent flow model. For the gamma/pi_inf model, the
763 ! exponential of the negative, normalized, gradient magnitude of the
764 ! density is computed. For the volume fraction model, the amplitude
765 ! of the exponential's inside is also modulated with respect to the
766 ! identity of the fluid in which the function is evaluated. For more
767 ! information, refer to Marquina and Mulet (2003).
768
769 if (model_eqns == 1) then ! Gamma/pi_inf model
770 q_sf = -gm_rho_sf/gm_rho_max(1)
771
772 else ! Volume fraction model
773 do l = -offset_z%beg, p + offset_z%end
774 do k = -offset_y%beg, n + offset_y%end
775 do j = -offset_x%beg, m + offset_x%end
776
777 q_sf(j, k, l) = 0._wp
778
779 do i = 1, adv_idx%end - e_idx
780 q_sf(j, k, l) = &
781 q_sf(j, k, l) - schlieren_alpha(i)* &
782 q_cons_vf(i + e_idx)%sf(j, k, l)* &
783 gm_rho_sf(j, k, l)/gm_rho_max(1)
784 end do
785 end do
786 end do
787 end do
788 end if
789
790 ! Up until now, only the inside of the exponential of the numerical
791 ! Schlieren function has been evaluated and stored. Then, to finish
792 ! the computation, the exponential of the inside quantity is taken.
793 q_sf = exp(q_sf)
794
796
797 !> Deallocation procedures for the module
799
800 ! Deallocating the variable containing the gradient magnitude of the
801 ! density field provided that the numerical Schlieren function was
802 ! was outputted during the post-process
803 if (schlieren_wrt) deallocate (gm_rho_sf)
804
805 ! Deallocating the variables that might have been used to bookkeep
806 ! the finite-difference coefficients in the x-, y- and z-directions
807 if (allocated(fd_coeff_x)) deallocate (fd_coeff_x)
808 if (allocated(fd_coeff_y)) deallocate (fd_coeff_y)
809 if (allocated(fd_coeff_z)) deallocate (fd_coeff_z)
810
812
813end module m_derived_variables
814
type(scalar_field), dimension(sys_size), intent(inout) q_cons_vf
integer, intent(in) k
integer, intent(in) j
integer, intent(in) l
Shared derived types for field data, patch geometry, bubble dynamics, and MPI I/O structures.
Computes derived flow quantities (sound speed, vorticity, Schlieren, etc.) from conservative and prim...
subroutine s_solve_linear_system(a, b, sol, ndim)
Computes the solution to the linear system Ax=b w/ sol = x.
real(wp), dimension(:, :), allocatable, public fd_coeff_z
integer, private flg
Flagging (flg) variable used to annotate the dimensionality of the dataset that is undergoing the pos...
real(wp), dimension(:, :), allocatable, public fd_coeff_y
impure subroutine, public s_derive_liutex(q_prim_vf, liutex_mag, liutex_axis)
This subroutine gets as inputs the primitive variables. From those inputs, it proceeds to calculate t...
subroutine, public s_derive_specific_heat_ratio(q_sf)
This subroutine receives as input the specific heat ratio function, gamma_sf, and derives from it the...
subroutine, public s_derive_liquid_stiffness(q_sf)
This subroutine admits as inputs the specific heat ratio function and the liquid stiffness function,...
real(wp), dimension(:, :, :), allocatable gm_rho_sf
Gradient magnitude (gm) of the density for each cell of the computational sub-domain....
impure subroutine, public s_initialize_derived_variables_module
Computation of parameters, allocation procedures, and/or any other tasks needed to properly setup the...
real(wp), dimension(:, :), allocatable, public fd_coeff_x
subroutine, public s_derive_vorticity_component(i, q_prim_vf, q_sf)
This subroutine receives as inputs the indicator of the component of the vorticity that should be out...
subroutine, public s_derive_sound_speed(q_prim_vf, q_sf)
This subroutine admits as inputs the primitive variables, the density, the specific heat ratio functi...
impure subroutine, public s_derive_numerical_schlieren_function(q_cons_vf, q_sf)
This subroutine gets as inputs the conservative variables and density. From those inputs,...
subroutine, public s_derive_flux_limiter(i, q_prim_vf, q_sf)
This subroutine derives the flux_limiter at cell boundary i+1/2. This is an approximation because the...
impure subroutine, public s_finalize_derived_variables_module
Deallocation procedures for the module.
subroutine, public s_derive_qm(q_prim_vf, q_sf)
This subroutine gets as inputs the primitive variables. From those inputs, it proceeds to calculate t...
Global parameters for the post-process: domain geometry, equation of state, and output database setti...
type(int_bounds_info) offset_y
type(int_bounds_info) mom_idx
Indexes of first & last momentum eqns.
real(wp), dimension(num_fluids_max) schlieren_alpha
Amplitude coefficients of the numerical Schlieren function that are used to adjust the intensity of n...
real(wp), dimension(:), allocatable y_cc
integer proc_rank
Rank of the local processor.
logical mixture_err
Mixture error limiter.
logical alt_soundspeed
Alternate sound speed.
type(int_bounds_info) cont_idx
Indexes of first & last continuity eqns.
integer fd_number
The finite-difference number is given by MAX(1, fd_order/2). Essentially, it is a measure of the half...
integer model_eqns
Multicomponent flow model.
type(int_bounds_info) offset_x
logical, dimension(3) omega_wrt
integer num_procs
Number of processors.
type(int_bounds_info) adv_idx
Indexes of first & last advection eqns.
type(int_bounds_info) offset_z
integer e_idx
Index of energy equation.
Basic floating-point utilities: approximate equality, default detection, and coordinate bounds.
logical elemental function, public f_approx_equal(a, b, tol_input)
This procedure checks if two floating point numbers of wp are within tolerance.
MPI gather and scatter operations for distributing post-process grid and flow-variable data.
Conservative-to-primitive variable conversion, mixture property evaluation, and pressure computation.
real(wp), dimension(:), allocatable, public gammas
real(wp), dimension(:, :, :), allocatable, public pi_inf_sf
Scalar liquid stiffness function.
subroutine s_compute_speed_of_sound(pres, rho, gamma, pi_inf, h, adv, vel_sum, c_c, c, qv)
Computes the speed of sound from thermodynamic state variables, supporting multiple equation-of-state...
real(wp), dimension(:, :, :), allocatable, public rho_sf
Scalar density function.
real(wp), dimension(:, :, :), allocatable, public gamma_sf
Scalar sp. heat ratio function.
real(wp), dimension(:), allocatable, public pi_infs
Derived type annexing a scalar field (SF).