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>
5.6 KiB
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:
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
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
"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 :
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.