rdb_efp Module

Order-invariant real-to-integer summation (Hallberg & Adcroft, 2014, An Order-invariant Real-to-Integer Conversion Sum, Parallel Computing 40(5-6), doi:10.1016/j.parco.2014.04.007).

Floating-point addition is not associative, so Sum x_i depends on the order of summation – the reduction tree shape, the rank count, the decomposition. This module maps each real onto a fixed-point integer vector (efp_t) so accumulation becomes plain integer(int64) addition, which IS associative and exact. A base exponent P (EFP_PREC_WIDTH) splits the mantissa into EFP_DIGITS = 6 bins of weight 2**(P*(3-n)) for n = 1..6; the decomposition (efp_decompose) is a pure function of the input value, so two ranks decomposing the same value produce identical bins, and summing bins (efp_plus) is then order-invariant by construction.

This module is host-only, pure-arithmetic, and depends on nothing but rdb_constants (for wp) and iso_fortran_env – no MPI, no grid, no state – so src/comm/ can consume it (halo_allreduce_efp_list) with no dependency cycle, and rdb_console_stats (shared coastal + ocean) can too.

Parameter choice: EFP_PREC_WIDTH = 36 (MOM6 uses 46). Roundabout picks a smaller P because the cross-rank transport (see halo_allreduce_efp_list in src/comm/) has no integer(int64) MPI allreduce available (pic_mpi_lib binds only dp/sp/i32 allreduce overloads) and instead transports the bins as exactly- represented real64 values summed by MPI_SUM – exact only while every partial sum stays within the 53-bit double mantissa. At P = 36 that bounds the rank count to EFP_MAX_RANKS = 2**17 = 131072, far beyond any Roundabout run, while EFP_MAX_SUMMANDS = 2**27 ~= 1.34e8 per local reduction block comfortably covers a single k-slab of an 11500^2 grid. See docs/CAPABILITIES_AND_LIMITATIONS.md for the full bound table.

Do NOT widen EFP_PREC_WIDTH without re-deriving EFP_MAX_RANKS and EFP_MAX_SUMMANDS – both are parameters computed FROM it, and test_efp_bounds pins the arithmetic.

Non-finite propagation. A fixed-point decomposition has nowhere to put a NaN or +-Inf – int(NaN, int64) is compiler-undefined, and the naive fallback (zero the bins, matching a NaN’s “no value” intuition) is exactly the wrong choice for a REDUCTION: it makes a poisoned summand silently vanish, and the reconstructed total comes back as a plausible finite number instead of aborting – the same hazard class as CLAUDE.md’s NaN-blind if/else clamp gotcha, just in the reduction layer. efp_t therefore carries a poison counter alongside its bins (see the type’s own docstring): every entry point (efp_decompose/efp_from_real/efp_plus/efp_minus/efp_to_real/ efp_to_transport/efp_from_transport) propagates it, so a single NaN/+-Inf/bin-1-overflow summand ANYWHERE in an accumulation – local or cross-rank, through halo_allreduce_efp_list – makes efp_to_real return a quiet NaN, identically on every rank, instead of laundering the corruption into “0.000” or a saturated-but-finite value. This was a real regression, not a hypothetical: once &ocean_diag_nml reproducing_sums became the console default (a0755ada1), the in-module device duplicate rdb_ocean_console_stats::efp_decompose_impl had NO non-finite handling at all, so a blown-up run’s console printed finite-looking En/Salt/Temp columns instead of the NaN a diverged run must show – see that module’s efp_decompose_impl docstring and tests/test_efp.F90’s test_efp_poison_propagates_nan/_inf.


Uses

  • module~~rdb_efp~~UsesGraph module~rdb_efp rdb_efp ieee_arithmetic ieee_arithmetic module~rdb_efp->ieee_arithmetic iso_fortran_env iso_fortran_env module~rdb_efp->iso_fortran_env

