Skip to content

Commit 2e0c334

Browse files
committed
Clamp the cubic Hugoniot at its turnover, and count the relaxation solves that miss
1 parent 74f8203 commit 2e0c334

4 files changed

Lines changed: 89 additions & 11 deletions

File tree

src/common/m_global_parameters_common.fpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,12 @@ module m_global_parameters_common
5959
!> Reference state and Gruneisen closure Gamma_G = Gamma_0 + a mu of a state-dependent EOS, whatever its family
6060
real(wp), allocatable, dimension(:) :: rho0s, t0s, gruneisen0s, gruneisen_as
6161
real(wp), allocatable, dimension(:) :: mg_c0s, mg_ss, mg_s2s, mg_s3s
62+
real(wp), allocatable, dimension(:) :: mg_mu_maxs !< Compression at which a cubic Hugoniot fit turns over
6263
real(wp), allocatable, dimension(:) :: jwl_as, jwl_bs, jwl_r1s, jwl_r2s
6364
real(wp), allocatable, dimension(:) :: vinet_k0s, vinet_k0ps
64-
logical :: any_state_dependent_eos !< True when some fluid's coefficients vary with density; set at init
65-
$:GPU_DECLARE(create='[eoss, rho0s, t0s, gruneisen0s, gruneisen_as, mg_c0s, mg_ss, mg_s2s, mg_s3s, jwl_as, jwl_bs, jwl_r1s, &
66-
& jwl_r2s, vinet_k0s, vinet_k0ps, any_state_dependent_eos]')
65+
logical :: any_state_dependent_eos !< True when some fluid's coefficients vary with density; set at init
66+
$:GPU_DECLARE(create='[eoss, rho0s, t0s, gruneisen0s, gruneisen_as, mg_c0s, mg_ss, mg_s2s, mg_s3s, mg_mu_maxs, jwl_as, &
67+
& jwl_bs, jwl_r1s, jwl_r2s, vinet_k0s, vinet_k0ps, any_state_dependent_eos]')
6768
!> @}
6869
6970
!> @name Fluids participating in shear and bulk viscosity

src/common/m_variables_conversion.fpp

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,8 @@ contains
270270
@:ALLOCATE(gammas (1:num_fluids))
271271
@:ALLOCATE(eoss (1:num_fluids), rho0s (1:num_fluids), t0s (1:num_fluids), gruneisen0s (1:num_fluids), &
272272
& gruneisen_as (1:num_fluids), mg_c0s (1:num_fluids), mg_ss (1:num_fluids), mg_s2s (1:num_fluids), &
273-
& mg_s3s (1:num_fluids), jwl_as (1:num_fluids), jwl_bs (1:num_fluids), jwl_r1s (1:num_fluids), &
274-
& jwl_r2s (1:num_fluids), vinet_k0s (1:num_fluids), vinet_k0ps (1:num_fluids))
273+
& mg_s3s (1:num_fluids), mg_mu_maxs (1:num_fluids), jwl_as (1:num_fluids), jwl_bs (1:num_fluids), &
274+
& jwl_r1s (1:num_fluids), jwl_r2s (1:num_fluids), vinet_k0s (1:num_fluids), vinet_k0ps (1:num_fluids))
275275
@:ALLOCATE(isentrope_n (1:num_fluids))
276276
@:ALLOCATE(pi_infs(1:num_fluids))
277277
@:ALLOCATE(isentrope_B(1:num_fluids))
@@ -303,6 +303,9 @@ contains
303303
mg_ss(i) = fluid_pp(i)%mg_s
304304
mg_s2s(i) = fluid_pp(i)%mg_s2
305305
mg_s3s(i) = fluid_pp(i)%mg_s3
306+
! Where a cubic Hugoniot fit turns over: mu(u_p) peaks where c0 = s2 u_p^2 + 2 s3 u_p^3, and past it
307+
! no shock state exists, so the Newton below would wander. Solved once here, on the host.
308+
mg_mu_maxs(i) = f_hugoniot_compression_limit(fluid_pp(i)%mg_c0, fluid_pp(i)%mg_s, fluid_pp(i)%mg_s2, fluid_pp(i)%mg_s3)
306309
jwl_as(i) = fluid_pp(i)%jwl_a
307310
jwl_bs(i) = fluid_pp(i)%jwl_b
308311
jwl_r1s(i) = fluid_pp(i)%jwl_r1
@@ -335,7 +338,8 @@ contains
335338
if (f_is_state_dependent(i)) any_state_dependent_eos = .true.
336339
end do
337340
$:GPU_UPDATE(device='[gammas, isentrope_n, pi_infs, isentrope_B, cvs, qvs, qvps, Gs_vc, eoss, rho0s, t0s, gruneisen0s, &
338-
& gruneisen_as, mg_c0s, mg_ss, mg_s2s, mg_s3s, jwl_as, jwl_bs, jwl_r1s, jwl_r2s, vinet_k0s, vinet_k0ps, any_state_dependent_eos]')
341+
& gruneisen_as, mg_c0s, mg_ss, mg_s2s, mg_s3s, mg_mu_maxs, jwl_as, jwl_bs, jwl_r1s, jwl_r2s, vinet_k0s, &
342+
& vinet_k0ps, any_state_dependent_eos]')
339343

