見出し画像

Pythonで有限要素法(FEM)解析

有限要素法(FEM)を用いて1次元熱伝導方程式の近似解を求めます。FEMの計算にはpythonを使用しました。

対象とする境界値問題は、以下の定常熱伝導問題です。

【問題】
1次元の棒部材に対して、棒の左端では温度T_0が与えられ、右端ではQ_lの熱交換が行われています。また、棒の周辺では大気(温度はT_a)との熱交換が生じているとします。

一次元熱伝導問題

このとき、平衡方程式と境界条件は次のように書くことができます。

画像1
画像6

ここで、kは棒部材の熱伝導率、αは大気と棒部材間の熱伝導率です(その他の値は図の通り)。

上式について、

画像4

という置換を行うと方程式を単純化できます。具体的には下記のようになります。

画像5
画像6

【解析解】
右端の自由端では熱交換が行われないとすると(つまりQ_l = 0)、解析解が次のように求められます。

画像7

【近似解】
pythonを使って、ガラーキン有限要素法での近似解を求めます。
部材(領域)の長さはl = 1、左端のディリクレ境界条件はu_0 = 10、右端のノイマン境界条件は上記と同じくQ_l = 0とします。

例として要素数3(1次元問題なので節点数は4)での計算結果を示します。各節点の温度は、左端をu_1@x=0として
 [u_1  u_2  u_3  u_4] =  [10.  7.96085048  6.82292932  6.45741525]
となりました。参考として、解析解を使って各節点の温度を計算すると
   [u_1  u_2  u_3  u_4] =  [10.     7.97479764      6.84391887     6.48054274]
となり、それらしい近似計算ができている?ように見えます。

【最後に】
参考文献は、森北出版の竹内則雄、樫山和男、寺田賢二郎共著「計算力学(第2版) 有限要素法の基礎」日本計算工学会編です(上記の境界値問題も文献から引用しました)。一応、計算に使用したpythonのコードも載せておきます(python初心者なので、あまり参考にならないかも。。)

【pythonサンプルコード】

""" 1次元熱伝導方程式の有限要素法 """
# 参考:計算力学-有限要素法の基礎_§5.3

import math
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns

""" 計算条件の設定 """
# 計算領域
X_max = 1.0  # 最大値
X_min = 0  # 最小値
# 接点数
node_total = 4
# 境界条件
Dirichlet_U0 = 10
Neumann_xl = 0

""" 要素の準備 """
def preparation(X_min, X_max, node_total):
   # 要素数の計算
   ele_total = node_total - 1
   # 計算領域を等分割
   node_x_glo = np.linspace(X_min, X_max, node_total)
   # 各要素のGlobal節点番号
   node_num = np.empty((ele_total, 2), np.int)
   for e in range(ele_total):
       node_num[e, 0] = e
       node_num[e, 1] = e + 1
   # 各要素のLocal節点座標
   node_x_ele = np.empty((ele_total, 2), np.float)  # 各要素のLocal節点のx座標
   for e in range(ele_total):
       for j in range(2):
           node_x_ele[e, j] = node_x_glo[node_num[e, j]]
   # 各要素長さの計算
   length = np.empty((ele_total, 1), np.float)  # 各要素長さ
   for e in range(ele_total):
       length[e] = np.absolute(node_x_ele[e, 1] - node_x_ele[e, 0])

   return node_num, ele_total, length


""" 要素行列の計算 """
def Ke(ele_total, length):
   matrix_Ke1 = np.zeros((ele_total, 2, 2), np.float)
   matrix_Ke2 = np.zeros((ele_total, 2, 2), np.float)
   # 要素行列の成分計算
   for e in range(ele_total):
       for i in range(2):
           for j in range(2):
               matrix_Ke1[e, i, j] = ((-1) ** j * (-1) ** i) / length[e]
       matrix_Ke2[e, 0, 0] = length[e] / 3
       matrix_Ke2[e, 0, 1] = length[e] / 6
       matrix_Ke2[e, 1, 0] = length[e] / 6
       matrix_Ke2[e, 1, 1] = length[e] / 3

   matrix_Ke = matrix_Ke1 + matrix_Ke2
   print("要素係数行列\n", matrix_Ke)

   return matrix_Ke


""" 全体行列への組み立て """
def assemble(node_num, ele_total, matrix_Ke):
   matrix_K = np.zeros((node_total, node_total), np.float)  # 全体係数行列(ゼロで初期化)
   vector_Q = np.zeros(node_total, np.float)  # 全体係数ベクトル(ゼロで初期化)

   for e in range(ele_total):
       for i in range(2):
           for j in range(2):
               matrix_K[node_num[e, i], node_num[e, j]] += matrix_Ke[e, i, j]

   # 任意の節点に境界条件を実装する
   def boundary(node, Dirichlet, Neumann):
       # ディリクレ境界条件
       if (Dirichlet != "inf"):  # Dirichlet=無限大の時は処理しない
           vector_Q[:] -= Dirichlet * matrix_K[:, node]  # 定数ベクトルに行の値を移項
           vector_Q[node] = Dirichlet_U0  # 関数を任意の値で固定
           matrix_K[node, :] = 0.0  # 行を全て0にする
           matrix_K[:, node] = 0.0  # 列を全て0にする

       # ノイマン境界条件
       if (Neumann != "inf"):  # Neumann=無限大の時は処理しない
           vector_Q[node] = Neumann

   # 境界条件の処理
   boundary(0, Dirichlet_U0, "inf")
   boundary(node_total - 1, "inf", Neumann_xl)

   print("全体係数行列:\n", matrix_K)
   print("全体係数ベクトル\n", vector_Q)

   return matrix_K, vector_Q


""" 解を求める """
def solve(matrix_K, vector_Q, length):
   matrix_K_solve = np.zeros((node_total - 1, node_total - 1), np.float)  # 全体係数行列(ゼロで初期化)
   vector_Q_solve = np.zeros(node_total - 1, np.float)  # 全体係数ベクトル(ゼロで初期化)
   for i in range(node_total - 1):
       for j in range(node_total - 1):
           matrix_K_solve[i][j] = matrix_K[i + 1][j + 1]
       vector_Q_solve[i] = vector_Q[i + 1]

   vector_U = np.linalg.solve(matrix_K_solve, vector_Q_solve)
   vector_U_solve = np.insert(vector_U, 0, Dirichlet_U0)
   print("数値解\n", vector_U_solve)

   U = np.zeros(node_total, np.float)  # 解ベクトル(ゼロで初期化)
   for i in range(node_total):
       U[i] = Dirichlet_U0 * math.cosh((X_max - X_min) - i * length[i - 1]) / math.cosh(X_max - X_min)
   print("厳密解\n", U)

   return vector_U_solve


""" グラフの描画 """
def draw(vector_U_solve):
   array = np.array([vector_U_solve])
   plt.figure(figsize=(10, 2))
   sns.heatmap(array, cmap="Reds", vmax=10, vmin=0, yticklabels=False)
   plt.show()


def main():
   # 計算要素の準備
   node_num, ele_total, length = preparation(X_min, X_max, node_total)
   # 要素行列の作成
   matrix_Ke = Ke(ele_total, length)
   # 要素行列から全体行列への組み立て
   matrix_K, vector_Q = assemble(node_num, ele_total, matrix_Ke)
   # 求解
   vector_U_solve = solve(matrix_K, vector_Q, length)
   # 解の描画
   draw(vector_U_solve)

main()





この記事が気に入ったらサポートをしてみませんか?