完善通用求解器回归与前端交互
- 引入因果坐标内核、热流体恢复和递进长时回归\n- 完善正交连线、线桥、视图保持与结果曲线缩放\n- 补充依赖约束、CI、测试基线和北京时间更新日志
This commit is contained in:
1 parent
143e8dd309
commit
b435daecf2
65 files changed
+172271
-701
No files matched your search
@@ -9,6 +9,7 @@ from app.simulation.solvers.algebraic import (
|
||||
PRESSURE_LOWER_BOUND_PA,
|
||||
AlgebraicSolveDiagnostics,
|
||||
AlgebraicUnknown,
|
||||
CausalFlowKernelStage,
|
||||
ExplicitFlowStage,
|
||||
PressureFlowSolver,
|
||||
)
|
||||
@@ -237,6 +238,27 @@ class StreamPressureBlockSolver:
|
||||
)
|
||||
for stage in pressure_flow_solver._explicit_flow_plan
|
||||
)
|
||||
secondary_coordinate = 0
|
||||
secondary_kernel_plan: list[CausalFlowKernelStage] = []
|
||||
for stage in self._selected_explicit_flow_plan:
|
||||
coordinate_indices = tuple(
|
||||
range(
|
||||
secondary_coordinate,
|
||||
secondary_coordinate + len(stage.assignments),
|
||||
)
|
||||
)
|
||||
secondary_coordinate += len(stage.assignments)
|
||||
secondary_kernel_plan.append(
|
||||
CausalFlowKernelStage(
|
||||
stage=stage,
|
||||
coordinate_indices=coordinate_indices,
|
||||
)
|
||||
)
|
||||
self._causal_secondary_flow_kernel_plan = tuple(secondary_kernel_plan)
|
||||
self._causal_secondary_coordinate_values = [0.0] * secondary_coordinate
|
||||
self._causal_v2_entry_values = [0.0] * len(
|
||||
self._selected_flow_unknowns
|
||||
)
|
||||
self._selected_equation_evaluation = (
|
||||
self._compile_selected_equation_evaluation()
|
||||
if self.blocks
|
||||
@@ -259,7 +281,11 @@ class StreamPressureBlockSolver:
|
||||
self._causal_full_residual_audit_count = 0
|
||||
self._causal_audit_failure_count = 0
|
||||
self._causal_legacy_fallback_count = 0
|
||||
self._causal_v2_fast_solve_count = 0
|
||||
self._causal_v2_runtime_validation_failure_count = 0
|
||||
self._causal_coordinate_fast_solve_count = 0
|
||||
self._causal_last_verified_diagnostics: AlgebraicSolveDiagnostics | None = None
|
||||
self._causal_cached_fast_diagnostics: AlgebraicSolveDiagnostics | None = None
|
||||
|
||||
@property
|
||||
def available(self) -> bool:
|
||||
@@ -273,6 +299,13 @@ class StreamPressureBlockSolver:
|
||||
and self.pressure_flow_solver.causal_fast_path_enabled
|
||||
)
|
||||
|
||||
@property
|
||||
def causal_executor_v2_enabled(self) -> bool:
|
||||
return (
|
||||
self.causal_fast_path_enabled
|
||||
and self.pressure_flow_solver._causal_executor_v2_environment_enabled
|
||||
)
|
||||
|
||||
def request_causal_audit(self) -> None:
|
||||
self._causal_audit_required = True
|
||||
|
||||
@@ -280,7 +313,9 @@ class StreamPressureBlockSolver:
|
||||
parent = self.pressure_flow_solver.causal_execution_diagnostics()
|
||||
disabled_reason = self._causal_runtime_disabled_reason
|
||||
if not bool(parent["enabled"]):
|
||||
disabled_reason = str(parent["disabledReason"] or "parentCausalPathDisabled")
|
||||
disabled_reason = str(
|
||||
parent["disabledReason"] or "parentCausalPathDisabled"
|
||||
)
|
||||
elif not self._causal_fast_path_eligible:
|
||||
disabled_reason = self._causal_fast_path_fallback_reason
|
||||
verified = self._causal_last_verified_diagnostics
|
||||
@@ -298,6 +333,34 @@ class StreamPressureBlockSolver:
|
||||
"lastVerifiedMaxScaledResidual": (
|
||||
verified.max_scaled_residual if verified is not None else None
|
||||
),
|
||||
"executorV2Configured": (
|
||||
self.pressure_flow_solver._causal_executor_v2_environment_enabled
|
||||
),
|
||||
"executorV2Enabled": self.causal_executor_v2_enabled,
|
||||
"executorV2FastSolveCount": self._causal_v2_fast_solve_count,
|
||||
"executorV2RuntimeValidationFailureCount": (
|
||||
self._causal_v2_runtime_validation_failure_count
|
||||
),
|
||||
"coordinateKernelConfigured": (
|
||||
self.pressure_flow_solver._causal_coordinate_kernel_environment_enabled
|
||||
),
|
||||
"coordinateKernelEnabled": (
|
||||
self.causal_executor_v2_enabled
|
||||
and self.pressure_flow_solver.causal_coordinate_kernel_enabled
|
||||
),
|
||||
"coordinateKernelFastSolveCount": (
|
||||
self._causal_coordinate_fast_solve_count
|
||||
),
|
||||
"compiledEffortUnknownCount": len(
|
||||
self._causal_effort_entry_positions
|
||||
),
|
||||
"compiledFlowAssignmentCount": len(
|
||||
self._causal_expected_flow_equation_ids
|
||||
),
|
||||
"compiledAssignmentCount": (
|
||||
len(self._causal_effort_entry_positions)
|
||||
+ len(self._causal_expected_flow_equation_ids)
|
||||
),
|
||||
}
|
||||
|
||||
def _disable_causal_fast_path(self, reason: str) -> None:
|
||||
@@ -405,6 +468,28 @@ class StreamPressureBlockSolver:
|
||||
self._causal_solves_since_audit = 0
|
||||
self._causal_audit_required = False
|
||||
self._causal_last_verified_diagnostics = diagnostics
|
||||
self._causal_cached_fast_diagnostics = replace(
|
||||
diagnostics,
|
||||
message=(
|
||||
"Compiled causal stream-pressure block completed; residuals "
|
||||
"reuse the latest full audit."
|
||||
),
|
||||
evaluations=0,
|
||||
residual_evaluations=0,
|
||||
dense_fallback_used=False,
|
||||
nonlinear_block_count=0,
|
||||
nonlinear_block_unknown_count=0,
|
||||
block_fallback_used=False,
|
||||
block_fallback_reason=None,
|
||||
residual_verified_this_solve=False,
|
||||
causal_fast_path_used=True,
|
||||
)
|
||||
|
||||
def _causal_v2_fast_diagnostics(self) -> AlgebraicSolveDiagnostics:
|
||||
cached = self._causal_cached_fast_diagnostics
|
||||
if cached is None:
|
||||
raise RuntimeError("Stream causal execution has no residual audit.")
|
||||
return cached
|
||||
|
||||
def _causal_fast_diagnostics(
|
||||
self,
|
||||
@@ -788,6 +873,45 @@ class StreamPressureBlockSolver:
|
||||
unknown.write(entry_values[position])
|
||||
return frozenset(seeded_equation_ids)
|
||||
|
||||
def _execute_compiled_secondary_flow_plan(self) -> str | None:
|
||||
"""Execute selected flow assignments without equation-id sets."""
|
||||
|
||||
if self.pressure_flow_solver.causal_coordinate_kernel_enabled:
|
||||
failure = (
|
||||
self.pressure_flow_solver._execute_causal_coordinate_flow_stages(
|
||||
self._causal_secondary_flow_kernel_plan,
|
||||
self._causal_secondary_coordinate_values,
|
||||
)
|
||||
)
|
||||
if failure == "causalFlowAssignmentCountMismatch":
|
||||
return "causalSecondaryFlowAssignmentCountMismatch"
|
||||
if failure == "nonFiniteCausalFlowAssignment":
|
||||
return "nonFiniteCausalSecondaryFlowAssignment"
|
||||
if failure and failure.startswith("causalFlowEvaluationFailed:"):
|
||||
return "causalSecondaryFlowEvaluationFailed:" + failure.rsplit(
|
||||
":", 1
|
||||
)[-1]
|
||||
return failure
|
||||
|
||||
for unknown in self._selected_flow_unknowns:
|
||||
unknown.write(0.0)
|
||||
for stage in self._selected_explicit_flow_plan:
|
||||
try:
|
||||
values = self.pressure_flow_solver._evaluate_explicit_flow_stage(
|
||||
stage
|
||||
)
|
||||
except MemoryError:
|
||||
raise
|
||||
except (ArithmeticError, RuntimeError, ValueError) as exc:
|
||||
return f"causalSecondaryFlowEvaluationFailed:{type(exc).__name__}"
|
||||
if len(values) != len(stage.assignments):
|
||||
return "causalSecondaryFlowAssignmentCountMismatch"
|
||||
for assignment, target_value in zip(stage.assignments, values):
|
||||
if not isfinite(target_value):
|
||||
return "nonFiniteCausalSecondaryFlowAssignment"
|
||||
assignment.unknown.write(target_value)
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
def _equation_scales_from_specs(
|
||||
specs: tuple[_EquationScaleSpec, ...],
|
||||
@@ -1083,11 +1207,62 @@ class StreamPressureBlockSolver:
|
||||
scale_context: Mapping[str, float] | None = None,
|
||||
) -> StreamBlockSolveResult:
|
||||
solver = self.pressure_flow_solver
|
||||
context = dict(scale_context or solver.scale_context())
|
||||
causal_candidate = self.causal_fast_path_enabled
|
||||
causal_audit_due = (
|
||||
self._causal_audit_is_due() if causal_candidate else False
|
||||
)
|
||||
causal_v2_candidate = (
|
||||
causal_candidate
|
||||
and solver._causal_executor_v2_environment_enabled
|
||||
and not causal_audit_due
|
||||
)
|
||||
if causal_v2_candidate:
|
||||
# The secondary causal proof rejects every special pressure seed,
|
||||
# so this executor mutates selected flow coordinates only. Keep
|
||||
# the minimal transactional snapshot for the rare fallback path.
|
||||
v2_entry_values = self._causal_v2_entry_values
|
||||
for position, unknown in enumerate(self._selected_flow_unknowns):
|
||||
v2_entry_values[position] = unknown.state.m_flow
|
||||
|
||||
def restore_v2_entry_mutations() -> None:
|
||||
for unknown, value in zip(
|
||||
self._selected_flow_unknowns,
|
||||
v2_entry_values,
|
||||
):
|
||||
unknown.state.m_flow = value
|
||||
|
||||
try:
|
||||
v2_failure_reason = (
|
||||
self._execute_compiled_secondary_flow_plan()
|
||||
)
|
||||
except BaseException:
|
||||
restore_v2_entry_mutations()
|
||||
raise
|
||||
if v2_failure_reason is None:
|
||||
diagnostics = self._causal_v2_fast_diagnostics()
|
||||
self._causal_fast_solve_count += 1
|
||||
self._causal_v2_fast_solve_count += 1
|
||||
if solver.causal_coordinate_kernel_enabled:
|
||||
self._causal_coordinate_fast_solve_count += 1
|
||||
self._causal_solves_since_audit += 1
|
||||
selected = self._selected_equation_evaluation
|
||||
assert selected is not None
|
||||
return StreamBlockSolveResult(
|
||||
diagnostics=(diagnostics,),
|
||||
scopes=(selected.scope_components,),
|
||||
used_global_fallback=False,
|
||||
)
|
||||
restore_v2_entry_mutations()
|
||||
self._causal_v2_runtime_validation_failure_count += 1
|
||||
self._causal_legacy_fallback_count += 1
|
||||
self._disable_causal_fast_path(v2_failure_reason)
|
||||
causal_candidate = False
|
||||
causal_audit_due = False
|
||||
|
||||
# Keep scale construction and the full mutation snapshot off the v2
|
||||
# success path. Callers may still precompute a shared scale mapping;
|
||||
# avoiding that producer requires a later Generic-system API change.
|
||||
context = dict(scale_context or solver.scale_context())
|
||||
entry_values = tuple(
|
||||
unknown.read() for unknown in self._entry_mutated_unknowns
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user