Metadata-Version: 2.4
Name: decompression-diver
Version: 1.2.1
Summary: Diving decompression library (MN90, compartment model)
Home-page: https://github.com/pidancier/decompression
Author: Fred Pidancier
Author-email: frederic.pidancier@gmail.com
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: Apache Software License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license-file
Dynamic: requires-python
Dynamic: summary

[![Python 3.8+](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
[![Version](https://img.shields.io/badge/version-1.2.1-green.svg)](CHANGELOG.md)

# 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`](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`](android/README.md).

Historique des changements : [`CHANGELOG.md`](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** :
  ```python
  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](https://github.com/pidancier/decompression/releases/download/v1.2.0/packages.png "Project Package organisation")

The UML Class diagram:

![decompression packages](https://github.com/pidancier/decompression/releases/download/v1.2.0/classes.png "Project UML Classes diagram")

### 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:**
```python
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:**
```python
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:**
```python
# 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 ✅
