from __future__ import annotations import tempfile import unittest from pathlib import Path from PythonModels.core.solver import SolveIVPConfig from PythonModels.systems.test_mql import TestMqlPnl0001LineRhsDiagnostic from PythonModels.scripts.run_test_mql_full_state_comparison import ( TestMqlFullStateComparisonExecutionConfig, TestMqlFullStateComparisonPathConfig, TestMqlFullStateComparisonScriptConfig, TestMqlPnl0001EnergyFlowDiagnostic, TestMqlP4NodeEnthalpyBreakdownDiagnostic, TestMqlPnl0003EnergyDiagnostic, TestMqlPneumatic96InletLineDiagnostic, TestMqlPnch012EnergyEquationDiagnostic, TestMqlPnvoUpstreamEnthalpyDiagnostic, TestMqlPnl0001PressureLossCalibrationDiagnostic, TestMqlPnvoEventBoundaryDiagnostic, TestMqlPnvoEventWindowCandidateComparisonDiagnostic, TestMqlPnvoEventWindowCandidateMetricComparison, TestMqlPnvoEventWindowDiagnostic, TestMqlPnvoEventWindowSampleDiagnostic, TestMqlPnvoFlowParameterDiagnostic, TestMqlPnvoEventWindowSegmentDiagnostic, format_test_mql_full_state_comparison_summary, format_test_mql_pnvo_event_boundary_summary, format_test_mql_pnvo_event_window_candidate_comparison_summary, format_test_mql_pnvo_event_window_summary, run_test_mql_full_state_comparison, _ideal_pn2vol_reference_dtemp, _series_sample_bracket, ) REPO_ROOT = Path(__file__).resolve().parents[1] TEST_MQL_AME = REPO_ROOT / "AmesimModels" / "test_mql.ame" class RunTestMqlFullStateComparisonScriptTests(unittest.TestCase): def test_ideal_pn2vol_reference_dtemp_uses_internal_energy_mass_offset( self, ) -> None: from PythonModels.components.amesim_pneumatic import HELIUM_PNEUMATIC_GAS gas = HELIUM_PNEUMATIC_GAS result = _ideal_pn2vol_reference_dtemp( gas=gas, mass_kg=0.002, temperature_k=320.0, mass_derivative_kg_s=0.1, enthalpy_flow_w=-100.0, heat_flow_w=5.0, ) expected = ( -100.0 + (gas.cp * 298.15 - gas.cv * 320.0) * 0.1 + 5.0 ) / (0.002 * gas.cv) self.assertAlmostEqual(result, expected) def test_series_sample_bracket_distinguishes_exact_and_interpolated_times(self) -> None: times = (0.04000000000000009, 0.050000000000000114) exact_left, exact_right = _series_sample_bracket( times=times, time_s=0.04, ) interpolated_left, interpolated_right = _series_sample_bracket( times=times, time_s=0.041, ) self.assertEqual(exact_left, exact_right) self.assertAlmostEqual(exact_left, 0.04) self.assertAlmostEqual(interpolated_left, 0.04) self.assertAlmostEqual(interpolated_right, 0.05) def test_full_state_script_writes_summary_and_comparison_csv(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: output_dir = Path(tmpdir) run, returned_output_dir = run_test_mql_full_state_comparison( TestMqlFullStateComparisonScriptConfig( paths=TestMqlFullStateComparisonPathConfig( archive_path=TEST_MQL_AME, output_dir=output_dir, ), execution=TestMqlFullStateComparisonExecutionConfig( data_paths=( "press@pn_c1_8", "vol1@pn_brp2_8", "dm1@pneumatic_69", "xv@pn_morifice_1", "dm2@pn_morifice_1", ), solver=SolveIVPConfig(t_start=0.0, t_stop=0.0), t_eval=(0.0,), ), ) ) summary_path = output_dir / "test_mql_full_state_comparison_summary.txt" csv_path = output_dir / "test_mql_amesim_comparison.csv" csv_summary_path = output_dir / "test_mql_amesim_comparison_summary.txt" summary = summary_path.read_text(encoding="utf-8") csv_header = csv_path.read_text(encoding="utf-8").splitlines()[0] csv_summary_exists = csv_summary_path.exists() self.assertEqual(returned_output_dir, output_dir) self.assertEqual(run.sample_count, 1) self.assertEqual(run.signal_count, 5) self.assertAlmostEqual(run.comparison.max_abs_error, 0.0, delta=1.0e-8) self.assertEqual( run.metrics_by_max_abs_error()[0].data_path, "press@pn_c1_8", ) diagnostic = run.signal_diagnostic("press@pn_c1_8") self.assertEqual(diagnostic.data_path, "press@pn_c1_8") self.assertAlmostEqual(diagnostic.initial_python_value, -1300.0) self.assertAlmostEqual(diagnostic.final_python_value, -1300.0) self.assertAlmostEqual(diagnostic.final_abs_error, 0.0, delta=1.0e-8) self.assertEqual( run.diagnostics_by_final_abs_error()[0].data_path, "press@pn_c1_8", ) flow_diagnostic = run.pnl0001_mass_flow_diagnostic() self.assertEqual(flow_diagnostic.data_path, "dm1@pneumatic_69") self.assertAlmostEqual(flow_diagnostic.initial_python_dm1_g_s, 0.0) self.assertAlmostEqual(flow_diagnostic.initial_amesim_dm1_g_s, 0.0) self.assertAlmostEqual( flow_diagnostic.initial_python_canonical_kg_s, -flow_diagnostic.initial_python_dm1_g_s * 1.0e-3, ) pnvo_diagnostic = run.pnvo_diagnostic() self.assertEqual(pnvo_diagnostic.alias, "pn_morifice_1") self.assertAlmostEqual(pnvo_diagnostic.initial_python_opening, 0.0) self.assertAlmostEqual(pnvo_diagnostic.initial_amesim_opening, 0.0) self.assertAlmostEqual(pnvo_diagnostic.initial_python_mass_flow_kg_s, 0.0) self.assertAlmostEqual(pnvo_diagnostic.initial_amesim_mass_flow_kg_s, 0.0) chamber_diagnostic = run.chamber_rhs_diagnostic("pn_c1_8") self.assertEqual(chamber_diagnostic.chamber_alias, "pn_c1_8") self.assertEqual(chamber_diagnostic.piston_alias, "pn_brp2_8") self.assertAlmostEqual(chamber_diagnostic.chamber_volume_m3, 0.015) self.assertAlmostEqual( chamber_diagnostic.energy_derivative_w, chamber_diagnostic.port_a_energy_flow_w + chamber_diagnostic.port_b_energy_flow_w + chamber_diagnostic.boundary_work_w + chamber_diagnostic.thermal_energy_flow_w, ) self.assertTrue(csv_summary_exists) self.assertIn("Mode: Python 132 full-state closure comparison", summary) self.assertIn("Compared signals: 5", summary) self.assertIn("AMESim first saved interval: 0.010000000000000023", summary) self.assertIn("Final time aligns with AMESim sample: True", summary) self.assertIn("Largest absolute error: press@pn_c1_8=", summary) self.assertIn("Largest final endpoint error: press@pn_c1_8=", summary) self.assertIn("Metrics by max absolute error:", summary) self.assertIn("Endpoint diagnostics by final absolute error:", summary) self.assertIn( "PNL0001 canonical mass-flow diagnostic: dm1@pneumatic_69", summary, ) self.assertIn("equals -AMESim dm1 * 1e-3", summary) self.assertIn("PNVO diagnostic: pn_morifice_1", summary) self.assertIn("final_python_opening=", summary) self.assertIn("final_python_mass_flow_kg_s=", summary) self.assertIn("Largest endpoint chamber RHS breakdown: pn_c1_8", summary) self.assertIn("piston_alias=pn_brp2_8", summary) self.assertIn("mass_derivative_kg_s=", summary) self.assertIn("boundary_work_w=", summary) self.assertIn("thermal_energy_flow_w=", summary) self.assertIn("final_python=", summary) self.assertIn("final_amesim=", summary) self.assertIn("python.press@pn_c1_8", csv_header) self.assertIn("amesim.vol1@pn_brp2_8", csv_header) self.assertIn("python.dm1@pneumatic_69", csv_header) self.assertIn("python.xv@pn_morifice_1", csv_header) self.assertIn("python.dm2@pn_morifice_1", csv_header) def test_default_execution_aligns_first_amesim_saved_sample(self) -> None: execution = TestMqlFullStateComparisonExecutionConfig() self.assertEqual(execution.solver.t_stop, 0.01) self.assertEqual(execution.solver.max_step, 1.0e-3) self.assertEqual(execution.t_eval, (0.0, 0.01)) self.assertIn("xv@pn_morifice_1", execution.data_paths) self.assertIn("dm2@pn_morifice_1", execution.data_paths) def test_formats_pnvo_event_boundary_diagnostic(self) -> None: data_paths = ( "press@pn_c1_8", "dm1@pneumatic_69", "xv@pn_morifice_1", "dm2@pn_morifice_1", ) diagnostic = TestMqlPnvoEventBoundaryDiagnostic( orifice_alias="pn_morifice_1", event_time_s=0.04, integration_stop_time_s=0.039999999999999994, data_paths=data_paths, python_values_by_data_path={ "press@pn_c1_8": -1090.9, "dm1@pneumatic_69": -0.00949, "xv@pn_morifice_1": 1.0, "dm2@pn_morifice_1": 502.945, }, amesim_values_by_data_path={ "press@pn_c1_8": -1146.38, "dm1@pneumatic_69": -0.00667, "xv@pn_morifice_1": 1.0, "dm2@pn_morifice_1": 497.824, }, ) self.assertEqual(diagnostic.orifice_alias, "pn_morifice_1") self.assertLess(diagnostic.integration_stop_time_s, diagnostic.event_time_s) self.assertEqual(diagnostic.abs_error("xv@pn_morifice_1"), 0.0) self.assertAlmostEqual( diagnostic.abs_error("dm2@pn_morifice_1"), 5.121, ) summary = format_test_mql_pnvo_event_boundary_summary(diagnostic) self.assertIn("PNVO event boundary diagnostic (pn_morifice_1)", summary) self.assertIn("Observation side: right-continuous STEP0 opening", summary) self.assertTrue(summary.endswith("\n")) def test_formats_pnvo_event_window_diagnostic_with_samples(self) -> None: data_paths = ( "press@pn_c1_8", "temp@pn_c1_8", "vol@pn_c1_8", "mgas1@pn_c1_8", "p2@pneumatic_69", "t2@pneumatic_69", "mgas@pneumatic_69", "re@pneumatic_69", "v@pneumatic_69", "ff@pneumatic_69", "dm1@pneumatic_69", "xv@pn_morifice_1", "dm2@pn_morifice_1", ) sample = TestMqlPnvoEventWindowSampleDiagnostic( time_s=0.05, data_paths=data_paths, python_values_by_data_path={ "press@pn_c1_8": 219127.9, "temp@pn_c1_8": 372.3, "vol@pn_c1_8": 15000.0, "mgas1@pn_c1_8": 5.46, "p2@pneumatic_69": 93250.0, "t2@pneumatic_69": 293.15, "mgas@pneumatic_69": 0.53, "re@pneumatic_69": 1.41e6, "v@pneumatic_69": 832.0, "ff@pneumatic_69": 0.0243, "dm1@pneumatic_69": 443.2, "xv@pn_morifice_1": 1.0, "dm2@pn_morifice_1": 463.7, }, amesim_values_by_data_path={ "press@pn_c1_8": 187060.0, "temp@pn_c1_8": 296.1, "vol@pn_c1_8": 15000.0, "mgas1@pn_c1_8": 5.47, "p2@pneumatic_69": 1484689.6, "t2@pneumatic_69": 296.1, "mgas@pneumatic_69": 0.81, "re@pneumatic_69": 1.35e6, "v@pneumatic_69": 519.8, "ff@pneumatic_69": 0.0241, "dm1@pneumatic_69": 419.9, "xv@pn_morifice_1": 1.0, "dm2@pn_morifice_1": 456.8, }, amesim_sample_left_time_s=0.05, amesim_sample_right_time_s=0.05, pnl0001_rhs_diagnostics=( TestMqlPnl0001LineRhsDiagnostic( line_alias="pneumatic_69", chamber_alias="pn_c1_8", line_pressure_pa=1034250.0, line_temperature_k=293.15, chamber_pressure_pa=320427.9, chamber_temperature_k=379.9, chamber_to_line_flow_kg_s=-0.4432, node_to_line_flow_kg_s=0.4210, mass_derivative_kg_s=-0.0222, port_1_energy_flow_w=-404000.0, port_2_energy_flow_w=384000.0, thermal_energy_flow_w=0.0, energy_derivative_w=-20000.0, ), ), pnl0001_pressure_loss_diagnostics=( TestMqlPnl0001PressureLossCalibrationDiagnostic( line_alias="pneumatic_69", chamber_alias="pn_c1_8", time_s=0.05, amesim_cm=0.0159, amesim_dm1_g_s=419.9, amesim_mass_flow_magnitude_kg_s=0.4199, amesim_line_gauge_pressure_pa=1484689.6, amesim_chamber_gauge_pressure_pa=187060.0, amesim_line_temperature_k=296.1, amesim_pressure_drop_pa=1297629.6, current_darcy_pressure_drop_pa=422633.2, pressure_drop_multiplier=3.07, candidate_pn2pipefr_dm1_g_s=424.2, candidate_pn2pipefr_to_amesim_dm1_ratio=1.01, amesim_linear_conductance_kg_s_sqrt_k_per_pa=5.56e-6, python_linear_conductance_kg_s_sqrt_k_per_pa=1.06e-5, python_to_amesim_linear_conductance_ratio=1.91, ), ), pnvo_flow_parameter_diagnostics=( TestMqlPnvoFlowParameterDiagnostic( orifice_alias="pn_morifice_1", time_s=0.05, amesim_cm=0.0158, amesim_dm2_g_s=456.8, amesim_opening=1.0, amesim_gas_velocity_m_s=885.9, python_opening=1.0, python_flow_coefficient=0.45, python_effective_area_m2=78.5e-6, python_line_pressure_pa=15310000.0, python_boundary_pressure_pa=1586000.0, python_upstream_pressure_pa=15310000.0, python_upstream_temperature_k=293.15, python_mass_flow_kg_s=0.4637, python_cm=0.0147, python_to_amesim_cm_ratio=0.93, ), ), pnvo_upstream_enthalpy_diagnostics=( TestMqlPnvoUpstreamEnthalpyDiagnostic( orifice_alias="pn_morifice_1", upstream_line_alias="pneumatic_87", time_s=0.05, amesim_orifice_port_2_enthalpy_flow_w=-18851.7, amesim_orifice_port_2_mass_flow_g_s=456.8, amesim_orifice_port_3_enthalpy_flow_w=18851.7, amesim_orifice_port_3_mass_flow_g_s=-456.8, amesim_upstream_line_center_enthalpy_flow_w=15823.7, amesim_upstream_line_center_mass_flow_g_s=-454.2, amesim_upstream_line_port_2_temperature_k=287.7, amesim_upstream_line_port_2_pressure_pa=13.871e6, amesim_orifice_port_2_implied_reference_temperature_k=290.2, amesim_orifice_port_2_implied_temperature_delta_to_line_k=2.5, amesim_upstream_line_port_2_reference_enthalpy_flow_w=-24714.8, amesim_upstream_line_port_2_reference_enthalpy_error_w=-5863.1, amesim_upstream_line_port_2_real_gas_reference_enthalpy_flow_w=-19240.8, amesim_upstream_line_port_2_real_gas_reference_enthalpy_error_w=-389.1, python_orifice_to_node_flow_kg_s=0.4637, python_upstream_line_port_2_temperature_k=282.2, python_upstream_line_port_2_pressure_pa=13.84e6, python_current_port_2_enthalpy_flow_w=679490.8, python_reference_port_2_enthalpy_flow_w=-38478.5, python_real_gas_reference_port_2_enthalpy_flow_w=-32998.5, python_current_port_2_enthalpy_error_w=698342.5, python_reference_port_2_enthalpy_error_w=-19626.8, python_real_gas_reference_port_2_enthalpy_error_w=-14146.8, ), ), pnl0003_energy_diagnostics=( TestMqlPnl0003EnergyDiagnostic( line_alias="pneumatic_87", time_s=0.05, amesim_port_1_temperature_k=293.1, amesim_port_2_temperature_k=287.7, amesim_port_1_pressure_pa=15.2e6, amesim_port_2_pressure_pa=13.9e6, amesim_port_1_mass_flow_g_s=-8.5, amesim_port_1_enthalpy_flow_w=85.0, amesim_center_mass_flow_g_s=-454.2, amesim_center_enthalpy_flow_w=15823.7, amesim_port_2_mass_flow_g_s=-456.8, amesim_port_2_enthalpy_flow_w=18851.7, amesim_port_1_storage_mass_derivative_g_s=-462.7, amesim_port_1_storage_enthalpy_sum_w=15908.7, amesim_port_2_storage_mass_derivative_g_s=-2.6, amesim_port_2_storage_enthalpy_sum_w=3028.0, python_pn3_node_port_1_mass_flow_g_s=-333.0, python_pn3_node_port_3_mass_flow_g_s=-119.9, python_pn3_node_to_line_mass_flow_g_s=452.9, python_pn3_node_to_line_mass_flow_error_g_s=461.4, amesim_port_1_mass_estimate_g=1.12, amesim_port_2_mass_estimate_g=1.05, python_port_1_mass_g=1.11, python_port_2_mass_g=1.04, python_port_1_mass_error_to_amesim_g=-0.01, python_port_2_mass_error_to_amesim_g=-0.01, amesim_total_mass_derivative_fd_g_s=-470.0, amesim_total_mass_derivative_backward_g_s=-540.0, amesim_total_mass_derivative_forward_g_s=-400.0, amesim_total_storage_mass_derivative_g_s=-465.3, amesim_total_storage_mass_derivative_residual_g_s=4.7, amesim_center_flow_python_sign_equivalent_g_s=454.2, amesim_center_flow_python_sign_error_g_s=-0.2, python_center_mass_flow_g_s=454.0, amesim_port_1_center_mass_derivative_g_s=-454.2, amesim_port_2_center_mass_derivative_g_s=454.2, python_port_1_external_mass_flow_g_s=337.0, python_port_1_external_mass_flow_error_g_s=15.1, python_port_1_center_mass_derivative_g_s=-454.0, python_port_1_center_mass_derivative_error_g_s=0.2, python_port_1_storage_mass_derivative_g_s=-117.0, python_port_1_storage_mass_derivative_error_g_s=345.7, python_port_2_external_mass_flow_g_s=-456.4, python_port_2_external_mass_flow_error_g_s=0.4, python_port_2_center_mass_derivative_g_s=454.0, python_port_2_center_mass_derivative_error_g_s=-0.2, python_port_2_storage_mass_derivative_g_s=-2.4, python_port_2_storage_mass_derivative_error_g_s=0.2, python_amesim_state_darcy_center_mass_flow_g_s=453.5, python_amesim_state_darcy_center_mass_flow_error_g_s=-0.7, python_amesim_state_pn2pipefr_center_mass_flow_g_s=455.0, python_amesim_state_pn2pipefr_center_mass_flow_error_g_s=0.8, amesim_center_real_gas_reference_port_1_estimate_w=15700.0, amesim_center_real_gas_reference_port_1_error_w=-123.7, python_center_ideal_reference_port_1_estimate_w=16100.0, python_center_ideal_reference_port_1_error_w=276.3, python_center_ideal_reference_port_2_estimate_w=16200.0, python_center_ideal_reference_port_2_error_w=376.3, python_center_real_gas_reference_port_1_estimate_w=8300.0, python_center_real_gas_reference_port_1_error_w=-7523.7, python_center_real_gas_reference_port_2_estimate_w=8400.0, python_center_real_gas_reference_port_2_error_w=-7423.7, python_total_mass_derivative_kg_s=-0.47, python_port_1_reference_external_enthalpy_flow_w=80.0, python_port_1_reference_center_enthalpy_flow_w=15800.0, python_port_1_reference_storage_enthalpy_sum_w=15880.0, python_port_1_reference_storage_enthalpy_error_w=-28.7, python_port_2_reference_external_enthalpy_flow_w=19000.0, python_port_2_reference_center_enthalpy_flow_w=-15800.0, python_port_2_reference_storage_enthalpy_sum_w=3200.0, python_port_2_reference_storage_enthalpy_error_w=172.0, amesim_port_1_external_enthalpy_dtemp_component_k_s=80.0, amesim_port_1_center_enthalpy_dtemp_component_k_s=14000.0, amesim_port_1_mass_offset_dtemp_component_k_s=-13200.0, amesim_port_1_heat_dtemp_component_k_s=0.0, python_port_1_external_enthalpy_dtemp_component_k_s=75.0, python_port_1_center_enthalpy_dtemp_component_k_s=13900.0, python_port_1_mass_offset_dtemp_component_k_s=-13100.0, python_port_1_heat_dtemp_component_k_s=0.0, amesim_port_2_external_enthalpy_dtemp_component_k_s=6000.0, amesim_port_2_center_enthalpy_dtemp_component_k_s=-5000.0, amesim_port_2_mass_offset_dtemp_component_k_s=-1500.0, amesim_port_2_heat_dtemp_component_k_s=0.0, python_port_2_external_enthalpy_dtemp_component_k_s=6100.0, python_port_2_center_enthalpy_dtemp_component_k_s=-5100.0, python_port_2_mass_offset_dtemp_component_k_s=-1650.0, python_port_2_heat_dtemp_component_k_s=0.0, amesim_port_1_temperature_derivative_fd_k_s=-210.0, amesim_port_1_temperature_derivative_backward_k_s=-420.0, amesim_port_1_temperature_derivative_forward_k_s=100.0, amesim_port_2_temperature_derivative_fd_k_s=-540.0, amesim_port_2_temperature_derivative_backward_k_s=-600.0, amesim_port_2_temperature_derivative_forward_k_s=120.0, amesim_port_1_pn2vol_dtemp_k_s=-220.0, amesim_port_2_pn2vol_dtemp_k_s=-530.0, amesim_port_1_pn2vol_dtemp_residual_k_s=-10.0, amesim_port_2_pn2vol_dtemp_residual_k_s=10.0, amesim_port_1_pressure_derivative_fd_pa_s=-2100000.0, amesim_port_2_pressure_derivative_fd_pa_s=-5400000.0, amesim_port_1_pressure_derivative_mass_component_pa_s=-3100000.0, amesim_port_1_pressure_derivative_temperature_component_pa_s=900000.0, amesim_port_1_pressure_derivative_eos_sum_pa_s=-2200000.0, amesim_port_1_pressure_derivative_eos_residual_pa_s=-100000.0, amesim_port_2_pressure_derivative_mass_component_pa_s=-6100000.0, amesim_port_2_pressure_derivative_temperature_component_pa_s=800000.0, amesim_port_2_pressure_derivative_eos_sum_pa_s=-5300000.0, amesim_port_2_pressure_derivative_eos_residual_pa_s=100000.0, python_port_1_pressure_derivative_mass_component_pa_s=-3000000.0, python_port_1_pressure_derivative_temperature_component_pa_s=950000.0, python_port_1_pressure_derivative_eos_sum_pa_s=-2050000.0, python_port_2_pressure_derivative_mass_component_pa_s=-6000000.0, python_port_2_pressure_derivative_temperature_component_pa_s=850000.0, python_port_2_pressure_derivative_eos_sum_pa_s=-5150000.0, python_port_1_temperature_k=293.15, python_port_2_temperature_k=282.2, python_port_1_pressure_pa=15.3e6, python_port_2_pressure_pa=13.8e6, python_port_1_mass_derivative_kg_s=-0.46, python_port_2_mass_derivative_kg_s=-0.01, python_port_1_current_dtemp_k_s=0.0, python_port_2_current_dtemp_k_s=-1200.0, python_port_1_real_gas_reference_dtemp_k_s=-100.0, python_port_2_real_gas_reference_dtemp_k_s=-650.0, python_port_2_amesim_center_counterfactual_dtemp_k_s=-610.0, python_port_2_amesim_external_counterfactual_dtemp_k_s=-620.0, python_port_2_amesim_external_center_counterfactual_dtemp_k_s=-630.0, python_port_2_temperature_error_to_amesim_k=-5.5, ), ), pneumatic_96_inlet_line_diagnostics=( TestMqlPneumatic96InletLineDiagnostic( line_alias="pneumatic_96", node_alias="pn_node3_8", orifice_alias="pn_orifice_18", chamber_alias="pn_general_chamber", time_s=0.05, amesim_line_pressure_pa=14256386.8, amesim_line_temperature_k=289.8, amesim_line_mass_g=3.51, amesim_line_mass_derivative_fd_g_s=-6.0, amesim_line_to_node_mass_flow_g_s=452.2, amesim_orifice_to_line_mass_flow_g_s=446.2, amesim_storage_mass_derivative_from_ports_g_s=-6.0, amesim_storage_mass_derivative_residual_g_s=0.0, amesim_chamber_pressure_pa=15131788.1, amesim_chamber_temperature_k=291.9, amesim_node_pressure_pa=13887929.6, amesim_node_temperature_k=288.9, amesim_line_temperature_derivative_fd_k_s=149.5, amesim_line_pressure_derivative_fd_pa_s=-17835932.3, amesim_port_1_enthalpy_dtemp_component_k_s=1234.0, amesim_port_2_enthalpy_dtemp_component_k_s=-724.0, amesim_mass_offset_dtemp_component_k_s=-351.0, amesim_reference_temperature_derivative_sum_k_s=159.0, amesim_reference_temperature_derivative_residual_k_s=9.5, amesim_pressure_derivative_mass_component_pa_s=-30562435.9, amesim_pressure_derivative_temperature_component_pa_s=12794597.7, amesim_pressure_derivative_eos_sum_pa_s=-17767838.2, amesim_pressure_derivative_eos_residual_pa_s=68094.1, python_line_pressure_pa=14257000.0, python_line_temperature_k=290.1, python_line_mass_g=3.52, python_line_pressure_error_pa=613.2, python_line_temperature_error_k=0.3, python_line_mass_error_g=0.01, python_line_to_node_mass_flow_g_s=453.0, python_line_to_node_mass_flow_error_g_s=0.8, python_orifice_to_line_mass_flow_g_s=447.0, python_orifice_to_line_mass_flow_error_g_s=0.8, python_storage_mass_derivative_g_s=-6.0, python_storage_mass_derivative_error_g_s=0.0, python_line_to_node_amesim_node_mass_flow_g_s=451.0, python_line_to_node_amesim_node_mass_flow_error_g_s=-1.2, python_line_to_node_amesim_line_mass_flow_g_s=452.8, python_line_to_node_amesim_line_mass_flow_error_g_s=0.6, python_line_to_node_amesim_node_line_mass_flow_g_s=452.0, python_line_to_node_amesim_node_line_mass_flow_error_g_s=-0.2, python_orifice_to_line_amesim_line_mass_flow_g_s=445.8, python_orifice_to_line_amesim_line_mass_flow_error_g_s=-0.4, python_orifice_to_line_amesim_chamber_mass_flow_g_s=446.6, python_orifice_to_line_amesim_chamber_mass_flow_error_g_s=0.4, python_orifice_to_line_amesim_line_chamber_mass_flow_g_s=446.0, python_orifice_to_line_amesim_line_chamber_mass_flow_error_g_s=-0.2, python_storage_amesim_boundary_states_mass_derivative_g_s=-6.0, python_storage_amesim_boundary_states_mass_derivative_error_g_s=0.0, python_chamber_pressure_pa=15131800.0, python_chamber_temperature_k=292.0, python_current_temperature_derivative_k_s=261.9, python_port_1_current_dtemp_component_k_s=0.0, python_port_2_current_dtemp_component_k_s=261.9, python_heat_dtemp_component_k_s=0.0, python_temperature_derivative_error_k_s=112.4, python_pressure_derivative_mass_component_pa_s=-17917501.3, python_pressure_derivative_temperature_component_pa_s=650331.8, python_pressure_derivative_eos_sum_pa_s=-17267169.5, python_pressure_derivative_eos_error_pa_s=568762.8, python_reference_port_1_enthalpy_flow_w=0.0, python_reference_port_2_enthalpy_flow_w=-710.0, python_reference_port_1_dtemp_component_k_s=0.0, python_reference_port_2_dtemp_component_k_s=-65.3, python_reference_mass_offset_dtemp_component_k_s=-382.2, python_reference_temperature_derivative_k_s=-447.5, python_reference_temperature_derivative_error_k_s=-597.0, python_reference_pressure_derivative_mass_component_pa_s=( -17917501.3 ), python_reference_pressure_derivative_temperature_component_pa_s=( -22045000.0 ), python_reference_pressure_derivative_eos_sum_pa_s=-39962501.3, python_reference_pressure_derivative_eos_error_pa_s=( -21955100.0 ), python_reference_amesim_storage_mass_offset_dtemp_component_k_s=( -351.0 ), python_reference_amesim_storage_temperature_derivative_k_s=( -416.3 ), python_reference_amesim_storage_temperature_derivative_error_k_s=( -565.8 ), python_reference_amesim_storage_pressure_derivative_mass_component_pa_s=( -25407051.3 ), python_reference_amesim_storage_pressure_derivative_temperature_component_pa_s=( -20500000.0 ), python_reference_amesim_storage_pressure_derivative_eos_sum_pa_s=( -45907051.3 ), python_reference_amesim_storage_pressure_derivative_eos_error_pa_s=( -27999648.2 ), python_active_temperature_derivative_k_s=261.9, python_active_temperature_derivative_error_k_s=112.4, python_active_pressure_derivative_eos_sum_pa_s=-17267169.5, python_active_pressure_derivative_eos_error_pa_s=568762.8, python_chamber_pressure_error_pa=11.9, python_chamber_temperature_error_k=0.1, ), ), pnl0001_energy_flow_diagnostics=( TestMqlPnl0001EnergyFlowDiagnostic( line_alias="pneumatic_69", chamber_alias="pn_c1_8", node_alias="pnnode4_16", time_s=0.05, amesim_port_1_enthalpy_flow_w=-4248.6, amesim_port_1_mass_flow_g_s=419.9, amesim_node_port_2_enthalpy_flow_w=-18664.2, amesim_node_port_2_mass_flow_g_s=438.3, amesim_observed_port_enthalpy_sum_w=-22912.8, python_port_1_internal_energy_flow_w=-378000.0, python_port_2_internal_energy_flow_w=398000.0, python_current_energy_derivative_w=20000.0, python_direct_enthalpy_port_1_flow_w=-629000.0, python_direct_enthalpy_port_2_flow_w=663000.0, python_direct_enthalpy_energy_derivative_w=34000.0, python_direct_minus_current_energy_derivative_w=14000.0, python_p4_node_port_2_enthalpy_flow_w=640000.0, python_p4_node_port_2_mass_flow_g_s=430.0, python_p4_node_port_2_connected_h_j_kg=1488372.1, python_p4_node_port_2_connected_temperature_k=286.6, python_p4_node_port_2_connected_h_delta_to_line_h_j_kg=-34000.0, python_p4_node_port_2_counterfactual_dtemp_k_s=-5100.0, reference_temperature_k=298.15, amesim_port_1_reference_enthalpy_estimate_w=-4467.0, amesim_port_1_reference_enthalpy_error_w=-218.4, amesim_candidate_pn2pipefr_dm2i_g_s=-419.9, amesim_candidate_pn2pipefr_dh2i_w=4467.0, amesim_candidate_storage_enthalpy_sum_w=-14197.2, amesim_candidate_pn2pipefr_dtemp_k_s=-5480.0, amesim_candidate_pn2pipefr_dtemp_residual_k_s=-5420.3, python_reference_port_1_enthalpy_flow_w=-10760.0, python_reference_node_port_2_enthalpy_flow_w=-18500.0, python_reference_observed_port_enthalpy_sum_w=-29260.0, python_reference_port_1_to_amesim_error_w=-6511.4, python_reference_node_port_2_to_amesim_error_w=164.2, python_reference_sum_to_amesim_error_w=-6347.2, python_current_line_temperature_derivative_k_s=0.0, amesim_line_temperature_derivative_fd_k_s=277.3, amesim_pn2vol2_dtemp_node_plus_dh1_k_s=-9000.0, amesim_pn2vol2_dtemp_node_minus_dh1_k_s=-5600.0, python_reference_pn2vol2_dtemp_node_minus_dh1_k_s=-11200.0, ), ), p4_node_enthalpy_breakdown_diagnostics=( TestMqlP4NodeEnthalpyBreakdownDiagnostic( node_alias="pnnode4_16", time_s=0.05, amesim_port_1_enthalpy_flow_w=-1000.0, amesim_port_1_mass_flow_g_s=-30.0, amesim_port_3_enthalpy_flow_w=-2000.0, amesim_port_3_mass_flow_g_s=-40.0, amesim_port_4_enthalpy_flow_w=-15664.2, amesim_port_4_mass_flow_g_s=508.3, amesim_port_2_enthalpy_flow_w=-18664.2, amesim_port_2_mass_flow_g_s=438.3, python_port_1_enthalpy_flow_w=-900.0, python_port_1_mass_flow_g_s=-29.0, python_port_3_enthalpy_flow_w=-2100.0, python_port_3_mass_flow_g_s=-41.0, python_port_4_enthalpy_flow_w=643000.0, python_port_4_mass_flow_g_s=500.0, python_port_2_enthalpy_flow_w=640000.0, python_port_2_mass_flow_g_s=430.0, python_reference_port_1_enthalpy_flow_w=-800.0, python_reference_port_3_enthalpy_flow_w=-1700.0, python_reference_port_4_enthalpy_flow_w=-16000.0, python_reference_port_2_enthalpy_flow_w=-18500.0, python_real_gas_reference_port_1_enthalpy_flow_w=-790.0, python_real_gas_reference_port_3_enthalpy_flow_w=-1685.0, python_real_gas_reference_port_4_enthalpy_flow_w=-15750.0, python_real_gas_reference_port_2_enthalpy_flow_w=-18225.0, python_port_2_enthalpy_error_w=658664.2, python_reference_port_2_enthalpy_error_w=164.2, python_real_gas_reference_port_2_enthalpy_error_w=439.2, ), ), pnch012_energy_equation_diagnostics=( TestMqlPnch012EnergyEquationDiagnostic( chamber_alias="pn_c1_8", piston_alias="pn_brp2_8", line_alias="pneumatic_69", time_s=0.05, amesim_port_1_enthalpy_flow_w=-4248.6, amesim_port_1_mass_flow_g_s=419.9, amesim_chamber_mass_derivative_fd_g_s=370.8, amesim_chamber_mass_derivative_residual_g_s=49.1, amesim_chamber_temperature_derivative_fd_k_s=5134.6, amesim_chamber_volume_rate_fd_m3_s=0.001004, amesim_piston_volume_rate_m3_s=2.5e-8, amesim_heat_flow_w=-0.0, amesim_boundary_work_fd_volume_w=-289.5, amesim_pn2vol_reference_dtemp_fd_volume_k_s=8090.0, amesim_pn2vol_reference_dtemp_fd_volume_residual_k_s=2955.4, amesim_pn2vol_reference_dtemp_piston_volume_k_s=8107.0, amesim_pn2vol_reference_dtemp_piston_volume_residual_k_s=2972.4, python_port_a_mass_flow_kg_s=-0.42, python_current_energy_derivative_w=-28000.0, python_current_chamber_temperature_derivative_k_s=-7800.0, reference_temperature_k=298.15, ), ), ) diagnostic = TestMqlPnvoEventWindowDiagnostic( orifice_alias="pn_morifice_1", event_time_s=0.04, final_time_s=0.05, data_paths=data_paths, segment_diagnostics=( TestMqlPnvoEventWindowSegmentDiagnostic( t_start=0.048, t_stop=0.05, method="BDF", rtol=1.0e-5, atol=1.0e-8, max_step=1.0e-5, rhs_evaluations=8154, success=True, message="success", ), ), sample_diagnostics=(sample,), python_values_by_data_path=sample.python_values_by_data_path, amesim_values_by_data_path=sample.amesim_values_by_data_path, ) summary = format_test_mql_pnvo_event_window_summary(diagnostic) self.assertAlmostEqual(diagnostic.abs_error("dm2@pn_morifice_1"), 6.9) self.assertAlmostEqual(sample.abs_error("press@pn_c1_8"), 32067.9) self.assertIn("PNVO event window diagnostic (pn_morifice_1)", summary) self.assertIn("Sample comparisons:", summary) self.assertFalse(sample.amesim_values_are_interpolated) self.assertIn("t=0.05 (amesim=exact@0.05)", summary) self.assertIn("rhs=8154", summary) self.assertIn("rhs@pneumatic_69", summary) self.assertIn("dm_dt=-0.0222", summary) self.assertIn("pressure_loss@pneumatic_69", summary) self.assertIn("cm=0.0159", summary) self.assertIn("dp_multiplier=3.07", summary) self.assertIn("candidate_pn2pipefr_dm1=424.2", summary) self.assertIn("candidate_pn2pipefr_ratio=1.01", summary) self.assertIn("amesim_linear_k=5.56e-06", summary) self.assertIn("python_linear_k=1.06e-05", summary) self.assertIn("linear_k_ratio=1.91", summary) self.assertIn("flow_parameter@pn_morifice_1", summary) self.assertIn("amesim_cm=0.0158", summary) self.assertIn("python_cm=0.0147", summary) self.assertIn("cm_ratio=0.93", summary) self.assertIn("amesim_gasvel=885.9", summary) self.assertIn("pnvo_upstream_enthalpy@pn_morifice_1", summary) self.assertIn("amesim_dh2=-18851.7", summary) self.assertIn("amesim_line_dhctr=15823.7", summary) self.assertIn("amesim_implied_t2=290.2", summary) self.assertIn("amesim_implied_t2_delta=2.5", summary) self.assertIn("amesim_ref_dh2=-24714.8", summary) self.assertIn("amesim_real_ref_dh2=-19240.8", summary) self.assertIn("amesim_real_ref_dh2_error=-389.1", summary) self.assertIn("python_current_dh2=679490.8", summary) self.assertIn("python_ref_dh2=-38478.5", summary) self.assertIn("python_real_ref_dh2=-32998.5", summary) self.assertIn("inlet_line@pneumatic_96", summary) self.assertIn("node=pn_node3_8", summary) self.assertIn("orifice=pn_orifice_18", summary) self.assertIn("amesim_line_to_node_dm1=452.2", summary) self.assertIn("amesim_orifice_to_line_dm2=446.2", summary) self.assertIn("python_line_to_node_dm=453.0", summary) self.assertIn("python_line_to_node_dm_error=0.8", summary) self.assertIn("python_storage_dm_error=0.0", summary) self.assertIn("python_line_to_node_amesim_node_line_dm=452.0", summary) self.assertIn("python_orifice_to_line_amesim_line_chamber_dm=446.0", summary) self.assertIn("python_storage_amesim_boundary_states_dm_error=0.0", summary) self.assertIn("amesim_fd_dT=149.5", summary) self.assertIn("amesim_dT_m_offset=-351.0", summary) self.assertIn("python_current_dT=261.9", summary) self.assertIn("python_dT_error=112.4", summary) self.assertIn("python_dP_error=568762.8", summary) self.assertIn("python_ref_dT=-447.5", summary) self.assertIn("python_ref_dT_error=-597.0", summary) self.assertIn("python_ref_dP_error=-21955100.0", summary) self.assertIn("python_ref_amesim_storage_dT=-416.3", summary) self.assertIn("python_ref_amesim_storage_dP_error=-27999648.2", summary) self.assertIn("python_active_dT=261.9", summary) self.assertIn("python_active_dP_error=568762.8", summary) self.assertIn("pnl0003_energy@pneumatic_87", summary) self.assertIn("amesim_sdm1=-462.7", summary) self.assertIn("amesim_sdh2=3028.0", summary) self.assertIn("python_node_dm1=-333.0", summary) self.assertIn("python_node_dm3=-119.9", summary) self.assertIn("python_node_to_line_dm=452.9", summary) self.assertIn("python_node_to_line_dm_error=461.4", summary) self.assertIn("amesim_m2=1.05", summary) self.assertIn("python_m2=1.04", summary) self.assertIn("python_m2_error=-0.01", summary) self.assertIn("amesim_fd_dmgas=-470.0", summary) self.assertIn("amesim_back_dmgas=-540.0", summary) self.assertIn("amesim_fwd_dmgas=-400.0", summary) self.assertIn("amesim_storage_dmgas=-465.3", summary) self.assertIn("amesim_storage_dmgas_residual=4.7", summary) self.assertIn("amesim_dmctr_py_sign=454.2", summary) self.assertIn("python_center_dm=454.0", summary) self.assertIn("amesim_sdm1_center=-454.2", summary) self.assertIn("amesim_sdm2_center=454.2", summary) self.assertIn("python_sdm1_ext=337.0", summary) self.assertIn("python_sdm1_ext_error=15.1", summary) self.assertIn("python_sdm1_center=-454.0", summary) self.assertIn("python_sdm1_center_error=0.2", summary) self.assertIn("python_sdm1=-117.0", summary) self.assertIn("python_sdm1_error=345.7", summary) self.assertIn("python_sdm2_ext=-456.4", summary) self.assertIn("python_sdm2_ext_error=0.4", summary) self.assertIn("python_sdm2_center=454.0", summary) self.assertIn("python_sdm2_center_error=-0.2", summary) self.assertIn("python_sdm2=-2.4", summary) self.assertIn("python_sdm2_error=0.2", summary) self.assertIn("python_amesim_state_darcy_center_dm=453.5", summary) self.assertIn("python_amesim_state_darcy_center_dm_error=-0.7", summary) self.assertIn("python_amesim_state_pn2pipefr_center_dm=455.0", summary) self.assertIn("python_amesim_state_pn2pipefr_center_dm_error=0.8", summary) self.assertIn("amesim_center_real_p1_error=-123.7", summary) self.assertIn("python_center_ideal_p2=16200.0", summary) self.assertIn("python_center_dm_error=-0.2", summary) self.assertIn("python_dmgas_dt=-0.47", summary) self.assertIn("python_ref_sdh1=15880.0", summary) self.assertIn("python_ref_sdh2_error=172.0", summary) self.assertIn("amesim_dT1_ext_h=80.0", summary) self.assertIn("amesim_dT1_ctr_h=14000.0", summary) self.assertIn("amesim_dT1_m_offset=-13200.0", summary) self.assertIn("python_dT1_ext_h=75.0", summary) self.assertIn("python_dT1_ctr_h=13900.0", summary) self.assertIn("python_dT1_m_offset=-13100.0", summary) self.assertIn("amesim_dT2_ext_h=6000.0", summary) self.assertIn("amesim_dT2_ctr_h=-5000.0", summary) self.assertIn("amesim_dT2_m_offset=-1500.0", summary) self.assertIn("python_dT2_ext_h=6100.0", summary) self.assertIn("python_dT2_ctr_h=-5100.0", summary) self.assertIn("python_dT2_m_offset=-1650.0", summary) self.assertIn("amesim_back_dT2=-600.0", summary) self.assertIn("amesim_fwd_dT2=120.0", summary) self.assertIn("amesim_pn2vol_dT2=-530.0", summary) self.assertIn("amesim_fd_dP1=-2100000.0", summary) self.assertIn("amesim_dP1_m=-3100000.0", summary) self.assertIn("amesim_dP1_T=900000.0", summary) self.assertIn("amesim_dP1_residual=-100000.0", summary) self.assertIn("amesim_dP2_sum=-5300000.0", summary) self.assertIn("python_dP1_sum=-2050000.0", summary) self.assertIn("python_dP2_T=850000.0", summary) self.assertIn("python_real_ref_dT2=-650.0", summary) self.assertIn("python_amesim_center_dT2=-610.0", summary) self.assertIn("python_amesim_ext_dT2=-620.0", summary) self.assertIn("python_amesim_ext_ctr_dT2=-630.0", summary) self.assertIn("python_t2_error=-5.5", summary) self.assertIn("energy_flow@pneumatic_69", summary) self.assertIn("amesim_dh1=-4248.6", summary) self.assertIn("amesim_node_dh2=-18664.2", summary) self.assertIn("direct_h_dU_dt=34000.0", summary) self.assertIn("python_p4_node_dh2=640000.0", summary) self.assertIn("python_p4_node_dm2=430.0", summary) self.assertIn("python_p4_node_h2=1488372.1", summary) self.assertIn("python_p4_node_t2=286.6", summary) self.assertIn("python_p4_node_h2_delta=-34000.0", summary) self.assertIn("python_p4_node_counterfactual_dT=-5100.0", summary) self.assertIn("ref_dh1_est=-4467.0", summary) self.assertIn("ref_dh1_error=-218.4", summary) self.assertIn("candidate_dm2i=-419.9", summary) self.assertIn("candidate_dh2i=4467.0", summary) self.assertIn("candidate_storage_sdh=-14197.2", summary) self.assertIn("candidate_pn2pipefr_dT=-5480.0", summary) self.assertIn("python_ref_dh1=-10760.0", summary) self.assertIn("python_ref_node_dh2=-18500.0", summary) self.assertIn("python_ref_sdh=-29260.0", summary) self.assertIn("python_ref_sdh_error=-6347.2", summary) self.assertIn("python_current_dT=0.0", summary) self.assertIn("amesim_fd_dT=277.3", summary) self.assertIn("amesim_pn2vol2_dT_node_plus_dh1=-9000.0", summary) self.assertIn("python_ref_pn2vol2_dT_node_minus_dh1=-11200.0", summary) self.assertIn("p4_node_enthalpy@pnnode4_16", summary) self.assertIn("python_real_ref_p2_dh_error=439.2", summary) self.assertIn("amesim_p4_dh=-15664.2", summary) self.assertIn("python_p4_dh=643000.0", summary) self.assertIn("python_ref_p2_dh=-18500.0", summary) self.assertIn("python_ref_p2_dh_error=164.2", summary) self.assertIn("pnch012_energy@pn_c1_8", summary) self.assertIn("amesim_fd_dmgas=370.8", summary) self.assertIn("amesim_dmgas_residual=49.1", summary) self.assertIn("amesim_fd_dvol=0.001004", summary) self.assertIn("amesim_pn2vol_ref_dT_fdvol=8090.0", summary) self.assertIn("python_current_dT=-7800.0", summary) self.assertIn("Final comparison:", summary) self.assertTrue(summary.endswith("\n")) def test_formats_pnvo_event_window_candidate_comparison(self) -> None: data_paths = ("press@pn_c1_8", "dm2@pn_morifice_1") default_diagnostic = TestMqlPnvoEventWindowDiagnostic( orifice_alias="pn_morifice_1", event_time_s=0.04, final_time_s=0.05, data_paths=data_paths, segment_diagnostics=(), sample_diagnostics=(), python_values_by_data_path={ "press@pn_c1_8": 1.0, "dm2@pn_morifice_1": 10.0, }, amesim_values_by_data_path={ "press@pn_c1_8": 4.0, "dm2@pn_morifice_1": 8.0, }, ) candidate_diagnostic = TestMqlPnvoEventWindowDiagnostic( orifice_alias="pn_morifice_1", event_time_s=0.04, final_time_s=0.05, data_paths=data_paths, segment_diagnostics=(), sample_diagnostics=(), python_values_by_data_path={ "press@pn_c1_8": 3.0, "dm2@pn_morifice_1": 13.0, }, amesim_values_by_data_path={ "press@pn_c1_8": 4.0, "dm2@pn_morifice_1": 8.0, }, ) metric = TestMqlPnvoEventWindowCandidateMetricComparison( time_s=0.0401, metric_name="p96:p2_abs_error_pa", default_abs_error=32000.0, candidate_abs_error=3600.0, ) diagnostic = TestMqlPnvoEventWindowCandidateComparisonDiagnostic( orifice_alias="pn_morifice_1", event_time_s=0.04, final_time_s=0.05, default_diagnostic=default_diagnostic, candidate_diagnostic=candidate_diagnostic, metric_comparisons=(metric,), ) summary = format_test_mql_pnvo_event_window_candidate_comparison_summary( diagnostic ) self.assertEqual(metric.abs_error_delta, -28400.0) self.assertIn("PNVO event window candidate comparison", summary) self.assertIn("negative delta means the candidate reduced error", summary) self.assertIn("t=0.0401 - p96:p2_abs_error_pa", summary) self.assertIn("default_abs_error=32000.0", summary) self.assertIn("candidate_abs_error=3600.0", summary) self.assertIn("delta=-28400.0", summary) self.assertIn("Final comparison:", summary) self.assertIn("press@pn_c1_8", summary) self.assertIn("delta=-2.0", summary) self.assertIn("dm2@pn_morifice_1", summary) self.assertIn("delta=3.0", summary) self.assertTrue(summary.endswith("\n")) def test_full_state_script_can_skip_artifact_files(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: output_dir = Path(tmpdir) run, returned_output_dir = run_test_mql_full_state_comparison( TestMqlFullStateComparisonScriptConfig( paths=TestMqlFullStateComparisonPathConfig( archive_path=TEST_MQL_AME, output_dir=output_dir, ), execution=TestMqlFullStateComparisonExecutionConfig( write_summary=False, write_comparison_csv=False, data_paths=("press@pn_c1_8",), solver=SolveIVPConfig(t_start=0.0, t_stop=0.0), t_eval=(0.0,), ), ) ) self.assertEqual(returned_output_dir, output_dir) self.assertEqual(run.signal_count, 1) self.assertFalse((output_dir / "test_mql_full_state_comparison_summary.txt").exists()) self.assertFalse((output_dir / "test_mql_amesim_comparison.csv").exists()) def test_format_test_mql_full_state_comparison_summary(self) -> None: run, _ = run_test_mql_full_state_comparison( TestMqlFullStateComparisonScriptConfig( paths=TestMqlFullStateComparisonPathConfig(archive_path=TEST_MQL_AME), execution=TestMqlFullStateComparisonExecutionConfig( write_summary=False, write_comparison_csv=False, data_paths=("press@pn_c1_8",), solver=SolveIVPConfig(t_start=0.0, t_stop=0.0), t_eval=(0.0,), ), ) ) summary = format_test_mql_full_state_comparison_summary(run) self.assertIn("Model: test_mql", summary) self.assertIn("Output schema signals: 858", summary) self.assertIn("Compared signals: 1", summary) self.assertIn("Largest absolute error: press@pn_c1_8=", summary) self.assertIn("Largest final endpoint error: press@pn_c1_8=", summary) self.assertIn("Metrics by max absolute error:", summary) self.assertIn("Endpoint diagnostics by final absolute error:", summary) self.assertIn("Largest endpoint chamber RHS breakdown: pn_c1_8", summary) self.assertTrue(summary.endswith("\n")) if __name__ == "__main__": unittest.main()