Used by

  • module~~rdb_efp~~UsedByGraph module~rdb_efp rdb_efp module~rdb_console_stats rdb_console_stats module~rdb_console_stats->module~rdb_efp module~rdb_halo rdb_halo module~rdb_halo->module~rdb_efp module~rdb_decomp rdb_decomp module~rdb_halo->module~rdb_decomp module~rdb_multilayer_state rdb_multilayer_state module~rdb_multilayer_state->module~rdb_efp module~rdb_ocean_console_stats rdb_ocean_console_stats module~rdb_ocean_console_stats->module~rdb_efp module~rdb_ocean_console_stats->module~rdb_console_stats module~rdb_ocean_console_stats->module~rdb_halo module~rdb_ocean_console_stats->module~rdb_multilayer_state module~rdb_ocean_dyn rdb_ocean_dyn module~rdb_ocean_dyn->module~rdb_efp module~rdb_ocean_dyn->module~rdb_multilayer_state module~rdb_ocean_dyn->module~rdb_ocean_console_stats module~rdb_barotropic_coupling rdb_barotropic_coupling module~rdb_ocean_dyn->module~rdb_barotropic_coupling module~rdb_continuity rdb_continuity module~rdb_ocean_dyn->module~rdb_continuity module~rdb_coriolis_adv rdb_coriolis_adv module~rdb_ocean_dyn->module~rdb_coriolis_adv module~rdb_ocean_bottom_drag rdb_ocean_bottom_drag module~rdb_ocean_dyn->module~rdb_ocean_bottom_drag module~rdb_ocean_bt_budget_probe rdb_ocean_bt_budget_probe module~rdb_ocean_dyn->module~rdb_ocean_bt_budget_probe module~rdb_ocean_cavity_flux rdb_ocean_cavity_flux module~rdb_ocean_dyn->module~rdb_ocean_cavity_flux module~rdb_ocean_chksum rdb_ocean_chksum module~rdb_ocean_dyn->module~rdb_ocean_chksum module~rdb_ocean_eos_compute rdb_ocean_eos_compute module~rdb_ocean_dyn->module~rdb_ocean_eos_compute module~rdb_ocean_epbl rdb_ocean_epbl module~rdb_ocean_dyn->module~rdb_ocean_epbl module~rdb_ocean_fold_apply rdb_ocean_fold_apply module~rdb_ocean_dyn->module~rdb_ocean_fold_apply module~rdb_ocean_geothermal rdb_ocean_geothermal module~rdb_ocean_dyn->module~rdb_ocean_geothermal module~rdb_ocean_ghost_poison rdb_ocean_ghost_poison module~rdb_ocean_dyn->module~rdb_ocean_ghost_poison module~rdb_ocean_gm rdb_ocean_gm module~rdb_ocean_dyn->module~rdb_ocean_gm module~rdb_ocean_halo_state rdb_ocean_halo_state module~rdb_ocean_dyn->module~rdb_ocean_halo_state module~rdb_ocean_hdiff_tracer rdb_ocean_hdiff_tracer module~rdb_ocean_dyn->module~rdb_ocean_hdiff_tracer module~rdb_ocean_horizontal_viscosity rdb_ocean_horizontal_viscosity module~rdb_ocean_dyn->module~rdb_ocean_horizontal_viscosity module~rdb_ocean_ideal_age rdb_ocean_ideal_age module~rdb_ocean_dyn->module~rdb_ocean_ideal_age module~rdb_ocean_isopycnal_slopes rdb_ocean_isopycnal_slopes module~rdb_ocean_dyn->module~rdb_ocean_isopycnal_slopes module~rdb_ocean_kappa_shear rdb_ocean_kappa_shear module~rdb_ocean_dyn->module~rdb_ocean_kappa_shear module~rdb_ocean_ke_probe rdb_ocean_ke_probe module~rdb_ocean_dyn->module~rdb_ocean_ke_probe module~rdb_ocean_lateral_mix rdb_ocean_lateral_mix module~rdb_ocean_dyn->module~rdb_ocean_lateral_mix module~rdb_ocean_meke rdb_ocean_meke module~rdb_ocean_dyn->module~rdb_ocean_meke module~rdb_ocean_min_thickness rdb_ocean_min_thickness module~rdb_ocean_dyn->module~rdb_ocean_min_thickness module~rdb_ocean_mle rdb_ocean_mle module~rdb_ocean_dyn->module~rdb_ocean_mle module~rdb_ocean_obc_baroclinic rdb_ocean_obc_baroclinic module~rdb_ocean_dyn->module~rdb_ocean_obc_baroclinic module~rdb_ocean_periodic rdb_ocean_periodic module~rdb_ocean_dyn->module~rdb_ocean_periodic module~rdb_ocean_pressure_force rdb_ocean_pressure_force module~rdb_ocean_dyn->module~rdb_ocean_pressure_force module~rdb_ocean_redi rdb_ocean_redi module~rdb_ocean_dyn->module~rdb_ocean_redi module~rdb_ocean_remap rdb_ocean_remap module~rdb_ocean_dyn->module~rdb_ocean_remap module~rdb_ocean_sponge rdb_ocean_sponge module~rdb_ocean_dyn->module~rdb_ocean_sponge module~rdb_ocean_surface_flux rdb_ocean_surface_flux module~rdb_ocean_dyn->module~rdb_ocean_surface_flux module~rdb_ocean_surface_stress rdb_ocean_surface_stress module~rdb_ocean_dyn->module~rdb_ocean_surface_stress module~rdb_ocean_tidal_mixing rdb_ocean_tidal_mixing module~rdb_ocean_dyn->module~rdb_ocean_tidal_mixing module~rdb_ocean_top_drag rdb_ocean_top_drag module~rdb_ocean_dyn->module~rdb_ocean_top_drag module~rdb_ocean_varmix rdb_ocean_varmix module~rdb_ocean_dyn->module~rdb_ocean_varmix module~rdb_ocean_vdiff rdb_ocean_vdiff module~rdb_ocean_dyn->module~rdb_ocean_vdiff module~rdb_ocean_vertical_advection rdb_ocean_vertical_advection module~rdb_ocean_dyn->module~rdb_ocean_vertical_advection module~rdb_ocean_vmix rdb_ocean_vmix module~rdb_ocean_dyn->module~rdb_ocean_vmix module~rdb_ocean_wave_speed rdb_ocean_wave_speed module~rdb_ocean_dyn->module~rdb_ocean_wave_speed module~rdb_ocean_halo rdb_ocean_halo module~rdb_ocean_dyn->module~rdb_ocean_halo module~rdb_barotropic_substep rdb_barotropic_substep module~rdb_ocean_dyn->module~rdb_barotropic_substep module~rdb_ocean_bt_wide rdb_ocean_bt_wide module~rdb_ocean_dyn->module~rdb_ocean_bt_wide module~rdb_barotropic_coupling->module~rdb_multilayer_state module~rdb_barotropic_coupling->module~rdb_coriolis_adv module~rdb_barotropic_coupling->module~rdb_ocean_bottom_drag module~rdb_barotropic_coupling->module~rdb_ocean_horizontal_viscosity module~rdb_barotropic_coupling->module~rdb_ocean_pressure_force module~rdb_barotropic_coupling->module~rdb_ocean_surface_stress module~rdb_barotropic_coupling->module~rdb_ocean_top_drag module~rdb_continuity->module~rdb_multilayer_state module~rdb_continuity->module~rdb_ocean_fold_apply module~rdb_continuity->module~rdb_ocean_gm module~rdb_continuity->module~rdb_ocean_mle module~rdb_continuity->module~rdb_ocean_periodic module~rdb_continuity->module~rdb_ocean_halo module~rdb_coriolis_adv->module~rdb_multilayer_state module~rdb_driver rdb_driver module~rdb_driver->module~rdb_console_stats module~rdb_driver->module~rdb_halo module~rdb_driver->module~rdb_ocean_console_stats module~rdb_driver->module~rdb_ocean_dyn module~rdb_ocean_engine rdb_ocean_engine module~rdb_driver->module~rdb_ocean_engine module~rdb_ocean_state rdb_ocean_state module~rdb_driver->module~rdb_ocean_state module~rdb_config rdb_config module~rdb_driver->module~rdb_config module~rdb_ice_basal_flux rdb_ice_basal_flux module~rdb_ice_basal_flux->module~rdb_multilayer_state module~rdb_ice_basal_flux->module~rdb_ocean_surface_flux module~rdb_ice_evp rdb_ice_evp module~rdb_ice_evp->module~rdb_multilayer_state module~rdb_ice_evp->module~rdb_ocean_periodic module~rdb_ice_evp->module~rdb_ocean_halo module~rdb_ice_frazil rdb_ice_frazil module~rdb_ice_frazil->module~rdb_multilayer_state module~rdb_ice_frazil->module~rdb_ocean_surface_flux module~rdb_ice_frazil_uptake rdb_ice_frazil_uptake module~rdb_ice_frazil_uptake->module~rdb_multilayer_state module~rdb_ice_init rdb_ice_init module~rdb_ice_init->module~rdb_multilayer_state module~rdb_ice_itd rdb_ice_itd module~rdb_ice_itd->module~rdb_multilayer_state module~rdb_ice_thermo_driver rdb_ice_thermo_driver module~rdb_ice_thermo_driver->module~rdb_multilayer_state module~rdb_ice_transport rdb_ice_transport module~rdb_ice_transport->module~rdb_halo module~rdb_ice_transport->module~rdb_multilayer_state module~rdb_ice_transport->module~rdb_continuity module~rdb_ice_transport->module~rdb_ice_itd module~rdb_ice_transport->module~rdb_ocean_halo_state module~rdb_ice_transport->module~rdb_ocean_halo module~rdb_ocean_api rdb_ocean_api module~rdb_ocean_api->module~rdb_ocean_dyn module~rdb_ocean_api->module~rdb_ocean_engine module~rdb_ocean_api->module~rdb_ocean_eos_compute module~rdb_ocean_api->module~rdb_ocean_fold_apply module~rdb_ocean_api->module~rdb_ocean_periodic module~rdb_ocean_api->module~rdb_config module~rdb_handle rdb_handle module~rdb_ocean_api->module~rdb_handle module~rdb_ocean_diag_derived rdb_ocean_diag_derived module~rdb_ocean_api->module~rdb_ocean_diag_derived module~rdb_ocean_diag_fills rdb_ocean_diag_fills module~rdb_ocean_api->module~rdb_ocean_diag_fills module~rdb_ocean_halo_width rdb_ocean_halo_width module~rdb_ocean_api->module~rdb_ocean_halo_width module~rdb_ocean_bottom_drag->module~rdb_multilayer_state module~rdb_ocean_bt_budget_probe->module~rdb_multilayer_state module~rdb_ocean_bt_budget_probe->module~rdb_coriolis_adv module~rdb_ocean_bt_budget_probe->module~rdb_ocean_bottom_drag module~rdb_ocean_bt_budget_probe->module~rdb_ocean_horizontal_viscosity module~rdb_ocean_bt_budget_probe->module~rdb_ocean_pressure_force module~rdb_ocean_bt_budget_probe->module~rdb_ocean_surface_stress module~rdb_ocean_budgets rdb_ocean_budgets module~rdb_ocean_budgets->module~rdb_multilayer_state module~rdb_ocean_cavity_flux->module~rdb_multilayer_state module~rdb_ocean_cavity_flux->module~rdb_ocean_surface_flux module~rdb_ocean_chksum->module~rdb_halo module~rdb_ocean_chksum->module~rdb_multilayer_state module~rdb_ocean_engine->module~rdb_halo module~rdb_ocean_engine->module~rdb_ocean_dyn module~rdb_ocean_engine->module~rdb_ice_basal_flux module~rdb_ocean_engine->module~rdb_ice_evp module~rdb_ocean_engine->module~rdb_ice_frazil module~rdb_ocean_engine->module~rdb_ice_frazil_uptake module~rdb_ocean_engine->module~rdb_ice_init module~rdb_ocean_engine->module~rdb_ice_itd module~rdb_ocean_engine->module~rdb_ice_thermo_driver module~rdb_ocean_engine->module~rdb_ice_transport module~rdb_ocean_engine->module~rdb_ocean_cavity_flux module~rdb_ocean_engine->module~rdb_ocean_fold_apply module~rdb_ocean_engine->module~rdb_ocean_geothermal module~rdb_ocean_engine->module~rdb_ocean_halo_state module~rdb_ocean_engine->module~rdb_ocean_periodic module~rdb_ocean_setup rdb_ocean_setup module~rdb_ocean_engine->module~rdb_ocean_setup module~rdb_ocean_engine->module~rdb_ocean_sponge module~rdb_ocean_engine->module~rdb_ocean_state module~rdb_ocean_engine->module~rdb_ocean_surface_flux module~rdb_ocean_engine->module~rdb_ocean_surface_stress module~rdb_ocean_engine->module~rdb_config module~rdb_ice_ocean_coupler rdb_ice_ocean_coupler module~rdb_ocean_engine->module~rdb_ice_ocean_coupler module~rdb_ocean_data_forcing rdb_ocean_data_forcing module~rdb_ocean_engine->module~rdb_ocean_data_forcing module~rdb_ocean_engine->module~rdb_ocean_diag_derived module~rdb_ocean_engine->module~rdb_ocean_diag_fills module~rdb_ocean_engine->module~rdb_ocean_halo module~rdb_ocean_engine->module~rdb_decomp module~rdb_ocean_data_input rdb_ocean_data_input module~rdb_ocean_engine->module~rdb_ocean_data_input module~rdb_ocean_stability_audit rdb_ocean_stability_audit module~rdb_ocean_engine->module~rdb_ocean_stability_audit module~rdb_state rdb_state module~rdb_ocean_engine->module~rdb_state module~rdb_ocean_eos_compute->module~rdb_multilayer_state module~rdb_ocean_epbl->module~rdb_multilayer_state module~rdb_ocean_epbl->module~rdb_ocean_surface_flux module~rdb_ocean_epbl->module~rdb_ocean_surface_stress module~rdb_ocean_fold_apply->module~rdb_multilayer_state module~rdb_ocean_geothermal->module~rdb_multilayer_state module~rdb_ocean_geothermal->module~rdb_ocean_surface_flux module~rdb_ocean_ghost_poison->module~rdb_multilayer_state module~rdb_ocean_ghost_poison->module~rdb_ocean_surface_stress module~rdb_ocean_gm->module~rdb_multilayer_state module~rdb_ocean_gm->module~rdb_ocean_isopycnal_slopes module~rdb_ocean_halo_state->module~rdb_multilayer_state module~rdb_ocean_halo_state->module~rdb_ocean_fold_apply module~rdb_ocean_halo_state->module~rdb_ocean_periodic module~rdb_ocean_halo_state->module~rdb_ocean_surface_stress module~rdb_ocean_halo_state->module~rdb_ocean_halo module~rdb_ocean_hdiff_tracer->module~rdb_multilayer_state module~rdb_ocean_horizontal_viscosity->module~rdb_multilayer_state module~rdb_ocean_horizontal_viscosity->module~rdb_ocean_lateral_mix module~rdb_ocean_ideal_age->module~rdb_multilayer_state module~rdb_ocean_isopycnal_slopes->module~rdb_multilayer_state module~rdb_ocean_kappa_shear->module~rdb_multilayer_state module~rdb_ocean_ke_probe->module~rdb_multilayer_state module~rdb_ocean_ke_probe->module~rdb_coriolis_adv module~rdb_ocean_lateral_mix->module~rdb_multilayer_state module~rdb_ocean_meke->module~rdb_multilayer_state module~rdb_ocean_meke->module~rdb_ocean_gm module~rdb_ocean_meke->module~rdb_ocean_varmix module~rdb_ocean_meke->module~rdb_ocean_wave_speed module~rdb_ocean_min_thickness->module~rdb_multilayer_state module~rdb_ocean_mle->module~rdb_multilayer_state module~rdb_ocean_mle->module~rdb_ocean_epbl module~rdb_ocean_mle->module~rdb_ocean_surface_stress module~rdb_ocean_obc_baroclinic->module~rdb_multilayer_state module~rdb_ocean_periodic->module~rdb_multilayer_state module~rdb_ocean_pressure_force->module~rdb_multilayer_state module~rdb_ocean_pseudo_salt rdb_ocean_pseudo_salt module~rdb_ocean_pseudo_salt->module~rdb_multilayer_state module~rdb_ocean_redi->module~rdb_multilayer_state module~rdb_ocean_remap->module~rdb_multilayer_state module~rdb_ocean_setup->module~rdb_halo module~rdb_ocean_setup->module~rdb_ocean_dyn module~rdb_ocean_setup->module~rdb_coriolis_adv module~rdb_ocean_setup->module~rdb_ocean_bottom_drag module~rdb_ocean_setup->module~rdb_ocean_epbl module~rdb_ocean_setup->module~rdb_ocean_fold_apply module~rdb_ocean_setup->module~rdb_ocean_geothermal module~rdb_ocean_setup->module~rdb_ocean_halo_state module~rdb_ocean_setup->module~rdb_ocean_horizontal_viscosity module~rdb_ocean_setup->module~rdb_ocean_lateral_mix module~rdb_ocean_setup->module~rdb_ocean_pressure_force module~rdb_ocean_setup->module~rdb_ocean_sponge module~rdb_ocean_setup->module~rdb_ocean_state module~rdb_ocean_setup->module~rdb_ocean_surface_flux module~rdb_ocean_setup->module~rdb_ocean_surface_stress module~rdb_ocean_setup->module~rdb_ocean_top_drag module~rdb_ocean_setup->module~rdb_ocean_vdiff module~rdb_ocean_setup->module~rdb_ocean_vmix module~rdb_ocean_setup->module~rdb_config module~rdb_ocean_setup->module~rdb_ocean_halo module~rdb_ocean_setup->module~rdb_decomp module~rdb_ocean_sponge->module~rdb_multilayer_state module~rdb_ocean_state->module~rdb_multilayer_state module~rdb_ocean_state->module~rdb_ocean_dyn module~rdb_ocean_state->module~rdb_continuity module~rdb_ocean_state->module~rdb_coriolis_adv module~rdb_ocean_state->module~rdb_ocean_bottom_drag module~rdb_ocean_state->module~rdb_ocean_cavity_flux module~rdb_ocean_state->module~rdb_ocean_epbl module~rdb_ocean_state->module~rdb_ocean_gm module~rdb_ocean_state->module~rdb_ocean_hdiff_tracer module~rdb_ocean_state->module~rdb_ocean_horizontal_viscosity module~rdb_ocean_state->module~rdb_ocean_isopycnal_slopes module~rdb_ocean_state->module~rdb_ocean_kappa_shear module~rdb_ocean_state->module~rdb_ocean_lateral_mix module~rdb_ocean_state->module~rdb_ocean_meke module~rdb_ocean_state->module~rdb_ocean_mle module~rdb_ocean_state->module~rdb_ocean_periodic module~rdb_ocean_state->module~rdb_ocean_pressure_force module~rdb_ocean_state->module~rdb_ocean_pseudo_salt module~rdb_ocean_state->module~rdb_ocean_redi module~rdb_ocean_state->module~rdb_ocean_sponge module~rdb_ocean_state->module~rdb_ocean_surface_flux module~rdb_ocean_state->module~rdb_ocean_surface_stress module~rdb_ocean_state->module~rdb_ocean_tidal_mixing module~rdb_ocean_state->module~rdb_ocean_top_drag module~rdb_ocean_state->module~rdb_ocean_varmix module~rdb_ocean_state->module~rdb_ocean_vdiff module~rdb_ocean_state->module~rdb_ocean_vertical_advection module~rdb_ocean_state->module~rdb_ocean_vmix module~rdb_ocean_state->module~rdb_ocean_wave_speed module~rdb_ocean_z_init rdb_ocean_z_init module~rdb_ocean_state->module~rdb_ocean_z_init module~rdb_ocean_state->module~rdb_config module~rdb_ocean_state->module~rdb_ocean_data_forcing module~rdb_ocean_state->module~rdb_decomp module~rdb_ocean_state->module~rdb_ocean_data_input module~rdb_ocean_surface_flux->module~rdb_multilayer_state module~rdb_ocean_surface_stress->module~rdb_multilayer_state module~rdb_ocean_tidal_mixing->module~rdb_multilayer_state module~rdb_ocean_top_drag->module~rdb_multilayer_state module~rdb_ocean_varmix->module~rdb_multilayer_state module~rdb_ocean_varmix->module~rdb_ocean_isopycnal_slopes module~rdb_ocean_varmix->module~rdb_ocean_wave_speed module~rdb_ocean_vdiff->module~rdb_multilayer_state module~rdb_ocean_vertical_advection->module~rdb_multilayer_state module~rdb_ocean_vmix->module~rdb_multilayer_state module~rdb_ocean_vmix->module~rdb_ocean_surface_flux module~rdb_ocean_vmix->module~rdb_ocean_surface_stress module~rdb_ocean_wave_speed->module~rdb_multilayer_state module~rdb_ocean_z_init->module~rdb_multilayer_state module~rdb_ocean_z_init->module~rdb_config proc~ocean_cavity_mass_step ocean_cavity_mass_step proc~ocean_cavity_mass_step->module~rdb_halo module~rdb_config->module~rdb_ice_init module~rdb_handle->module~rdb_ocean_engine module~rdb_handle->module~rdb_ocean_state module~rdb_handle->module~rdb_config module~rdb_ice_ocean_coupler->module~rdb_ocean_halo_state module~rdb_ice_ocean_coupler->module~rdb_ocean_surface_flux module~rdb_ice_ocean_coupler->module~rdb_ocean_surface_stress module~rdb_ocean_data_forcing->module~rdb_ocean_halo_state module~rdb_ocean_data_forcing->module~rdb_ocean_surface_flux module~rdb_ocean_data_forcing->module~rdb_ocean_surface_stress module~rdb_ocean_data_forcing->module~rdb_config module~rdb_ocean_data_forcing->module~rdb_ocean_data_input module~rdb_ocean_diag_derived->module~rdb_ocean_state module~rdb_ocean_diag_derived->module~rdb_ocean_diag_fills module~rdb_ocean_diag_fills->module~rdb_ocean_pseudo_salt module~rdb_ocean_diag_fills->module~rdb_ocean_state module~rdb_ocean_halo->module~rdb_ocean_periodic module~rdb_ocean_halo->module~rdb_decomp module~rdb_ocean_halo_width->module~rdb_coriolis_adv proc~validate_config validate_config proc~validate_config->module~rdb_coriolis_adv proc~validate_config->module~rdb_ocean_bottom_drag proc~validate_config->module~rdb_ocean_horizontal_viscosity proc~validate_config->module~rdb_ocean_lateral_mix proc~validate_config->module~rdb_ocean_pressure_force proc~validate_config->module~rdb_ocean_pseudo_salt proc~validate_config->module~rdb_ocean_surface_flux proc~validate_config->module~rdb_ocean_tidal_mixing proc~validate_config->module~rdb_ocean_top_drag proc~validate_config->module~rdb_ocean_vmix module~rdb_barotropic_substep->module~rdb_ocean_halo module~rdb_config_schema rdb_config_schema module~rdb_config_schema->module~rdb_config module~rdb_decomp->module~rdb_config module~rdb_ocean_bt_wide->module~rdb_ocean_halo module~rdb_ocean_bt_wide->module~rdb_barotropic_substep module~rdb_ocean_data_input->module~rdb_config module~rdb_ocean_stability_audit->module~rdb_config module~rdb_state->module~rdb_config module~rdb_ocean_fold_exchange rdb_ocean_fold_exchange module~rdb_ocean_fold_exchange->module~rdb_decomp module~rdb_ocean_restart_io rdb_ocean_restart_io module~rdb_ocean_restart_io->module~rdb_decomp

