from __future__ import annotations import unittest from PythonModels.components.cylinder import Cylinder from PythonModels.components.orifice import Orifice from PythonModels.components.pipe import Pipe from PythonModels.components.resistive_pipe import ResistivePipe from PythonModels.components.tank import Tank from PythonModels.components.tee import Tee from PythonModels.core.medium import IdealGasMedium from PythonModels.core.ports import PortVariableDefinition from PythonModels.registry import COMPONENT_MODEL_REGISTRY class ComponentMetadataTests(unittest.TestCase): def setUp(self) -> None: self.medium = IdealGasMedium() def component_instances(self): return ( Cylinder("cylinder_1", self.medium), Tank("tank_1", self.medium), Pipe("pipe_dynamic_1", self.medium), ResistivePipe("pipe_1", self.medium), Orifice("orifice_1"), Tee("tee_1"), ) def test_every_component_declares_a_complete_model_contract(self) -> None: for component in self.component_instances(): with self.subTest(component=component.name): for attribute in ( "MODEL_TYPE", "PORTS", "PARAMETERS", "RESULT_VARIABLES", ): self.assertIn(attribute, type(component).__dict__) self.assertTrue(component.MODEL_TYPE) self.assertEqual(component.model_type, component.MODEL_TYPE) self.assertEqual(component.port_definitions, component.PORTS) self.assertEqual( set(component.parameter_values), {definition.name for definition in component.PARAMETERS}, ) self.assertTrue( all(definition.label for definition in component.PARAMETERS) ) self.assertTrue( all(definition.quantity for definition in component.PARAMETERS) ) def test_registry_reuses_component_parameter_and_port_declarations(self) -> None: registered_classes = { "cylinder": Cylinder, "tank": Tank, "pipe": ResistivePipe, "orifice": Orifice, "tee": Tee, } for model_type, component_class in registered_classes.items(): with self.subTest(model_type=model_type): spec = COMPONENT_MODEL_REGISTRY[model_type] self.assertEqual(spec.model_type, component_class.MODEL_TYPE) self.assertEqual(spec.ports, component_class.PORTS) self.assertEqual(spec.parameters, component_class.PARAMETERS) component = spec.create(f"{model_type}_1", self.medium, {}) self.assertEqual( component.parameter_values, { definition.name: definition.default for definition in component_class.PARAMETERS }, ) def test_result_metadata_exactly_describes_each_exposed_value(self) -> None: for component in self.component_instances(): with self.subTest(component=component.name): values = component.result_values() metadata = component.result_variable_metadata() keys = [variable.key for variable in metadata] self.assertEqual(len(keys), len(set(keys))) self.assertEqual( set(values), { variable.key.removeprefix(f"{component.name}.") for variable in metadata }, ) self.assertTrue(all(variable.label for variable in metadata)) self.assertTrue(all(variable.quantity for variable in metadata)) def test_storage_and_port_variables_include_display_metadata(self) -> None: cylinder = Cylinder("cylinder_1", self.medium) metadata = { variable.key: variable for variable in cylinder.result_variable_metadata() } pressure = metadata["cylinder_1.p"] self.assertEqual(pressure.scope, "component") self.assertEqual(pressure.label, "压力") self.assertEqual(pressure.quantity, "pressure") self.assertEqual(pressure.unit, "Pa") mass_flow = metadata["cylinder_1.port_b.m_flow"] self.assertEqual(mass_flow.scope, "port") self.assertEqual(mass_flow.port_name, "port_b") self.assertEqual(mass_flow.label, "质量流量") self.assertEqual(mass_flow.quantity, "mass_flow") self.assertEqual(mass_flow.unit, "kg/s") def test_port_display_metadata_is_not_part_of_the_physical_contract(self) -> None: first = PortVariableDefinition( "p", "effort", "equal", label="压力", quantity="pressure", unit="Pa", ) second = PortVariableDefinition( "p", "effort", "equal", label="Pressure", quantity="absolute_pressure", unit="kPa", ) self.assertEqual(first, second) if __name__ == "__main__": unittest.main()