完善通用求解器回归与前端交互
- 引入因果坐标内核、热流体恢复和递进长时回归\n- 完善正交连线、线桥、视图保持与结果曲线缩放\n- 补充依赖约束、CI、测试基线和北京时间更新日志
This commit is contained in:
1 parent
143e8dd309
commit
b435daecf2
65 files changed
+172271
-701
No files matched your search
@@ -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
|
||||
|
||||
Reference in new issue
Block a user