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.

Companion to the SHOC host-interface audit, which has the step-by-step location tables and the term-by-term formula checks behind every chip on this page. All line numbers and mapping claims verified against components/eam/src/physics/cam/shoc_intr.F90, shoc.F90 and components/eamxx/src/physics/shoc/ on maint-3.0-scm-mchinita-shoc_in_and_out_cpp, including an independent audit pass (Jul 2026).