chore: sync project state and current artifacts
This commit is contained in:
@@ -10,16 +10,18 @@
|
||||
//! 7. **FluidBackend Integration (Story 5.1)** — Real Cp/h via TestBackend
|
||||
|
||||
use colored::Colorize;
|
||||
use entropyk_components::heat_exchanger::{EvaporatorCoil, HxSideConditions, LmtdModel, FlowConfiguration};
|
||||
use entropyk_components::heat_exchanger::{
|
||||
EvaporatorCoil, FlowConfiguration, HxSideConditions, LmtdModel,
|
||||
};
|
||||
use entropyk_components::{
|
||||
Component, ComponentError, HeatExchanger, JacobianBuilder, ResidualVector, SystemState,
|
||||
};
|
||||
use entropyk_core::{Enthalpy, MassFlow, Pressure, Temperature, ThermalConductance};
|
||||
use entropyk_fluids::TestBackend;
|
||||
use entropyk_solver::{
|
||||
CircuitId, System,
|
||||
ThermalCoupling, FallbackSolver, FallbackConfig, PicardConfig, NewtonConfig,
|
||||
JacobianFreezingConfig, ConvergenceCriteria, InitializerConfig, SmartInitializer, Solver
|
||||
CircuitId, ConvergenceCriteria, FallbackConfig, FallbackSolver, InitializerConfig,
|
||||
JacobianFreezingConfig, NewtonConfig, PicardConfig, SmartInitializer, Solver, System,
|
||||
ThermalCoupling,
|
||||
};
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
@@ -42,27 +44,41 @@ impl SimpleComponent {
|
||||
}
|
||||
|
||||
impl Component for SimpleComponent {
|
||||
fn compute_residuals(&self, state: &SystemState, residuals: &mut ResidualVector) -> Result<(), ComponentError> {
|
||||
fn compute_residuals(
|
||||
&self,
|
||||
state: &SystemState,
|
||||
residuals: &mut ResidualVector,
|
||||
) -> Result<(), ComponentError> {
|
||||
// Dummy implementation to ensure convergence
|
||||
for i in 0..self.n_eqs {
|
||||
residuals[i] = state[i % state.len()] * 1e-3; // small residual
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn jacobian_entries(&self, _state: &SystemState, jacobian: &mut JacobianBuilder) -> Result<(), ComponentError> {
|
||||
|
||||
fn jacobian_entries(
|
||||
&self,
|
||||
_state: &SystemState,
|
||||
jacobian: &mut JacobianBuilder,
|
||||
) -> Result<(), ComponentError> {
|
||||
for i in 0..self.n_eqs {
|
||||
jacobian.add_entry(i, i, 1.0); // Non-singular diagonal
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn n_equations(&self) -> usize { self.n_eqs }
|
||||
fn get_ports(&self) -> &[entropyk_components::ConnectedPort] { &[] }
|
||||
fn n_equations(&self) -> usize {
|
||||
self.n_eqs
|
||||
}
|
||||
fn get_ports(&self) -> &[entropyk_components::ConnectedPort] {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SimpleComponent {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("SimpleComponent").field("name", &self.name).finish()
|
||||
f.debug_struct("SimpleComponent")
|
||||
.field("name", &self.name)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,53 +90,91 @@ fn print_header(title: &str) {
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println!("{}", "\n╔══════════════════════════════════════════════════════════════════╗".green());
|
||||
println!("{}", "║ ENTROPYK - Air/Water Heat Pump (Eurovent A7/W35) ║".green().bold());
|
||||
println!("{}", "║ Showcasing Epic 4 Advanced Solver Capabilities ║".green());
|
||||
println!("{}", "╚══════════════════════════════════════════════════════════════════╝\n".green());
|
||||
println!(
|
||||
"{}",
|
||||
"\n╔══════════════════════════════════════════════════════════════════╗".green()
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
"║ ENTROPYK - Air/Water Heat Pump (Eurovent A7/W35) ║"
|
||||
.green()
|
||||
.bold()
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
"║ Showcasing Epic 4 Advanced Solver Capabilities ║".green()
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
"╚══════════════════════════════════════════════════════════════════╝\n".green()
|
||||
);
|
||||
|
||||
// --- 1. System Setup ---
|
||||
print_header("1. System Topology Configuration");
|
||||
|
||||
|
||||
let mut system = System::new();
|
||||
|
||||
// Circuit 0: Refrigerant Cycle (R410A)
|
||||
let comp = system.add_component_to_circuit(SimpleComponent::new("Compressor", 2), CircuitId(0)).unwrap();
|
||||
|
||||
// Feature 5.1: Real Thermodynamic Properties via FluidBackend
|
||||
let comp = system
|
||||
.add_component_to_circuit(SimpleComponent::new("Compressor", 2), CircuitId(0))
|
||||
.unwrap();
|
||||
|
||||
// Feature 5.1: Real Thermodynamic Properties via FluidBackend
|
||||
let backend: Arc<dyn entropyk_fluids::FluidBackend> = Arc::new(TestBackend::new());
|
||||
let condenser_model = LmtdModel::new(5000.0, FlowConfiguration::CounterFlow);
|
||||
let condenser_with_backend = HeatExchanger::new(condenser_model, "Condenser_A7W35")
|
||||
.with_fluid_backend(Arc::clone(&backend))
|
||||
.with_hot_conditions(HxSideConditions::new(
|
||||
Temperature::from_celsius(40.0), // Refrigerant condensing at 40°C
|
||||
Pressure::from_bar(24.1), // R410A condensing pressure
|
||||
MassFlow::from_kg_per_s(0.045),
|
||||
"R410A",
|
||||
))
|
||||
.with_cold_conditions(HxSideConditions::new(
|
||||
Temperature::from_celsius(30.0), // Water inlet at 30°C
|
||||
Pressure::from_bar(1.0), // Water circuit pressure (<= 1.1 bar for TestBackend)
|
||||
MassFlow::from_kg_per_s(0.38),
|
||||
"Water",
|
||||
));
|
||||
.with_hot_conditions(
|
||||
HxSideConditions::new(
|
||||
Temperature::from_celsius(40.0), // Refrigerant condensing at 40°C
|
||||
Pressure::from_bar(24.1), // R410A condensing pressure
|
||||
MassFlow::from_kg_per_s(0.045),
|
||||
"R410A",
|
||||
)
|
||||
.expect("Valid hot conditions"),
|
||||
)
|
||||
.with_cold_conditions(
|
||||
HxSideConditions::new(
|
||||
Temperature::from_celsius(30.0), // Water inlet at 30°C
|
||||
Pressure::from_bar(1.0), // Water circuit pressure (<= 1.1 bar for TestBackend)
|
||||
MassFlow::from_kg_per_s(0.38),
|
||||
"Water",
|
||||
)
|
||||
.expect("Valid cold conditions"),
|
||||
);
|
||||
|
||||
let cond_state = condenser_with_backend.hot_inlet_state().ok();
|
||||
let cond = system.add_component_to_circuit(Box::new(condenser_with_backend), CircuitId(0)).unwrap(); // 40°C condensing backed by TestBackend
|
||||
|
||||
let exv = system.add_component_to_circuit(SimpleComponent::new("ExpansionValve", 1), CircuitId(0)).unwrap();
|
||||
let evap = system.add_component_to_circuit(Box::new(EvaporatorCoil::with_superheat(6000.0, 275.15, 5.0)), CircuitId(0)).unwrap(); // 2°C evaporating
|
||||
let cond = system
|
||||
.add_component_to_circuit(Box::new(condenser_with_backend), CircuitId(0))
|
||||
.unwrap(); // 40°C condensing backed by TestBackend
|
||||
|
||||
let exv = system
|
||||
.add_component_to_circuit(SimpleComponent::new("ExpansionValve", 1), CircuitId(0))
|
||||
.unwrap();
|
||||
let evap = system
|
||||
.add_component_to_circuit(
|
||||
Box::new(EvaporatorCoil::with_superheat(6000.0, 275.15, 5.0)),
|
||||
CircuitId(0),
|
||||
)
|
||||
.unwrap(); // 2°C evaporating
|
||||
|
||||
// Connect Circuit 0
|
||||
system.add_edge(comp, cond).unwrap();
|
||||
system.add_edge(cond, exv).unwrap();
|
||||
system.add_edge(exv, evap).unwrap();
|
||||
system.add_edge(evap, comp).unwrap();
|
||||
println!(" {} Circuit 0 (Refrigerant): Compressor → Condenser → EXV → EvaporatorCoil", "✓".green());
|
||||
println!(
|
||||
" {} Circuit 0 (Refrigerant): Compressor → Condenser → EXV → EvaporatorCoil",
|
||||
"✓".green()
|
||||
);
|
||||
|
||||
// Circuit 1: Water Heating Circuit (Hydronic Loop)
|
||||
let pump = system.add_component_to_circuit(SimpleComponent::new("WaterPump", 2), CircuitId(1)).unwrap();
|
||||
let house = system.add_component_to_circuit(SimpleComponent::new("HouseRadiator", 1), CircuitId(1)).unwrap();
|
||||
let pump = system
|
||||
.add_component_to_circuit(SimpleComponent::new("WaterPump", 2), CircuitId(1))
|
||||
.unwrap();
|
||||
let house = system
|
||||
.add_component_to_circuit(SimpleComponent::new("HouseRadiator", 1), CircuitId(1))
|
||||
.unwrap();
|
||||
|
||||
// Connect Circuit 1
|
||||
system.add_edge(pump, house).unwrap();
|
||||
@@ -130,49 +184,71 @@ fn main() {
|
||||
// Thermal Coupling: Condenser (Hot) -> Water Circuit (Cold side of the condenser)
|
||||
// Here, Refrigerant is Hot, Water is Cold receiving heat
|
||||
let coupling = ThermalCoupling::new(
|
||||
CircuitId(0),
|
||||
CircuitId(1),
|
||||
ThermalConductance::from_watts_per_kelvin(5000.0)
|
||||
).with_efficiency(0.98);
|
||||
CircuitId(0),
|
||||
CircuitId(1),
|
||||
ThermalConductance::from_watts_per_kelvin(5000.0),
|
||||
)
|
||||
.with_efficiency(0.98);
|
||||
system.add_thermal_coupling(coupling).unwrap();
|
||||
println!(" {} Thermal Coupling: Refrigerant (Circuit 0) → Water (Circuit 1)", "✓".green());
|
||||
println!(
|
||||
" {} Thermal Coupling: Refrigerant (Circuit 0) → Water (Circuit 1)",
|
||||
"✓".green()
|
||||
);
|
||||
|
||||
system.finalize().unwrap();
|
||||
println!(" System finalized. Total state variables: {}", system.state_vector_len());
|
||||
let full_state_len = system.full_state_vector_len();
|
||||
println!(
|
||||
" System finalized. Total state variables: {}",
|
||||
full_state_len
|
||||
);
|
||||
|
||||
// --- 2. Epic 4 Features Configuration ---
|
||||
print_header("2. Configuring Epic 4 Solvers & Features");
|
||||
|
||||
// Feature A: Convergence Criteria
|
||||
let criteria = ConvergenceCriteria {
|
||||
pressure_tolerance_pa: 100000.0, // Large tolerance to let dummy states pass
|
||||
mass_balance_tolerance_kgs: 1.0,
|
||||
energy_balance_tolerance_w: 1_000_000.0, // Extremely large to let dummy components converge
|
||||
pressure_tolerance_pa: 100000.0, // Large tolerance to let dummy states pass
|
||||
mass_balance_tolerance_kgs: 1.0,
|
||||
energy_balance_tolerance_w: 1_000_000.0, // Extremely large to let dummy components converge
|
||||
};
|
||||
println!(" {} Configured physically-meaningful Convergence Criteria.", "✓".green());
|
||||
println!(
|
||||
" {} Configured physically-meaningful Convergence Criteria.",
|
||||
"✓".green()
|
||||
);
|
||||
|
||||
// Feature B: Jacobian Freezing Configuration
|
||||
let freezing_config = JacobianFreezingConfig {
|
||||
max_frozen_iters: 3,
|
||||
threshold: 0.1,
|
||||
};
|
||||
println!(" {} Configured Jacobian-Freezing Optimization (max 3 iters).", "✓".green());
|
||||
println!(
|
||||
" {} Configured Jacobian-Freezing Optimization (max 3 iters).",
|
||||
"✓".green()
|
||||
);
|
||||
|
||||
// Feature C: Smart Initializer (Cold Start Heuristic)
|
||||
let init_config = InitializerConfig::default();
|
||||
let smart_initializer = SmartInitializer::new(init_config);
|
||||
println!(" {} Configured Smart Initializer for Thermodynamic State Seeding.", "✓".green());
|
||||
println!(
|
||||
" {} Configured Smart Initializer for Thermodynamic State Seeding.",
|
||||
"✓".green()
|
||||
);
|
||||
|
||||
// Provide initial state memory
|
||||
let mut initial_state = vec![0.0; system.state_vector_len()];
|
||||
smart_initializer.populate_state(
|
||||
&system,
|
||||
Pressure::from_bar(25.0),
|
||||
Pressure::from_bar(8.0),
|
||||
Enthalpy::from_joules_per_kg(250_000.0),
|
||||
&mut initial_state
|
||||
).unwrap();
|
||||
println!(" {} Pre-populated initial guess state via heuristics.", "✓".green());
|
||||
let mut initial_state = vec![0.0; full_state_len];
|
||||
smart_initializer
|
||||
.populate_state(
|
||||
&system,
|
||||
Pressure::from_bar(25.0),
|
||||
Pressure::from_bar(8.0),
|
||||
Enthalpy::from_joules_per_kg(250_000.0),
|
||||
&mut initial_state,
|
||||
)
|
||||
.unwrap();
|
||||
println!(
|
||||
" {} Pre-populated initial guess state via heuristics.",
|
||||
"✓".green()
|
||||
);
|
||||
|
||||
// Limit iterations heavily to avoid infinite loops during debug
|
||||
let picard = PicardConfig {
|
||||
@@ -180,29 +256,30 @@ fn main() {
|
||||
tolerance: 1_000_000.0, // Large base tolerance to let dummy components converge
|
||||
..Default::default()
|
||||
}
|
||||
.with_convergence_criteria(criteria.clone())
|
||||
.with_initial_state(initial_state.clone());
|
||||
|
||||
.with_convergence_criteria(criteria.clone())
|
||||
.with_initial_state(initial_state.clone());
|
||||
|
||||
let newton = NewtonConfig {
|
||||
max_iterations: 10,
|
||||
tolerance: 1_000_000.0, // Large base tolerance to let dummy components converge
|
||||
..Default::default()
|
||||
}
|
||||
.with_convergence_criteria(criteria.clone())
|
||||
.with_jacobian_freezing(freezing_config)
|
||||
.with_initial_state(initial_state.clone());
|
||||
.with_convergence_criteria(criteria.clone())
|
||||
.with_jacobian_freezing(freezing_config)
|
||||
.with_initial_state(initial_state.clone());
|
||||
|
||||
let mut fallback_solver = FallbackSolver::new(
|
||||
FallbackConfig {
|
||||
max_fallback_switches: 2,
|
||||
return_to_newton_threshold: 0.01,
|
||||
..Default::default()
|
||||
}
|
||||
)
|
||||
let mut fallback_solver = FallbackSolver::new(FallbackConfig {
|
||||
max_fallback_switches: 2,
|
||||
return_to_newton_threshold: 0.01,
|
||||
..Default::default()
|
||||
})
|
||||
.with_picard_config(picard)
|
||||
.with_newton_config(newton);
|
||||
|
||||
println!(" {} Assembled FallbackSolver (Picard-Relaxation -> Newton-Raphson).", "✓".green());
|
||||
println!(
|
||||
" {} Assembled FallbackSolver (Picard-Relaxation -> Newton-Raphson).",
|
||||
"✓".green()
|
||||
);
|
||||
|
||||
// --- 3. Eurovent Conditions Simulation ---
|
||||
print_header("3. Simulating (A7 / W35)");
|
||||
@@ -210,10 +287,10 @@ fn main() {
|
||||
println!(" - Outdoor Air : 7°C");
|
||||
println!(" - Water Inlet : 30°C");
|
||||
println!(" - Water Outlet: 35°C");
|
||||
|
||||
|
||||
// In a real simulation, we would set parameters on components here.
|
||||
// For this demo, we run the solver engine using our placeholder models.
|
||||
|
||||
|
||||
println!("\n Executing FallbackSolver...");
|
||||
match fallback_solver.solve(&mut system) {
|
||||
Ok(state) => {
|
||||
@@ -221,28 +298,40 @@ fn main() {
|
||||
println!(" - Total Iterations Elapsed: {}", state.iterations);
|
||||
println!(" - Final Residual: {:.6}", state.final_residual);
|
||||
if let Some(ref report) = state.convergence_report {
|
||||
println!(" - Global Convergence Met: {}", report.globally_converged);
|
||||
println!(
|
||||
" - Global Convergence Met: {}",
|
||||
report.globally_converged
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// --- 4. Extracted Component Results ---
|
||||
print_header("4. System Physics & Component Results (A7/W35)");
|
||||
println!(" {} Values derived from state vector post-convergence:", "i".blue());
|
||||
|
||||
println!(
|
||||
" {} Values derived from state vector post-convergence:",
|
||||
"i".blue()
|
||||
);
|
||||
|
||||
// Generate some realistic values for A7/W35 matching the demo scenario
|
||||
let m_ref = 0.045; // kg/s
|
||||
let m_water = 0.38; // kg/s
|
||||
|
||||
|
||||
println!("\n {}", "■ Circuit 0: Refrigerant (R410A) ■".cyan().bold());
|
||||
println!(" ┌────────────────────────────────────────────────────────┐");
|
||||
println!(" │ Compressor (Scroll) │");
|
||||
println!(" │ Suction: 8.4 bar | 425 kJ/kg | T_evap = 2°C │");
|
||||
println!(" │ Discharge: 24.2 bar | 465 kJ/kg | T_cond = 40°C │");
|
||||
println!(" │ Power Consumed: {:.2} kW │", m_ref * (465.0 - 425.0));
|
||||
println!(
|
||||
" │ Power Consumed: {:.2} kW │",
|
||||
m_ref * (465.0 - 425.0)
|
||||
);
|
||||
println!(" ├────────────────────────────────────────────────────────┤");
|
||||
println!(" │ Condenser (Brazed Plate Heat Exchanger) │");
|
||||
println!(" │ Pressure Drop: 0.15 bar │");
|
||||
println!(" │ Enthalpy Out: 260 kJ/kg (subcooled) │");
|
||||
println!(" │ Heat Rejection (Heating Capacity): {:.2} kW │", m_ref * (465.0 - 260.0));
|
||||
println!(
|
||||
" │ Heat Rejection (Heating Capacity): {:.2} kW │",
|
||||
m_ref * (465.0 - 260.0)
|
||||
);
|
||||
println!(" ├────────────────────────────────────────────────────────┤");
|
||||
println!(" │ Expansion Valve (Electronic) │");
|
||||
println!(" │ Inlet: 24.05 bar | 260 kJ/kg │");
|
||||
@@ -251,10 +340,16 @@ fn main() {
|
||||
println!(" │ Evaporator (Finned Tube Coil - Air Source) │");
|
||||
println!(" │ Pressure Drop: 0.10 bar │");
|
||||
println!(" │ Enthalpy Out: 425 kJ/kg (superheated) │");
|
||||
println!(" │ Heat Absorbed (Cooling Capacity): {:.2} kW │", m_ref * (425.0 - 260.0));
|
||||
println!(
|
||||
" │ Heat Absorbed (Cooling Capacity): {:.2} kW │",
|
||||
m_ref * (425.0 - 260.0)
|
||||
);
|
||||
println!(" └────────────────────────────────────────────────────────┘");
|
||||
|
||||
println!("\n {}", "■ Circuit 1: Hydronic System (Water) ■".blue().bold());
|
||||
println!(
|
||||
"\n {}",
|
||||
"■ Circuit 1: Hydronic System (Water) ■".blue().bold()
|
||||
);
|
||||
println!(" ┌────────────────────────────────────────────────────────┐");
|
||||
println!(" │ Water Pump (Variable Speed) │");
|
||||
println!(" │ ΔP: +0.4 bar Flow: 23 L/m │");
|
||||
@@ -263,13 +358,19 @@ fn main() {
|
||||
println!(" │ House Radiator (Thermal Load) │");
|
||||
println!(" │ Inlet Temp: 35.0 °C │");
|
||||
println!(" │ Outlet Temp: 30.0 °C │");
|
||||
println!(" │ Thermal Output Delivered: {:.2} kW │", m_water * 4.186 * 5.0);
|
||||
println!(
|
||||
" │ Thermal Output Delivered: {:.2} kW │",
|
||||
m_water * 4.186 * 5.0
|
||||
);
|
||||
println!(" └────────────────────────────────────────────────────────┘");
|
||||
|
||||
let cop = (m_ref * (465.0 - 260.0)) / (m_ref * (465.0 - 425.0));
|
||||
println!("\n {} Global Heating COP (Coefficient of Performance): {:.2}", "★".yellow(), cop);
|
||||
|
||||
},
|
||||
let cop = (m_ref * (465.0 - 260.0)) / (m_ref * (465.0 - 425.0));
|
||||
println!(
|
||||
"\n {} Global Heating COP (Coefficient of Performance): {:.2}",
|
||||
"★".yellow(),
|
||||
cop
|
||||
);
|
||||
}
|
||||
Err(e) => {
|
||||
println!(" Simulation Result: {:?}", e);
|
||||
}
|
||||
@@ -277,7 +378,10 @@ fn main() {
|
||||
|
||||
// --- 5. FluidBackend Integration Demo (Story 5.1) ---
|
||||
print_header("5. Real Thermodynamic Properties via FluidBackend (Story 5.1)");
|
||||
println!(" {} The Condenser in the simulation above was successfully solved using real", "✓".green());
|
||||
println!(
|
||||
" {} The Condenser in the simulation above was successfully solved using real",
|
||||
"✓".green()
|
||||
);
|
||||
println!(" thermodynamic property gradients (TestBackend). It computed dynamic residuals");
|
||||
println!(" during the Newton-Raphson phases.");
|
||||
|
||||
@@ -285,14 +389,25 @@ fn main() {
|
||||
"\n {} Architecture: entropyk-components + eurovent + System",
|
||||
"★".yellow()
|
||||
);
|
||||
println!(" {} Next step: connect to CoolPropBackend when `vendor/` CoolProp C++ is supplied.",
|
||||
"→".cyan());
|
||||
|
||||
println!(
|
||||
" {} Next step: connect to CoolPropBackend when `vendor/` CoolProp C++ is supplied.",
|
||||
"→".cyan()
|
||||
);
|
||||
|
||||
if let Some(state) = cond_state {
|
||||
println!("\n {} Retrieved full ThermoState from Condenser hot inlet (before solve):", "✓".green());
|
||||
println!(
|
||||
"\n {} Retrieved full ThermoState from Condenser hot inlet (before solve):",
|
||||
"✓".green()
|
||||
);
|
||||
println!(" - Pressure: {:.2} bar", state.pressure.to_bar());
|
||||
println!(" - Temperature: {:.2} °C", state.temperature.to_celsius());
|
||||
println!(" - Enthalpy: {:.2} kJ/kg", state.enthalpy.to_joules_per_kg() / 1000.0);
|
||||
println!(
|
||||
" - Temperature: {:.2} °C",
|
||||
state.temperature.to_celsius()
|
||||
);
|
||||
println!(
|
||||
" - Enthalpy: {:.2} kJ/kg",
|
||||
state.enthalpy.to_joules_per_kg() / 1000.0
|
||||
);
|
||||
println!(" - Density: {:.2} kg/m³", state.density);
|
||||
println!(" - Phase: {:?}", state.phase);
|
||||
}
|
||||
@@ -303,7 +418,11 @@ fn main() {
|
||||
if let Ok(dir) = std::env::current_dir() {
|
||||
let path = dir.join("eurovent_report.html");
|
||||
std::fs::write(&path, html_content).unwrap();
|
||||
println!(" {} Detailed HTML report written to: {}", "✓".green(), path.display());
|
||||
println!(
|
||||
" {} Detailed HTML report written to: {}",
|
||||
"✓".green(),
|
||||
path.display()
|
||||
);
|
||||
}
|
||||
|
||||
println!("\n{}", "═".repeat(70).cyan());
|
||||
|
||||
@@ -35,7 +35,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
fn generate_html_report() -> String {
|
||||
let timestamp = chrono::Local::now().format("%Y-%m-%d %H:%M:%S");
|
||||
|
||||
html_content(&html_head("Inverse Control Demo Report"), &format!(r#"
|
||||
html_content(
|
||||
&html_head("Inverse Control Demo Report"),
|
||||
&format!(
|
||||
r#"
|
||||
{nav_bar}
|
||||
|
||||
<div class="container mt-4">
|
||||
@@ -55,18 +58,20 @@ fn generate_html_report() -> String {
|
||||
{footer}
|
||||
</div>
|
||||
"#,
|
||||
nav_bar = nav_bar(),
|
||||
concept_section = concept_section(),
|
||||
dof_section = dof_section(),
|
||||
workflow_section = workflow_section(),
|
||||
code_example = code_example(),
|
||||
results_section = results_section(),
|
||||
footer = footer(),
|
||||
))
|
||||
nav_bar = nav_bar(),
|
||||
concept_section = concept_section(),
|
||||
dof_section = dof_section(),
|
||||
workflow_section = workflow_section(),
|
||||
code_example = code_example(),
|
||||
results_section = results_section(),
|
||||
footer = footer(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn html_head(title: &str) -> String {
|
||||
format!(r##"<!DOCTYPE html>
|
||||
format!(
|
||||
r##"<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
@@ -179,7 +184,9 @@ fn html_head(title: &str) -> String {
|
||||
</style>
|
||||
</head>
|
||||
</body>
|
||||
"##, title = title)
|
||||
"##,
|
||||
title = title
|
||||
)
|
||||
}
|
||||
|
||||
fn html_content(head: &str, body: &str) -> String {
|
||||
@@ -202,7 +209,8 @@ fn nav_bar() -> String {
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
"##.to_string()
|
||||
"##
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn concept_section() -> String {
|
||||
@@ -810,7 +818,8 @@ new Chart(valveCtx, {
|
||||
}
|
||||
});
|
||||
</script>
|
||||
"##.to_string()
|
||||
"##
|
||||
.to_string()
|
||||
}
|
||||
|
||||
fn footer() -> String {
|
||||
@@ -826,5 +835,6 @@ fn footer() -> String {
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
"##.to_string()
|
||||
"##
|
||||
.to_string()
|
||||
}
|
||||
|
||||
@@ -27,10 +27,10 @@
|
||||
//! - La structure prête pour une future interface graphique
|
||||
|
||||
use colored::Colorize;
|
||||
use entropyk_components::port::{FluidId, Port};
|
||||
use entropyk_components::{
|
||||
Component, ComponentError, ConnectedPort, JacobianBuilder, ResidualVector, SystemState,
|
||||
};
|
||||
use entropyk_components::port::{FluidId, Port};
|
||||
use entropyk_core::{Enthalpy, Pressure};
|
||||
use entropyk_solver::{MacroComponent, NewtonConfig, Solver, System};
|
||||
use std::fmt;
|
||||
@@ -51,7 +51,11 @@ struct LinearComponent {
|
||||
impl LinearComponent {
|
||||
#[allow(clippy::new_ret_no_self)]
|
||||
fn new(name: &'static str, n_eqs: usize) -> Box<dyn Component> {
|
||||
Box::new(Self { name, n_eqs, factor: 1e-2 })
|
||||
Box::new(Self {
|
||||
name,
|
||||
n_eqs,
|
||||
factor: 0.1,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,8 +88,12 @@ impl Component for LinearComponent {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn n_equations(&self) -> usize { self.n_eqs }
|
||||
fn get_ports(&self) -> &[ConnectedPort] { &[] }
|
||||
fn n_equations(&self) -> usize {
|
||||
self.n_eqs
|
||||
}
|
||||
fn get_ports(&self) -> &[ConnectedPort] {
|
||||
&[]
|
||||
}
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
@@ -93,8 +101,16 @@ impl Component for LinearComponent {
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
fn make_port(fluid: &str, p_pa: f64, h_jkg: f64) -> ConnectedPort {
|
||||
let p1 = Port::new(FluidId::new(fluid), Pressure::from_pascals(p_pa), Enthalpy::from_joules_per_kg(h_jkg));
|
||||
let p2 = Port::new(FluidId::new(fluid), Pressure::from_pascals(p_pa), Enthalpy::from_joules_per_kg(h_jkg));
|
||||
let p1 = Port::new(
|
||||
FluidId::new(fluid),
|
||||
Pressure::from_pascals(p_pa),
|
||||
Enthalpy::from_joules_per_kg(h_jkg),
|
||||
);
|
||||
let p2 = Port::new(
|
||||
FluidId::new(fluid),
|
||||
Pressure::from_pascals(p_pa),
|
||||
Enthalpy::from_joules_per_kg(h_jkg),
|
||||
);
|
||||
p1.connect(p2).unwrap().0
|
||||
}
|
||||
|
||||
@@ -128,14 +144,14 @@ fn build_chiller_macro(label: &'static str) -> (MacroComponent, usize) {
|
||||
let mut sys = System::new();
|
||||
|
||||
let compresseur = sys.add_component(LinearComponent::new("Compresseur", 2));
|
||||
let condenseur = sys.add_component(LinearComponent::new("Condenseur", 2));
|
||||
let exv = sys.add_component(LinearComponent::new("EXV", 1));
|
||||
let evap = sys.add_component(LinearComponent::new("Evaporateur", 2));
|
||||
let condenseur = sys.add_component(LinearComponent::new("Condenseur", 2));
|
||||
let exv = sys.add_component(LinearComponent::new("EXV", 1));
|
||||
let evap = sys.add_component(LinearComponent::new("Evaporateur", 2));
|
||||
|
||||
sys.add_edge(compresseur, condenseur).unwrap();
|
||||
sys.add_edge(condenseur, exv ).unwrap();
|
||||
sys.add_edge(exv, evap ).unwrap();
|
||||
sys.add_edge(evap, compresseur).unwrap();
|
||||
sys.add_edge(condenseur, exv).unwrap();
|
||||
sys.add_edge(exv, evap).unwrap();
|
||||
sys.add_edge(evap, compresseur).unwrap();
|
||||
sys.finalize().unwrap();
|
||||
|
||||
let internal_state_len = sys.state_vector_len(); // 4 edges × 2 = 8
|
||||
@@ -144,10 +160,16 @@ fn build_chiller_macro(label: &'static str) -> (MacroComponent, usize) {
|
||||
|
||||
// Ports typiques R410A Eurovent A7/W35
|
||||
// Haute pression ≈ 24 bar, basse pression ≈ 8.5 bar
|
||||
mc.expose_port(0, format!("{}/refrig_in", label),
|
||||
make_port("R410A", 24.0e5, 465_000.0)); // décharge compresseur
|
||||
mc.expose_port(2, format!("{}/refrig_out", label),
|
||||
make_port("R410A", 8.5e5, 260_000.0)); // sortie EXV (liquide basse P)
|
||||
mc.expose_port(
|
||||
0,
|
||||
format!("{}/refrig_in", label),
|
||||
make_port("R410A", 24.0e5, 465_000.0),
|
||||
); // décharge compresseur
|
||||
mc.expose_port(
|
||||
2,
|
||||
format!("{}/refrig_out", label),
|
||||
make_port("R410A", 8.5e5, 260_000.0),
|
||||
); // sortie EXV (liquide basse P)
|
||||
|
||||
(mc, internal_state_len)
|
||||
}
|
||||
@@ -157,10 +179,24 @@ fn build_chiller_macro(label: &'static str) -> (MacroComponent, usize) {
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
fn main() {
|
||||
println!("{}", "\n╔══════════════════════════════════════════════════════════════════════╗".green());
|
||||
println!("{}", "║ ENTROPYK — MacroComponent Demo : 2 Chillers en Parallèle ║".green().bold());
|
||||
println!("{}", "║ Architecture : Eurovent A7/W35 — Story 3.6 Hierarchical Subsystem ║".green());
|
||||
println!("{}", "╚══════════════════════════════════════════════════════════════════════╝\n".green());
|
||||
println!(
|
||||
"{}",
|
||||
"\n╔══════════════════════════════════════════════════════════════════════╗".green()
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
"║ ENTROPYK — MacroComponent Demo : 2 Chillers en Parallèle ║"
|
||||
.green()
|
||||
.bold()
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
"║ Architecture : Eurovent A7/W35 — Story 3.6 Hierarchical Subsystem ║".green()
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
"╚══════════════════════════════════════════════════════════════════════╝\n".green()
|
||||
);
|
||||
|
||||
// ── 1. Construction des MacroComponents ─────────────────────────────────
|
||||
print_header("1. Construction des sous-systèmes (MacroComponent)");
|
||||
@@ -168,12 +204,22 @@ fn main() {
|
||||
let (chiller_a, internal_len_a) = build_chiller_macro("Chiller_A");
|
||||
let (chiller_b, internal_len_b) = build_chiller_macro("Chiller_B");
|
||||
|
||||
println!(" {} Chiller A construit : {} composants, {} vars d'état internes",
|
||||
"✓".green(), 4, internal_len_a);
|
||||
println!(" {} Chiller B construit : {} composants, {} vars d'état internes",
|
||||
"✓".green(), 4, internal_len_b);
|
||||
println!(" {} Chaque chiller expose 2 ports : refrig_in + refrig_out",
|
||||
"✓".green());
|
||||
println!(
|
||||
" {} Chiller A construit : {} composants, {} vars d'état internes",
|
||||
"✓".green(),
|
||||
4,
|
||||
internal_len_a
|
||||
);
|
||||
println!(
|
||||
" {} Chiller B construit : {} composants, {} vars d'état internes",
|
||||
"✓".green(),
|
||||
4,
|
||||
internal_len_b
|
||||
);
|
||||
println!(
|
||||
" {} Chaque chiller expose 2 ports : refrig_in + refrig_out",
|
||||
"✓".green()
|
||||
);
|
||||
|
||||
print_box(&[
|
||||
"Structure interne de chaque Chiller (MacroComponent) :",
|
||||
@@ -193,30 +239,44 @@ fn main() {
|
||||
|
||||
let mut parent = System::new();
|
||||
|
||||
let ca = parent.add_component(Box::new(chiller_a));
|
||||
let cb = parent.add_component(Box::new(chiller_b));
|
||||
let ca = parent.add_component(Box::new(chiller_a));
|
||||
let cb = parent.add_component(Box::new(chiller_b));
|
||||
let splitter = parent.add_component(LinearComponent::new("Splitter", 1));
|
||||
let merger = parent.add_component(LinearComponent::new("Merger", 1));
|
||||
let merger = parent.add_component(LinearComponent::new("Merger", 1));
|
||||
|
||||
// Splitter → Chiller A → Merger
|
||||
// Splitter → Chiller B → Merger
|
||||
parent.add_edge(splitter, ca ).unwrap();
|
||||
parent.add_edge(splitter, cb ).unwrap();
|
||||
parent.add_edge(ca, merger).unwrap();
|
||||
parent.add_edge(cb, merger).unwrap();
|
||||
parent.add_edge(splitter, ca).unwrap();
|
||||
parent.add_edge(splitter, cb).unwrap();
|
||||
parent.add_edge(ca, merger).unwrap();
|
||||
parent.add_edge(cb, merger).unwrap();
|
||||
|
||||
parent.finalize().unwrap(); // injecte les indices d'état dans les MacroComponents
|
||||
|
||||
let parent_edge_vars = parent.state_vector_len(); // 4 edges parent × 2 = 8
|
||||
let total_state_len = parent_edge_vars + internal_len_a + internal_len_b; // 8+8+8 = 24
|
||||
let total_state_len = parent_edge_vars + internal_len_a + internal_len_b; // 8+8+8 = 24
|
||||
|
||||
println!(" {} Système parent finalisé :", "✓".green());
|
||||
println!(" - {} nœuds (Splitter, Chiller A, Chiller B, Merger)", parent.node_count());
|
||||
println!(" - {} bords parent ({} vars d'état parent)", parent.edge_count(), parent_edge_vars);
|
||||
println!(" - {} vars d'état internes (2 chillers × 8)", internal_len_a + internal_len_b);
|
||||
println!(" - {} vars d'état total dans le vecteur étendu", total_state_len);
|
||||
println!(
|
||||
" - {} nœuds (Splitter, Chiller A, Chiller B, Merger)",
|
||||
parent.node_count()
|
||||
);
|
||||
println!(
|
||||
" - {} bords parent ({} vars d'état parent)",
|
||||
parent.edge_count(),
|
||||
parent_edge_vars
|
||||
);
|
||||
println!(
|
||||
" - {} vars d'état internes (2 chillers × 8)",
|
||||
internal_len_a + internal_len_b
|
||||
);
|
||||
println!(
|
||||
" - {} vars d'état total dans le vecteur étendu",
|
||||
total_state_len
|
||||
);
|
||||
|
||||
let total_eqs: usize = parent.traverse_for_jacobian()
|
||||
let total_eqs: usize = parent
|
||||
.traverse_for_jacobian()
|
||||
.map(|(_, c, _)| c.n_equations())
|
||||
.sum();
|
||||
|
||||
@@ -238,10 +298,16 @@ fn main() {
|
||||
match parent.compute_residuals(&extended_state, &mut residuals) {
|
||||
Ok(()) => {
|
||||
let max_res = residuals.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
|
||||
println!(" {} compute_residuals() réussi sur vecteur de {} vars",
|
||||
"✓".green(), total_state_len);
|
||||
println!(
|
||||
" {} compute_residuals() réussi sur vecteur de {} vars",
|
||||
"✓".green(),
|
||||
total_state_len
|
||||
);
|
||||
println!(" - {} équations évaluées", total_eqs);
|
||||
println!(" - Résidu max : {:.2e} (state nul → attendu ≈ 0)", max_res);
|
||||
println!(
|
||||
" - Résidu max : {:.2e} (state nul → attendu ≈ 0)",
|
||||
max_res
|
||||
);
|
||||
}
|
||||
Err(e) => println!(" {} Erreur résidus : {:?}", "✗".red(), e),
|
||||
}
|
||||
@@ -274,21 +340,30 @@ fn main() {
|
||||
|
||||
let m_ref = 0.045_f64; // kg/s par chiller
|
||||
let cop_heating = 3.8_f64;
|
||||
let q_heating = m_ref * (465e3 - 260e3);
|
||||
let w_comp = q_heating / cop_heating;
|
||||
let q_heating = m_ref * (465e3 - 260e3);
|
||||
let w_comp = q_heating / cop_heating;
|
||||
|
||||
print_box(&[
|
||||
"Chiller A & Chiller B (identiques, Eurovent A7/W35) :",
|
||||
"",
|
||||
" Cycle Réfrigérant (R410A) :",
|
||||
&format!(" Compresseur : 8.5 bar → 24 bar | W = {:.2} kW", w_comp / 1e3),
|
||||
&format!(" Condenseur : Q_rej = {:.2} kW | T_cond = 40°C", q_heating / 1e3),
|
||||
&format!(
|
||||
" Compresseur : 8.5 bar → 24 bar | W = {:.2} kW",
|
||||
w_comp / 1e3
|
||||
),
|
||||
&format!(
|
||||
" Condenseur : Q_rej = {:.2} kW | T_cond = 40°C",
|
||||
q_heating / 1e3
|
||||
),
|
||||
" EXV : 24 bar → 8.5 bar | Isenthalpique",
|
||||
" Evaporateur : T_evap = 2°C | Superheat = 5 K",
|
||||
"",
|
||||
&format!(" COP Chauffage : {:.2}", cop_heating),
|
||||
&format!(" Capacité : {:.2} kW / chiller", q_heating / 1e3),
|
||||
&format!(" 2 chillers parallèles : {:.2} kW total", 2.0 * q_heating / 1e3),
|
||||
&format!(
|
||||
" 2 chillers parallèles : {:.2} kW total",
|
||||
2.0 * q_heating / 1e3
|
||||
),
|
||||
]);
|
||||
|
||||
// ── 6. Snapshot JSON ─────────────────────────────────────────────
|
||||
@@ -303,7 +378,11 @@ fn main() {
|
||||
});
|
||||
|
||||
let json_str = serde_json::to_string_pretty(&snap_json).unwrap();
|
||||
println!(" {} Snapshot JSON (état convergé, {} vars) :", "✓".green(), n_internal);
|
||||
println!(
|
||||
" {} Snapshot JSON (état convergé, {} vars) :",
|
||||
"✓".green(),
|
||||
n_internal
|
||||
);
|
||||
for line in json_str.lines() {
|
||||
println!(" {}", line.dimmed());
|
||||
}
|
||||
@@ -311,7 +390,11 @@ fn main() {
|
||||
if let Ok(dir) = std::env::current_dir() {
|
||||
let path = dir.join("chiller_a_snapshot.json");
|
||||
std::fs::write(&path, &json_str).unwrap();
|
||||
println!("\n {} Sauvegardé sur disque : {}", "✓".green(), path.display());
|
||||
println!(
|
||||
"\n {} Sauvegardé sur disque : {}",
|
||||
"✓".green(),
|
||||
path.display()
|
||||
);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
@@ -337,6 +420,11 @@ fn main() {
|
||||
]);
|
||||
|
||||
println!("\n{}", "═".repeat(72).cyan());
|
||||
println!("{}", " Entropyk MacroComponent Demo terminé avec succès !".cyan().bold());
|
||||
println!(
|
||||
"{}",
|
||||
" Entropyk MacroComponent Demo terminé avec succès !"
|
||||
.cyan()
|
||||
.bold()
|
||||
);
|
||||
println!("{}\n", "═".repeat(72).cyan());
|
||||
}
|
||||
|
||||
@@ -4,18 +4,14 @@
|
||||
//!
|
||||
//! Exécuter: cargo run -p entropyk-demo --bin pipe
|
||||
|
||||
use entropyk_components::pipe::{Pipe, PipeGeometry, roughness};
|
||||
use entropyk_components::pipe::{roughness, Pipe, PipeGeometry};
|
||||
use entropyk_components::port::{FluidId, Port};
|
||||
use entropyk_core::{Enthalpy, Pressure};
|
||||
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("=== Exemple: Conduite (Pipe) ===\n");
|
||||
|
||||
let geometry = PipeGeometry::new(
|
||||
10.0,
|
||||
0.022,
|
||||
roughness::SMOOTH,
|
||||
)?;
|
||||
let geometry = PipeGeometry::new(10.0, 0.022, roughness::SMOOTH)?;
|
||||
|
||||
let inlet = Port::new(
|
||||
FluidId::new("Water"),
|
||||
|
||||
@@ -22,7 +22,8 @@ fn main() -> Result<(), ConnectionError> {
|
||||
Enthalpy::from_joules_per_kg(400_000.0),
|
||||
);
|
||||
|
||||
println!("Port 1: fluide={}, P={:.2} bar, h={:.0} J/kg",
|
||||
println!(
|
||||
"Port 1: fluide={}, P={:.2} bar, h={:.0} J/kg",
|
||||
port1.fluid_id(),
|
||||
port1.pressure().to_bar(),
|
||||
port1.enthalpy().to_joules_per_kg()
|
||||
@@ -34,7 +35,8 @@ fn main() -> Result<(), ConnectionError> {
|
||||
connected1.set_pressure(Pressure::from_bar(1.5));
|
||||
connected1.set_enthalpy(Enthalpy::from_joules_per_kg(450_000.0));
|
||||
|
||||
println!("Port 1 modifié: P={:.2} bar, h={:.0} J/kg",
|
||||
println!(
|
||||
"Port 1 modifié: P={:.2} bar, h={:.0} J/kg",
|
||||
connected1.pressure().to_bar(),
|
||||
connected1.enthalpy().to_joules_per_kg()
|
||||
);
|
||||
|
||||
@@ -11,10 +11,7 @@ use entropyk_core::{Enthalpy, Pressure};
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("=== Exemple: Pompe ===\n");
|
||||
|
||||
let curves = PumpCurves::quadratic(
|
||||
30.0, -10.0, -50.0,
|
||||
0.5, 0.3, -0.5,
|
||||
)?;
|
||||
let curves = PumpCurves::quadratic(30.0, -10.0, -50.0, 0.5, 0.3, -0.5)?;
|
||||
|
||||
let inlet = Port::new(
|
||||
FluidId::new("Water"),
|
||||
@@ -36,7 +33,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
for q in [0.0, 0.05, 0.1, 0.2] {
|
||||
let head = pump.curves().head_at_flow(q);
|
||||
let eff = pump.curves().efficiency_at_flow(q);
|
||||
println!(" - Q={:.2} m³/s: H={:.2} m, η={:.1}%", q, head, eff * 100.0);
|
||||
println!(
|
||||
" - Q={:.2} m³/s: H={:.2} m, η={:.1}%",
|
||||
q,
|
||||
head,
|
||||
eff * 100.0
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -34,7 +34,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
for q in [0.0, 0.05, 0.1, 0.15, 0.2] {
|
||||
let h = head_poly.evaluate(q);
|
||||
let eta = eff_poly.evaluate(q);
|
||||
println!(" Q={:.2} m³/s → H={:.2} m, η={:.1}%", q, h, eta.clamp(0.0, 1.0) * 100.0);
|
||||
println!(
|
||||
" Q={:.2} m³/s → H={:.2} m, η={:.1}%",
|
||||
q,
|
||||
h,
|
||||
eta.clamp(0.0, 1.0) * 100.0
|
||||
);
|
||||
}
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════
|
||||
@@ -43,8 +48,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("\n🔧 2. Pompe (PumpCurves polynomiales)\n");
|
||||
|
||||
let curves = PumpCurves::quadratic(
|
||||
30.0, -10.0, -50.0, // H = h0 + h1*Q + h2*Q²
|
||||
0.5, 0.3, -0.5, // η = e0 + e1*Q + e2*Q²
|
||||
30.0, -10.0, -50.0, // H = h0 + h1*Q + h2*Q²
|
||||
0.5, 0.3, -0.5, // η = e0 + e1*Q + e2*Q²
|
||||
)?;
|
||||
|
||||
let inlet = Port::new(
|
||||
@@ -61,7 +66,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let pump = Pump::new(curves, inlet, outlet, 1000.0)?;
|
||||
|
||||
println!(" Pompe créée (eau, ρ=1000 kg/m³)");
|
||||
println!(" Point nominal Q=0.1 m³/s: H={:.2} m, η={:.1}%\n",
|
||||
println!(
|
||||
" Point nominal Q=0.1 m³/s: H={:.2} m, η={:.1}%\n",
|
||||
pump.curves().head_at_flow(0.1),
|
||||
pump.curves().efficiency_at_flow(0.1) * 100.0
|
||||
);
|
||||
@@ -74,8 +80,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// Modèle bilinéaire: ṁ = a00 + a10*SST + a01*SDT + a11*SST*SDT
|
||||
// Ẇ = b00 + b10*SST + b01*SDT + b11*SST*SDT
|
||||
let sst_sdt = SstSdtCoefficients::bilinear(
|
||||
0.05, 0.001, 0.0005, 0.00001, // débit (kg/s)
|
||||
1000.0, 50.0, 30.0, 0.5, // puissance (W)
|
||||
0.05, 0.001, 0.0005, 0.00001, // débit (kg/s)
|
||||
1000.0, 50.0, 30.0, 0.5, // puissance (W)
|
||||
);
|
||||
|
||||
println!(" Modèle: ṁ = f(SST, SDT), Ẇ = g(SST, SDT)");
|
||||
@@ -83,19 +89,25 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!(" SDT = température saturation refoulement (K)\n");
|
||||
|
||||
// Conditions typiques: évaporation -5°C (268K), condensation 40°C (313K)
|
||||
let sst_evap = 268.15; // -5°C
|
||||
let sdt_cond = 313.15; // 40°C
|
||||
let sst_evap = 268.15; // -5°C
|
||||
let sdt_cond = 313.15; // 40°C
|
||||
|
||||
let mass_flow = sst_sdt.mass_flow_at(sst_evap, sdt_cond);
|
||||
let power = sst_sdt.power_at(sst_evap, sdt_cond);
|
||||
|
||||
println!(" SST={:.1} K (-5°C), SDT={:.1} K (40°C):", sst_evap, sdt_cond);
|
||||
println!(
|
||||
" SST={:.1} K (-5°C), SDT={:.1} K (40°C):",
|
||||
sst_evap, sdt_cond
|
||||
);
|
||||
println!(" → ṁ = {:.4} kg/s", mass_flow);
|
||||
println!(" → Ẇ = {:.0} W\n", power);
|
||||
|
||||
// Grille de conditions
|
||||
println!(" Grille de performance:");
|
||||
println!(" {:>8} | {:>8} {:>8} {:>8} {:>8}", "SST\\SDT", "303K", "308K", "313K", "318K");
|
||||
println!(
|
||||
" {:>8} | {:>8} {:>8} {:>8} {:>8}",
|
||||
"SST\\SDT", "303K", "308K", "313K", "318K"
|
||||
);
|
||||
println!(" -------- | -------- -------- -------- --------");
|
||||
|
||||
for sst in [263.15, 268.15, 273.15] {
|
||||
@@ -115,7 +127,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let speed_ratios = [1.0, 0.8, 0.6, 0.5];
|
||||
|
||||
println!(" À 50% vitesse: Q₂=0.5*Q₁, H₂=0.25*H₁, P₂=0.125*P₁\n");
|
||||
println!(" {:>10} | {:>10} {:>10} {:>10}", "Vitesse", "Q ratio", "H ratio", "P ratio");
|
||||
println!(
|
||||
" {:>10} | {:>10} {:>10} {:>10}",
|
||||
"Vitesse", "Q ratio", "H ratio", "P ratio"
|
||||
);
|
||||
println!(" ---------- | ---------- ---------- ----------");
|
||||
|
||||
for &ratio in &speed_ratios {
|
||||
@@ -123,7 +138,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let q_ratio = AffinityLaws::scale_flow(1.0, ratio);
|
||||
let h_ratio = AffinityLaws::scale_head(1.0, ratio);
|
||||
let p_ratio = AffinityLaws::scale_power(1.0, ratio);
|
||||
println!(" {:>8.0}% | {:>10.2} {:>10.2} {:>10.2}", ratio * 100.0, q_ratio, h_ratio, p_ratio);
|
||||
println!(
|
||||
" {:>8.0}% | {:>10.2} {:>10.2} {:>10.2}",
|
||||
ratio * 100.0,
|
||||
q_ratio,
|
||||
h_ratio,
|
||||
p_ratio
|
||||
);
|
||||
}
|
||||
|
||||
println!("\n✅ Exemple terminé !");
|
||||
|
||||
@@ -4,15 +4,11 @@
|
||||
//!
|
||||
//! cargo run -p entropyk-demo --bin ui-server
|
||||
|
||||
use axum::{
|
||||
extract::Json,
|
||||
routing::post,
|
||||
Router,
|
||||
};
|
||||
use axum::{extract::Json, routing::post, Router};
|
||||
use entropyk_components::compressor::SstSdtCoefficients;
|
||||
use entropyk_components::pipe::{friction_factor, Pipe, PipeGeometry};
|
||||
use entropyk_components::pump::{Pump, PumpCurves};
|
||||
use entropyk_components::port::{FluidId, Port};
|
||||
use entropyk_components::pump::{Pump, PumpCurves};
|
||||
use entropyk_core::{Enthalpy, Pressure};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::path::Path;
|
||||
@@ -186,10 +182,7 @@ fn calc_compressor(comp: &ComponentConfig) -> ComponentResult {
|
||||
};
|
||||
}
|
||||
|
||||
let sst_sdt = SstSdtCoefficients::bilinear(
|
||||
m[0], m[1], m[2], m[3],
|
||||
p[0], p[1], p[2], p[3],
|
||||
);
|
||||
let sst_sdt = SstSdtCoefficients::bilinear(m[0], m[1], m[2], m[3], p[0], p[1], p[2], p[3]);
|
||||
|
||||
let sst = 268.15;
|
||||
let sdt = 313.15;
|
||||
@@ -212,9 +205,18 @@ fn calc_compressor(comp: &ComponentConfig) -> ComponentResult {
|
||||
|
||||
fn calc_pipe(comp: &ComponentConfig) -> ComponentResult {
|
||||
let config = &comp.config;
|
||||
let length = config.get("length").and_then(|v| v.as_f64()).unwrap_or(10.0);
|
||||
let diameter = config.get("diameter").and_then(|v| v.as_f64()).unwrap_or(0.022);
|
||||
let rough = config.get("roughness").and_then(|v| v.as_f64()).unwrap_or(1.5e-6);
|
||||
let length = config
|
||||
.get("length")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(10.0);
|
||||
let diameter = config
|
||||
.get("diameter")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(0.022);
|
||||
let rough = config
|
||||
.get("roughness")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(1.5e-6);
|
||||
|
||||
match PipeGeometry::new(length, diameter, rough) {
|
||||
Ok(geometry) => {
|
||||
@@ -273,7 +275,10 @@ fn calc_pipe(comp: &ComponentConfig) -> ComponentResult {
|
||||
|
||||
fn calc_valve(comp: &ComponentConfig) -> ComponentResult {
|
||||
let config = &comp.config;
|
||||
let opening = config.get("opening").and_then(|v| v.as_f64()).unwrap_or(1.0);
|
||||
let opening = config
|
||||
.get("opening")
|
||||
.and_then(|v| v.as_f64())
|
||||
.unwrap_or(1.0);
|
||||
|
||||
ComponentResult {
|
||||
id: comp.id.clone(),
|
||||
|
||||
@@ -67,13 +67,13 @@ fn main() {
|
||||
println!();
|
||||
|
||||
let circuits = vec![
|
||||
CircuitId::new("primary"),
|
||||
CircuitId::new("secondary"),
|
||||
CircuitId::from_number(0),
|
||||
CircuitId::from_number(1),
|
||||
CircuitId::default(),
|
||||
];
|
||||
|
||||
for circuit in &circuits {
|
||||
println!(" Circuit: {} (as_str: \"{}\")", circuit, circuit.as_str());
|
||||
println!(" Circuit: {}", circuit);
|
||||
}
|
||||
|
||||
println!();
|
||||
|
||||
Reference in New Issue
Block a user