整合求解器活动监控与步长回归证据

同步远端 PNL0003 诊断和大采样网格能力,语义合并活动感知的 60 秒真停滞判定与旧后端 15 分钟兼容兜底。

纳管热路径优化、15 单元运行证据、浏览器与 API 报告,并补充北京时间更新日志和遗留问题。
This commit is contained in:
lujingze committed 2026-08-19 16:24:31 +00:00
1 parent c19cf77aee
commit e18399c022
46 files changed
+181589 -170

No files matched your search

+78 -6
View File
@@ -31,6 +31,7 @@ from app.simulation.solvers.solver import (
IntegrationCancelled,
ODESolution,
SolveIVPConfig,
SolverActivityTracker,
integrate_ode,
)
from app.simulation.solvers.signal import SignalResolver
@@ -453,6 +454,7 @@ class GenericFluidSystem:
self.pneumatic_volume_propagation_count = 0
self._jacobian_sparsity = None
self._ode_tangent_provider: ThreePistonTangentProvider | None = None
self._activity_tracker: SolverActivityTracker | None = None
def _request_causal_residual_audit(self) -> None:
"""Make topology or mode boundaries verify the next causal closure."""
@@ -957,6 +959,8 @@ class GenericFluidSystem:
*,
record_rhs_outcome: bool = False,
) -> dict[str, dict[str, float]]:
if self._activity_tracker is not None:
self._activity_tracker.record_thermofluid_closure(time)
transaction = self._thermofluid_transaction_plan.capture()
last_algebraic_diagnostics = self._last_algebraic_diagnostics
last_algebraic_scope = self._last_algebraic_scope
@@ -1156,6 +1160,8 @@ class GenericFluidSystem:
self,
connected_h: dict[str, dict[str, float]],
) -> list[float]:
if self._activity_tracker is not None:
self._activity_tracker.record_phase("state_derivatives")
return self.pneumatic_storage_reducer.coupled_derivatives(
self.mechanical_state_reducer.state_derivatives(connected_h)
)
@@ -1194,7 +1200,32 @@ class GenericFluidSystem:
sample_step: float,
progress_callback: SimulationProgressCallback | None = None,
cancel_check: SimulationCancellationCheck | None = None,
activity_tracker: SolverActivityTracker | None = None,
) -> GenericSimulationResult:
previous_activity_tracker = self._activity_tracker
self._activity_tracker = activity_tracker
try:
return self._simulate(
config,
sample_step=sample_step,
progress_callback=progress_callback,
cancel_check=cancel_check,
activity_tracker=activity_tracker,
)
finally:
self._activity_tracker = previous_activity_tracker
def _simulate(
self,
config: SolveIVPConfig,
*,
sample_step: float,
progress_callback: SimulationProgressCallback | None = None,
cancel_check: SimulationCancellationCheck | None = None,
activity_tracker: SolverActivityTracker | None = None,
) -> GenericSimulationResult:
if activity_tracker is not None:
activity_tracker.record_phase("initializing", config.t_start)
last_reported_progress = -1.0
last_reported_phase = ""
@@ -1223,12 +1254,21 @@ class GenericFluidSystem:
report_progress(0.0, "initializing", force=True)
with performance_span("simulation.sample_initialization"):
integration_config = config
mechanical_tolerance_plan = None
if isinstance(config.atol, (int, float)):
mechanical_tolerance_plan = (
self.mechanical_state_reducer.absolute_tolerance_plan(
float(config.atol),
mode=(
None
if config.method == "BDF"
else "legacy"
),
)
)
integration_config = replace(
config,
atol=self.mechanical_state_reducer.absolute_tolerances(
float(config.atol)
),
atol=list(mechanical_tolerance_plan.values),
)
t_eval = simulation_sample_times(config, sample_step)
signal_event_times = self.signal_resolver.event_times(
@@ -1321,6 +1361,8 @@ class GenericFluidSystem:
def evaluate_jacobian_rhs(time, state):
if cancel_check is not None and cancel_check():
raise IntegrationCancelled
if activity_tracker is not None:
activity_tracker.record_rhs(float(time))
return monitored_rhs(
time,
[float(value) for value in state],
@@ -1372,6 +1414,7 @@ class GenericFluidSystem:
jac_sparsity=jac_sparsity,
jac=jacobian,
recoverable_trial_retries=True,
activity_tracker=activity_tracker,
)
finally:
self._ode_tangent_provider = None
@@ -1388,6 +1431,11 @@ class GenericFluidSystem:
else max(0.0, last_reported_progress)
)
report_progress(postprocess_progress, "postprocessing", force=True)
if activity_tracker is not None:
activity_tracker.record_phase(
"postprocessing",
furthest_solver_time,
)
times = [float(value) for value in solution.t]
if isinstance(solution, ODESolution):
solver_segment_diagnostics = [
@@ -1601,6 +1649,11 @@ class GenericFluidSystem:
self._close_current_state(times[time_index])
self._append_current_state(series)
series["time"].append(times[time_index])
if activity_tracker is not None:
activity_tracker.record_phase(
"postprocessing",
times[time_index],
)
except Exception as exc:
run_status = "failed"
result_message = str(exc)
@@ -1621,6 +1674,14 @@ class GenericFluidSystem:
diagnostics = {
"integration": {
"method": integration_config.method,
"mechanicalAbsoluteTolerance": (
mechanical_tolerance_plan.as_dict()
if mechanical_tolerance_plan is not None
else {
"mode": "callerVector",
"stateCount": len(initial_state),
}
),
"jacobianSparsity": jacobian_diagnostics,
"jacobian": runtime_jacobian_diagnostics,
"segmentCount": len(solver_segment_diagnostics),
@@ -1730,6 +1791,19 @@ class GenericFluidSystem:
for variable in self.network.result_variable_metadata()
if variable.key in series
)
final_simulated_time = (
float(series["time"][-1])
if series["time"]
else float(config.t_start)
)
if activity_tracker is not None:
activity_tracker.record_phase(
"complete" if run_status == "completed" else run_status,
final_simulated_time,
)
diagnostics["activity"] = (
activity_tracker.snapshot().as_dict()
)
report_progress(
1.0 if run_status == "completed" else max(0.0, last_reported_progress),
"complete" if run_status == "completed" else run_status,
@@ -1740,9 +1814,7 @@ class GenericFluidSystem:
status=run_status,
message=result_message,
simulated_until=(
float(series["time"][-1])
if series["time"]
else float(config.t_start)
final_simulated_time
),
requested_stop_time=float(config.t_stop),
variables=variables,