Files
Entropyk/apps/web/public/docs/components/thermal-load.md
sepehr 3358b74342 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>
2026-07-17 22:46:46 +02:00

158 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`.