Variables

Type Visibility Attributes Name Initial
integer, public, parameter :: EFP_DIGITS = 6

Number of fixed-point bins per efp_t value.

integer, public, parameter :: EFP_GUARD_WIDTH = 63-EFP_PREC_WIDTH

= 27. int64 has 63 usable bits (sign-magnitude use here, not two’s-complement range); this is the headroom for accumulating multiple summands into one bin before it could overflow.

integer, public, parameter :: EFP_MAX_RANKS = 2**(53-EFP_PREC_WIDTH)

= 131072. Upper bound on the number of MPI ranks the double- precision transport (halo_allreduce_efp_list) can combine exactly: after a local efp_carry, bins 2..6 satisfy |e(n)| < 2**P, so a partial sum over EFP_MAX_RANKS ranks stays <= 2**53, the largest exactly-representable double integer.

integer(kind=int64), public, parameter :: EFP_MAX_SUMMANDS = 2_int64**EFP_GUARD_WIDTH

= 134217728 (~1.34e8). Local (single-rank, single k-slab) upper bound on the number of values that may be accumulated into one bin via efp_plus before an efp_carry is required to keep |bin| < 2**63. Enforced fail-loud at the kernel call site (rdb_ocean_console_stats), not silently.

integer, public, parameter :: EFP_PREC_WIDTH = 36

