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; 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, 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!"); }