导出test_mql总闭包关键序列

This commit is contained in:
huojiarong committed 2026-07-20 09:45:32 +00:00
1 parent 958d813300
commit 6953c864c4
2 files changed
+231

No files matched your search

+174
View File
@@ -3879,6 +3879,33 @@ _ELASTIC_ENDSTOP_FORCE_BINDINGS = (
)
def _default_full_state_data_paths() -> tuple[str, ...]:
chamber_paths: list[str] = []
piston_paths: list[str] = []
for _mass_alias, piston_alias, chamber_field in _PISTON_FORCE_BINDINGS:
chamber_alias = _PNCH012_ALIAS_BY_SNAPSHOT_FIELD[chamber_field]
chamber_paths.extend([f"press@{chamber_alias}", f"vol@{chamber_alias}"])
piston_paths.extend([f"vol1@{piston_alias}", f"vvol1@{piston_alias}"])
mass_paths = [
f"{signal}@mass_friction_endstops_{index}"
for index in range(10, 20)
for signal in ("x1", "v1", "acc1")
]
return tuple([*chamber_paths, *piston_paths, *mass_paths])
_PNCH012_ALIAS_BY_SNAPSHOT_FIELD = {
"p4_port3_remote_primary_chamber": "pn_c1_8",
"p4_primary_chamber": "pn_c1_9",
"p4_port1_remote_primary_chamber": "pn_c1_10",
"p4_port1_far_primary_chamber": "pn_c1_11",
"p4_port1_next_primary_chamber": "pn_c1_12",
"p4_bridge_primary_chamber": "pn_c1_13",
"p4_port3_next_primary_chamber": "pn_c1_14",
"p4_port3_far_primary_chamber": "pn_c1_15",
}
class TestMqlFullStateClosure:
def __init__(self, *, pneumatic_closure: object, mechanical_closure: object) -> None:
self.pneumatic_closure = pneumatic_closure
@@ -3961,6 +3988,116 @@ class TestMqlFullStateClosure:
),
)
def data_path_values(
self,
*,
time_s: float,
state_vector: list[float],
data_paths: tuple[str, ...] | list[str] | None = None,
) -> dict[str, float]:
from PythonModels.systems.test_mql_pneumatic import pressure_to_amesim_gauge_pa
selected_paths = tuple(data_paths) if data_paths is not None else _default_full_state_data_paths()
snapshot = self.snapshot(state_vector)
rhs = self.rhs_at(time_s, state_vector)
chamber_properties_by_alias = {}
chamber_by_alias = {}
for _mass_alias, _piston_alias, chamber_field in _PISTON_FORCE_BINDINGS:
chamber = getattr(self.pneumatic_closure.components, chamber_field)
chamber_by_alias[chamber.name] = chamber
chamber_properties_by_alias[chamber.name] = getattr(snapshot.pneumatic, chamber_field)
state_by_alias = {state.alias: state for state in snapshot.mechanical.states}
acceleration_by_mass_alias = {
alias: rhs[self.pneumatic_state_count + 2 * index]
for index, alias in enumerate(self.mechanical_closure.mass_aliases)
}
values_by_data_path = {}
for data_path in selected_paths:
signal, alias = self._split_data_path(data_path)
if alias in chamber_properties_by_alias:
chamber_properties = chamber_properties_by_alias[alias]
chamber = chamber_by_alias[alias]
if signal == "press":
values_by_data_path[data_path] = pressure_to_amesim_gauge_pa(
chamber_properties.p
)
elif signal == "temp":
values_by_data_path[data_path] = chamber_properties.T
elif signal == "vol":
values_by_data_path[data_path] = chamber.volume_cm3()
else:
raise KeyError(data_path)
elif alias in self.mechanical_closure.assembly.pistons:
piston = self.mechanical_closure.assembly.pistons[alias]
kinematics = snapshot.mechanical.piston_kinematics_by_alias[alias]
geometry = piston.geometry()
if signal == "vol1":
values_by_data_path[data_path] = geometry.chamber_volume_cm3(
kinematics.port_3_displacement_m,
kinematics.port_2_displacement_m,
)
elif signal == "vvol1":
values_by_data_path[data_path] = geometry.chamber_volume_rate_l_min(
kinematics.port_3_velocity_m_s,
kinematics.port_2_velocity_m_s,
)
elif signal == "length":
values_by_data_path[data_path] = geometry.chamber_length_mm(
kinematics.port_3_displacement_m,
kinematics.port_2_displacement_m,
)
elif signal == "x4":
values_by_data_path[data_path] = kinematics.port_3_displacement_m
elif signal == "x5":
values_by_data_path[data_path] = kinematics.port_2_displacement_m
elif signal == "v4":
values_by_data_path[data_path] = kinematics.port_3_velocity_m_s
elif signal == "v5":
values_by_data_path[data_path] = kinematics.port_2_velocity_m_s
else:
raise KeyError(data_path)
elif alias in state_by_alias:
mass_state = state_by_alias[alias]
if signal == "x1":
values_by_data_path[data_path] = mass_state.displacement_m
elif signal == "v1":
values_by_data_path[data_path] = mass_state.velocity_m_s
elif signal == "acc1":
values_by_data_path[data_path] = acceleration_by_mass_alias[alias]
else:
raise KeyError(data_path)
else:
raise KeyError(data_path)
return values_by_data_path
def data_path_series(
self,
*,
times: tuple[float, ...] | list[float],
state_rows: list[list[float]],
data_paths: tuple[str, ...] | list[str] | None = None,
) -> dict[str, list[float]]:
selected_paths = tuple(data_paths) if data_paths is not None else _default_full_state_data_paths()
series = {data_path: [] for data_path in selected_paths}
for sample_index, time_value in enumerate(times):
state_vector = [row[sample_index] for row in state_rows]
values = self.data_path_values(
time_s=float(time_value),
state_vector=state_vector,
data_paths=selected_paths,
)
for data_path in selected_paths:
series[data_path].append(values[data_path])
return series
@staticmethod
def _split_data_path(data_path: str) -> tuple[str, str]:
if "@" not in data_path:
raise KeyError(data_path)
signal, alias = data_path.split("@", 1)
return signal, alias
def _force_by_mass_alias(
self,
time_s: float,
@@ -4406,6 +4543,43 @@ class TestMqlSystem:
t_eval=t_eval,
)
def simulate_full_state_series_from_spec(
self,
spec,
*,
inlet_node_pressure_pa: float,
resistance_boundary_pressure_pa: float,
inlet_node_temperature_k: float = 293.15,
resistance_boundary_temperature_k: float = 293.15,
config: SolveIVPConfig | None = None,
t_eval: list[float] | None = None,
data_paths: tuple[str, ...] | list[str] | None = None,
) -> TestMqlSimulationResult:
closure = self.full_state_closure_from_spec(
spec,
inlet_node_pressure_pa=inlet_node_pressure_pa,
resistance_boundary_pressure_pa=resistance_boundary_pressure_pa,
inlet_node_temperature_k=inlet_node_temperature_k,
resistance_boundary_temperature_k=resistance_boundary_temperature_k,
)
solution = integrate_ode(
rhs=lambda t, state: closure.rhs_at(t, state),
initial_state=closure.initial_state_vector(),
config=config or SolveIVPConfig(t_stop=1.0e-4, max_step=1.0e-5),
t_eval=t_eval,
)
times = [float(value) for value in solution.t]
state_rows = [list(row) for row in solution.y]
series = {
"time": times,
**closure.data_path_series(
times=times,
state_rows=state_rows,
data_paths=data_paths,
),
}
return TestMqlSimulationResult(t=times, y=state_rows, series=series)
@staticmethod
def pneumatic_branch_spec(
*,