完善通用求解器回归与前端交互
- 引入因果坐标内核、热流体恢复和递进长时回归\n- 完善正交连线、线桥、视图保持与结果曲线缩放\n- 补充依赖约束、CI、测试基线和北京时间更新日志
This commit is contained in:
1 parent
143e8dd309
commit
b435daecf2
65 files changed
+172271
-701
No files matched your search
@@ -6,6 +6,8 @@ from unittest.mock import patch
|
||||
|
||||
from app.main import compile_reactflow_network
|
||||
from app.simulation.solvers.algebraic import (
|
||||
CAUSAL_COORDINATE_KERNEL_ENVIRONMENT_VARIABLE,
|
||||
CAUSAL_EXECUTOR_V2_ENVIRONMENT_VARIABLE,
|
||||
CAUSAL_FAST_PATH_ENVIRONMENT_VARIABLE,
|
||||
)
|
||||
from app.simulation.systems.generic import GenericFluidSystem
|
||||
@@ -16,11 +18,275 @@ from tests.test_amesim_pnvo001_signal_xml import (
|
||||
from tests.test_generic_system_xml_simulation import chain_project
|
||||
|
||||
|
||||
def _system(project) -> GenericFluidSystem:
|
||||
return GenericFluidSystem(compile_reactflow_network(project))
|
||||
def _system(
|
||||
project,
|
||||
*,
|
||||
executor_v2: bool | None = False,
|
||||
coordinate_kernel: bool | None = None,
|
||||
) -> GenericFluidSystem:
|
||||
environment = {}
|
||||
if executor_v2 is not None:
|
||||
environment[CAUSAL_EXECUTOR_V2_ENVIRONMENT_VARIABLE] = (
|
||||
"1" if executor_v2 else "0"
|
||||
)
|
||||
if coordinate_kernel is not None:
|
||||
environment[CAUSAL_COORDINATE_KERNEL_ENVIRONMENT_VARIABLE] = (
|
||||
"1" if coordinate_kernel else "0"
|
||||
)
|
||||
with patch.dict(
|
||||
os.environ,
|
||||
environment,
|
||||
):
|
||||
if executor_v2 is None:
|
||||
os.environ.pop(CAUSAL_EXECUTOR_V2_ENVIRONMENT_VARIABLE, None)
|
||||
if coordinate_kernel is None:
|
||||
os.environ.pop(
|
||||
CAUSAL_COORDINATE_KERNEL_ENVIRONMENT_VARIABLE,
|
||||
None,
|
||||
)
|
||||
return GenericFluidSystem(compile_reactflow_network(project))
|
||||
|
||||
|
||||
class PressureFlowCausalExecutionTests(unittest.TestCase):
|
||||
def test_compiled_v2_is_enabled_by_default_and_can_be_disabled(self) -> None:
|
||||
default = _system(zero_force_mass_project(), executor_v2=None)
|
||||
disabled = _system(zero_force_mass_project(), executor_v2=False)
|
||||
enabled = _system(zero_force_mass_project(), executor_v2=True)
|
||||
|
||||
self.assertTrue(
|
||||
default.pressure_flow_solver.causal_execution_diagnostics()[
|
||||
"executorV2Configured"
|
||||
]
|
||||
)
|
||||
self.assertFalse(
|
||||
disabled.pressure_flow_solver.causal_execution_diagnostics()[
|
||||
"executorV2Configured"
|
||||
]
|
||||
)
|
||||
self.assertTrue(
|
||||
enabled.pressure_flow_solver.causal_execution_diagnostics()[
|
||||
"executorV2Configured"
|
||||
]
|
||||
)
|
||||
|
||||
def test_coordinate_kernel_is_default_on_and_independently_disabled(
|
||||
self,
|
||||
) -> None:
|
||||
default = _system(
|
||||
zero_force_mass_project(),
|
||||
executor_v2=True,
|
||||
)
|
||||
disabled = _system(
|
||||
zero_force_mass_project(),
|
||||
executor_v2=True,
|
||||
coordinate_kernel=False,
|
||||
)
|
||||
|
||||
default_diagnostics = (
|
||||
default.pressure_flow_solver.causal_execution_diagnostics()
|
||||
)
|
||||
disabled_diagnostics = (
|
||||
disabled.pressure_flow_solver.causal_execution_diagnostics()
|
||||
)
|
||||
self.assertTrue(default_diagnostics["coordinateKernelConfigured"])
|
||||
self.assertTrue(default_diagnostics["coordinateKernelEnabled"])
|
||||
self.assertFalse(disabled_diagnostics["coordinateKernelConfigured"])
|
||||
self.assertFalse(disabled_diagnostics["coordinateKernelEnabled"])
|
||||
|
||||
def test_compiled_v2_matches_v1_bitwise_without_target_names(self) -> None:
|
||||
compiled = _system(
|
||||
high_pressure_helium_step_project(),
|
||||
executor_v2=True,
|
||||
)
|
||||
v1 = _system(high_pressure_helium_step_project())
|
||||
compiled_state = compiled.initial_state_vector()
|
||||
v1_state = v1.initial_state_vector()
|
||||
|
||||
for time in (0.0, 0.041, 0.8):
|
||||
self.assertEqual(
|
||||
compiled.rhs(time, compiled_state),
|
||||
v1.rhs(time, v1_state),
|
||||
)
|
||||
self.assertEqual(
|
||||
tuple(
|
||||
unknown.read()
|
||||
for unknown in compiled.pressure_flow_solver.unknowns
|
||||
),
|
||||
tuple(
|
||||
unknown.read()
|
||||
for unknown in v1.pressure_flow_solver.unknowns
|
||||
),
|
||||
)
|
||||
|
||||
diagnostics = (
|
||||
compiled.pressure_flow_solver.causal_execution_diagnostics()
|
||||
)
|
||||
self.assertGreater(diagnostics["executorV2FastSolveCount"], 0)
|
||||
self.assertGreater(diagnostics["coordinateKernelFastSolveCount"], 0)
|
||||
self.assertEqual(
|
||||
diagnostics["compiledAssignmentCount"],
|
||||
len(compiled.pressure_flow_solver.unknowns),
|
||||
)
|
||||
self.assertEqual(
|
||||
diagnostics["executorV2RuntimeValidationFailureCount"],
|
||||
0,
|
||||
)
|
||||
self.assertEqual(
|
||||
diagnostics["canonicalCoordinateCount"],
|
||||
diagnostics["logicalEffortCoordinateCount"]
|
||||
+ diagnostics["compiledFlowAssignmentCount"],
|
||||
)
|
||||
self.assertEqual(
|
||||
diagnostics["eliminatedEffortAliasCount"],
|
||||
diagnostics["compiledEffortUnknownCount"]
|
||||
- diagnostics["logicalEffortCoordinateCount"],
|
||||
)
|
||||
|
||||
def test_compiled_v2_fast_solve_skips_legacy_seed_scan_and_scales(
|
||||
self,
|
||||
) -> None:
|
||||
system = _system(zero_force_mass_project(), executor_v2=True)
|
||||
state = system.initial_state_vector()
|
||||
system.rhs(0.0, state)
|
||||
solver = system.pressure_flow_solver
|
||||
|
||||
with patch.object(
|
||||
solver,
|
||||
"_solve_explicit_flow_unknowns",
|
||||
wraps=solver._solve_explicit_flow_unknowns,
|
||||
) as legacy_seed, patch.object(
|
||||
solver,
|
||||
"_pressure_flow_equation_values",
|
||||
wraps=solver._pressure_flow_equation_values,
|
||||
) as residuals, patch.object(
|
||||
solver,
|
||||
"_scales",
|
||||
wraps=solver._scales,
|
||||
) as scales, patch.object(
|
||||
solver,
|
||||
"_evaluate_explicit_flow_stage",
|
||||
wraps=solver._evaluate_explicit_flow_stage,
|
||||
) as allocating_stage:
|
||||
diagnostics = solver.solve(effort_variables=("p",))
|
||||
|
||||
legacy_seed.assert_not_called()
|
||||
residuals.assert_not_called()
|
||||
scales.assert_not_called()
|
||||
allocating_stage.assert_not_called()
|
||||
self.assertIs(
|
||||
diagnostics,
|
||||
solver._causal_cached_fast_diagnostics,
|
||||
)
|
||||
self.assertTrue(diagnostics.causal_fast_path_used)
|
||||
|
||||
def test_compiled_v2_nonfinite_assignment_fuses_and_audits_same_solve(
|
||||
self,
|
||||
) -> None:
|
||||
system = _system(zero_force_mass_project(), executor_v2=True)
|
||||
state = system.initial_state_vector()
|
||||
system.rhs(0.0, state)
|
||||
solver = system.pressure_flow_solver
|
||||
with patch.object(
|
||||
solver,
|
||||
"_execute_causal_coordinate_flow_plan",
|
||||
return_value="nonFiniteCausalFlowAssignment",
|
||||
):
|
||||
diagnostics = solver.solve(effort_variables=("p",))
|
||||
|
||||
self.assertTrue(diagnostics.success)
|
||||
self.assertTrue(diagnostics.residual_verified_this_solve)
|
||||
execution = solver.causal_execution_diagnostics()
|
||||
self.assertFalse(execution["enabled"])
|
||||
self.assertEqual(
|
||||
execution["disabledReason"],
|
||||
"nonFiniteCausalFlowAssignment",
|
||||
)
|
||||
self.assertEqual(execution["executorV2RuntimeValidationFailureCount"], 1)
|
||||
self.assertEqual(execution["legacyFallbackCount"], 1)
|
||||
|
||||
def test_compiled_v2_rejects_nonfinite_external_mechanical_effort(
|
||||
self,
|
||||
) -> None:
|
||||
system = _system(zero_force_mass_project(), executor_v2=True)
|
||||
state = system.initial_state_vector()
|
||||
system.rhs(0.0, state)
|
||||
solver = system.pressure_flow_solver
|
||||
velocity = next(
|
||||
unknown for unknown in solver.unknowns if unknown.variable == "v"
|
||||
)
|
||||
velocity.write(float("nan"))
|
||||
|
||||
with self.assertRaises(ValueError):
|
||||
solver.solve(effort_variables=("p",))
|
||||
|
||||
execution = solver.causal_execution_diagnostics()
|
||||
self.assertFalse(execution["enabled"])
|
||||
self.assertEqual(
|
||||
execution["disabledReason"],
|
||||
"nonFiniteCausalExternalEffort",
|
||||
)
|
||||
self.assertEqual(execution["executorV2RuntimeValidationFailureCount"], 1)
|
||||
self.assertEqual(execution["legacyFallbackCount"], 1)
|
||||
|
||||
def test_compiled_v2_stage_error_fuses_to_existing_fallback(self) -> None:
|
||||
system = _system(zero_force_mass_project(), executor_v2=True)
|
||||
state = system.initial_state_vector()
|
||||
system.rhs(0.0, state)
|
||||
solver = system.pressure_flow_solver
|
||||
with patch.object(
|
||||
solver,
|
||||
"_execute_causal_coordinate_flow_plan",
|
||||
return_value="causalFlowEvaluationFailed:ValueError",
|
||||
):
|
||||
diagnostics = solver.solve(effort_variables=("p",))
|
||||
|
||||
self.assertTrue(diagnostics.success)
|
||||
self.assertTrue(diagnostics.residual_verified_this_solve)
|
||||
execution = solver.causal_execution_diagnostics()
|
||||
self.assertEqual(
|
||||
execution["disabledReason"],
|
||||
"causalFlowEvaluationFailed:ValueError",
|
||||
)
|
||||
self.assertEqual(execution["executorV2RuntimeValidationFailureCount"], 1)
|
||||
|
||||
def test_compiled_v2_assignment_count_drift_fuses_to_fallback(self) -> None:
|
||||
system = _system(zero_force_mass_project(), executor_v2=True)
|
||||
state = system.initial_state_vector()
|
||||
system.rhs(0.0, state)
|
||||
solver = system.pressure_flow_solver
|
||||
with patch.object(
|
||||
solver,
|
||||
"_execute_causal_coordinate_flow_plan",
|
||||
return_value="causalFlowAssignmentCountMismatch",
|
||||
):
|
||||
diagnostics = solver.solve(effort_variables=("p",))
|
||||
|
||||
self.assertTrue(diagnostics.success)
|
||||
self.assertTrue(diagnostics.residual_verified_this_solve)
|
||||
execution = solver.causal_execution_diagnostics()
|
||||
self.assertEqual(
|
||||
execution["disabledReason"],
|
||||
"causalFlowAssignmentCountMismatch",
|
||||
)
|
||||
self.assertEqual(execution["executorV2RuntimeValidationFailureCount"], 1)
|
||||
|
||||
def test_compiled_v2_keeps_the_sixty_four_solve_audit_boundary(self) -> None:
|
||||
system = _system(zero_force_mass_project(), executor_v2=True)
|
||||
state = system.initial_state_vector()
|
||||
solver = system.pressure_flow_solver
|
||||
system.rhs(0.0, state)
|
||||
|
||||
for _iteration in range(64):
|
||||
system.rhs(0.0, state)
|
||||
before = solver.causal_execution_diagnostics()
|
||||
self.assertEqual(before["executorV2FastSolveCount"], 64)
|
||||
self.assertEqual(before["fullResidualAuditCount"], 1)
|
||||
|
||||
system.rhs(0.0, state)
|
||||
after = solver.causal_execution_diagnostics()
|
||||
self.assertEqual(after["executorV2FastSolveCount"], 64)
|
||||
self.assertEqual(after["fullResidualAuditCount"], 2)
|
||||
|
||||
def test_strict_causal_rhs_matches_environment_disabled_legacy_bitwise(
|
||||
self,
|
||||
) -> None:
|
||||
|
||||
Reference in new issue
Block a user