对齐PNL0002上游温度并稳定热流体闭合

This commit is contained in:
huojiarong committed 2026-08-11 07:12:52 +00:00
1 parent 6abcc220de
commit 0f73d5b568
5 files changed
+224 -13

No files matched your search

+47 -6
View File
@@ -541,6 +541,7 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
self.port_2 = self.register_declared_port("port_2")
self.port_2.p = self.p0
self.port_2.h_outflow = initial_h
self._connected_h: dict[str, float] = {}
@staticmethod
def _integer_parameter(name: str, value: float) -> int:
@@ -860,7 +861,7 @@ class AmesimPnl0002(AmesimPnl0001):
"""AMESim PNL0002 R-C-R pneumatic pipe with one center compliance."""
MODEL_TYPE = "amesim_pnl0002"
MODEL_VERSION = "0.2.0"
MODEL_VERSION = "0.3.0"
PORTS = (
PortDefinition.pneumatic("port_1", nominal_role="bidirectional"),
PortDefinition.pneumatic("port_2", nominal_role="bidirectional"),
@@ -940,13 +941,43 @@ class AmesimPnl0002(AmesimPnl0001):
port_pressure: float,
center_pressure: float,
center_temperature: float,
*,
port_name: str | None = None,
) -> float:
return self.mass_flow(port_pressure, center_pressure, center_temperature)
upstream_temperature = center_temperature
if (
port_name is not None
and port_name in self._connected_h
and port_pressure > center_pressure
):
inlet_h = self._connected_h[port_name]
upstream_temperature = self.medium.temperature_from_pressure_enthalpy(
max(port_pressure, 1.0),
inlet_h,
)
return self.mass_flow(
port_pressure,
center_pressure,
max(upstream_temperature, 1.0),
)
def update_stream_outflows(self, connected_h: Mapping[str, float]) -> None:
self._connected_h = dict(connected_h)
def component_result_values(self) -> Mapping[str, float]:
props = self.properties()
flow_1 = self.port_mass_flow(self.port_1.p, props.p, props.T)
flow_2 = self.port_mass_flow(self.port_2.p, props.p, props.T)
flow_1 = self.port_mass_flow(
self.port_1.p,
props.p,
props.T,
port_name="port_1",
)
flow_2 = self.port_mass_flow(
self.port_2.p,
props.p,
props.T,
port_name="port_2",
)
diagnostic_flow = flow_1 if abs(flow_1) >= abs(flow_2) else flow_2
upstream_pressure = max(self.port_1.p, self.port_2.p, props.p, 1.0)
density = max(self.medium.density(upstream_pressure, props.T), 1.0e-12)
@@ -980,7 +1011,12 @@ class AmesimPnl0002(AmesimPnl0001):
),
role="flow",
value=self.port_1.m_flow
- self.port_mass_flow(self.port_1.p, props.p, props.T),
- self.port_mass_flow(
self.port_1.p,
props.p,
props.T,
port_name="port_1",
),
),
EquationResidual(
id=f"{self.name}:port_2_pressure_flow_relation",
@@ -994,7 +1030,12 @@ class AmesimPnl0002(AmesimPnl0001):
),
role="flow",
value=self.port_2.m_flow
- self.port_mass_flow(self.port_2.p, props.p, props.T),
- self.port_mass_flow(
self.port_2.p,
props.p,
props.T,
port_name="port_2",
),
),
)
+54 -6
View File
@@ -40,6 +40,10 @@ class SimulationPreparationError(ValueError):
self.issues = issues
class ThermofluidClosureError(RuntimeError):
"""Raised when stream enthalpy and pressure-flow do not reach one fixed point."""
@dataclass(frozen=True)
class GenericSimulationResult:
success: bool
@@ -260,6 +264,7 @@ class GenericFluidSystem:
self.max_algebraic_residual = 0.0
self.max_algebraic_evaluations = 0
self.max_stream_iterations = 0
self.max_thermofluid_iterations = 0
self.signal_propagation_count = 0
self.pneumatic_volume_propagation_count = 0
@@ -280,20 +285,62 @@ class GenericFluidSystem:
for component in self.dynamic_components:
component.refresh_thermodynamic_ports()
algebraic = self.pressure_flow_solver.solve()
pressure_flow_solve_count = 1
pneumatic_volume = self.pneumatic_volume_resolver.solve()
self.pneumatic_volume_propagation_count += pneumatic_volume.propagated
if pneumatic_volume.propagated:
for component in self.dynamic_components:
component.refresh_thermodynamic_ports()
algebraic = self.pressure_flow_solver.solve()
stream, connected_h = self.stream_resolver.solve()
pressure_flow_solve_count += 1
# Some constitutive flow laws recover their upstream temperature from
# the connected stream enthalpy. Stream propagation updates that
# cache after the first pressure-flow pass, so refresh explicit flows
# once more before evaluating state derivatives and result variables.
algebraic = self.pressure_flow_solver.solve()
# connected stream enthalpy, while junction stream mixing itself depends
# on the resulting mass flows. A single stream -> pressure-flow refresh
# leaves that two-way coupling to the next RHS call, making the ODE RHS
# depend on evaluation history and corrupting finite-difference
# Jacobians. Close both layers to one fixed point inside this call.
physical_ports = tuple(
port
for component in self.network.components.values()
for definition in component.port_definitions
if definition.kind == "physical"
for port in (component.get_port(definition.name),)
)
connected_h: dict[str, dict[str, float]] = {}
max_coupling_iterations = 25
flow_relative_tolerance = 1.0e-12
for coupling_iteration in range(1, max_coupling_iterations + 1):
previous_flows = tuple(port.m_flow for port in physical_ports)
stream, connected_h = self.stream_resolver.solve()
for component in self.dynamic_components:
component.update_stream_outflows(connected_h[component.name])
algebraic = self.pressure_flow_solver.solve()
pressure_flow_solve_count += 1
current_flows = tuple(port.m_flow for port in physical_ports)
flow_scale = max(
[abs(value) for value in (*previous_flows, *current_flows)] + [1.0]
)
max_flow_delta = max(
(
abs(current - previous)
for previous, current in zip(previous_flows, current_flows)
),
default=0.0,
)
if max_flow_delta <= flow_relative_tolerance * flow_scale:
break
else:
raise ThermofluidClosureError(
"Stream enthalpy and pressure-flow coupling did not converge "
f"after {max_coupling_iterations} iterations."
)
self.max_thermofluid_iterations = max(
self.max_thermofluid_iterations,
coupling_iteration,
)
self.mechanical_state_reducer.update_constraint_accelerations()
self.algebraic_solve_count += 2 + int(bool(pneumatic_volume.propagated))
self.algebraic_solve_count += pressure_flow_solve_count
self.max_algebraic_residual = max(
self.max_algebraic_residual,
algebraic.max_scaled_residual,
@@ -467,6 +514,7 @@ class GenericFluidSystem:
},
"stream": {
"maxIterationsPerSolve": self.max_stream_iterations,
"maxThermofluidIterations": self.max_thermofluid_iterations,
"last": (
self.stream_resolver.last_diagnostics.as_dict()
if self.stream_resolver.last_diagnostics is not None