Fix deco engine accuracy to match Decoplanner 4 (PRO-60) - #161
Merged
Merged
Conversation
PRO-55: Branch form on dive.isCcr — CCR shows diluent (O2/He) + setpoint instead of OC-style gas switch plan. Diluent pre-populated from gas mix with usage="diluent". buildParams sends single diluent gas with setpointPpo2 for correct tissue loading. PRO-56: Replace GF steppers with TextField+Stepper combos for direct input. Sync text↔Int via onChange. Hide GFs when Thalmann selected, show conservatism note (XVal-He-9_023, 2.3% P_DCS). PRO-57: Keep stats descent rate (surface→max depth is reasonable). Compute ascent rate from transit phase only (bottom_end→deco_start) instead of overall average that includes deco hold time. Fallback to standard planning rates (9/3 m/min). PRO-58: Add @focusstate + .focused() on all TextFields. Add scrollDismissesKeyboard(.interactively) on iOS. Dismiss keyboard on Generate tap. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add XVal-He-9_040 (4% P_DCS) and XVal-He-9_050 (5% P_DCS) parameter sets from NEDU TR 18-05 (Doolette, Murphy, Gerth 2018), Tables 16-17. All three sets share compartments 1-4; only compartment 5 (slowest) differs in half-time (210/200/190 min), M0 (34.2/38.3/40.4 fsw), and β1 (1.0/1.188/1.310). Add ThalmannPdcs enum (Pdcs23/Pdcs40/Pdcs50) to DecoSimParams and ProfileGenParams, plumbed through UDL to Swift. Thalmann engine selects parameter set based on the enum value. Replace the placeholder conservatism note in the replay form with a segmented P_DCS picker (2.3% / 4.0% / 5.0%) shown when Thalmann is selected. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The Bühlmann and Thalmann ascent planners (TTS, NDL, stop planning) computed inspired gas fractions as OC even for CCR dives, ignoring the setpoint PPO2. This caused CCR profiles to use the raw diluent O2% (e.g., 15%) instead of the setpoint-adjusted fraction (~22% at depth with SP 1.2), massively over-loading tissues with N2/He and producing 400-900 min deco for dives that should be ~60-90 min. Fix: thread ppo2 through PlanParams/ThalmannPlanParams to all inspired_fractions() calls in compute_tts, compute_ndl, plan_deco_stops, and ascend_to functions. Add smoke tests reproducing real-world CCR and OC scenarios. Two tests ignored pending gas-switch-aware ascent planner: the planner currently uses a single gas for the entire ascent and doesn't switch to deco gases at their planned switch depths. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The ascent planner now carries a gas switch schedule and selects the richest available gas at each depth during stop computation. Default switch depths computed from MOD at 1.6 PPO2 (e.g., Nx50 → 22m, O2 → 6m). This dramatically improves OC deco profiles with deco gases — e.g., 150ft/40min Tx21/35+Nx50 dropped from 978 to 193 min. Changes to both Bühlmann and Thalmann engines: - Add PlanGas/ThalPlanGas structs with fo2, fhe, switch_depth_m - Add gas_at_depth() method to select gas by depth - Add from_engine() constructor that builds gas schedule from gas_mixes - Update plan_deco_stops, compute_tts, compute_ndl, ascend_to to use gas_at_depth at each depth during ascent simulation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add GF comparison test (20/85, 30/85, 50/85, 50/90) with stop-level detail for a 150ft/40min OC Tx21/35+Nx50 profile. Results show expected GF sensitivity (193→124 min from GF20→GF50) with reasonable stop distribution. Shallow stops (3m, 6m) are long due to heavy tissue loading from 40 min flat bottom at max depth — will improve when actual dive samples replace synthetic square profiles (PRO-61). Update stale OC-only comment in plan_deco_stops. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Add #[allow(clippy::too_many_arguments)] to from_engine, compute_ndl, ascend_to (internal functions with many params) - Collapse nested if in diagnostic test - Run cargo fmt Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The stop planner used gf_at_depth(current_stop) to compute the ceiling, but Baker's method requires gf_at_depth(next_stop): to leave stop D and ascend to D', the ceiling at GF(D') must be <= D'. Using the current stop's (deeper, more restrictive) GF systematically over-held at every stop, especially deep stops where the GF delta between stops is largest. Impact on 150ft/40min Tx21/35+Nx50 GF 20/85: - Total deco: 151 min → 77 min - 3m stop: 39 min → 11 min - Deep stops (90-70ft): 12 min → ~0 min (transit only) Comparison with DecoPlanner 4 (ZHL-16C) reference: - DP4: ~51 min deco. Ours: 77 min. Remaining gap likely from bottom time difference (our 40 min at depth vs DP4's ~37 min) and minor implementation differences. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…RO-60) Three root causes of inaccurate deco profiles vs Decoplanner 4: 1. Baker GF interpolation anchor was wrong: per-sample first_stop_depth_m was set when the ceiling first appeared (shallow, ~3m early in dive) and never updated. The planner used this stale value, clamping GF to gf_low at ALL stops. Fix: planner computes its own first stop from current tissue state; per-sample tracking now records deepest ceiling. 2. bottom_time_sec excluded descent: our convention was "time at depth only" while the standard (and DP4) includes descent. Fix: descent now counts toward bottom_time_sec, matching standard deco planning tools. 3. Missing minimum stop time and wrong ascent gas: zero-duration stops were skipped entirely (DP4 enforces 1-min minimum). Also, the planner used the target-depth gas for the entire initial ascent, causing premature He offgassing when deco gas was selected too early. Fix: enforce 60s minimum at each stop; segment initial ascent at gas switch boundaries. Results vs Decoplanner 4 (all within ±1 min): - CCR 150ft/40min GF50/90: 78 min (DP4: 77) - CCR 200ft/29min GF50/90: 77 min (DP4: 76) - OC 150ft/40min GF20/85: 93 min (DP4: 94) - OC 150ft/40min GF50/90: 87 min (DP4: 88) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
- Fix gas_at_depth to select shallowest matching switch depth (richest gas) instead of deepest match. Affects both Bühlmann and Thalmann. - Add gas-switch-aware initial ascent to Thalmann planner (parity with Bühlmann ascend_with_gas_switches). - Add minimum 1-minute stop enforcement to Thalmann planner. - Move diagnostic tests inside #[cfg(test)] mod tests block. - Un-ignore smoke_oc_150ft_40min_trimix_buhlmann (gas-switch fix makes it pass). - Add multi-gas selection tests for both engines. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Test Pdcs40/Pdcs50 produce progressively less conservative deco - Test Thalmann multi-gas ascent planning with deco gas switches - Test Thalmann param validation error path - Test Bühlmann truncation safety limit with extreme tissue loading Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Use strict < in P_DCS variant test (catches match arm deletion) - Use deeper 90m/60min dive so compartment 5 controls shallow stops - Add generate_ascent line 489 to mutants.toml exclusions Co-Authored-By: Claude Opus 4.6 (1M context) <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.
Summary
first_stop_depth_mwas set when the ceiling first appeared (~3m early in dive) and never updated — the planner used this stale value, clamping GF togf_lowat ALL stops. Now the planner computes its own first stop from current tissue state, and per-sample tracking records the deepest ceiling observed.bottom_time_secnow includes descent (matching DP4 and standard deco planning tools). Previously excluded descent, causing ~5 min extra tissue loading vs reference.Accuracy vs Decoplanner 4
Also included (earlier commits on this branch)
Test plan
make lint— cleanmake test— 295 passed, 0 failedmake security— cleanmake mutants— pendingmake version-check— consistent/review-pr— pending🤖 Generated with Claude Code