Add diagram workbench UI with Modelica DoF coaching and ISO glyphs.

Ship the Next.js cycle editor with CAD chrome, technical HX symbols, Fixed/Free boundary guidance, and secondary water/air pressure drop support in the solver stack.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-17 22:46:46 +02:00
parent 62efea0646
commit 3358b74342
275 changed files with 70187 additions and 5230 deletions

View File

@@ -0,0 +1,149 @@
# FloodedEvaporator
Config type: `"FloodedEvaporator"`
Source: `crates/components/src/heat_exchanger/flooded_evaporator.rs`
Example: `crates/cli/examples/chiller_flooded_4port_watercooled.json` (DoF 19=19, COP ≈ 6.45)
---
## EN
### Purpose & physical model
Shell-and-tube **flooded** evaporator. Refrigerant boils on the shell side; secondary (water/brine) flows in the tubes. Heat duty uses **phase-change ε-NTU** (`C_min = C_sec`, `C_r → 0`):
```
ε = 1 exp(UA / C_sec)
Q = ε · C_sec · (T_sec,in T_evap(P))
```
- `T_evap(P)` = saturation temperature of the refrigerant at edge pressure
- `C_sec` = secondary heat-capacity rate [W/K]
There is **no plate-geometry correlation** inside this component (unlike BPHX). UA is a **lumped parameter** (possibly scaled by calibration `z_ua` via the inner `HeatExchanger` calib API).
### Dual operating modes (both enter Newton residuals)
| Mode | How secondary is defined | Secondary Newton unknowns | When to use |
|------|--------------------------|---------------------------|-------------|
| **System (4-port)** | Live edges `secondary_inlet` / `secondary_outlet` (e.g. BrineSource → HX → BrineSink) | Yes (`n_secondary` = 1 or 2) | Closed water loop, real machine |
| **Rating** | Scalars `secondary_inlet_temp_*` + `C_sec` (`secondary_mass_flow_kg_s` × `cp` or `secondary_capacity_rate_w_per_k`) | No (`n_secondary` = 0) | Qualification / open-loop duty; still **coupled ε-NTU in residuals** |
`coupled_ready` = refrigerant indices ready **and** (live secondary edges **or** rating scalars).
Never falls through to generic four-port `HeatExchanger::inner` residuals for normal operation (seed path is local and finite).
**Rating residual energy:** uses full `Q` (not `α(ṁ)·Q`) so `ṁ = 0` is not a trivial root when `C_sec > 0`.
**System residual energy:** uses `effective_duty(Q, α_ref, α_sec)` from `flow_regularization` for zero-flow safety.
Also exposed: `rate(p_in)` open-loop rating API for sweeps (same ε-NTU formulas).
### Outlet closure (DoF-critical)
| Setting | Residual r2 | Typical use |
|---------|-------------|-------------|
| Default (`quality_control: false`) | `h_out h_g(P)` saturated vapor | Compressor suction after disengagement |
| `quality_control: true` | `x_out target_quality` | Legacy recirculation / two-phase outlet |
Both keep **the same** `n_equations` (quality replaces sat-vapor; it does not add an extra free residual by itself).
`quality_control: true` on a closed cycle often needs a **free actuator** elsewhere or the DoF gate rejects the graph.
### Residuals & `n_equations()`
| Row | Equation |
|-----|----------|
| r0 | `P_out P_in` (no refrigerant ΔP by default) |
| r1 | `ṁ_ref · (h_out h_in) Q_eff` |
| r2 | sat-vapor **or** quality target (see above) |
| r_sec mass | `ṁ_sec,out ṁ_sec,in` only if live edges and **not** same-branch |
| r_sec energy | live secondary energy + duty (blended at low ṁ) |
```
n_equations = 3 + n_secondary
n_secondary = 0 # rating (no live edges)
| 1 # live edges, same-branch ṁ
| 2 # live edges, independent ṁ in/out
```
### Ports
| Port | Index | Role |
|------|-------|------|
| `inlet` | 0 | Refrigerant from EXV |
| `outlet` | 1 | Refrigerant to compressor suction |
| `secondary_inlet` | 2 | Water/brine in |
| `secondary_outlet` | 3 | Water/brine out |
CLI aliases: `water_in` / `brine_in` → secondary_inlet, etc. (`resolve_port_index`).
### Calibration
| Factor | Effect | Default |
|--------|--------|---------|
| `z_ua` (BOLT `Z_UA`) | `UA_eff = z_ua · UA` via inner calib | **1.0** |
| `z_dp` | pressure-drop scale if ΔP model used | 1.0 |
Inverse calibration (CLI `controls[]` / UI Fixed checkboxes): impose a measure (e.g. SST = `saturationTemperature`) and free `z_ua`.
### JSON parameters
| Key | Meaning | Unit | Default |
|-----|---------|------|---------|
| `ua` | UA | W/K | **required** |
| `refrigerant` | refrigerant id | | primary fluid |
| `secondary_fluid` | secondary fluid | | Water / MEG |
| `quality_control` | quality residual instead of sat-vapor | bool | `false` |
| `target_quality` | x target if quality_control | | 0.7 |
| `secondary_inlet_temp_c` / `_k` | rating T_sec,in | °C / K | |
| `secondary_mass_flow_kg_s` | rating ṁ_sec | kg/s | |
| `secondary_cp_j_per_kgk` | rating cp | J/(kg·K) | 4186 |
| `secondary_capacity_rate_w_per_k` | rating C_sec direct | W/K | |
| calib `z_ua` / `z_dp` | Z-factors | | 1.0 |
### energy_transfers / mass
When coupled: cooling `Q ≈ ṁ·(h_out h_in)` (positive heat absorbed by refrigerant).
`port_mass_flows` reports 4-port signs without calling generic inner four-port.
### Zero flow
`flow_regularization` on system path: smooth `|ṁ|` for live `C_sec`, activity factors, secondary Δh hold. Seed residuals stay finite if P is non-physical.
---
## FR
### But & modèle
Évaporateur **noyé** tubes-calandre. Duty **ε-NTU** à changement de phase :
```
ε = 1 exp(UA / C_sec)
Q = ε · C_sec · (T_sec,in T_evap(P))
```
Pas de corrélation géométrique type Longo/Shah (voir BPHX pour ça). UA est un **paramètre global**, modulable par `z_ua` (défaut **1**).
### Deux modes (tous deux dans le Newton)
| Mode | Secondaire | Inconnues eau | Usage |
|------|------------|---------------|--------|
| **Système** | Ports live Source → HX → Sink | oui | Machine fermée |
| **Rating** | Scalaires T + ṁ·cp (ou C_sec) | non | Qualification ; Q ε-NTU **dans** les résidus |
### Clôture de sortie
- Défaut : **vapeur saturée** `h_out = h_g(P)` (aspiration compresseur).
- `quality_control: true` : `x_out x_cible` (même nombre déquations).
### Résiduels
`n_equations = 3 + n_secondary` (0 / 1 / 2). Voir tableau EN.
### Calibration
Imposer une mesure (SST) et libérer `z_ua` (UI case Fixed, ou `controls[]`).
`z_ua = 1` = pas de correction.
### Ports / JSON
Identiques aux tableaux EN.