s_save_data in src/post_process/m_start_up.fpp (lines 169–747, ~580 lines) writes every output variable with the same repeated scaffolding. The call s_write_variable_to_formatted_database_file(varname, t_step) appears 38 times, and nearly every one is wrapped in the identical "fill → name → write → clear" stanza:
q_sf(:,:,:) = q_cons_vf(i)%sf(x_beg:x_end, y_beg:y_end, z_beg:z_end) ! fill the module scratch field
write (varname, '(A,I0)') 'alpha_rho', i ! build the name
call s_write_variable_to_formatted_database_file(varname, t_step) ! write it
varname(:) = ' ' ! clear the name buffer
That 4-line pattern repeats for densities, momenta, velocities, energies, species, stresses, B-fields, vorticity, etc. Two things make it worth tidying:
- the
q_sf(:,:,:) = …(x_beg:x_end, y_beg:y_end, z_beg:z_end) slice and the varname(:) = ' ' clear are identical every time and pure boilerplate;
- the
varname(:) = ' ' clear is easy to forget when adding a new output variable (a missed clear leaves stale characters in the name buffer).
Proposed helper
q_sf is a module-global scratch field (it's how s_write_variable_to_formatted_database_file receives the data — it reads q_sf directly, not via an argument). So a small private helper in m_start_up can wrap the common tail, with an optional source that handles the dominant "slice a scalar_field" case:
!> Fill q_sf from `src` (if given) over the interior, write it as `varname`, and clear varname.
!! @param varname field name (set by caller); blanked on return
!! @param t_step current time step
!! @param src optional scalar_field to slice into q_sf; omit if q_sf is already filled
subroutine s_write_field(varname, t_step, src)
character(LEN=name_len), intent(inout) :: varname
integer, intent(in) :: t_step
type(scalar_field), intent(in), optional :: src
if (present(src)) then
q_sf(:,:,:) = src%sf(x_beg:x_end, y_beg:y_end, z_beg:z_end)
end if
call s_write_variable_to_formatted_database_file(varname, t_step)
varname(:) = ' '
end subroutine s_write_field
Before → after
Common case (a scalar_field slice — the majority of the 38 sites), e.g. the partial densities:
! before (4 lines)
q_sf(:,:,:) = q_cons_vf(i)%sf(x_beg:x_end, y_beg:y_end, z_beg:z_end)
write (varname, '(A,I0)') 'alpha_rho', i
call s_write_variable_to_formatted_database_file(varname, t_step)
varname(:) = ' '
! after (2 lines)
write (varname, '(A,I0)') 'alpha_rho', i
call s_write_field(varname, t_step, q_cons_vf(i))
Derive-filled case (q_sf is filled in place by an s_derive_* call — ~6 sites such as vorticity, schlieren, flux limiter, specific-heat-ratio): just omit src:
! before
call s_derive_vorticity_component(i, q_prim_vf, q_sf)
write (varname, '(A,I0)') 'omega', i
call s_write_variable_to_formatted_database_file(varname, t_step)
varname(:) = ' '
! after
call s_derive_vorticity_component(i, q_prim_vf, q_sf)
write (varname, '(A,I0)') 'omega', i
call s_write_field(varname, t_step)
Variations to keep at the call site (don't over-unify)
- Name building stays at the call site. It is genuinely variable — single names (
write(varname,'(A)') 'rho'), indexed names ('(A,I0)') 'alpha_rho', i), and a few multi-branch if/else ladders (e.g. picking Bx/By/Bz). Don't try to fold name logic into the helper.
- Irregular fills keep their explicit
q_sf = … and use the no-src form. A handful of sources are not a plain scalar_field slice: rho_sf (a bare 3D array), q_T_sf%sf, and the real(ib_markers%sf(…), wp) cast for IB markers. For those, leave the explicit q_sf(:,:,:) = … line and call s_write_field(varname, t_step) — you still drop the varname(:) = ' ' clear and unify the write call.
- Guards stay put. The surrounding
if (… _wrt …) / cons_vars_wrt/prim_vars_wrt conditions are unchanged.
Net effect: ~38 four-line stanzas become two- or three-line calls, removing the duplicated slice expression and the forgettable clear, while the write order (which the golden output depends on) is untouched.
Golden-file safety & verification
Output-neutral by construction: the helper performs the exact same slice assignment, the same writer call, and the same clear, in the same order — so the database files are bit-identical. This is still output code, so verify with the post_process golden suite after the change: e.g. ./mfc.sh test -j 8 (or --only the relevant 1D/2D/3D feature tests), which exercise these write paths for the common variables, MHD (Bx/By/Bz), elasticity (stresses), species, vorticity, etc.
Scope / placement
- Put
s_write_field as a module-private routine in m_start_up (next to s_save_data, its only caller), or co-locate it in m_data_output beside s_write_variable_to_formatted_database_file. Either works since q_sf is module-global; the m_start_up-private option has the smallest blast radius.
- post_process is CPU-only, so no GPU/
GPU_* considerations.
Follow-on to #1520 (which lifts the bubble-moment block out of this same routine); the two can be done independently or together.
Filed from a repo-wide code-cleanliness review; verified against master @ 40dde5e.
Code references
s_save_datainsrc/post_process/m_start_up.fpp(lines 169–747, ~580 lines) writes every output variable with the same repeated scaffolding. The calls_write_variable_to_formatted_database_file(varname, t_step)appears 38 times, and nearly every one is wrapped in the identical "fill → name → write → clear" stanza:That 4-line pattern repeats for densities, momenta, velocities, energies, species, stresses, B-fields, vorticity, etc. Two things make it worth tidying:
q_sf(:,:,:) = …(x_beg:x_end, y_beg:y_end, z_beg:z_end)slice and thevarname(:) = ' 'clear are identical every time and pure boilerplate;varname(:) = ' 'clear is easy to forget when adding a new output variable (a missed clear leaves stale characters in the name buffer).Proposed helper
q_sfis a module-global scratch field (it's hows_write_variable_to_formatted_database_filereceives the data — it readsq_sfdirectly, not via an argument). So a small private helper inm_start_upcan wrap the common tail, with an optional source that handles the dominant "slice ascalar_field" case:Before → after
Common case (a
scalar_fieldslice — the majority of the 38 sites), e.g. the partial densities:Derive-filled case (q_sf is filled in place by an
s_derive_*call — ~6 sites such as vorticity, schlieren, flux limiter, specific-heat-ratio): just omitsrc:Variations to keep at the call site (don't over-unify)
write(varname,'(A)') 'rho'), indexed names ('(A,I0)') 'alpha_rho', i), and a few multi-branchif/elseladders (e.g. pickingBx/By/Bz). Don't try to fold name logic into the helper.q_sf = …and use the no-srcform. A handful of sources are not a plainscalar_fieldslice:rho_sf(a bare 3D array),q_T_sf%sf, and thereal(ib_markers%sf(…), wp)cast for IB markers. For those, leave the explicitq_sf(:,:,:) = …line and calls_write_field(varname, t_step)— you still drop thevarname(:) = ' 'clear and unify the write call.if (… _wrt …)/cons_vars_wrt/prim_vars_wrtconditions are unchanged.Net effect: ~38 four-line stanzas become two- or three-line calls, removing the duplicated slice expression and the forgettable clear, while the write order (which the golden output depends on) is untouched.
Golden-file safety & verification
Output-neutral by construction: the helper performs the exact same slice assignment, the same writer call, and the same clear, in the same order — so the database files are bit-identical. This is still output code, so verify with the post_process golden suite after the change: e.g.
./mfc.sh test -j 8(or--onlythe relevant 1D/2D/3D feature tests), which exercise these write paths for the common variables, MHD (Bx/By/Bz), elasticity (stresses), species, vorticity, etc.Scope / placement
s_write_fieldas a module-private routine inm_start_up(next tos_save_data, its only caller), or co-locate it inm_data_outputbesides_write_variable_to_formatted_database_file. Either works sinceq_sfis module-global; them_start_up-private option has the smallest blast radius.GPU_*considerations.Follow-on to #1520 (which lifts the bubble-moment block out of this same routine); the two can be done independently or together.
Filed from a repo-wide code-cleanliness review; verified against
master@40dde5e.Code references
src/post_process/m_start_up.fpp:228-240— representative fill→name→write→clear stanza (alpha_rho)src/post_process/m_start_up.fpp:169-747—s_save_data(38 write calls)src/post_process/m_data_output.fpp:578-708—s_write_variable_to_formatted_database_file(reads module-globalq_sf)src/post_process/m_start_up.fpp:310— derive-filled variant (nosrcneeded)