Skip to content

Support decompression gas switches #2

Description

@brunopaes

Summary

DiveState carries a single gas_fraction for the whole dive, stops included. Real ascents switch to an oxygen-rich mix at a planned depth — EAN50 at 21 m, oxygen at 6 m are the conventional ones — and most of the reduction in stop time comes from exactly that. The schedules this tool produces are therefore correct for "the whole dive on back gas" and considerably longer than any plan a diver would actually run.

Why it matters

The bottom gas is the wrong gas to decompress on. Dropping the inert fraction at a stop widens the gradient between tissue and inspired pressure, which is what makes shallow stops clear quickly. Without it:

  • reported stop times overstate the real obligation, sometimes by a large factor at the 6 m and 3 m stops;
  • --gas ean40 looks strictly better than --gas air on deco while being undiveable at depth (see the MOD_EXCEEDED finding), when the real answer is to dive air and switch to EAN40 on the way up;
  • there is no way to express the standard recreational and technical ascent plans at all.

Design sketch

A gas plan is a list of (switch_depth, gas_fraction) ordered shallow-to-deep, resolved to "the richest mix breathable at this depth". The natural home is DiveState, since it already owns the gas:

@dataclass
class GasPlan:
    stages: list[tuple[float, float]]   # (max_depth, n2_fraction)

    def at_depth(self, depth: float) -> float: ...

DiveState.gas_fraction becomes a property resolving the plan at the current depth, so step_at_pressure and load_gas_at_pressure need no signature change. The ascent walk is where the care is: walk_ceiling_to_surface currently threads one gas_fraction through the whole generator, and it would need to resolve per step instead.

Scope

  • configs/limits.py — standard stage presets alongside GAS_MIXES.
  • model/dive_state.pyGasPlan, and gas_fraction resolved from depth.
  • model/splinter_decompression.pywalk_ceiling_to_surface and calculate_deco_schedule take the plan rather than a scalar fraction.
  • processors/dive_analysis.py_check_ppo2 must validate each stage at its own depth range, not one fraction against the whole profile. A switch gas is by design over its own MOD at depth; the check has to become "breathable where it is actually breathed", which is a real change in meaning, not a refactor.
  • handlers/profile_analyser.py — a --deco-gas DEPTH:MIX flag, repeatable.
  • README.md — the gas section, and the "Comparing breathing gases" scenario, which currently draws the wrong conclusion for want of switching.

Open questions

  • Switch time. A real switch costs a minute or two at the stop for the valve drill and a buddy check. Model it as a fixed cost, or ignore it?
    Ignoring it is optimistic in the same direction as everything else here.
  • CNS / OTU. Not modelled today (_check_ppo2 bounds partial pressure only). Deco on oxygen is where the CNS clock actually bites, so switching makes the absence of that accounting more visible. Probably its own issue.
  • Interacts with the helium issue: a trimix plan without switching is not a plan. Sequencing them together is likely cheaper than doing either twice.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions