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:
157
apps/web/public/docs/components/thermal-load.md
Normal file
157
apps/web/public/docs/components/thermal-load.md
Normal file
@@ -0,0 +1,157 @@
|
||||
# ThermalLoad
|
||||
|
||||
> Cold-side receiver of a physical inter-circuit thermal coupling.
|
||||
> Récepteur côté froid d'un couplage thermique physique inter-circuits.
|
||||
|
||||
---
|
||||
|
||||
## EN
|
||||
|
||||
### Physical model
|
||||
|
||||
`ThermalLoad` models a hydronic load segment — e.g. the cooling-water side of
|
||||
a shared heat exchanger — that receives an **externally-determined heat rate
|
||||
Q [W]** from the solver's thermal-coupling layer.
|
||||
|
||||
It follows the BOLT/Modelica boundary pattern
|
||||
(`BOLT.BoundaryNode.Coolant.Source → HX → Sink`): the loop's pressure and
|
||||
inlet temperature are fixed by **boundary components**, not by the load:
|
||||
|
||||
```text
|
||||
BrineSource(P_set, T_in) ──edge──▶ ThermalLoad ──edge──▶ BrineSink(P_back, T free)
|
||||
```
|
||||
|
||||
The outlet temperature is **emergent**: `T_out = T_in + Q / (ṁ·cp)` (the sink
|
||||
temperature must be left free — do not set `t_set_c` on the `BrineSink`, or
|
||||
the loop becomes over-determined).
|
||||
|
||||
### Residual equations — `n_equations() = 2`
|
||||
|
||||
```text
|
||||
r0: ṁ − ṁ_design (imposed design flow)
|
||||
r1: ṁ_design·(h_out − h_in) − Q_ext (energy balance, Q_ext = state[q_idx])
|
||||
```
|
||||
|
||||
The energy balance uses the *design* flow (a constant): r0 already pins
|
||||
`ṁ = ṁ_design`, and the constant form keeps the block linear and structurally
|
||||
nonsingular even when the initializer starts at `ṁ = 0`.
|
||||
|
||||
`Q_ext` is read from the per-coupling state unknown wired by
|
||||
`System::finalize()` via `Component::set_external_heat_index`. Unwired ⇒
|
||||
`Q_ext = 0` (adiabatic pass-through).
|
||||
|
||||
### DoF balance (water loop)
|
||||
|
||||
Unknowns: 1 ṁ (shared branch) + 2×(P,h) + 1 Q = 6.
|
||||
Equations: BrineSource 2 + ThermalLoad 2 + BrineSink 1 (T free) + coupling 1 = 6. ✓
|
||||
|
||||
### Jacobian
|
||||
|
||||
Exact and analytic (the whole block is linear): unit entry on the ṁ row,
|
||||
`±ṁ_design` on r1's enthalpy columns, `−1` on the coupling Q column.
|
||||
|
||||
### Operational states
|
||||
|
||||
| State | r0 | r1 |
|
||||
|---|---|---|
|
||||
| `On` | `ṁ = ṁ_design` | `ṁ_design·Δh = Q` |
|
||||
| `Bypass` | `ṁ = ṁ_design` | `h_out = h_in` (adiabatic) |
|
||||
| `Off` | `ṁ = 0` | `h_out = h_in` |
|
||||
|
||||
### `measure_output`
|
||||
|
||||
| Kind | Value |
|
||||
|---|---|
|
||||
| `Capacity` / `HeatTransferRate` | `abs(Q_ext)` [W] |
|
||||
| `MassFlowRate` | inlet ṁ [kg/s] |
|
||||
|
||||
### `energy_transfers`
|
||||
|
||||
`(Q_ext, 0)` — heat added *to* the component is positive. The component is
|
||||
**excluded from cycle-performance aggregation** (`counts_in_cycle_performance()
|
||||
= false`): the absorbed Q is the primary cycle's rejected duty, not extra
|
||||
cooling capacity. It still participates in per-component First Law validation.
|
||||
|
||||
### JSON parameters (CLI)
|
||||
|
||||
| Parameter | Unit | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `mass_flow_kg_s` | kg/s | `0.5` | Imposed design mass flow (must be > 0) |
|
||||
|
||||
### Usage with `thermal_couplings`
|
||||
|
||||
```json
|
||||
"components": [
|
||||
{ "type": "BrineSource", "name": "cw_in", "fluid": "Water",
|
||||
"p_set_bar": 2.0, "t_set_c": 30.0 },
|
||||
{ "type": "ThermalLoad", "name": "cw_load", "mass_flow_kg_s": 0.9 },
|
||||
{ "type": "BrineSink", "name": "cw_out", "fluid": "Water", "p_back_bar": 2.0 }
|
||||
],
|
||||
...
|
||||
"thermal_couplings": [
|
||||
{ "hot_circuit": 0, "cold_circuit": 1, "ua": 5000.0, "efficiency": 1.0,
|
||||
"hot_component": "cond", "cold_component": "cw_load" }
|
||||
]
|
||||
```
|
||||
|
||||
The coupling owns one unknown Q closed against the hot component's measured
|
||||
duty (`Q = η·duty_hot` via `measure_output(Capacity)`); the `ThermalLoad`
|
||||
consumes Q in r1, so the heat genuinely crosses the circuit boundary and the
|
||||
First Law closes across circuits. Keep the water-loop conditions consistent
|
||||
with the hot component's secondary stream (same T_in, ṁ, cp).
|
||||
|
||||
Full example: `crates/cli/examples/chiller_r410a_coupled_water_loop.json`.
|
||||
|
||||
---
|
||||
|
||||
## FR
|
||||
|
||||
### Modèle physique
|
||||
|
||||
`ThermalLoad` modélise un segment de charge hydronique — par exemple le côté
|
||||
eau de refroidissement d'un échangeur partagé — qui reçoit une **puissance
|
||||
thermique Q [W] déterminée extérieurement** par la couche de couplage
|
||||
thermique du solveur.
|
||||
|
||||
Il suit le pattern de frontières BOLT/Modelica
|
||||
(`BOLT.BoundaryNode.Coolant.Source → HX → Sink`) : la pression et la
|
||||
température d'entrée de la boucle sont fixées par des **composants
|
||||
frontières**, pas par la charge :
|
||||
|
||||
```text
|
||||
BrineSource(P_set, T_in) ──arête──▶ ThermalLoad ──arête──▶ BrineSink(P_back, T libre)
|
||||
```
|
||||
|
||||
La température de sortie est **émergente** : `T_out = T_in + Q / (ṁ·cp)`
|
||||
(laisser la température du sink libre — ne pas mettre `t_set_c` sur le
|
||||
`BrineSink`, sinon la boucle est surdéterminée).
|
||||
|
||||
### Équations résiduelles — `n_equations() = 2`
|
||||
|
||||
Débit imposé (`ṁ = ṁ_design`) + bilan d'énergie
|
||||
(`ṁ_design·(h_out − h_in) = Q_ext`). Le bilan utilise le débit de *conception*
|
||||
(constante) : r0 épingle déjà ṁ, et la forme constante garde le bloc linéaire
|
||||
et structurellement non singulier même si l'initialiseur part de ṁ = 0.
|
||||
|
||||
`Q_ext` est lu depuis l'inconnu d'état du couplage, câblé par
|
||||
`System::finalize()` via `set_external_heat_index`. Non câblé ⇒ `Q_ext = 0`
|
||||
(passage adiabatique).
|
||||
|
||||
### Bilan DoF (boucle d'eau)
|
||||
|
||||
Inconnues : 1 ṁ (branche partagée) + 2×(P,h) + 1 Q = 6.
|
||||
Équations : BrineSource 2 + ThermalLoad 2 + BrineSink 1 (T libre) + couplage 1 = 6. ✓
|
||||
|
||||
### `energy_transfers` et performance
|
||||
|
||||
`(Q_ext, 0)` — chaleur reçue positive. Le composant est **exclu de
|
||||
l'agrégation de performance du cycle** (`counts_in_cycle_performance() =
|
||||
false`) : le Q absorbé est la puissance rejetée du cycle primaire. Il
|
||||
participe néanmoins à la validation du 1er principe par composant.
|
||||
|
||||
### Paramètres JSON (CLI)
|
||||
|
||||
`mass_flow_kg_s` (kg/s, défaut 0.5) — débit de conception imposé.
|
||||
P et T_in se règlent sur le `BrineSource` ; P_back sur le `BrineSink`.
|
||||
|
||||
Exemple complet : `crates/cli/examples/chiller_r410a_coupled_water_loop.json`.
|
||||
Reference in New Issue
Block a user