Files
Entropyk/crates/solver/tests/traceability.rs
Sepehr ab5dc7e568 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>
2026-04-25 15:01:09 +02:00

130 lines
4.0 KiB
Rust

use entropyk_components::port::{FluidId, Port};
use entropyk_components::{Component, ComponentError, ConnectedPort, JacobianBuilder, StateSlice};
use entropyk_core::{Enthalpy, Pressure};
use entropyk_solver::solver::{NewtonConfig, Solver};
use entropyk_solver::system::System;
struct DummyComponent {
ports: Vec<ConnectedPort>,
/// Fluid label used in signature() so input_hash reflects fluid configuration.
fluid_label: String,
}
impl Component for DummyComponent {
fn compute_residuals(
&self,
_state: &StateSlice,
residuals: &mut entropyk_components::ResidualVector,
) -> Result<(), ComponentError> {
residuals[0] = 0.0;
residuals[1] = 0.0;
Ok(())
}
fn jacobian_entries(
&self,
_state: &StateSlice,
jacobian: &mut JacobianBuilder,
) -> Result<(), 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) -> &[ConnectedPort] {
&self.ports
}
fn signature(&self) -> String {
format!("DummyComponent({})", self.fluid_label)
}
}
fn make_dummy_component() -> Box<dyn Component> {
make_dummy_component_with_fluid("R134a")
}
fn make_dummy_component_with_fluid(fluid: &str) -> Box<dyn Component> {
let inlet = Port::new(
FluidId::new(fluid),
Pressure::from_pascals(100_000.0),
Enthalpy::from_joules_per_kg(400_000.0),
);
let outlet = Port::new(
FluidId::new(fluid),
Pressure::from_pascals(100_000.0),
Enthalpy::from_joules_per_kg(400_000.0),
);
let (connected_inlet, connected_outlet) = inlet.connect(outlet).unwrap();
let ports = vec![connected_inlet, connected_outlet];
Box::new(DummyComponent {
ports,
fluid_label: fluid.to_string(),
})
}
#[test]
fn test_simulation_metadata_outputs() {
let mut sys = System::new();
let n0 = sys.add_component(make_dummy_component());
let n1 = sys.add_component(make_dummy_component());
sys.add_edge_with_ports(n0, 1, n1, 0).unwrap();
sys.add_edge_with_ports(n1, 1, n0, 0).unwrap();
sys.finalize().unwrap();
let input_hash = sys.input_hash();
let mut solver = NewtonConfig {
max_iterations: 5,
..Default::default()
};
let result = solver.solve(&mut sys).unwrap();
assert!(result.is_converged());
let metadata = result.metadata;
assert_eq!(metadata.input_hash, input_hash);
assert_eq!(metadata.solver_version, env!("CARGO_PKG_VERSION"));
assert_eq!(metadata.fluid_backend_version, "0.1.0");
}
/// Same topology (two nodes, two edges) but different fluid → different input_hash.
#[test]
fn test_input_hash_different_fluid_same_topology() {
let mut sys_r134a = System::new();
let n0 = sys_r134a.add_component(make_dummy_component_with_fluid("R134a"));
let n1 = sys_r134a.add_component(make_dummy_component_with_fluid("R134a"));
sys_r134a.add_edge_with_ports(n0, 1, n1, 0).unwrap();
sys_r134a.add_edge_with_ports(n1, 1, n0, 0).unwrap();
sys_r134a.finalize().unwrap();
let mut sys_r410a = System::new();
let n0 = sys_r410a.add_component(make_dummy_component_with_fluid("R410A"));
let n1 = sys_r410a.add_component(make_dummy_component_with_fluid("R410A"));
sys_r410a.add_edge_with_ports(n0, 1, n1, 0).unwrap();
sys_r410a.add_edge_with_ports(n1, 1, n0, 0).unwrap();
sys_r410a.finalize().unwrap();
assert_ne!(
sys_r134a.input_hash(),
sys_r410a.input_hash(),
"input_hash must differ when only fluid configuration differs"
);
}
#[test]
fn test_metadata_to_json() {
use entropyk_solver::SimulationMetadata;
let meta = SimulationMetadata::new("abc123".to_string());
let json = meta.to_json().unwrap();
assert!(json.contains("\"solver_version\""));
assert!(json.contains("\"fluid_backend_version\""));
assert!(json.contains("\"input_hash\""));
assert!(json.contains("abc123"));
}