完善通用求解器回归与前端交互

- 引入因果坐标内核、热流体恢复和递进长时回归\n- 完善正交连线、线桥、视图保持与结果曲线缩放\n- 补充依赖约束、CI、测试基线和北京时间更新日志
This commit is contained in:
lujingze committed 2026-08-18 06:42:07 +00:00
1 parent 143e8dd309
commit b435daecf2
65 files changed
+172271 -701

No files matched your search

+87 -30
View File
@@ -37,7 +37,14 @@ from app.simulation.solvers.stream import StreamResolver
from app.simulation.solvers.tangent import (
ThreePistonTangentCompilation,
ThreePistonTangentProvider,
compile_three_piston_tangent_provider,
compile_supported_piston_tangent_provider,
)
from app.simulation.solvers.thermofluid import (
ThermofluidClosureDiagnostics,
ThermofluidClosureError,
ThermofluidClosureFailure,
ThermofluidClosureSuccess,
ThermofluidTransactionPlan,
)
from app.simulation.systems.network import Endpoint, SimulationNetwork
@@ -114,10 +121,6 @@ class SimulationPreparationError(ValueError):
self.issues = issues
class ThermofluidClosureError(RuntimeError):
"""Raised when stream enthalpy and pressure-flow do not reach one fixed point."""
class SimulationSampleTimeError(ValueError):
"""Stable failure contract for an unsafe or unrepresentable sample grid."""
@@ -428,6 +431,17 @@ class GenericFluidSystem:
self.signal_resolver = SignalResolver(network)
self.stream_resolver = StreamResolver(network)
self._thermofluid_closure_plan = self._build_thermofluid_closure_plan()
self._thermofluid_transaction_plan = ThermofluidTransactionPlan.compile(
network,
diagnostic_owners=(
self.signal_resolver,
self.pneumatic_volume_resolver,
self.stream_resolver,
self.pressure_flow_solver,
*self._thermofluid_closure_plan.secondary_pressure_solvers,
),
)
self._thermofluid_closure_diagnostics = ThermofluidClosureDiagnostics()
self.algebraic_solve_count = 0
self.algebraic_seeded_solve_count = 0
self.algebraic_nonlinear_solve_count = 0
@@ -945,7 +959,45 @@ class GenericFluidSystem:
minimum_mode="audit",
reset_property_shadow=True,
)
def _close_current_state(self, time: float) -> dict[str, dict[str, float]]:
def _close_current_state(
self,
time: float,
*,
record_rhs_outcome: bool = False,
) -> dict[str, dict[str, float]]:
transaction = self._thermofluid_transaction_plan.capture()
last_algebraic_diagnostics = self._last_algebraic_diagnostics
last_algebraic_scope = self._last_algebraic_scope
try:
connected_h, success = self._close_current_state_unchecked(time)
except ThermofluidClosureError as exc:
transaction.restore()
self._last_algebraic_diagnostics = last_algebraic_diagnostics
self._last_algebraic_scope = last_algebraic_scope
self._request_causal_residual_audit()
if record_rhs_outcome:
failure = self._thermofluid_closure_diagnostics.record_failure(
exc.diagnostics
)
raise ThermofluidClosureError(failure) from None
raise
except BaseException:
transaction.restore()
self._last_algebraic_diagnostics = last_algebraic_diagnostics
self._last_algebraic_scope = last_algebraic_scope
self._request_causal_residual_audit()
raise
if record_rhs_outcome:
self._thermofluid_closure_diagnostics.record_success(success)
return connected_h
def _close_current_state_unchecked(
self,
time: float,
) -> tuple[
dict[str, dict[str, float]],
ThermofluidClosureSuccess,
]:
signal = self.signal_resolver.solve(time)
self.signal_propagation_count += signal.propagated
self.pressure_flow_solver.propagate_equal_efforts(("x", "v"))
@@ -971,27 +1023,22 @@ class GenericFluidSystem:
closure_plan = self._thermofluid_closure_plan
self._last_algebraic_diagnostics = initial_algebraic
self._last_algebraic_scope = closure_plan.global_component_group
physical_ports = closure_plan.physical_ports
secondary_pressure_solvers = closure_plan.secondary_pressure_solvers
secondary_block_solvers = closure_plan.secondary_block_solvers
connected_h: dict[str, dict[str, float]] = {}
stream_diagnostics = []
coupling_deltas = []
max_coupling_iterations = 25
flow_relative_tolerance = 1.0e-12
for coupling_iteration in range(1, max_coupling_iterations + 1):
previous_flows = tuple(port.m_flow for port in physical_ports)
previous_flows = self._thermofluid_transaction_plan.flow_values()
stream, connected_h = self.stream_resolver.solve(
dynamic_ports_are_current=True,
)
stream_diagnostics.append(stream)
temperature_reference_h = (
self.stream_resolver.connected_temperature_reference_enthalpies()
)
for component in self.dynamic_components:
component.update_stream_outflows(connected_h[component.name])
component.update_flow_temperature_references(
temperature_reference_h[component.name]
)
self.stream_resolver.refresh_flow_temperature_references()
if secondary_pressure_solvers:
self.thermofluid_pressure_pass_count += 1
block_scale_context = (
@@ -1043,26 +1090,23 @@ class GenericFluidSystem:
self._last_algebraic_diagnostics = algebraic
self._last_algebraic_scope = component_group
pressure_flow_solve_count += 1
current_flows = tuple(port.m_flow for port in physical_ports)
flow_scale = max(
[abs(value) for value in (*previous_flows, *current_flows)] + [1.0]
)
max_flow_delta = max(
(
abs(current - previous)
for previous, current in zip(previous_flows, current_flows)
),
default=0.0,
coupling_delta = self._thermofluid_transaction_plan.measure_flow_delta(
previous_flows,
iteration=coupling_iteration,
relative_tolerance=flow_relative_tolerance,
)
coupling_deltas.append(coupling_delta)
if (
not secondary_pressure_solvers
or max_flow_delta <= flow_relative_tolerance * flow_scale
or coupling_delta.max_delta <= coupling_delta.tolerance
):
break
else:
raise ThermofluidClosureError(
"Stream enthalpy and pressure-flow coupling did not converge "
f"after {max_coupling_iterations} iterations."
ThermofluidClosureFailure.from_iterations(
time,
coupling_deltas,
)
)
self.max_thermofluid_iterations = max(
self.max_thermofluid_iterations,
@@ -1105,7 +1149,10 @@ class GenericFluidSystem:
self.max_stream_iterations,
*(item.iterations for item in stream_diagnostics),
)
return connected_h
return connected_h, ThermofluidClosureSuccess.from_iteration(
time,
coupling_deltas[-1],
)
@profile_phase("simulation.refresh", minimum_mode="audit")
def _refresh_dynamic_components(self) -> None:
@@ -1130,7 +1177,10 @@ class GenericFluidSystem:
@profile_phase("simulation.rhs", minimum_mode="audit")
def rhs(self, _time: float, state_vector: list[float]) -> list[float]:
self.apply_state_vector(state_vector)
connected_h = self._close_current_state(_time)
connected_h = self._close_current_state(
_time,
record_rhs_outcome=True,
)
derivatives = self._state_derivatives(connected_h)
provider = self._ode_tangent_provider
if provider is not None:
@@ -1253,7 +1303,7 @@ class GenericFluidSystem:
exact_columns = None
if requested_jacobian_mode == "semi-analytic":
tangent_compilation = (
compile_three_piston_tangent_provider(self)
compile_supported_piston_tangent_provider(self)
)
if tangent_compilation.eligible:
provider = tangent_compilation.provider
@@ -1329,6 +1379,7 @@ class GenericFluidSystem:
),
jac_sparsity=jac_sparsity,
jac=jacobian,
recoverable_trial_retries=True,
)
finally:
self._ode_tangent_provider = None
@@ -1650,6 +1701,12 @@ class GenericFluidSystem:
"stream": {
"maxIterationsPerSolve": self.max_stream_iterations,
"maxThermofluidIterations": self.max_thermofluid_iterations,
"thermofluidClosure": {
**self._thermofluid_closure_diagnostics.as_dict(),
"transaction": (
self._thermofluid_transaction_plan.diagnostics()
),
},
"last": (
self.stream_resolver.last_diagnostics.as_dict()
if self.stream_resolver.last_diagnostics is not None