From 8320e550ef12e6341e5e55e92501597e34840323 Mon Sep 17 00:00:00 2001 From: Cowsreal Date: Mon, 1 Jun 2026 11:33:10 -0400 Subject: [PATCH 1/4] Fix numerical Schlieren for single fluid IGR and add guard for schlieren_alpha --- docs/documentation/case.md | 2 +- src/post_process/m_derived_variables.fpp | 11 +++++++++++ toolchain/mfc/case_validator.py | 12 +++++++++++- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/docs/documentation/case.md b/docs/documentation/case.md index b92e8f945e..63a4ecd8da 100644 --- a/docs/documentation/case.md +++ b/docs/documentation/case.md @@ -704,7 +704,7 @@ If `file_per_process` is true, then pre_process, simulation, and post_process mu - ``[variable's name]_wrt`` activates the output of each specified variable into the database. -- `schlieren_alpha(i)` specifies the intensity of the numerical Schlieren of $i$-th component. +- `schlieren_alpha(i)` specifies the intensity of the numerical Schlieren of $i$-th component. It must be specified for every fluid when `schlieren_wrt` is enabled. - `fd_order` specifies the order of the finite difference scheme used to compute the vorticity from the velocity field and the numerical schlieren from the density field using an integer of 1, 2, and 4. `fd_order = 1`, `2`, and `4` correspond to the first, second, and fourth-order finite difference schemes. diff --git a/src/post_process/m_derived_variables.fpp b/src/post_process/m_derived_variables.fpp index 9676e44396..c1d2d1fec8 100644 --- a/src/post_process/m_derived_variables.fpp +++ b/src/post_process/m_derived_variables.fpp @@ -438,6 +438,7 @@ contains real(wp) :: drho_dx, drho_dy, drho_dz !< Spatial derivatives of the density in the x-, y- and z-directions real(wp), dimension(2) :: gm_rho_max !< Global (max gradient magnitude, rank) pair for density + real(wp) :: alpha_last !< Volume fraction of the fluid not explicitly stored (IGR) integer :: i, j, k, l do l = -offset_z%beg, p + offset_z%end @@ -494,11 +495,21 @@ contains do k = -offset_y%beg, n + offset_y%end do j = -offset_x%beg, m + offset_x%end q_sf(j, k, l) = 0._wp + ! Tracks the volume fraction of the fluid not explicitly stored (IGR reconstructs it as 1 - sum) + alpha_last = 1._wp do i = 1, eqn_idx%adv%end - eqn_idx%E q_sf(j, k, l) = q_sf(j, k, l) - schlieren_alpha(i)*q_cons_vf(i + eqn_idx%E)%sf(j, k, l)*gm_rho_sf(j, & & k, l)/gm_rho_max(1) + alpha_last = alpha_last - q_cons_vf(i + eqn_idx%E)%sf(j, k, l) end do + + ! IGR stores only num_fluids-1 volume fractions; the last fluid's volume fraction is untracked. + ! For a single fluid this is simply 1.0 everywhere. Without this term, the entire single-fluid + ! Schlieren field) is dropped, leaving exp(0) = 1 everywhere. Compute that term below. + if (igr) then + q_sf(j, k, l) = q_sf(j, k, l) - schlieren_alpha(num_fluids)*alpha_last*gm_rho_sf(j, k, l)/gm_rho_max(1) + end if end do end do end do diff --git a/toolchain/mfc/case_validator.py b/toolchain/mfc/case_validator.py index 9663676d7e..6ab1253415 100644 --- a/toolchain/mfc/case_validator.py +++ b/toolchain/mfc/case_validator.py @@ -1553,14 +1553,24 @@ def check_schlieren(self): n = self.get("n", 0) fd_order = self.get("fd_order") num_fluids = self.get("num_fluids") + model_eqns = self.get("model_eqns") self.prohibit(n is not None and n == 0 and schlieren_wrt, "schlieren_wrt requires n > 0 (at least 2D)") self.prohibit(schlieren_wrt and fd_order is None, "fd_order must be set for schlieren_wrt") + # The volume-fraction model (model_eqns /= 1) weights the Schlieren field by schlieren_alpha(i) for every fluid; an unset + # value stays at the -1e6 sentinel and produces nonsensical output. Under IGR the last fluid's volume fraction is + # reconstructed (not stored), so schlieren_alpha must still be set for all num_fluids components, including the single + # fluid of a single-fluid IGR case. The gamma/pi_inf model (model_eqns == 1) does not use schlieren_alpha. if num_fluids is not None: for i in range(1, num_fluids + 1): schlieren_alpha = self.get(f"schlieren_alpha({i})") - if schlieren_alpha is not None: + if schlieren_alpha is None: + self.prohibit( + schlieren_wrt and model_eqns is not None and model_eqns != 1, + f"schlieren_alpha({i}) must be set for every fluid when schlieren_wrt is enabled (unless model_eqns == 1)", + ) + else: self.prohibit(schlieren_alpha <= 0, f"schlieren_alpha({i}) must be greater than zero") self.prohibit(not schlieren_wrt, f"schlieren_alpha({i}) should be set only with schlieren_wrt enabled") From 7c27856af427c1d1d024e02b9fb752e8456ce6d6 Mon Sep 17 00:00:00 2001 From: Cowsreal Date: Mon, 1 Jun 2026 11:47:48 -0400 Subject: [PATCH 2/4] Add missing if (igr) --- src/post_process/m_derived_variables.fpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/post_process/m_derived_variables.fpp b/src/post_process/m_derived_variables.fpp index c1d2d1fec8..6c4fe16452 100644 --- a/src/post_process/m_derived_variables.fpp +++ b/src/post_process/m_derived_variables.fpp @@ -501,7 +501,9 @@ contains do i = 1, eqn_idx%adv%end - eqn_idx%E q_sf(j, k, l) = q_sf(j, k, l) - schlieren_alpha(i)*q_cons_vf(i + eqn_idx%E)%sf(j, k, l)*gm_rho_sf(j, & & k, l)/gm_rho_max(1) - alpha_last = alpha_last - q_cons_vf(i + eqn_idx%E)%sf(j, k, l) + if (igr) then + alpha_last = alpha_last - q_cons_vf(i + eqn_idx%E)%sf(j, k, l) + end if end do ! IGR stores only num_fluids-1 volume fractions; the last fluid's volume fraction is untracked. From c6bfbe2b13486d0a0c9d2b56e28675af62dd4d56 Mon Sep 17 00:00:00 2001 From: Cowsreal Date: Mon, 1 Jun 2026 18:07:31 -0400 Subject: [PATCH 3/4] Minor adjustments --- src/post_process/m_derived_variables.fpp | 28 +++++++++++++----------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/post_process/m_derived_variables.fpp b/src/post_process/m_derived_variables.fpp index 6c4fe16452..3a74d49da7 100644 --- a/src/post_process/m_derived_variables.fpp +++ b/src/post_process/m_derived_variables.fpp @@ -495,22 +495,24 @@ contains do k = -offset_y%beg, n + offset_y%end do j = -offset_x%beg, m + offset_x%end q_sf(j, k, l) = 0._wp - ! Tracks the volume fraction of the fluid not explicitly stored (IGR reconstructs it as 1 - sum) - alpha_last = 1._wp - do i = 1, eqn_idx%adv%end - eqn_idx%E - q_sf(j, k, l) = q_sf(j, k, l) - schlieren_alpha(i)*q_cons_vf(i + eqn_idx%E)%sf(j, k, l)*gm_rho_sf(j, & - & k, l)/gm_rho_max(1) - if (igr) then - alpha_last = alpha_last - q_cons_vf(i + eqn_idx%E)%sf(j, k, l) - end if - end do - - ! IGR stores only num_fluids-1 volume fractions; the last fluid's volume fraction is untracked. - ! For a single fluid this is simply 1.0 everywhere. Without this term, the entire single-fluid - ! Schlieren field) is dropped, leaving exp(0) = 1 everywhere. Compute that term below. + ! Tracks the volume fraction of the fluid not explicitly stored (IGR reconstructs it as 1 - sum) if (igr) then + ! IGR stores only num_fluids-1 volume fractions; the last fluid's volume fraction is untracked. + ! For a single fluid this is simply 1.0 everywhere. Without this term, the entire single-fluid + ! Schlieren field is dropped, leaving exp(0) = 1 everywhere. Compute that term below. + alpha_last = 1._wp + do i = 1, eqn_idx%adv%end - eqn_idx%E + q_sf(j, k, l) = q_sf(j, k, l) - schlieren_alpha(i)*q_cons_vf(i + eqn_idx%E)%sf(j, k, l)*gm_rho_sf(j, & + & k, l)/gm_rho_max(1) + alpha_last = alpha_last - q_cons_vf(i + eqn_idx%E)%sf(j, k, l) + end do q_sf(j, k, l) = q_sf(j, k, l) - schlieren_alpha(num_fluids)*alpha_last*gm_rho_sf(j, k, l)/gm_rho_max(1) + else + do i = 1, eqn_idx%adv%end - eqn_idx%E + q_sf(j, k, l) = q_sf(j, k, l) - schlieren_alpha(i)*q_cons_vf(i + eqn_idx%E)%sf(j, k, l)*gm_rho_sf(j, & + & k, l)/gm_rho_max(1) + end do end if end do end do From 8d4a8bb8f14cf6b81ef5d317222860266c2c98aa Mon Sep 17 00:00:00 2001 From: Cowsreal Date: Mon, 1 Jun 2026 19:13:52 -0400 Subject: [PATCH 4/4] Formatting --- src/post_process/m_derived_variables.fpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/post_process/m_derived_variables.fpp b/src/post_process/m_derived_variables.fpp index 3a74d49da7..e70a117fa4 100644 --- a/src/post_process/m_derived_variables.fpp +++ b/src/post_process/m_derived_variables.fpp @@ -503,15 +503,15 @@ contains ! Schlieren field is dropped, leaving exp(0) = 1 everywhere. Compute that term below. alpha_last = 1._wp do i = 1, eqn_idx%adv%end - eqn_idx%E - q_sf(j, k, l) = q_sf(j, k, l) - schlieren_alpha(i)*q_cons_vf(i + eqn_idx%E)%sf(j, k, l)*gm_rho_sf(j, & - & k, l)/gm_rho_max(1) + q_sf(j, k, l) = q_sf(j, k, l) - schlieren_alpha(i)*q_cons_vf(i + eqn_idx%E)%sf(j, k, & + & l)*gm_rho_sf(j, k, l)/gm_rho_max(1) alpha_last = alpha_last - q_cons_vf(i + eqn_idx%E)%sf(j, k, l) end do q_sf(j, k, l) = q_sf(j, k, l) - schlieren_alpha(num_fluids)*alpha_last*gm_rho_sf(j, k, l)/gm_rho_max(1) - else + else do i = 1, eqn_idx%adv%end - eqn_idx%E - q_sf(j, k, l) = q_sf(j, k, l) - schlieren_alpha(i)*q_cons_vf(i + eqn_idx%E)%sf(j, k, l)*gm_rho_sf(j, & - & k, l)/gm_rho_max(1) + q_sf(j, k, l) = q_sf(j, k, l) - schlieren_alpha(i)*q_cons_vf(i + eqn_idx%E)%sf(j, k, & + & l)*gm_rho_sf(j, k, l)/gm_rho_max(1) end do end if end do