[TF24] Prototype of multirate stepper (DO NOT MERGE) - #569
Draft
aornugent wants to merge 12 commits into
Draft
Conversation
Wire odelia's method="mri" multirate stepper through the SCM's resident
Solver, so the TF24 soil column (the stiff fast block) is sub-cycled while the
cohorts (the slow block) advance on a fixed macro grid.
The Patch is already laid out [cohorts | environment], which is exactly MRI's
[slow | fast] layout, so it becomes a multirate System with additive hooks and
no wrapper:
- slow_size / fast_size / coupling_size (0: the fast block reads its slow
context from the light field frozen per leg, not a linear aggregate),
- freeze_slow(x): set the cohorts and rebuild the light field once per leg,
- fast_rates / slow_rates: the soil and cohort tendencies.
patch.h includes odelia/mri.hpp so every Solver<Patch> TU has MriStep::step
defined, not just declared.
Control gains an ode_method string ("" == "rkck"), mapped to the odelia
Method enum where the SCM builds its Solver. Every other integration path is
untouched: an SCM run with ode_method "" or "rkck" is bit-identical to
before (verified: offspring and net_reproduction_ratios identical). On a real
TF24 run method="mri" at a daily macro step is stable and matches the adaptive
RKCK offspring to ~0.5%.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BvyqPT8pVL7bCZvWqmhZHM
… item 4) The multirate fast sub-cycle needs the per-layer root uptake at each micro-step. That uptake is a density-weighted integral of per-cohort consumption over the size distribution, so with the canopy frozen for a leg it can be quadratured at m << N cohorts instead of re-solving every one. - Individual::consumption_given_height(h, env): the factored coupling seam -- per-cohort per-layer uptake at an arbitrary height under the frozen-light, soil-theta environment. Mirrors growth_rate_given_height. - Species::set_collocation_nodes(m) / add_collocated_consumption(env, out): capture m log-spaced heights spanning the current distribution and their density*trapezium weights, then re-evaluate only those m cohorts under the current soil state. The same integral consumption_rate() computes, sampled at m points (O(m^-2)). - control (0 = exact full-N): switchable, consulted only on the method="mri" path, so every other run is bit-identical. - Patch::freeze_slow caches the nodes per leg; Patch::fast_rates uses the m-node quadrature instead of a full-N compute_rates. No odelia change and no new class: coupling_size=0 + freeze_slow keep the aggregate inside fast_rates, so the engine stays model-agnostic. Compiles clean; collocated-vs-full-N accuracy + timing being measured next. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BvyqPT8pVL7bCZvWqmhZHM
The first cut placed collocation nodes at synthetic log-spaced heights and interpolated the size distribution onto them. Interpolating the irregular evolved distribution was the dominant error (soil ~8% at m=20), and log-density interpolation produced NaN weights where a cohort's density had vanished (log_density -> -Inf), crashing aged stands. Subsample m of the N nodes evenly by index and quadrature over their actual heights and densities -- the same trapezium consumption_rate uses, on a coarser node set. No interpolation, no NaN: soil error falls to ~2.5% at m=20 and ~0.5% at m=40 on a real N=84 stand, converging ~O(m^-2); m >= N reduces to exact. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BvyqPT8pVL7bCZvWqmhZHM
Measured on real cohorts: the even-index subsample + trapezium meets sub-1% only for young/uniform distributions by m≈20; on mature skewed stands it needs m close to N (life=8, N=91: m=20 -> 38% soil error, m=80 -> 0.8%). Correct the control-field comment accordingly. Smarter, importance-weighted node placement (plant#53 §6/§4.4) is the open item to make collocation meet gate 4. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BvyqPT8pVL7bCZvWqmhZHM
Global counter incremented per Patch::fast_rates call (one O(M) coupling eval, the fast-block cost driver), with mri_fast_rate_calls_get/reset exported to R. Measures fast-evals per macro step: current no-split MRI does ~58/macro (~9.7 micro-steps/macro) on a life=3 TF24 run -- the headroom the exact-flow split (Lever 1) targets. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BvyqPT8pVL7bCZvWqmhZHM
…ver 1) Bring TF24_Environment::analytic_partial_flow / residual_rhs / infiltration_rate / drainage_touchdown_time (+ R wrappers, + test-tf24-soil-split.R) onto this branch. Reviewed for conformance: idiomatic (uses soil_parameter_value/dz), infiltration_rate is a single-source refactor of the compute_rates inline code, and the split reproduces compute_rates (test passes here). This is the exact, knob-free fast-block flow map the multirate split inner (Lever 1) needs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BvyqPT8pVL7bCZvWqmhZHM
…ontrol Patch opts into odelia's split inner when control$mri_use_split is set and the environment provides the R1 hooks (env_has_split trait; FF16 still compiles and is never split). New hooks: mri_split() (the runtime switch), analytic_flow (exact drainage recession on the soil block), residual_rhs (the gentle remainder + uptake; aux flux diagnostics left at zero rate during split legs). compute_rates refactored into compute_species_rates + assemble_resource_depletion, reused by a shared fast_block_uptake() helper (adaptive and split paths); production values unchanged (test-patch / test-environment-TF24 pass, control default off). E-A (daily macro grid, life=3): split is correct (offspring rel 4e-4, soil 8e-5 vs adaptive) but NOT faster -- 103 vs 58 fast-evals/macro, ~0.6x. Both ~10 micro-steps/macro: at the daily grid the sub-cycle is accuracy-driven, not drainage-stiffness-driven, so exact flow doesn't cut the step count and ROS Jacobian overhead dominates. Lever 1's win regime is coarse H (measuring next). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BvyqPT8pVL7bCZvWqmhZHM
patch_rhs_calls counts Patch::compute_rates (one full coupled RHS eval), the cost/step proxy shared by rkck and rodas, with get/reset exported to R. Used to test whether the REAL coupled patch (not the soil-only/surrogate #43 analysis) is accuracy- or stability-limited: does RODAS clear the RK45 tight-tolerance wall and take fewer steps? Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BvyqPT8pVL7bCZvWqmhZHM
… diagnostic) Borrowed from the review branch (commit 8399794), reviewed for conformance: set_shutdown_state left soil_consumption_/E_up_ stale, so a reused leaf fed a phantom, soil-independent uptake into the balance -- draining soil when nothing transpired and zeroing the reverse-mode uptake gradient across drought. Zero them at shutdown so uptake vanishes smoothly at the wilting point. Idiomatic, disjoint from the MRI work; test-tf24-shutdown.R passes. Also adds a patch_rhs_calls diagnostic counter (full-RHS evals) used for the stiff-regime method comparison. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BvyqPT8pVL7bCZvWqmhZHM
Borrowed from the review branch (commit 4e96b4b), reconciled with R1: the soil ODE state is now zeta = ln(theta - theta_res); theta recovered wherever the physics needs it; R interface (set/get_soil_water_state) stays in theta; env set_ode_state/ode_state/ode_rates pass raw zeta so the MRI partition is transparent. Purpose: verify whether R-D changes the MRI drought accuracy (predicted neutral, per the branch's rd_rodas_bench + the errors being cohort-layer not soil-layer). NOTE: R-D's updated tests (test-patch, test-environment-TF24, test-tf24-shutdown raw-state assertions, test-tf24-logchart) are not yet ported -- keep only if the verification warrants. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BvyqPT8pVL7bCZvWqmhZHM
…patch) Maps control$ode_method='imex' to the odelia IMEX stepper. No Patch hook changes: the MRI fast/slow partition already places the soil+aux block as the contiguous tail, which is exactly the implicit block the block-FD Jacobian needs. Bit-identical when off. Real-patch accuracy/timing verification in progress. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BvyqPT8pVL7bCZvWqmhZHM
…periment]" This reverts commit fc0dd2b. R-D (the zeta = ln(theta - theta_res) soil log-depletion chart) is confirmed dead: the accuracy wall is in the cohort layer, not the soil layer, so a soil-chart reshape is provably neutral (see the event-aware recharacterization and BASELINE). Removing it returns tf24_environment.h to the plain theta state, dropping named machinery that no longer earns its place. rkck production is unaffected (soil state representation is internal; R interface stayed in theta). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BvyqPT8pVL7bCZvWqmhZHM
aornugent
force-pushed
the
claude/tf24-multi-rate-stepper-n5audm
branch
from
July 22, 2026 04:48
5a48347 to
9c8bd2d
Compare
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.
Hi!
I have been tinkering with different solver configurations for TF24 including multirate integration (MRI) and implicit/explicit (IMEX) split operators.
This prototype has a bunch of baggage (e.g. cohort collocation, ruled out) and is only really a quick spike to see if there were any quick wins. I don't think it constitutes a production quality design, but I'm sharing it in case there are parts you'd like to cherry pick.
Maybe addresses some of: #566
This PR is paired with traitecoevo/odelia#42