SHOC call-flow map: Fortran (EAM) as the anchor, C++ (EAMxx) identified alongside
The main (green) container is the EAM Fortran flow around and inside the
shoc_main call. Purple boxes on the right identify where each piece lives in EAMxx C++.
Chip colors: green = one-to-one equivalent,
amber = real structural difference.
Line numbers refer to branch maint-3.0-scm-mchinita-shoc_in_and_out_cpp
(code-verified Jul 2026, incl. an independent audit pass).
Fortran / EAM flow
C++ / EAMxx identification
1:1 equivalent
structural difference
shoc_intr.F90 :: shoc_tend_e3sm (host interface, EAM)
PRE-PROCESSING (before the call)
1 · physics_state_copy(state, state1) (:791) — work on a copy of the host state
C++ [D]: no copy — the field manager holds a single instance of each field
2 · build SHOC inputs from state1: thlm (:898), thv (:899), rtm (:893), inv_exner (:879)
C++ [A]: same formulas in SHOCPreprocess::operator() (.hpp:113–114), verified in the audit
3 · tke from state%q(:,:,ixtke) (:901, advected constituent), tk/tkh/cldfrac from pbuf
C++ [A]: tke_copy / qc_copy created here (.hpp:105–106); originals discarded later
4 · host_dx/dy : grid_size(state1) or namelist host_dx_dy_nml in SCM (:861–870)
C++ [B]: in initialize_impl (:449–467) — dx_short geometry if present, else from cell area
5 · SHOC step: dtime = shoc_timestep (:829, <0 → macmic); shoc_num_steps (:855)
C++ [B]: NOT equivalent — run_impl forces nadv = 1 (hdtime := dt, :496–500) and requires dt ≤ 300 s
shoc_main (shoc.F90 :238–784) — nadv-substep loop — C++ [C]
entry dispatch (:482–506): SCREAM/CMake builds call shoc_main_f → the C++ shoc_main
the Fortran body below is the EAM path — this is the literal Fortran↔C++ bridge
shoc_energy_integrals (:516) — energy bookkeeping before the loop
LOOP × nadv substeps (do t = 1, nadv — :521)
check_tke → shoc_grid → compute_shoc_vapor → compute_shoc_temperature
refresh shoc_thv from current state (:560) — the θv-staleness fix lives here
C++ [C]: same fix ported to shoc_main_impl.hpp (:206, :488), commit 63e246b4ee
shoc_diag_obklen → pblintd (surface layer + PBL height)
shoc_compute_lscale (:596) — Larson length-scale diagnostic
C++ [C]: not ported — outputs zeroed on the C++ path (prognostics unaffected)
shoc_length (:607) — mixing length + brunt; receives the refreshed shoc_thv
compute_shoc_mix_terms (:624) — SHOC_MIX decomposition (uses brunt from above)
C++ [C]: not ported — diagnostic only, zeroed on the C++ path
shoc_tke (:631) — TKE prognostic equation (production, dissipation, isotropy)
update_prognostics_implicit (:642) — implicit diffusion of prognostics + tracers
C++ [C]: tke_copy/qc_copy carry the physics; originals only get tracer diffusion
diag_second_shoc_moments → diag_third_shoc_moments (:652, :664)
shoc_assumed_pdf (:673) — cloud/buoyancy closure (wthv_sec, shoc_ql)
check_tke (:684); in-and-out txt writer per substep (:694–721, namelist-gated)
All green steps map 1:1 [C] to same-named C++ functions, each in its own file:
shoc_grid_impl.hpp, shoc_length_impl.hpp, shoc_tke_impl.hpp, ...
(headers because of templates; orchestrated from shoc_main_impl.hpp)
after the loop: update_host_dse → energy_integrals → energy_fixer (:730–740)
fixer only adjusts host_dse — no feedback to prognostics (exp1 ≡ exp2 basis)
final diagnostics: compute_shoc_vapor → shoc_diag_obklen → pblintd (:757–767)
POST-PROCESSING (after the call)
6 · tendencies: ptend = (updated − state1)/hdtime (:1117–1123), ptend_sum (:1146)
C++ [D]: no ptend — fields are updated in place in the field manager
7 · restore work copies into outputs (tke, tk, tkh, cldfrac back to pbuf / state%q)
C++ [A]: SHOCPostprocess (.hpp:284–285) — tke = tke_copy; qc = qc_copy
8 · history: outfld('SHOC_TKE', ...) etc. (:1359)
C++ [D]: no outfld — output streams read fields straight from the field manager
9 · in-and-out only: reader + outer loop (:971–1018; l_turb_standalone, l_shoc_outer_loop)
[A] eamxx_shoc_process_interface.hpp
SHOCPreprocess::operator() (:71)
= PRE steps 2–3 (thlm/thv/rtm; tke_copy, qc_copy)
SHOCPostprocess::operator() (:271)
= POST step 7 (copy-back)
header-only: templates must be visible
[B] eamxx_shoc_process_interface.cpp
set_grids — declares fields (add_field
Required/Updated/Computed ≈ intent in/inout/out);
tke registered in the "tracers" group (:78)
initialize_impl — buffers, npbl, pref_mid, dx/dy
run_impl — the shoc_tend_e3sm equivalent:
preprocess → shoc_main → postprocess; nadv ≡ 1
[C] shoc_main_impl.hpp
Functions<S,D>::shoc_main — same substep
loop and same subroutine order as shoc.F90
two compile-time variants (:67 team-policy,
:343 small-kernels); θv fix in both
temporaries via EKAT WorkspaceManager (:627)
[D] Field manager (SCREAM)
class FieldManager — share/field/field_manager.hpp:
allocates + owns the single copy of every field
SHOC never touches it directly: it declares needs in
set_grids (add_field), then gets direct views via
get_field_in/out(...) in [B]'s .cpp (:264–300)
replaces state/state1 + pbuf + outfld all at once —
why PRE 1 and POST 6/8 have no C++ counterpart
How to read the tags
Each Fortran step carries a purple [A]–[D] tag naming
the C++ box above where that piece lives in EAMxx.
Steps without a tag map 1:1 by subroutine name.