Skip to main content

Diving decompression library (MN90, compartment model)

Project description

Python 3.8+ Version

Decompression — bibliothèque Python & DivePlanner Android

Ce dépôt regroupe :

  1. Bibliothèque Python decompression-diver (v1.1.0) — saturation en azote / modèles de décompression : tables MN90, modèle dynamique par compartiments (Haldane, Schreiner) et modèle Bühlmann ZHL-16, avec support Nitrox (voir README_NITROX.md).
  2. Application Android DivePlanner (v1.0.0) — planification, mélanges et blocs gazeux, calculs Haldane, Bühlmann ZHL-16C et MN90. Détails dans android/README.md.

Historique des changements : CHANGELOG.md.

Publication PyPI : voir Makefile.

  • Pour créer les archives : make dist
  • Pour déployer vers le dépôt PyPI configuré par défaut dans ~/.pypirc : make dist-deploy
  • Pour déployer vers TestPyPI : make dist-testpypi

Fonctionnalités (Python)

✅ Modèles de Décompression

  • Tables MN90 : Implémentation des tables françaises de décompression FFESSM
  • Modèle Compartiments Dynamique : Équations de Haldane et Schreiner avec multiples compartiments

✅ Modèle Bühlmann ZHL-16 avec Gradient Factors

  • 16 compartiments avec périodes de 4 à 635 minutes
  • Gradient Factors configurables (GF Low/GF High) pour ajuster la conservativité
    • GF Low (30-100%) : tolérance à la narcose pendant la descente
    • GF High (70-100%) : tolérance aux bulles pendant la remontée
  • Calcul automatique des paliers avec optimisation des profondeurs
  • Exemple d'utilisation :
    from deco.modelfactory import DecompressionModelFactory
    
    # Modèle conservateur (GF 30/70)
    model = DecompressionModelFactory.create('BUHLMANN', gf_low=30, gf_high=70)
    model.append(30, 50)  # 30 min à 50m
    stops = model.decompression_stop()  # Retourne les paliers
    

⚠️ Limitations et Comportements Importants

  • GF vs MN90 FFESSM : L'implémentation GF est maintenant cohérente avec la théorie Bühlmann. GF 30/70 (conservateur) produit généralement plus de paliers que MN90, tandis que GF 50/95 (permissif) en produit moins.
  • NDL sur faibles profondeurs : Les calculs NDL sont maintenant corrects et cohérents avec les principes physiques de Bühlmann.
  • Nitrox non implémenté : Le support des mélanges gazeux enrichis (Nitrox) n'est pas encore disponible. Les calculs utilisent actuellement l'air uniquement.
  • Algorithme corrigé : Les formules de pression critique et la logique des paliers ont été corrigées pour être conformes à la théorie Bühlmann ZHL-16 avec Gradient Factors.

✅ Compartment-Based Decompression Model

  • Haldane Equation implementation for constant depth
  • Schreiner Equation for variable pressure (descent/ascent)
  • Multiple compartments with different periods (5, 10, 20, 40, 80 minutes)
  • Physiologically correct initialization (atmospheric pressure)
  • Validated algorithms with comprehensive test suite

✅ Nitrox Support

  • Gas mixture management (Air, Nitrox 32, 50, 80, etc.)
  • Gas switching during dive
  • Oxygen toxicity consideration (1.6 bar max)
  • Realistic dive profiles with proper ascent rates

✅ Comprehensive Testing

  • 52 tests covering all functionality
  • Algorithm validation (Haldane, Schreiner, Bühlmann, compartment behavior)
  • Physiological consistency checks
  • Multi-compartment behavior validation

code validation and coverage

A makefile is available, run test and coverage:

Prepare the virtual environment:

 $> make venv

Once run the python environment is ready to use with the command line:

 $> source venv/bin/activate

Run tests

$> make test
 ----------------------------------------------------------------------
Ran 52 tests in 0.055s

OK
test_haldane_surface_to_const_depth (test_DecompressionDynamique.TestDecoDynamique) ... ok
test_multiple_compartments_ascent (test_DecompressionDynamique.TestDecoDynamique) ... ok
test_multiple_compartments_descent (test_DecompressionDynamique.TestDecoDynamique) ... ok
test_saturation_curve_validation (test_DecompressionDynamique.TestDecoDynamique) ... ok
test_choose_depth (test_DecompressionModelMn90.TestMN90) ... ok
test_choose_depth_exact (test_DecompressionModelMn90.TestMN90) ... ok
...
----------------------------------------------------------------------
Ran 49 tests in 0.055s