Bits per bin (P). See the module docstring for the derivation.

integer, public, parameter :: EFP_TRANSPORT_WIDTH = EFP_DIGITS+1

Reals transported per efp_t value by efp_to_transport/ efp_from_transport: the EFP_DIGITS fixed-point bins plus ONE extra slot for the non-finite “poison” counter (efp_t%poison, see its docstring). Callers that size their own send/recv buffers (halo_allreduce_efp_list) MUST use this, not EFP_DIGITS, or the poison slot silently aliases the next value’s bin 1.

real(kind=real64), private, parameter :: EFP_IPR1 = 1.0_real64/EFP_PR1
real(kind=real64), private, parameter :: EFP_IPR2 = 1.0_real64/EFP_PR2
real(kind=real64), private, parameter :: EFP_IPR3 = 1.0_real64/EFP_PR3
real(kind=real64), private, parameter :: EFP_IPR4 = 1.0_real64/EFP_PR4
real(kind=real64), private, parameter :: EFP_IPR5 = 1.0_real64/EFP_PR5
real(kind=real64), private, parameter :: EFP_IPR6 = 1.0_real64/EFP_PR6
real(kind=real64), private, parameter :: EFP_PR1 = 2.0_real64**(2*EFP_PREC_WIDTH)
real(kind=real64), private, parameter :: EFP_PR2 = 2.0_real64**(1*EFP_PREC_WIDTH)
real(kind=real64), private, parameter :: EFP_PR3 = 1.0_real64
real(kind=real64), private, parameter :: EFP_PR4 = 2.0_real64**(-1*EFP_PREC_WIDTH)
real(kind=real64), private, parameter :: EFP_PR5 = 2.0_real64**(-2*EFP_PREC_WIDTH)
real(kind=real64), private, parameter :: EFP_PR6 = 2.0_real64**(-3*EFP_PREC_WIDTH)
integer(kind=int64), private, parameter :: EFP_PREC_I64 = 2_int64**EFP_PREC_WIDTH

