结构动力学仿真-主题084-仿真验证与确认
第八十四篇:结构动力学仿真验证与确认
摘要
仿真验证与确认(Verification and Validation, V&V)是确保结构动力学仿真结果可靠性和可信度的关键环节。本文系统介绍V&V的基本概念、理论框架和实施方法,包括代码验证、计算验证、模型确认等核心内容。通过数值实验与解析解对比、网格收敛性分析、不确定性量化等方法,建立完整的仿真可信度评估体系。文章结合典型工程案例,演示如何系统地进行仿真验证与确认,为工程实践提供理论指导和方法参考。
关键词
验证与确认;代码验证;计算验证;模型确认;不确定性量化;网格收敛性;基准测试;误差估计












1. 验证与确认的基本概念
1.1 V&V的定义与区别
验证(Verification)和确认(Validation)是仿真质量保障的两个核心环节,虽然常被一起提及,但具有本质区别:
验证(Verification):回答"我们是否正确地求解了方程?"关注的是数值求解过程的正确性,包括:
- 代码验证:检查程序是否正确实现了数学模型
- 计算验证:检查数值解是否收敛于精确解
确认(Validation):回答"我们是否求解了正确的方程?"关注的是数学模型对物理现实的描述能力,包括:
- 模型确认:通过实验数据验证模型的预测能力
- 适用范围确定:明确模型在什么条件下有效
1.2 V&V的重要性
在结构动力学仿真中,V&V具有特殊的重要性:
工程安全需求:结构失效可能导致灾难性后果,必须确保仿真结果可信
经济性考虑:过度保守设计造成浪费,过于激进设计带来风险
法规要求:许多行业标准(如ASME、AIAA)对V&V有明确要求
科学严谨性:确保仿真结果可以被同行复现和验证
1.3 V&V标准与指南
国际上主要的V&V标准和指南包括:
ASME V&V 10-2006:计算固体力学和流体力学的验证与确认指南
AIAA G-077-1998:计算流体力学验证与确认指南
Oberkampf et al. (2002):预测能力评估框架
Schwer (2007):结构动力学验证与确认方法
这些标准共同构成了V&V的理论基础和实践指南。
2. 代码验证方法
2.1 代码验证的基本原理
代码验证的目标是证明数值程序正确实现了数学模型。主要方法包括:
基准测试(Benchmark Testing):使用具有精确解析解的问题测试代码
代码间比较(Code-to-Code Comparison):多个独立开发的代码求解同一问题
代码审查(Code Inspection):人工检查源代码的正确性
单元测试(Unit Testing):对代码的各个模块进行独立测试
2.2 基准测试问题
基准测试是代码验证的核心方法。理想的基准测试问题应具有:
精确解已知:可以是解析解、级数解或高精度数值解
覆盖关键特性:包含待验证代码的主要功能
数学适定性:问题在数学上是良态的
物理合理性:具有明确的物理意义
结构动力学中常用的基准测试问题包括:
单自由度系统:
mu¨+cu˙+ku=F(t)m\ddot{u} + c\dot{u} + ku = F(t)mu¨+cu˙+ku=F(t)
对于简谐激励 F(t)=F0sin(ωt)F(t) = F_0\sin(\omega t)F(t)=F0sin(ωt),稳态响应为:
u(t)=F0/k(1−β2)2+(2ζβ)2sin(ωt−ϕ)u(t) = \frac{F_0/k}{\sqrt{(1-\beta^2)^2 + (2\zeta\beta)^2}}\sin(\omega t - \phi)u(t)=(1−β2)2+(2ζβ)2F0/ksin(ωt−ϕ)
其中 β=ω/ωn\beta = \omega/\omega_nβ=ω/ωn,ϕ=arctan2ζβ1−β2\phi = \arctan\frac{2\zeta\beta}{1-\beta^2}ϕ=arctan1−β22ζβ
简支梁振动:
固有频率:
ωn=(nπL)2EIρA\omega_n = \left(\frac{n\pi}{L}\right)^2\sqrt{\frac{EI}{\rho A}}ωn=(Lnπ)2ρAEI
振型函数:
ϕn(x)=sin(nπxL)\phi_n(x) = \sin\left(\frac{n\pi x}{L}\right)ϕn(x)=sin(Lnπx)
圆板振动:
固有频率(简支边界):
ωmn=λmn2a2Dρh\omega_{mn} = \frac{\lambda_{mn}^2}{a^2}\sqrt{\frac{D}{\rho h}}ωmn=a2λmn2ρhD
其中 D=Eh312(1−ν2)D = \frac{Eh^3}{12(1-\nu^2)}D=12(1−ν2)Eh3 为弯曲刚度,λmn\lambda_{mn}λmn 为特征值
2.3 收敛性分析
收敛性分析是验证数值方法正确性的重要手段:
空间收敛:减小网格尺寸,观察解是否收敛
时间收敛:减小时间步长,观察解是否收敛
收敛阶数验证:数值解的误差应与理论收敛阶数一致
对于 ppp 阶精度的方法,误差应满足:
∥u−uh∥≤Chp\|u - u_h\| \leq Ch^p∥u−uh∥≤Chp
其中 hhh 为网格尺寸,CCC 为与网格无关的常数
通过绘制 log(∥u−uh∥)\log(\|u - u_h\|)log(∥u−uh∥) 对 log(h)\log(h)log(h) 的曲线,斜率应接近 ppp
2.4 代码间比较
当精确解不可得时,代码间比较是一种有效的验证方法:
N-version编程:多个独立团队开发代码求解同一问题
结果一致性检验:比较不同代码的结果差异
统计分析:使用统计方法评估结果的一致性
代码间比较的局限性在于:如果多个代码存在相同的系统性误差,可能无法发现
3. 计算验证方法
3.1 网格收敛性分析
网格收敛性分析是计算验证的核心内容,用于评估数值解的精度:
Richardson外推法:利用不同网格上的解估计精确解
设细网格解为 u1u_1u1,粗网格解为 u2u_2u2,收敛阶数为 ppp,则外推解为:
uexact≈u1+u1−u2rp−1u_{exact} \approx u_1 + \frac{u_1 - u_2}{r^p - 1}uexact≈u1+rp−1u1−u2
其中 r=h2/h1r = h_2/h_1r=h2/h1 为网格细化比
网格收敛指标(GCI):
GCI=Fs∣u1−u2∣rp−1GCI = \frac{F_s|u_1 - u_2|}{r^p - 1}GCI=rp−1Fs∣u1−u2∣
其中 FsF_sFs 为安全因子(通常取1.25)
渐近收敛范围:只有当网格足够细时,收敛性分析才有效
判断准则:
GCIfinerp⋅GCIcoarse≈1\frac{GCI_{fine}}{r^p \cdot GCI_{coarse}} \approx 1rp⋅GCIcoarseGCIfine≈1
3.2 时间步长收敛性
对于动力问题,时间步长的选择至关重要:
稳定性限制:显式积分方法存在稳定性条件
Δt≤2ωmax\Delta t \leq \frac{2}{\omega_{max}}Δt≤ωmax2
精度要求:时间步长应足够小以捕捉动态响应
收敛性检验:通过减小时间步长检验解的收敛性
3.3 误差估计
误差估计是计算验证的重要组成部分:
先验误差估计:在计算前估计误差上界
后验误差估计:基于计算结果估计实际误差
残差法:利用控制方程的残差估计误差
恢复法:通过应力恢复等技术提高精度
对于结构动力学,常用的误差度量包括:
能量范数误差:
∥e∥E=∫0T(∫Ω(σ−σh):(ϵ−ϵh)dΩ)dt\|e\|_E = \sqrt{\int_0^T\left(\int_\Omega (\sigma - \sigma_h):(\epsilon - \epsilon_h)d\Omega\right)dt}∥e∥E=∫0T(∫Ω(σ−σh):(ϵ−ϵh)dΩ)dt
L2范数误差:
∥e∥L2=∫0T∫Ω(u−uh)2dΩdt\|e\|_{L2} = \sqrt{\int_0^T\int_\Omega (u - u_h)^2 d\Omega dt}∥e∥L2=∫0T∫Ω(u−uh)2dΩdt
3.4 自适应网格细化
自适应网格细化是提高计算效率的有效方法:
误差指示器:识别误差较大的区域
细化策略:h-细化(减小单元尺寸)、p-细化(提高多项式阶数)
收敛控制:设定误差容差,自动调整网格
4. 模型确认方法
4.1 模型确认的基本框架
模型确认是将仿真结果与实验数据对比,评估模型预测能力的过程:
确认实验设计:设计专门用于模型确认的实验
数据获取:高精度测量系统的响应
对比分析:定量比较仿真与实验结果
适用域确定:明确模型的有效适用范围
4.2 确认度量
定量评估模型确认程度需要合适的度量指标:
相对误差:
ϵrel=∣usim−uexp∣∣uexp∣\epsilon_{rel} = \frac{|u_{sim} - u_{exp}|}{|u_{exp}|}ϵrel=∣uexp∣∣usim−uexp∣
决定系数(R²):
R2=1−∑(uexp−usim)2∑(uexp−uˉexp)2R^2 = 1 - \frac{\sum(u_{exp} - u_{sim})^2}{\sum(u_{exp} - \bar{u}_{exp})^2}R2=1−∑(uexp−uˉexp)2∑(uexp−usim)2
归一化均方根误差(NRMSE):
NRMSE=1N∑(usim−uexp)2uexp,max−uexp,minNRMSE = \frac{\sqrt{\frac{1}{N}\sum(u_{sim} - u_{exp})^2}}{u_{exp,max} - u_{exp,min}}NRMSE=uexp,max−uexp,minN1∑(usim−uexp)2
相关系数:
ρ=∑(usim−uˉsim)(uexp−uˉexp)∑(usim−uˉsim)2∑(uexp−uˉexp)2\rho = \frac{\sum(u_{sim} - \bar{u}_{sim})(u_{exp} - \bar{u}_{exp})}{\sqrt{\sum(u_{sim} - \bar{u}_{sim})^2\sum(u_{exp} - \bar{u}_{exp})^2}}ρ=∑(usim−uˉsim)2∑(uexp−uˉexp)2∑(usim−uˉsim)(uexp−uˉexp)
4.3 模型修正与更新
当模型预测与实验结果存在偏差时,需要进行模型修正:
参数识别:调整模型参数使预测与实验吻合
模型结构修正:改进模型的数学形式
贝叶斯模型更新:利用贝叶斯理论更新模型参数的概率分布
模型选择:在多个候选模型中选择最优模型
4.4 确认层次
模型确认通常分为多个层次进行:
单元级确认:对单个组件进行确认
子系统级确认:对子系统进行确认
系统级确认:对整个系统进行确认
这种分层方法可以降低确认难度,提高确认效率
5. 不确定性量化
5.1 不确定性的来源
结构动力学仿真中存在多种不确定性:
偶然不确定性(Aleatory):固有的随机性,如材料属性的自然变异
认知不确定性(Epistemic):由于知识不足导致的不确定性,如模型简化
输入不确定性:载荷、边界条件、几何尺寸的不确定性
模型形式不确定性:模型结构选择带来的不确定性
5.2 不确定性传播方法
蒙特卡洛模拟:通过大量随机采样传播不确定性
优点:概念简单,适用于非线性问题
缺点:计算量大,收敛速度慢
摄动法:基于泰勒展开近似不确定性传播
对于小不确定性,一阶近似:
μY≈f(μX)\mu_Y \approx f(\mu_X)μY≈f(μX)
σY2≈∑i∑j∂f∂Xi∂f∂XjCov(Xi,Xj)\sigma_Y^2 \approx \sum_i\sum_j\frac{\partial f}{\partial X_i}\frac{\partial f}{\partial X_j}Cov(X_i, X_j)σY2≈i∑j∑∂Xi∂f∂Xj∂fCov(Xi,Xj)
多项式混沌展开:用正交多项式展开随机过程
Y(ξ)=∑k=0PykΨk(ξ)Y(\xi) = \sum_{k=0}^P y_k\Psi_k(\xi)Y(ξ)=k=0∑PykΨk(ξ)
其中 Ψk\Psi_kΨk 为混沌多项式,ξ\xiξ 为标准随机变量
随机有限元法:将有限元与随机分析相结合
5.3 验证与确认中的不确定性
考虑不确定性后,V&V需要采用概率方法:
概率验证:验证数值解在统计意义上收敛
概率确认:评估模型预测与实验数据的统计一致性
置信区间:给出预测结果的置信区间而非单值
假设检验:使用统计检验评估模型 adequacy
6. 工程应用案例
6.1 案例1:简支梁模态分析验证
本案例验证有限元模态分析代码的正确性:
问题描述:简支梁的横向振动
几何参数:长度 L=1.0L=1.0L=1.0 m,截面 0.01×0.010.01 \times 0.010.01×0.01 m²
材料参数:E=200E=200E=200 GPa,ρ=7850\rho=7850ρ=7850 kg/m³
理论解:前三阶固有频率
f1=16.13 Hz,f2=64.52 Hz,f3=145.17 Hzf_1 = 16.13 \text{ Hz}, \quad f_2 = 64.52 \text{ Hz}, \quad f_3 = 145.17 \text{ Hz}f1=16.13 Hz,f2=64.52 Hz,f3=145.17 Hz
验证内容:
- 网格收敛性分析
- 频率计算精度
- 振型正交性检验
6.2 案例2:冲击响应分析确认
本案例通过实验数据确认冲击响应分析模型:
实验设置:悬臂梁受脉冲载荷
测量内容:应变、加速度时程
仿真模型:有限元模型,考虑材料非线性
确认度量:
- 峰值响应误差
- 响应时程相关系数
- 频谱一致性
6.3 案例3:不确定性量化与验证
本案例演示不确定性量化在V&V中的应用:
不确定性来源:
- 材料属性:E∼N(200,102)E \sim N(200, 10^2)E∼N(200,102) GPa
- 几何尺寸:厚度 h∼N(0.01,0.00052)h \sim N(0.01, 0.0005^2)h∼N(0.01,0.00052) m
- 边界条件:刚度 k∼N(106,105)k \sim N(10^6, 10^5)k∼N(106,105) N/m
分析方法:
- 蒙特卡洛模拟
- 敏感性分析
- 置信区间估计
7. V&V最佳实践
7.1 V&V计划制定
有效的V&V需要系统的计划:
明确目标:确定V&V的范围和深度
选择方法:根据问题特点选择合适的V&V方法
资源分配:合理分配计算和实验资源
文档记录:详细记录V&V过程和结果
7.2 常见误区与注意事项
过度依赖单一验证方法:应综合使用多种方法
忽视网格收敛性:未充分检验网格无关性
实验数据质量问题:实验误差可能大于仿真误差
确认与验证混淆:明确区分两者的目的和方法
忽视不确定性:未考虑输入和模型不确定性
7.3 质量保证体系
建立完善的V&V质量保证体系:
标准化流程:制定V&V标准操作程序
人员培训:提高工程师的V&V意识和能力
独立审查:引入第三方进行V&V审查
持续改进:基于经验反馈改进V&V方法
8. 前沿发展方向
8.1 机器学习方法在V&V中的应用
代理模型:用机器学习模型替代昂贵的高保真仿真
误差预测:训练神经网络预测仿真误差
模型修正:利用深度学习进行模型参数识别
不确定性量化:结合贝叶斯神经网络进行不确定性估计
8.2 数字孪生与实时V&V
实时验证:基于传感器数据进行在线模型验证
自适应模型:根据实测数据自动更新模型
预测性维护:结合V&V进行结构健康预测
8.3 多尺度多物理场V&V
跨尺度验证:从原子尺度到宏观尺度的验证
多物理场耦合:热-力-电等多物理场耦合验证
异构模型集成:不同保真度模型的集成验证
9. 总结
仿真验证与确认是确保结构动力学仿真可靠性的关键环节。本文系统介绍了:
验证方法:代码验证和计算验证的基本方法,包括基准测试、收敛性分析等
确认方法:模型确认的理论框架和定量度量
不确定性量化:考虑不确定性的V&V方法
工程实践:通过典型案例演示V&V的实施过程
V&V不是一次性工作,而是贯穿仿真全过程的持续活动。只有建立完善的V&V体系,才能确保仿真结果的可信度,为工程决策提供可靠依据。
完整Python代码实现
以下是本主题的完整Python仿真代码:
import matplotlib
matplotlib.use('Agg')
"""
案例1: 仿真验证方法
本案例演示结构动力学仿真中的验证方法,包括:
1. 代码验证:基准测试问题与解析解对比
2. 计算验证:网格收敛性分析
3. 时间步长收敛性分析
4. 误差估计与收敛阶数验证
"""
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from scipy.linalg import eigh
import warnings
warnings.filterwarnings('ignore')
# 设置中文字体
plt.rcParams['font.size'] = 10
# ==================== 代码验证:基准测试 ====================
def sdof_exact_solution(m, c, k, F0, omega, t):
"""
单自由度系统简谐激励的精确解析解
Parameters:
-----------
m, c, k : float
质量、阻尼、刚度
F0 : float
激励幅值
omega : float
激励频率
t : ndarray
时间数组
Returns:
--------
u : 位移响应
v : 速度响应
a : 加速度响应
"""
omega_n = np.sqrt(k / m)
zeta = c / (2 * np.sqrt(k * m))
beta = omega / omega_n
# 稳态响应幅值和相位
H = 1 / np.sqrt((1 - beta**2)**2 + (2*zeta*beta)**2)
phi = np.arctan2(2*zeta*beta, 1 - beta**2)
# 稳态响应
u_steady = (F0 / k) * H * np.sin(omega * t - phi)
v_steady = (F0 / k) * H * omega * np.cos(omega * t - phi)
a_steady = -(F0 / k) * H * omega**2 * np.sin(omega * t - phi)
# 瞬态响应(假设初始静止)
omega_d = omega_n * np.sqrt(1 - zeta**2)
A = -(F0/k) * H * (np.sin(-phi))
B = -(F0/k) * H * (omega*np.cos(-phi) + zeta*omega_n*np.sin(-phi)) / omega_d
u_transient = np.exp(-zeta*omega_n*t) * (A*np.cos(omega_d*t) + B*np.sin(omega_d*t))
v_transient = np.exp(-zeta*omega_n*t) * ((-zeta*omega_n*A + omega_d*B)*np.cos(omega_d*t) +
(-zeta*omega_n*B - omega_d*A)*np.sin(omega_d*t))
u = u_steady + u_transient
v = v_steady + v_transient
a = a_steady + np.exp(-zeta*omega_n*t) * ((-zeta*omega_n)*(-zeta*omega_n*A + omega_d*B)*np.cos(omega_d*t) + ...)
return u, v, a_steady
def newmark_beta(m, c, k, F, dt, beta=0.25, gamma=0.5):
"""
Newmark-beta法数值积分
Parameters:
-----------
m, c, k : float
质量、阻尼、刚度
F : ndarray
激励力时程
dt : float
时间步长
beta, gamma : float
Newmark参数
Returns:
--------
u, v, a : 位移、速度、加速度时程
"""
n = len(F)
u = np.zeros(n)
v = np.zeros(n)
a = np.zeros(n)
# 初始加速度
a[0] = (F[0] - c*v[0] - k*u[0]) / m
# 等效刚度
k_eff = m / (beta*dt**2) + gamma*c / (beta*dt) + k
for i in range(n-1):
# 等效载荷
f_eff = (F[i+1] +
m/(beta*dt**2)*(u[i] + dt*v[i] + (0.5-beta)*dt**2*a[i]) +
gamma*c/(beta*dt)*(u[i] + (1-gamma/beta)*dt*v[i] + (0.5-gamma/beta)*dt**2*a[i]) -
(1-gamma/beta)*c*v[i] - (1-gamma/(2*beta))*c*dt*a[i])
# 求解位移
u[i+1] = f_eff / k_eff
# 更新速度和加速度
a[i+1] = (u[i+1] - u[i] - dt*v[i] - (0.5-beta)*dt**2*a[i]) / (beta*dt**2)
v[i+1] = v[i] + dt*((1-gamma)*a[i] + gamma*a[i+1])
return u, v, a
def central_difference(m, c, k, F, dt):
"""
中心差分法数值积分
Parameters:
-----------
m, c, k : float
质量、阻尼、刚度
F : ndarray
激励力时程
dt : float
时间步长
Returns:
--------
u, v, a : 位移、速度、加速度时程
"""
n = len(F)
u = np.zeros(n)
v = np.zeros(n)
a = np.zeros(n)
# 初始条件
a[0] = (F[0] - c*v[0] - k*u[0]) / m
# 虚拟位移u[-1]
u_minus1 = u[0] - dt*v[0] + dt**2/2*a[0]
# 等效参数
a0 = m/dt**2 + c/(2*dt)
a1 = m/dt**2 - c/(2*dt)
a2 = k - 2*m/dt**2
for i in range(n-1):
if i == 0:
u[i+1] = (F[i] - a2*u[i] - a1*u_minus1) / a0
else:
u[i+1] = (F[i] - a2*u[i] - a1*u[i-1]) / a0
# 速度和加速度
if i > 0:
v[i] = (u[i+1] - u[i-1]) / (2*dt)
a[i] = (u[i+1] - 2*u[i] + u[i-1]) / dt**2
# 最后一点
v[-1] = (u[-1] - u[-2]) / dt
a[-1] = (F[-1] - c*v[-1] - k*u[-1]) / m
return u, v, a
# ==================== 计算验证:网格收敛性 ====================
def beam_modal_analysis(n_elements, L, E, I, rho, A):
"""
简支梁模态分析(有限元)
Parameters:
-----------
n_elements : int
单元数
L, E, I, rho, A : float
梁的几何和材料参数
Returns:
--------
freqs : 固有频率
modes : 振型
"""
n_nodes = n_elements + 1
n_dof = 2 * n_nodes
# 单元长度
Le = L / n_elements
# 单元刚度矩阵(Euler-Bernoulli梁)
k_e = E*I/Le**3 * np.array([
[12, 6*Le, -12, 6*Le],
[6*Le, 4*Le**2, -6*Le, 2*Le**2],
[-12, -6*Le, 12, -6*Le],
[6*Le, 2*Le**2, -6*Le, 4*Le**2]
])
# 一致质量矩阵
m_e = rho*A*Le/420 * np.array([
[156, 22*Le, 54, -13*Le],
[22*Le, 4*Le**2, 13*Le, -3*Le**2],
[54, 13*Le, 156, -22*Le],
[-13*Le, -3*Le**2, -22*Le, 4*Le**2]
])
# 组装全局矩阵
K = np.zeros((n_dof, n_dof))
M = np.zeros((n_dof, n_dof))
for e in range(n_elements):
dof = np.array([2*e, 2*e+1, 2*(e+1), 2*(e+1)+1])
for i in range(4):
for j in range(4):
K[dof[i], dof[j]] += k_e[i, j]
M[dof[i], dof[j]] += m_e[i, j]
# 简支边界条件(两端位移为0)
bc_dofs = [0, 2*(n_nodes-1)] # 第1个和最后1个节点的位移
free_dofs = [i for i in range(n_dof) if i not in bc_dofs]
K_reduced = K[np.ix_(free_dofs, free_dofs)]
M_reduced = M[np.ix_(free_dofs, free_dofs)]
# 求解特征值问题
eigenvalues, eigenvectors = eigh(K_reduced, M_reduced)
# 固有频率
freqs = np.sqrt(eigenvalues) / (2*np.pi)
# 重构完整振型
modes = np.zeros((n_dof, len(freqs)))
for i, fd in enumerate(free_dofs):
modes[fd, :] = eigenvectors[i, :]
return freqs, modes
def beam_exact_frequencies(L, E, I, rho, A, n_modes=5):
"""
简支梁精确固有频率
Parameters:
-----------
L, E, I, rho, A : float
梁参数
n_modes : int
模态数
Returns:
--------
freqs : 精确频率
"""
freqs = []
for n in range(1, n_modes+1):
omega_n = (n * np.pi / L)**2 * np.sqrt(E*I / (rho*A))
freqs.append(omega_n / (2*np.pi))
return np.array(freqs)
# ==================== 可视化函数 ====================
def plot_code_verification():
"""代码验证:数值解与解析解对比"""
# 系统参数
m = 1.0
c = 0.2
k = 100.0
F0 = 10.0
omega = 8.0
# 时间参数
T = 10.0
dt = 0.01
t = np.arange(0, T, dt)
F = F0 * np.sin(omega * t)
# 精确解(稳态部分)
omega_n = np.sqrt(k / m)
zeta = c / (2 * np.sqrt(k * m))
beta = omega / omega_n
H = 1 / np.sqrt((1 - beta**2)**2 + (2*zeta*beta)**2)
phi = np.arctan2(2*zeta*beta, 1 - beta**2)
u_exact = (F0 / k) * H * np.sin(omega * t - phi)
# 数值解
u_newmark, _, _ = newmark_beta(m, c, k, F, dt)
u_central, _, _ = central_difference(m, c, k, F, dt)
# 绘图
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 位移时程对比
ax = axes[0, 0]
ax.plot(t, u_exact, 'k-', linewidth=2, label='Exact (Steady-state)')
ax.plot(t, u_newmark, 'r--', linewidth=1.5, alpha=0.8, label='Newmark-beta')
ax.plot(t, u_central, 'b:', linewidth=1.5, alpha=0.8, label='Central Difference')
ax.set_xlabel('Time (s)')
ax.set_ylabel('Displacement (m)')
ax.set_title('Code Verification: Numerical vs Exact Solution')
ax.legend()
ax.grid(True, alpha=0.3)
ax.set_xlim(5, 10) # 显示稳态部分
# 误差分析
ax = axes[0, 1]
error_newmark = np.abs(u_newmark - u_exact)
error_central = np.abs(u_central - u_exact)
ax.semilogy(t, error_newmark, 'r-', label='Newmark Error')
ax.semilogy(t, error_central, 'b-', label='Central Difference Error')
ax.set_xlabel('Time (s)')
ax.set_ylabel('Absolute Error (log scale)')
ax.set_title('Numerical Error vs Time')
ax.legend()
ax.grid(True, alpha=0.3)
# 不同时间步长的收敛性
ax = axes[1, 0]
dts = [0.1, 0.05, 0.02, 0.01, 0.005, 0.002]
errors_newmark = []
errors_central = []
for dt_test in dts:
t_test = np.arange(0, T, dt_test)
F_test = F0 * np.sin(omega * t_test)
u_exact_test = (F0 / k) * H * np.sin(omega * t_test - phi)
u_num, _, _ = newmark_beta(m, c, k, F_test, dt_test)
error = np.max(np.abs(u_num[-100:] - u_exact_test[-100:]))
errors_newmark.append(error)
u_num_cd, _, _ = central_difference(m, c, k, F_test, dt_test)
error_cd = np.max(np.abs(u_num_cd[-100:] - u_exact_test[-100:]))
errors_central.append(error_cd)
ax.loglog(dts, errors_newmark, 'ro-', linewidth=2, markersize=8, label='Newmark-beta')
ax.loglog(dts, errors_central, 'bs-', linewidth=2, markersize=8, label='Central Difference')
# 参考斜率线
dt_ref = np.array([0.1, 0.002])
ax.loglog(dt_ref, 0.1*dt_ref**2, 'k--', alpha=0.5, label='2nd Order Slope')
ax.set_xlabel('Time Step (s)')
ax.set_ylabel('Max Error')
ax.set_title('Time Step Convergence Analysis')
ax.legend()
ax.grid(True, alpha=0.3)
# 频谱分析
ax = axes[1, 1]
from scipy.fft import fft, fftfreq
# 计算FFT
N = len(t)
yf_exact = fft(u_exact)
yf_newmark = fft(u_newmark)
xf = fftfreq(N, dt)[:N//2]
ax.semilogy(xf, 2.0/N * np.abs(yf_exact[0:N//2]), 'k-', linewidth=2, label='Exact')
ax.semilogy(xf, 2.0/N * np.abs(yf_newmark[0:N//2]), 'r--', linewidth=1.5, label='Newmark')
ax.axvline(omega/(2*np.pi), color='blue', linestyle=':', linewidth=2, label=f'Excitation Freq: {omega/(2*np.pi):.2f} Hz')
ax.set_xlabel('Frequency (Hz)')
ax.set_ylabel('Amplitude')
ax.set_title('Frequency Domain Comparison')
ax.legend()
ax.grid(True, alpha=0.3)
ax.set_xlim(0, 5)
plt.tight_layout()
plt.savefig('code_verification.png', dpi=150, bbox_inches='tight')
plt.close()
print("代码验证图已保存")
def plot_mesh_convergence():
"""网格收敛性分析"""
# 梁参数
L = 1.0
E = 200e9
b = h = 0.01
I = b * h**3 / 12
A = b * h
rho = 7850
# 不同网格数 - 从4开始确保有足够自由度
n_elements_list = [4, 8, 16, 32, 64, 128]
# 精确频率
n_modes_target = 5
freqs_exact = beam_exact_frequencies(L, E, I, rho, A, n_modes=n_modes_target)
# 存储结果 - 使用列表存储,允许不同长度
freqs_fem_list = []
for n_elem in n_elements_list:
freqs_num, _ = beam_modal_analysis(n_elem, L, E, I, rho, A)
freqs_fem_list.append(freqs_num[:n_modes_target]) # 最多取5阶
# 确定实际可用的模态数(所有网格都能计算的模态数)
min_modes = min(len(f) for f in freqs_fem_list)
# 重新组织数据
freqs_fem = {i: [] for i in range(min_modes)}
errors = {i: [] for i in range(min_modes)}
for freqs_num in freqs_fem_list:
for i in range(min_modes):
freqs_fem[i].append(freqs_num[i])
errors[i].append(np.abs(freqs_num[i] - freqs_exact[i]) / freqs_exact[i] * 100)
# 绘图
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 频率收敛
ax = axes[0, 0]
h_sizes = [L/n for n in n_elements_list]
colors = ['red', 'blue', 'green', 'orange', 'purple']
for i in range(min_modes):
ax.loglog(h_sizes, freqs_fem[i], 'o-', color=colors[i],
linewidth=2, markersize=8, label=f'Mode {i+1}')
ax.axhline(freqs_exact[i], color=colors[i], linestyle='--', alpha=0.5)
ax.set_xlabel('Element Size h (m)')
ax.set_ylabel('Natural Frequency (Hz)')
ax.set_title('Mesh Convergence: Natural Frequencies')
ax.legend()
ax.grid(True, alpha=0.3)
# 相对误差收敛
ax = axes[0, 1]
for i in range(min_modes):
ax.loglog(h_sizes, errors[i], 'o-', color=colors[i],
linewidth=2, markersize=8, label=f'Mode {i+1}')
# 参考斜率线(2阶收敛)
h_ref = np.array([h_sizes[0], h_sizes[-1]])
ax.loglog(h_ref, 10*h_ref**2, 'k--', alpha=0.5, label='2nd Order Slope')
ax.set_xlabel('Element Size h (m)')
ax.set_ylabel('Relative Error (%)')
ax.set_title('Convergence Rate Analysis')
ax.legend()
ax.grid(True, alpha=0.3)
# Richardson外推
ax = axes[1, 0]
# 使用最后三个网格计算外推解
p = 2 # 假设2阶收敛
n_modes_richardson = min(3, min_modes) # 确保不超过可用模态数
for i in range(n_modes_richardson): # 前n阶模态
f_coarse = freqs_fem[i][-3]
f_medium = freqs_fem[i][-2]
f_fine = freqs_fem[i][-1]
r = 2 # 细化比
f_extrap = f_fine + (f_fine - f_medium) / (r**p - 1)
ax.bar([i*4, i*4+1, i*4+2, i*4+3],
[f_coarse, f_medium, f_fine, freqs_exact[i]],
color=['lightblue', 'blue', 'darkblue', 'red'],
alpha=0.7)
ax.plot(i*4+3, f_extrap, 'go', markersize=12, markerfacecolor='none',
markeredgewidth=2, label='Richardson Extrapolation' if i==0 else '')
ax.set_xticks([1.5, 5.5, 9.5])
ax.set_xticklabels(['Mode 1', 'Mode 2', 'Mode 3'])
ax.set_ylabel('Frequency (Hz)')
ax.set_title('Richardson Extrapolation')
ax.legend()
ax.grid(True, alpha=0.3, axis='y')
# GCI分析
ax = axes[1, 1]
Fs = 1.25 # 安全因子
n_modes_gci = min(3, min_modes) # 确保不超过可用模态数
for i in range(n_modes_gci):
f_medium = freqs_fem[i][-2]
f_fine = freqs_fem[i][-1]
r = 2
p = 2
GCI = Fs * np.abs(f_fine - f_medium) / (r**p - 1)
relative_GCI = GCI / f_fine * 100
ax.bar(i, relative_GCI, color=colors[i], alpha=0.7, label=f'Mode {i+1}')
ax.set_xlabel('Mode Number')
ax.set_ylabel('GCI (%)')
ax.set_title('Grid Convergence Index')
ax.set_xticks(range(n_modes_gci))
ax.set_xticklabels([f'Mode {i+1}' for i in range(n_modes_gci)])
ax.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
plt.savefig('mesh_convergence.png', dpi=150, bbox_inches='tight')
plt.close()
print("网格收敛性图已保存")
# 打印结果
print("\n网格收敛性分析结果:")
print("="*60)
for i in range(min_modes):
print(f"\n第{i+1}阶模态:")
print(f" 精确频率: {freqs_exact[i]:.4f} Hz")
print(f" 最细网格频率: {freqs_fem[i][-1]:.4f} Hz")
print(f" 相对误差: {errors[i][-1]:.4f}%")
def plot_benchmark_tests():
"""基准测试问题验证"""
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 测试1:自由振动衰减
ax = axes[0, 0]
m, c, k = 1.0, 0.4, 100.0
omega_n = np.sqrt(k/m)
zeta = c / (2*np.sqrt(k*m))
omega_d = omega_n * np.sqrt(1-zeta**2)
t = np.linspace(0, 10, 1000)
u0, v0 = 1.0, 0.0
# 精确解
A = u0
B = (v0 + zeta*omega_n*u0) / omega_d
u_exact = np.exp(-zeta*omega_n*t) * (A*np.cos(omega_d*t) + B*np.sin(omega_d*t))
# 数值解
dt = 0.01
F = np.zeros(len(t))
u_num, _, _ = newmark_beta(m, c, k, F, dt)
u_num = u_num[:len(t)]
ax.plot(t, u_exact, 'k-', linewidth=2, label='Exact')
ax.plot(t, u_num, 'r--', linewidth=1.5, alpha=0.8, label='Numerical')
ax.set_xlabel('Time (s)')
ax.set_ylabel('Displacement (m)')
ax.set_title('Test 1: Free Vibration Decay')
ax.legend()
ax.grid(True, alpha=0.3)
# 测试2:共振响应
ax = axes[0, 1]
m, c, k = 1.0, 0.2, 100.0
omega_n = np.sqrt(k/m)
zeta = c / (2*np.sqrt(k*m))
# 扫频分析
omega_ratios = np.linspace(0.5, 1.5, 100)
H_exact = []
H_numerical = []
for beta in omega_ratios:
omega = beta * omega_n
H = 1 / np.sqrt((1-beta**2)**2 + (2*zeta*beta)**2)
H_exact.append(H)
# 数值稳态幅值
t = np.linspace(0, 20, 2000)
F = np.sin(omega*t)
u, _, _ = newmark_beta(m, c, k, F, 0.01)
H_numerical.append(np.max(u[-500:]))
ax.plot(omega_ratios, H_exact, 'k-', linewidth=2, label='Exact')
ax.plot(omega_ratios, H_numerical, 'ro', markersize=4, alpha=0.7, label='Numerical')
ax.axvline(1.0, color='blue', linestyle='--', alpha=0.5, label='Resonance')
ax.set_xlabel('Frequency Ratio β')
ax.set_ylabel('Amplitude Ratio H')
ax.set_title('Test 2: Frequency Response')
ax.legend()
ax.grid(True, alpha=0.3)
# 测试3:能量守恒
ax = axes[1, 0]
m, k = 1.0, 100.0
c = 0 # 无阻尼
t = np.linspace(0, 10, 1000)
u0, v0 = 1.0, 0.0
omega_n = np.sqrt(k/m)
# 精确解
u_exact = u0 * np.cos(omega_n*t) + (v0/omega_n) * np.sin(omega_n*t)
v_exact = -u0*omega_n * np.sin(omega_n*t) + v0 * np.cos(omega_n*t)
E_kinetic = 0.5 * m * v_exact**2
E_potential = 0.5 * k * u_exact**2
E_total_exact = E_kinetic + E_potential
# 数值解
dt = 0.01
F = np.zeros(len(t))
u_num, v_num, _ = newmark_beta(m, c, k, F, dt)
u_num = u_num[:len(t)]
v_num = v_num[:len(t)]
E_kinetic_num = 0.5 * m * v_num**2
E_potential_num = 0.5 * k * u_num**2
E_total_num = E_kinetic_num + E_potential_num
ax.plot(t, E_total_exact, 'k-', linewidth=2, label='Exact (Constant)')
ax.plot(t, E_total_num, 'r--', linewidth=1.5, alpha=0.8, label='Numerical')
ax.set_xlabel('Time (s)')
ax.set_ylabel('Total Energy (J)')
ax.set_title('Test 3: Energy Conservation (Undamped)')
ax.legend()
ax.grid(True, alpha=0.3)
# 测试4:模态正交性
ax = axes[1, 1]
L, E, I, rho, A = 1.0, 200e9, 1e-8, 7850, 1e-4
n_elem = 32
freqs, modes = beam_modal_analysis(n_elem, L, E, I, rho, A)
# 计算模态质量矩阵(应近似对角)
n_modes = 5
modal_mass = np.zeros((n_modes, n_modes))
for i in range(n_modes):
for j in range(n_modes):
phi_i = modes[:, i]
phi_j = modes[:, j]
# 简化的模态质量计算
modal_mass[i, j] = np.dot(phi_i, phi_j)
# 归一化
for i in range(n_modes):
modal_mass[i, :] /= np.sqrt(modal_mass[i, i])
modal_mass[:, i] /= np.sqrt(modal_mass[i, i])
im = ax.imshow(np.abs(modal_mass), cmap='Blues', aspect='auto')
plt.colorbar(im, ax=ax)
ax.set_xlabel('Mode j')
ax.set_ylabel('Mode i')
ax.set_title('Test 4: Modal Orthogonality Check')
ax.set_xticks(range(n_modes))
ax.set_yticks(range(n_modes))
ax.set_xticklabels([f'{i+1}' for i in range(n_modes)])
ax.set_yticklabels([f'{i+1}' for i in range(n_modes)])
plt.tight_layout()
plt.savefig('benchmark_tests.png', dpi=150, bbox_inches='tight')
plt.close()
print("基准测试图已保存")
# ==================== 主程序 ====================
def main():
"""主函数"""
print("\n" + "="*70)
print("案例1: 仿真验证方法")
print("="*70)
# 1. 代码验证
print("\n1. 代码验证:数值解与解析解对比...")
plot_code_verification()
# 2. 网格收敛性分析
print("\n2. 网格收敛性分析...")
plot_mesh_convergence()
# 3. 基准测试
print("\n3. 基准测试问题验证...")
plot_benchmark_tests()
print("\n" + "="*70)
print("所有验证分析完成!")
print("="*70)
if __name__ == "__main__":
main()
代码说明
- 参数设置区:定义系统的质量、刚度、阻尼等基本参数
- 核心计算模块:实现振动方程的求解算法
- 可视化模块:生成分析图表和动画
- 结果输出:保存图片文件供文档使用
运行上述代码将生成本主题所需的所有可视化素材。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐

所有评论(0)