找回密码
 立即注册
首页 业界区 业界 Note_Fem边界条件的处理和numpy实现的四种方法 ...

Note_Fem边界条件的处理和numpy实现的四种方法

訾懵 2025-6-4 21:55:18
将单元刚度矩阵组装为全局刚度矩阵后,有:
1.png

此时的线性方程没有唯一解,\([K]\)是奇异矩阵,这是没有引入边界条件,消除刚体位移的原因.
边界条件分为两类:Forced and Geometric;对于力边界条件可以直接附加到节点力向量\([P]\)中,即\(P_j=P_j^{*}\),\(P_j^{*}\)是给定的节点力值.
因此我们基本只需要处理Geometric Boundary condition.下面介绍三种方法,将Bcs引入到\([K]、[P]\)
以位移边界条件为例,指定相关自由度值即:\(\Phi_j=\Phi_j^{*}\)
Method 1

将开头的\([K][\Phi]=[P]\)划分为:

\[\begin{bmatrix}[K_{11}] & [K_{12}] \\[K_{21}] & [K_{22}]\end{bmatrix}\begin{Bmatrix}\overrightarrow{\Phi}_1 \\\overrightarrow{\Phi}_2\end{Bmatrix}=\begin{Bmatrix}\overrightarrow{P}_1 \\\overrightarrow{P}_2\end{Bmatrix}\tag{1}\]
其中,\(\Phi_1\)是未知的自由节点自由度向量(free dofs);\(\Phi_2\)是已知的约束节点自由度值\(\Phi_j^{*}\)向量(specified nodal dof);\(P_1\)是已知节点力向量;\(P_2\)是未知的支反力向量
公式2进一步:

\[[K_{11}]\overrightarrow{\Phi}_1+[K_{12}]\overrightarrow{\Phi}_2=\overrightarrow{P}_1\tag{2}\]

\[[K_{21}]\overrightarrow{\Phi}_1+[K_{22}]\overrightarrow{\Phi}_2=\overrightarrow{P}_2\tag{3}\]
这时,\([K_{11}]\)是非奇异矩阵.因此自由节点自由度(未知节点位移)可求:

\[\overrightarrow{\Phi}_1=[K_{11}]^{-1}(\overrightarrow{P}_1-[K_{12}]\overrightarrow{\Phi}_2)\tag{4}\]
一旦\(\Phi_1\)求得,则未知支反力\(P_2\)可由公式3求得.
Method 2

也称划行划列法.method 1 中需要对\([K] ,[\Phi],[P]\)进行行列对调,重新排序.当出现非0位移边界时,method 1耗时长且需要记录过程,之后还需要恢复刚度矩阵.因此和method 1等效的处理方法是构建下式:

\[\begin{bmatrix}\left[K_{11}\right] & \left[0\right] \\\left[0\right] & \left[I\right]\end{bmatrix}\begin{bmatrix}\overrightarrow{\Phi}_1 \\\overrightarrow{\Phi}_2\end{bmatrix}=\begin{bmatrix}\overrightarrow{P}_1-\left[K_{12}\right]\overrightarrow{\Phi}_2\\{\overrightarrow{\Phi}_2}\end{bmatrix}\tag{5}\]
实际计算中,不需要对刚度阵重新排序.算法操作如下:
2.png

3.png

对所有的约束自由度\(\Phi_j\)重复Step 1~3即可,这种操作能够保持刚度和方程的对称性.
Method 3

该方法也称乘大数法.假设约束自由度为\(\Phi_j=\Phi_j^*\),操作如下:
4.png

该方法通用性强,适合大多数的静力学线性问题,但数值精度与大数的取值有关,太小了精度差,太大了容易出现"矩阵奇异"的现象
Method 4(对角元素置1法)

该方法的做法是,对于约束自由度\(\Phi_j=0\),把\([K]\)的j行j列置0,但对角元素Kjj=1,\([P]\)中对应元素置0.
以6x6的刚度矩阵为例子,

\[\begin{gathered}\begin{bmatrix}k_{11} & k_{12} & 0 & k_{14} & k_{15} & k_{16} \\k_{21} & k_{22} & 0 & k_{24} & k_{25} & k_{26} \\0 & 0 & 1 & 0 & 0 & 0 \\k_{41} & k_{42} & 0 & k_{44} & k_{45} & k_{46} \\k_{51} & k_{52} & 0 & k_{54} & k_{55} & k_{56} \\k_{e1} & k_{e3} & 0 & k_{eA} & k_{e5} & k_{e6}\end{bmatrix}\begin{bmatrix}\delta_1 \\\delta_2 \\\delta_3 \\\delta_4 \\\delta_5 \\\delta_6\end{bmatrix}=\begin{bmatrix}f_1 \\f_2 \\0 \\f_4 \\f_5 \\f_6\end{bmatrix}\end{gathered}\]
不引入大数,避免了数值稳定性的问题,不会影响矩阵的条件数; 但只适合\(\Phi_j=0\)这样的简单边界;可能影响系统矩阵的特性,直接替换可能改变矩阵的对称性(尤其在动力学和非线性问题中);不能处理非0的位移加载,只能处理力加载
Example