2**P as an int64 – the bin-2..6 magnitude bound used by efp_carry / efp_regularize.


Derived Types

type, public ::  efp_t

An order-invariant fixed-point value: Sum_n v(n) * pr(n). The component is PUBLIC (unlike MOM6’s private EFP_type%v) so the comm facade (halo_allreduce_efp_list) can pack/unpack it without an accessor procedure – Roundabout splits the arithmetic module (here) from the collective (in src/comm/), so the layering requires v to be reachable from both. Tests asserting bit- identity compare v(:) directly, never the reconstructed real.

Components

Type Visibility Attributes Name Initial
integer(kind=int64), public :: poison = 0_int64

Non-finite “poison” counter – the number of NaN / +-Inf / bin-1-overflow summands folded into this value so far (0 = clean). efp_decompose/efp_from_real seed it from is_nan .or. is_ovf; efp_plus/efp_minus add it forward (never reset it); efp_to_real returns a quiet NaN whenever it is nonzero, INSTEAD OF reconstructing a value from v(:). This is the fix for the EFP fixed-point path laundering a non-finite summand into a plausible finite number (0 comes out of a NaN decompose’s zeroed bins, a saturated bin 1 comes out of an overflowing one) – the exact hazard class CLAUDE.md’s NaN-blind-clamp gotcha describes, just in the reduction layer instead of a clamp. efp_to_transport/efp_from_transport carry it in the SAME collective as the bins (see EFP_TRANSPORT_WIDTH), summed by the same MPI_SUM: one poisoned rank makes the transported count nonzero on every rank, so every rank’s efp_to_real reports NaN identically – never a rank-dependent branch. A plain count (not a saturating flag) because it costs nothing extra (still an exact double under MPI_SUM at any realistic magnitude) and is simpler to reason about than a boolean OR chain.

