# Story 10.6: Mise à jour des Bindings Python

**Epic:** 10 - Enhanced Boundary Conditions  
**Priorité:** P1-HIGH  
**Estimation:** 2h  
**Statut:** backlog  
**Dépendances:** Stories 10-2, 10-3, 10-4

---

## Story

> En tant qu'utilisateur Python de la librairie Entropyk,  
> Je veux accéder aux nouveaux types de conditions aux limites via l'API Python,  
> Afin de pouvoir utiliser les fonctionnalités avancées (concentration glycol, titre, psychrométrie).

---

## Contexte

Les bindings Python (via PyO3) doivent être mis à jour pour exposer:

1. Les nouveaux types physiques (`Concentration`, `VolumeFlow`, `RelativeHumidity`, `VaporQuality`)
2. Les nouveaux composants (`RefrigerantSource`, `BrineSource`, `AirSource`, etc.)

---

## Spécifications Techniques

### 1. Exposer les Nouveaux Types Physiques

```python
# bindings/python/entropyk/core.py

class Concentration:
    """Concentration massique en % (0-100)"""
    
    @staticmethod
    def from_percent(value: float) -> 'Concentration':
        """Crée une concentration depuis un pourcentage."""
        pass
    
    def to_percent(self) -> float:
        """Retourne la concentration en pourcentage."""
        pass
    
    def to_mass_fraction(self) -> float:
        """Retourne la fraction massique (0-1)."""
        pass


class VolumeFlow:
    """Débit volumique en m³/s"""
    
    @staticmethod
    def from_m3_per_s(value: float) -> 'VolumeFlow':
        pass
    
    @staticmethod
    def from_l_per_min(value: float) -> 'VolumeFlow':
        pass
    
    def to_m3_per_s(self) -> float:
        pass
    
    def to_l_per_min(self) -> float:
        pass


class RelativeHumidity:
    """Humidité relative en % (0-100)"""
    
    @staticmethod
    def from_percent(value: float) -> 'RelativeHumidity':
        pass
    
    def to_percent(self) -> float:
        pass
    
    def to_fraction(self) -> float:
        pass


class VaporQuality:
    """Titre (vapor quality) pour fluides frigorigènes (0-1)"""
    
    @staticmethod
    def from_fraction(value: float) -> 'VaporQuality':
        pass
    
    def to_fraction(self) -> float:
        pass
    
    def to_percent(self) -> float:
        pass
```

### 2. Exposer les Nouveaux Composants

```python
# bindings/python/entropyk/components.py

class RefrigerantSource:
    """Source pour fluides frigorigènes compressibles."""
    
    @staticmethod
    def new(
        fluid_id: str,
        pressure: Pressure,
        enthalpy: Enthalpy,
        outlet: ConnectedPort,
    ) -> 'RefrigerantSource':
        """Crée une source avec pression et enthalpie fixées."""
        pass
    
    @staticmethod
    def with_vapor_quality(
        fluid_id: str,
        pressure: Pressure,
        vapor_quality: VaporQuality,
        outlet: ConnectedPort,
    ) -> 'RefrigerantSource':
        """Crée une source avec pression et titre fixés."""
        pass
    
    def set_mass_flow(self, mass_flow: MassFlow) -> None:
        """Définit le débit massique imposé."""
        pass


class BrineSource:
    """Source pour fluides caloporteurs liquides."""
    
    @staticmethod
    def water(
        temperature: Temperature,
        pressure: Pressure,
        outlet: ConnectedPort,
    ) -> 'BrineSource':
        """Crée une source d'eau pure."""
        pass
    
    @staticmethod
    def glycol_mixture(
        fluid_id: str,
        concentration: Concentration,
        temperature: Temperature,
        pressure: Pressure,
        outlet: ConnectedPort,
    ) -> 'BrineSource':
        """Crée une source de mélange eau-glycol."""
        pass


class AirSource:
    """Source pour air humide."""
    
    @staticmethod
    def from_dry_bulb_rh(
        temperature_dry: Temperature,
        relative_humidity: RelativeHumidity,
        pressure: Pressure,
        outlet: ConnectedPort,
    ) -> 'AirSource':
        """Crée une source avec température sèche et humidité relative."""
        pass
    
    @staticmethod
    def from_dry_and_wet_bulb(
        temperature_dry: Temperature,
        temperature_wet_bulb: Temperature,
        pressure: Pressure,
        outlet: ConnectedPort,
    ) -> 'AirSource':
        """Crée une source avec températures sèche et bulbe humide."""
        pass
    
    def specific_enthalpy(self) -> Enthalpy:
        """Retourne l'enthalpie spécifique de l'air humide."""
        pass
    
    def humidity_ratio(self) -> float:
        """Retourne le rapport d'humidité."""
        pass
```

---

## Fichiers à Modifier

| Fichier | Action |
|---------|--------|
| `bindings/python/src/core.rs` | Ajouter bindings pour nouveaux types |
| `bindings/python/src/components.rs` | Ajouter bindings pour nouveaux composants |
| `bindings/python/src/lib.rs` | Exporter les nouveaux types |
| `bindings/python/tests/test_boundary.py` | Tests pour nouveaux types |

---

## Critères d'Acceptation

- [ ] `Concentration` accessible depuis Python
- [ ] `VolumeFlow` accessible depuis Python
- [ ] `RelativeHumidity` accessible depuis Python
- [ ] `VaporQuality` accessible depuis Python
- [ ] `RefrigerantSource` et `RefrigerantSink` accessibles
- [ ] `BrineSource` et `BrineSink` accessibles
- [ ] `AirSource` et `AirSink` accessibles
- [ ] Tests Python passent
- [ ] Documentation Python générée

---

## Tests Requis

```python
# bindings/python/tests/test_boundary.py

def test_concentration():
    c = Concentration.from_percent(30.0)
    assert c.to_percent() == 30.0
    assert c.to_mass_fraction() == 0.3

def test_vapor_quality():
    vq = VaporQuality.from_fraction(0.5)
    assert vq.to_fraction() == 0.5
    assert vq.to_percent() == 50.0

def test_refrigerant_source():
    source = RefrigerantSource.new(
        "R410A",
        Pressure.from_pascals(1e6),
        Enthalpy.from_joules_per_kg(280000),
        port
    )
    assert source is not None

def test_brine_source_glycol():
    source = BrineSource.glycol_mixture(
        "MEG",
        Concentration.from_percent(30.0),
        Temperature.from_celsius(10.0),
        Pressure.from_pascals(3e5),
        port
    )
    assert source is not None

def test_air_source_psychrometrics():
    source = AirSource.from_dry_bulb_rh(
        Temperature.from_celsius(25.0),
        RelativeHumidity.from_percent(50.0),
        Pressure.from_pascals(101325),
        port
    )
    h = source.specific_enthalpy()
    w = source.humidity_ratio()
    assert h is not None
    assert w > 0
```

---

## Références

- [Architecture Document](../../plans/boundary-condition-refactoring-architecture.md)
- [Story 10-1: Nouveaux types physiques](./10-1-new-physical-types.md)
- [PyO3 Documentation](https://pyo3.rs/)