例题来自《The Finite Element Method in Engineering》的悬臂梁模型(example6.4, page227)
5.png

静力平衡方程为:
6.png

解为:

\[W=[0,0,-16.5667,-0.2480]\]

\[P=[50.0,4980.0,-50,20]\]
solve by method 1

7.png

8.png

9.png

solve by method 2

10.png

循环每个位移约束,需要注意高亮处的操作:
11.png

求解:
12.png

solve by method 3

13.png

14.png

Code Realize

四种方法进行Python+Numpy+Scipy编程实现,并与Example的解进行对比.
  1. #-------------------------------------------------------------------------------
  2. # Name:        BcsProcess
  3. # Purpose:     引入边界条件到[K]中,并返回解[U],[P]
  4. #               input:
  5. #                   K:全局刚度矩阵,(M,M) numpy.array
  6. #                   BcDict:位移约束,key (int) = 自由度序号(1-based) , value (float) = 自由度约束值
  7. #                   LoadDict:节点力加载,key (int) = 自由度序号(1-based) , value (float) = 施加的节点力加载或者等效节点力加载
  8. #
  9. # Author:      Administrator
  10. #
  11. # Created:     08-03-2025
  12. # Copyright:   (c) Administrator 2025
  13. # Licence:     <your licence>
  14. #-------------------------------------------------------------------------------
  15. import numpy as np
  16. from typing import Dict,List,Tuple
  17. import scipy as sc
  18. def Method1(K:np.ndarray,BcDict:Dict[int,float],LoadDict:Dict[int,float])->Tuple:
  19.     dofNum=K.shape[0]
  20.     # 初始化向量
  21.     U,P=np.zeros((dofNum,1)),np.zeros((dofNum,1))
  22.     prescribedDofIndexs=np.array(list(BcDict.keys()))-1
  23.     #使用集合运算,全部自由度与约束自由度求差, 得到自由位移自由度的
  24.     freeDofIndexs=np.array(list(set(range(dofNum))-set(prescribedDofIndexs.tolist())),dtype=int)
  25.     # 已知节点力加到P
  26.     for label,Pval in LoadDict.items():
  27.         ind=label-1
  28.         P[ind,0]+=Pval
  29.     # 已知节点位移(prescribed dof)
  30.     for label,Uval in BcDict.items():
  31.         ind=label-1
  32.         U[ind,0]+=Uval
  33.     U2=U[np.ix_(prescribedDofIndexs,[0])].copy()
  34.     # 已知节点力(free dof)
  35.     P1=P[np.ix_(freeDofIndexs,[0])].copy()
  36.     # 重新划分K行列
  37.     K11=K[np.ix_(freeDofIndexs,freeDofIndexs)].copy()
  38.     K12=K[np.ix_(freeDofIndexs,prescribedDofIndexs)].copy()
  39.     K21=K[np.ix_(prescribedDofIndexs,freeDofIndexs)].copy()
  40.     K22=K[np.ix_(prescribedDofIndexs,prescribedDofIndexs)].copy()
  41.     # 计算自由节点位移值
  42.     U1=np.dot(sc.linalg.inv(K11),P1-K12.dot(U2))
  43.     # 计算支反力
  44.     P2=np.dot(K21,U1)+np.dot(K22,U2)
  45.     # 合并到U,P向量
  46.     U[np.ix_(freeDofIndexs,[0])]=U1
  47.     P[np.ix_(prescribedDofIndexs,[0])]=P2
  48.     return U,P
  49. def Method2(K:np.ndarray,BcDict:Dict[int,float],LoadDict:Dict[int,float])->Tuple:
  50.     K_origin=K.copy()
  51.     dofNum=K.shape[0]
  52.     # 初始化向量
  53.     U,P=np.zeros((dofNum,1)),np.zeros((dofNum,1))
  54.     # 已知节点力加到 P
  55.     for label,Pval in LoadDict.items():
  56.         ind=label-1
  57.         P[ind,0]+=Pval
  58.     # 循环所有的位移约束
  59.     for label,Uval in BcDict.items():
  60.         j=label-1
  61.         #Step1
  62.         for i in range(dofNum):
  63.             P[i,0]=P[i,0]-K[i,j]*Uval
  64.         #Step2
  65.         for i in range(dofNum):
  66.             K[i,j]=0
  67.             K[j,i]=0
  68.         K[j,j]=1
  69.         #Step3
  70.         P[j,0]=Uval
  71.     # 求解 K'U=P'
  72.     U_=sc.linalg.solve(K,P)
  73.     P_=np.dot(K_origin,U_)
  74.     return U_,P_
  75. def Method3(K:np.ndarray,BcDict:Dict[int,float],LoadDict:Dict[int,float])->Tuple:
  76.     C=np.max(K)*10e6
  77.     K_origin=K.copy()
  78.     dofNum=K.shape[0]
  79.     # 初始化向量
  80.     U,P=np.zeros((dofNum,1)),np.zeros((dofNum,1))
  81.     # 已知节点力加到 P
  82.     for label,Pval in LoadDict.items():
  83.         ind=label-1
  84.         P[ind,0]+=Pval
  85.     # 循环所有位移约束
  86.     for label,Uval in BcDict.items():
  87.         j=label-1
  88.         # Step1
  89.         K[j,j]=K[j,j]*C
  90.         # Step2
  91.         P[j,0]=K[j,j]*Uval
  92.     # 求解 K'U=P'
  93.     U_=sc.linalg.solve(K,P)
  94.     P_=np.dot(K_origin,U_)
  95.     return U_,P_
  96. def Method4(K:np.ndarray,BcDict:Dict[int,float],LoadDict:Dict[int,float])->Tuple:
  97.     if np.any(np.array(list(BcDict.values())) != 0):
  98.        raise ValueError('该方法不能处理非0位移加载')
  99.     K_origin=K.copy()
  100.     dofNum=K.shape[0]
  101.     # 初始化向量
  102.     U,P=np.zeros((dofNum,1)),np.zeros((dofNum,1))
  103.     # 已知节点力加到 P
  104.     for label,Pval in LoadDict.items():
  105.         ind=label-1
  106.         P[ind,0]+=Pval
  107.     # loop all nodal bcs
  108.     for label, Uval in BcDict.items():
  109.         j=label-1
  110.         K[j,:]=0.0
  111.         K[:,j]=0.0
  112.         K[j,j]=1.0
  113.         P[j,0]=0
  114.     # solve K'U=P'
  115.     U_=sc.linalg.solve(K,P)
  116.     P_=np.dot(K_origin,U_)
  117.     return U_,P_
  118. if __name__ == '__main__':
  119.     K=np.array([[12,600,-12,600],
  120.                 [600,40000,-600,20000],
  121.                 [-12,-600,12,-600],
  122.                 [600,20000,-600,40000]])
  123.     Bcs={1:0,2:0}
  124.     loads={3:-50,4:20}
  125.     # 精确解
  126.     extract_U=np.array([0,0,-16.5667,-0.2480])
  127.     extract_P=np.array([50.0,4980.0,-50,20])
  128.     # 求解
  129.     u,p=Method4(K,Bcs,loads)
  130.     print(f"extract U=\n{extract_U}")
  131.     print(f"u=\n{u.T}")
  132.     print(f"extract_P=\n{extract_P}")
  133.     print(f"p=\n{p.T}")