integer(kind=int64), public :: v(EFP_DIGITS) = 0_int64

Functions

public pure function efp_bin1_within_transport_bound(a, nranks) result(ok)

Bin 1 is NOT bounded by efp_carry (only bins 2..6 are – see the module docstring), so the double-precision transport needs its own guard: after summing nranks local values, the partial sum in bin 1 must stay <= 2**53 / nranks for the MPI_SUM-on-doubles combine to remain exact (the analogue of MOM6’s prec_error = huge(1_int64) / num_PEs, but for the int64-as- double transport rather than int64 transport). .false. ⇒ the caller must error stop, never silently proceed.

Arguments

Type IntentOptional Attributes Name
type(efp_t), intent(in) :: a
integer, intent(in) :: nranks

Return Value logical

public pure function efp_from_real(r) result(a)

real64 -> efp_t. Wraps efp_decompose; unlike the pre-fix version, the NaN / overflow flags are NOT discarded – they seed a%poison (nonzero iff is_nan .or. is_ovf), so a poisoned summand still taints every later efp_plus/efp_to_real even though the flags themselves aren’t returned here (callers needing the raw flags call efp_decompose directly).

Arguments

Type IntentOptional Attributes Name
real(kind=real64), intent(in) :: r

Return Value type(efp_t)