340344
@:ALLOCATE(Res_vc(1:2, 1:max(1, Re_size_max)))
341345
Res_vc = dflt_real
@@ -1219,7 +1223,7 @@ contains
12191223
if (allocated(rho_sf)) deallocate (rho_sf, gamma_sf, pi_inf_sf)
12201224

12211225
@:DEALLOCATE(gammas, isentrope_n, pi_infs, isentrope_B, cvs, qvs, qvps, Gs_vc, eoss, rho0s, t0s, gruneisen0s, &
1222-
& gruneisen_as, mg_c0s, mg_ss, mg_s2s, mg_s3s, jwl_as, jwl_bs, jwl_r1s, jwl_r2s, vinet_k0s, vinet_k0ps)
1226+
& gruneisen_as, mg_c0s, mg_ss, mg_s2s, mg_s3s, mg_mu_maxs, jwl_as, jwl_bs, jwl_r1s, jwl_r2s, vinet_k0s, vinet_k0ps)
12231227
if (allocated(bubrs_vc)) then
12241228
@:DEALLOCATE(bubrs_vc)
12251229
end if
@@ -1353,6 +1357,9 @@ contains
13531357
integer :: iter
13541358
13551359
mu = rho/rho0s(i) - 1._wp
1360+
! Past the fit's turnover there is no shock state to find; clamp rather than let the Newton below wander
1361+
! off and return a silently wrong pressure. mg_mu_maxs is huge for the linear fit, so this is a no-op there.
1362+
if (eoss(i) == eos_mie_gruneisen .and. mu > mg_mu_maxs(i)) mu = mg_mu_maxs(i)
13561363
select case (eoss(i))
13571364
case (eos_mie_gruneisen)
13581365
! Hugoniot reference u_s = c0 + s u_p + s2 u_p^2 + s3 u_p^3, with p_H = rho0 u_s u_p and the Hugoniot
@@ -1435,6 +1442,33 @@ contains
14351442

14361443
end function f_has_isentropic_reference
14371444

1445+
!> The largest compression a cubic Hugoniot fit can represent. mu(u_p) = u_p/(u_s - u_p) rises, peaks where c0 = s2 u_p^2 + 2 s3
1446+
!! u_p^3, and falls after; only the rising branch is a physical shock. Returns a huge value for the linear fit, which never
1447+
!! turns over. Host-side: called once per fluid at initialization.
1448+
impure function f_hugoniot_compression_limit(c0, s, s2, s3) result(mu_max)
1449+
1450+
real(wp), intent(in) :: c0, s, s2, s3
1451+
real(wp) :: mu_max, up, f, df, us
1452+
integer :: iter
1453+
1454+
if (s2 == 0._wp .and. s3 == 0._wp) then
1455+
mu_max = huge(1._wp)
1456+
return
1457+
end if
1458+
1459+
! Newton on c0 - s2 u^2 - 2 s3 u^3 = 0, from a guess that brackets the physical range
1460+
up = c0
1461+
do iter = 1, 100
1462+
f = c0 - s2*up**2 - 2._wp*s3*up**3
1463+
df = -2._wp*s2*up - 6._wp*s3*up**2
1464+
if (abs(df) < verysmall) exit
1465+
up = max(up - f/df, verysmall)
1466+
end do
1467+
us = c0 + up*(s + up*(s2 + up*s3))
1468+
mu_max = up/max(us - up, verysmall)
1469+
1470+
end function f_hugoniot_compression_limit
1471+
14381472
!> Gamma, Pi, dPi/drho and dGamma/drho of fluid i at density rho, the coefficients of rho e = Gamma p + Pi(rho). Stiffened and
14391473
!! ideal gas keep the constants resolved at init, bit for bit.
14401474
subroutine s_eos_coefficients(rho, i, gamma, pi_inf, dpi, dgamma)

src/simulation/m_pressure_relaxation.fpp

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,12 @@ module m_pressure_relaxation
1616

1717
implicit none
1818

19-
private; public :: s_pressure_relaxation_procedure
19+
private; public :: s_pressure_relaxation_procedure, s_report_pressure_relaxation
20+
21+
!> Cell updates in which the Newton below stopped on its iteration cap rather than on the tolerance. Counted because the answer
22+
!! then depends on that cap, and nothing else in the code says so.
23+
integer :: n_hit_cap = 0
24+
real(wp) :: worst_residual = 0._wp !< Largest |f| left behind when the cap was reached
2025

2126
contains
2227

@@ -32,18 +37,27 @@ contains
3237
real(wp), dimension(num_fluids) :: alpha_rho, alpha
3338
#:endif
3439
real(wp) :: rho, gamma, pi_inf, qv_mix
40+
integer :: hit_cap, hit_cap_sum
41+
real(wp) :: resid, resid_max
3542

