T-Tail HALE 03:Linearization 与 Flutter damping¶
这一页从 trim 点出发,运行 Modal、LinearAssembler 和 AsymptoticStability。除单点特征值外,这里也给出一个轻量扫速,用来展示 flutter 分析里最常看的 damping value、damping trend 和代表性频率变化。
0. 导入公共工具¶
import os
import sys
from pathlib import Path
os.environ.setdefault("MPLCONFIGDIR", str(Path("_sharpy_runs") / ".mplconfig"))
candidate_dirs = [
Path.cwd(),
Path.cwd() / "docs" / "reference-aircraft",
Path("docs/reference-aircraft"),
]
for candidate in candidate_dirs:
if (candidate / "ttail_hale_utils.py").exists():
sys.path.insert(0, str(candidate.resolve()))
break
else:
raise FileNotFoundError("没有找到 ttail_hale_utils.py。请从仓库根目录或 docs/reference-aircraft 目录运行本 Notebook。")
import matplotlib.pyplot as plt
import numpy as np
from IPython.display import HTML, display
import ttail_hale_utils as hale
hale.configure_plots()
1. 写入 linearization 配置¶
这里的 flow 是:
BeamLoader -> AerogridLoader -> StaticTrim -> Modal -> LinearAssembler -> AsymptoticStability
num_modes = 12 用来让教学版本更轻;如果要做正式 flutter 边界搜索,可以把模态数、num_evals 和速度点加密。
case_dir = hale.prepare_hale_case("03_linearization", force=True)
linear_config = hale.write_solver_variant(
case_dir,
"ttail_hale_linearization.sharpy",
["BeamLoader", "AerogridLoader", "StaticTrim", "Modal", "LinearAssembler", "AsymptoticStability"],
n_steps=2,
num_modes=12,
num_evals=40,
)
print("linearization config:", linear_config)
linearization config: /Users/raiot/Documents/Programing/Steps-to-Aeroelastic-Sim/_sharpy_runs/ttail_hale_reference_aircraft/03_linearization/simple_HALE/ttail_hale_linearization.sharpy
2. 运行 linearization¶
linear_data, linear_log = hale.run_solver(linear_config)
ss = linear_data.linear.ss
print("state-space A shape:", ss.A.shape)
print("inputs:", ss.inputs)
print("outputs:", ss.outputs)
print("captured solver messages:", len(linear_log))
state-space A shape: (2578, 2578) inputs: 1183 outputs: 1179 captured solver messages: 4
3. 结构模态频率¶
Modal 会把结构模态分析输出到 beam_modal_analysis/eigenvaluetable.txt。这里先看前若干个非零频率,确认线性化前的结构部分不是空结果。
modal_rows = hale.modal_rows(case_dir)
nonzero_modal = [row for row in modal_rows if row.get("freq_n (Hz)", 0.0) > 1e-6]
display(HTML(hale.html_table(nonzero_modal[:12])))
fig, ax = plt.subplots(figsize=(7.2, 3.2))
ax.bar(
[int(row["mode"]) for row in nonzero_modal[:12]],
[row["freq_n (Hz)"] for row in nonzero_modal[:12]],
color=hale.COLORS["blue"],
)
ax.set_title("First non-zero structural modal frequencies", loc="left")
ax.set_xlabel("mode")
ax.set_ylabel("freq_n [Hz]")
ax.grid(True, axis="y", color=hale.COLORS["light_grey"], linewidth=0.6)
hale.finish_figure(fig)
No rows.
4. 单点 damping values¶
AsymptoticStability 会把代表性特征值写成表格。这里先把共轭模态折叠成更容易读的一组 damping value,再画特征值位置和 damping-frequency 图。表中的 damping 符号沿用 SHARPy 输出定义,读 flutter 时重点看 damping 是否向零附近移动,同时结合 real(lambda) 判断稳定性。
stability_rows = hale.stability_rows(case_dir)
representative_modes = hale.representative_stability_modes(stability_rows, max_modes=12)
display(HTML(hale.html_table(
representative_modes,
columns=["mode", "eval_real", "eval_imag", "freq_d (Hz)", "damping", "period (s)"],
precision=5,
)))
hale.plot_stability_rows(stability_rows)
| mode | eval_real | eval_imag | freq_d (Hz) | damping | period (s) |
|---|---|---|---|---|---|
| 0 | 34.36697 | 125.66371 | 20.00000 | -0.26380 | 0.05000 |
| 1 | 31.51895 | 125.66371 | 20.00000 | -0.24328 | 0.05000 |
| 2 | 8.51248 | 117.65913 | 18.72603 | -0.07216 | 0.05340 |
| 4 | 8.40623 | 117.75383 | 18.74110 | -0.07121 | 0.05336 |
| 6 | 6.89650 | -105.41739 | 16.77770 | -0.06528 | 0.05960 |
| 8 | 6.88002 | 105.44173 | 16.78157 | -0.06511 | 0.05959 |
| 10 | 4.72855 | 113.92873 | 18.13232 | -0.04147 | 0.05515 |
| 12 | 4.64540 | 113.91029 | 18.12938 | -0.04075 | 0.05516 |
| 14 | 0.07084 | 114.96669 | 18.29752 | -0.00062 | 0.05465 |
| 16 | 0.00446 | -115.00811 | 18.30411 | -0.00004 | 0.05463 |
| 28 | -0.05856 | -0.55681 | 0.08862 | 0.10459 | 11.28427 |
| 30 | -0.06485 | -0.30718 | 0.04889 | 0.20656 | 20.45430 |
5. Flutter speed sweep:damping 随速度变化¶
老师说 flutter analysis 要有 damping plot 和 damping value,这里就用两个已确认可稳定收敛的速度点做一个轻量扫速。它不是正式的 flutter boundary 搜索,而是给出完整管线:每个速度重新 trim、linearize,再从稳定性表里抽取最小 damping、最大 real(lambda) 和对应频率。
正式分析时可以把 SWEEP_SPEEDS 加密,例如 np.linspace(6.0, 18.0, 13),并增加 num_modes 与 num_evals。
SWEEP_SPEEDS = [8.0, 10.0]
flutter_rows = hale.flutter_speed_sweep(
SWEEP_SPEEDS,
label_prefix="03_flutter_damping",
num_modes=8,
num_evals=24,
force=True,
)
display(HTML(hale.html_table(
flutter_rows,
columns=[
"U_inf [m/s]",
"min damping",
"mode @ min damping",
"freq_d @ min damping [Hz]",
"max real(lambda)",
"mode @ max real",
"eigenvalue rows",
],
precision=5,
)))
hale.plot_flutter_sweep(flutter_rows)
| U_inf [m/s] | min damping | mode @ min damping | freq_d @ min damping [Hz] | max real(lambda) | mode @ max real | eigenvalue rows |
|---|---|---|---|---|---|---|
| 8.00000 | -0.53895 | 0 | 2.61665 | 10.51930 | 0 | 24 |
| 10.00000 | -0.26380 | 0 | 20.00000 | 34.36697 | 0 | 24 |
6. 线性化点的三维构型¶
linearization 不是围绕原始几何做,而是围绕 trim 后的状态做。下面显示 StaticTrim 之后进入 LinearAssembler 的气动网格。
traces = hale.aircraft_traces_from_data(linear_data, timestep=-1, opacity=0.42, suffix=" (linearization point)")
hale.show_plotly(
traces,
"Linearization point after StaticTrim",
camera={"eye": {"x": 1.55, "y": -1.9, "z": 0.85}},
)