Snapshot WIP: solver HP epic progress, BPHX/HX physics, BMAD skill refresh.
Some checks failed
CI / check (push) Has been cancelled

Capture uncommitted solver robustness work (regularization, domain errors, linear solver lifecycle, tube DP/MSH), web workbench updates, and synced BMAD skills across IDE agent folders before starting BPHX pressure-drop.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-19 16:35:31 +02:00
parent 88620790d6
commit 5bd180b5b8
1363 changed files with 101041 additions and 58547 deletions

View File

@@ -48,6 +48,13 @@ export interface ParamMeta {
/** 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). */
@@ -87,29 +94,42 @@ export interface ComponentMeta {
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" {
// Secondary (heat-transfer fluid) ports cross the refrigerant flow vertically:
// caloporteur in at the bottom, out at the top (Modelica counterflow convention).
if (port === "secondary_inlet") return "bottom";
if (port === "secondary_outlet") return "top";
if (port === "hot_inlet" || port === "cold_inlet") return "left";
if (port === "hot_outlet" || port === "cold_outlet") return "right";
if (port.startsWith("outlet")) return "right";
if (port === "discharge") return "right";
if (port === "liquid_outlet" || port === "vapor_outlet") return "bottom";
if (port.startsWith("inlet")) return "left";
if (port === "suction") return "left";
if (port === "economizer") return "top";
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 {
return port === "secondary_inlet" || port === "secondary_outlet";
const p = String(port || "");
return p === "secondary_inlet" || p === "secondary_outlet";
}
/**
@@ -118,12 +138,13 @@ export function isSecondaryPort(port: string): boolean {
* (outlet/discharge) to a target (inlet/suction).
*/
export function portRole(port: string): "source" | "target" {
const p = String(port || "");
if (
port.startsWith("outlet") ||
port === "discharge" ||
port === "liquid_outlet" ||
port === "vapor_outlet" ||
port.endsWith("_outlet")
p.startsWith("outlet") ||
p === "discharge" ||
p === "liquid_outlet" ||
p === "vapor_outlet" ||
p.endsWith("_outlet")
) {
return "source";
}
@@ -136,21 +157,22 @@ export function portRole(port: string): "source" | "target" {
* compact. Size encodes what the part is, not just decoration.
*/
export function nodeSize(type: string): { w: number; h: number } {
if (type === "SaturatedController") return { w: 184, h: 96 };
if (type.includes("Compressor")) return { w: 76, h: 76 };
if (type === "HeatExchanger") return { w: 92, h: 92 };
const t = String(type || "");
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 (type.includes("Flooded")) return { w: 128, h: 92 };
if (type.includes("Bphx")) return { w: 80, h: 110 };
if (type.includes("FinCoil") || type.includes("AirCooled") || type.includes("Mchx"))
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 (type.includes("Condenser") || type.includes("Evaporator")) return { w: 126, h: 88 };
if (type.includes("Valve")) return { w: 56, h: 56 };
if (type === "Pump" || type === "Fan") return { w: 72, h: 72 };
if (type === "Pipe" || type.includes("Pipe") || type === "AirDuct") return { w: 118, h: 42 };
if (type === "Drum") return { w: 58, h: 108 };
if (type === "FlowSplitter" || type === "FlowMerger") return { w: 72, h: 64 };
if (type.endsWith("Source") || type.endsWith("Sink")) return { w: 62, h: 56 };
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 };
}
@@ -319,6 +341,8 @@ export const COMPONENTS: ComponentMeta[] = [
min: 0.05,
max: 1.0,
description: "η_is : h_dis = h_suc + (h_ish_suc)/η_is",
sweepable: true,
sweepGroup: "machine",
},
{
key: "emergent_pressure",
@@ -427,6 +451,8 @@ export const COMPONENTS: ComponentMeta[] = [
required: true,
section: "Machine",
min: 1,
sweepable: true,
sweepGroup: "machine",
},
{
key: "displacement_m3",
@@ -481,7 +507,7 @@ export const COMPONENTS: ComponentMeta[] = [
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 },
{ 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 },
],
@@ -522,6 +548,8 @@ export const COMPONENTS: ComponentMeta[] = [
unit: "Hz",
default: 50.0,
section: "Machine",
sweepable: true,
sweepGroup: "machine",
},
{
key: "nominal_frequency_hz",
@@ -558,6 +586,8 @@ export const COMPONENTS: ComponentMeta[] = [
min: 1.1,
max: 6,
description: "Built-in Vi; slide valve reduces effective Vi.",
sweepable: true,
sweepGroup: "machine",
},
{
key: "slide_valve_position",
@@ -567,6 +597,8 @@ export const COMPONENTS: ComponentMeta[] = [
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" },
@@ -601,7 +633,7 @@ export const COMPONENTS: ComponentMeta[] = [
{ 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 },
{ 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",
@@ -768,7 +800,7 @@ export const COMPONENTS: ComponentMeta[] = [
{ 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 },
{ 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 },
{
@@ -931,7 +963,7 @@ export const COMPONENTS: ComponentMeta[] = [
{ 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 },
{ 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" },
],
},
{
@@ -945,8 +977,8 @@ export const COMPONENTS: ComponentMeta[] = [
{ 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 },
{ 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." },
{ 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 },
],
},
@@ -983,8 +1015,8 @@ export const COMPONENTS: ComponentMeta[] = [
{ 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" },
{ 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 },
{ 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",
@@ -1023,6 +1055,15 @@ export const COMPONENTS: ComponentMeta[] = [
{ 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",
@@ -1101,6 +1142,15 @@ export const COMPONENTS: ComponentMeta[] = [
{ 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",
@@ -1173,7 +1223,7 @@ export const COMPONENTS: ComponentMeta[] = [
{ 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 },
{ 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",
@@ -1324,7 +1374,7 @@ export const COMPONENTS: ComponentMeta[] = [
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 },
{ 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 },
@@ -1341,14 +1391,39 @@ export const COMPONENTS: ComponentMeta[] = [
help:
"Rôle : fait chuter la pression frigo (h constant).\n\n" +
"Ports : inlet (liquide HP) · outlet (biphasique BP).\n\n" +
"opening : 1 = plein ouvert. Peut être libéré par une boucle de régulation avancée.\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: "opening", label: "Valve opening (01)", kind: "number", default: 1.0 },
{
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",
},
],
},
{
@@ -1360,7 +1435,7 @@ export const COMPONENTS: ComponentMeta[] = [
color: "#10b981",
params: [
{ key: "fluid", label: "Fluid", kind: "string", default: "R134a", required: true },
{ key: "opening", label: "Opening (01)", kind: "number", default: 1.0 },
{ key: "opening", label: "Opening (01)", kind: "number", default: 1.0, sweepable: true, sweepGroup: "machine" },
{
key: "flow_model",
label: "Flow model",
@@ -1421,7 +1496,7 @@ export const COMPONENTS: ComponentMeta[] = [
ports: ["inlet", "outlet"],
color: "#14b8a6",
params: [
{ key: "opening", label: "Opening (01)", kind: "number", default: 0.5 },
{ key: "opening", label: "Opening (01)", kind: "number", default: 0.5, sweepable: true, sweepGroup: "machine" },
],
},
@@ -1433,14 +1508,24 @@ export const COMPONENTS: ComponentMeta[] = [
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). pressure_drop_pa=0 → isenthalpic pass-through (P_out=P_in).",
"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", kind: "number", unit: "Pa", default: 0, section: "Hydraulics" },
{
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 },
@@ -1452,14 +1537,23 @@ export const COMPONENTS: ComponentMeta[] = [
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.",
"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", kind: "number", unit: "Pa", default: 0, section: "Hydraulics" },
{
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 },
@@ -1471,14 +1565,23 @@ export const COMPONENTS: ComponentMeta[] = [
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.",
"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", kind: "number", unit: "Pa", default: 0, section: "Hydraulics" },
{
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 },
@@ -1554,7 +1657,7 @@ export const COMPONENTS: ComponentMeta[] = [
color: "#d946ef",
params: [
{ key: "fluid", label: "Fluid", kind: "string", default: "Air" },
{ key: "speed_ratio", label: "Speed ratio", kind: "number", default: 1.0 },
{ 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 },
@@ -1580,7 +1683,7 @@ export const COMPONENTS: ComponentMeta[] = [
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 },
{ 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 },
],
},
@@ -1646,6 +1749,8 @@ export const COMPONENTS: ComponentMeta[] = [
required: true,
fixable: true,
defaultFixed: true,
sweepable: true,
sweepGroup: "boundaries",
},
{
key: "m_flow_kg_s",
@@ -1657,6 +1762,8 @@ export const COMPONENTS: ComponentMeta[] = [
fixable: true,
defaultFixed: true,
description: "Fixed ⇒ Free P auto. Free ⇒ ṁ issu du bilan (avec Fixed T_out au Sink).",
sweepable: true,
sweepGroup: "boundaries",
},
],
},
@@ -1692,6 +1799,8 @@ export const COMPONENTS: ComponentMeta[] = [
fixable: true,
defaultFixed: false,
description: "Fixed ON ⇒ impose h_out. Libère ṁ sur la Source.",
sweepable: true,
sweepGroup: "boundaries",
},
],
},
@@ -1725,6 +1834,8 @@ export const COMPONENTS: ComponentMeta[] = [
required: true,
fixable: true,
defaultFixed: true,
sweepable: true,
sweepGroup: "boundaries",
},
{ key: "rh", label: "Relative humidity", kind: "number", unit: "%", default: 50.0 },
{
@@ -1736,6 +1847,8 @@ export const COMPONENTS: ComponentMeta[] = [
required: true,
fixable: true,
defaultFixed: true,
sweepable: true,
sweepGroup: "boundaries",
},
],
},