Michal Frontczak commited on
Commit
0c1e1ac
·
verified ·
1 Parent(s): a399a5e

Add gas.py

Browse files
Files changed (1) hide show
  1. gas.py +52 -0
gas.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Gas consumption and gas-switch planning.
2
+
3
+ SAC/consumption formula cross-checked against DAN's "Estimating Your Air
4
+ Consumption" and multiple independent dive-training SAC-rate references,
5
+ which all agree on the same normalization:
6
+
7
+ ATA(depth) = depth_m / 10 + 1
8
+ volume_used = SAC_surface_L_per_min * ATA * time_min
9
+
10
+ Gas-switch selection follows standard technical-diving practice: at any
11
+ point during ascent, breathe the richest available gas whose MOD (maximum
12
+ operating depth, from a configurable max ppO2) is at or below the current
13
+ depth.
14
+ """
15
+ from __future__ import annotations
16
+
17
+ from dataclasses import dataclass
18
+
19
+ from buhlmann import Gas, depth_to_pressure
20
+
21
+
22
+ @dataclass
23
+ class CylinderGas:
24
+ """A gas the diver is carrying, with a name for display."""
25
+
26
+ gas: Gas
27
+ name: str
28
+ max_ppo2: float = 1.4
29
+
30
+ @property
31
+ def mod_m(self) -> float:
32
+ return self.gas.mod_m(self.max_ppo2)
33
+
34
+
35
+ def ata(depth_m: float) -> float:
36
+ """Atmospheres absolute at a given depth."""
37
+ return depth_to_pressure(depth_m) / depth_to_pressure(0.0)
38
+
39
+
40
+ def gas_volume_used(sac_l_per_min: float, depth_m: float, time_min: float) -> float:
41
+ """Litres of gas consumed breathing at a constant depth for time_min."""
42
+ return sac_l_per_min * ata(depth_m) * time_min
43
+
44
+
45
+ def select_gas(available: list, depth_m: float) -> CylinderGas:
46
+ """Pick the richest (highest fO2) available gas whose MOD is safe at
47
+ this depth. Falls back to the gas with the deepest MOD if none is
48
+ nominally safe (better than returning nothing to breathe)."""
49
+ usable = [g for g in available if g.mod_m >= depth_m - 1e-9]
50
+ if usable:
51
+ return max(usable, key=lambda g: g.gas.fo2)
52
+ return max(available, key=lambda g: g.mod_m)