3643
! Formed here, not one call deeper: CCE OpenACC accepts a num_fluids-sized array passed to a device routine from a
3744
! parallel-loop body, and rejects the same call from inside another acc routine seq.
38-
$:GPU_PARALLEL_LOOP(private='[i, j, k, l, alpha_rho, alpha, rho, gamma, pi_inf, qv_mix]', collapse=3)
45+
hit_cap_sum = 0
46+
resid_max = 0._wp
47+
$:GPU_PARALLEL_LOOP(private='[i, j, k, l, alpha_rho, alpha, rho, gamma, pi_inf, qv_mix, hit_cap, resid]', collapse=3, &
48+
& reduction='[[hit_cap_sum], [resid_max]]', reductionOp='[+, max]')
3949
do l = 0, p
4050
do k = 0, n
4151
do j = 0, m
4252
if (mpp_lim) call s_correct_volume_fractions(q_cons_vf, j, k, l)
4353

54+
hit_cap = 0
55+
resid = 0._wp
4456
if (s_needs_pressure_relaxation(q_cons_vf, j, k, l)) then
45-
call s_equilibrate_pressure(q_cons_vf, j, k, l)
57+
call s_equilibrate_pressure(q_cons_vf, j, k, l, hit_cap, resid)
4658
end if
59+
hit_cap_sum = hit_cap_sum + hit_cap
60+
resid_max = max(resid_max, resid)
4761

4862
$:GPU_LOOP(parallelism='[seq]')
4963
do i = 1, num_fluids
@@ -58,9 +72,22 @@ contains
5872
end do
5973
end do
6074
$:END_GPU_PARALLEL_LOOP()
75+
n_hit_cap = n_hit_cap + hit_cap_sum
76+
worst_residual = max(worst_residual, resid_max)
6177

6278
end subroutine s_pressure_relaxation_procedure
6379

80+
!> One line at the end of a run if the equilibration ever stopped on its iteration cap. Silence means every cell reached the
81+
!! tolerance.
82+
impure subroutine s_report_pressure_relaxation
83+
84+
if (n_hit_cap > 0 .and. proc_rank == 0) then
85+
print '(A,I0,A,ES10.3,A)', ' Pressure relaxation reached its iteration cap in ', n_hit_cap, &
86+
& ' cell updates; worst residual left behind ', worst_residual, ' against a 1e-10 tolerance.'
87+
end if
88+
89+
end subroutine s_report_pressure_relaxation
90+
6491
!> Check if pressure relaxation is needed for this cell
6592
logical function s_needs_pressure_relaxation(q_cons_vf, j, k, l)
6693

@@ -111,12 +138,14 @@ contains
111138
end subroutine s_correct_volume_fractions
112139

113140
!> Main pressure equilibration using Newton-Raphson
114-
subroutine s_equilibrate_pressure(q_cons_vf, j, k, l)
141+
subroutine s_equilibrate_pressure(q_cons_vf, j, k, l, hit_cap, resid)
115142

116143
$:GPU_ROUTINE(parallelism='[seq]')
117144

118145
type(scalar_field), dimension(sys_size), intent(inout) :: q_cons_vf
119146
integer, intent(in) :: j, k, l
147+
integer, intent(out) :: hit_cap
148+
real(wp), intent(out) :: resid
120149
real(wp) :: pres_relax, f_pres, df_pres
121150
#:if not MFC_CASE_OPTIMIZATION and USING_AMD
122151
real(wp), dimension(3) :: pres_K_init, rho_K_init, rho_K_s
@@ -194,6 +223,17 @@ contains
194223
end if
195224
end do
196225

226+
! Written as .not. (<=) so a NaN residual is reported as a miss, like a diverged one.
227+
hit_cap = 0
228+
resid = 0._wp
229+
if (.not. (abs(f_pres) <= TOLERANCE)) then
230+
hit_cap = 1
231+
! A NaN residual has to be mapped, not passed on: max() with a NaN keeps the other operand, so the
232+
! reduction would report the miss as zero.
233+
resid = abs(f_pres)
234+
if (f_pres /= f_pres) resid = huge(1._wp)
235+
end if
236+
197237
! Update volume fractions. The Newton above often stops on the iteration cap rather than on the
198238
! tolerance, and the answer then depends on that cap, so an unconverged-but-physical density is still
199239
! used -- the alternative would move every six-equation result. What is refused is a density that is

src/simulation/m_start_up.fpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ module m_start_up
2222
use m_boundary_io
2323
use m_acoustic_src
2424
use m_rhs
25+
use m_pressure_relaxation, only: s_report_pressure_relaxation
2526
use m_chemistry
2627
use m_data_output
2728
use m_time_steppers
@@ -1108,6 +1109,8 @@ contains
11081109
!> Finalize and deallocate all simulation sub-modules in reverse initialization order
11091110
impure subroutine s_finalize_modules
11101111

1112+
if (model_eqns == model_eqns_6eq) call s_report_pressure_relaxation()
1113+
11111114
call s_finalize_time_steppers_module()
11121115
if (hypoelasticity) call s_finalize_hypoelastic_module()
11131116
call s_finalize_derived_variables_module()

0 commit comments

Comments
 (0)