chore: remove BMAD framework files and IDE configuration artifacts
Clean up unused BMAD workflow, agent, and command files across all IDE configurations (.agent, .clinerules, .cursor, .gemini, .github, .kilocode, .opencode) and internal module files (_bmad/bmb, _bmad/bmm). Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -152,7 +152,8 @@ fn execute_simulation(
|
||||
|
||||
// Track component name -> (node index, component type) mapping per circuit
|
||||
// The component type is needed for port-name-to-index resolution (Task 3.3)
|
||||
let mut component_indices: HashMap<String, (petgraph::graph::NodeIndex, String)> = HashMap::new();
|
||||
let mut component_indices: HashMap<String, (petgraph::graph::NodeIndex, String)> =
|
||||
HashMap::new();
|
||||
|
||||
// Collect variables and constraints to add *after* components are added
|
||||
struct PendingControl {
|
||||
@@ -395,9 +396,7 @@ fn execute_simulation(
|
||||
// Add variables and constraints
|
||||
for control in pending_controls {
|
||||
if control.control_type == "fan_speed" {
|
||||
use entropyk_solver::inverse::{
|
||||
BoundedVariable, BoundedVariableId,
|
||||
};
|
||||
use entropyk_solver::inverse::{BoundedVariable, BoundedVariableId};
|
||||
|
||||
// Generate unique IDs
|
||||
let var_id =
|
||||
@@ -520,7 +519,11 @@ fn resolve_port_index(component_type: &str, port_name: &str, is_source: bool) ->
|
||||
"Unknown port name for ScrewEconomizerCompressor, defaulting to {}",
|
||||
if is_source { 1 } else { 0 }
|
||||
);
|
||||
if is_source { 1 } else { 0 }
|
||||
if is_source {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
@@ -537,7 +540,11 @@ fn resolve_port_index(component_type: &str, port_name: &str, is_source: bool) ->
|
||||
"Unknown port name, defaulting to {}",
|
||||
if is_source { 1 } else { 0 }
|
||||
);
|
||||
if is_source { 1 } else { 0 }
|
||||
if is_source {
|
||||
1
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -590,6 +597,24 @@ fn parse_side_conditions(
|
||||
)?)
|
||||
}
|
||||
|
||||
/// Build BphxGeometry from JSON params: dh (m), area (m²), n_plates. Defaults: 0.003, 0.5, 20.
|
||||
fn bphx_geometry_from_params(
|
||||
params: &std::collections::HashMap<String, serde_json::Value>,
|
||||
exchanger_type: entropyk_components::heat_exchanger::BphxType,
|
||||
) -> entropyk_components::heat_exchanger::BphxGeometry {
|
||||
use entropyk_components::heat_exchanger::BphxGeometry;
|
||||
let dh = params
|
||||
.get("dh_m")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.003);
|
||||
let area = params.get("area_m2").and_then(|v| v.as_f64()).unwrap_or(0.5);
|
||||
let n_plates = params
|
||||
.get("n_plates")
|
||||
.and_then(|v| v.as_u64())
|
||||
.unwrap_or(20) as u32;
|
||||
BphxGeometry::from_dh_area(dh, area, n_plates).with_exchanger_type(exchanger_type)
|
||||
}
|
||||
|
||||
/// Creates a pair of connected ports for components that need them (screw, MCHX, fan...).
|
||||
///
|
||||
/// Ports are initialised at the given pressure and enthalpy. Both ports are connected
|
||||
@@ -841,6 +866,9 @@ fn create_component(
|
||||
}
|
||||
|
||||
"Compressor" => {
|
||||
use entropyk::{Ahri540Coefficients, Compressor, ComponentFluidId, Port};
|
||||
use entropyk_core::{Enthalpy, Pressure};
|
||||
|
||||
let speed_rpm = get_param_f64(params, "speed_rpm")?;
|
||||
let displacement_m3 = get_param_f64(params, "displacement_m3")?;
|
||||
let efficiency = params
|
||||
@@ -849,6 +877,7 @@ fn create_component(
|
||||
.unwrap_or(0.85);
|
||||
let fluid = get_param_string(params, "fluid")?;
|
||||
|
||||
// AHRI 540 coefficients (M1-M10)
|
||||
let m1 = params.get("m1").and_then(|v| v.as_f64()).unwrap_or(0.85);
|
||||
let m2 = params.get("m2").and_then(|v| v.as_f64()).unwrap_or(2.5);
|
||||
let m3 = params.get("m3").and_then(|v| v.as_f64()).unwrap_or(500.0);
|
||||
@@ -860,24 +889,485 @@ fn create_component(
|
||||
let m9 = params.get("m9").and_then(|v| v.as_f64()).unwrap_or(-3.0);
|
||||
let m10 = params.get("m10").and_then(|v| v.as_f64()).unwrap_or(2.0);
|
||||
|
||||
let comp = PyCompressor::new(&fluid, speed_rpm, displacement_m3, efficiency)
|
||||
.with_coefficients(m1, m2, m3, m4, m5, m6, m7, m8, m9, m10);
|
||||
let coeffs = Ahri540Coefficients::new(m1, m2, m3, m4, m5, m6, m7, m8, m9, m10);
|
||||
|
||||
// Initial port conditions (same pattern as ScrewCompressor)
|
||||
let p_suc = params.get("p_suction_bar").and_then(|v| v.as_f64()).unwrap_or(3.5);
|
||||
let h_suc = params.get("h_suction_kj_kg").and_then(|v| v.as_f64()).unwrap_or(400.0);
|
||||
let p_dis = params.get("p_discharge_bar").and_then(|v| v.as_f64()).unwrap_or(12.0);
|
||||
let h_dis = params.get("h_discharge_kj_kg").and_then(|v| v.as_f64()).unwrap_or(440.0);
|
||||
|
||||
let fluid_id = ComponentFluidId::new(&fluid);
|
||||
|
||||
// Create disconnected ports for building the compressor
|
||||
let suction_a = Port::new(
|
||||
fluid_id.clone(),
|
||||
Pressure::from_bar(p_suc),
|
||||
Enthalpy::from_joules_per_kg(h_suc * 1000.0),
|
||||
);
|
||||
let discharge_a = Port::new(
|
||||
fluid_id.clone(),
|
||||
Pressure::from_bar(p_dis),
|
||||
Enthalpy::from_joules_per_kg(h_dis * 1000.0),
|
||||
);
|
||||
|
||||
// Build Compressor<Disconnected> with AHRI 540 model
|
||||
let comp_disconnected = Compressor::new(
|
||||
coeffs, suction_a, discharge_a, speed_rpm, displacement_m3, efficiency,
|
||||
)
|
||||
.map_err(|e| CliError::Component(e))?;
|
||||
|
||||
// Connect ports to transition Disconnected → Connected
|
||||
let suction_b = Port::new(
|
||||
fluid_id.clone(),
|
||||
Pressure::from_bar(p_suc),
|
||||
Enthalpy::from_joules_per_kg(h_suc * 1000.0),
|
||||
);
|
||||
let discharge_b = Port::new(
|
||||
fluid_id,
|
||||
Pressure::from_bar(p_dis),
|
||||
Enthalpy::from_joules_per_kg(h_dis * 1000.0),
|
||||
);
|
||||
|
||||
let comp = comp_disconnected.connect(suction_b, discharge_b)
|
||||
.map_err(|e| CliError::Component(e))?;
|
||||
|
||||
Ok(Box::new(comp))
|
||||
}
|
||||
|
||||
"ExpansionValve" => {
|
||||
use entropyk::{ComponentFluidId, ExpansionValve, Port};
|
||||
use entropyk_core::{Enthalpy, Pressure};
|
||||
|
||||
let fluid = get_param_string(params, "fluid")?;
|
||||
let opening = params.get("opening").and_then(|v| v.as_f64()).unwrap_or(1.0);
|
||||
let valve = PyExpansionValve::new(&fluid, opening);
|
||||
|
||||
// Initial port conditions
|
||||
let p_in = params.get("p_inlet_bar").and_then(|v| v.as_f64()).unwrap_or(12.0);
|
||||
let h_in = params.get("h_inlet_kj_kg").and_then(|v| v.as_f64()).unwrap_or(260.0);
|
||||
let p_out = params.get("p_outlet_bar").and_then(|v| v.as_f64()).unwrap_or(3.5);
|
||||
let h_out = params.get("h_outlet_kj_kg").and_then(|v| v.as_f64()).unwrap_or(260.0);
|
||||
|
||||
let fluid_id = ComponentFluidId::new(&fluid);
|
||||
|
||||
// Create disconnected ports
|
||||
let inlet_a = Port::new(
|
||||
fluid_id.clone(),
|
||||
Pressure::from_bar(p_in),
|
||||
Enthalpy::from_joules_per_kg(h_in * 1000.0),
|
||||
);
|
||||
let outlet_a = Port::new(
|
||||
fluid_id.clone(),
|
||||
Pressure::from_bar(p_out),
|
||||
Enthalpy::from_joules_per_kg(h_out * 1000.0),
|
||||
);
|
||||
|
||||
// Build ExpansionValve<Disconnected> with isenthalpic model
|
||||
let valve_disconnected = ExpansionValve::new(inlet_a, outlet_a, Some(opening))
|
||||
.map_err(|e| CliError::Component(e))?;
|
||||
|
||||
// Connect ports to transition Disconnected → Connected
|
||||
let inlet_b = Port::new(
|
||||
fluid_id.clone(),
|
||||
Pressure::from_bar(p_in),
|
||||
Enthalpy::from_joules_per_kg(h_in * 1000.0),
|
||||
);
|
||||
let outlet_b = Port::new(
|
||||
fluid_id,
|
||||
Pressure::from_bar(p_out),
|
||||
Enthalpy::from_joules_per_kg(h_out * 1000.0),
|
||||
);
|
||||
|
||||
let valve = valve_disconnected.connect(inlet_b, outlet_b)
|
||||
.map_err(|e| CliError::Component(e))?;
|
||||
|
||||
Ok(Box::new(valve))
|
||||
}
|
||||
|
||||
"Pump" => {
|
||||
let name = params
|
||||
.get("name")
|
||||
"RefrigerantSource" => {
|
||||
use entropyk::RefrigerantSource;
|
||||
use entropyk_core::{Pressure, VaporQuality};
|
||||
|
||||
let fluid = params
|
||||
.get("fluid")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("Pump");
|
||||
Ok(Box::new(SimpleComponent::new(name, 0)))
|
||||
.unwrap_or_else(|| _primary_fluid.as_str());
|
||||
let p_set = params.get("p_set_bar").and_then(|v| v.as_f64()).unwrap_or(10.0);
|
||||
let q = params.get("quality").and_then(|v| v.as_f64()).unwrap_or(1.0);
|
||||
|
||||
let outlet = make_connected_port(fluid, p_set, 250.0);
|
||||
let comp = RefrigerantSource::new(
|
||||
fluid,
|
||||
Pressure::from_bar(p_set),
|
||||
VaporQuality::from_fraction(q),
|
||||
backend,
|
||||
outlet,
|
||||
)
|
||||
.map_err(CliError::Component)?;
|
||||
Ok(Box::new(comp))
|
||||
}
|
||||
|
||||
"RefrigerantSink" => {
|
||||
use entropyk::RefrigerantSink;
|
||||
use entropyk_core::{Pressure, VaporQuality};
|
||||
|
||||
let fluid = params
|
||||
.get("fluid")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or_else(|| _primary_fluid.as_str());
|
||||
let p_back = params.get("p_back_bar").and_then(|v| v.as_f64()).unwrap_or(10.0);
|
||||
let q_opt = params
|
||||
.get("quality")
|
||||
.and_then(|v| v.as_f64())
|
||||
.map(VaporQuality::from_fraction);
|
||||
|
||||
let inlet = make_connected_port(fluid, p_back, 250.0);
|
||||
let comp = RefrigerantSink::new(
|
||||
fluid,
|
||||
Pressure::from_bar(p_back),
|
||||
q_opt,
|
||||
backend,
|
||||
inlet,
|
||||
)
|
||||
.map_err(CliError::Component)?;
|
||||
Ok(Box::new(comp))
|
||||
}
|
||||
|
||||
"BrineSource" => {
|
||||
use entropyk::BrineSource;
|
||||
use entropyk_core::{Concentration, Pressure, Temperature};
|
||||
|
||||
let fluid = params.get("fluid").and_then(|v| v.as_str()).unwrap_or("Water");
|
||||
let p_set = params.get("p_set_bar").and_then(|v| v.as_f64()).unwrap_or(2.0);
|
||||
let t_set = params.get("t_set_c").and_then(|v| v.as_f64()).unwrap_or(12.0);
|
||||
let conc = params.get("concentration").and_then(|v| v.as_f64()).unwrap_or(0.0);
|
||||
|
||||
let outlet = make_connected_port(fluid, p_set, 100.0);
|
||||
let comp = BrineSource::new(
|
||||
fluid,
|
||||
Pressure::from_bar(p_set),
|
||||
Temperature::from_celsius(t_set),
|
||||
Concentration::from_percent(conc),
|
||||
backend,
|
||||
outlet,
|
||||
)
|
||||
.map_err(CliError::Component)?;
|
||||
Ok(Box::new(comp))
|
||||
}
|
||||
|
||||
"BrineSink" => {
|
||||
use entropyk::BrineSink;
|
||||
use entropyk_core::{Concentration, Pressure, Temperature};
|
||||
|
||||
let fluid = params.get("fluid").and_then(|v| v.as_str()).unwrap_or("Water");
|
||||
let p_back = params.get("p_back_bar").and_then(|v| v.as_f64()).unwrap_or(2.0);
|
||||
let t_opt = params
|
||||
.get("t_set_c")
|
||||
.and_then(|v| v.as_f64())
|
||||
.map(Temperature::from_celsius);
|
||||
let conc = params.get("concentration").and_then(|v| v.as_f64()).unwrap_or(0.0);
|
||||
let conc_opt = if t_opt.is_some() {
|
||||
Some(Concentration::from_percent(conc))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let inlet = make_connected_port(fluid, p_back, 100.0);
|
||||
let comp = BrineSink::new(
|
||||
fluid,
|
||||
Pressure::from_bar(p_back),
|
||||
t_opt,
|
||||
conc_opt,
|
||||
backend,
|
||||
inlet,
|
||||
)
|
||||
.map_err(CliError::Component)?;
|
||||
Ok(Box::new(comp))
|
||||
}
|
||||
|
||||
"AirSource" => {
|
||||
use entropyk::AirSource;
|
||||
use entropyk_core::{Pressure, RelativeHumidity, Temperature};
|
||||
|
||||
let p_set = params.get("p_set_bar").and_then(|v| v.as_f64()).unwrap_or(1.01325);
|
||||
let t_dry = params.get("t_dry_c").and_then(|v| v.as_f64()).unwrap_or(35.0);
|
||||
let rh = params.get("rh").and_then(|v| v.as_f64()).unwrap_or(50.0);
|
||||
let t_wet = params.get("t_wet_c").and_then(|v| v.as_f64());
|
||||
|
||||
let outlet = make_connected_port("Air", p_set, 50.0);
|
||||
|
||||
let comp = if let Some(tw) = t_wet {
|
||||
AirSource::from_dry_and_wet_bulb(
|
||||
Temperature::from_celsius(t_dry),
|
||||
Temperature::from_celsius(tw),
|
||||
Pressure::from_bar(p_set),
|
||||
outlet,
|
||||
)
|
||||
} else {
|
||||
AirSource::from_dry_bulb_rh(
|
||||
Temperature::from_celsius(t_dry),
|
||||
RelativeHumidity::from_percent(rh),
|
||||
Pressure::from_bar(p_set),
|
||||
outlet,
|
||||
)
|
||||
}
|
||||
.map_err(CliError::Component)?;
|
||||
Ok(Box::new(comp))
|
||||
}
|
||||
|
||||
"AirSink" => {
|
||||
use entropyk::AirSink;
|
||||
use entropyk_core::{Pressure, RelativeHumidity, Temperature};
|
||||
|
||||
let p_back = params.get("p_back_bar").and_then(|v| v.as_f64()).unwrap_or(1.01325);
|
||||
let t_back = params.get("t_back_c").and_then(|v| v.as_f64());
|
||||
let rh_back = params.get("rh_back").and_then(|v| v.as_f64()).unwrap_or(50.0);
|
||||
|
||||
let inlet = make_connected_port("Air", p_back, 50.0);
|
||||
let mut comp = AirSink::new(Pressure::from_bar(p_back), inlet)
|
||||
.map_err(CliError::Component)?;
|
||||
|
||||
if let Some(tb) = t_back {
|
||||
comp.set_return_temperature(
|
||||
Temperature::from_celsius(tb),
|
||||
RelativeHumidity::from_percent(rh_back),
|
||||
)
|
||||
.map_err(CliError::Component)?;
|
||||
}
|
||||
Ok(Box::new(comp))
|
||||
}
|
||||
|
||||
"Pump" => {
|
||||
use entropyk::{ComponentFluidId, Pump, PumpCurves, Port};
|
||||
use entropyk_core::{Enthalpy, Pressure};
|
||||
|
||||
let fluid = params
|
||||
.get("fluid")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or_else(|| _primary_fluid.as_str());
|
||||
let fluid_density = params
|
||||
.get("fluid_density_kg_m3")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(1000.0);
|
||||
let speed_ratio = params
|
||||
.get("speed_ratio")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(1.0);
|
||||
|
||||
let curves = if let (Some(h), Some(e)) = (
|
||||
params.get("head_coeffs").and_then(|v| v.as_array()),
|
||||
params.get("eff_coeffs").and_then(|v| v.as_array()),
|
||||
) {
|
||||
let head_coeffs: Vec<f64> = h.iter().filter_map(|v| v.as_f64()).collect();
|
||||
let eff_coeffs: Vec<f64> = e.iter().filter_map(|v| v.as_f64()).collect();
|
||||
if !head_coeffs.is_empty() && !eff_coeffs.is_empty() {
|
||||
PumpCurves::from_coefficients(head_coeffs, eff_coeffs).map_err(CliError::Component)?
|
||||
} else {
|
||||
PumpCurves::default()
|
||||
}
|
||||
} else {
|
||||
PumpCurves::default()
|
||||
};
|
||||
|
||||
let p_in = params
|
||||
.get("p_inlet_bar")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(2.0);
|
||||
let h_in = params
|
||||
.get("h_inlet_kj_kg")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(100.0);
|
||||
let p_out = params
|
||||
.get("p_outlet_bar")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(2.0);
|
||||
let h_out = params
|
||||
.get("h_outlet_kj_kg")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(100.0);
|
||||
|
||||
let fluid_id = ComponentFluidId::new(fluid);
|
||||
|
||||
let inlet_a = Port::new(
|
||||
fluid_id.clone(),
|
||||
Pressure::from_bar(p_in),
|
||||
Enthalpy::from_joules_per_kg(h_in * 1000.0),
|
||||
);
|
||||
let outlet_a = Port::new(
|
||||
fluid_id.clone(),
|
||||
Pressure::from_bar(p_out),
|
||||
Enthalpy::from_joules_per_kg(h_out * 1000.0),
|
||||
);
|
||||
|
||||
let pump_disconnected = Pump::new(curves, inlet_a, outlet_a, fluid_density)
|
||||
.map_err(CliError::Component)?;
|
||||
|
||||
let inlet_b = Port::new(
|
||||
fluid_id.clone(),
|
||||
Pressure::from_bar(p_in),
|
||||
Enthalpy::from_joules_per_kg(h_in * 1000.0),
|
||||
);
|
||||
let outlet_b = Port::new(
|
||||
fluid_id,
|
||||
Pressure::from_bar(p_out),
|
||||
Enthalpy::from_joules_per_kg(h_out * 1000.0),
|
||||
);
|
||||
|
||||
let mut pump = pump_disconnected
|
||||
.connect(inlet_b, outlet_b)
|
||||
.map_err(CliError::Component)?;
|
||||
|
||||
if (speed_ratio - 1.0).abs() > 1e-9 {
|
||||
pump.set_speed_ratio(speed_ratio)
|
||||
.map_err(CliError::Component)?;
|
||||
}
|
||||
|
||||
Ok(Box::new(pump))
|
||||
}
|
||||
|
||||
// ── BphxEvaporator (brazed plate HX evaporator) ─────────────────────────
|
||||
"BphxEvaporator" => {
|
||||
use entropyk_components::heat_exchanger::{
|
||||
BphxCorrelation, BphxEvaporator, BphxEvaporatorMode, BphxType,
|
||||
};
|
||||
use entropyk_core::Calib;
|
||||
|
||||
let geo = bphx_geometry_from_params(params, BphxType::Evaporator);
|
||||
let refrigerant = params
|
||||
.get("refrigerant")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or_else(|| _primary_fluid.as_str());
|
||||
let secondary_fluid = params
|
||||
.get("secondary_fluid")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("Water");
|
||||
|
||||
let mode = match params.get("mode").and_then(|v| v.as_str()).unwrap_or("dx") {
|
||||
"flooded" => {
|
||||
let target_quality = params
|
||||
.get("target_quality")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.7);
|
||||
BphxEvaporatorMode::Flooded { target_quality }
|
||||
}
|
||||
_ => {
|
||||
let target_superheat = params
|
||||
.get("target_superheat_k")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(5.0);
|
||||
BphxEvaporatorMode::Dx {
|
||||
target_superheat,
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let correlation = match params.get("correlation").and_then(|v| v.as_str()) {
|
||||
Some("Shah1979") => BphxCorrelation::Shah1979,
|
||||
Some("Shah2021") => BphxCorrelation::Shah2021,
|
||||
_ => BphxCorrelation::Longo2004,
|
||||
};
|
||||
|
||||
let mut evap = BphxEvaporator::new(geo)
|
||||
.with_mode(mode)
|
||||
.with_refrigerant(refrigerant)
|
||||
.with_secondary_fluid(secondary_fluid)
|
||||
.with_fluid_backend(Arc::clone(&backend))
|
||||
.with_correlation(correlation);
|
||||
|
||||
if params.contains_key("hot_fluid") {
|
||||
let hot = parse_side_conditions(params, "hot")?;
|
||||
evap.set_secondary_conditions(hot);
|
||||
}
|
||||
if params.contains_key("cold_fluid") {
|
||||
let cold = parse_side_conditions(params, "cold")?;
|
||||
evap.set_refrigerant_conditions(cold);
|
||||
}
|
||||
|
||||
let ua_nominal = evap.ua();
|
||||
let config_ua = params.get("ua").and_then(|v| v.as_f64());
|
||||
let f_ua = config_ua
|
||||
.map(|u| if ua_nominal > 0.0 { u / ua_nominal } else { 1.0 })
|
||||
.unwrap_or_else(|| {
|
||||
params
|
||||
.get("f_ua")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(1.0)
|
||||
});
|
||||
let f_dp = params.get("f_dp").and_then(|v| v.as_f64()).unwrap_or(1.0);
|
||||
evap.set_calib(Calib {
|
||||
f_m: 1.0,
|
||||
f_dp,
|
||||
f_ua,
|
||||
f_power: 1.0,
|
||||
f_etav: 1.0,
|
||||
});
|
||||
|
||||
Ok(Box::new(evap))
|
||||
}
|
||||
|
||||
// ── BphxCondenser (brazed plate HX condenser) ───────────────────────────
|
||||
"BphxCondenser" => {
|
||||
use entropyk_components::heat_exchanger::{
|
||||
BphxCondenser, BphxCorrelation, BphxType,
|
||||
};
|
||||
use entropyk_core::Calib;
|
||||
|
||||
let geo = bphx_geometry_from_params(params, BphxType::Condenser);
|
||||
let refrigerant = params
|
||||
.get("refrigerant")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or_else(|| _primary_fluid.as_str());
|
||||
let secondary_fluid = params
|
||||
.get("secondary_fluid")
|
||||
.and_then(|v| v.as_str())
|
||||
.unwrap_or("Water");
|
||||
let target_subcooling = params
|
||||
.get("target_subcooling_k")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(3.0);
|
||||
|
||||
let correlation = match params.get("correlation").and_then(|v| v.as_str()) {
|
||||
Some("Shah1979") => BphxCorrelation::Shah1979,
|
||||
Some("Shah2021") => BphxCorrelation::Shah2021,
|
||||
_ => BphxCorrelation::Longo2004,
|
||||
};
|
||||
|
||||
let mut cond = BphxCondenser::new(geo)
|
||||
.with_refrigerant(refrigerant)
|
||||
.with_secondary_fluid(secondary_fluid)
|
||||
.with_fluid_backend(Arc::clone(&backend))
|
||||
.with_target_subcooling(target_subcooling)
|
||||
.with_correlation(correlation);
|
||||
|
||||
if params.contains_key("hot_fluid") {
|
||||
let hot = parse_side_conditions(params, "hot")?;
|
||||
cond.set_refrigerant_conditions(hot);
|
||||
}
|
||||
if params.contains_key("cold_fluid") {
|
||||
let cold = parse_side_conditions(params, "cold")?;
|
||||
cond.set_secondary_conditions(cold);
|
||||
}
|
||||
|
||||
let ua_nominal = cond.ua();
|
||||
let config_ua = params.get("ua").and_then(|v| v.as_f64());
|
||||
let f_ua = config_ua
|
||||
.map(|u| if ua_nominal > 0.0 { u / ua_nominal } else { 1.0 })
|
||||
.unwrap_or_else(|| {
|
||||
params
|
||||
.get("f_ua")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(1.0)
|
||||
});
|
||||
let f_dp = params.get("f_dp").and_then(|v| v.as_f64()).unwrap_or(1.0);
|
||||
cond.set_calib(Calib {
|
||||
f_m: 1.0,
|
||||
f_dp,
|
||||
f_ua,
|
||||
f_power: 1.0,
|
||||
f_etav: 1.0,
|
||||
});
|
||||
|
||||
Ok(Box::new(cond))
|
||||
}
|
||||
|
||||
"Placeholder" => {
|
||||
@@ -886,7 +1376,7 @@ fn create_component(
|
||||
}
|
||||
|
||||
_ => Err(CliError::Config(format!(
|
||||
"Unknown component type: '{}'. Supported: ScrewEconomizerCompressor, MchxCondenserCoil, FloodedEvaporator, Condenser, CondenserCoil, Evaporator, EvaporatorCoil, HeatExchanger, Compressor, ExpansionValve, Pump, Placeholder",
|
||||
"Unknown component type: '{}'. Supported: ScrewEconomizerCompressor, MchxCondenserCoil, FloodedEvaporator, BphxEvaporator, BphxCondenser, Condenser, CondenserCoil, Evaporator, EvaporatorCoil, HeatExchanger, Compressor, ExpansionValve, Pump, Placeholder",
|
||||
component_type
|
||||
))),
|
||||
}
|
||||
@@ -914,7 +1404,6 @@ fn extract_state(converged: &entropyk::ConvergedState) -> Vec<StateEntry> {
|
||||
// Python-style components for CLI (no type-state pattern)
|
||||
// =============================================================================
|
||||
|
||||
use entropyk_fluids::FluidId as FluidsFluidId;
|
||||
use std::fmt;
|
||||
|
||||
struct SimpleComponent {
|
||||
@@ -974,153 +1463,9 @@ impl fmt::Debug for SimpleComponent {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)] // fields retained for documentation & future physical residuals
|
||||
struct PyCompressor {
|
||||
fluid: FluidsFluidId,
|
||||
speed_rpm: f64,
|
||||
displacement_m3: f64,
|
||||
efficiency: f64,
|
||||
m1: f64,
|
||||
m2: f64,
|
||||
m3: f64,
|
||||
m4: f64,
|
||||
m5: f64,
|
||||
m6: f64,
|
||||
m7: f64,
|
||||
m8: f64,
|
||||
m9: f64,
|
||||
m10: f64,
|
||||
}
|
||||
// PyCompressor stub REMOVED — replaced by real Compressor<Connected> in create_component().
|
||||
|
||||
impl PyCompressor {
|
||||
fn new(fluid: &str, speed_rpm: f64, displacement_m3: f64, efficiency: f64) -> Self {
|
||||
Self {
|
||||
fluid: FluidsFluidId::new(fluid),
|
||||
speed_rpm,
|
||||
displacement_m3,
|
||||
efficiency,
|
||||
m1: 0.85,
|
||||
m2: 2.5,
|
||||
m3: 500.0,
|
||||
m4: 1500.0,
|
||||
m5: -2.5,
|
||||
m6: 1.8,
|
||||
m7: 600.0,
|
||||
m8: 1600.0,
|
||||
m9: -3.0,
|
||||
m10: 2.0,
|
||||
}
|
||||
}
|
||||
|
||||
fn with_coefficients(
|
||||
mut self,
|
||||
m1: f64,
|
||||
m2: f64,
|
||||
m3: f64,
|
||||
m4: f64,
|
||||
m5: f64,
|
||||
m6: f64,
|
||||
m7: f64,
|
||||
m8: f64,
|
||||
m9: f64,
|
||||
m10: f64,
|
||||
) -> Self {
|
||||
self.m1 = m1;
|
||||
self.m2 = m2;
|
||||
self.m3 = m3;
|
||||
self.m4 = m4;
|
||||
self.m5 = m5;
|
||||
self.m6 = m6;
|
||||
self.m7 = m7;
|
||||
self.m8 = m8;
|
||||
self.m9 = m9;
|
||||
self.m10 = m10;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl entropyk::Component for PyCompressor {
|
||||
fn compute_residuals(
|
||||
&self,
|
||||
state: &[f64],
|
||||
residuals: &mut entropyk::ResidualVector,
|
||||
) -> Result<(), entropyk::ComponentError> {
|
||||
for r in residuals.iter_mut() {
|
||||
*r = 0.0;
|
||||
}
|
||||
if state.len() >= 2 {
|
||||
residuals[0] = state[0] * 1e-3;
|
||||
residuals[1] = state[1] * 1e-3;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn jacobian_entries(
|
||||
&self,
|
||||
_state: &[f64],
|
||||
jacobian: &mut entropyk::JacobianBuilder,
|
||||
) -> Result<(), entropyk::ComponentError> {
|
||||
jacobian.add_entry(0, 0, 1.0);
|
||||
jacobian.add_entry(1, 1, 1.0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn n_equations(&self) -> usize {
|
||||
2
|
||||
}
|
||||
fn get_ports(&self) -> &[entropyk::ConnectedPort] {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
#[allow(dead_code)] // fields retained for documentation & future physical residuals
|
||||
struct PyExpansionValve {
|
||||
fluid: FluidsFluidId,
|
||||
opening: f64,
|
||||
}
|
||||
|
||||
impl PyExpansionValve {
|
||||
fn new(fluid: &str, opening: f64) -> Self {
|
||||
Self {
|
||||
fluid: FluidsFluidId::new(fluid),
|
||||
opening,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl entropyk::Component for PyExpansionValve {
|
||||
fn compute_residuals(
|
||||
&self,
|
||||
state: &[f64],
|
||||
residuals: &mut entropyk::ResidualVector,
|
||||
) -> Result<(), entropyk::ComponentError> {
|
||||
for r in residuals.iter_mut() {
|
||||
*r = 0.0;
|
||||
}
|
||||
if !state.is_empty() {
|
||||
residuals[0] = state[0] * 1e-3;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn jacobian_entries(
|
||||
&self,
|
||||
_state: &[f64],
|
||||
jacobian: &mut entropyk::JacobianBuilder,
|
||||
) -> Result<(), entropyk::ComponentError> {
|
||||
jacobian.add_entry(0, 0, 1.0);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn n_equations(&self) -> usize {
|
||||
1
|
||||
}
|
||||
fn get_ports(&self) -> &[entropyk::ConnectedPort] {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
// PyExpansionValve stub REMOVED — replaced by real ExpansionValve<Connected> in create_component().
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
Reference in New Issue
Block a user