接入test_mql真实固定气室拓扑

This commit is contained in:
huojiarong committed 2026-07-17 02:49:06 +00:00
1 parent c0f28520b4
commit 2d33a8f4bc
8 files changed
+747 -5

No files matched your search

+195
View File
@@ -4,10 +4,12 @@ from dataclasses import dataclass
from typing import Callable
from PythonModels.components.amesim_pneumatic import (
AmesimPneumaticGas,
AmesimPneumaticOrifice,
AmesimPneumaticVolume,
)
from PythonModels.core.medium import ThermodynamicProperties
from PythonModels.core.ports import PortState
from PythonModels.core.state import VolumeState
@@ -30,15 +32,72 @@ class TestMqlPneumaticTopologyCandidate:
target_port: str
source_is_typed_pneumatic: bool
target_is_typed_pneumatic: bool
category: str
reason: str
@dataclass(frozen=True)
class TestMqlPneumaticChamberSegmentSpec:
name: str
inlet_node_alias: str
inlet_line_alias: str
inlet_orifice_alias: str
inlet_orifice_boundary_port: str
inlet_orifice_volume_port: str
volume_alias: str
volume_inlet_port: str
volume_outlet_port: str
outlet_orifice_alias: str
outlet_orifice_volume_port: str
outlet_orifice_boundary_port: str
outlet_line_alias: str
outlet_node_alias: str
source: str = "amesim-cir-topology"
@dataclass(frozen=True)
class TestMqlPneumaticTopologyDiscovery:
branch_specs: tuple[TestMqlPneumaticBranchSpec, ...]
chamber_segment_specs: tuple[TestMqlPneumaticChamberSegmentSpec, ...]
blocked_candidates: tuple[TestMqlPneumaticTopologyCandidate, ...]
@dataclass(frozen=True)
class TestMqlPneumaticBoundaryCondition:
pressure_pa: float
temperature_k: float
def properties(self, gas: AmesimPneumaticGas) -> ThermodynamicProperties:
if self.pressure_pa <= 0.0:
raise ValueError("boundary pressure must be positive")
if self.temperature_k <= 0.0:
raise ValueError("boundary temperature must be positive")
return ThermodynamicProperties(
p=self.pressure_pa,
T=self.temperature_k,
rho=gas.density(self.pressure_pa, self.temperature_k),
u=gas.specific_internal_energy(self.temperature_k),
h=gas.specific_enthalpy(self.temperature_k),
)
@dataclass(frozen=True)
class TestMqlPneumaticChamberSegmentComponents:
volume: AmesimPneumaticVolume
inlet_orifice: AmesimPneumaticOrifice
outlet_orifice: AmesimPneumaticOrifice
spec: TestMqlPneumaticChamberSegmentSpec
@dataclass(frozen=True)
class TestMqlPneumaticChamberSegmentSnapshot:
chamber: ThermodynamicProperties
inlet_boundary: ThermodynamicProperties
outlet_boundary: ThermodynamicProperties
inlet_flow: float
outlet_flow: float
@dataclass(frozen=True)
class TestMqlPneumaticBranchComponents:
name: str
@@ -184,3 +243,139 @@ class TestMqlPneumaticClosure:
*upstream_derivative.as_vector(),
*downstream_derivative.as_vector(),
]
class TestMqlPneumaticChamberSegmentClosure:
"""Boundary-reduced fixed chamber segment discovered from AMESim topology.
The two PNL0001 lines remain prescribed boundary interfaces in this first
closure. Their aliases are retained in the spec so line dynamics can be
inserted later without rediscovering the component path.
"""
def __init__(
self,
*,
components: TestMqlPneumaticChamberSegmentComponents,
inlet_boundary: TestMqlPneumaticBoundaryCondition,
outlet_boundary: TestMqlPneumaticBoundaryCondition,
) -> None:
self.components = components
self.inlet_boundary = inlet_boundary
self.outlet_boundary = outlet_boundary
def initial_state_vector(self) -> list[float]:
return self.components.volume.get_state_vector()
def apply_state_vector(self, values: list[float]) -> None:
if len(values) != 2:
raise ValueError("single-chamber segment state vector requires two values")
self.components.volume.set_state_vector(values)
def snapshot(
self,
state_vector: list[float] | None = None,
) -> TestMqlPneumaticChamberSegmentSnapshot:
if state_vector is not None:
self.apply_state_vector(state_vector)
chamber = self.components.volume.properties()
inlet_boundary = self.inlet_boundary.properties(self.components.volume.gas)
outlet_boundary = self.outlet_boundary.properties(self.components.volume.gas)
inlet_temperature = (
inlet_boundary.T if inlet_boundary.p >= chamber.p else chamber.T
)
outlet_temperature = (
chamber.T if chamber.p >= outlet_boundary.p else outlet_boundary.T
)
inlet_flow = self.components.inlet_orifice.mass_flow(
inlet_boundary.p,
chamber.p,
inlet_temperature,
)
outlet_flow = self.components.outlet_orifice.mass_flow(
chamber.p,
outlet_boundary.p,
outlet_temperature,
)
snapshot = TestMqlPneumaticChamberSegmentSnapshot(
chamber=chamber,
inlet_boundary=inlet_boundary,
outlet_boundary=outlet_boundary,
inlet_flow=inlet_flow,
outlet_flow=outlet_flow,
)
self._write_port_states(snapshot)
return snapshot
@staticmethod
def _port(
component: AmesimPneumaticVolume | AmesimPneumaticOrifice,
port_name: str,
) -> PortState:
if port_name == "port_1":
return component.port_a
if port_name == "port_2":
return component.port_b
raise ValueError(f"unsupported pneumatic port: {port_name}")
def _write_port_states(
self,
snapshot: TestMqlPneumaticChamberSegmentSnapshot,
) -> None:
spec = self.components.spec
chamber = self.components.volume
inlet_orifice = self.components.inlet_orifice
outlet_orifice = self.components.outlet_orifice
inlet_boundary_port = self._port(
inlet_orifice,
spec.inlet_orifice_boundary_port,
)
inlet_volume_port = self._port(inlet_orifice, spec.inlet_orifice_volume_port)
inlet_boundary_port.p = snapshot.inlet_boundary.p
inlet_boundary_port.m_flow = snapshot.inlet_flow
inlet_boundary_port.h_outflow = snapshot.inlet_boundary.h
inlet_volume_port.p = snapshot.chamber.p
inlet_volume_port.m_flow = -snapshot.inlet_flow
inlet_volume_port.h_outflow = snapshot.chamber.h
chamber_inlet_port = self._port(chamber, spec.volume_inlet_port)
chamber_outlet_port = self._port(chamber, spec.volume_outlet_port)
chamber_inlet_port.m_flow = snapshot.inlet_flow
chamber_outlet_port.m_flow = -snapshot.outlet_flow
outlet_volume_port = self._port(
outlet_orifice,
spec.outlet_orifice_volume_port,
)
outlet_boundary_port = self._port(
outlet_orifice,
spec.outlet_orifice_boundary_port,
)
outlet_volume_port.p = snapshot.chamber.p
outlet_volume_port.m_flow = snapshot.outlet_flow
outlet_volume_port.h_outflow = snapshot.chamber.h
outlet_boundary_port.p = snapshot.outlet_boundary.p
outlet_boundary_port.m_flow = -snapshot.outlet_flow
outlet_boundary_port.h_outflow = snapshot.outlet_boundary.h
def rhs(self, state_vector: list[float]) -> list[float]:
snapshot = self.snapshot(state_vector)
spec = self.components.spec
chamber = self.components.volume
derivative = chamber.derivatives_from_two_connections(
port_a_m_flow=self._port(chamber, "port_1").m_flow,
connected_h_a=(
snapshot.inlet_boundary.h
if spec.volume_inlet_port == "port_1"
else snapshot.outlet_boundary.h
),
port_b_m_flow=self._port(chamber, "port_2").m_flow,
connected_h_b=(
snapshot.inlet_boundary.h
if spec.volume_inlet_port == "port_2"
else snapshot.outlet_boundary.h
),
internal_h=snapshot.chamber.h,
)
return derivative.as_vector()