Files
Entropyk/apps/web/src/lib/componentMeta.ts
sepehr 3808e0f11b
Some checks failed
CI / check (push) Has been cancelled
Snapshot WIP: Probe calibration path, faer LU backend, and BPHX phase-change duty.
Checkpoint incomplete calibration work (cond SDT green, evap SST failing) plus related solver/UI changes so the next pass can fix and extend safely.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-19 21:44:01 +02:00

1976 lines
80 KiB
TypeScript
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.
/**
* Entropyk component catalogue.
*
* Mirrors the backend `/api/components` endpoint. Each entry defines:
* - type: CLI JSON "type" string (matches crates/cli/src/run.rs create_component)
* - label: human-readable name shown in the palette and node
* - category: palette grouping
* - ports: ordered port names (inlet/outlet/suction/discharge/...)
* — used to place React Flow handles.
* - color: hex colour for the node header
* - params: editable parameters with type, unit, default, required flag
*/
export type ParamKind = "number" | "string" | "boolean";
export interface ParamMeta {
key: string;
label: string;
kind: ParamKind;
unit?: string;
default?: number | string | boolean;
required?: boolean;
section?: string;
description?: string;
min?: number;
max?: number;
step?: number;
options?: Array<{ value: string | number; label: string }>;
advanced?: boolean;
/**
* Dymola/EES-style Fixed checkbox.
* Fixed ON = value imposed (parameter or measured target).
* Fixed OFF = free for the solver (calibration tuner / free unknown).
*/
fixable?: boolean;
/** Default for the Fixed checkbox when `fixable` (true = Fixed). */
defaultFixed?: boolean;
/**
* When Fixed ON: use this param value as a control setpoint for the named
* measure output (e.g. `saturationTemperature`, `superheat`).
*/
measureOutput?: string;
/**
* When Fixed OFF: free this actuator/calibration factor (e.g. `z_ua`).
* Requires a Fixed measure on the same component to stay DoF-balanced.
*/
actuatorFactor?: string;
/** Bounds used when the factor is free (actuator min/max). */
freeMin?: number;
freeMax?: number;
/**
* Whether this param can be picked as a Multi-run sweep axis.
* Source of truth for sweepability — replaces the old hardcoded whitelist.
*/
sweepable?: boolean;
/** Multi-run optgroup for the param (only meaningful when `sweepable`). */
sweepGroup?: "boundaries" | "thermal" | "machine" | "global";
}
/** Internal params key prefix for Fixed flags (not sent as component fields). */
export const FIXED_FLAG_PREFIX = "__fixed_";
export function fixedFlagKey(paramKey: string): string {
return `${FIXED_FLAG_PREFIX}${paramKey}`;
}
/** Whether a param is Fixed (default = defaultFixed ?? true). */
export function isParamFixed(
params: Record<string, number | string | boolean | undefined>,
meta: ParamMeta,
): boolean {
if (!meta.fixable) return true;
const flag = params[fixedFlagKey(meta.key)];
if (flag === true || flag === "true") return true;
if (flag === false || flag === "false") return false;
return meta.defaultFixed !== false;
}
export interface ComponentMeta {
type: string;
label: string;
category: string;
/** Short purpose shown under the component title (plain language). */
description: string;
/**
* Contextual help (French) shown in the Help panel.
* Explain when to use it, main ports, and typical Fixed/Free usage.
*/
help?: string;
/** Doc file under docs/components/ (without path), e.g. "flooded-evaporator". */
docSlug?: string;
ports: string[];
color: string;
params: ParamMeta[];
}
export function genericMeta(type?: string): ComponentMeta {
const cleanType = String(type || "GenericComponent");
return {
type: cleanType,
label: cleanType,
category: "Generic",
description: `Composant thermodynamique (${cleanType})`,
ports: ["inlet", "outlet"],
color: "#64748b",
params: [],
};
}
/**
* Port side on the node: "inlet"-like ports go on the left, "outlet"-like
* ports on the right. Economizer / liquid_outlet sit on the top/bottom.
*/
export function portSide(port: string): "left" | "right" | "top" | "bottom" {
const p = String(port || "");
if (p === "secondary_inlet") return "bottom";
if (p === "secondary_outlet") return "top";
if (p === "hot_inlet" || p === "cold_inlet") return "left";
if (p === "hot_outlet" || p === "cold_outlet") return "right";
if (p.startsWith("outlet")) return "right";
if (p === "discharge") return "right";
if (p === "liquid_outlet" || p === "vapor_outlet") return "bottom";
if (p.startsWith("inlet")) return "left";
if (p === "suction") return "left";
if (p === "economizer") return "top";
return "left";
}
/** True for the secondary (heat-transfer fluid / caloporteur) ports of an exchanger. */
export function isSecondaryPort(port: string): boolean {
const p = String(port || "");
return p === "secondary_inlet" || p === "secondary_outlet";
}
/**
* Whether a port emits flow (source) or receives it (target).
* Flow in a P&ID is directional: a connection is dragged from a source
* (outlet/discharge) to a target (inlet/suction).
*/
export function portRole(port: string): "source" | "target" {
const p = String(port || "");
if (
p.startsWith("outlet") ||
p === "discharge" ||
p === "liquid_outlet" ||
p === "vapor_outlet" ||
p.endsWith("_outlet")
) {
return "source";
}
return "target";
}
/**
* Per-family node footprint (px), Dymola-style: a heat exchanger is a wide
* vessel, a separator is a tall column, a pipe is a long thin run, a valve is
* compact. Size encodes what the part is, not just decoration.
*/
export function nodeSize(type: string): { w: number; h: number } {
const t = String(type || "");
if (t === "Probe") return { w: 36, h: 36 };
if (t === "SaturatedController") return { w: 184, h: 96 };
if (t.includes("Compressor")) return { w: 76, h: 76 };
if (t === "HeatExchanger") return { w: 92, h: 92 };
// Flooded is a horizontal drum; plate a tall pack; coils wide batteries.
if (t.includes("Flooded")) return { w: 128, h: 92 };
if (t.includes("Bphx")) return { w: 80, h: 110 };
if (t.includes("FinCoil") || t.includes("AirCooled") || t.includes("Mchx"))
return { w: 126, h: 88 };
if (t.includes("Condenser") || t.includes("Evaporator")) return { w: 126, h: 88 };
if (t.includes("Valve")) return { w: 56, h: 56 };
if (t === "Pump" || t === "Fan") return { w: 72, h: 72 };
if (t === "Pipe" || t.includes("Pipe") || t === "AirDuct") return { w: 118, h: 42 };
if (t === "Drum") return { w: 58, h: 108 };
if (t === "FlowSplitter" || t === "FlowMerger") return { w: 72, h: 64 };
if (t.endsWith("Source") || t.endsWith("Sink")) return { w: 62, h: 56 };
return { w: 88, h: 58 };
}
/** Short label shown next to a handle on the node. */
export function portLabel(port: string): string {
const map: Record<string, string> = {
inlet: "in",
outlet: "out",
suction: "suc",
discharge: "dis",
economizer: "eco",
liquid_outlet: "liq",
vapor_outlet: "vap",
outlet_a: "A",
outlet_b: "B",
inlet_a: "A",
inlet_b: "B",
hot_inlet: "h·in",
hot_outlet: "h·out",
cold_inlet: "c·in",
cold_outlet: "c·out",
secondary_inlet: "htf·in",
secondary_outlet: "htf·out",
};
return map[port] ?? port;
}
export const COMPONENT_CATEGORIES = [
"Compressors",
"Heat Exchangers",
"Expansion",
"Piping",
"Hydraulics",
"Boundaries",
"Instrumentation",
"Advanced",
"Generic",
] as const;
export const COMPONENTS: ComponentMeta[] = [
// ── Instrumentation: measurement taps for calibration (Probe = all measurements) ──
{
type: "Probe",
label: "Probe (sonde)",
category: "Instrumentation",
description:
"Sonde de mesure posée sur une ligne. Lit P/T/SH/SC/SST/SDT/DGT/DSH/capacité/ṁ/h à cet endroit. Toute calibration impose sa cible sur un Probe.",
help:
"Sonde de calibration (Probe)\n" +
"Toute calibration se fait via un Probe posé sur une ligne (règle dure).\n\n" +
"1. Glisse le Probe sur un fil du schéma (comme pour insérer un Pipe).\n" +
"2. Choisis la grandeur mesurée (SST, SDT, DGT, DSH, SH, SC, T, P, débit, capacité, enthalpie) et le fluide.\n" +
"3. Coche Fixed sur « Cible » et tape la valeur mesurée.\n" +
"4. Libère le Z-factor correspondant (Z_UA, Z_dP, Z_flow…) sur le composant à calibrer.\n" +
"Le solveur appaire automatiquement le Probe (mesure) et le Z-factor libre (inconnue).",
ports: ["inlet", "outlet"],
color: "#0ea5e9",
params: [
{
key: "measure",
label: "Grandeur mesurée",
kind: "string",
default: "SH",
required: true,
section: "Mesure",
description:
"Grandeur physique mesurée à l'emplacement du Probe. SST/SDT = Tsat(P), SH/DSH = TTsat(P), SC = Tsat(P)T.",
options: [
{ value: "SST", label: "SST (Tsat aspiration)" },
{ value: "SDT", label: "SDT (Tsat refoulement)" },
{ value: "DGT", label: "DGT (T° gaz refoulement)" },
{ value: "DSH", label: "DSH (surch. refoulement)" },
{ value: "SH", label: "SH (surchauffe)" },
{ value: "SC", label: "SC (sous-refroid.)" },
{ value: "T", label: "T (température)" },
{ value: "P", label: "P (pression)" },
{ value: "MassFlow", label: "Débit massique" },
{ value: "Capacity", label: "Capacité (kW)" },
{ value: "Enthalpy", label: "Enthalpie" },
],
},
{
key: "fluid",
label: "Fluide",
kind: "string",
default: "R134a",
required: true,
section: "Mesure",
description: "Fluide frigorigène pour le calcul de Tsat/SH/SC (via CoolProp).",
},
{
key: "target",
label: "Cible (Fixed = impose)",
kind: "number",
section: "Calibration",
fixable: true,
defaultFixed: false,
// Auto: resolved from `measure` at config-build time
// (SST/SDT → saturationTemperature, SH/DSH → superheat, SC → subcooling,
// DGT/T → temperature, P → pressure, MassFlow → massFlowRate,
// Capacity → capacity, Enthalpy → heatTransferRate as carrier).
measureOutput: "auto",
description:
"Coche Fixed et tape la valeur mesurée (K pour températures, K pour SH/SC, Pa pour P, kg/s pour débit, W pour capacité, J/kg pour h).",
},
],
},
// ── Advanced regulation (optional — most users use Fixed on Calibration tab) ──
{
type: "SaturatedController",
label: "Regulation loop (advanced)",
category: "Advanced",
description:
"Optional. Links a measured quantity (e.g. superheat) to a free actuator (e.g. valve opening). For simple calibration (SST + Z_UA), prefer the Calibration tab Fixed checkboxes — you do not need this block.",
help:
"À quoi ça sert ?\n" +
"• Boucle de régulation avancée : tu imposes une grandeur mesurée et tu libères un actionneur.\n" +
"• Exemple : superheat mesuré → ouverture EXV libre.\n\n" +
"Quand NE PAS lutiliser ?\n" +
"• Pour calibrer Z_UA sur une SST : utilise longlet Calibration du composant (cases Fixed).\n" +
"• Ce bloc est pour les cas multi-objectifs / priorités (override chain).\n\n" +
"Ce nest pas un composant thermodynamique : il na pas de ports fluide.",
docSlug: undefined,
ports: [],
color: "#7c3aed",
params: [
{
key: "measure_component",
label: "Composant mesuré",
kind: "string",
default: "evap",
required: true,
section: "Mesure (imposé)",
description: "Nom du composant dont on lit la grandeur (ex. evap).",
},
{
key: "measure_output",
label: "Grandeur mesurée",
kind: "string",
default: "saturationTemperature",
required: true,
section: "Mesure (imposé)",
description: "Ex. saturationTemperature (SST/SDT), superheat, capacity…",
},
{
key: "target",
label: "Consigne",
kind: "number",
default: 278.15,
required: true,
section: "Mesure (imposé)",
description: "Valeur cible en SI : K pour températures, Pa, W, kg/s…",
},
{
key: "gain",
label: "Gain",
kind: "number",
default: -0.5,
section: "Mesure (imposé)",
description: "Signe et échelle de lerreur (consigne mesure).",
},
{
key: "actuator_component",
label: "Composant actionné",
kind: "string",
default: "evap",
required: true,
section: "Actionneur (libre)",
description: "Composant dont on libère un facteur (souvent le même que mesuré).",
},
{
key: "actuator_factor",
label: "Facteur libre",
kind: "string",
default: "z_ua",
required: true,
section: "Actionneur (libre)",
description: "z_ua, opening, z_flow, z_power… Valeur initiale ci-dessous = point de départ.",
},
{
key: "initial",
label: "Valeur initiale",
kind: "number",
default: 1.0,
required: true,
section: "Actionneur (libre)",
description: "Départ du facteur libre (ex. Z_UA = 1).",
},
{
key: "min",
label: "Minimum",
kind: "number",
default: 0.2,
required: true,
section: "Actionneur (libre)",
},
{
key: "max",
label: "Maximum",
kind: "number",
default: 3.0,
required: true,
section: "Actionneur (libre)",
},
{ key: "band", label: "Bande de saturation", kind: "number", default: 5.0, section: "Réglages fins", min: 0.000001, advanced: true },
{ key: "smooth_eps", label: "Lissage saturation", kind: "number", default: 0.001, section: "Réglages fins", min: 0.000000001, advanced: true },
{ key: "alpha", label: "Lissage overrides", kind: "number", default: 0.001, section: "Réglages fins", min: 0.000000001, advanced: true },
],
},
// ── Compressors ────────────────────────────────────────────────────────
{
type: "IsentropicCompressor",
label: "Isentropic Compressor",
category: "Compressors",
description: "Compresseur physique : η_is + (option) ṁ = ρ·V·N·η_vol.",
help:
"Modèle : compression isentropique corrigée η_is.\n" +
"h_dis = h_suc + (h_is h_suc) / η_is\n" +
"Mode émergent : ṁ = ρ_suc · V_d · N · η_vol · z_flow\n" +
"T_cond / T_evap = guesses dinit si pressions émergentes côté HX.\n" +
"Doc : docs/components/isentropic-compressor.md · Inventaire : correlations-and-maps.md",
docSlug: "isentropic-compressor",
ports: ["inlet", "outlet"],
color: "#6366f1",
params: [
{
key: "isentropic_efficiency",
label: "η isentropique",
kind: "number",
default: 0.75,
required: true,
section: "Model",
min: 0.05,
max: 1.0,
description: "η_is : h_dis = h_suc + (h_ish_suc)/η_is",
sweepable: true,
sweepGroup: "machine",
},
{
key: "emergent_pressure",
label: "Pression émergente (ṁ par cylindrée)",
kind: "boolean",
default: true,
section: "Model",
description: "ON : ṁ = ρ·V·N·η_vol (recommandé machine réelle).",
},
{
key: "displacement_m3",
label: "Cylindrée V_d",
kind: "number",
unit: "m³/tr",
default: 5.0e-5,
section: "Displacement map",
min: 0,
description: "Volume balayé par tour — requis si pression émergente.",
},
{
key: "speed_hz",
label: "Vitesse N",
kind: "number",
unit: "tr/s",
default: 50.0,
section: "Displacement map",
min: 0,
},
{
key: "volumetric_efficiency",
label: "η volumétrique",
kind: "number",
default: 0.92,
section: "Displacement map",
min: 0.05,
max: 1.0,
},
{
key: "t_cond_k",
label: "T_cond guess",
kind: "number",
unit: "K",
default: 323.15,
section: "Init / design",
description: "Guess dinit (ou pin si non émergent).",
},
{
key: "t_evap_k",
label: "T_evap guess",
kind: "number",
unit: "K",
default: 275.15,
section: "Init / design",
},
{
key: "superheat_k",
label: "Superheat aspiration guess",
kind: "number",
unit: "K",
default: 5.0,
section: "Init / design",
},
],
},
{
type: "Compressor",
label: "Map Compressor (AHRI / SST-SDT)",
category: "Compressors",
description: "Deux modèles : AHRI 540 (M1M10) ou polynôme bilinéaire SST/SDT.",
help:
"Choisis model_type :\n" +
"• Ahri540 : ṁ = M1·(1(Ps/Pd)^(1/M2))·ρ·V·N/60 ; Ẇ = M3…M10\n" +
"• SstSdt : ṁ,Ẇ = a00 + a10·SST + a01·SDT + a11·SST·SDT (SST/SDT en K)\n" +
"Doc : compressor-ahri540.md · correlations-and-maps.md",
docSlug: "compressor-ahri540",
ports: ["inlet", "outlet"],
color: "#6366f1",
params: [
{
key: "model_type",
label: "Modèle de carte",
kind: "string",
default: "Ahri540",
section: "Model",
required: true,
options: [
{ value: "Ahri540", label: "AHRI 540 (M1M10)" },
{ value: "SstSdt", label: "Polynôme SST / SDT (bilinéaire)" },
],
description: "Ahri540 = coefficients M1M10. SstSdt = carte constructeur ṁ(SST,SDT), Ẇ(SST,SDT).",
},
{
key: "fluid",
label: "Frigorigène",
kind: "string",
default: "R134a",
section: "Machine",
required: true,
},
{
key: "speed_rpm",
label: "Vitesse",
kind: "number",
unit: "tr/min",
default: 3000,
required: true,
section: "Machine",
min: 1,
sweepable: true,
sweepGroup: "machine",
},
{
key: "displacement_m3",
label: "Cylindrée",
kind: "number",
unit: "m³",
default: 0.0005,
required: true,
section: "Machine",
min: 0,
},
{
key: "efficiency",
label: "Efficacité",
kind: "number",
default: 0.85,
section: "Machine",
min: 0.05,
max: 1,
},
// AHRI 540
{ key: "m1", label: "M1 (flow scale)", kind: "number", default: 0.85, section: "AHRI 540 (si Ahri540)", description: "ṁ ∝ M1·(1(Ps/Pd)^(1/M2))" },
{ key: "m2", label: "M2 (PR exponent)", kind: "number", default: 2.5, section: "AHRI 540 (si Ahri540)", min: 0.01 },
{ key: "m3", label: "M3 (Ẇ cool const)", kind: "number", default: 500, section: "AHRI 540 (si Ahri540)", unit: "W" },
{ key: "m4", label: "M4 (Ẇ cool · PR)", kind: "number", default: 1500, section: "AHRI 540 (si Ahri540)" },
{ key: "m5", label: "M5 (Ẇ cool · Ts)", kind: "number", default: -2.5, section: "AHRI 540 (si Ahri540)" },
{ key: "m6", label: "M6 (Ẇ cool · Td)", kind: "number", default: 1.8, section: "AHRI 540 (si Ahri540)" },
{ key: "m7", label: "M7 (Ẇ heat const)", kind: "number", default: 600, section: "AHRI 540 (si Ahri540)", unit: "W", advanced: true },
{ key: "m8", label: "M8 (Ẇ heat · PR)", kind: "number", default: 1600, section: "AHRI 540 (si Ahri540)", advanced: true },
{ key: "m9", label: "M9 (Ẇ heat · Ts)", kind: "number", default: -3.0, section: "AHRI 540 (si Ahri540)", advanced: true },
{ key: "m10", label: "M10 (Ẇ heat · Td)", kind: "number", default: 2.0, section: "AHRI 540 (si Ahri540)", advanced: true },
// SST/SDT bilinear
{ key: "mf_a00", label: "ṁ a00", kind: "number", default: 0.05, section: "Map SST/SDT (si SstSdt)", description: "ṁ = a00 + a10·SST + a01·SDT + a11·SST·SDT (K)" },
{ key: "mf_a10", label: "ṁ a10 (×SST)", kind: "number", default: 0.001, section: "Map SST/SDT (si SstSdt)" },
{ key: "mf_a01", label: "ṁ a01 (×SDT)", kind: "number", default: 0.0005, section: "Map SST/SDT (si SstSdt)" },
{ key: "mf_a11", label: "ṁ a11 (×SST·SDT)", kind: "number", default: 0.00001, section: "Map SST/SDT (si SstSdt)" },
{ key: "pw_b00", label: "Ẇ b00", kind: "number", default: 1000, unit: "W", section: "Map SST/SDT (si SstSdt)", description: "Ẇ = b00 + b10·SST + b01·SDT + b11·SST·SDT (K)" },
{ key: "pw_b10", label: "Ẇ b10 (×SST)", kind: "number", default: 50, section: "Map SST/SDT (si SstSdt)" },
{ key: "pw_b01", label: "Ẇ b01 (×SDT)", kind: "number", default: 30, section: "Map SST/SDT (si SstSdt)" },
{ key: "pw_b11", label: "Ẇ b11 (×SST·SDT)", kind: "number", default: 0.5, section: "Map SST/SDT (si SstSdt)" },
],
},
{
type: "CentrifugalCompressor",
label: "Centrifugal Compressor",
category: "Compressors",
description: "Centrifugal compressor with φMach polytropic map (CLI-wired).",
help:
"Carte φ / Mach → tête polytropique et η_p. VFD via speed_rpm.\n" +
"Modèle encore simplifié côté solveur (Jacobien partiel).",
ports: ["inlet", "outlet"],
color: "#4338ca",
params: [
{ key: "diameter_m", label: "Impeller diameter", kind: "number", unit: "m", default: 0.25, required: true },
{ key: "speed_rpm", label: "Speed", kind: "number", unit: "rpm", default: 9000.0, required: true, sweepable: true, sweepGroup: "machine" },
{ key: "gas_constant", label: "Gas constant R", kind: "number", unit: "J/kg·K", default: 188.9, advanced: true },
{ key: "gamma", label: "Heat capacity ratio γ", kind: "number", default: 1.12, advanced: true },
],
},
{
type: "ScrewEconomizerCompressor",
label: "Screw Compressor (Economizer)",
category: "Compressors",
description: "Vis 3 ports — polynôme bilinéaire SST/SDT + presets.",
help:
"Carte bilinéaire :\n" +
"ṁ = a00 + a10·SST + a01·SDT + a11·SST·SDT\n" +
"Ẇ = b00 + b10·SST + b01·SDT + b11·SST·SDT\n" +
"Presets : bitzer_generic_200kw, grasso_generic_200kw.\n" +
"Ports : suction, discharge, economizer.\n" +
"Doc : screw-economizer-compressor.md · correlations-and-maps.md",
docSlug: "screw-economizer-compressor",
ports: ["suction", "discharge", "economizer"],
color: "#4f46e5",
params: [
{
key: "preset",
label: "Preset carte",
kind: "string",
default: "bitzer_generic_200kw",
section: "Model",
options: [
{ value: "bitzer_generic_200kw", label: "Bitzer generic ~200 kW" },
{ value: "grasso_generic_200kw", label: "Grasso generic ~200 kW" },
{ value: "", label: "Custom (defaults)" },
],
description: "Remplit les coeffs mf_* / pw_* (surchargeables).",
},
{
key: "frequency_hz",
label: "Fréquence",
kind: "number",
unit: "Hz",
default: 50.0,
section: "Machine",
sweepable: true,
sweepGroup: "machine",
},
{
key: "nominal_frequency_hz",
label: "Fréquence nominale",
kind: "number",
unit: "Hz",
default: 50.0,
section: "Machine",
},
{
key: "mechanical_efficiency",
label: "η mécanique",
kind: "number",
default: 0.92,
section: "Machine",
min: 0.05,
max: 1,
},
{
key: "economizer_fraction",
label: "Fraction éco ṁ",
kind: "number",
default: 0.13,
section: "Machine",
min: 0,
max: 1,
},
{
key: "volume_index",
label: "Volume index Vi",
kind: "number",
default: 2.8,
section: "Machine",
min: 1.1,
max: 6,
description: "Built-in Vi; slide valve reduces effective Vi.",
sweepable: true,
sweepGroup: "machine",
},
{
key: "slide_valve_position",
label: "Slide valve (01)",
kind: "number",
default: 1.0,
section: "Machine",
min: 0.05,
max: 1,
sweepable: true,
sweepGroup: "machine",
},
// Mass-flow bilinear (Bitzer defaults as numbers)
{ key: "mf_a00", label: "ṁ a00", kind: "number", default: 1.35, section: "Map ṁ (SST,SDT)", description: "ṁ = a00 + a10·SST + a01·SDT + a11·SST·SDT" },
{ key: "mf_a10", label: "ṁ a10 (×SST)", kind: "number", default: 0.004, section: "Map ṁ (SST,SDT)" },
{ key: "mf_a01", label: "ṁ a01 (×SDT)", kind: "number", default: -0.0025, section: "Map ṁ (SST,SDT)" },
{ key: "mf_a11", label: "ṁ a11 (×SST·SDT)", kind: "number", default: 0.000012, section: "Map ṁ (SST,SDT)" },
{ key: "pw_b00", label: "Ẇ b00", kind: "number", default: 58000, unit: "W", section: "Map Ẇ (SST,SDT)", description: "Ẇ = b00 + b10·SST + b01·SDT + b11·SST·SDT" },
{ key: "pw_b10", label: "Ẇ b10 (×SST)", kind: "number", default: 180, section: "Map Ẇ (SST,SDT)" },
{ key: "pw_b01", label: "Ẇ b01 (×SDT)", kind: "number", default: -280, section: "Map Ẇ (SST,SDT)" },
{ key: "pw_b11", label: "Ẇ b11 (×SST·SDT)", kind: "number", default: 0.4, section: "Map Ẇ (SST,SDT)" },
],
},
// ── Heat exchangers ────────────────────────────────────────────────────
{
type: "Condenser",
label: "Condenser",
category: "Heat Exchangers",
description: "Condenseur frigo avec secondaire eau/air (ε-NTU).",
help:
"Rôle : rejette la chaleur du frigorigène vers leau ou lair.\n\n" +
"Ports : inlet/outlet frigo · secondary_in/out caloporteur.\n\n" +
"ΔP frigo : dp_model=msh (MüllerSteinhagenHeck + accélération) avec géométrie tube, " +
"ou quadratic (dp_nominal), ou isobaric.\n\n" +
"Machine réelle : cocher « Solve condensing pressure » et câbler Source/Sink secondaires.\n" +
"Calibration : Fixed sur SDT + Z_UA libre (Z_UA défaut = 1).",
docSlug: "condenser",
ports: ["inlet", "outlet", "secondary_inlet", "secondary_outlet"],
color: "#ef4444",
params: [
{ key: "manufacturer", label: "Manufacturer", kind: "string", default: "", section: "Identification" },
{ key: "model", label: "Model / reference", kind: "string", default: "", section: "Identification" },
{ key: "fluid", label: "Refrigerant", kind: "string", default: "R134a", section: "Fluids" },
{ key: "secondary_fluid", label: "Secondary fluid", kind: "string", default: "Water", section: "Fluids" },
{ key: "ua", label: "UA", kind: "number", unit: "W/K", default: 5000.0, required: true, section: "Thermal model", min: 0.0, sweepable: true, sweepGroup: "thermal" },
{ key: "t_sat_k", label: "Fixed saturation temperature", kind: "number", unit: "K", default: 323.15, section: "Thermal model", description: "Used only in fixed-pressure mode. Do not treat it as a solved-cycle input when emergent pressure is enabled." },
{
key: "secondary_inlet_temp_c",
label: "Secondary inlet T (rating only)",
kind: "number",
unit: "°C",
default: 30.0,
section: "Secondary side (rating)",
description: "Rating scalar. Prefer live secondary ports + BrineSource for system mode.",
},
{
key: "secondary_mass_flow_kg_s",
label: "Secondary mass flow (rating only)",
kind: "number",
unit: "kg/s",
default: 2.0,
section: "Secondary side (rating)",
min: 0.0,
},
{
key: "secondary_cp_j_per_kgk",
label: "Secondary specific heat (rating only)",
kind: "number",
unit: "J/kg·K",
default: 4186.0,
section: "Secondary side (rating)",
min: 0.0,
advanced: true,
},
{ key: "secondary_humidity_ratio", label: "Secondary humidity ratio", kind: "number", unit: "kg/kg", default: 0.0, section: "Secondary side (rating)", min: 0.0, advanced: true },
{
key: "dp_model",
label: "Refrigerant ΔP model",
kind: "string",
default: "msh",
section: "Hydraulics",
options: [
{ value: "msh", label: "MSH 1986 (tube, + accel)" },
{ value: "friedel", label: "Friedel 1979 (tube, + accel)" },
{ value: "quadratic", label: "Quadratic (dp_nominal)" },
{ value: "isobaric", label: "Isobaric (ΔP = 0)" },
],
description:
"MSH = NIST EVAP-COND default two-phase friction at mean quality + acceleration. Quadratic = Modelica Buildings k·ṁ². Flooded shell-side → isobaric.",
},
{ key: "tube_length_m", label: "Tube length", kind: "number", unit: "m", default: 6.0, section: "Hydraulics", min: 0.0, description: "Path length for MSH/Friedel." },
{ key: "tube_diameter_m", label: "Tube diameter", kind: "number", unit: "m", default: 0.0095, section: "Hydraulics", min: 0.0, advanced: true },
{ key: "n_parallel_tubes", label: "Parallel tubes", kind: "number", default: 2, section: "Hydraulics", min: 1, advanced: true },
{
key: "rated_pressure_drop_pa",
label: "Design ΔP (quadratic)",
kind: "number",
unit: "Pa",
default: 15000,
section: "Hydraulics",
min: 0.0,
advanced: true,
description: "Only when dp_model=quadratic. Calibrates k = ΔP/ṁ².",
},
{
key: "rated_mass_flow_kg_s",
label: "Design ṁ (quadratic)",
kind: "number",
unit: "kg/s",
default: 0.05,
section: "Hydraulics",
min: 0.0,
advanced: true,
},
{
key: "pressure_drop_coeff_pa_s2_kg2",
label: "Pressure-drop coefficient k",
kind: "number",
unit: "Pa·s²/kg²",
section: "Hydraulics",
min: 0.0,
advanced: true,
description: "Direct k for quadratic mode.",
},
{
key: "secondary_rated_pressure_drop_pa",
label: "Water/air design ΔP",
kind: "number",
unit: "Pa",
default: 30000,
section: "Secondary hydraulics",
min: 0.0,
description:
"Secondary-side ΔP at rated ṁ (Modelica dp_nominal). NOT the refrigerant tube MSH model. 0 = isobaric water path.",
},
{
key: "secondary_rated_m_flow_kg_s",
label: "Water/air design ṁ",
kind: "number",
unit: "kg/s",
default: 0.45,
section: "Secondary hydraulics",
min: 0.0,
description: "Pairs with Water/air design ΔP → k = ΔP/ṁ².",
},
{ key: "emergent_pressure", label: "Solve condensing pressure", kind: "boolean", default: true, section: "Solved-cycle mode", description: "ON (recommandé machine): P_cond émerge du bilan — pas un Dirichlet fixe (esprit Modelica cycle fermé)." },
{ key: "subcooling_k", label: "Outlet subcooling closure", kind: "number", unit: "K", default: 5.0, section: "Solved-cycle mode", min: 0.0, description: "FIX residual when emergent_pressure is ON (+1 eq, closes P_cond with energy balance)." },
{ key: "fan_head_pressure_target_c", label: "Fan head-pressure target", kind: "number", unit: "°C", section: "Controls", advanced: true },
{ key: "flooded_head_pressure_target_c", label: "Flooded head-pressure target", kind: "number", unit: "°C", section: "Controls", advanced: true },
{ key: "skip_pressure_eq", label: "Skip pressure equation", kind: "boolean", default: false, section: "Solver structure", advanced: true },
{
key: "z_ua",
label: "Z_UA (UA factor)",
kind: "number",
default: 1.0,
section: "Calibration",
min: 0.000001,
fixable: true,
defaultFixed: true,
actuatorFactor: "z_ua",
freeMin: 0.2,
freeMax: 3.0,
description: "Fixed = valeur figée. Décocher Fixed = libre (calibration).",
},
{
key: "z_dp",
label: "Z_dP (pressure-drop factor)",
kind: "number",
default: 1.0,
section: "Calibration",
min: 0.000001,
advanced: true,
fixable: true,
defaultFixed: true,
actuatorFactor: "z_dp",
freeMin: 0.2,
freeMax: 3.0,
},
],
},
{
type: "Evaporator",
label: "Evaporator",
category: "Heat Exchangers",
description: "Évaporateur DX (détente directe) avec secondaire eau/brine.",
help:
"Rôle : évaporateur à détente directe (sortie surchauffée).\n\n" +
"Ports : inlet/outlet frigo · secondary_in/out.\n\n" +
"Différence flooded : ici la sortie nest pas noyée — superheat en sortie.\n" +
"Calibration : Fixed sur SST + Z_UA libre (défaut Z_UA = 1).",
docSlug: "evaporator",
ports: ["inlet", "outlet", "secondary_inlet", "secondary_outlet"],
color: "#3b82f6",
params: [
{ key: "manufacturer", label: "Manufacturer", kind: "string", default: "", section: "Identification" },
{ key: "model", label: "Model / reference", kind: "string", default: "", section: "Identification" },
{ key: "fluid", label: "Refrigerant", kind: "string", default: "R134a", section: "Fluids" },
{ key: "secondary_fluid", label: "Secondary fluid", kind: "string", default: "Water", section: "Fluids" },
{ key: "ua", label: "UA", kind: "number", unit: "W/K", default: 6000.0, required: true, section: "Thermal model", min: 0.0, sweepable: true, sweepGroup: "thermal" },
{ key: "t_sat_k", label: "Fixed saturation temperature", kind: "number", unit: "K", default: 275.15, section: "Thermal model", description: "Used only in fixed-pressure mode." },
{ key: "superheat_k", label: "Outlet superheat closure", kind: "number", unit: "K", default: 5.0, section: "Thermal model", min: 0.0 },
{
key: "z_ua",
label: "Z_UA (UA factor)",
kind: "number",
default: 1.0,
section: "Calibration",
min: 0.000001,
fixable: true,
defaultFixed: true,
actuatorFactor: "z_ua",
freeMin: 0.2,
freeMax: 3.0,
description: "Fixed = figé. Décocher Fixed = libre pour le solveur.",
},
{
key: "z_dp",
label: "Z_dP (pressure-drop factor)",
kind: "number",
default: 1.0,
section: "Calibration",
min: 0.000001,
advanced: true,
fixable: true,
defaultFixed: true,
actuatorFactor: "z_dp",
freeMin: 0.2,
freeMax: 3.0,
},
{
key: "secondary_inlet_temp_c",
label: "Secondary inlet T (rating only)",
kind: "number",
unit: "°C",
default: 12.0,
section: "Secondary side (rating)",
description: "Rating scalar. Prefer live secondary ports + BrineSource for system mode.",
},
{
key: "secondary_mass_flow_kg_s",
label: "Secondary mass flow (rating only)",
kind: "number",
unit: "kg/s",
default: 1.5,
section: "Secondary side (rating)",
min: 0.0,
},
{
key: "secondary_cp_j_per_kgk",
label: "Secondary specific heat (rating only)",
kind: "number",
unit: "J/kg·K",
default: 4186.0,
section: "Secondary side (rating)",
min: 0.0,
advanced: true,
},
{ key: "secondary_humidity_ratio", label: "Secondary humidity ratio", kind: "number", unit: "kg/kg", default: 0.0, section: "Secondary side (rating)", min: 0.0, advanced: true },
{
key: "dp_model",
label: "Refrigerant ΔP model",
kind: "string",
default: "msh",
section: "Hydraulics",
options: [
{ value: "msh", label: "MSH 1986 (tube, + accel)" },
{ value: "friedel", label: "Friedel 1979 (tube, + accel)" },
{ value: "quadratic", label: "Quadratic (dp_nominal)" },
{ value: "isobaric", label: "Isobaric (ΔP = 0)" },
],
description: "DX tube ΔP. For flooded shell-side use FloodedEvaporator (ΔP ≈ 0).",
},
{ key: "tube_length_m", label: "Tube length", kind: "number", unit: "m", default: 6.0, section: "Hydraulics", min: 0.0 },
{ key: "tube_diameter_m", label: "Tube diameter", kind: "number", unit: "m", default: 0.0095, section: "Hydraulics", min: 0.0, advanced: true },
{ key: "n_parallel_tubes", label: "Parallel tubes", kind: "number", default: 2, section: "Hydraulics", min: 1, advanced: true },
{
key: "rated_pressure_drop_pa",
label: "Design ΔP (quadratic)",
kind: "number",
unit: "Pa",
default: 15000,
section: "Hydraulics",
min: 0.0,
advanced: true,
},
{
key: "rated_mass_flow_kg_s",
label: "Design ṁ (quadratic)",
kind: "number",
unit: "kg/s",
default: 0.05,
section: "Hydraulics",
min: 0.0,
advanced: true,
},
{
key: "pressure_drop_coeff_pa_s2_kg2",
label: "Pressure-drop coefficient k",
kind: "number",
unit: "Pa·s²/kg²",
section: "Hydraulics",
min: 0.0,
advanced: true,
},
{
key: "secondary_rated_pressure_drop_pa",
label: "Water/air design ΔP",
kind: "number",
unit: "Pa",
default: 30000,
section: "Secondary hydraulics",
min: 0.0,
description: "Secondary-side ΔP at rated ṁ. Distinct from refrigerant dp_model. 0 = isobaric.",
},
{
key: "secondary_rated_m_flow_kg_s",
label: "Water/air design ṁ",
kind: "number",
unit: "kg/s",
default: 0.5,
section: "Secondary hydraulics",
min: 0.0,
},
{ key: "emergent_pressure", label: "Solve evaporating pressure", kind: "boolean", default: true, section: "Solved-cycle mode", description: "ON (recommandé machine): P_evap émerge du bilan — pas un Dirichlet fixe." },
{
key: "superheat_regulated",
label: "Regulate superheat with EXV controller",
kind: "boolean",
default: false,
section: "Solved-cycle mode",
description: "Drops SH residual (1 eq). Pair with SaturatedController → EXV opening (FREE).",
},
{ key: "skip_pressure_eq", label: "Skip pressure equation", kind: "boolean", default: false, section: "Solver structure", advanced: true },
],
},
{
type: "MchxCondenserCoil",
label: "MCHX Condenser Coil",
category: "Heat Exchangers",
description: "Multi-channel heat exchanger air-cooled condenser coil.",
ports: ["inlet", "outlet", "secondary_inlet", "secondary_outlet"],
color: "#f97316",
params: [
{ key: "manufacturer", label: "Manufacturer", kind: "string", default: "", section: "Identification", description: "Supplier or coil manufacturer. Stored as project traceability data." },
{ key: "model", label: "Model / reference", kind: "string", default: "", section: "Identification", description: "Manufacturer model, selection reference, or internal equipment code." },
{ key: "ua_nominal_kw_k", label: "Nominal UA", kind: "number", unit: "kW/K", default: 15.0, required: true, section: "Thermal model", min: 0.001, description: "Overall conductance at nominal air flow." },
{ key: "n_air_exponent", label: "Air-flow exponent", kind: "number", default: 0.5, section: "Thermal model", min: 0.0, max: 2.0, description: "Exponent used to scale UA with air-flow ratio." },
{ key: "coil_index", label: "Coil index", kind: "number", default: 0, section: "Identification", min: 0, step: 1 },
{ key: "air_inlet_temp_c", label: "Air inlet temperature", kind: "number", unit: "°C", default: 35.0, section: "Air side" },
{ key: "fan_speed", label: "Fan speed ratio", kind: "number", default: 1.0, section: "Air side", min: 0.0, max: 1.0, step: 0.01, sweepable: true, sweepGroup: "machine" },
],
},
{
type: "AirCooledCondenser",
label: "Air-Cooled Condenser",
category: "Heat Exchangers",
description: "Condenser with air-side fan model. The caloporteur ports represent the ambient air stream.",
ports: ["inlet", "outlet", "secondary_inlet", "secondary_outlet"],
color: "#f59e0b",
params: [
{ key: "manufacturer", label: "Manufacturer", kind: "string", default: "", section: "Identification" },
{ key: "model", label: "Model / reference", kind: "string", default: "", section: "Identification" },
{ key: "fluid", label: "Refrigerant", kind: "string", default: "R134a", section: "Fluids" },
{ key: "ua", label: "UA at full fan", kind: "number", unit: "W/K", default: 4000.0, required: true, section: "Thermal model", min: 0.0, sweepable: true, sweepGroup: "thermal" },
{ key: "oat_k", label: "Outdoor air temperature", kind: "number", unit: "K", default: 308.15, section: "Air side", description: "Ambient dry-bulb temperature used by the condenser model.", sweepable: true, sweepGroup: "boundaries" },
{ key: "approach_k", label: "Condensing approach", kind: "number", unit: "K", default: 12.0, section: "Thermal model", min: 0.0 },
],
},
{
type: "FinCoilCondenser",
label: "Finned-Tube Condenser Coil",
category: "Heat Exchangers",
description:
"Round-tube plate-fin air-cooled condenser. UA and condensing temperature are derived from the actual coil face, tube rows, fin pitch and air velocity.",
ports: ["inlet", "outlet", "secondary_inlet", "secondary_outlet"],
color: "#d97706",
params: [
{ key: "manufacturer", label: "Manufacturer", kind: "string", default: "", section: "Identification", description: "Coil supplier or OEM." },
{ key: "model", label: "Model / reference", kind: "string", default: "", section: "Identification", description: "Supplier selection reference or equipment code." },
{ key: "fluid", label: "Refrigerant", kind: "string", default: "R134a", section: "Fluids" },
{ key: "face_width_m", label: "Coil face width", kind: "number", unit: "m", default: 1.5, required: true, section: "Coil geometry", min: 0.01 },
{ key: "face_height_m", label: "Coil face height", kind: "number", unit: "m", default: 1.0, required: true, section: "Coil geometry", min: 0.01 },
{ key: "n_rows", label: "Tube rows", kind: "number", default: 3, required: true, section: "Coil geometry", min: 1, step: 1 },
{
key: "fin_type",
label: "Fin type",
kind: "string",
default: "louvered",
section: "Fin geometry",
options: [
{ value: "louvered", label: "Louvered — Chang & Wang" },
{ value: "slit", label: "Slit fin" },
{ value: "wavy", label: "Wavy / corrugated" },
{ value: "plain", label: "Plain fin" },
],
},
{ key: "fin_pitch_fpi", label: "Fin pitch", kind: "number", unit: "FPI", default: 14.0, required: true, section: "Fin geometry", min: 1.0, description: "Number of fins per inch." },
{ key: "fin_thickness_mm", label: "Fin thickness", kind: "number", unit: "mm", default: 0.1, section: "Fin geometry", min: 0.01 },
{ key: "louver_pitch_mm", label: "Louver pitch", kind: "number", unit: "mm", default: 1.4, section: "Fin geometry", min: 0.01 },
{ key: "tube_od_mm", label: "Tube outside diameter", kind: "number", unit: "mm", default: 9.525, required: true, section: "Tube geometry", min: 0.1 },
{ key: "tube_pitch_mm", label: "Transverse tube pitch", kind: "number", unit: "mm", default: 25.4, required: true, section: "Tube geometry", min: 0.1, description: "Longitudinal pitch is derived using the staggered-tube ratio." },
{ key: "oat_k", label: "Outdoor air temperature", kind: "number", unit: "K", default: 308.15, required: true, section: "Air side", sweepable: true, sweepGroup: "boundaries" },
{ key: "air_face_velocity_m_s", label: "Air face velocity", kind: "number", unit: "m/s", default: 2.5, required: true, section: "Air side", min: 0.01, sweepable: true, sweepGroup: "boundaries" },
{ key: "design_capacity_kw", label: "Design heat rejection", kind: "number", unit: "kW", default: 100.0, required: true, section: "Design point", min: 0.01 },
{
key: "wet_surface",
label: "Wet air-side (Wang)",
kind: "boolean",
default: false,
section: "Air side",
description: "Apply Wang wet j-factor correction when condensate forms.",
},
],
},
{
type: "BphxEvaporator",
label: "Brazed Plate Evaporator",
category: "Heat Exchangers",
description: "Évaporateur à plaques brasées : géométrie + corrélation Longo/Shah → UA.",
help:
"Modèle : corrélation biphasique (Longo 2004 / Shah 1979 / Shah 2021) estime h et UA à partir des plaques, puis le solveur utilise un HX ε-NTU.\n\n" +
"DX uniquement (sortie surchauffée). Pour un noyé calandre/tubes → FloodedEvaporator.\n\n" +
"Corrélations et formules : docs/components/bphx.md",
docSlug: "bphx",
ports: ["inlet", "outlet", "secondary_inlet", "secondary_outlet"],
color: "#0891b2",
params: [
{ key: "manufacturer", label: "Manufacturer", kind: "string", default: "", section: "Identification" },
{ key: "model", label: "Model / reference", kind: "string", default: "", section: "Identification" },
{ key: "refrigerant", label: "Refrigerant", kind: "string", default: "R134a", section: "Fluids" },
{ key: "secondary_fluid", label: "Secondary fluid", kind: "string", default: "Water", section: "Fluids", description: "Water, brine, or glycol solution connected to the secondary ports." },
{ key: "n_plates", label: "Number of plates", kind: "number", default: 20, required: true, section: "Plate pack", min: 2, step: 1 },
{ key: "plate_length_m", label: "Effective plate length", kind: "number", unit: "m", default: 0.25, section: "Plate pack", min: 0.001 },
{ key: "plate_width_m", label: "Effective plate width", kind: "number", unit: "m", default: 0.05, section: "Plate pack", min: 0.001 },
{ key: "plate_thickness_mm", label: "Plate thickness", kind: "number", unit: "mm", default: 0.6, section: "Plate pack", min: 0.01 },
{ key: "channel_spacing_mm", label: "Channel spacing", kind: "number", unit: "mm", default: 1.5, section: "Corrugation", min: 0.01 },
{ key: "chevron_angle_deg", label: "Chevron angle", kind: "number", unit: "°", default: 60.0, section: "Corrugation", min: 10.0, max: 80.0 },
{ key: "corrugation_pitch_mm", label: "Corrugation pitch", kind: "number", unit: "mm", default: 3.0, section: "Corrugation", min: 0.01 },
{ key: "dh_m", label: "Hydraulic diameter override", kind: "number", unit: "m", section: "Direct manufacturer data", min: 0.000001, advanced: true, description: "Optional direct override. Use together with heat-transfer area when the supplier provides both values." },
{ key: "area_m2", label: "Heat-transfer area override", kind: "number", unit: "m²", section: "Direct manufacturer data", min: 0.000001, advanced: true },
{ key: "target_superheat_k", label: "Target superheat", kind: "number", unit: "K", default: 5.0, section: "Operating model", min: 0.0, description: "Brazed plate evaporators are Direct Expansion (DX) only in this project — the refrigerant outlet is always superheated. A flooded design is a system-level property (Drum + recirculation loop); use the Flooded Evaporator (shell-and-tube) component for that." },
{
key: "emergent_pressure",
label: "Solve evaporating pressure",
kind: "boolean",
default: true,
section: "Solved-cycle mode",
description:
"ON: +1 outlet-closure residual pins h_out to target superheat so P_evap emerges from the secondary balance.",
},
{
key: "correlation",
label: "Two-phase correlation",
kind: "string",
default: "Longo2004",
section: "Heat-transfer model",
options: [
{ value: "Longo2004", label: "Longo 2004" },
{ value: "Shah1979", label: "Shah 1979" },
{ value: "Shah2021", label: "Shah 2021" },
],
},
{
key: "dp_correlation",
label: "Pressure-drop correlation",
kind: "string",
default: "SimplifiedChannel",
section: "Pressure drop",
description:
"Channel friction on both hot and cold sides (independent of HTC). Default SimplifiedChannel; Martin1996 uses chevron angle.",
options: [
{ value: "SimplifiedChannel", label: "Simplified channel (default)" },
{ value: "Martin1996", label: "Martin 1996 (chevron)" },
],
},
{ key: "ua", label: "UA override", kind: "number", unit: "W/K", section: "Calibration", min: 0.0, advanced: true },
{
key: "z_ua",
label: "Z_UA (UA factor)",
kind: "number",
default: 1.0,
section: "Calibration",
min: 0.000001,
fixable: true,
defaultFixed: true,
actuatorFactor: "z_ua",
freeMin: 0.2,
freeMax: 3.0,
},
{
key: "z_dp",
label: "Z_dP (pressure-drop factor)",
kind: "number",
default: 1.0,
section: "Calibration",
min: 0.000001,
fixable: true,
defaultFixed: true,
actuatorFactor: "z_dp",
freeMin: 0.2,
freeMax: 3.0,
},
],
},
{
type: "BphxCondenser",
label: "Brazed Plate Condenser",
category: "Heat Exchangers",
description: "Condenseur à plaques brasées : géométrie + corrélation Longo/Shah → UA.",
help:
"Même famille que BphxEvaporator. Corrélation de condensation (Longo/Shah) → h → UA, solveur ε-NTU.\n\n" +
"Détail des Nu / Re_eq / ΔP : docs/components/bphx.md",
docSlug: "bphx",
ports: ["inlet", "outlet", "secondary_inlet", "secondary_outlet"],
color: "#dc2626",
params: [
{ key: "manufacturer", label: "Manufacturer", kind: "string", default: "", section: "Identification" },
{ key: "model", label: "Model / reference", kind: "string", default: "", section: "Identification" },
{ key: "refrigerant", label: "Refrigerant", kind: "string", default: "R134a", section: "Fluids" },
{ key: "secondary_fluid", label: "Secondary fluid", kind: "string", default: "Water", section: "Fluids" },
{ key: "n_plates", label: "Number of plates", kind: "number", default: 20, required: true, section: "Plate pack", min: 2, step: 1 },
{ key: "plate_length_m", label: "Effective plate length", kind: "number", unit: "m", default: 0.25, section: "Plate pack", min: 0.001 },
{ key: "plate_width_m", label: "Effective plate width", kind: "number", unit: "m", default: 0.05, section: "Plate pack", min: 0.001 },
{ key: "plate_thickness_mm", label: "Plate thickness", kind: "number", unit: "mm", default: 0.6, section: "Plate pack", min: 0.01 },
{ key: "channel_spacing_mm", label: "Channel spacing", kind: "number", unit: "mm", default: 1.5, section: "Corrugation", min: 0.01 },
{ key: "chevron_angle_deg", label: "Chevron angle", kind: "number", unit: "°", default: 60.0, section: "Corrugation", min: 10.0, max: 80.0 },
{ key: "corrugation_pitch_mm", label: "Corrugation pitch", kind: "number", unit: "mm", default: 3.0, section: "Corrugation", min: 0.01 },
{ key: "dh_m", label: "Hydraulic diameter override", kind: "number", unit: "m", section: "Direct manufacturer data", min: 0.000001, advanced: true },
{ key: "area_m2", label: "Heat-transfer area override", kind: "number", unit: "m²", section: "Direct manufacturer data", min: 0.000001, advanced: true },
{ key: "target_subcooling_k", label: "Target subcooling", kind: "number", unit: "K", default: 3.0, section: "Operating model", min: 0.0 },
{
key: "emergent_pressure",
label: "Solve condensing pressure",
kind: "boolean",
default: true,
section: "Solved-cycle mode",
description:
"ON: +1 outlet-closure residual pins h_out to target subcooling so P_cond emerges from the secondary balance.",
},
{
key: "correlation",
label: "Two-phase correlation",
kind: "string",
default: "Longo2004",
section: "Heat-transfer model",
options: [
{ value: "Longo2004", label: "Longo 2004" },
{ value: "Shah1979", label: "Shah 1979" },
{ value: "Shah2021", label: "Shah 2021" },
],
},
{
key: "dp_correlation",
label: "Pressure-drop correlation",
kind: "string",
default: "SimplifiedChannel",
section: "Pressure drop",
description:
"Channel friction on both hot and cold sides (independent of HTC). Default SimplifiedChannel; Martin1996 uses chevron angle.",
options: [
{ value: "SimplifiedChannel", label: "Simplified channel (default)" },
{ value: "Martin1996", label: "Martin 1996 (chevron)" },
],
},
{ key: "ua", label: "UA override", kind: "number", unit: "W/K", section: "Calibration", min: 0.0, advanced: true },
{
key: "z_ua",
label: "Z_UA (UA factor)",
kind: "number",
default: 1.0,
section: "Calibration",
min: 0.000001,
fixable: true,
defaultFixed: true,
actuatorFactor: "z_ua",
freeMin: 0.2,
freeMax: 3.0,
},
{
key: "z_dp",
label: "Z_dP (pressure-drop factor)",
kind: "number",
default: 1.0,
section: "Calibration",
min: 0.000001,
fixable: true,
defaultFixed: true,
actuatorFactor: "z_dp",
freeMin: 0.2,
freeMax: 3.0,
},
],
},
{
type: "FloodedEvaporator",
label: "Flooded Evaporator",
category: "Heat Exchangers",
description: "Évaporateur noyé (shell & tube). Sortie vapeur saturée par défaut.",
help:
"Rôle : évaporateur noyé — le frigorigène bout dans la calandre, leau/glycol dans les tubes.\n\n" +
"Ports : inlet/outlet frigo · secondary_in/out eau.\n\n" +
"Mode machine : relier BrineSource → secondary_in → secondary_out → BrineSink.\n" +
"Mode rating : renseigner T et débit secondaires sans câbler leau.\n\n" +
"Calibration : onglet Calibration — Fixed sur SST + Fixed décoché sur Z_UA (défaut Z_UA = 1).\n\n" +
"quality_control : laisser OFF sauf modèle de recirculation spécialisé.",
docSlug: "flooded-evaporator",
ports: ["inlet", "outlet", "secondary_inlet", "secondary_outlet"],
color: "#06b6d4",
params: [
{ key: "manufacturer", label: "Manufacturer", kind: "string", default: "", section: "Identification" },
{ key: "model", label: "Model / reference", kind: "string", default: "", section: "Identification" },
{ key: "refrigerant", label: "Refrigerant", kind: "string", default: "R134a", section: "Fluids" },
{ key: "secondary_fluid", label: "Secondary fluid", kind: "string", default: "Water", section: "Fluids" },
{ key: "ua", label: "UA", kind: "number", unit: "W/K", default: 8000.0, required: true, section: "Thermal model", min: 0.0, sweepable: true, sweepGroup: "thermal" },
{
key: "secondary_rated_pressure_drop_pa",
label: "Tube-side water design ΔP",
kind: "number",
unit: "Pa",
default: 40000,
section: "Secondary hydraulics",
min: 0.0,
description:
"Water/brine ΔP through the tubes at rated ṁ (quadratic). 0 = isobaric (legacy).",
},
{
key: "secondary_rated_m_flow_kg_s",
label: "Tube-side water design ṁ",
kind: "number",
unit: "kg/s",
default: 0.55,
section: "Secondary hydraulics",
min: 0.0,
},
{
key: "z_ua",
label: "Z_UA (UA factor)",
kind: "number",
default: 1.0,
section: "Calibration",
min: 0.000001,
fixable: true,
defaultFixed: true,
actuatorFactor: "z_ua",
freeMin: 0.2,
freeMax: 3.0,
description: "Fixed = figé. Décocher Fixed = le solveur ajuste Z_UA.",
},
{
key: "z_dp",
label: "Z_dP (pressure-drop factor)",
kind: "number",
default: 1.0,
section: "Calibration",
min: 0.000001,
advanced: true,
fixable: true,
defaultFixed: true,
actuatorFactor: "z_dp",
freeMin: 0.2,
freeMax: 3.0,
},
{
key: "quality_control",
label: "Quality control residual",
kind: "boolean",
default: false,
section: "Solved-cycle mode",
description:
"FIX: adds q_out target residual (+1 equation). Over-constrains a closed cycle unless you FREE an actuator (EXV/level). Prefer OFF for compressor suction.",
},
{
key: "target_quality",
label: "Outlet vapour quality target",
kind: "number",
default: 0.7,
section: "Solved-cycle mode",
min: 0.0,
max: 1.0,
description: "Only used when quality_control is ON. Legacy flooded recirculation target — not physical for dry suction.",
},
{
key: "secondary_inlet_temp_c",
label: "Secondary inlet T (rating only)",
kind: "number",
unit: "°C",
default: 12.0,
section: "Secondary side (rating)",
description: "Rating-mode scalar. For system solves, impose T via BrineSource on secondary_inlet instead.",
},
{
key: "secondary_mass_flow_kg_s",
label: "Secondary mass flow (rating only)",
kind: "number",
unit: "kg/s",
default: 1.5,
section: "Secondary side (rating)",
min: 0.0,
description: "Rating-mode scalar. For system solves, set ṁ on BrineSource.",
},
{
key: "secondary_cp_j_per_kgk",
label: "Secondary cp (rating only)",
kind: "number",
unit: "J/kg·K",
default: 4186.0,
section: "Secondary side (rating)",
min: 0.0,
advanced: true,
},
{
key: "ua_mode",
label: "UA mode",
kind: "string",
default: "fixed",
section: "Thermal model",
options: [
{ value: "fixed", label: "Fixed UA" },
{ value: "cooper", label: "Cooper pool boiling" },
],
description: "cooper = outer-loop UA from Cooper + Palen (Picard).",
},
{
key: "area_ref_m2",
label: "Refrigerant HTA",
kind: "number",
unit: "m²",
default: 20.0,
section: "Pool boiling",
advanced: true,
},
{
key: "area_sec_m2",
label: "Secondary HTA",
kind: "number",
unit: "m²",
default: 18.0,
section: "Pool boiling",
advanced: true,
},
],
},
{
type: "HeatExchanger",
label: "Generic Heat Exchanger",
category: "Heat Exchangers",
description: "Strict four-port two-fluid heat exchanger. Every side is a real solver edge.",
ports: ["hot_inlet", "hot_outlet", "cold_inlet", "cold_outlet"],
color: "#8b5cf6",
params: [
{ key: "manufacturer", label: "Manufacturer", kind: "string", default: "", section: "Identification" },
{ key: "model", label: "Model / reference", kind: "string", default: "", section: "Identification" },
{ key: "ua", label: "UA", kind: "number", unit: "W/K", default: 3000.0, required: true, section: "Thermal model", min: 0.0, sweepable: true, sweepGroup: "thermal" },
{ key: "hot_fluid_id", label: "Hot-side fluid", kind: "string", default: "Water", section: "Fluids" },
{ key: "cold_fluid_id", label: "Cold-side fluid", kind: "string", default: "Air", section: "Fluids" },
{ key: "hot_humidity_ratio", label: "Hot-side humidity ratio", kind: "number", unit: "kg/kg", default: 0.0, section: "Psychrometrics", min: 0.0, advanced: true },
{ key: "cold_humidity_ratio", label: "Cold-side humidity ratio", kind: "number", unit: "kg/kg", default: 0.01, section: "Psychrometrics", min: 0.0, advanced: true },
],
},
// ── Expansion ──────────────────────────────────────────────────────────
{
type: "IsenthalpicExpansionValve",
label: "Expansion Valve (EXV)",
category: "Expansion",
description: "Détendeur isenthalpique (EXV).",
help:
"Rôle : fait chuter la pression frigo (h constant).\n\n" +
"Ports : inlet (liquide HP) · outlet (biphasique BP).\n\n" +
"Sans orifice_kv : isenthalpique seul — opening na aucun effet physique.\n" +
"Avec orifice_kv : ṁ = Kv·opening·√(2·ρ·ΔP). Opening Fixed (défaut) = paramètre ; " +
"Free = inconnue (régulation / SaturatedController).\n" +
"Orifice Fixed → le compresseur lâche la loi de ṁ (débit métré par lEXV).\n" +
"t_evap_k : guess / mode pression fixée selon le cycle.",
docSlug: "isenthalpic-expansion-valve",
ports: ["inlet", "outlet"],
color: "#10b981",
params: [
{ key: "t_evap_k", label: "Evaporating temp (K)", kind: "number", unit: "K", default: 275.15 },
{
key: "orifice_kv",
label: "Orifice Kv",
kind: "number",
unit: "m²",
section: "Orifice",
description:
"Capacité dorifice. Obligatoire pour que opening change le débit. Typique ~1e-7…1e-5 m².",
},
{
key: "opening",
label: "Valve opening (01)",
kind: "number",
default: 1.0,
min: 0,
max: 1,
section: "Orifice",
fixable: true,
defaultFixed: true,
description: "Nécessite orifice_kv. Fixed = impose opening ; Free = inconnue solver.",
sweepable: true,
sweepGroup: "machine",
},
],
},
{
type: "ExpansionValve",
label: "Expansion Valve (basic)",
category: "Expansion",
description: "Physical EXV/TXV with selectable flow model (orifice, Cd·A, Eames).",
ports: ["inlet", "outlet"],
color: "#10b981",
params: [
{ key: "fluid", label: "Fluid", kind: "string", default: "R134a", required: true },
{ key: "opening", label: "Opening (01)", kind: "number", default: 1.0, sweepable: true, sweepGroup: "machine" },
{
key: "flow_model",
label: "Flow model",
kind: "string",
default: "orifice",
options: [
{ value: "orifice", label: "Isenthalpic orifice" },
{ value: "exv", label: "EXV Cd·A" },
{ value: "txv", label: "TXV Eames" },
],
},
{ key: "beta_m2", label: "β / orifice area", kind: "number", unit: "m²", default: 1e-6, advanced: true },
{ key: "cd", label: "Discharge Cd", kind: "number", default: 0.7, advanced: true },
{ key: "area_max_m2", label: "A_max", kind: "number", unit: "m²", default: 2e-6, advanced: true },
{ key: "bulb_pressure_pa", label: "TXV bulb P", kind: "number", unit: "Pa", default: 400000, advanced: true },
],
},
{
type: "CapillaryTube",
label: "Capillary Tube",
category: "Expansion",
description: "Adiabatic capillary (segmented ΔP, isenthalpic).",
help: "Diamètre / longueur / segments. Corrélation DP MSH par défaut.",
ports: ["inlet", "outlet"],
color: "#059669",
params: [
{ key: "diameter_m", label: "Inner diameter", kind: "number", unit: "m", default: 0.001, required: true },
{ key: "length_m", label: "Length", kind: "number", unit: "m", default: 2.0, required: true },
{ key: "n_segments", label: "Segments", kind: "number", default: 20, advanced: true },
],
},
{
type: "ReversingValve",
label: "Reversing Valve (4-way)",
category: "Expansion",
description: "Four-way reversing valve for heat-pump mode switch.",
ports: ["inlet", "outlet"],
color: "#0d9488",
params: [
{
key: "mode",
label: "Mode",
kind: "string",
default: "cooling",
options: [
{ value: "cooling", label: "Cooling" },
{ value: "heating", label: "Heating" },
],
},
{ key: "pressure_drop_pa", label: "Pressure drop", kind: "number", unit: "Pa", default: 5000 },
],
},
{
type: "BypassValve",
label: "Bypass Valve",
category: "Expansion",
description: "Proportional bypass valve.",
ports: ["inlet", "outlet"],
color: "#14b8a6",
params: [
{ key: "opening", label: "Opening (01)", kind: "number", default: 0.5, sweepable: true, sweepGroup: "machine" },
],
},
// ── Piping (Modelica/TIL media: green ref · blue water · yellow air) ────
{
type: "RefrigerantPipe",
label: "Pipe — refrigerant",
category: "Piping",
description: "Ligne frigorifique (DarcyWeisbach). Glisser sur une ligne verte pour linsérer.",
help:
"Conduit frigorigène. Dépose-le sur une connexion verte pour couper la ligne et linsérer automatiquement.\n" +
"CLI type: RefrigerantPipe (alias Pipe).\n" +
"Design ΔP = 0 (défaut) → perte DarcyWeisbach depuis L/D + ṁ.\n" +
"Design ΔP > 0 → impose cette chute constante (ignore L/D pour ΔP).",
ports: ["inlet", "outlet"],
color: "#2e7d32",
params: [
{ key: "fluid", label: "Fluid", kind: "string", default: "R134a", section: "Medium" },
{ key: "length_m", label: "Length", kind: "number", unit: "m", default: 3.0, required: true, section: "Geometry" },
{ key: "diameter_m", label: "Inner diameter", kind: "number", unit: "m", default: 0.012, required: true, section: "Geometry" },
{
key: "pressure_drop_pa",
label: "Design ΔP (0 = Darcy)",
kind: "number",
unit: "Pa",
default: 0,
section: "Hydraulics",
description: "0 = ΔP Darcy depuis géométrie + débit. >0 = ΔP imposé constant.",
},
{ key: "roughness_m", label: "Roughness", kind: "number", unit: "m", default: 0.0000015, section: "Geometry", advanced: true },
{ key: "density_kg_m3", label: "Density (design)", kind: "number", unit: "kg/m³", default: 1140, section: "Properties", advanced: true },
{ key: "viscosity_pa_s", label: "Viscosity (design)", kind: "number", unit: "Pa·s", default: 0.0002, section: "Properties", advanced: true },
],
},
{
type: "WaterPipe",
label: "Pipe — water / brine",
category: "Piping",
description: "Tuyauterie eau/glycol. Glisser sur une ligne bleue pour linsérer.",
help:
"Conduit caloporteur. Dépose-le sur une connexion bleue (BrineSource ↔ HX) pour linsérer dans le circuit.\n" +
"Design ΔP = 0 → Darcy L/D ; >0 → ΔP imposé.",
ports: ["inlet", "outlet"],
color: "#1565c0",
params: [
{ key: "fluid", label: "Fluid", kind: "string", default: "Water", section: "Medium" },
{ key: "length_m", label: "Length", kind: "number", unit: "m", default: 5.0, required: true, section: "Geometry" },
{ key: "diameter_m", label: "Inner diameter", kind: "number", unit: "m", default: 0.025, required: true, section: "Geometry" },
{
key: "pressure_drop_pa",
label: "Design ΔP (0 = Darcy)",
kind: "number",
unit: "Pa",
default: 0,
section: "Hydraulics",
description: "0 = ΔP Darcy depuis géométrie + débit. >0 = ΔP imposé constant.",
},
{ key: "roughness_m", label: "Roughness", kind: "number", unit: "m", default: 0.000045, section: "Geometry", advanced: true },
{ key: "density_kg_m3", label: "Density", kind: "number", unit: "kg/m³", default: 998, section: "Properties", advanced: true },
{ key: "viscosity_pa_s", label: "Viscosity", kind: "number", unit: "Pa·s", default: 0.001, section: "Properties", advanced: true },
],
},
{
type: "AirDuct",
label: "Duct — air",
category: "Piping",
description: "Gaine dair. Glisser sur une ligne jaune pour linsérer.",
help:
"Conduit air. Dépose-le sur une connexion jaune (AirSource / Fan / coil air) pour linsérer.\n" +
"Design ΔP = 0 → Darcy L/D ; >0 → ΔP imposé.",
ports: ["inlet", "outlet"],
color: "#f9a825",
params: [
{ key: "fluid", label: "Fluid", kind: "string", default: "Air", section: "Medium" },
{ key: "length_m", label: "Length", kind: "number", unit: "m", default: 5.0, required: true, section: "Geometry" },
{ key: "diameter_m", label: "Hydraulic diameter", kind: "number", unit: "m", default: 0.2, required: true, section: "Geometry" },
{
key: "pressure_drop_pa",
label: "Design ΔP (0 = Darcy)",
kind: "number",
unit: "Pa",
default: 0,
section: "Hydraulics",
description: "0 = ΔP Darcy depuis géométrie + débit. >0 = ΔP imposé constant.",
},
{ key: "roughness_m", label: "Roughness", kind: "number", unit: "m", default: 0.00015, section: "Geometry", advanced: true },
{ key: "density_kg_m3", label: "Density", kind: "number", unit: "kg/m³", default: 1.2, section: "Properties", advanced: true },
{ key: "viscosity_pa_s", label: "Viscosity", kind: "number", unit: "Pa·s", default: 0.000018, section: "Properties", advanced: true },
],
},
{
type: "Pipe",
label: "Pipe (generic)",
category: "Piping",
description: "Legacy generic pipe — prefer medium-specific pipes above.",
ports: ["inlet", "outlet"],
color: "#64748b",
params: [
{ key: "fluid", label: "Fluid", kind: "string", default: "R134a" },
{ key: "length_m", label: "Length (m)", kind: "number", unit: "m", default: 10.0, required: true },
{ key: "diameter_m", label: "Diameter (m)", kind: "number", unit: "m", default: 0.022, required: true },
{ key: "roughness_m", label: "Roughness (m)", kind: "number", unit: "m", default: 0.0000015 },
],
},
{
type: "Drum",
label: "Liquid-Vapor Separator",
category: "Piping",
description: "Phase separator / accumulator.",
ports: ["inlet", "liquid_outlet", "vapor_outlet"],
color: "#94a3b8",
params: [],
},
{
type: "FlowSplitter",
label: "Flow Splitter",
category: "Piping",
description: "Splits a flow into two branches.",
ports: ["inlet", "outlet_a", "outlet_b"],
color: "#94a3b8",
params: [
{ key: "split_ratio", label: "Split ratio (outlet_a)", kind: "number", default: 0.5 },
],
},
{
type: "FlowMerger",
label: "Flow Merger",
category: "Piping",
description: "Merges two flows into one.",
ports: ["inlet_a", "inlet_b", "outlet"],
color: "#94a3b8",
params: [],
},
// ── Hydraulics ─────────────────────────────────────────────────────────
{
type: "Pump",
label: "Pump",
category: "Hydraulics",
description: "Pompe — impose ΔP/courbe (esprit Modelica.Fluid.Machines).",
help:
"Modelica: une pompe prescrit ṁ ou ΔP via sa courbe — ne pas aussi Fixed ṁ " +
"sur un MassFlowSource de la même branche (surcontraint).",
ports: ["inlet", "outlet"],
color: "#a855f7",
params: [
{ key: "head_coeffs", label: "Head curve (a0,a1,a2)", kind: "string", unit: "m", default: "30,-10,-50", required: true },
{ key: "eff_coeffs", label: "Efficiency curve (a0,a1,a2)", kind: "string", default: "0.5,0.3,-0.5", required: true },
{ key: "density_kg_m3", label: "Fluid density (kg/m³)", kind: "number", unit: "kg/m³", default: 1000.0 },
],
},
{
type: "Fan",
label: "Fan",
category: "Hydraulics",
description: "Edge-coupled fan for air-side loops; pressure rise and shaft heat are solved on live edges.",
ports: ["inlet", "outlet"],
color: "#d946ef",
params: [
{ key: "fluid", label: "Fluid", kind: "string", default: "Air" },
{ key: "speed_ratio", label: "Speed ratio", kind: "number", default: 1.0, sweepable: true, sweepGroup: "machine" },
{ key: "air_density_kg_m3", label: "Air density", kind: "number", unit: "kg/m³", default: 1.204 },
{ key: "design_flow_m3_s", label: "Design flow", kind: "number", unit: "m³/s", default: 0.83, required: true },
{ key: "curve_p0", label: "Curve p0", kind: "number", unit: "Pa", default: 250.0 },
{ key: "curve_p1", label: "Curve p1", kind: "number", default: 0.0 },
{ key: "curve_p2", label: "Curve p2", kind: "number", default: -20.0 },
{ key: "eff_e0", label: "Efficiency e0", kind: "number", default: 0.65 },
{ key: "eff_e1", label: "Efficiency e1", kind: "number", default: 0.0 },
{ key: "eff_e2", label: "Efficiency e2", kind: "number", default: 0.0 },
{
key: "drive_chain",
label: "Wire-to-air drive chain",
kind: "boolean",
default: false,
description: "Apply η_VFD × η_motor on top of fan efficiency (BernierBourret).",
},
],
},
{
type: "FreeCoolingExchanger",
label: "Free Cooling Exchanger",
category: "Heat Exchangers",
description: "Waterwater free-cooling HX (effectiveness / UA).",
ports: ["cold_inlet", "cold_outlet", "hot_inlet", "hot_outlet"],
color: "#7c3aed",
params: [
{ key: "ua", label: "UA", kind: "number", unit: "W/K", default: 10000.0, required: true, sweepable: true, sweepGroup: "thermal" },
{ key: "effectiveness", label: "Effectiveness", kind: "number", default: 0.85 },
],
},
// ── Boundaries ─────────────────────────────────────────────────────────
{
type: "RefrigerantSource",
label: "Refrigerant Source",
category: "Boundaries",
description: "Fixed-state refrigerant boundary.",
ports: ["outlet"],
color: "#f1f5f9",
params: [
{ key: "pressure_bar", label: "Pressure (bar)", kind: "number", unit: "bar", default: 10.0, required: true },
{ key: "temperature_c", label: "Temperature (°C)", kind: "number", unit: "°C", default: 40.0 },
],
},
{
type: "RefrigerantSink",
label: "Refrigerant Sink",
category: "Boundaries",
description: "Fixed-pressure refrigerant boundary.",
ports: ["inlet"],
color: "#f1f5f9",
params: [
{ key: "pressure_bar", label: "Pressure (bar)", kind: "number", unit: "bar", default: 4.0, required: true },
],
},
{
type: "BrineSource",
label: "Brine / Glycol Source",
category: "Boundaries",
description: "Modelica MassFlowSource_T / Boundary_pT.",
help:
"Modelica.Fluid.Sources (doc.modelica.org):\n" +
"• MassFlowSource_T — Fixed T + Fixed ṁ, Free P (défaut)\n" +
"• Boundary_pT — Fixed P + Fixed T, Free ṁ\n" +
"Jamais Fixed P et Fixed ṁ ensemble.\n" +
"Ancre P = Sink. Double Fixed P seulement avec ΔP hydraulique (pipe).\n" +
"T_out imposé : Free ṁ ici + Fixed T_out sur le Sink.",
docSlug: "boundaries",
ports: ["outlet"],
color: "#e0f2fe",
params: [
{ key: "fluid", label: "Fluid", kind: "string", default: "Water" },
{
key: "p_set_bar",
label: "Pressure setpoint",
kind: "number",
unit: "bar",
default: 2.0,
required: true,
fixable: true,
defaultFixed: false,
description: "Free (défaut MassFlowSource_T). Fixed ⇒ mode Boundary_pT (Free ṁ).",
},
{
key: "t_set_c",
label: "Temperature setpoint",
kind: "number",
unit: "°C",
default: 12.0,
required: true,
fixable: true,
defaultFixed: true,
sweepable: true,
sweepGroup: "boundaries",
},
{
key: "m_flow_kg_s",
label: "Mass flow",
kind: "number",
unit: "kg/s",
default: 0.5,
required: true,
fixable: true,
defaultFixed: true,
description: "Fixed ⇒ Free P auto. Free ⇒ ṁ issu du bilan (avec Fixed T_out au Sink).",
sweepable: true,
sweepGroup: "boundaries",
},
],
},
{
type: "BrineSink",
label: "Brine / Glycol Sink",
category: "Boundaries",
description: "Modelica Boundary_p — ancre de pression.",
help:
"Ancre P du circuit (Boundary_p). Fixed P par défaut.\n" +
"Fixed T_out ⇒ Free ṁ sur la Source (pas de Fixed ṁ en même temps).",
docSlug: "boundaries",
ports: ["inlet"],
color: "#e0f2fe",
params: [
{ key: "fluid", label: "Fluid", kind: "string", default: "Water" },
{
key: "p_back_bar",
label: "Back pressure",
kind: "number",
unit: "bar",
default: 2.0,
required: true,
fixable: true,
defaultFixed: true,
},
{
key: "t_set_c",
label: "Outlet temperature",
kind: "number",
unit: "°C",
default: 7.0,
fixable: true,
defaultFixed: false,
description: "Fixed ON ⇒ impose h_out. Libère ṁ sur la Source.",
sweepable: true,
sweepGroup: "boundaries",
},
],
},
{
type: "AirSource",
label: "Air Source",
category: "Boundaries",
description: "Modelica MassFlowSource_T (air).",
help:
"MassFlowSource_T : Fixed T + Fixed ṁ, Free P (défaut).\n" +
"Boundary_pT : Fixed P + Free ṁ. Jamais Fixed P+ṁ ensemble.",
ports: ["outlet"],
color: "#f0fdf4",
params: [
{
key: "p_set_bar",
label: "Pressure setpoint",
kind: "number",
unit: "bar",
default: 1.01325,
required: true,
fixable: true,
defaultFixed: false,
},
{
key: "t_dry_c",
label: "Dry-bulb temperature",
kind: "number",
unit: "°C",
default: 20.0,
required: true,
fixable: true,
defaultFixed: true,
sweepable: true,
sweepGroup: "boundaries",
},
{ key: "rh", label: "Relative humidity", kind: "number", unit: "%", default: 50.0 },
{
key: "m_flow_kg_s",
label: "Mass flow",
kind: "number",
unit: "kg/s",
default: 1.0,
required: true,
fixable: true,
defaultFixed: true,
sweepable: true,
sweepGroup: "boundaries",
},
],
},
{
type: "AirSink",
label: "Air Sink",
category: "Boundaries",
description: "Modelica Boundary_p (air) — ancre P.",
ports: ["inlet"],
color: "#f0fdf4",
params: [
{
key: "p_back_bar",
label: "Back pressure",
kind: "number",
unit: "bar",
default: 1.01325,
required: true,
fixable: true,
defaultFixed: true,
},
{
key: "t_back_c",
label: "Return temperature",
kind: "number",
unit: "°C",
default: 25.0,
fixable: true,
defaultFixed: false,
},
],
},
// ── Generic ────────────────────────────────────────────────────────────
{
type: "Placeholder",
label: "Placeholder",
category: "Generic",
description: "Generic placeholder with configurable equation count.",
ports: ["inlet", "outlet"],
color: "#e2e8f0",
params: [
{ key: "n_equations", label: "Number of equations", kind: "number", default: 2, required: true },
],
},
];
export const COMPONENT_BY_TYPE: Record<string, ComponentMeta> = Object.fromEntries(
COMPONENTS.map((c) => [c.type, c]),
);
/** Build the default parameter values for a component type. */
export function defaultParams(type: string): Record<string, number | string | boolean> {
const meta = COMPONENT_BY_TYPE[type];
if (!meta) return {};
const out: Record<string, number | string | boolean> = {};
for (const p of meta.params) {
if (p.default !== undefined) out[p.key] = p.default;
// Seed Fixed flags so Free/Fixed state is explicit after drop
if (p.fixable) {
out[fixedFlagKey(p.key)] = p.defaultFixed !== false;
}
}
return out;
}
/**
* Value shown / used for a param: stored value, else meta.default.
* Ensures Z_UA=1 etc. always appear even if an old node lacks the key.
*/
export function effectiveParamValue(
params: Record<string, number | string | boolean | undefined>,
meta: ParamMeta,
): number | string | boolean | undefined {
const v = params[meta.key];
if (v !== undefined && v !== "") return v;
return meta.default;
}
/** Merge missing defaults into a params object (non-destructive for existing keys). */
export function mergeDefaultParams(
type: string,
params: Record<string, number | string | boolean>,
): Record<string, number | string | boolean> {
const defaults = defaultParams(type);
return { ...defaults, ...params };
}