完善通用求解器回归与前端交互
- 引入因果坐标内核、热流体恢复和递进长时回归\n- 完善正交连线、线桥、视图保持与结果曲线缩放\n- 补充依赖约束、CI、测试基线和北京时间更新日志
This commit is contained in:
1 parent
143e8dd309
commit
b435daecf2
65 files changed
+172271
-701
No files matched your search
@@ -31,6 +31,10 @@ PRESSURE_LOWER_BOUND_PA = 0.0
|
||||
|
||||
|
||||
CAUSAL_FAST_PATH_ENVIRONMENT_VARIABLE = "SIMULATION_CAUSAL_FAST_PATH"
|
||||
CAUSAL_EXECUTOR_V2_ENVIRONMENT_VARIABLE = "SIMULATION_CAUSAL_EXECUTOR_V2"
|
||||
CAUSAL_COORDINATE_KERNEL_ENVIRONMENT_VARIABLE = (
|
||||
"SIMULATION_CAUSAL_COORDINATE_KERNEL"
|
||||
)
|
||||
CAUSAL_FAST_PATH_AUDIT_INTERVAL = 64
|
||||
|
||||
|
||||
@@ -39,6 +43,20 @@ def _causal_fast_path_environment_enabled() -> bool:
|
||||
return value.strip().lower() not in {"0", "false", "no", "off"}
|
||||
|
||||
|
||||
def _causal_executor_v2_environment_enabled() -> bool:
|
||||
"""Return whether the allocation-light causal executor is enabled."""
|
||||
|
||||
value = os.getenv(CAUSAL_EXECUTOR_V2_ENVIRONMENT_VARIABLE, "1")
|
||||
return value.strip().lower() not in {"0", "false", "no", "off"}
|
||||
|
||||
|
||||
def _causal_coordinate_kernel_environment_enabled() -> bool:
|
||||
"""Return whether the canonical-coordinate causal kernel is enabled."""
|
||||
|
||||
value = os.getenv(CAUSAL_COORDINATE_KERNEL_ENVIRONMENT_VARIABLE, "1")
|
||||
return value.strip().lower() not in {"0", "false", "no", "off"}
|
||||
|
||||
|
||||
class AlgebraicSolveError(RuntimeError):
|
||||
def __init__(
|
||||
self,
|
||||
@@ -120,6 +138,42 @@ class CausalEffortAssignment:
|
||||
anchor: EffortAnchor
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CausalEffortKernelTarget:
|
||||
"""One canonical effort coordinate extracted from a residual evaluator."""
|
||||
|
||||
coordinate_index: int
|
||||
assignment: CausalEffortAssignment
|
||||
equation_index: int
|
||||
equation_id: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CausalEffortKernelEvaluation:
|
||||
"""One component call shared by every state anchor that it owns."""
|
||||
|
||||
evaluate: Callable[[], tuple[float, ...]]
|
||||
targets: tuple[CausalEffortKernelTarget, ...]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CausalEffortKernelStage:
|
||||
"""Precompiled canonical coordinates and compatibility broadcasts."""
|
||||
|
||||
variable: str
|
||||
assignments: tuple[tuple[int, CausalEffortAssignment], ...]
|
||||
direct_targets: tuple[tuple[int, CausalEffortAssignment], ...]
|
||||
component_evaluations: tuple[CausalEffortKernelEvaluation, ...]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CausalFlowKernelStage:
|
||||
"""Map one existing dependency stage into the canonical workspace."""
|
||||
|
||||
stage: ExplicitFlowStage
|
||||
coordinate_indices: tuple[int, ...]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ConnectionEquationEvaluation:
|
||||
template: EquationResidual
|
||||
@@ -366,6 +420,56 @@ class PressureFlowSolver:
|
||||
self._causal_fast_path_environment_enabled = (
|
||||
_causal_fast_path_environment_enabled()
|
||||
)
|
||||
self._causal_executor_v2_environment_enabled = (
|
||||
_causal_executor_v2_environment_enabled()
|
||||
)
|
||||
self._causal_coordinate_kernel_environment_enabled = (
|
||||
_causal_coordinate_kernel_environment_enabled()
|
||||
)
|
||||
self._causal_compiled_effort_unknown_count = sum(
|
||||
len(assignment.members)
|
||||
for assignments in self._causal_effort_plan_by_variable.values()
|
||||
for assignment in assignments
|
||||
)
|
||||
self._causal_compiled_flow_assignment_count = sum(
|
||||
len(stage.assignments) for stage in self._explicit_flow_plan
|
||||
)
|
||||
self._causal_external_effort_unknowns = tuple(
|
||||
member
|
||||
for variable in ("x", "v")
|
||||
for assignment in self._causal_effort_plan_by_variable.get(
|
||||
variable, ()
|
||||
)
|
||||
for member in assignment.members
|
||||
)
|
||||
self._causal_external_x_states = tuple(
|
||||
unknown.state
|
||||
for unknown in self._causal_external_effort_unknowns
|
||||
if unknown.variable == "x"
|
||||
)
|
||||
self._causal_external_v_states = tuple(
|
||||
unknown.state
|
||||
for unknown in self._causal_external_effort_unknowns
|
||||
if unknown.variable == "v"
|
||||
)
|
||||
(
|
||||
self._causal_effort_kernel_by_variable,
|
||||
self._causal_flow_kernel_plan,
|
||||
self._causal_coordinate_values,
|
||||
) = self._compile_causal_coordinate_kernel()
|
||||
self._causal_logical_effort_coordinate_count = sum(
|
||||
len(stage.assignments)
|
||||
for stage in self._causal_effort_kernel_by_variable.values()
|
||||
)
|
||||
self._causal_eliminated_effort_alias_count = max(
|
||||
self._causal_compiled_effort_unknown_count
|
||||
- self._causal_logical_effort_coordinate_count,
|
||||
0,
|
||||
)
|
||||
self._causal_compatibility_scatter_count = (
|
||||
self._causal_compiled_effort_unknown_count
|
||||
+ self._causal_compiled_flow_assignment_count
|
||||
)
|
||||
self._causal_runtime_disabled_reason: str | None = None
|
||||
self._causal_audit_interval = CAUSAL_FAST_PATH_AUDIT_INTERVAL
|
||||
self._causal_audit_required = True
|
||||
@@ -374,7 +478,11 @@ class PressureFlowSolver:
|
||||
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
|
||||
self.last_diagnostics: AlgebraicSolveDiagnostics | None = None
|
||||
|
||||
@property
|
||||
@@ -435,6 +543,25 @@ class PressureFlowSolver:
|
||||
and self._causal_runtime_disabled_reason is None
|
||||
)
|
||||
|
||||
@property
|
||||
def causal_executor_v2_enabled(self) -> bool:
|
||||
"""Whether this run may use the v2 executor (environment opt-out)."""
|
||||
|
||||
return (
|
||||
self._causal_executor_v2_environment_enabled
|
||||
and self.causal_fast_path_enabled
|
||||
)
|
||||
|
||||
@property
|
||||
def causal_coordinate_kernel_enabled(self) -> bool:
|
||||
"""Whether the canonical-coordinate executor may run now."""
|
||||
|
||||
return (
|
||||
self._causal_coordinate_kernel_environment_enabled
|
||||
and self.causal_executor_v2_enabled
|
||||
and bool(self._causal_coordinate_values)
|
||||
)
|
||||
|
||||
def causal_execution_diagnostics(self) -> dict[str, object]:
|
||||
disabled_reason = self._causal_runtime_disabled_reason
|
||||
if not self._causal_fast_path_environment_enabled:
|
||||
@@ -458,6 +585,39 @@ class PressureFlowSolver:
|
||||
if last_verified is not None
|
||||
else None
|
||||
),
|
||||
"executorV2Configured": self._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._causal_coordinate_kernel_environment_enabled
|
||||
),
|
||||
"coordinateKernelEnabled": self.causal_coordinate_kernel_enabled,
|
||||
"coordinateKernelFastSolveCount": (
|
||||
self._causal_coordinate_fast_solve_count
|
||||
),
|
||||
"compiledEffortUnknownCount": (
|
||||
self._causal_compiled_effort_unknown_count
|
||||
),
|
||||
"compiledFlowAssignmentCount": (
|
||||
self._causal_compiled_flow_assignment_count
|
||||
),
|
||||
"compiledAssignmentCount": (
|
||||
self._causal_compiled_effort_unknown_count
|
||||
+ self._causal_compiled_flow_assignment_count
|
||||
),
|
||||
"logicalEffortCoordinateCount": (
|
||||
self._causal_logical_effort_coordinate_count
|
||||
),
|
||||
"eliminatedEffortAliasCount": (
|
||||
self._causal_eliminated_effort_alias_count
|
||||
),
|
||||
"canonicalCoordinateCount": len(self._causal_coordinate_values),
|
||||
"compatibilityScatterCount": (
|
||||
self._causal_compatibility_scatter_count
|
||||
),
|
||||
}
|
||||
|
||||
def request_causal_audit(self) -> None:
|
||||
@@ -651,10 +811,163 @@ class PressureFlowSolver:
|
||||
None,
|
||||
)
|
||||
|
||||
def _compile_causal_coordinate_kernel(
|
||||
self,
|
||||
) -> tuple[
|
||||
dict[str, CausalEffortKernelStage],
|
||||
tuple[CausalFlowKernelStage, ...],
|
||||
list[float],
|
||||
]:
|
||||
"""Compile independent coordinates without changing public port state.
|
||||
|
||||
``PortState`` remains the compatibility surface consumed by component
|
||||
methods. The workspace stores one value per proven effort equality
|
||||
group and one per explicit flow assignment; compatibility aliases are
|
||||
populated only after every target in an effort stage has been checked.
|
||||
"""
|
||||
|
||||
if not self._causal_fast_path_eligible:
|
||||
return {}, (), []
|
||||
|
||||
equation_index_by_id = {
|
||||
equation.id: index
|
||||
for index, equation in enumerate(self._equation_templates)
|
||||
}
|
||||
effort_stages: dict[str, CausalEffortKernelStage] = {}
|
||||
next_coordinate = 0
|
||||
for variable in ("p", "x", "v"):
|
||||
assignments = self._causal_effort_plan_by_variable.get(variable, ())
|
||||
indexed_assignments = tuple(
|
||||
(next_coordinate + offset, assignment)
|
||||
for offset, assignment in enumerate(assignments)
|
||||
)
|
||||
next_coordinate += len(indexed_assignments)
|
||||
direct_targets: list[tuple[int, CausalEffortAssignment]] = []
|
||||
targets_by_component: dict[
|
||||
int,
|
||||
list[CausalEffortKernelTarget],
|
||||
] = {}
|
||||
component_evaluators: dict[int, Callable[[], tuple[float, ...]]] = {}
|
||||
for coordinate_index, assignment in indexed_assignments:
|
||||
equation_index = equation_index_by_id[
|
||||
assignment.anchor.equation_id
|
||||
]
|
||||
kind, evaluation_plan, source = (
|
||||
self._equation_evaluation_locations[equation_index]
|
||||
)
|
||||
if kind != "component":
|
||||
direct_targets.append((coordinate_index, assignment))
|
||||
continue
|
||||
component_plan = evaluation_plan
|
||||
key = id(component_plan)
|
||||
component_evaluators[key] = component_plan.evaluate
|
||||
targets_by_component.setdefault(key, []).append(
|
||||
CausalEffortKernelTarget(
|
||||
coordinate_index=coordinate_index,
|
||||
assignment=assignment,
|
||||
equation_index=source,
|
||||
equation_id=assignment.anchor.equation_id,
|
||||
)
|
||||
)
|
||||
effort_stages[variable] = CausalEffortKernelStage(
|
||||
variable=variable,
|
||||
assignments=indexed_assignments,
|
||||
direct_targets=tuple(direct_targets),
|
||||
component_evaluations=tuple(
|
||||
CausalEffortKernelEvaluation(
|
||||
evaluate=component_evaluators[key],
|
||||
targets=tuple(targets),
|
||||
)
|
||||
for key, targets in targets_by_component.items()
|
||||
),
|
||||
)
|
||||
|
||||
flow_stages: list[CausalFlowKernelStage] = []
|
||||
for stage in self._explicit_flow_plan:
|
||||
coordinate_indices = tuple(
|
||||
range(next_coordinate, next_coordinate + len(stage.assignments))
|
||||
)
|
||||
next_coordinate += len(stage.assignments)
|
||||
flow_stages.append(
|
||||
CausalFlowKernelStage(
|
||||
stage=stage,
|
||||
coordinate_indices=coordinate_indices,
|
||||
)
|
||||
)
|
||||
return effort_stages, tuple(flow_stages), [0.0] * next_coordinate
|
||||
|
||||
@staticmethod
|
||||
def _read_effort_anchor(assignment: CausalEffortAssignment) -> float:
|
||||
state = assignment.anchor.unknown.state
|
||||
if assignment.variable == "p":
|
||||
return state.p
|
||||
if assignment.variable == "x":
|
||||
return state.x
|
||||
return state.v
|
||||
|
||||
@staticmethod
|
||||
def _scatter_effort_assignment(
|
||||
assignment: CausalEffortAssignment,
|
||||
value: float,
|
||||
) -> None:
|
||||
if assignment.variable == "p":
|
||||
for unknown in assignment.members:
|
||||
unknown.state.p = value
|
||||
return
|
||||
if assignment.variable == "x":
|
||||
for unknown in assignment.members:
|
||||
unknown.state.x = value
|
||||
return
|
||||
for unknown in assignment.members:
|
||||
unknown.state.v = value
|
||||
|
||||
def _execute_causal_coordinate_effort_plan(
|
||||
self,
|
||||
variables: tuple[str, ...],
|
||||
) -> bool:
|
||||
"""Evaluate canonical effort coordinates in component-sized batches."""
|
||||
|
||||
workspace = self._causal_coordinate_values
|
||||
for variable in variables:
|
||||
stage = self._causal_effort_kernel_by_variable.get(variable)
|
||||
if stage is None:
|
||||
return False
|
||||
for coordinate_index, assignment in stage.direct_targets:
|
||||
workspace[coordinate_index] = (
|
||||
self._read_effort_anchor(assignment)
|
||||
- assignment.anchor.evaluate()
|
||||
)
|
||||
for evaluation in stage.component_evaluations:
|
||||
equation_values = evaluation.evaluate()
|
||||
for target in evaluation.targets:
|
||||
if target.equation_index >= len(equation_values):
|
||||
raise RuntimeError(
|
||||
"Compiled algebraic equation disappeared at runtime: "
|
||||
f"{target.equation_id}."
|
||||
)
|
||||
workspace[target.coordinate_index] = (
|
||||
self._read_effort_anchor(target.assignment)
|
||||
- float(equation_values[target.equation_index])
|
||||
)
|
||||
for coordinate_index, assignment in stage.assignments:
|
||||
target = workspace[coordinate_index]
|
||||
if not isfinite(target) or (
|
||||
variable == "p" and target <= PRESSURE_LOWER_BOUND_PA
|
||||
):
|
||||
return False
|
||||
for coordinate_index, assignment in stage.assignments:
|
||||
self._scatter_effort_assignment(
|
||||
assignment,
|
||||
workspace[coordinate_index],
|
||||
)
|
||||
return True
|
||||
|
||||
def _execute_causal_effort_plan(
|
||||
self,
|
||||
variables: tuple[str, ...],
|
||||
) -> bool:
|
||||
if self.causal_coordinate_kernel_enabled:
|
||||
return self._execute_causal_coordinate_effort_plan(variables)
|
||||
for variable in variables:
|
||||
assignments = self._causal_effort_plan_by_variable.get(variable)
|
||||
if assignments is None:
|
||||
@@ -685,15 +998,8 @@ class PressureFlowSolver:
|
||||
self._causal_solves_since_audit = 0
|
||||
self._causal_audit_required = False
|
||||
self._causal_last_verified_diagnostics = diagnostics
|
||||
|
||||
def _causal_fast_diagnostics(
|
||||
self,
|
||||
) -> AlgebraicSolveDiagnostics:
|
||||
verified = self._causal_last_verified_diagnostics
|
||||
if verified is None:
|
||||
raise RuntimeError("Causal execution has no verified residual baseline.")
|
||||
return replace(
|
||||
verified,
|
||||
self._causal_cached_fast_diagnostics = replace(
|
||||
diagnostics,
|
||||
message=(
|
||||
"Compiled causal pressure-flow program completed; residuals "
|
||||
"reuse the latest full audit."
|
||||
@@ -709,6 +1015,14 @@ class PressureFlowSolver:
|
||||
causal_fast_path_used=True,
|
||||
)
|
||||
|
||||
def _causal_fast_diagnostics(
|
||||
self,
|
||||
) -> AlgebraicSolveDiagnostics:
|
||||
cached = self._causal_cached_fast_diagnostics
|
||||
if cached is None:
|
||||
raise RuntimeError("Causal execution has no verified residual baseline.")
|
||||
return cached
|
||||
|
||||
def _build_jacobian_sparsity(self):
|
||||
"""Compile the residual dependency contract into one CSR pattern.
|
||||
|
||||
@@ -1024,6 +1338,10 @@ class PressureFlowSolver:
|
||||
unknown = sorted(set(variables) - set(self._effort_groups))
|
||||
if unknown:
|
||||
raise ValueError("Unsupported effort variables: " + ", ".join(unknown))
|
||||
if self.causal_coordinate_kernel_enabled:
|
||||
if self._execute_causal_coordinate_effort_plan(variables):
|
||||
return
|
||||
self._disable_causal_fast_path("nonFiniteCausalEffortAnchor")
|
||||
for variable in variables:
|
||||
self._seed_equal_effort(variable)
|
||||
|
||||
@@ -1835,6 +2153,121 @@ class PressureFlowSolver:
|
||||
seeded_ids.add(assignment.unknown.id)
|
||||
return seeded_ids
|
||||
|
||||
def _execute_compiled_causal_flow_plan(self) -> str | None:
|
||||
"""Execute the compile-proven full flow plan without coverage sets."""
|
||||
|
||||
if self.causal_coordinate_kernel_enabled:
|
||||
return self._execute_causal_coordinate_flow_plan()
|
||||
|
||||
# Position and velocity are propagated by the mechanical reducer
|
||||
# before the pressure-only causal solve. They are therefore not
|
||||
# rewritten below, but remain part of the compiled algebraic contract.
|
||||
# Validate that small external boundary explicitly instead of restoring
|
||||
# the legacy scan over every pressure/flow/force unknown.
|
||||
if any(
|
||||
not isfinite(unknown.read())
|
||||
for unknown in self._causal_external_effort_unknowns
|
||||
):
|
||||
return "nonFiniteCausalExternalEffort"
|
||||
|
||||
reset_unknowns = self._explicit_flow_unknowns_by_variables[
|
||||
frozenset(("f", "m_flow"))
|
||||
]
|
||||
for unknown in reset_unknowns:
|
||||
unknown.write(0.0)
|
||||
|
||||
for stage in self._explicit_flow_plan:
|
||||
try:
|
||||
values = self._evaluate_explicit_flow_stage(stage)
|
||||
except MemoryError:
|
||||
raise
|
||||
except (ArithmeticError, RuntimeError, ValueError) as exc:
|
||||
return f"causalFlowEvaluationFailed:{type(exc).__name__}"
|
||||
if len(values) != len(stage.assignments):
|
||||
return "causalFlowAssignmentCountMismatch"
|
||||
for assignment, target_value in zip(stage.assignments, values):
|
||||
if not isfinite(target_value):
|
||||
return "nonFiniteCausalFlowAssignment"
|
||||
assignment.unknown.write(target_value)
|
||||
return None
|
||||
|
||||
def _execute_causal_coordinate_flow_plan(self) -> str | None:
|
||||
"""Run flow stages through reusable canonical coordinates."""
|
||||
|
||||
if any(not isfinite(state.x) for state in self._causal_external_x_states):
|
||||
return "nonFiniteCausalExternalEffort"
|
||||
if any(not isfinite(state.v) for state in self._causal_external_v_states):
|
||||
return "nonFiniteCausalExternalEffort"
|
||||
|
||||
return self._execute_causal_coordinate_flow_stages(
|
||||
self._causal_flow_kernel_plan,
|
||||
self._causal_coordinate_values,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _execute_causal_coordinate_flow_stages(
|
||||
kernel_plan: tuple[CausalFlowKernelStage, ...],
|
||||
workspace: list[float],
|
||||
) -> str | None:
|
||||
"""Execute proven flow stages without per-call result containers."""
|
||||
|
||||
# Residual-based explicit assignments use ``-residual`` and therefore
|
||||
# require their target coordinate to be zero. Keep this compatibility
|
||||
# initialization until a component exposes a proven direct target op.
|
||||
for kernel_stage in kernel_plan:
|
||||
for assignment in kernel_stage.stage.assignments:
|
||||
if assignment.unknown.variable == "m_flow":
|
||||
assignment.unknown.state.m_flow = 0.0
|
||||
else:
|
||||
assignment.unknown.state.f = 0.0
|
||||
|
||||
for kernel_stage in kernel_plan:
|
||||
stage = kernel_stage.stage
|
||||
coordinate_indices = kernel_stage.coordinate_indices
|
||||
if len(coordinate_indices) != len(stage.assignments):
|
||||
return "causalFlowAssignmentCountMismatch"
|
||||
try:
|
||||
for assignment_index, evaluate in stage.direct_evaluations:
|
||||
workspace[coordinate_indices[assignment_index]] = float(
|
||||
evaluate()
|
||||
)
|
||||
for evaluation in stage.component_evaluations:
|
||||
equation_values = evaluation.evaluate()
|
||||
for assignment_index, equation_index, equation_id in zip(
|
||||
evaluation.assignment_indices,
|
||||
evaluation.equation_indices,
|
||||
evaluation.equation_ids,
|
||||
):
|
||||
if equation_index >= len(equation_values):
|
||||
raise RuntimeError(
|
||||
"Compiled algebraic equation disappeared at "
|
||||
f"runtime: {equation_id}."
|
||||
)
|
||||
workspace[coordinate_indices[assignment_index]] = (
|
||||
0.0 - float(equation_values[equation_index])
|
||||
)
|
||||
except MemoryError:
|
||||
raise
|
||||
except (ArithmeticError, RuntimeError, ValueError) as exc:
|
||||
return f"causalFlowEvaluationFailed:{type(exc).__name__}"
|
||||
for assignment, coordinate_index in zip(
|
||||
stage.assignments,
|
||||
coordinate_indices,
|
||||
):
|
||||
target_value = workspace[coordinate_index]
|
||||
if not isfinite(target_value):
|
||||
return "nonFiniteCausalFlowAssignment"
|
||||
for assignment, coordinate_index in zip(
|
||||
stage.assignments,
|
||||
coordinate_indices,
|
||||
):
|
||||
target_value = workspace[coordinate_index]
|
||||
if assignment.unknown.variable == "m_flow":
|
||||
assignment.unknown.state.m_flow = target_value
|
||||
else:
|
||||
assignment.unknown.state.f = target_value
|
||||
return None
|
||||
|
||||
def _build_closed_resistance_pressure_plan(
|
||||
self,
|
||||
) -> tuple[ClosedResistancePressureBinding, ...]:
|
||||
@@ -2088,6 +2521,9 @@ class PressureFlowSolver:
|
||||
causal_audit_due = (
|
||||
self._causal_audit_is_due() if causal_candidate else False
|
||||
)
|
||||
causal_v2_candidate = (
|
||||
causal_candidate and self._causal_executor_v2_environment_enabled
|
||||
)
|
||||
for component in self._causal_contact_components:
|
||||
component.clear_causal_contact()
|
||||
|
||||
@@ -2100,29 +2536,55 @@ class PressureFlowSolver:
|
||||
self._disable_causal_fast_path("nonFiniteCausalEffortAnchor")
|
||||
causal_candidate = False
|
||||
causal_audit_due = False
|
||||
causal_v2_candidate = False
|
||||
self._seed_equal_efforts(effort_variables)
|
||||
else:
|
||||
self._seed_equal_efforts(effort_variables)
|
||||
self._seed_closed_resistance_pressures()
|
||||
self._seed_resistance_pnl0001_series_pressures()
|
||||
seeded_flow_ids = self._solve_explicit_flow_unknowns()
|
||||
contact_bindings = self._seed_unilateral_contacts()
|
||||
if contact_bindings:
|
||||
seeded_flow_ids.update(self._solve_explicit_flow_unknowns(("f",)))
|
||||
self._refresh_unilateral_contacts(contact_bindings)
|
||||
|
||||
seeded_flow_ids: set[str] | None = None
|
||||
contact_bindings: tuple[UnilateralContactBinding, ...] = ()
|
||||
if causal_v2_candidate:
|
||||
v2_failure_reason = self._execute_compiled_causal_flow_plan()
|
||||
if v2_failure_reason is not None:
|
||||
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
|
||||
causal_v2_candidate = False
|
||||
# Rebuild the ordinary seed from scratch in the same solve.
|
||||
# A partial compiled stage must never influence fallback.
|
||||
self._seed_equal_efforts(effort_variables)
|
||||
if not causal_v2_candidate:
|
||||
self._seed_closed_resistance_pressures()
|
||||
self._seed_resistance_pnl0001_series_pressures()
|
||||
seeded_flow_ids = self._solve_explicit_flow_unknowns()
|
||||
contact_bindings = self._seed_unilateral_contacts()
|
||||
if contact_bindings:
|
||||
seeded_flow_ids.update(
|
||||
self._solve_explicit_flow_unknowns(("f",))
|
||||
)
|
||||
self._refresh_unilateral_contacts(contact_bindings)
|
||||
if causal_candidate:
|
||||
causal_unknowns_are_feasible = (
|
||||
not contact_bindings
|
||||
and seeded_flow_ids == self._causal_flow_unknown_ids
|
||||
and all(
|
||||
isfinite(unknown.read())
|
||||
and (
|
||||
unknown.variable != "p"
|
||||
or unknown.read() > PRESSURE_LOWER_BOUND_PA
|
||||
if causal_v2_candidate:
|
||||
# Compilation proves a disjoint, complete effort/flow
|
||||
# partition. The v2 executors validate each produced value,
|
||||
# so no coverage set or full unknown scan is needed here.
|
||||
causal_unknowns_are_feasible = True
|
||||
else:
|
||||
assert seeded_flow_ids is not None
|
||||
causal_unknowns_are_feasible = (
|
||||
not contact_bindings
|
||||
and seeded_flow_ids == self._causal_flow_unknown_ids
|
||||
and all(
|
||||
isfinite(unknown.read())
|
||||
and (
|
||||
unknown.variable != "p"
|
||||
or unknown.read() > PRESSURE_LOWER_BOUND_PA
|
||||
)
|
||||
for unknown in self.unknowns
|
||||
)
|
||||
for unknown in self.unknowns
|
||||
)
|
||||
)
|
||||
if not causal_unknowns_are_feasible:
|
||||
self._causal_legacy_fallback_count += 1
|
||||
self._disable_causal_fast_path("causalRuntimeGateFailed")
|
||||
@@ -2131,6 +2593,10 @@ class PressureFlowSolver:
|
||||
elif not causal_audit_due:
|
||||
diagnostics = self._causal_fast_diagnostics()
|
||||
self._causal_fast_solve_count += 1
|
||||
if causal_v2_candidate:
|
||||
self._causal_v2_fast_solve_count += 1
|
||||
if self.causal_coordinate_kernel_enabled:
|
||||
self._causal_coordinate_fast_solve_count += 1
|
||||
self._causal_solves_since_audit += 1
|
||||
self.last_diagnostics = diagnostics
|
||||
return diagnostics
|
||||
|
||||
Reference in new issue
Block a user