public pure function efp_minus(a, b) result(c)

Exact bin-wise integer subtraction, regularised. See efp_plus for why regularisation (not mere carry) is required for bit-for-bit order invariance, and for the poison propagation (additive here too – subtraction of a poisoned operand is still poisoned, never “cancels” back to clean).

Arguments

Type IntentOptional Attributes Name
type(efp_t), intent(in) :: a
type(efp_t), intent(in) :: b

Return Value type(efp_t)

public pure function efp_plus(a, b) result(c)

Exact bin-wise integer addition, REGULARISED (not merely carried). Order-invariant BIT-FOR-BIT: efp_plus(a,b)%v == efp_plus(b,a)%v, and a running fold acc = efp_plus(acc, x_i) over any permutation of the x_i converges to the SAME raw bins (not merely the same reconstructed real) – test_efp_order_invariant asserts this on %v(:) directly, per the plan’s §9.2.

Read more…

Arguments

Type IntentOptional Attributes Name
type(efp_t), intent(in) :: a
type(efp_t), intent(in) :: b

Return Value type(efp_t)

public pure function efp_real_diff(a, b) result(r)

real64 = efp_to_real(efp_minus(a, b)) – the difference of two ~1e21-scale EFP totals resolved to the EFP quantum (2**-3P), NOT to ulp(1e21) as a double subtraction would give. This is the fix documented in the plan’s SS2.2: an implementer who converts both operands to real64 FIRST and subtracts loses the whole benefit of this module.

