Repository navigation
feat(output): CF time with bounds; clearer names, labels and defaults - #58
Merged
Merged
Conversation
…writes The cohort order of a month's records. The stand is re-sorted every slow step (daily growth and mortality), but the output folded cohort slabs by slot, so a monthly cohort record averaged two cohorts in one slot after a mid-month swap, and staged fast-loop rows were reduced with the post-sort weights. Every cohort value of a month is now folded into its cohort's slot of the month (keyed by global_cohort_id: the live state, the diagnostic rows and the staged rows, whose ids and weights the fast loop stages once per step), and every record of the month is written in the month's end order (write_order, before the I/O phase). The axes' bookkeeping is also written on the F tier, read from the stand. No overwrites. A run lists every diagnostic file it would write and stops before its first step if one exists; the writer creates files with NC_NOCLOBBER. RunConfig.remove_diagnostics() removes a configuration's files; the examples' scripts and calibrate_fast's trials call it before they run again. file_chunk honoured. A tier with a cohort or patch variable was cut to monthly files (F: daily) whatever its file_chunk said. The cohort and patch axes are now unlimited and grow within a file; each record names its cohorts and patches. "run" is renamed "all"; a monthly chunk of a day and an annual one shorter than a year are refused. Model state is bit for bit (ifx and gfortran dev builds, two runs). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ion, dew Four polygon outputs split ET by where the vapour comes from, as the canopy air's vapour ledger booked it on each step: - mean_transp_py transpiration - mean_evap_canopy_py evaporation of the intercepted water (leaf and wood films) - mean_evap_ground_py ground evaporation: bare soil and snow pack - mean_dew_py condensate (dew, fog) sent to soil layer 1 ET = transp + evap_canopy + evap_ground - dew - the canopy air's vapour gain. Default tiers D M Y, F on request (they are patch-block fast rows). The films' evaporation is b-weighted into the step's boundary-flux amounts (stage_bflux_t / boundary_flux_t, both integrators); close_ledgers exports the four amounts in step_exports_t, and patch_diag_row turns them into rates. PA_TRANSP held le_flux / L_v (ET) under a transpiration label and no output read it; it now holds transpiration. The patch rows after it move up by three. test_fast_integ_step: on both schemes, each step's sources less the dew less ET equal the canopy air's net vapour inflow (worst 1.5e-16 ESDIRK2, 2.1e-16 RK45, relative), the morning rain's films evaporate, and the day's canopy and ground evaporation agree between ESDIRK2 and RK45. Bit for bit: example 04's calibrated BCI run on the production ifx build matches the archived v0.4.0 run in all 24 variables both write (43,824 hourly and 1,826 daily records). ctest 95/95 on ifx and gfortran dev builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…-daily values everywhere Every term the whole-column water and energy ledgers book is now on the patch row and an output variable, so both budgets close from the files on any tier (docs/science/diagnostics.md §4, The budgets): shed water, film excess, store gain and the water's paths and stores; bottom heat, advected enthalpy, the stores' energy gains and H by source. Adds Ra, the respiration split and Rh on the patch row, ecosystem carbon, absorbed PAR, the leaves' wet fraction and patch ET. ET and runoff are read from the fast loop's exports (the polygon accumulators are gone), and the formerly sub-daily-only variables get coarse sources, so every fast-loop quantity is on F and on D/M/Y. A per-cohort row reduced to the patch or polygon is reduced on F from the staged rows. New pytest output_budgets closes both budgets from a run's D and F files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…y record
Every mean_<x> with a sub-daily value has a diel_<x>: its mean at each time of day over the
record, in bins of the new [output].diel_interval ("1 hour" to "4 hours"), on a diel axis (the
last) with the coordinate diel_hour and its bounds. Each staged sub-step is folded into its bin
of the coarse tiers' open records as the F tier reads it, before the step closes them; a cohort's
cycle goes to its slot of the month. D, M and Y (D and M on the cohort and patch axes), never F;
off by default, so the output is bit for bit. Needs the fast loop and a dt_fast that divides the
bin. MAX_OUTPUT_VARS rises to 1024 for the ~150 new names.
New pytest output_diel: a daily record is, bin by bin, its day's sub-daily records, on the
polygon, patch, soil and cohort axes, at 1- and 3-hour bins; a diel_ variable on F is refused.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
time is now each record's start in days since the run's start (proleptic Gregorian, standard_name time), with time_bnds: the start and the end of the record's last step, so a partial record carries its true span and xarray and CDO decode the axis. The year/month/day companions stay. mean_transp_leaf_py -> mean_leaf_transp_py (ecophys, off by default) and mean_transp_co -> mean_leaf_transp_co, so the per-leaf-area transpiration has one name at every level apart from mean_transp_py. mean_abs_par_co's long name says it is the incident-equivalent PAR; mean_cas_co2_pa joins its polygon twin in the energy group; mean_soil_temp_top_pa is on D by default like its siblings; diagnostics.md §7 no longer promises an albedo. ex05's plot script and README read the new time axis. Model state is bit for bit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Debug build at the top of the stack (this branch, ifx, runtime checks on): builds clean, ctest 96/96 passed (the Debug build registers 96 of the dev builds' 98 tests). 🤖 Generated with Claude Code |
A run still stops before its first step when a file it would write exists, unless the new [output].overwrite = true (default false) lets it replace them (NC_CLOBBER, no start-up check). The examples' configs and the Python scripts set it, so rerunning an example replaces its own files; RunConfig.remove_diagnostics() and the scripts' clean-up helpers are gone. The output_files test reruns with overwrite on and checks every file is written again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xiangtaoxu
commented
Oct 30, 2026
| ! Rn + bottom heat - H - LE + advected enthalpy - store gains = the energy residual. | ||
| !---~--- | ||
| integer(ik), parameter, public :: PA_HEAT_BOTTOM = 23_ik ! [W/m2] heat into the soil column through its bottom | ||
| integer(ik), parameter, public :: PA_ENTH_ADVECT = 24_ik ! [W/m2] enthalpy the water carries in, beyond H and LE |
Contributor
Author
There was a problem hiding this comment.
What water flux does this term cover?
| integer(ik), parameter, public :: PA_LEAF_RESP = 33_ik ! [umol CO2/m2/s] leaf dark respiration | ||
| integer(ik), parameter, public :: PA_STEM_RESP = 34_ik ! [umol CO2/m2/s] stem maintenance respiration | ||
| integer(ik), parameter, public :: PA_ROOT_RESP = 35_ik ! [umol CO2/m2/s] fine-root maintenance respiration | ||
| integer(ik), parameter, public :: PA_RH = 36_ik ! [umol CO2/m2/s] heterotrophic respiration |
Contributor
Author
There was a problem hiding this comment.
This can be mistaken as relative humidity. I suggest use RHet and for RA use RAut
| integer(ik), parameter, public :: CO_LEAF_PAR = 27_ik ! [umol photon/m2 leaf/s] incident-equivalent PAR per leaf area | ||
| integer(ik), parameter, public :: CO_GB_MOL = 28_ik ! [mol H2O/m2 leaf/s] boundary-layer conductance | ||
| integer(ik), parameter, public :: CO_PSI_PREDAWN = 29_ik ! [MPa] the stomatal limb's predawn leaf potential | ||
| integer(ik), parameter, public :: CO_CA = 30_ik ! [umol/mol] canopy-air CO2 the leaf sees |
Contributor
Author
There was a problem hiding this comment.
Is this the leaf surface boundary layer CO2?
| integer(ik), parameter, public :: CO_GB_MOL = 28_ik ! [mol H2O/m2 leaf/s] boundary-layer conductance | ||
| integer(ik), parameter, public :: CO_PSI_PREDAWN = 29_ik ! [MPa] the stomatal limb's predawn leaf potential | ||
| integer(ik), parameter, public :: CO_CA = 30_ik ! [umol/mol] canopy-air CO2 the leaf sees | ||
| integer(ik), parameter, public :: CO_PRESSURE = 31_ik ! [Pa] air pressure the leaf sees |
Contributor
Author
There was a problem hiding this comment.
Why do we need to output this variable, isn't it the same as CAS pressure?
| integer(ik), parameter, public :: PA_SNOWMELT = 48_ik ! [kg/m2/s] meltwater the snow pack drains to the pond | ||
| integer(ik), parameter, public :: PA_SUBLIMATION = 49_ik ! [kg/m2/s] snow pack -> canopy air (deposition < 0) | ||
| integer(ik), parameter, public :: PA_SHED_WATER = 50_ik ! [kg/m2/s] tissue water the slow step shed to the pond | ||
| integer(ik), parameter, public :: PA_FILM_EXCESS = 51_ik ! [kg/m2/s] film water above capacity, removed |
Contributor
Author
There was a problem hiding this comment.
I recommend avoid film, use veg_surface_water or something similar instead
…s enthalpy split by direction Review of EDmodel#58: "film" and "canopy" read unclearly, and "rh"/"ra" read as relative humidity. - mean_canopy_water_py -> mean_veg_surface_water_py, mean_evap_canopy_py -> mean_veg_surface_evap_py, mean_film_excess_py -> mean_veg_surface_overflow_py, mean_evap_ground_py -> mean_ground_evap_py, mean_ra_py -> mean_raut_py; the patch rows and exports this PR added follow (PA_RHET, PA_RAUT, ...). - mean_enth_advect_py ("advected" suggests horizontal transport) is split by direction: mean_precip_enth_py (the rain and snow's in) and mean_outflow_enth_py (runoff, drainage, overflow and the exported vapour's beyond LE, out). The budget test closes on the pair. Model state is bit for bit with the previous head. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Merged
3 of 10 tasks
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…CO2 and pressure removed Review of EDmodel#58: - mean_rh_py/_pa -> mean_rhet_py/_pa (FLD_P_RH -> FLD_P_RHET): "rh" read as relative humidity. - mean_ca_co and mean_pressure_co are removed with their rows (CO_CA, CO_PRESSURE): they repeated, for every cohort, the patch's canopy-air CO2 at the sub-step's start and the forcing's air pressure. Only edmore.canopy's docstring named them; it now points the offline leaf replay at mean_cas_co2_pa (the previous sub-step's) and mean_psurf_py. 29 fast cohort rows, 39 in all. Model state is bit for bit; mean_rhet_* equals the old mean_rh_* record for record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 30, 2026
xiangtaoxu
added a commit
that referenced
this pull request
Oct 30, 2026
…-daily values everywhere (#56) * fix(output): one cohort order per month, file_chunk honoured, no overwrites The cohort order of a month's records. The stand is re-sorted every slow step (daily growth and mortality), but the output folded cohort slabs by slot, so a monthly cohort record averaged two cohorts in one slot after a mid-month swap, and staged fast-loop rows were reduced with the post-sort weights. Every cohort value of a month is now folded into its cohort's slot of the month (keyed by global_cohort_id: the live state, the diagnostic rows and the staged rows, whose ids and weights the fast loop stages once per step), and every record of the month is written in the month's end order (write_order, before the I/O phase). The axes' bookkeeping is also written on the F tier, read from the stand. No overwrites. A run lists every diagnostic file it would write and stops before its first step if one exists; the writer creates files with NC_NOCLOBBER. RunConfig.remove_diagnostics() removes a configuration's files; the examples' scripts and calibrate_fast's trials call it before they run again. file_chunk honoured. A tier with a cohort or patch variable was cut to monthly files (F: daily) whatever its file_chunk said. The cohort and patch axes are now unlimited and grow within a file; each record names its cohorts and patches. "run" is renamed "all"; a monthly chunk of a day and an annual one shorter than a year are refused. Model state is bit for bit (ifx and gfortran dev builds, two runs). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): cite #55 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): ET's sources: transpiration, canopy and ground evaporation, dew Four polygon outputs split ET by where the vapour comes from, as the canopy air's vapour ledger booked it on each step: - mean_transp_py transpiration - mean_evap_canopy_py evaporation of the intercepted water (leaf and wood films) - mean_evap_ground_py ground evaporation: bare soil and snow pack - mean_dew_py condensate (dew, fog) sent to soil layer 1 ET = transp + evap_canopy + evap_ground - dew - the canopy air's vapour gain. Default tiers D M Y, F on request (they are patch-block fast rows). The films' evaporation is b-weighted into the step's boundary-flux amounts (stage_bflux_t / boundary_flux_t, both integrators); close_ledgers exports the four amounts in step_exports_t, and patch_diag_row turns them into rates. PA_TRANSP held le_flux / L_v (ET) under a transpiration label and no output read it; it now holds transpiration. The patch rows after it move up by three. test_fast_integ_step: on both schemes, each step's sources less the dew less ET equal the canopy air's net vapour inflow (worst 1.5e-16 ESDIRK2, 2.1e-16 RK45, relative), the morning rain's films evaporate, and the day's canopy and ground evaporation agree between ESDIRK2 and RK45. Bit for bit: example 04's calibrated BCI run on the production ifx build matches the archived v0.4.0 run in all 24 variables both write (43,824 hourly and 1,826 daily records). ctest 95/95 on ifx and gfortran dev builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): the water and energy budgets close from the output; sub-daily values everywhere Every term the whole-column water and energy ledgers book is now on the patch row and an output variable, so both budgets close from the files on any tier (docs/science/diagnostics.md §4, The budgets): shed water, film excess, store gain and the water's paths and stores; bottom heat, advected enthalpy, the stores' energy gains and H by source. Adds Ra, the respiration split and Rh on the patch row, ecosystem carbon, absorbed PAR, the leaves' wet fraction and patch ET. ET and runoff are read from the fast loop's exports (the polygon accumulators are gone), and the formerly sub-daily-only variables get coarse sources, so every fast-loop quantity is on F and on D/M/Y. A per-cohort row reduced to the patch or polygon is reduced on F from the staged rows. New pytest output_budgets closes both budgets from a run's D and F files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): the default files' new variables, an upgrading note Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): cite #56 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): [output].overwrite, an opt-in to replace existing output A run still stops before its first step when a file it would write exists, unless the new [output].overwrite = true (default false) lets it replace them (NC_CLOBBER, no start-up check). The examples' configs and the Python scripts set it, so rerunning an example replaces its own files; RunConfig.remove_diagnostics() and the scripts' clean-up helpers are gone. The output_files test reruns with overwrite on and checks every file is written again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(output): veg_surface, ground_evap and raut names; the water's enthalpy split by direction Review of #58: "film" and "canopy" read unclearly, and "rh"/"ra" read as relative humidity. - mean_canopy_water_py -> mean_veg_surface_water_py, mean_evap_canopy_py -> mean_veg_surface_evap_py, mean_film_excess_py -> mean_veg_surface_overflow_py, mean_evap_ground_py -> mean_ground_evap_py, mean_ra_py -> mean_raut_py; the patch rows and exports this PR added follow (PA_RHET, PA_RAUT, ...). - mean_enth_advect_py ("advected" suggests horizontal transport) is split by direction: mean_precip_enth_py (the rain and snow's in) and mean_outflow_enth_py (runoff, drainage, overflow and the exported vapour's beyond LE, out). The budget test closes on the pair. Model state is bit for bit with the previous head. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
xiangtaoxu
added a commit
that referenced
this pull request
Oct 30, 2026
…y record (#57) * fix(output): one cohort order per month, file_chunk honoured, no overwrites The cohort order of a month's records. The stand is re-sorted every slow step (daily growth and mortality), but the output folded cohort slabs by slot, so a monthly cohort record averaged two cohorts in one slot after a mid-month swap, and staged fast-loop rows were reduced with the post-sort weights. Every cohort value of a month is now folded into its cohort's slot of the month (keyed by global_cohort_id: the live state, the diagnostic rows and the staged rows, whose ids and weights the fast loop stages once per step), and every record of the month is written in the month's end order (write_order, before the I/O phase). The axes' bookkeeping is also written on the F tier, read from the stand. No overwrites. A run lists every diagnostic file it would write and stops before its first step if one exists; the writer creates files with NC_NOCLOBBER. RunConfig.remove_diagnostics() removes a configuration's files; the examples' scripts and calibrate_fast's trials call it before they run again. file_chunk honoured. A tier with a cohort or patch variable was cut to monthly files (F: daily) whatever its file_chunk said. The cohort and patch axes are now unlimited and grow within a file; each record names its cohorts and patches. "run" is renamed "all"; a monthly chunk of a day and an annual one shorter than a year are refused. Model state is bit for bit (ifx and gfortran dev builds, two runs). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): cite #55 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): ET's sources: transpiration, canopy and ground evaporation, dew Four polygon outputs split ET by where the vapour comes from, as the canopy air's vapour ledger booked it on each step: - mean_transp_py transpiration - mean_evap_canopy_py evaporation of the intercepted water (leaf and wood films) - mean_evap_ground_py ground evaporation: bare soil and snow pack - mean_dew_py condensate (dew, fog) sent to soil layer 1 ET = transp + evap_canopy + evap_ground - dew - the canopy air's vapour gain. Default tiers D M Y, F on request (they are patch-block fast rows). The films' evaporation is b-weighted into the step's boundary-flux amounts (stage_bflux_t / boundary_flux_t, both integrators); close_ledgers exports the four amounts in step_exports_t, and patch_diag_row turns them into rates. PA_TRANSP held le_flux / L_v (ET) under a transpiration label and no output read it; it now holds transpiration. The patch rows after it move up by three. test_fast_integ_step: on both schemes, each step's sources less the dew less ET equal the canopy air's net vapour inflow (worst 1.5e-16 ESDIRK2, 2.1e-16 RK45, relative), the morning rain's films evaporate, and the day's canopy and ground evaporation agree between ESDIRK2 and RK45. Bit for bit: example 04's calibrated BCI run on the production ifx build matches the archived v0.4.0 run in all 24 variables both write (43,824 hourly and 1,826 daily records). ctest 95/95 on ifx and gfortran dev builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): the water and energy budgets close from the output; sub-daily values everywhere Every term the whole-column water and energy ledgers book is now on the patch row and an output variable, so both budgets close from the files on any tier (docs/science/diagnostics.md §4, The budgets): shed water, film excess, store gain and the water's paths and stores; bottom heat, advected enthalpy, the stores' energy gains and H by source. Adds Ra, the respiration split and Rh on the patch row, ecosystem carbon, absorbed PAR, the leaves' wet fraction and patch ET. ET and runoff are read from the fast loop's exports (the polygon accumulators are gone), and the formerly sub-daily-only variables get coarse sources, so every fast-loop quantity is on F and on D/M/Y. A per-cohort row reduced to the patch or polygon is reduced on F from the staged rows. New pytest output_budgets closes both budgets from a run's D and F files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): the default files' new variables, an upgrading note Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): cite #56 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): diel_, the mean diel cycle of a daily, monthly or yearly record Every mean_<x> with a sub-daily value has a diel_<x>: its mean at each time of day over the record, in bins of the new [output].diel_interval ("1 hour" to "4 hours"), on a diel axis (the last) with the coordinate diel_hour and its bounds. Each staged sub-step is folded into its bin of the coarse tiers' open records as the F tier reads it, before the step closes them; a cohort's cycle goes to its slot of the month. D, M and Y (D and M on the cohort and patch axes), never F; off by default, so the output is bit for bit. Needs the fast loop and a dt_fast that divides the bin. MAX_OUTPUT_VARS rises to 1024 for the ~150 new names. New pytest output_diel: a daily record is, bin by bin, its day's sub-daily records, on the polygon, patch, soil and cohort axes, at 1- and 3-hour bins; a diel_ variable on F is refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): cite #57 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): [output].overwrite, an opt-in to replace existing output A run still stops before its first step when a file it would write exists, unless the new [output].overwrite = true (default false) lets it replace them (NC_CLOBBER, no start-up check). The examples' configs and the Python scripts set it, so rerunning an example replaces its own files; RunConfig.remove_diagnostics() and the scripts' clean-up helpers are gone. The output_files test reruns with overwrite on and checks every file is written again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(output): veg_surface, ground_evap and raut names; the water's enthalpy split by direction Review of #58: "film" and "canopy" read unclearly, and "rh"/"ra" read as relative humidity. - mean_canopy_water_py -> mean_veg_surface_water_py, mean_evap_canopy_py -> mean_veg_surface_evap_py, mean_film_excess_py -> mean_veg_surface_overflow_py, mean_evap_ground_py -> mean_ground_evap_py, mean_ra_py -> mean_raut_py; the patch rows and exports this PR added follow (PA_RHET, PA_RAUT, ...). - mean_enth_advect_py ("advected" suggests horizontal transport) is split by direction: mean_precip_enth_py (the rain and snow's in) and mean_outflow_enth_py (runoff, drainage, overflow and the exported vapour's beyond LE, out). The budget test closes on the pair. Model state is bit for bit with the previous head. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * build(output): regenerate ed_io_config.toml after the renames Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xiangtaoxu
added a commit
to xiangtaoxu/ED-MORE
that referenced
this pull request
Oct 30, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
xiangtaoxu
added a commit
to xiangtaoxu/ED-MORE
that referenced
this pull request
Oct 30, 2026
…EDmodel#59) * fix(output): one cohort order per month, file_chunk honoured, no overwrites The cohort order of a month's records. The stand is re-sorted every slow step (daily growth and mortality), but the output folded cohort slabs by slot, so a monthly cohort record averaged two cohorts in one slot after a mid-month swap, and staged fast-loop rows were reduced with the post-sort weights. Every cohort value of a month is now folded into its cohort's slot of the month (keyed by global_cohort_id: the live state, the diagnostic rows and the staged rows, whose ids and weights the fast loop stages once per step), and every record of the month is written in the month's end order (write_order, before the I/O phase). The axes' bookkeeping is also written on the F tier, read from the stand. No overwrites. A run lists every diagnostic file it would write and stops before its first step if one exists; the writer creates files with NC_NOCLOBBER. RunConfig.remove_diagnostics() removes a configuration's files; the examples' scripts and calibrate_fast's trials call it before they run again. file_chunk honoured. A tier with a cohort or patch variable was cut to monthly files (F: daily) whatever its file_chunk said. The cohort and patch axes are now unlimited and grow within a file; each record names its cohorts and patches. "run" is renamed "all"; a monthly chunk of a day and an annual one shorter than a year are refused. Model state is bit for bit (ifx and gfortran dev builds, two runs). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): cite EDmodel#55 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): ET's sources: transpiration, canopy and ground evaporation, dew Four polygon outputs split ET by where the vapour comes from, as the canopy air's vapour ledger booked it on each step: - mean_transp_py transpiration - mean_evap_canopy_py evaporation of the intercepted water (leaf and wood films) - mean_evap_ground_py ground evaporation: bare soil and snow pack - mean_dew_py condensate (dew, fog) sent to soil layer 1 ET = transp + evap_canopy + evap_ground - dew - the canopy air's vapour gain. Default tiers D M Y, F on request (they are patch-block fast rows). The films' evaporation is b-weighted into the step's boundary-flux amounts (stage_bflux_t / boundary_flux_t, both integrators); close_ledgers exports the four amounts in step_exports_t, and patch_diag_row turns them into rates. PA_TRANSP held le_flux / L_v (ET) under a transpiration label and no output read it; it now holds transpiration. The patch rows after it move up by three. test_fast_integ_step: on both schemes, each step's sources less the dew less ET equal the canopy air's net vapour inflow (worst 1.5e-16 ESDIRK2, 2.1e-16 RK45, relative), the morning rain's films evaporate, and the day's canopy and ground evaporation agree between ESDIRK2 and RK45. Bit for bit: example 04's calibrated BCI run on the production ifx build matches the archived v0.4.0 run in all 24 variables both write (43,824 hourly and 1,826 daily records). ctest 95/95 on ifx and gfortran dev builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): the water and energy budgets close from the output; sub-daily values everywhere Every term the whole-column water and energy ledgers book is now on the patch row and an output variable, so both budgets close from the files on any tier (docs/science/diagnostics.md §4, The budgets): shed water, film excess, store gain and the water's paths and stores; bottom heat, advected enthalpy, the stores' energy gains and H by source. Adds Ra, the respiration split and Rh on the patch row, ecosystem carbon, absorbed PAR, the leaves' wet fraction and patch ET. ET and runoff are read from the fast loop's exports (the polygon accumulators are gone), and the formerly sub-daily-only variables get coarse sources, so every fast-loop quantity is on F and on D/M/Y. A per-cohort row reduced to the patch or polygon is reduced on F from the staged rows. New pytest output_budgets closes both budgets from a run's D and F files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): the default files' new variables, an upgrading note Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): cite EDmodel#56 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): diel_, the mean diel cycle of a daily, monthly or yearly record Every mean_<x> with a sub-daily value has a diel_<x>: its mean at each time of day over the record, in bins of the new [output].diel_interval ("1 hour" to "4 hours"), on a diel axis (the last) with the coordinate diel_hour and its bounds. Each staged sub-step is folded into its bin of the coarse tiers' open records as the F tier reads it, before the step closes them; a cohort's cycle goes to its slot of the month. D, M and Y (D and M on the cohort and patch axes), never F; off by default, so the output is bit for bit. Needs the fast loop and a dt_fast that divides the bin. MAX_OUTPUT_VARS rises to 1024 for the ~150 new names. New pytest output_diel: a daily record is, bin by bin, its day's sub-daily records, on the polygon, patch, soil and cohort axes, at 1- and 3-hour bins; a diel_ variable on F is refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): cite EDmodel#57 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): CF time with bounds; clearer names, labels and defaults time is now each record's start in days since the run's start (proleptic Gregorian, standard_name time), with time_bnds: the start and the end of the record's last step, so a partial record carries its true span and xarray and CDO decode the axis. The year/month/day companions stay. mean_transp_leaf_py -> mean_leaf_transp_py (ecophys, off by default) and mean_transp_co -> mean_leaf_transp_co, so the per-leaf-area transpiration has one name at every level apart from mean_transp_py. mean_abs_par_co's long name says it is the incident-equivalent PAR; mean_cas_co2_pa joins its polygon twin in the energy group; mean_soil_temp_top_pa is on D by default like its siblings; diagnostics.md §7 no longer promises an albedo. ex05's plot script and README read the new time axis. Model state is bit for bit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): cite EDmodel#58 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): [output].overwrite, an opt-in to replace existing output A run still stops before its first step when a file it would write exists, unless the new [output].overwrite = true (default false) lets it replace them (NC_CLOBBER, no start-up check). The examples' configs and the Python scripts set it, so rerunning an example replaces its own files; RunConfig.remove_diagnostics() and the scripts' clean-up helpers are gone. The output_files test reruns with overwrite on and checks every file is written again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(output): veg_surface, ground_evap and raut names; the water's enthalpy split by direction Review of EDmodel#58: "film" and "canopy" read unclearly, and "rh"/"ra" read as relative humidity. - mean_canopy_water_py -> mean_veg_surface_water_py, mean_evap_canopy_py -> mean_veg_surface_evap_py, mean_film_excess_py -> mean_veg_surface_overflow_py, mean_evap_ground_py -> mean_ground_evap_py, mean_ra_py -> mean_raut_py; the patch rows and exports this PR added follow (PA_RHET, PA_RAUT, ...). - mean_enth_advect_py ("advected" suggests horizontal transport) is split by direction: mean_precip_enth_py (the rain and snow's in) and mean_outflow_enth_py (runoff, drainage, overflow and the exported vapour's beyond LE, out). The budget test closes on the pair. Model state is bit for bit with the previous head. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * build(output): regenerate ed_io_config.toml after the renames Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(output): mean_rhet_*, and the cohort repeats of the patch's CO2 and pressure removed Review of EDmodel#58: - mean_rh_py/_pa -> mean_rhet_py/_pa (FLD_P_RH -> FLD_P_RHET): "rh" read as relative humidity. - mean_ca_co and mean_pressure_co are removed with their rows (CO_CA, CO_PRESSURE): they repeated, for every cohort, the patch's canopy-air CO2 at the sub-step's start and the forcing's air pressure. Only edmore.canopy's docstring named them; it now points the offline leaf replay at mean_cas_co2_pa (the previous sub-step's) and mean_psurf_py. 29 fast cohort rows, 39 in all. Model state is bit for bit; mean_rhet_* equals the old mean_rh_* record for record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(fast): "film" identifiers become veg_surface / surface water The water the leaves and wood hold on their surfaces was called a "film" in the code; its state, config keys and outputs already say surf_water / veg_surface. Identifiers now follow: frozen%film -> frozen%veg_surface, film_evap_leaf/_wood -> leaf_/wood_surface_evap, cohort_film_water -> cohort_surface_water, limit_film_evaporation -> limit_surface_evaporation, clamp_canopy_film -> clamp_surface_water, leaf_film_coeff -> leaf_surface_evap_coeff, g_film_leaf/_w -> g_surf_leaf/_wood, and the tests' locals and names. No config key, state-file variable or output changes; comments and docs follow in the next commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(fast): "film" in comments, messages and docs becomes "surface water" The prose follows the identifiers of the previous commit: the water the leaves and wood hold on their surfaces is "surface water" throughout the comments, the tests' messages, the TOML comments and the science docs (vegetation_energy_dynamics.md's section is now "The canopy's surface water"). Kept: soil_biophysics.md's "film flow" (soil physics), and example 04's recorded fit.json, which the next calibration rewrites from parameters.toml. Bit for bit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): cite EDmodel#59 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
xiangtaoxu
added a commit
that referenced
this pull request
Oct 30, 2026
…es and residuals (#60) * fix(output): one cohort order per month, file_chunk honoured, no overwrites The cohort order of a month's records. The stand is re-sorted every slow step (daily growth and mortality), but the output folded cohort slabs by slot, so a monthly cohort record averaged two cohorts in one slot after a mid-month swap, and staged fast-loop rows were reduced with the post-sort weights. Every cohort value of a month is now folded into its cohort's slot of the month (keyed by global_cohort_id: the live state, the diagnostic rows and the staged rows, whose ids and weights the fast loop stages once per step), and every record of the month is written in the month's end order (write_order, before the I/O phase). The axes' bookkeeping is also written on the F tier, read from the stand. No overwrites. A run lists every diagnostic file it would write and stops before its first step if one exists; the writer creates files with NC_NOCLOBBER. RunConfig.remove_diagnostics() removes a configuration's files; the examples' scripts and calibrate_fast's trials call it before they run again. file_chunk honoured. A tier with a cohort or patch variable was cut to monthly files (F: daily) whatever its file_chunk said. The cohort and patch axes are now unlimited and grow within a file; each record names its cohorts and patches. "run" is renamed "all"; a monthly chunk of a day and an annual one shorter than a year are refused. Model state is bit for bit (ifx and gfortran dev builds, two runs). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): cite #55 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): ET's sources: transpiration, canopy and ground evaporation, dew Four polygon outputs split ET by where the vapour comes from, as the canopy air's vapour ledger booked it on each step: - mean_transp_py transpiration - mean_evap_canopy_py evaporation of the intercepted water (leaf and wood films) - mean_evap_ground_py ground evaporation: bare soil and snow pack - mean_dew_py condensate (dew, fog) sent to soil layer 1 ET = transp + evap_canopy + evap_ground - dew - the canopy air's vapour gain. Default tiers D M Y, F on request (they are patch-block fast rows). The films' evaporation is b-weighted into the step's boundary-flux amounts (stage_bflux_t / boundary_flux_t, both integrators); close_ledgers exports the four amounts in step_exports_t, and patch_diag_row turns them into rates. PA_TRANSP held le_flux / L_v (ET) under a transpiration label and no output read it; it now holds transpiration. The patch rows after it move up by three. test_fast_integ_step: on both schemes, each step's sources less the dew less ET equal the canopy air's net vapour inflow (worst 1.5e-16 ESDIRK2, 2.1e-16 RK45, relative), the morning rain's films evaporate, and the day's canopy and ground evaporation agree between ESDIRK2 and RK45. Bit for bit: example 04's calibrated BCI run on the production ifx build matches the archived v0.4.0 run in all 24 variables both write (43,824 hourly and 1,826 daily records). ctest 95/95 on ifx and gfortran dev builds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): the water and energy budgets close from the output; sub-daily values everywhere Every term the whole-column water and energy ledgers book is now on the patch row and an output variable, so both budgets close from the files on any tier (docs/science/diagnostics.md §4, The budgets): shed water, film excess, store gain and the water's paths and stores; bottom heat, advected enthalpy, the stores' energy gains and H by source. Adds Ra, the respiration split and Rh on the patch row, ecosystem carbon, absorbed PAR, the leaves' wet fraction and patch ET. ET and runoff are read from the fast loop's exports (the polygon accumulators are gone), and the formerly sub-daily-only variables get coarse sources, so every fast-loop quantity is on F and on D/M/Y. A per-cohort row reduced to the patch or polygon is reduced on F from the staged rows. New pytest output_budgets closes both budgets from a run's D and F files. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): the default files' new variables, an upgrading note Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): cite #56 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): diel_, the mean diel cycle of a daily, monthly or yearly record Every mean_<x> with a sub-daily value has a diel_<x>: its mean at each time of day over the record, in bins of the new [output].diel_interval ("1 hour" to "4 hours"), on a diel axis (the last) with the coordinate diel_hour and its bounds. Each staged sub-step is folded into its bin of the coarse tiers' open records as the F tier reads it, before the step closes them; a cohort's cycle goes to its slot of the month. D, M and Y (D and M on the cohort and patch axes), never F; off by default, so the output is bit for bit. Needs the fast loop and a dt_fast that divides the bin. MAX_OUTPUT_VARS rises to 1024 for the ~150 new names. New pytest output_diel: a daily record is, bin by bin, its day's sub-daily records, on the polygon, patch, soil and cohort axes, at 1- and 3-hour bins; a diel_ variable on F is refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): cite #57 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): CF time with bounds; clearer names, labels and defaults time is now each record's start in days since the run's start (proleptic Gregorian, standard_name time), with time_bnds: the start and the end of the record's last step, so a partial record carries its true span and xarray and CDO decode the axis. The year/month/day companions stay. mean_transp_leaf_py -> mean_leaf_transp_py (ecophys, off by default) and mean_transp_co -> mean_leaf_transp_co, so the per-leaf-area transpiration has one name at every level apart from mean_transp_py. mean_abs_par_co's long name says it is the incident-equivalent PAR; mean_cas_co2_pa joins its polygon twin in the energy group; mean_soil_temp_top_pa is on D by default like its siblings; diagnostics.md §7 no longer promises an albedo. ex05's plot script and README read the new time axis. Model state is bit for bit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): cite #58 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): [output].overwrite, an opt-in to replace existing output A run still stops before its first step when a file it would write exists, unless the new [output].overwrite = true (default false) lets it replace them (NC_CLOBBER, no start-up check). The examples' configs and the Python scripts set it, so rerunning an example replaces its own files; RunConfig.remove_diagnostics() and the scripts' clean-up helpers are gone. The output_files test reruns with overwrite on and checks every file is written again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(output): veg_surface, ground_evap and raut names; the water's enthalpy split by direction Review of #58: "film" and "canopy" read unclearly, and "rh"/"ra" read as relative humidity. - mean_canopy_water_py -> mean_veg_surface_water_py, mean_evap_canopy_py -> mean_veg_surface_evap_py, mean_film_excess_py -> mean_veg_surface_overflow_py, mean_evap_ground_py -> mean_ground_evap_py, mean_ra_py -> mean_raut_py; the patch rows and exports this PR added follow (PA_RHET, PA_RAUT, ...). - mean_enth_advect_py ("advected" suggests horizontal transport) is split by direction: mean_precip_enth_py (the rain and snow's in) and mean_outflow_enth_py (runoff, drainage, overflow and the exported vapour's beyond LE, out). The budget test closes on the pair. Model state is bit for bit with the previous head. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * build(output): regenerate ed_io_config.toml after the renames Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(output): mean_rhet_*, and the cohort repeats of the patch's CO2 and pressure removed Review of #58: - mean_rh_py/_pa -> mean_rhet_py/_pa (FLD_P_RH -> FLD_P_RHET): "rh" read as relative humidity. - mean_ca_co and mean_pressure_co are removed with their rows (CO_CA, CO_PRESSURE): they repeated, for every cohort, the patch's canopy-air CO2 at the sub-step's start and the forcing's air pressure. Only edmore.canopy's docstring named them; it now points the offline leaf replay at mean_cas_co2_pa (the previous sub-step's) and mean_psurf_py. 29 fast cohort rows, 39 in all. Model state is bit for bit; mean_rhet_* equals the old mean_rh_* record for record. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * refactor(fast): "film" identifiers become veg_surface / surface water The water the leaves and wood hold on their surfaces was called a "film" in the code; its state, config keys and outputs already say surf_water / veg_surface. Identifiers now follow: frozen%film -> frozen%veg_surface, film_evap_leaf/_wood -> leaf_/wood_surface_evap, cohort_film_water -> cohort_surface_water, limit_film_evaporation -> limit_surface_evaporation, clamp_canopy_film -> clamp_surface_water, leaf_film_coeff -> leaf_surface_evap_coeff, g_film_leaf/_w -> g_surf_leaf/_wood, and the tests' locals and names. No config key, state-file variable or output changes; comments and docs follow in the next commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(fast): "film" in comments, messages and docs becomes "surface water" The prose follows the identifiers of the previous commit: the water the leaves and wood hold on their surfaces is "surface water" throughout the comments, the tests' messages, the TOML comments and the science docs (vegetation_energy_dynamics.md's section is now "The canopy's surface water"). Kept: soil_biophysics.md's "film flow" (soil physics), and example 04's recorded fit.json, which the next calibration rewrites from parameters.toml. Bit for bit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): cite #59 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * feat(output): one source for the canopy-air depth; per-patch ET sources and residuals The leftovers of #58: - mean_cas_depth_py reads the patch row, the depth the fast loop used, as mean_cas_depth_pa does, so the polygon value is the patches' area-weighted sum; it joins mean_cas_depth_pa in the forcing group. It moves by at most 6 mm; the model is bit for bit. - mean_transp_pa, mean_veg_surface_evap_pa, mean_ground_evap_pa, mean_dew_pa, mean_resid_energy_pa and mean_resid_water_pa, off by default. mean_sw_up_py stays (calibrate_fast scores albedo with it), and example 04's composite stays hand-built (it skips the tower's gaps, which a diel_ record cannot). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs(changelog): cite #60 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
The last of four stacked output pull requests (fixes, new variables,
diel_, clean-ups). Stacked on#57 (on #56, on #55): until those merge, this PR's diff includes them, so review the last commit,
c1161e7.
1. CF time with bounds.
The problem.
timewas the decimal calendar year of a record's start, withunits = "year".No CF reader decodes that, and a record had no end. ex05's README told users to take a record's
width as
t[i+1] - t[i]and drop the last, partial record.The change.
timeis each record's start indays since <run start>, withcalendar = "proleptic_gregorian"(ED-MORE has leap years, and ex05 starts in 1600) andstandard_name = "time".time_bnds(time, nv)holds the start and the end of the record's last step, so a partial record(run start or end, a restart) carries its true span. A snapshot's instant is the upper bound.
year,monthanddaycompanions (and F'shour,minute,second) stay.diagnostics.md§4 says how to turn a rate into an amount with the bounds. ex05's README andplot_pft_size.py(the one shipped reader oftime) use the new axis.The time units (days since the run's start, proleptic Gregorian) are the owner's choice, confirmed
in review.
2. Names, labels and defaults (the plan's §5, decision D9):
mean_transp_leaf_py→mean_leaf_transp_py, moved to theecophysgroup and off by default.Its cohort source
mean_transp_co→mean_leaf_transp_co. Both are per leaf area (mol m⁻² leafs⁻¹);
mean_transp_py, the transpiration in kg m⁻² s⁻¹, keeps its name. Theirdiel_twinsfollow. ex05's Palo Verde io_config is updated.
mean_abs_par_co's long name says what it holds, the incident-equivalent PAR (the absorbed VISover the leaf absorptance), not "absorbed".
mean_cas_co2_pamoves to theenergygroup, besidemean_cas_co2_py.mean_soil_temp_top_padefaults to F D M, like its siblings (it was F M).diagnostics.md§7 no longer says the radiation group holds an albedo.mean_rh_py/_pa→mean_rhet_py/_pa, so "rh" no longer reads as relativehumidity (feat(output): the water and energy budgets close from the output; sub-daily values everywhere #56 names the new autotrophic term
mean_raut_py).mean_ca_coandmean_pressure_coare removed. They repeated, for everycohort, the patch's canopy-air CO₂ and the forcing's pressure, and only
edmore.canopy'sdocstring named them; it now points the offline leaf replay at
mean_cas_co2_pa(the previoussub-step's, the CO₂ the solve saw) and
mean_psurf_py. Two cohort rows fewer (29 fast, 39 inall).
Left for two follow-up PRs, after the owner's review: first a refactor of the internal "film"
vocabulary to
veg_surface, then these:mean_cas_depth_py/_paread different sources (the stateand the forcing's echo);
mean_sw_up_pyduplicates the VIS + NIR pair; the ET sources and theresiduals have no
_patwins.Design: the plan agreed with the owner (§5 and D9). Drafted with Claude Code (see Credit).
Effect on results (tick exactly one; CI checks)
Verification. A new option that is off by default counts when the model is unchanged with it off.
The model and every value are bit for bit. What changes is how the files label and select them: the
encoding of
time, the newtime_bnds, two renames and one default (listed under Verification).User-facing changes (tick all that apply; each needs an "Upgrading" note in the changelog)
None
Config keys added, renamed or removed, or a value in the shipped config files changed
Output variables or output files changed
Input files (forcing, maps, initial conditions) or their format changed
Building or running changed: command-line or CMake options, dependencies, the Python API
State files written by the last release no longer load
Output:
timeunits and calendar, andtime_bnds;mean_transp_leaf_py,mean_transp_coandmean_rh_py/_parenamed (the first off by default);mean_ca_coandmean_pressure_coremoved;mean_soil_temp_top_paon D by default. Each has an Upgrading note.Verification
Two runs: the reference config with the shipped census, and example 04's calibrated BCI run on the
tower forcing. On both compilers:
time_bnds(new on every tier),mean_soil_temp_top_pa(now on D) andmean_transp_leaf_py(renamed, off by default);timedecodes asdays since 2001-06-01 00:00:00, proleptic Gregorian, with the monthly bounds[[0, 30], [30, 61], [61, 92]].
output_filesnow checkstime's units, calendar and bounds on M (each month),D (each day) and F (hourly, end to end).
ruff checkclean.ed_io_config.tomlregenerated.long): not run.Credit and AI use
with the owner, and ran the verification above.
Not in this PR
diel_records.🤖 Generated with Claude Code