整合求解器活动监控与步长回归证据
同步远端 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
@@ -26,7 +26,10 @@ from app.main import (
|
||||
)
|
||||
from app.simulation.components.experimental.flow.resistive_pipe import ResistivePipe
|
||||
from app.simulation.registry import get_component_model_spec
|
||||
from app.simulation.solvers.solver import SolveIVPConfig
|
||||
from app.simulation.solvers.solver import (
|
||||
SolveIVPConfig,
|
||||
SolverActivityTracker,
|
||||
)
|
||||
from app.simulation.systems.generic import (
|
||||
GenericFluidSystem,
|
||||
SimulationPreparationError,
|
||||
@@ -259,7 +262,9 @@ class GenericSystemXmlSimulationTests(unittest.TestCase):
|
||||
def test_generic_chain_simulation_conserves_mass_and_moves_pressures(self) -> None:
|
||||
network = compile_reactflow_network(chain_project())
|
||||
progress: list[tuple[float, str]] = []
|
||||
result = GenericFluidSystem(network).simulate(
|
||||
activity_tracker = SolverActivityTracker()
|
||||
system = GenericFluidSystem(network)
|
||||
result = system.simulate(
|
||||
SolveIVPConfig(
|
||||
t_start=0.0,
|
||||
t_stop=0.01,
|
||||
@@ -268,6 +273,7 @@ class GenericSystemXmlSimulationTests(unittest.TestCase):
|
||||
),
|
||||
sample_step=0.005,
|
||||
progress_callback=lambda value, phase: progress.append((value, phase)),
|
||||
activity_tracker=activity_tracker,
|
||||
)
|
||||
|
||||
self.assertTrue(result.success)
|
||||
@@ -281,6 +287,23 @@ class GenericSystemXmlSimulationTests(unittest.TestCase):
|
||||
)
|
||||
self.assertIn("integrating", {phase for _, phase in progress})
|
||||
self.assertIn("postprocessing", {phase for _, phase in progress})
|
||||
activity = activity_tracker.snapshot()
|
||||
self.assertEqual(activity.activity_kind, "complete")
|
||||
self.assertGreater(activity.rhs_call_count, 0)
|
||||
self.assertGreater(activity.accepted_step_sequence, 0)
|
||||
self.assertEqual(activity.accepted_time, 0.01)
|
||||
self.assertGreater(activity.thermofluid_closure_count, 0)
|
||||
self.assertEqual(
|
||||
result.diagnostics["activity"],
|
||||
activity.as_dict(),
|
||||
)
|
||||
self.assertIsNone(system._activity_tracker)
|
||||
self.assertEqual(
|
||||
result.diagnostics["integration"][
|
||||
"mechanicalAbsoluteTolerance"
|
||||
]["mode"],
|
||||
"legacy",
|
||||
)
|
||||
self.assertLess(result.series["cylinder_1.p"][-1], 500000.0)
|
||||
self.assertGreater(result.series["tank_1.p"][-1], 100000.0)
|
||||
total_mass = [
|
||||
@@ -315,6 +338,33 @@ class GenericSystemXmlSimulationTests(unittest.TestCase):
|
||||
pressure_flow["optimizerEvaluationCount"],
|
||||
)
|
||||
|
||||
def test_activity_tracker_scope_is_restored_after_simulation_error(self) -> None:
|
||||
system = GenericFluidSystem(compile_reactflow_network(chain_project()))
|
||||
previous_tracker = SolverActivityTracker()
|
||||
active_tracker = SolverActivityTracker()
|
||||
system._activity_tracker = previous_tracker
|
||||
|
||||
with (
|
||||
patch.object(
|
||||
system,
|
||||
"_simulate",
|
||||
side_effect=RuntimeError("synthetic simulation failure"),
|
||||
),
|
||||
self.assertRaisesRegex(RuntimeError, "synthetic simulation failure"),
|
||||
):
|
||||
system.simulate(
|
||||
SolveIVPConfig(
|
||||
t_start=0.0,
|
||||
t_stop=0.01,
|
||||
method="BDF",
|
||||
max_step=0.001,
|
||||
),
|
||||
sample_step=0.005,
|
||||
activity_tracker=active_tracker,
|
||||
)
|
||||
|
||||
self.assertIs(system._activity_tracker, previous_tracker)
|
||||
|
||||
def test_cancelled_simulation_returns_accepted_partial_samples(self) -> None:
|
||||
cancel_event = threading.Event()
|
||||
|
||||
@@ -569,10 +619,24 @@ class GenericSystemXmlSimulationTests(unittest.TestCase):
|
||||
self.assertEqual(snapshot["result"]["status"], "stopped")
|
||||
|
||||
def test_streaming_endpoint_keeps_quiet_solver_connection_alive(self) -> None:
|
||||
def delayed_simulation(_xml_bytes, progress_callback, _cancel_check=None):
|
||||
def delayed_simulation(
|
||||
_xml_bytes,
|
||||
progress_callback,
|
||||
_cancel_check=None,
|
||||
activity_tracker=None,
|
||||
):
|
||||
progress_callback(49, "integrating", "正在进行时间积分与压力流量求解")
|
||||
time.sleep(0.03)
|
||||
return {"success": True}
|
||||
self.assertIsNotNone(activity_tracker)
|
||||
activity_tracker.start_integration(0.0)
|
||||
for trial_time in (0.0487, 0.0488, 0.0489, 0.0490):
|
||||
activity_tracker.record_rhs(trial_time)
|
||||
time.sleep(0.008)
|
||||
return {
|
||||
"success": True,
|
||||
"status": "completed",
|
||||
"simulatedUntil": 0.2,
|
||||
"requestedStopTime": 0.2,
|
||||
}
|
||||
|
||||
with (
|
||||
patch("app.main.SIMULATION_STREAM_HEARTBEAT_SECONDS", 0.005),
|
||||
@@ -590,7 +654,21 @@ class GenericSystemXmlSimulationTests(unittest.TestCase):
|
||||
self.assertGreaterEqual(len(heartbeats), 1)
|
||||
self.assertTrue(all(event["progress"] == 49 for event in heartbeats))
|
||||
self.assertTrue(all(event["phase"] == "integrating" for event in heartbeats))
|
||||
self.assertTrue(
|
||||
all(event["acceptedStepSequence"] == 0 for event in heartbeats)
|
||||
)
|
||||
self.assertTrue(all(event["acceptedTime"] == 0.0 for event in heartbeats))
|
||||
self.assertGreater(
|
||||
len({event["activitySequence"] for event in heartbeats}),
|
||||
1,
|
||||
)
|
||||
self.assertTrue(
|
||||
all(event["activityKind"] == "rhs" for event in heartbeats)
|
||||
)
|
||||
self.assertEqual(events[-1]["event"], "result")
|
||||
self.assertEqual(events[-1]["activityKind"], "complete")
|
||||
self.assertEqual(events[-1]["acceptedStepSequence"], 0)
|
||||
self.assertEqual(events[-1]["rhsCallCount"], 4)
|
||||
|
||||
def test_streaming_endpoint_returns_structured_validation_error(self) -> None:
|
||||
events = [
|
||||
|
||||
Reference in new issue
Block a user