What shoc_in_and_out.cpp does

The standalone C++ driver is the equivalent of the E3SM l_turb_standalone path: it feeds the same inputs to the same shoc_main, writes the same .txt columns, and skips the host machinery. Line numbers on branch maint-3.0-scm-mchinita-shoc_in_and_out_cpp.

Driver shoc_in_and_out.cpp vs E3SM with l_turb_standalone In E3SM the inputs are built from state1 first (:898) and only then overwritten by the IC files (:986); both write the same shoc_output columns; both run in exp1/exp2 modes. The driver loads the ICs directly and equals the E3SM path. The frozen-host-state integration is also what surfaced the theta-v staleness bug. Driver ≈ the l_turb_standalone path (same inputs, same shoc_main, same .txt columns) Real E3SM · l_turb_standalone = .true. shoc_intr.F90 (full host interface) 1st build inputs from state1 (:898–899) thv, thlm, qw from state1%t, state1%q 2nd standalone ONLY: OVERWRITE (:986) read_and_set_input_to_shoc_main (sub :1448) thv,thlm,qw,tke,u,v + state1 pressures ← ICs 3rd LOOP shoc_main × nadv (:1034, shoc.F90) ← SHARED physics, identical → host machinery (present, but...): ptend · energy fixer → host_dse only · outfld standalone: does NOT feed back to SHOC prognostics → irrelevant Output: written by shoc_intr, after each call (:1064) shoc_output_nadv<N>_..._nstep<S>.txt (:999) Net effect: the 1st-step build is discarded by the 2nd-step overwrite → what reaches shoc_main = ICs. Driver · shoc_in_and_out.cpp replaces shoc_intr with minimal glue load ready-made inputs from IC (:193) thv, thlm, qw, tke read straight from file no build step, no overwrite the ICs already ARE the inputs — a single step LOOP shoc_main × nadv (:317, shoc.F90) ← SAME call, same file → host machinery: — OMITTED — no ptend, energy-fixer, outfld, state_copy (unnecessary in standalone) Output: write_state → .txt directly (:223) <prefix>_<engine>_<mode>.txt (:304) Same columns as E3SM (u,v,tke,qv,qc,T,P per level & time) → the two diff directly (compare_inout.py). Two run modes — available in BOTH the driver and real E3SM exp2 · OUTER loop: nadv = 1, shoc_main called N times → output after EACH call (driver write_state :318 · E3SM shoc_intr :1064) exp1 · INNER loop: nadv = N, shoc_main called once → E3SM writes per substep from inside shoc_main (shoc.F90 :708) exp1 ≡ exp2 bitwise: the call count changes only the output granularity, not the result. → Net result: driver (cxx and f90) ≡ E3SM l_turb_standalone run to ~1e-6 (compiler noise). shoc_main (shoc.F90) is called in full and unchanged on both sides — the physics is 100% identical; only the surrounding glue differs. A second thing this framework surfaced — the θv-staleness bug: by integrating SHOC standalone with a frozen host state, the in-and-out framework amplified the θv-staleness into a visible artifact — which is how the bug was identified.

Companion to the in-and-out C++ validation and the SHOC call-flow map. Driver source: components/eamxx/src/physics/shoc/tests/shoc_in_and_out.cpp; host interface: components/eam/src/physics/cam/shoc_intr.F90; physics: shoc.F90. Verified against the code (Jul 2026).