完善通用求解器回归与前端交互
- 引入因果坐标内核、热流体恢复和递进长时回归\n- 完善正交连线、线桥、视图保持与结果曲线缩放\n- 补充依赖约束、CI、测试基线和北京时间更新日志
This commit is contained in:
1 parent
143e8dd309
commit
b435daecf2
65 files changed
+172271
-701
No files matched your search
@@ -1,11 +1,13 @@
|
||||
"""Proof-gated tangent columns for the three-piston reference network.
|
||||
"""Proof-gated tangent columns for supported piston branch networks.
|
||||
|
||||
This module is deliberately narrower than the generic algebraic solver. It
|
||||
only compiles a tangent provider after proving the state layout, component
|
||||
types, physical connections, and causal execution plan used by the committed
|
||||
three-piston XML. A failed proof leaves the ordinary seed-0 numerical
|
||||
Jacobian in control; a runtime mode boundary requests the same one-build
|
||||
fallback through :class:`ExactColumnsUnavailable`.
|
||||
types, physical connections, and causal execution plan used by every selected
|
||||
piston branch. The legacy three-piston entry point remains available for its
|
||||
committed fixture, while the topology-driven entry point discovers any number
|
||||
of branches without depending on component names. A failed proof leaves the
|
||||
ordinary seed-0 numerical Jacobian in control; a runtime mode boundary requests
|
||||
the same one-build fallback through :class:`ExactColumnsUnavailable`.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -57,6 +59,7 @@ class ThreePistonBranch:
|
||||
chamber: object
|
||||
pipe: object
|
||||
contact: object
|
||||
chamber_connection_port: str
|
||||
velocity_index: int
|
||||
position_index: int
|
||||
|
||||
@@ -91,7 +94,7 @@ def _failed(reason: str) -> ThreePistonTangentCompilation:
|
||||
|
||||
|
||||
class ThreePistonTangentProvider:
|
||||
"""Batched six-direction provider compiled for one system instance."""
|
||||
"""Batched selected-branch provider compiled for one system instance."""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
@@ -611,10 +614,17 @@ class ThreePistonTangentProvider:
|
||||
return out
|
||||
|
||||
|
||||
def compile_three_piston_tangent_provider(
|
||||
@dataclass(frozen=True)
|
||||
class _PistonBranchSpec:
|
||||
names: tuple[str, str, str, str, str]
|
||||
chamber_connection_port: str
|
||||
|
||||
|
||||
def _compile_named_piston_tangent_provider(
|
||||
system: "GenericFluidSystem",
|
||||
branch_specs: Sequence[_PistonBranchSpec],
|
||||
) -> ThreePistonTangentCompilation:
|
||||
"""Compile the proof-gated target provider, or return a stable reason."""
|
||||
"""Compile a named, topology-proven set of supported piston branches."""
|
||||
|
||||
solver = system.pressure_flow_solver
|
||||
if not solver.causal_fast_path_eligible:
|
||||
@@ -651,7 +661,8 @@ def compile_three_piston_tangent_provider(
|
||||
"amesim_lstp00a",
|
||||
)
|
||||
branches: list[ThreePistonBranch] = []
|
||||
for names in _TARGET_BRANCH_NAMES:
|
||||
for branch_spec in branch_specs:
|
||||
names = branch_spec.names
|
||||
try:
|
||||
components = tuple(system.network.components[name] for name in names)
|
||||
except KeyError:
|
||||
@@ -671,6 +682,9 @@ def compile_three_piston_tangent_provider(
|
||||
chamber=chamber,
|
||||
pipe=pipe,
|
||||
contact=contact,
|
||||
chamber_connection_port=(
|
||||
branch_spec.chamber_connection_port
|
||||
),
|
||||
velocity_index=offset,
|
||||
position_index=offset + 1,
|
||||
)
|
||||
@@ -681,7 +695,15 @@ def compile_three_piston_tangent_provider(
|
||||
required_pairs.update(
|
||||
{
|
||||
frozenset((Endpoint(branch.mass.name, "port_1"), Endpoint(branch.piston.name, "port_2"))),
|
||||
frozenset((Endpoint(branch.piston.name, "port_1"), Endpoint(branch.chamber.name, "port_3"))),
|
||||
frozenset(
|
||||
(
|
||||
Endpoint(branch.piston.name, "port_1"),
|
||||
Endpoint(
|
||||
branch.chamber.name,
|
||||
branch.chamber_connection_port,
|
||||
),
|
||||
)
|
||||
),
|
||||
frozenset((Endpoint(branch.chamber.name, "port_1"), Endpoint(branch.pipe.name, "port_1"))),
|
||||
frozenset((Endpoint(branch.piston.name, "port_5"), Endpoint(branch.contact.name, "port_1"))),
|
||||
}
|
||||
@@ -790,7 +812,7 @@ def compile_three_piston_tangent_provider(
|
||||
# Secondary pressure blocks may contain the same causal flow coordinates,
|
||||
# so membership alone is not evidence of a stream derivative. The direct
|
||||
# enthalpy reach proof below, plus the runtime dynamic-owner gate, is the
|
||||
# relevant condition for this target-specific program.
|
||||
# relevant condition for this proof-gated branch program.
|
||||
neighbor_by_endpoint: dict[Endpoint, Endpoint] = {}
|
||||
for connection in system.network.connections:
|
||||
if connection.kind != "physical":
|
||||
@@ -848,3 +870,138 @@ def compile_three_piston_tangent_provider(
|
||||
provider,
|
||||
reached_assignment_count=len(reached_assignments),
|
||||
)
|
||||
|
||||
|
||||
def _physical_neighbor_map(
|
||||
system: "GenericFluidSystem",
|
||||
) -> dict[Endpoint, Endpoint]:
|
||||
"""Return the one-to-one physical connector map proved by the network."""
|
||||
|
||||
neighbors: dict[Endpoint, Endpoint] = {}
|
||||
for connection in system.network.connections:
|
||||
if connection.kind != "physical":
|
||||
continue
|
||||
first, second = connection.endpoints
|
||||
# SimulationNetwork already rejects multiply connected physical ports.
|
||||
# Retain a defensive gate because this compiler may also be called by
|
||||
# custom network builders in tests or downstream applications.
|
||||
if first in neighbors or second in neighbors:
|
||||
raise ValueError("Physical endpoint has more than one connection.")
|
||||
neighbors[first] = second
|
||||
neighbors[second] = first
|
||||
return neighbors
|
||||
|
||||
|
||||
def _discover_supported_piston_branch_specs(
|
||||
system: "GenericFluidSystem",
|
||||
) -> tuple[_PistonBranchSpec, ...] | ThreePistonTangentCompilation:
|
||||
"""Discover every complete catalog piston branch by type and port topology.
|
||||
|
||||
A PNRP17 is the unambiguous root: its mechanical piston-side port must be
|
||||
driven by a singleton MECMAS21 coordinate, its pneumatic port must feed a
|
||||
PNCH012 whose first port feeds PNL0001, and its rod-side port must meet an
|
||||
LSTP00A contact. If even one PNRP17 is only partially supported, reject the
|
||||
batch with a stable reason instead of silently omitting derivative columns.
|
||||
"""
|
||||
|
||||
try:
|
||||
neighbors = _physical_neighbor_map(system)
|
||||
except ValueError:
|
||||
return _failed("unsupportedPistonBranchTopology:multipleConnection")
|
||||
|
||||
components = system.network.components
|
||||
|
||||
def model_type(endpoint: Endpoint | None) -> str | None:
|
||||
if endpoint is None:
|
||||
return None
|
||||
return getattr(components[endpoint.component], "MODEL_TYPE", None)
|
||||
|
||||
pistons = tuple(
|
||||
component
|
||||
for component in components.values()
|
||||
if getattr(component, "MODEL_TYPE", None) == "amesim_pnrp17"
|
||||
)
|
||||
if not pistons:
|
||||
return _failed("supportedPistonBranchMissing")
|
||||
|
||||
specs: list[_PistonBranchSpec] = []
|
||||
for piston in pistons:
|
||||
mass_endpoint = neighbors.get(Endpoint(piston.name, "port_2"))
|
||||
if (
|
||||
model_type(mass_endpoint) != "amesim_mecmas21"
|
||||
or mass_endpoint is None
|
||||
or mass_endpoint.port != "port_1"
|
||||
):
|
||||
return _failed("unsupportedPistonBranchTopology:mass")
|
||||
|
||||
chamber_endpoint = neighbors.get(Endpoint(piston.name, "port_1"))
|
||||
if model_type(chamber_endpoint) != "amesim_pnch012":
|
||||
return _failed("unsupportedPistonBranchTopology:chamber")
|
||||
assert chamber_endpoint is not None
|
||||
|
||||
pipe_endpoint = neighbors.get(
|
||||
Endpoint(chamber_endpoint.component, "port_1")
|
||||
)
|
||||
if (
|
||||
model_type(pipe_endpoint) != "amesim_pnl0001"
|
||||
or pipe_endpoint is None
|
||||
or pipe_endpoint.port != "port_1"
|
||||
):
|
||||
return _failed("unsupportedPistonBranchTopology:pipe")
|
||||
|
||||
contact_endpoint = neighbors.get(Endpoint(piston.name, "port_5"))
|
||||
if (
|
||||
model_type(contact_endpoint) != "amesim_lstp00a"
|
||||
or contact_endpoint is None
|
||||
or contact_endpoint.port != "port_1"
|
||||
):
|
||||
return _failed("unsupportedPistonBranchTopology:contact")
|
||||
|
||||
specs.append(
|
||||
_PistonBranchSpec(
|
||||
names=(
|
||||
mass_endpoint.component,
|
||||
piston.name,
|
||||
chamber_endpoint.component,
|
||||
pipe_endpoint.component,
|
||||
contact_endpoint.component,
|
||||
),
|
||||
chamber_connection_port=chamber_endpoint.port,
|
||||
)
|
||||
)
|
||||
|
||||
# Port uniqueness already proves unique pistons and masses, but explicitly
|
||||
# reject a custom multi-port chamber/contact/pipe shared by two roots. The
|
||||
# tangent propagation assumes one geometry seed per selected state owner.
|
||||
for role_index in range(5):
|
||||
if len({spec.names[role_index] for spec in specs}) != len(specs):
|
||||
return _failed("unsupportedPistonBranchTopology:sharedComponent")
|
||||
return tuple(specs)
|
||||
|
||||
|
||||
def compile_supported_piston_tangent_provider(
|
||||
system: "GenericFluidSystem",
|
||||
) -> ThreePistonTangentCompilation:
|
||||
"""Compile all name-independent, topology-supported piston branches."""
|
||||
|
||||
discovered = _discover_supported_piston_branch_specs(system)
|
||||
if isinstance(discovered, ThreePistonTangentCompilation):
|
||||
return discovered
|
||||
return _compile_named_piston_tangent_provider(system, discovered)
|
||||
|
||||
|
||||
def compile_three_piston_tangent_provider(
|
||||
system: "GenericFluidSystem",
|
||||
) -> ThreePistonTangentCompilation:
|
||||
"""Compile the committed legacy three-piston target by its stable names."""
|
||||
|
||||
return _compile_named_piston_tangent_provider(
|
||||
system,
|
||||
tuple(
|
||||
_PistonBranchSpec(
|
||||
names=names,
|
||||
chamber_connection_port="port_3",
|
||||
)
|
||||
for names in _TARGET_BRANCH_NAMES
|
||||
),
|
||||
)
|
||||
Reference in new issue
Block a user