整合求解器活动监控与步长回归证据
同步远端 PNL0003 诊断和大采样网格能力,语义合并活动感知的 60 秒真停滞判定与旧后端 15 分钟兼容兜底。 纳管热路径优化、15 单元运行证据、浏览器与 API 报告,并补充北京时间更新日志和遗留问题。
This commit is contained in:
1 parent
c19cf77aee
commit
e18399c022
46 files changed
+181589
-170
No files matched your search
@@ -12,6 +12,7 @@ from app.simulation.solvers.causal_ir import (
|
||||
from app.simulation.systems.generic import GenericFluidSystem
|
||||
from app.system_xml import validate_system_xml_document
|
||||
from tests.test_amesim_mechanical_xml import zero_force_mass_project
|
||||
from tests.test_amesim_pnl0001_xml import amesim_pnl0001_project
|
||||
from tests.test_amesim_pnvo001_signal_xml import high_pressure_helium_step_project
|
||||
from tests.test_generic_system_xml_simulation import chain_project
|
||||
|
||||
@@ -19,11 +20,102 @@ from tests.test_generic_system_xml_simulation import chain_project
|
||||
TARGET_XML = Path("tests/data/test-mql-8.xml")
|
||||
|
||||
|
||||
class _SyntheticDirectReaderError(Exception):
|
||||
pass
|
||||
|
||||
|
||||
def _system(project) -> GenericFluidSystem:
|
||||
return GenericFluidSystem(compile_reactflow_network(project))
|
||||
|
||||
|
||||
class CausalNumericIRTests(unittest.TestCase):
|
||||
def _assert_direct_reader_exception_is_controlled(
|
||||
self,
|
||||
exception: Exception,
|
||||
) -> None:
|
||||
system = _system(amesim_pnl0001_project())
|
||||
solver = system.pressure_flow_solver
|
||||
compilation = compile_causal_numeric_ir(solver)
|
||||
self.assertTrue(compilation.supported, compilation.fallback_reason)
|
||||
assert compilation.ir is not None
|
||||
ir = compilation.ir
|
||||
direct_evaluation = next(
|
||||
evaluation
|
||||
for stage in ir.program.effort_stages
|
||||
for evaluation in stage.evaluations
|
||||
if evaluation.opcode is CausalIROpcode.EFFORT_DIRECT_RESIDUAL
|
||||
)
|
||||
evaluators = list(ir.bindings.evaluators)
|
||||
victim = solver.unknowns[-1]
|
||||
before = tuple(unknown.read() for unknown in solver.unknowns)
|
||||
|
||||
def failing_reader() -> float:
|
||||
victim.write(victim.read() + 123.0)
|
||||
raise exception
|
||||
|
||||
evaluators[direct_evaluation.evaluator_slot] = failing_reader
|
||||
failing_ir = replace(
|
||||
ir,
|
||||
bindings=replace(ir.bindings, evaluators=tuple(evaluators)),
|
||||
)
|
||||
|
||||
result = failing_ir.execute(
|
||||
failing_ir.create_workspace(),
|
||||
transactional=True,
|
||||
)
|
||||
|
||||
self.assertFalse(result.success)
|
||||
self.assertEqual(
|
||||
result.fallback_reason,
|
||||
f"effortEvaluationFailed:{type(exception).__name__}",
|
||||
)
|
||||
self.assertTrue(result.rolled_back)
|
||||
self.assertEqual(
|
||||
tuple(unknown.read() for unknown in solver.unknowns),
|
||||
before,
|
||||
)
|
||||
|
||||
def test_direct_reader_key_error_returns_controlled_failure(self) -> None:
|
||||
self._assert_direct_reader_exception_is_controlled(
|
||||
KeyError("synthetic direct-reader key failure")
|
||||
)
|
||||
|
||||
def test_direct_reader_custom_exception_returns_controlled_failure(
|
||||
self,
|
||||
) -> None:
|
||||
self._assert_direct_reader_exception_is_controlled(
|
||||
_SyntheticDirectReaderError("synthetic direct-reader failure")
|
||||
)
|
||||
|
||||
def test_direct_reader_memory_error_remains_fatal(self) -> None:
|
||||
system = _system(amesim_pnl0001_project())
|
||||
compilation = compile_causal_numeric_ir(system.pressure_flow_solver)
|
||||
self.assertTrue(compilation.supported, compilation.fallback_reason)
|
||||
assert compilation.ir is not None
|
||||
ir = compilation.ir
|
||||
direct_evaluation = next(
|
||||
evaluation
|
||||
for stage in ir.program.effort_stages
|
||||
for evaluation in stage.evaluations
|
||||
if evaluation.opcode is CausalIROpcode.EFFORT_DIRECT_RESIDUAL
|
||||
)
|
||||
evaluators = list(ir.bindings.evaluators)
|
||||
|
||||
def failing_reader() -> float:
|
||||
raise MemoryError("synthetic direct-reader allocation failure")
|
||||
|
||||
evaluators[direct_evaluation.evaluator_slot] = failing_reader
|
||||
failing_ir = replace(
|
||||
ir,
|
||||
bindings=replace(ir.bindings, evaluators=tuple(evaluators)),
|
||||
)
|
||||
|
||||
with self.assertRaisesRegex(MemoryError, "allocation failure"):
|
||||
failing_ir.execute(
|
||||
failing_ir.create_workspace(),
|
||||
transactional=True,
|
||||
)
|
||||
|
||||
def test_structure_signature_is_stable_and_excludes_bindings(self) -> None:
|
||||
first = compile_causal_numeric_ir(
|
||||
_system(zero_force_mass_project()).pressure_flow_solver
|
||||
@@ -242,18 +334,56 @@ class TargetCausalNumericIRStructureTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(len(pressure_stage.operations), 76)
|
||||
self.assertEqual(len(pressure_stage.evaluations), 40)
|
||||
self.assertTrue(
|
||||
all(
|
||||
self.assertEqual(
|
||||
sum(
|
||||
evaluation.opcode is CausalIROpcode.EFFORT_DIRECT_RESIDUAL
|
||||
for evaluation in pressure_stage.evaluations
|
||||
),
|
||||
20,
|
||||
)
|
||||
self.assertEqual(
|
||||
sum(
|
||||
evaluation.opcode
|
||||
is CausalIROpcode.EFFORT_COMPONENT_RESIDUAL
|
||||
for evaluation in pressure_stage.evaluations
|
||||
)
|
||||
),
|
||||
20,
|
||||
)
|
||||
self.assertEqual(
|
||||
self.system.pressure_flow_solver.causal_execution_diagnostics()[
|
||||
"directEffortAnchorCount"
|
||||
],
|
||||
20,
|
||||
)
|
||||
self.assertEqual(
|
||||
[len(stage.target_slots) for stage in program.flow_stages],
|
||||
[114, 134, 49, 33, 5, 1],
|
||||
)
|
||||
|
||||
def test_target_preserves_exact_direct_sum_assignment_counts(self) -> None:
|
||||
solver = self.system.pressure_flow_solver
|
||||
self.assertEqual(
|
||||
solver.causal_execution_diagnostics()[
|
||||
"directSumFlowAssignmentCount"
|
||||
],
|
||||
38,
|
||||
)
|
||||
component_equation_ids = frozenset(
|
||||
equation.id
|
||||
for evaluation in solver._component_equation_plan
|
||||
for equation in evaluation.templates
|
||||
)
|
||||
secondary = (
|
||||
self.system._thermofluid_closure_plan.secondary_block_solvers[0]
|
||||
)
|
||||
secondary_direct_count = sum(
|
||||
assignment.evaluate is not None
|
||||
and assignment.equation_id in component_equation_ids
|
||||
for stage in secondary._selected_explicit_flow_plan
|
||||
for assignment in stage.assignments
|
||||
)
|
||||
self.assertEqual(secondary_direct_count, 36)
|
||||
|
||||
def test_target_reference_execution_matches_all_compatibility_slots(self) -> None:
|
||||
assert self.compilation.ir is not None
|
||||
solver = self.system.pressure_flow_solver
|
||||
|
||||
Reference in new issue
Block a user