OK

Run tests coverage

$> make coverage
. venv/bin/activate ; pip install coverage
Requirement already satisfied: coverage in ./venv/lib/python3.6/site-packages
. venv/bin/activate 
coverage run -m unittest discover
coverage report -m
coverage html
...................................
----------------------------------------------------------------------
Ran 49 tests in 0.055s

OK
Name                                       Stmts   Miss  Cover   Missing
------------------------------------------------------------------------
deco/__init__.py                               0      0   100%
deco/model.py                                  3      0   100%
deco/modelmn90.py                            123      0   100%
deco/modelcompartment.py                     150      0   100%
deco/test/__init__.py                          0      0   100%
deco/test/test_DecompressionModelMn90.py     132      7    95%   34, 58, 102, 112, 138, 145, 188
deco/test/test_DecompressionDynamique.py     200      0   100%
------------------------------------------------------------------------
TOTAL                                        608      7    99%

cleanup the dev environment

$> make clean
rm -rf venv htmlcov
find -iname "*.pyc" -delete
find -iname ".coverage" -delete
rm -rf .pytest_cache
rm -rf deco/__pycache__
rm -rf deco/test/__pycache__

API

API for this module is quite simple.

decompression packages

The UML Class diagram:

decompression packages

The decompression factory

It implements a factory to select the desired decompression algorithm. Its usage can be done as following:

from deco.modelfactory import DecompressionModelFactory
    
deco = DecompressionModelFactory.create(DecompressionModelFactory.MODEL_MN90)
deco.append(time=0, depth=0)  
deco.append(5, 10)  
deco.append(10, 20)  
deco.append(20, 20)  
deco.append(30, 21)  
deco.append(30, 24)  
deco.append(40, 23)  
deco.append(50, 20)  
deco.append(60, 10)  
deco.append(69, 5)
print(deco.deco.decompression_stop())

returns

(70, 25, 0, 0, 1, 41, 'M')
means:
      computed dive time  = 70mn
      computed dive depth = 25m
      step 12m            = 0 mn
      step 9m             = 0mn
      step 6m             = 1mn
      step 3m             = 41mn
      GPS                 = M

It is possible to include a majoration :

print(deco.deco.decompression_stop(majoration=12))

returns

(85, 25, 0, 0, 9, 48, '*')
means:
      computed dive time  = 85mn
      computed dive depth = 25m
      step 12m            = 0mn
      step 9m             = 0mn
      step 6m             = 9mn
      step 3m             = 48mn
      GPS                 = not applicable with majoration

The model interface

The model interface defines the methods to be implemented for each models:

def append(self, time: int, depth: int, **options)

Append method aims to add new chunk (time, depth) of the dive into the algorithm. Optional inputs information could be provided, according to the implementation, to fine tune the compression model.

def reset(self)

Resets the model. Nothing is kept in memory.

def deco.decompression_stop(self, **kwargs)

Computes and return decompression tuple: (computed time, computed depth, step 12m, step 9m, step 6m, step 3m, GPS). kwargs may contains additional and optional information for extended usage of the floor computation.

Implemented models

MN90 Model

The MN90 model compute the decompression thanks to the MN90 tables. It implements the possibility to compute successive dives with the management of residual computation and extra time to include the the second dive. The example here before demonstrates this computation. Moreover this model implement methods not mandatory for the common interface. Most important one is the methods to retrieves residual nitrogen value from GPS, and majoration from residual and ground time interval:

from deco.modelmn90 import MN90
# Residual nitrogen after 4hours (4*60 mn) and previous GPS=L
print ("residual=" + str(MN90().residual_nitrogen(240, 'J')))
print ("residual=" + str(MN90().residual_nitrogen(239, 'J')))

results

residual=0.93
residual=0.96

Then to retrieve residual nitrogen time from residual nitrogen and depth:

from deco.modelfactory import DecompressionModelFactory
deco = DecompressionModelFactory.create(DecompressionModelFactory.MODEL_MN90)
print ("majoration=" + str(deco.residual_nitrogen_time(interval=240, gps='J', depth=20)))

results

majoration=13

Compartment-Based Model ✅

The compartment-based model implements the Haldane and Schreiner equations for dynamic decompression calculation.

Basic Usage:

from deco.modelcompartment import DecompressionModelCompartment

# Create model with 10-minute compartment
deco = DecompressionModelCompartment(
    period=10*60,  # 10 minutes in seconds
    sc=2.75,       # Critical threshold
    gas_mixture={"N2": 0.7902, "O2": 0.2095, "Ar": 0.0003}  # Air
)

# Simple dive profile
deco.append(0, 0)      # Surface
deco.append(3, 30)     # Descent to 30m
deco.append(20, 30)    # 20 minutes at 30m
deco.append(3, 0)      # Ascent to surface

# Get final tension
print(f"Final tension: {deco.current_tension:.3f} bar")

Nitrox Support:

from deco.modelcompartment import DecompressionModelCompartment, create_nitrox_mixture

# Create Nitrox 32
nitrox_32 = create_nitrox_mixture(32)
deco = DecompressionModelCompartment(
    period=10*60,
    sc=2.75,
    gas_mixture=nitrox_32
)

# Gas switching during dive
deco.append(0, 0)      # Surface with Nitrox 32
deco.append(5, 30)     # Descent
deco.append(20, 30)    # Bottom time

# Switch to Nitrox 80 for decompression
nitrox_80 = create_nitrox_mixture(80)
deco.append(0, 30, gas_mixture=nitrox_80)  # Gas switch
deco.append(10, 15)    # Decompression stops
deco.append(5, 6)      # More stops
deco.append(3, 0)      # Surface

Multi-Compartment Analysis:

# Analyze multiple compartments
periods = [5*60, 10*60, 20*60, 40*60]  # 5, 10, 20, 40 minutes
compartments = []

for period in periods:
    comp = DecompressionModelCompartment(period=period, sc=2.75)
    comp.append(0, 0)
    comp.append(3, 30)
    comp.append(20, 30)
    comp.append(3, 0)
    compartments.append(comp)

# Compare final tensions
for i, comp in enumerate(compartments):
    print(f"Compartment {periods[i]//60}min: {comp.current_tension:.3f} bar")

Key Features:

  • Physiologically correct initialization (atmospheric pressure)
  • Haldane equation for constant depth
  • Schreiner equation for variable pressure
  • Gas mixture support (Air, Nitrox, etc.)
  • Gas switching during dive
  • Oxygen toxicity consideration
  • Comprehensive validation with 49 tests

Validation Results:

  • Haldane Law: Tension increases by half the gradient in one period ✅
  • Descent/Ascent Consistency: Physiologically realistic behavior ✅
  • Multi-compartment Behavior: Fast compartments saturate/desaturate faster ✅
  • Gas Switching: Proper handling of different mixtures ✅

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

decompression_diver-1.2.2.tar.gz (25.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

decompression_diver-1.2.2-py3-none-any.whl (23.7 kB view details)

Uploaded Python 3

File details

Details for the file decompression_diver-1.2.2.tar.gz.

File metadata

  • Download URL: decompression_diver-1.2.2.tar.gz
  • Upload date:
  • Size: 25.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for decompression_diver-1.2.2.tar.gz
Algorithm Hash digest
SHA256 c3c84b784b9be89c0b14a0047168b51620964378a2358c6c0ecc467f643d85d4
MD5 6bb1655cb29c38eb25f3be6a0374bcd7
BLAKE2b-256 c2a79f6d5266e28515a7752978a9478ff0c2fd5942fde9f11668d2dfd1d317ab

See more details on using hashes here.

File details

Details for the file decompression_diver-1.2.2-py3-none-any.whl.

File metadata

File hashes

Hashes for decompression_diver-1.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 97bc696b7487047e3fc51883ac068f7207a2355cd95f5478dcd02b38e6189c3e
MD5 3341ea655db7a39e0357246ac0d082c5
BLAKE2b-256 42ad75203b673fca1c7b89f662cf41ce00a3a3f42441ea0439214d07d16c8f2c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page