Arguments

Type IntentOptional Attributes Name
type(efp_t), intent(in) :: a
type(efp_t), intent(in) :: b

Return Value real(kind=real64)

public pure function efp_to_real(a) result(r)

efp_t -> real64. Regularises a LOCAL COPY of a (never mutates the argument) – pure with intent(in), per FORTRAN_STYLE.md’s “default new procedures to pure” (MOM6’s EFP_to_real instead mutates its intent(inout) argument).

Read more…

Arguments

Type IntentOptional Attributes Name
type(efp_t), intent(in) :: a

Return Value real(kind=real64)


Subroutines

public pure subroutine efp_carry(e)

Renormalise bins 6..2 into (-2**P, 2**P), propagating the excess into the next-more-significant bin, without changing the represented value. Bin 1 is left untouched (unbounded by construction; see the module docstring on EFP_MAX_RANKS). Mirrors MOM6 carry_overflow, which loops EFP_DIGITS..2 for the same reason.

Arguments

Type IntentOptional Attributes Name
integer(kind=int64), intent(inout) :: e(EFP_DIGITS)

public pure subroutine efp_decompose(r, e, is_nan, is_ovf)

Greedy sign-magnitude fixed-point decomposition of r into six int64 bins of weight pr(n), n = 1..6. Unrolled (no loop, no array indexing over pr/I_pr) so this is a template a !$acc routine seq in-module duplicate can mirror exactly (see rdb_ocean_console_stats::efp_decompose_impl, which pins against this procedure in test_efp_impl_matches_canonical).

Read more…

Arguments

Type IntentOptional Attributes Name
real(kind=real64), intent(in) :: r
integer(kind=int64), intent(out) :: e(EFP_DIGITS)
logical, intent(out) :: is_nan
logical, intent(out) :: is_ovf

public pure subroutine efp_from_transport(buf, list, ok)

Inverse of efp_to_transport: unpack a flat real64(EFP_TRANSPORT_WIDTH*n) buffer (post-collective, still exact integers as doubles) back into efp_t values, converting each bin AND the summed poison counter back to int64 and carrying the bins. ok = .false. iff any unpacked double is not an exact integer (would indicate the transport-exactness bound was violated) – the caller (halo_allreduce_efp_list) turns that into a fail-loud error stop, never a silent truncation.

Arguments

Type IntentOptional Attributes Name
real(kind=real64), intent(in) :: buf(:)
type(efp_t), intent(out) :: list(:)
logical, intent(out) :: ok

public pure subroutine efp_regularize(e)

efp_carry plus: force every bin to share the overall sign, so a single well-conditioned FP accumulation (efp_to_real) can form Sum pr(n)*e(n) without alternating-sign cancellation error. Mirrors MOM6 regularize_ints.

Read more…

Arguments

Type IntentOptional Attributes Name
integer(kind=int64), intent(inout) :: e(EFP_DIGITS)

public pure subroutine efp_to_transport(list, buf)

Pack a list of efp_t values into a flat real64(EFP_TRANSPORT_WIDTH*n) buffer for a single collective (halo_allreduce_efp_list). Each bin, PLUS the poison counter, is transported as an EXACTLY-representable double (see EFP_MAX_RANKS): buf((i-1)*EFP_TRANSPORT_WIDTH + n) = real(list(i)%v(n)) for n = 1..EFP_DIGITS, and buf((i-1)*EFP_TRANSPORT_WIDTH + EFP_DIGITS+1) = real(list(i)%poison). Summing poison through the SAME MPI_SUM collective as the bins is what makes a poisoned rank’s contribution reach every other rank identically – see efp_t’s docstring.

Arguments

Type IntentOptional Attributes Name
type(efp_t), intent(in) :: list(:)
real(kind=real64), intent(out) :: buf(:)