完成求解器雅可比矩阵首轮优化,增加更新目录,整理了文档文件夹,增加了服务启动脚本
This commit is contained in:
1 parent
6bb0591d32
commit
16a7eb2d6c
48 files changed
+8172
-217
No files matched your search
@@ -1,7 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from math import pi
|
||||
from collections.abc import Mapping, Sequence
|
||||
from dataclasses import dataclass
|
||||
from math import isfinite, pi
|
||||
|
||||
from app.simulation.components.amesim.gases import (
|
||||
AMESIM_GAS_INDEX_PARAMETER,
|
||||
@@ -18,6 +19,18 @@ from app.simulation.core.ports import PortDefinition
|
||||
AMESIM_REFERENCE_PRESSURE_PA = 101300.0
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Pnrp17Linearization:
|
||||
volume: float
|
||||
volume_flow: float
|
||||
pressure_force: float
|
||||
volume_tangent: tuple[float, ...]
|
||||
volume_flow_tangent: tuple[float, ...]
|
||||
pressure_force_tangent: tuple[float, ...]
|
||||
valid: bool = True
|
||||
reason: str | None = None
|
||||
|
||||
|
||||
class AmesimPnrp17(AlgebraicComponent):
|
||||
"""AMESim PNRP17 pneumatic piston with two mechanical faces.
|
||||
|
||||
@@ -230,6 +243,53 @@ class AmesimPnrp17(AlgebraicComponent):
|
||||
def pneumatic_volume_outputs(self) -> Mapping[str, tuple[float, float]]:
|
||||
return {"port_1": (self.chamber_volume, self.chamber_volume_flow)}
|
||||
|
||||
def linearize_geometry_and_force(
|
||||
self,
|
||||
port_4_x_tangent: Sequence[float],
|
||||
port_5_x_tangent: Sequence[float],
|
||||
port_4_v_tangent: Sequence[float],
|
||||
port_5_v_tangent: Sequence[float],
|
||||
port_1_pressure_tangent: Sequence[float],
|
||||
) -> Pnrp17Linearization:
|
||||
"""Return exact piston geometry and pressure-force tangents."""
|
||||
|
||||
vectors = tuple(
|
||||
tuple(float(value) for value in values)
|
||||
for values in (
|
||||
port_4_x_tangent,
|
||||
port_5_x_tangent,
|
||||
port_4_v_tangent,
|
||||
port_5_v_tangent,
|
||||
port_1_pressure_tangent,
|
||||
)
|
||||
)
|
||||
widths = {len(values) for values in vectors}
|
||||
if len(widths) != 1:
|
||||
raise ValueError("PNRP17 tangent vectors must have equal lengths.")
|
||||
valid = all(isfinite(value) for values in vectors for value in values)
|
||||
area = self.effective_area
|
||||
volume_tangent = tuple(
|
||||
area * (right - left)
|
||||
for left, right in zip(vectors[0], vectors[1], strict=True)
|
||||
)
|
||||
volume_flow_tangent = tuple(
|
||||
area * (right - left)
|
||||
for left, right in zip(vectors[2], vectors[3], strict=True)
|
||||
)
|
||||
pressure_force_tangent = tuple(
|
||||
area * value for value in vectors[4]
|
||||
)
|
||||
return Pnrp17Linearization(
|
||||
volume=self.chamber_volume,
|
||||
volume_flow=self.chamber_volume_flow,
|
||||
pressure_force=self.pressure_force,
|
||||
volume_tangent=volume_tangent,
|
||||
volume_flow_tangent=volume_flow_tangent,
|
||||
pressure_force_tangent=pressure_force_tangent,
|
||||
valid=valid,
|
||||
reason=None if valid else "non_finite_tangent_input",
|
||||
)
|
||||
|
||||
def update_stream_outflows(self, connected_h: Mapping[str, float]) -> None:
|
||||
self.port_1.h_outflow = connected_h.get(
|
||||
"port_1",
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Mapping
|
||||
from math import expm1
|
||||
from collections.abc import Mapping, Sequence
|
||||
from dataclasses import dataclass
|
||||
from math import expm1, isfinite
|
||||
|
||||
from app.simulation.core.base import AlgebraicComponent, DynamicComponent
|
||||
from app.simulation.core.catalog import (
|
||||
@@ -20,6 +21,24 @@ from app.simulation.core.medium import IdealGasMedium
|
||||
from app.simulation.core.ports import PortDefinition
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class Mecmas21DerivativeLinearization:
|
||||
derivative: tuple[float, float]
|
||||
tangents: tuple[tuple[float, ...], tuple[float, ...]]
|
||||
mode: str
|
||||
valid: bool = True
|
||||
reason: str | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LstpContactForceLinearization:
|
||||
force: float
|
||||
force_tangent: tuple[float, ...]
|
||||
mode: str
|
||||
valid: bool = True
|
||||
reason: str | None = None
|
||||
|
||||
|
||||
_MECMAS21_FRICTION_ENABLED = ParameterCondition("useFriction", (2.0,))
|
||||
_MECMAS21_NON_RESTITUTION = ParameterCondition("stoptype", (1.0, 2.0, 4.0))
|
||||
_MECMAS21_LIMITS_ENABLED = ParameterCondition("stoptype", (1.0, 2.0, 3.0))
|
||||
@@ -723,6 +742,204 @@ class AmesimMecmas21(DynamicComponent):
|
||||
)
|
||||
return [self.acceleration(), velocity]
|
||||
|
||||
def linearize_state_derivative(
|
||||
self,
|
||||
port_1_force_tangent: Sequence[float],
|
||||
port_2_force_tangent: Sequence[float],
|
||||
velocity_tangent: Sequence[float],
|
||||
position_tangent: Sequence[float],
|
||||
*,
|
||||
constraint_mode: str = "current",
|
||||
boundary_tolerance: float = 1.0e-12,
|
||||
) -> Mecmas21DerivativeLinearization:
|
||||
"""Linearize one inertia in a declared fixed mechanical mode."""
|
||||
|
||||
vectors = tuple(
|
||||
tuple(float(value) for value in values)
|
||||
for values in (
|
||||
port_1_force_tangent,
|
||||
port_2_force_tangent,
|
||||
velocity_tangent,
|
||||
position_tangent,
|
||||
)
|
||||
)
|
||||
widths = {len(values) for values in vectors}
|
||||
if len(widths) != 1:
|
||||
raise ValueError("MECMAS21 tangent vectors must have equal lengths.")
|
||||
width = len(vectors[0])
|
||||
invalid_reason: str | None = None
|
||||
if not all(isfinite(value) for values in vectors for value in values):
|
||||
invalid_reason = "non_finite_tangent_input"
|
||||
|
||||
requested_mode = constraint_mode
|
||||
if requested_mode == "current":
|
||||
fixed = (
|
||||
self._constraint_acceleration == 0.0
|
||||
and self._constraint_velocity == 0.0
|
||||
)
|
||||
mode = "fixed" if fixed else "free"
|
||||
if self._constraint_acceleration is not None and not fixed:
|
||||
invalid_reason = invalid_reason or (
|
||||
"group_acceleration_requires_aggregate"
|
||||
)
|
||||
elif requested_mode == "free":
|
||||
mode = "free"
|
||||
elif requested_mode in {"lower", "upper"}:
|
||||
mode = requested_mode
|
||||
fixed = (
|
||||
self._constraint_acceleration == 0.0
|
||||
and self._constraint_velocity == 0.0
|
||||
)
|
||||
if not fixed:
|
||||
invalid_reason = invalid_reason or (
|
||||
"constraint_mode_not_statically_fixed"
|
||||
)
|
||||
elif requested_mode == "uninitialized":
|
||||
mode = requested_mode
|
||||
invalid_reason = invalid_reason or "constraint_mode_uninitialized"
|
||||
else:
|
||||
raise ValueError(
|
||||
"MECMAS21 constraint_mode must be current, free, lower, upper, "
|
||||
"or uninitialized."
|
||||
)
|
||||
|
||||
if mode in {"fixed", "lower", "upper"}:
|
||||
return Mecmas21DerivativeLinearization(
|
||||
derivative=(self.acceleration(), 0.0),
|
||||
tangents=((0.0,) * width, (0.0,) * width),
|
||||
mode=mode,
|
||||
valid=invalid_reason is None,
|
||||
reason=invalid_reason,
|
||||
)
|
||||
|
||||
force_1_tangent, force_2_tangent, dv, dx = vectors
|
||||
acceleration_tangent = [
|
||||
force_1_tangent[index] + force_2_tangent[index]
|
||||
for index in range(width)
|
||||
]
|
||||
if self.use_friction:
|
||||
for index in range(width):
|
||||
acceleration_tangent[index] += (
|
||||
-self.rvisc * dv[index]
|
||||
- 2.0 * self.wind * abs(self.v) * dv[index]
|
||||
)
|
||||
if (
|
||||
self.fcoul != 0.0
|
||||
and abs(self.v) <= boundary_tolerance
|
||||
and any(value != 0.0 for value in dv)
|
||||
):
|
||||
invalid_reason = invalid_reason or "dry_friction_direction_boundary"
|
||||
|
||||
def add_limit_tangent(
|
||||
*,
|
||||
side: str,
|
||||
stiffness: float,
|
||||
damping: float,
|
||||
damping_penetration: float,
|
||||
bound: float,
|
||||
damping_sign: float,
|
||||
force_sign: float,
|
||||
) -> None:
|
||||
nonlocal invalid_reason
|
||||
if int(self.stoptype) != 2:
|
||||
return
|
||||
penetration = (
|
||||
bound - self.x if side == "lower" else self.x - bound
|
||||
)
|
||||
penetration_tangent = tuple(
|
||||
(-value if side == "lower" else value) for value in dx
|
||||
)
|
||||
scale = max(abs(bound), abs(self.x), 1.0)
|
||||
if penetration <= 0.0:
|
||||
if (
|
||||
abs(penetration) <= boundary_tolerance * scale
|
||||
and any(value != 0.0 for value in penetration_tangent)
|
||||
):
|
||||
invalid_reason = invalid_reason or (
|
||||
f"soft_endstop_mode_boundary:{side}"
|
||||
)
|
||||
return
|
||||
if damping_penetration > 0.0:
|
||||
fraction = min(penetration / damping_penetration, 1.0)
|
||||
if penetration < damping_penetration:
|
||||
fraction_tangent = tuple(
|
||||
value / damping_penetration
|
||||
for value in penetration_tangent
|
||||
)
|
||||
else:
|
||||
fraction_tangent = (0.0,) * width
|
||||
if (
|
||||
abs(penetration - damping_penetration)
|
||||
<= boundary_tolerance
|
||||
* max(abs(damping_penetration), 1.0)
|
||||
and any(value != 0.0 for value in penetration_tangent)
|
||||
):
|
||||
invalid_reason = invalid_reason or (
|
||||
f"soft_endstop_damping_boundary:{side}"
|
||||
)
|
||||
else:
|
||||
fraction = 1.0
|
||||
fraction_tangent = (0.0,) * width
|
||||
|
||||
raw_force = (
|
||||
stiffness * penetration
|
||||
+ damping_sign * fraction * damping * self.v
|
||||
)
|
||||
raw_tangent = tuple(
|
||||
stiffness * penetration_tangent[index]
|
||||
+ damping_sign
|
||||
* damping
|
||||
* (
|
||||
fraction * dv[index]
|
||||
+ self.v * fraction_tangent[index]
|
||||
)
|
||||
for index in range(width)
|
||||
)
|
||||
if int(self.discContactOption) != 1 and raw_force <= 0.0:
|
||||
if (
|
||||
abs(raw_force)
|
||||
<= boundary_tolerance
|
||||
* max(abs(stiffness * penetration), 1.0)
|
||||
and any(value != 0.0 for value in raw_tangent)
|
||||
):
|
||||
invalid_reason = invalid_reason or (
|
||||
f"soft_endstop_force_boundary:{side}"
|
||||
)
|
||||
return
|
||||
for index in range(width):
|
||||
acceleration_tangent[index] += (
|
||||
force_sign * raw_tangent[index]
|
||||
)
|
||||
|
||||
add_limit_tangent(
|
||||
side="lower",
|
||||
stiffness=self.Kbmin,
|
||||
damping=self.Dbmin,
|
||||
damping_penetration=self.Pdmin,
|
||||
bound=self.xmin,
|
||||
damping_sign=-1.0,
|
||||
force_sign=1.0,
|
||||
)
|
||||
add_limit_tangent(
|
||||
side="upper",
|
||||
stiffness=self.Kbmax,
|
||||
damping=self.Dbmax,
|
||||
damping_penetration=self.Pdmax,
|
||||
bound=self.xmax,
|
||||
damping_sign=1.0,
|
||||
force_sign=-1.0,
|
||||
)
|
||||
acceleration_tangent = tuple(
|
||||
value / self.mass for value in acceleration_tangent
|
||||
)
|
||||
return Mecmas21DerivativeLinearization(
|
||||
derivative=(self.unconstrained_acceleration(), self.v),
|
||||
tangents=(acceleration_tangent, tuple(dv)),
|
||||
mode=mode,
|
||||
valid=invalid_reason is None,
|
||||
reason=invalid_reason,
|
||||
)
|
||||
|
||||
def component_result_values(self) -> Mapping[str, float]:
|
||||
return {
|
||||
"a": self.acceleration(),
|
||||
@@ -979,6 +1196,112 @@ class AmesimLstp00a(AlgebraicComponent):
|
||||
)
|
||||
return force if int(self.discContactOption) == 1 else max(force, 0.0)
|
||||
|
||||
def linearize_contact_force(
|
||||
self,
|
||||
port_1_x_tangent: Sequence[float],
|
||||
port_2_x_tangent: Sequence[float],
|
||||
port_1_velocity_tangent: Sequence[float],
|
||||
port_2_velocity_tangent: Sequence[float],
|
||||
*,
|
||||
boundary_tolerance: float = 1.0e-12,
|
||||
) -> LstpContactForceLinearization:
|
||||
"""Linearize the elastic contact in its current unilateral mode."""
|
||||
|
||||
vectors = tuple(
|
||||
tuple(float(value) for value in values)
|
||||
for values in (
|
||||
port_1_x_tangent,
|
||||
port_2_x_tangent,
|
||||
port_1_velocity_tangent,
|
||||
port_2_velocity_tangent,
|
||||
)
|
||||
)
|
||||
widths = {len(values) for values in vectors}
|
||||
if len(widths) != 1:
|
||||
raise ValueError("LSTP00A tangent vectors must have equal lengths.")
|
||||
width = len(vectors[0])
|
||||
if not all(isfinite(value) for values in vectors for value in values):
|
||||
return LstpContactForceLinearization(
|
||||
force=self.contact_force,
|
||||
force_tangent=(0.0,) * width,
|
||||
mode="invalid",
|
||||
valid=False,
|
||||
reason="non_finite_tangent_input",
|
||||
)
|
||||
|
||||
dx_1, dx_2, dv_1, dv_2 = vectors
|
||||
penetration_tangent = tuple(
|
||||
left - right for left, right in zip(dx_1, dx_2, strict=True)
|
||||
)
|
||||
velocity_tangent = tuple(
|
||||
left - right for left, right in zip(dv_1, dv_2, strict=True)
|
||||
)
|
||||
overlap = -self.gap
|
||||
force = self.contact_force
|
||||
scale = max(abs(self.gap0), abs(self.port_1.x), abs(self.port_2.x), 1.0)
|
||||
if overlap <= 0.0:
|
||||
on_boundary = abs(overlap) <= boundary_tolerance * scale
|
||||
crossing = any(value != 0.0 for value in penetration_tangent)
|
||||
return LstpContactForceLinearization(
|
||||
force=force,
|
||||
force_tangent=(0.0,) * width,
|
||||
mode="boundary" if on_boundary else "inactive",
|
||||
valid=not (on_boundary and crossing),
|
||||
reason=(
|
||||
"contact_mode_boundary"
|
||||
if on_boundary and crossing
|
||||
else None
|
||||
),
|
||||
)
|
||||
|
||||
penetration = overlap
|
||||
if self.Pdis > 0.0:
|
||||
damping_fraction = -expm1(-penetration / self.Pdis)
|
||||
damping_fraction_tangent = tuple(
|
||||
(1.0 - damping_fraction) * value / self.Pdis
|
||||
for value in penetration_tangent
|
||||
)
|
||||
else:
|
||||
damping_fraction = 1.0
|
||||
damping_fraction_tangent = (0.0,) * width
|
||||
relative_velocity = self.penetration_velocity
|
||||
raw_force = (
|
||||
self.kcont * penetration
|
||||
+ damping_fraction * self.rcont * relative_velocity
|
||||
)
|
||||
raw_tangent = tuple(
|
||||
self.kcont * penetration_tangent[index]
|
||||
+ self.rcont
|
||||
* (
|
||||
damping_fraction * velocity_tangent[index]
|
||||
+ relative_velocity * damping_fraction_tangent[index]
|
||||
)
|
||||
for index in range(width)
|
||||
)
|
||||
if int(self.discContactOption) != 1 and raw_force <= 0.0:
|
||||
on_boundary = (
|
||||
abs(raw_force)
|
||||
<= boundary_tolerance
|
||||
* max(abs(self.kcont * penetration), 1.0)
|
||||
)
|
||||
crossing = any(value != 0.0 for value in raw_tangent)
|
||||
return LstpContactForceLinearization(
|
||||
force=force,
|
||||
force_tangent=(0.0,) * width,
|
||||
mode="force_boundary" if on_boundary else "clamped",
|
||||
valid=not (on_boundary and crossing),
|
||||
reason=(
|
||||
"contact_force_boundary"
|
||||
if on_boundary and crossing
|
||||
else None
|
||||
),
|
||||
)
|
||||
return LstpContactForceLinearization(
|
||||
force=force,
|
||||
force_tangent=raw_tangent,
|
||||
mode="active",
|
||||
)
|
||||
|
||||
def clear_causal_contact(self) -> None:
|
||||
self._causal_penetration = None
|
||||
self._causal_contact_force = None
|
||||
|
||||
Reference in new issue
Block a user