完成求解器雅可比矩阵首轮优化,增加更新目录,整理了文档文件夹,增加了服务启动脚本
This commit is contained in:
1 parent
6bb0591d32
commit
16a7eb2d6c
48 files changed
+8172
-217
No files matched your search
@@ -1,8 +1,9 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from collections.abc import Mapping, Sequence
|
||||
from dataclasses import dataclass
|
||||
from functools import lru_cache
|
||||
from math import isclose, log, log10, pi, sqrt, tanh
|
||||
from math import isclose, isfinite, log, log10, pi, sqrt, tanh
|
||||
|
||||
from app.simulation.components.amesim.gases import (
|
||||
AMESIM_GAS_INDEX_PARAMETER,
|
||||
@@ -22,11 +23,35 @@ from app.simulation.core.metadata import (
|
||||
ResultVariableDefinition,
|
||||
THERMODYNAMIC_VOLUME_RESULT_VARIABLES,
|
||||
)
|
||||
from app.simulation.core.medium import GasMedium, ThermodynamicProperties
|
||||
from app.simulation.core.medium import (
|
||||
GasMedium,
|
||||
ThermodynamicProperties,
|
||||
ThermodynamicPropertiesLinearization,
|
||||
)
|
||||
from app.simulation.core.ports import PortDefinition
|
||||
from app.simulation.core.state import VolumeState
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Pnl0001MassFlowLinearization:
|
||||
value: float
|
||||
partial_p_1: float
|
||||
partial_p_2: float
|
||||
partial_temperature: float
|
||||
valid: bool = True
|
||||
reason: str | None = None
|
||||
direction: str = "forward"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Pnl0001DerivativeLinearization:
|
||||
derivative: tuple[float, float]
|
||||
tangents: tuple[tuple[float, ...], tuple[float, ...]]
|
||||
properties: ThermodynamicPropertiesLinearization
|
||||
valid: bool = True
|
||||
reason: str | None = None
|
||||
|
||||
|
||||
_MAX_REPORTED_FRICTION_FACTOR = 64_000_000.0
|
||||
|
||||
|
||||
@@ -829,6 +854,105 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
|
||||
)
|
||||
return magnitude if pressure_difference > 0.0 else -magnitude
|
||||
|
||||
def linearize_mass_flow(
|
||||
self,
|
||||
p_1: float,
|
||||
p_2: float,
|
||||
temperature: float,
|
||||
*,
|
||||
relative_step: float = 2.0 ** -26,
|
||||
slope_relative_tolerance: float = 5.0e-3,
|
||||
) -> Pnl0001MassFlowLinearization:
|
||||
"""Audit local flow-law slopes without perturbing the full system RHS."""
|
||||
|
||||
p_1 = float(p_1)
|
||||
p_2 = float(p_2)
|
||||
temperature = float(temperature)
|
||||
direction = "forward" if p_1 > p_2 else "reverse"
|
||||
value = self.mass_flow(p_1, p_2, temperature)
|
||||
|
||||
def invalid(reason: str) -> Pnl0001MassFlowLinearization:
|
||||
return Pnl0001MassFlowLinearization(
|
||||
value=value,
|
||||
partial_p_1=0.0,
|
||||
partial_p_2=0.0,
|
||||
partial_temperature=0.0,
|
||||
valid=False,
|
||||
reason=reason,
|
||||
direction=direction,
|
||||
)
|
||||
|
||||
if not all(isfinite(item) for item in (p_1, p_2, temperature, value)):
|
||||
return invalid("non_finite_primal")
|
||||
pressure_gap = abs(p_1 - p_2)
|
||||
if pressure_gap <= 1.0e-8:
|
||||
return invalid("flow_direction_boundary")
|
||||
if temperature <= 1.0 * (1.0 + 1.0e-10):
|
||||
return invalid("temperature_floor_boundary")
|
||||
if relative_step <= 0.0 or slope_relative_tolerance <= 0.0:
|
||||
raise ValueError("PNL0001 slope audit tolerances must be positive.")
|
||||
|
||||
pressure_step = min(
|
||||
relative_step * max(abs(p_1), abs(p_2), 1.0),
|
||||
0.25 * pressure_gap,
|
||||
)
|
||||
temperature_step = min(
|
||||
relative_step * max(abs(temperature), 1.0),
|
||||
0.25 * (temperature - 1.0),
|
||||
)
|
||||
if pressure_step <= 0.0 or temperature_step <= 0.0:
|
||||
return invalid("unresolved_local_step")
|
||||
|
||||
arguments = (p_1, p_2, temperature)
|
||||
argument_names = ("p_1", "p_2", "temperature")
|
||||
steps = (pressure_step, pressure_step, temperature_step)
|
||||
partials: list[float] = []
|
||||
for argument_index, (argument, step) in enumerate(
|
||||
zip(arguments, steps, strict=True)
|
||||
):
|
||||
lower = list(arguments)
|
||||
upper = list(arguments)
|
||||
lower[argument_index] = argument - step
|
||||
upper[argument_index] = argument + step
|
||||
lower_value = self.mass_flow(*lower)
|
||||
upper_value = self.mass_flow(*upper)
|
||||
left_slope = (value - lower_value) / step
|
||||
right_slope = (upper_value - value) / step
|
||||
slope_scale = max(
|
||||
abs(left_slope),
|
||||
abs(right_slope),
|
||||
abs(value) / max(abs(argument), 1.0),
|
||||
1.0e-12,
|
||||
)
|
||||
if not all(
|
||||
isfinite(item)
|
||||
for item in (
|
||||
lower_value,
|
||||
upper_value,
|
||||
left_slope,
|
||||
right_slope,
|
||||
)
|
||||
):
|
||||
return invalid(
|
||||
f"non_finite_local_slope:{argument_names[argument_index]}"
|
||||
)
|
||||
if (
|
||||
abs(left_slope - right_slope)
|
||||
> slope_relative_tolerance * slope_scale
|
||||
):
|
||||
return invalid(
|
||||
f"local_slope_disagreement:{argument_names[argument_index]}"
|
||||
)
|
||||
partials.append(0.5 * (left_slope + right_slope))
|
||||
|
||||
return Pnl0001MassFlowLinearization(
|
||||
value=value,
|
||||
partial_p_1=partials[0],
|
||||
partial_p_2=partials[1],
|
||||
partial_temperature=partials[2],
|
||||
direction=direction,
|
||||
)
|
||||
|
||||
def component_result_values(self) -> Mapping[str, float]:
|
||||
props = self.properties()
|
||||
flow = self.mass_flow(self.port_1.p, props.p, props.T)
|
||||
@@ -913,6 +1037,98 @@ class AmesimPnl0001(ThermodynamicVolumeComponent):
|
||||
)
|
||||
return derivative.as_vector()
|
||||
|
||||
def linearize_state_derivative(
|
||||
self,
|
||||
connected_h: Mapping[str, float],
|
||||
*,
|
||||
state_mass_tangent: Sequence[float],
|
||||
state_energy_tangent: Sequence[float],
|
||||
port_mass_flow_tangents: Mapping[str, Sequence[float]],
|
||||
connected_h_tangents: Mapping[str, Sequence[float]],
|
||||
property_linearization: ThermodynamicPropertiesLinearization | None = None,
|
||||
flow_boundary_tolerance: float = 1.0e-12,
|
||||
) -> Pnl0001DerivativeLinearization:
|
||||
"""Linearize the pipe storage balance in a fixed stream mode."""
|
||||
|
||||
port_names = ("port_1", "port_2")
|
||||
vectors = {
|
||||
"state_mass": tuple(float(value) for value in state_mass_tangent),
|
||||
"state_energy": tuple(float(value) for value in state_energy_tangent),
|
||||
}
|
||||
for port_name in port_names:
|
||||
vectors[f"flow:{port_name}"] = tuple(
|
||||
float(value) for value in port_mass_flow_tangents[port_name]
|
||||
)
|
||||
vectors[f"enthalpy:{port_name}"] = tuple(
|
||||
float(value) for value in connected_h_tangents[port_name]
|
||||
)
|
||||
widths = {len(values) for values in vectors.values()}
|
||||
if len(widths) != 1:
|
||||
raise ValueError("PNL0001 tangent vectors must have equal lengths.")
|
||||
width = len(vectors["state_mass"])
|
||||
invalid_reason: str | None = None
|
||||
if not all(isfinite(value) for values in vectors.values() for value in values):
|
||||
invalid_reason = "non_finite_tangent_input"
|
||||
|
||||
properties = property_linearization or self.medium.linearize_properties_from_mU(
|
||||
self.state.m,
|
||||
self.state.U,
|
||||
self.volume,
|
||||
vectors["state_mass"],
|
||||
vectors["state_energy"],
|
||||
(0.0,) * width,
|
||||
)
|
||||
if properties.tangents.width != width:
|
||||
raise ValueError(
|
||||
"PNL0001 property tangent width must match balance tangents."
|
||||
)
|
||||
props = properties.properties
|
||||
if not properties.valid:
|
||||
invalid_reason = invalid_reason or properties.reason
|
||||
|
||||
mass_derivative = self.port_1.m_flow + self.port_2.m_flow
|
||||
energy_derivative = self.thermal_energy_flow_w(props.T)
|
||||
mass_tangent = [0.0] * width
|
||||
thermal_coefficient = (
|
||||
0.0 if self.mode == 1 else self.kth * self.exchange_area
|
||||
)
|
||||
energy_tangent = [
|
||||
-thermal_coefficient * properties.tangents.T[index]
|
||||
for index in range(width)
|
||||
]
|
||||
|
||||
for port_name in port_names:
|
||||
port = self.get_port(port_name)
|
||||
flow_tangent = vectors[f"flow:{port_name}"]
|
||||
if (
|
||||
abs(port.m_flow) <= flow_boundary_tolerance
|
||||
and any(value != 0.0 for value in flow_tangent)
|
||||
):
|
||||
invalid_reason = invalid_reason or (
|
||||
f"flow_direction_boundary:{port_name}"
|
||||
)
|
||||
if port.m_flow > 0.0:
|
||||
inlet_h = connected_h[port_name]
|
||||
inlet_h_tangent = vectors[f"enthalpy:{port_name}"]
|
||||
else:
|
||||
inlet_h = props.h
|
||||
inlet_h_tangent = properties.tangents.h
|
||||
energy_derivative += port.m_flow * inlet_h
|
||||
for index in range(width):
|
||||
mass_tangent[index] += flow_tangent[index]
|
||||
energy_tangent[index] += (
|
||||
inlet_h * flow_tangent[index]
|
||||
+ port.m_flow * inlet_h_tangent[index]
|
||||
)
|
||||
|
||||
return Pnl0001DerivativeLinearization(
|
||||
derivative=(mass_derivative, energy_derivative),
|
||||
tangents=(tuple(mass_tangent), tuple(energy_tangent)),
|
||||
properties=properties,
|
||||
valid=invalid_reason is None,
|
||||
reason=invalid_reason,
|
||||
)
|
||||
|
||||
|
||||
class AmesimPnl0002(AmesimPnl0001):
|
||||
"""AMESim PNL0002 R-C-R pneumatic pipe with one center compliance."""
|
||||
|
||||
Reference in new issue
Block a user