209 lines
6.5 KiB
Rust
209 lines
6.5 KiB
Rust
use entropyk_components::port::{Connected, FluidId, Port};
|
|
use entropyk_components::state_machine::CircuitId;
|
|
use entropyk_components::{
|
|
Component, ComponentError, ConnectedPort, JacobianBuilder, MchxCondenserCoil, Polynomial2D,
|
|
ResidualVector, ScrewEconomizerCompressor, ScrewPerformanceCurves, StateSlice,
|
|
};
|
|
use entropyk_core::{Enthalpy, MassFlow, Power, Pressure};
|
|
use entropyk_solver::inverse::{BoundedVariable, BoundedVariableId, ComponentOutput, Constraint, ConstraintId};
|
|
use entropyk_solver::system::System;
|
|
|
|
type CP = Port<Connected>;
|
|
|
|
fn make_port(fluid: &str, p_bar: f64, h_kj_kg: f64) -> ConnectedPort {
|
|
let a = Port::new(
|
|
FluidId::new(fluid),
|
|
Pressure::from_bar(p_bar),
|
|
Enthalpy::from_joules_per_kg(h_kj_kg * 1000.0),
|
|
);
|
|
let b = Port::new(
|
|
FluidId::new(fluid),
|
|
Pressure::from_bar(p_bar),
|
|
Enthalpy::from_joules_per_kg(h_kj_kg * 1000.0),
|
|
);
|
|
a.connect(b).expect("port connection ok").0
|
|
}
|
|
|
|
fn make_screw_curves() -> ScrewPerformanceCurves {
|
|
ScrewPerformanceCurves::with_fixed_eco_fraction(
|
|
Polynomial2D::bilinear(1.20, 0.003, -0.002, 0.000_01),
|
|
Polynomial2D::bilinear(55_000.0, 200.0, -300.0, 0.5),
|
|
0.12,
|
|
)
|
|
}
|
|
|
|
struct Mock {
|
|
n: usize,
|
|
circuit_id: CircuitId,
|
|
}
|
|
|
|
impl Mock {
|
|
fn new(n: usize, circuit: u16) -> Self {
|
|
Self {
|
|
n,
|
|
circuit_id: CircuitId(circuit),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Component for Mock {
|
|
fn compute_residuals(
|
|
&self,
|
|
_state: &StateSlice,
|
|
residuals: &mut ResidualVector,
|
|
) -> Result<(), ComponentError> {
|
|
for r in residuals.iter_mut().take(self.n) {
|
|
*r = 0.0;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn jacobian_entries(
|
|
&self,
|
|
_state: &StateSlice,
|
|
_jacobian: &mut JacobianBuilder,
|
|
) -> Result<(), ComponentError> {
|
|
Ok(())
|
|
}
|
|
|
|
fn n_equations(&self) -> usize {
|
|
self.n
|
|
}
|
|
|
|
fn get_ports(&self) -> &[ConnectedPort] {
|
|
&[]
|
|
}
|
|
|
|
fn port_mass_flows(&self, _state: &StateSlice) -> Result<Vec<MassFlow>, ComponentError> {
|
|
Ok(vec![MassFlow::from_kg_per_s(1.0)])
|
|
}
|
|
|
|
fn energy_transfers(&self, _state: &StateSlice) -> Option<(Power, Power)> {
|
|
Some((Power::from_watts(0.0), Power::from_watts(0.0)))
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn test_real_cycle_inverse_control_integration() {
|
|
let mut sys = System::new();
|
|
|
|
// 1. Create components
|
|
let comp_suc = make_port("R134a", 3.2, 400.0);
|
|
let comp_dis = make_port("R134a", 12.8, 440.0);
|
|
let comp_eco = make_port("R134a", 6.4, 260.0);
|
|
|
|
let comp = ScrewEconomizerCompressor::new(
|
|
make_screw_curves(),
|
|
"R134a",
|
|
50.0,
|
|
0.92,
|
|
comp_suc,
|
|
comp_dis,
|
|
comp_eco,
|
|
).unwrap();
|
|
|
|
let coil = MchxCondenserCoil::for_35c_ambient(15_000.0, 0);
|
|
let exv = Mock::new(2, 0); // Expansion Valve
|
|
let evap = Mock::new(2, 0); // Evaporator
|
|
|
|
// 2. Add components to system
|
|
let comp_node = sys.add_component_to_circuit(Box::new(comp), CircuitId::ZERO).unwrap();
|
|
let coil_node = sys.add_component_to_circuit(Box::new(coil), CircuitId::ZERO).unwrap();
|
|
let exv_node = sys.add_component_to_circuit(Box::new(exv), CircuitId::ZERO).unwrap();
|
|
let evap_node = sys.add_component_to_circuit(Box::new(evap), CircuitId::ZERO).unwrap();
|
|
|
|
sys.register_component_name("compressor", comp_node);
|
|
sys.register_component_name("condenser", coil_node);
|
|
sys.register_component_name("expansion_valve", exv_node);
|
|
sys.register_component_name("evaporator", evap_node);
|
|
|
|
// 3. Connect components
|
|
sys.add_edge(comp_node, coil_node).unwrap();
|
|
sys.add_edge(coil_node, exv_node).unwrap();
|
|
sys.add_edge(exv_node, evap_node).unwrap();
|
|
sys.add_edge(evap_node, comp_node).unwrap();
|
|
|
|
// 4. Add Inverse Control Elements (Constraints and BoundedVariables)
|
|
// Constraint 1: Superheat at evaporator = 5K
|
|
sys.add_constraint(Constraint::new(
|
|
ConstraintId::new("superheat_control"),
|
|
ComponentOutput::Superheat {
|
|
component_id: "evaporator".to_string(),
|
|
},
|
|
5.0,
|
|
)).unwrap();
|
|
|
|
// Constraint 2: Capacity at compressor = 50000 W
|
|
sys.add_constraint(Constraint::new(
|
|
ConstraintId::new("capacity_control"),
|
|
ComponentOutput::Capacity {
|
|
component_id: "compressor".to_string(),
|
|
},
|
|
50000.0,
|
|
)).unwrap();
|
|
|
|
// Control 1: Valve Opening
|
|
let bv_valve = BoundedVariable::with_component(
|
|
BoundedVariableId::new("valve_opening"),
|
|
"expansion_valve",
|
|
0.5,
|
|
0.0,
|
|
1.0,
|
|
).unwrap();
|
|
sys.add_bounded_variable(bv_valve).unwrap();
|
|
|
|
// Control 2: Compressor Speed
|
|
let bv_comp = BoundedVariable::with_component(
|
|
BoundedVariableId::new("compressor_speed"),
|
|
"compressor",
|
|
0.7,
|
|
0.3,
|
|
1.0,
|
|
).unwrap();
|
|
sys.add_bounded_variable(bv_comp).unwrap();
|
|
|
|
// Link constraints to controls
|
|
sys.link_constraint_to_control(
|
|
&ConstraintId::new("superheat_control"),
|
|
&BoundedVariableId::new("valve_opening"),
|
|
).unwrap();
|
|
|
|
sys.link_constraint_to_control(
|
|
&ConstraintId::new("capacity_control"),
|
|
&BoundedVariableId::new("compressor_speed"),
|
|
).unwrap();
|
|
|
|
// 5. Finalize the system
|
|
sys.finalize().unwrap();
|
|
|
|
// Verify system state size and degrees of freedom
|
|
assert_eq!(sys.constraint_count(), 2);
|
|
assert_eq!(sys.bounded_variable_count(), 2);
|
|
|
|
// Validate DoF
|
|
sys.validate_inverse_control_dof().expect("System should be balanced for inverse control");
|
|
|
|
// Evaluate the total system residual and jacobian capability
|
|
let state_len = sys.state_vector_len();
|
|
assert!(state_len > 0, "System should have state variables");
|
|
|
|
// Create mock state and control values
|
|
let state = vec![400_000.0; state_len];
|
|
let control_values = vec![0.5, 0.7]; // Valve, Compressor speeds
|
|
|
|
let mut residuals = vec![0.0; state_len + 2];
|
|
|
|
// Evaluate constraints
|
|
let measured = sys.extract_constraint_values_with_controls(&state, &control_values);
|
|
let count = sys.compute_constraint_residuals(&state, &mut residuals[state_len..], &measured);
|
|
|
|
assert_eq!(count, 2, "Should have computed 2 constraint residuals");
|
|
|
|
// Evaluate jacobian
|
|
let jacobian_entries = sys.compute_inverse_control_jacobian(&state, state_len, &control_values);
|
|
|
|
assert!(!jacobian_entries.is_empty(), "Jacobian should have entries for inverse control");
|
|
|
|
println!("System integration with inverse control successful!");
|
|
}
|