完成求解器雅可比矩阵首轮优化,增加更新目录,整理了文档文件夹,增加了服务启动脚本

This commit is contained in:
lujingze committed 2026-08-17 07:33:31 +00:00
1 parent 6bb0591d32
commit 16a7eb2d6c
48 files changed
+8172 -217

No files matched your search

+219 -3
View File
@@ -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."""