复制代码
计算结果:
  1. extract_U=
  2. [  0.       0.     -16.5667  -0.248 ]
  3. extract_P=
  4. [  50. 4980.  -50.   20.]
  5. solving by method 1
  6. u=
  7. [[  0.           0.         -16.56666667  -0.248     ]]
  8. p=
  9. [[  50. 4980.  -50.   20.]]
  10. solving by method 2
  11. u=
  12. [[  0.           0.         -16.56666667  -0.248     ]]
  13. p=
  14. [[  50. 4980.  -50.   20.]]
  15. solving by method 3
  16. u=
  17. [[-1.04166667e-11 -3.11250000e-13 -1.65666667e+01 -2.48000000e-01]]
  18. p=
  19. [[  50. 4980.  -50.   20.]]
  20. solving by method 4
  21. u=
  22. [[  0.           0.         -16.56666667  -0.248     ]]
  23. p=
  24. [[  50. 4980.  -50.   20.]]
复制代码
总结

列举了四种直接节点位移边界条件的处理办法,并编程实现,求解案例.对比结果发现:相比Method3存在数值误差,其他三个都更加精确.
如果需要处理多点耦合边界条件,则有罚函数法,拉格朗日乘子法等.
参考资料:

  • 有限元基础编程 | 边界条件专题(对角元素置"1"法、乘大数法、划行划列法、拉格朗日乘子法、罚函数法)
  • 范雨有限元博客
  • 有限元软件开发  致力于国产大型通用商业有限元计算软件的开发
  • Singiresu S. Rao, sixth edition
Note Completed at 2025/03/08

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册