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
12 use m_mpi_proxy
16
17 implicit none
18
22
23 !> Finite-difference state: density gradient magnitude and centered FD coefficients in x-, y-, and z-directions.
24 type(fd_context) :: fd
25
26contains
27
28 !> Computation of parameters, allocation procedures, and/or any other tasks needed to properly setup the module
30
31 ! Allocate density gradient magnitude if Schlieren output requested
32 if (schlieren_wrt) then
33 allocate (fd%gm_rho_sf(-offset_x%beg:m + offset_x%end,-offset_y%beg:n + offset_y%end,-offset_z%beg:p + offset_z%end))
34 end if
35
36 ! Allocate FD coefficients (up to 4th order; higher orders need extension)
37
38 if (omega_wrt(2) .or. omega_wrt(3) .or. qm_wrt .or. schlieren_wrt .or. liutex_wrt) then
39 allocate (fd%fd_coeff_x(-fd_number:fd_number,-offset_x%beg:m + offset_x%end))
40 end if
41
42 if (omega_wrt(1) .or. omega_wrt(3) .or. qm_wrt .or. liutex_wrt .or. (n > 0 .and. schlieren_wrt)) then
43 allocate (fd%fd_coeff_y(-fd_number:fd_number,-offset_y%beg:n + offset_y%end))
44 end if
45
46 if (omega_wrt(1) .or. omega_wrt(2) .or. qm_wrt .or. liutex_wrt .or. (p > 0 .and. schlieren_wrt)) then
47 allocate (fd%fd_coeff_z(-fd_number:fd_number,-offset_z%beg:p + offset_z%end))
48 end if
49
51
52 !> Derive the specific heat ratio from the specific heat ratio function gamma_sf. The latter is stored in the derived flow
53 !! quantity storage variable, q_sf.
55
56 real(wp), dimension(-offset_x%beg:m + offset_x%end,-offset_y%beg:n + offset_y%end,-offset_z%beg:p + offset_z%end), &
57 & intent(inout) :: q_sf
58
59 integer :: i, j, k
60 do k = -offset_z%beg, p + offset_z%end
61 do j = -offset_y%beg, n + offset_y%end
62 do i = -offset_x%beg, m + offset_x%end
63 q_sf(i, j, k) = 1._wp + 1._wp/gamma_sf(i, j, k)
64 end do
65 end do
66 end do
67
68 end subroutine s_derive_specific_heat_ratio
69
70 !> Compute the liquid stiffness from the specific heat ratio function gamma_sf and the liquid stiffness function pi_inf_sf,
71 !! respectively. These are used to calculate the values of the liquid stiffness, which are stored in the derived flow quantity
72 !! storage variable, q_sf.
74
75 real(wp), dimension(-offset_x%beg:m + offset_x%end,-offset_y%beg:n + offset_y%end,-offset_z%beg:p + offset_z%end), &
76 & intent(inout) :: q_sf
77
78 integer :: i, j, k
79 do k = -offset_z%beg, p + offset_z%end
80 do j = -offset_y%beg, n + offset_y%end
81 do i = -offset_x%beg, m + offset_x%end
82 q_sf(i, j, k) = pi_inf_sf(i, j, k)/(gamma_sf(i, j, k) + 1._wp)
83 end do
84 end do
85 end do
86
87 end subroutine s_derive_liquid_stiffness
88
89 !> Compute the speed of sound from the primitive variables, density, specific heat ratio function, and liquid stiffness
90 !! function. It then computes from those variables the values of the speed of sound, which are stored in the derived flow
91 !! quantity storage variable, q_sf.
92 subroutine s_derive_sound_speed(q_prim_vf, q_sf)
93
94 type(scalar_field), dimension(sys_size), intent(in) :: q_prim_vf
95
96 real(wp), dimension(-offset_x%beg:m + offset_x%end,-offset_y%beg:n + offset_y%end,-offset_z%beg:p + offset_z%end), &
97 & intent(inout) :: q_sf
98
99 integer :: i, j, k
100 real(wp) :: blkmod1, blkmod2
101
102 do k = -offset_z%beg, p + offset_z%end
103 do j = -offset_y%beg, n + offset_y%end
104 do i = -offset_x%beg, m + offset_x%end
105 if (alt_soundspeed .neqv. .true.) then
106 q_sf(i, j, k) = (((gamma_sf(i, j, k) + 1._wp)*q_prim_vf(eqn_idx%E)%sf(i, j, k) + pi_inf_sf(i, j, &
107 & k))/(gamma_sf(i, j, k)*rho_sf(i, j, k)))
108 else
109 blkmod1 = ((gammas(1) + 1._wp)*q_prim_vf(eqn_idx%E)%sf(i, j, k) + pi_infs(1))/gammas(1)
110 blkmod2 = ((gammas(2) + 1._wp)*q_prim_vf(eqn_idx%E)%sf(i, j, k) + pi_infs(2))/gammas(2)
111 q_sf(i, j, k) = (1._wp/(rho_sf(i, j, k)*(q_prim_vf(eqn_idx%adv%beg)%sf(i, j, &
112 & k)/blkmod1 + (1._wp - q_prim_vf(eqn_idx%adv%beg)%sf(i, j, k))/blkmod2)))
113 end if
114
115 if (mixture_err .and. q_sf(i, j, k) < 0._wp) then
116 q_sf(i, j, k) = 1.e-16_wp
117 else
118 q_sf(i, j, k) = sqrt(q_sf(i, j, k))
119 end if
120 end do
121 end do
122 end do
123
124 end subroutine s_derive_sound_speed
125
126 !> Derive the flux limiter at cell boundary i+1/2. This is an approximation because the velocity used to determine the upwind
127 !! direction is the velocity at the cell center i instead of the contact velocity at the cell boundary from the Riemann solver.
128 subroutine s_derive_flux_limiter(i, q_prim_vf, q_sf)
129
130 integer, intent(in) :: i
131 type(scalar_field), dimension(sys_size), intent(in) :: q_prim_vf
132
133 real(wp), dimension(-offset_x%beg:m + offset_x%end,-offset_y%beg:n + offset_y%end,-offset_z%beg:p + offset_z%end), &
134 & intent(inout) :: q_sf
135
136 real(wp) :: top, bottom, slope
137 integer :: j, k, l
138 do l = -offset_z%beg, p + offset_z%end
139 do k = -offset_y%beg, n + offset_y%end
140 do j = -offset_x%beg, m + offset_x%end
141 if (i == 1) then
142 if (q_prim_vf(eqn_idx%cont%end + i)%sf(j, k, l) >= 0._wp) then
143 top = q_prim_vf(eqn_idx%adv%beg)%sf(j, k, l) - q_prim_vf(eqn_idx%adv%beg)%sf(j - 1, k, l)
144 bottom = q_prim_vf(eqn_idx%adv%beg)%sf(j + 1, k, l) - q_prim_vf(eqn_idx%adv%beg)%sf(j, k, l)
145 else
146 top = q_prim_vf(eqn_idx%adv%beg)%sf(j + 2, k, l) - q_prim_vf(eqn_idx%adv%beg)%sf(j + 1, k, l)
147 bottom = q_prim_vf(eqn_idx%adv%beg)%sf(j + 1, k, l) - q_prim_vf(eqn_idx%adv%beg)%sf(j, k, l)
148 end if
149 else if (i == 2) then
150 if (q_prim_vf(eqn_idx%cont%end + i)%sf(j, k, l) >= 0._wp) then
151 top = q_prim_vf(eqn_idx%adv%beg)%sf(j, k, l) - q_prim_vf(eqn_idx%adv%beg)%sf(j, k - 1, l)
152 bottom = q_prim_vf(eqn_idx%adv%beg)%sf(j, k + 1, l) - q_prim_vf(eqn_idx%adv%beg)%sf(j, k, l)
153 else
154 top = q_prim_vf(eqn_idx%adv%beg)%sf(j, k + 2, l) - q_prim_vf(eqn_idx%adv%beg)%sf(j, k + 1, l)
155 bottom = q_prim_vf(eqn_idx%adv%beg)%sf(j, k + 1, l) - q_prim_vf(eqn_idx%adv%beg)%sf(j, k, l)
156 end if
157 else
158 if (q_prim_vf(eqn_idx%cont%end + i)%sf(j, k, l) >= 0._wp) then
159 top = q_prim_vf(eqn_idx%adv%beg)%sf(j, k, l) - q_prim_vf(eqn_idx%adv%beg)%sf(j, k, l - 1)
160 bottom = q_prim_vf(eqn_idx%adv%beg)%sf(j, k, l + 1) - q_prim_vf(eqn_idx%adv%beg)%sf(j, k, l)
161 else
162 top = q_prim_vf(eqn_idx%adv%beg)%sf(j, k, l + 2) - q_prim_vf(eqn_idx%adv%beg)%sf(j, k, l + 1)
163 bottom = q_prim_vf(eqn_idx%adv%beg)%sf(j, k, l + 1) - q_prim_vf(eqn_idx%adv%beg)%sf(j, k, l)
164 end if
165 end if
166
167 if (abs(top) < 1.e-8_wp) top = 0._wp
168 if (abs(bottom) < 1.e-8_wp) bottom = 0._wp
169
170 if (f_approx_equal(top, bottom)) then
171 slope = 1._wp
172 else
173 slope = (top*bottom)/(bottom**2._wp + 1.e-16_wp)
174 end if
175
176 if (flux_lim == 1) then ! MINMOD (MM)
177 q_sf(j, k, l) = max(0._wp, min(1._wp, slope))
178 else if (flux_lim == 2) then ! MUSCL (MC)
179 q_sf(j, k, l) = max(0._wp, min(2._wp*slope, 5.e-1_wp*(1._wp + slope), 2._wp))
180 else if (flux_lim == 3) then ! OSPRE (OP)
181 q_sf(j, k, l) = (15.e-1_wp*(slope**2._wp + slope))/(slope**2._wp + slope + 1._wp)
182 else if (flux_lim == 4) then ! SUPERBEE (SB)
183 q_sf(j, k, l) = max(0._wp, min(1._wp, 2._wp*slope), min(slope, 2._wp))
184 else if (flux_lim == 5) then ! SWEBY (SW) (beta = 1.5)
185 q_sf(j, k, l) = max(0._wp, min(15.e-1_wp*slope, 1._wp), min(slope, 15.e-1_wp))
186 else if (flux_lim == 6) then ! VAN ALBADA (VA)
187 q_sf(j, k, l) = (slope**2._wp + slope)/(slope**2._wp + 1._wp)
188 else if (flux_lim == 7) then ! VAN LEER (VL)
189 q_sf(j, k, l) = (abs(slope) + slope)/(1._wp + abs(slope))
190 end if
191 end do
192 end do
193 end do
194
195 end subroutine s_derive_flux_limiter
196
197 !> Compute the specified component of the vorticity from the primitive variables. From those inputs, it proceeds to calculate
198 !! values of the desired vorticity component, which are subsequently stored in derived flow quantity storage variable, q_sf.
199 subroutine s_derive_vorticity_component(i, q_prim_vf, q_sf)
200
201 integer, intent(in) :: i
202 type(scalar_field), dimension(sys_size), intent(in) :: q_prim_vf
203
204 real(wp), dimension(-offset_x%beg:m + offset_x%end,-offset_y%beg:n + offset_y%end,-offset_z%beg:p + offset_z%end), &
205 & intent(inout) :: q_sf
206
207 integer :: j, k, l, r
208 if (i == 1) then
209 do l = -offset_z%beg, p + offset_z%end
210 do k = -offset_y%beg, n + offset_y%end
211 do j = -offset_x%beg, m + offset_x%end
212 q_sf(j, k, l) = 0._wp
213
214 do r = -fd_number, fd_number
215 if (grid_geometry == 3) then
216 q_sf(j, k, l) = q_sf(j, k, l) + 1._wp/y_cc(k)*(fd%fd_coeff_y(r, &
217 & k)*y_cc(r + k)*q_prim_vf(eqn_idx%mom%end)%sf(j, r + k, l) - fd%fd_coeff_z(r, &
218 & l)*q_prim_vf(eqn_idx%mom%beg + 1)%sf(j, k, r + l))
219 else
220 q_sf(j, k, l) = q_sf(j, k, l) + fd%fd_coeff_y(r, k)*q_prim_vf(eqn_idx%mom%end)%sf(j, r + k, &
221 & l) - fd%fd_coeff_z(r, l)*q_prim_vf(eqn_idx%mom%beg + 1)%sf(j, k, r + l)
222 end if
223 end do
224 end do
225 end do
226 end do
227 else if (i == 2) then
228 do l = -offset_z%beg, p + offset_z%end
229 do k = -offset_y%beg, n + offset_y%end
230 do j = -offset_x%beg, m + offset_x%end
231 q_sf(j, k, l) = 0._wp
232
233 do r = -fd_number, fd_number
234 if (grid_geometry == 3) then
235 q_sf(j, k, l) = q_sf(j, k, l) + fd%fd_coeff_z(r, l)/y_cc(k)*q_prim_vf(eqn_idx%mom%beg)%sf(j, k, &
236 & r + l) - fd%fd_coeff_x(r, j)*q_prim_vf(eqn_idx%mom%end)%sf(r + j, k, l)
237 else
238 q_sf(j, k, l) = q_sf(j, k, l) + fd%fd_coeff_z(r, l)*q_prim_vf(eqn_idx%mom%beg)%sf(j, k, &
239 & r + l) - fd%fd_coeff_x(r, j)*q_prim_vf(eqn_idx%mom%end)%sf(r + j, k, l)
240 end if
241 end do
242 end do
243 end do
244 end do
245 else
246 do l = -offset_z%beg, p + offset_z%end
247 do k = -offset_y%beg, n + offset_y%end
248 do j = -offset_x%beg, m + offset_x%end
249 q_sf(j, k, l) = 0._wp
250
251 do r = -fd_number, fd_number
252 q_sf(j, k, l) = q_sf(j, k, l) + fd%fd_coeff_x(r, j)*q_prim_vf(eqn_idx%mom%beg + 1)%sf(r + j, k, &
253 & l) - fd%fd_coeff_y(r, k)*q_prim_vf(eqn_idx%mom%beg)%sf(j, r + k, l)
254 end do
255 end do
256 end do
257 end do
258 end if
259
260 end subroutine s_derive_vorticity_component
261
262 !> Compute the Q_M criterion from the primitive variables. The Q_M function, which are subsequently stored in the derived flow
263 !! quantity storage variable, q_sf.
264 subroutine s_derive_qm(q_prim_vf, q_sf)
265
266 type(scalar_field), dimension(sys_size), intent(in) :: q_prim_vf
267
268 real(wp), dimension(-offset_x%beg:m + offset_x%end,-offset_y%beg:n + offset_y%end,-offset_z%beg:p + offset_z%end), &
269 & intent(inout) :: q_sf
270
271 real(wp), dimension(1:3,1:3) :: q_jacobian_sf, s, s2, o, o2
272 real(wp) :: trs, q, iis
273 integer :: j, k, l, r, jj, kk
274 do l = -offset_z%beg, p + offset_z%end
275 do k = -offset_y%beg, n + offset_y%end
276 do j = -offset_x%beg, m + offset_x%end
277 ! Get velocity gradient tensor
278 q_jacobian_sf(:,:) = 0._wp
279
280 do r = -fd_number, fd_number
281 do jj = 1, 3
282 ! d()/dx
283 q_jacobian_sf(jj, 1) = q_jacobian_sf(jj, 1) + fd%fd_coeff_x(r, &
284 & j)*q_prim_vf(eqn_idx%mom%beg + jj - 1)%sf(r + j, k, l)
285 ! d()/dy
286 q_jacobian_sf(jj, 2) = q_jacobian_sf(jj, 2) + fd%fd_coeff_y(r, &
287 & k)*q_prim_vf(eqn_idx%mom%beg + jj - 1)%sf(j, r + k, l)
288 ! d()/dz
289 q_jacobian_sf(jj, 3) = q_jacobian_sf(jj, 3) + fd%fd_coeff_z(r, &
290 & l)*q_prim_vf(eqn_idx%mom%beg + jj - 1)%sf(j, k, r + l)
291 end do
292 end do
293
294 ! Decompose velocity gradient into symmetric strain-rate S and skew-symmetric rotation-rate O
295 do jj = 1, 3
296 do kk = 1, 3
297 s(jj, kk) = 0.5_wp*(q_jacobian_sf(jj, kk) + q_jacobian_sf(kk, jj))
298 o(jj, kk) = 0.5_wp*(q_jacobian_sf(jj, kk) - q_jacobian_sf(kk, jj))
299 end do
300 end do
301
302 do jj = 1, 3
303 do kk = 1, 3
304 o2(jj, kk) = o(jj, 1)*o(kk, 1) + o(jj, 2)*o(kk, 2) + o(jj, 3)*o(kk, 3)
305 s2(jj, kk) = s(jj, 1)*s(kk, 1) + s(jj, 2)*s(kk, 2) + s(jj, 3)*s(kk, 3)
306 end do
307 end do
308
309 ! Q-criterion: Q = (||O||^2 - ||S||^2)/2, Hunt et al. CTR (1988)
310 q = 0.5_wp*((o2(1, 1) + o2(2, 2) + o2(3, 3)) - (s2(1, 1) + s2(2, 2) + s2(3, 3)))
311 trs = s(1, 1) + s(2, 2) + s(3, 3)
312 ! Second invariant of strain-rate tensor
313 iis = 0.5_wp*((s(1, 1) + s(2, 2) + s(3, 3))**2 - (s2(1, 1) + s2(2, 2) + s2(3, 3)))
314 q_sf(j, k, l) = q + iis
315 end do
316 end do
317 end do
318
319 end subroutine s_derive_qm
320
321 !> Compute the Liutex vector and its magnitude based on Xu et al. (2019).
322 impure subroutine s_derive_liutex(q_prim_vf, liutex_mag, liutex_axis)
323
324 ! Liutex vortex identification via real eigenvector of velocity gradient, Xu et al. PoF (2019)
325
326 integer, parameter :: nm = 3
327 type(scalar_field), dimension(sys_size), intent(in) :: q_prim_vf
328
329 !> Liutex magnitude
330
331 real(wp), dimension(-offset_x%beg:m + offset_x%end,-offset_y%beg:n + offset_y%end,-offset_z%beg:p + offset_z%end), &
332 & intent(out) :: liutex_mag
333 !> Liutex rigid rotation axis
334 real(wp), dimension(-offset_x%beg:m + offset_x%end,-offset_y%beg:n + offset_y%end,-offset_z%beg:p + offset_z%end,nm), &
335 & intent(out) :: liutex_axis
336 character, parameter :: ivl = 'N' !< compute left eigenvectors
337 character, parameter :: ivr = 'V' !< compute right eigenvectors
338 real(wp), dimension(nm, nm) :: vgt !< velocity gradient tensor
339 real(wp), dimension(nm) :: lr, li !< real and imaginary parts of eigenvalues
340 real(wp), dimension(nm, nm) :: vl, vr !< left and right eigenvectors
341 integer, parameter :: lwork = 4*nm !< size of work array (4*nm recommended)
342 real(wp), dimension(lwork) :: work !< work array
343 integer :: info
344 real(wp), dimension(nm) :: eigvec !< real eigenvector
345 real(wp) :: eigvec_mag !< magnitude of real eigenvector
346 real(wp) :: omega_proj !< projection of vorticity on real eigenvector
347 real(wp) :: lci !< imaginary part of complex eigenvalue
348 real(wp) :: alpha
349 integer :: j, k, l, r, i
350 integer :: idx
351
352 do l = -offset_z%beg, p + offset_z%end
353 do k = -offset_y%beg, n + offset_y%end
354 do j = -offset_x%beg, m + offset_x%end
355 ! Get velocity gradient tensor (VGT)
356 vgt(:,:) = 0._wp
357
358 do r = -fd_number, fd_number
359 do i = 1, 3
360 ! d()/dx
361 vgt(i, 1) = vgt(i, 1) + fd%fd_coeff_x(r, j)*q_prim_vf(eqn_idx%mom%beg + i - 1)%sf(r + j, k, l)
362 ! d()/dy
363 vgt(i, 2) = vgt(i, 2) + fd%fd_coeff_y(r, k)*q_prim_vf(eqn_idx%mom%beg + i - 1)%sf(j, r + k, l)
364 ! d()/dz
365 vgt(i, 3) = vgt(i, 3) + fd%fd_coeff_z(r, l)*q_prim_vf(eqn_idx%mom%beg + i - 1)%sf(j, k, r + l)
366 end do
367 end do
368
369 ! Call appropriate LAPACK routine based on precision
370#ifdef MFC_SINGLE_PRECISION
371 call sgeev(ivl, ivr, nm, vgt, nm, lr, li, vl, nm, vr, nm, work, lwork, info)
372#else
373 call dgeev(ivl, ivr, nm, vgt, nm, lr, li, vl, nm, vr, nm, work, lwork, info)
374#endif
375
376 ! Find eigenvector with smallest imaginary eigenvalue (real eigenvector of VGT)
377 idx = 1
378 do r = 2, 3
379 if (abs(li(r)) < abs(li(idx))) then
380 idx = r
381 end if
382 end do
383 eigvec = vr(:,idx)
384
385 ! Normalize real eigenvector if it is effectively non-zero
386 eigvec_mag = sqrt(eigvec(1)**2._wp + eigvec(2)**2._wp + eigvec(3)**2._wp)
387 if (eigvec_mag > sgm_eps) then
388 eigvec = eigvec/eigvec_mag
389 else
390 eigvec = 0._wp
391 end if
392
393 ! Compute vorticity projected on the eigenvector
394 omega_proj = (vgt(3, 2) - vgt(2, 3))*eigvec(1) + (vgt(1, 3) - vgt(3, 1))*eigvec(2) + (vgt(2, 1) - vgt(1, &
395 & 2))*eigvec(3)
396
397 ! As eigenvector can have +/- signs, we can choose the sign so that omega_proj is positive
398 if (omega_proj < 0._wp) then
399 eigvec = -eigvec
400 omega_proj = -omega_proj
401 end if
402
403 ! Imaginary eigenvalue of the complex conjugate pair (cyclic index selection)
404 lci = li(mod(idx, 3) + 1)
405
406 ! Discriminant: determines whether rotation dominates strain
407 alpha = omega_proj**2._wp - 4._wp*lci**2._wp
408 ! Liutex magnitude = omega_proj - sqrt(discriminant) when rotation dominates
409 if (alpha > 0._wp) then
410 liutex_mag(j, k, l) = omega_proj - sqrt(alpha)
411 else
412 liutex_mag(j, k, l) = omega_proj
413 end if
414
415 ! Compute Liutex axis
416 liutex_axis(j, k, l, 1) = eigvec(1)
417 liutex_axis(j, k, l, 2) = eigvec(2)
418 liutex_axis(j, k, l, 3) = eigvec(3)
419 end do
420 end do
421 end do
422
423 end subroutine s_derive_liutex
424
425 !> Compute the values of the numerical Schlieren function, which are subsequently stored in the derived flow quantity storage
426 !! variable, q_sf.
427 impure subroutine s_derive_numerical_schlieren_function(q_cons_vf, q_sf)
428
429 type(scalar_field), dimension(sys_size), intent(in) :: q_cons_vf
430
431 real(wp), dimension(-offset_x%beg:m + offset_x%end,-offset_y%beg:n + offset_y%end,-offset_z%beg:p + offset_z%end), &
432 & intent(inout) :: q_sf
433
434 real(wp) :: drho_dx, drho_dy, drho_dz !< Spatial derivatives of the density in the x-, y- and z-directions
435 real(wp), dimension(2) :: gm_rho_max !< Global (max gradient magnitude, rank) pair for density
436 real(wp) :: alpha_last !< Volume fraction of the fluid not explicitly stored (IGR)
437 integer :: i, j, k, l
438
439 do l = -offset_z%beg, p + offset_z%end
440 do k = -offset_y%beg, n + offset_y%end
441 do j = -offset_x%beg, m + offset_x%end
442 drho_dx = 0._wp
443 drho_dy = 0._wp
444
445 do i = -fd_number, fd_number
446 drho_dx = drho_dx + fd%fd_coeff_x(i, j)*rho_sf(i + j, k, l)
447 drho_dy = drho_dy + fd%fd_coeff_y(i, k)*rho_sf(j, i + k, l)
448 end do
449
450 fd%gm_rho_sf(j, k, l) = drho_dx*drho_dx + drho_dy*drho_dy
451 end do
452 end do
453 end do
454
455 if (p > 0) then
456 do l = -offset_z%beg, p + offset_z%end
457 do k = -offset_y%beg, n + offset_y%end
458 do j = -offset_x%beg, m + offset_x%end
459 drho_dz = 0._wp
460
461 do i = -fd_number, fd_number
462 if (grid_geometry == 3) then
463 drho_dz = drho_dz + fd%fd_coeff_z(i, l)/y_cc(k)*rho_sf(j, k, i + l)
464 else
465 drho_dz = drho_dz + fd%fd_coeff_z(i, l)*rho_sf(j, k, i + l)
466 end if
467 end do
468
469 fd%gm_rho_sf(j, k, l) = fd%gm_rho_sf(j, k, l) + drho_dz*drho_dz
470 end do
471 end do
472 end do
473 end if
474
475 fd%gm_rho_sf = sqrt(fd%gm_rho_sf)
476
477 gm_rho_max = (/maxval(fd%gm_rho_sf), real(proc_rank, wp)/)
478
479 if (num_procs > 1) call s_mpi_reduce_maxloc(gm_rho_max)
480
481 ! The form of the numerical Schlieren function depends on the choice of the multicomponent flow model. For the gamma/pi_inf
482 ! model, the exponential of the negative, normalized, gradient magnitude of the density is computed. For the volume fraction
483 ! model, the amplitude of the exponential's inside is also modulated with respect to the identity of the fluid in which the
484 ! function is evaluated. For more information, refer to Marquina and Mulet (2003).
485
486 if (model_eqns == model_eqns_gamma_law) then ! Gamma/pi_inf model
487 q_sf = -fd%gm_rho_sf/gm_rho_max(1)
488 else ! Volume fraction model
489 do l = -offset_z%beg, p + offset_z%end
490 do k = -offset_y%beg, n + offset_y%end
491 do j = -offset_x%beg, m + offset_x%end
492 q_sf(j, k, l) = 0._wp
493
494 ! Tracks the volume fraction of the fluid not explicitly stored (IGR reconstructs it as 1 - sum)
495 if (igr) then
496 ! IGR stores only num_fluids-1 volume fractions; the last fluid's volume fraction is untracked.
497 ! For a single fluid this is simply 1.0 everywhere. Without this term, the entire single-fluid
498 ! Schlieren field is dropped, leaving exp(0) = 1 everywhere. Compute that term below.
499 alpha_last = 1._wp
500 do i = 1, eqn_idx%adv%end - eqn_idx%E
501 q_sf(j, k, l) = q_sf(j, k, l) - schlieren_alpha(i)*q_cons_vf(i + eqn_idx%E)%sf(j, k, &
502 & l)*fd%gm_rho_sf(j, k, l)/gm_rho_max(1)
503 alpha_last = alpha_last - q_cons_vf(i + eqn_idx%E)%sf(j, k, l)
504 end do
505 q_sf(j, k, l) = q_sf(j, k, l) - schlieren_alpha(num_fluids)*alpha_last*fd%gm_rho_sf(j, k, &
506 & l)/gm_rho_max(1)
507 else
508 do i = 1, eqn_idx%adv%end - eqn_idx%E
509 q_sf(j, k, l) = q_sf(j, k, l) - schlieren_alpha(i)*q_cons_vf(i + eqn_idx%E)%sf(j, k, &
510 & l)*fd%gm_rho_sf(j, k, l)/gm_rho_max(1)
511 end do
512 end if
513 end do
514 end do
515 end do
516 end if
517
518 ! Up until now, only the inside of the exponential of the numerical Schlieren function has been evaluated and stored. Then,
519 ! to finish the computation, the exponential of the inside quantity is taken.
520 q_sf = exp(q_sf)
521
523
524 !> Deallocation procedures for the module
526
527 ! Deallocating the variable containing the gradient magnitude of the density field provided that the numerical Schlieren
528 ! function was was outputted during the post-process
529 if (schlieren_wrt) deallocate (fd%gm_rho_sf)
530
531 ! Deallocating the variables that might have been used to bookkeep the finite-difference coefficients in the x-, y- and
532 ! z-directions
533 if (allocated(fd%fd_coeff_x)) deallocate (fd%fd_coeff_x)
534 if (allocated(fd%fd_coeff_y)) deallocate (fd%fd_coeff_y)
535 if (allocated(fd%fd_coeff_z)) deallocate (fd%fd_coeff_z)
536
538
539end module m_derived_variables
type(scalar_field), dimension(sys_size), intent(inout) q_cons_vf
integer, intent(in) k
integer, intent(in) j
integer, intent(in) l
Compile-time constant parameters: default values, tolerances, and physical constants.
real(wp), parameter sgm_eps
Segmentation tolerance.
integer, parameter model_eqns_gamma_law
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...
type(fd_context), public fd
Finite-difference state: density gradient magnitude and centered FD coefficients in x-,...
impure subroutine, public s_derive_liutex(q_prim_vf, liutex_mag, liutex_axis)
Compute the Liutex vector and its magnitude based on Xu et al. (2019).
subroutine, public s_derive_specific_heat_ratio(q_sf)
Derive the specific heat ratio from the specific heat ratio function gamma_sf. The latter is stored i...
subroutine, public s_derive_liquid_stiffness(q_sf)
Compute the liquid stiffness from the specific heat ratio function gamma_sf and the liquid stiffness ...
impure subroutine, public s_initialize_derived_variables_module
Computation of parameters, allocation procedures, and/or any other tasks needed to properly setup the...
subroutine, public s_derive_vorticity_component(i, q_prim_vf, q_sf)
Compute the specified component of the vorticity from the primitive variables. From those inputs,...
subroutine, public s_derive_sound_speed(q_prim_vf, q_sf)
Compute the speed of sound from the primitive variables, density, specific heat ratio function,...
impure subroutine, public s_derive_numerical_schlieren_function(q_cons_vf, q_sf)
Compute the values of the numerical Schlieren function, which are subsequently stored in the derived ...
subroutine, public s_derive_flux_limiter(i, q_prim_vf, q_sf)
Derive the flux limiter at cell boundary i+1/2. This is an approximation because the velocity used to...
impure subroutine, public s_finalize_derived_variables_module
Deallocation procedures for the module.
subroutine, public s_derive_qm(q_prim_vf, q_sf)
Compute the Q_M criterion from the primitive variables. The Q_M function, which are subsequently stor...
Global parameters for the post-process: domain geometry, equation of state, and output database setti...
type(int_bounds_info) offset_y
real(wp), dimension(:), allocatable y_cc
integer proc_rank
Rank of the local processor.
integer fd_number
Finite-difference half-stencil size: MAX(1, fd_order/2).
type(int_bounds_info) offset_x
integer num_procs
Number of processors.
type(int_bounds_info) offset_z
Basic floating-point utilities: approximate equality, default detection, and coordinate bounds.
logical elemental function, public f_approx_equal(a, b, tol_input)
Check 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
subroutine s_compute_speed_of_sound(pres, rho, gamma, pi_inf, h, adv, vel_sum, c_c, c, qv)
Compute the speed of sound from thermodynamic state variables, supporting multiple equation-of-state ...
real(wp), dimension(:,:,:), allocatable, public pi_inf_sf
Scalar liquid stiffness function.
real(wp), dimension(:,:,:), allocatable, public gamma_sf
Scalar sp. heat ratio function.
real(wp), dimension(:,:,:), allocatable, public rho_sf
Scalar density function.
real(wp), dimension(:), allocatable, public pi_infs
Finite-difference state for post_process: density gradient magnitude for numerical Schlieren and cent...
Derived type annexing a scalar field (SF).