四角形1次要素応力解析のPythonコード

四角形1次要素を用いて2次元応力解析をするためのPythonコードを載せておく。
CAEとFEMとは?~構造解析の計算の流れ~ を基に作成した。

応力解析をするためのPythonコードを作成するにあたって、下図のような4要素で構成された簡単な解析モデルを題材にした。
各節点間の距離は1[mm]とし、固定支持した梁の端点に荷重を100[N]与えている。
要素の物性値は、ヤング率210000[MPa]、ポアソン比0.3[-]の鋼材を想定している。
構造解析をする上で最も重要なポイントが、単位系である。単位系とは、[N]や[MPa]などのような数値の物理的な意味のことである。構造解析のアルゴリズム内では、単位系は一切考慮されず、数値のみを操作している。そのため、入力する値の単位が揃っていないと、全く異なる結果が得られえることになる。

import math
root3 = math.sqrt(3)

nodes = 10 #全節点数
elements = 4 #全要素数
thickness = 0.01 #要素の厚さ
young = 210000 #ヤング率
poisson = 0.3 #ポアソン比

components = 3 #ひずみと応力の成分数
nodes_quad4 = 4 #四角形1次要素の節点数
integral_points = 4 #要素の積分点数
dof_node = 2 #節点自由度
dof_total = dof_node * nodes #全自由度
dof_quad4 = dof_node * nodes_quad4 #要素自由度

#積分点座標、重み
ip_xi = [0.0 for _ in range(integral_points)]
ip_et = [0.0 for _ in range(integral_points)]
ip_wi = [0.0 for _ in range(integral_points)]
ip_wj = [0.0 for _ in range(integral_points)]

#節点のX座標配列
x = [0.0 for _ in range(nodes)]
#節点のY座標配列
y = [0.0 for _ in range(nodes)]
#要素内節点順配列
connectivity = [[0.0 for _ in range(nodes_quad4)] for _ in range(elements)]
#Dマトリクス
D = [[0.0 for _ in range(components)] for _ in range(components)]
#Bマトリクス
B = [[[[0.0 for _ in range(dof_quad4)] for _ in range(components)] for _ in range(integral_points)] for _ in range(elements)]
#ヤコビ行列
detJ = [[0.0 for _ in range(integral_points)] for _ in range(elements)]
#要素剛性マトリクスの配列
Ke = [[[0.0 for _ in range(dof_quad4)] for _ in range(dof_quad4)] for _ in range(elements)]
#全体剛性マトリクスの配列
K = [[0.0 for _ in range(dof_total)] for _ in range(dof_total)]
#計算用全体剛性マトリクスの配列
Kc = [[0.0 for _ in range(dof_total)] for _ in range(dof_total)]
#境界条件ベクトルの配列
U = [0.0 for _ in range(dof_total)]
F = [0.0 for _ in range(dof_total)]
Um = [0.0 for _ in range(dof_total)]
#反力ベクトル
Fr = [0.0 for _ in range(dof_total)]
#積分点ひずみ
strain_ip = [[[0.0 for _ in range(components)] for _ in range(integral_points)] for _ in range(elements)]
#積分点応力
stress_ip = [[[0.0 for _ in range(components)] for _ in range(integral_points)] for _ in range(elements)]

def main():
    initialize()
    make_D()
    make_B()
    make_Ke()
    make_K()
    set_boundary_condition()
    solve()
    make_reaction()
    make_strain_element()
    make_stress_element()

    for e in range(0,elements):
        for ip in range(0,integral_points):
            for r in range(0,components):
                print(stress_ip[e][ip][r])

def initialize():
    #要素内節点順を設定
    connectivity[0][0] = 1
    connectivity[0][1] = 2
    connectivity[0][2] = 9
    connectivity[0][3] = 10
    connectivity[1][0] = 2
    connectivity[1][1] = 3
    connectivity[1][2] = 8
    connectivity[1][3] = 9
    connectivity[2][0] = 3
    connectivity[2][1] = 4
    connectivity[2][2] = 7
    connectivity[2][3] = 8
    connectivity[3][0] = 4
    connectivity[3][1] = 5
    connectivity[3][2] = 6
    connectivity[3][3] = 7

    #節点座標配列を設定
    x[0] = 0.0
    x[1] = 1.0
    x[2] = 2.0
    x[3] = 3.0
    x[4] = 4.0
    x[5] = 4.0
    x[6] = 3.0
    x[7] = 2.0
    x[8] = 1.0
    x[9] = 0.0
    y[0] = 0.0
    y[1] = 0.0
    y[2] = 0.0
    y[3] = 0.0
    y[4] = 0.0
    y[5] = 1.0
    y[6] = 1.0
    y[7] = 1.0
    y[8] = 1.0
    y[9] = 1.0

    #変位ベクトルを設定
    U[0] = 0.0
    U[1] = 0.0
    U[2] = 0.0
    U[3] = 0.0
    U[4] = 0.0
    U[5] = 0.0
    U[6] = 0.0
    U[7] = 0.0
    U[8] = 0.0
    U[9] = 0.0
    U[10] = 0.0
    U[11] = 0.0
    U[12] = 0.0
    U[13] = 0.0
    U[14] = 0.0
    U[15] = 0.0
    U[16] = 0.0
    U[17] = 0.0
    U[18] = 0.0
    U[19] = 0.0

    #荷重ベクトルを設定
    F[0] = 0.0
    F[1] = 0.0
    F[2] = 0.0
    F[3] = 0.0
    F[4] = 0.0
    F[5] = 0.0
    F[6] = 0.0
    F[7] = 0.0
    F[8] = 0.0
    F[9] = 0.0
    F[10] = 0.0
    F[11] = -100.0
    F[12] = 0.0
    F[13] = 0.0
    F[14] = 0.0
    F[15] = 0.0
    F[16] = 0.0
    F[17] = 0.0
    F[18] = 0.0
    F[19] = 0.0

    #拘束目印ベクトルを設定
    Um[0] = True
    Um[1] = True
    Um[2] = False
    Um[3] = False
    Um[4] = False
    Um[5] = False
    Um[6] = False
    Um[7] = False
    Um[8] = False
    Um[9] = False
    Um[10] = False
    Um[11] = False
    Um[12] = False
    Um[13] = False
    Um[14] = False
    Um[15] = False
    Um[16] = False
    Um[17] = False
    Um[18] = True
    Um[19] = False

    #積分点座標
    ip_xi[0] = - 1 / root3
    ip_xi[1] = 1 / root3
    ip_xi[2] = - 1 / root3
    ip_xi[3] = 1 / root3
    ip_et[0] = - 1 / root3
    ip_et[1] = - 1 / root3
    ip_et[2] = 1 / root3
    ip_et[3] = 1 / root3

    #積分点重み
    ip_wi[0] = 1.0
    ip_wi[1] = 1.0
    ip_wi[2] = 1.0
    ip_wi[3] = 1.0
    ip_wj[0] = 1.0
    ip_wj[1] = 1.0
    ip_wj[2] = 1.0
    ip_wj[3] = 1.0

def make_D():
    coef = young / (1.0 - 2.0 * poisson) / (1.0 + poisson)
    D[0][0] = coef * (1.0 - poisson)
    D[0][1] = coef * poisson
    D[0][2] = 0.0
    D[1][0] = D[0][1]
    D[1][1] = coef * (1.0 - poisson)
    D[1][2] = 0.0
    D[2][0] = D[0][2]
    D[2][1] = D[1][2]
    D[2][2] = coef * (1.0 - 2.0 * poisson) / 2.0

def make_B():
    for e in range(0,elements):
        x1 = x[connectivity[e][0]-1]
        x2 = x[connectivity[e][1]-1]
        x3 = x[connectivity[e][2]-1]
        x4 = x[connectivity[e][3]-1]
        y1 = y[connectivity[e][0]-1]
        y2 = y[connectivity[e][1]-1]
        y3 = y[connectivity[e][2]-1]
        y4 = y[connectivity[e][3]-1]

        for ip in range(0,integral_points):
            xi = ip_xi[ip]
            et = ip_et[ip]

            dN1dXi = - (1 - et) / 4
            dN2dXi = (1 - et) / 4
            dN3dXi = (1 + et) / 4
            dN4dXi = - (1 + et) / 4
            dN1dEt = - (1 - xi) / 4
            dN2dEt = - (1 + xi) / 4
            dN3dEt = (1 + xi) / 4
            dN4dEt = (1 - xi) / 4

            dXdXi = dN1dXi*x1 + dN2dXi*x2 + dN3dXi*x3 + dN4dXi*x4
            dXdEt = dN1dEt*x1 + dN2dEt*x2 + dN3dEt*x3 + dN4dEt*x4
            dYdXi = dN1dXi*y1 + dN2dXi*y2 + dN3dXi*y3 + dN4dXi*y4
            dYdEt = dN1dEt*x1 + dN2dEt*x2 + dN3dEt*x3 + dN4dEt*x4

            detJ[e][ip] = dXdXi * dYdEt - dXdEt * dYdXi

            dN1dX = (dN1dXi * dYdEt - dN1dEt * dYdXi) / detJ[e][ip]
            dN2dX = (dN2dXi * dYdEt - dN2dEt * dYdXi) / detJ[e][ip]
            dN3dX = (dN3dXi * dYdEt - dN3dEt * dYdXi) / detJ[e][ip]
            dN4dX = (dN4dXi * dYdEt - dN4dEt * dYdXi) / detJ[e][ip]
            dN1dY = ( - dN1dXi * dXdEt + dN1dEt * dXdXi) / detJ[e][ip]
            dN2dY = ( - dN2dXi * dXdEt + dN2dEt * dXdXi) / detJ[e][ip]
            dN3dY = ( - dN3dXi * dXdEt + dN3dEt * dXdXi) / detJ[e][ip]
            dN4dY = ( - dN4dXi * dXdEt + dN4dEt * dXdXi) / detJ[e][ip]

            B[e][ip][0][0] = dN1dX
            B[e][ip][0][1] = 0.0
            B[e][ip][0][2] = dN2dX
            B[e][ip][0][3] = 0.0
            B[e][ip][0][4] = dN3dX
            B[e][ip][0][5] = 0.0
            B[e][ip][0][6] = dN4dX
            B[e][ip][0][7] = 0.0
            B[e][ip][1][0] = 0.0
            B[e][ip][1][1] = dN1dY
            B[e][ip][1][2] = 0.0
            B[e][ip][1][3] = dN2dY
            B[e][ip][1][4] = 0.0
            B[e][ip][1][5] = dN3dY
            B[e][ip][1][6] = 0.0
            B[e][ip][1][7] = dN3dY
            B[e][ip][2][0] = dN1dY
            B[e][ip][2][1] = dN1dX
            B[e][ip][2][2] = dN2dY
            B[e][ip][2][3] = dN2dX
            B[e][ip][2][4] = dN3dY
            B[e][ip][2][5] = dN3dX
            B[e][ip][2][6] = dN4dY
            B[e][ip][2][7] = dN4dX

def make_Ke():
    Dt = [[0.0 for _ in range(components)] for _ in range(components)]
    Bt = [[0.0 for _ in range(components)] for _ in range(dof_quad4)]
    BtDt = [[0.0 for _ in range(components)] for _ in range(dof_quad4)]
    #Dマトリクスの転置
    for r in range(0,components):
        for c in range(0,components):
            Dt[c][r] = D[r][c]
    
    for e in range(0,elements):
        #Bマトリクスの転置
        for ip in range(0,integral_points):
            for r in range(0,components):
                for c in range(0,dof_quad4):
                    Bt[c][r] = B[e][ip][r][c]
        #Bt✖️Dtの計算
            for r in range(0,dof_quad4):
                for c in range(0,components):
                    BtDt[r][c] = 0.0
                    for m in range(0,components):
                        BtDt[r][c] = BtDt[r][c] + Bt[r][m] * Dt[m][c]
        #積分点剛性マトリクスKepの計算
            Kep = [[0.0 for _ in range(dof_quad4)] for _ in range(dof_quad4)]
            for r in range(0,dof_quad4):
                for c in range(0,dof_quad4):
                    Kep[r][c] = 0.0
                    for m in range(0,components):
                        Kep[r][c] = Kep[r][c] + BtDt[r][m] * B[e][ip][m][c]
                    wi = ip_wi[ip]
                    wj = ip_wj[ip]
                    Kep[r][c] = Kep[r][c] * detJ[e][ip] * wi * wj * thickness
        #要素剛性マトリクスKeの計算
            for r in range(0,components):
                for c in range(0,components):
                    Ke[e][r][c] = Ke[e][r][c] + Kep[r][c]

def make_K():
    for rt in range(0,dof_total):
        for ct in range(0,dof_total):
            K[rt][ct] = 0.0
    for e in range(0,elements):
        for r in range(0,dof_quad4):
            rt = (connectivity[e][r // dof_node] * dof_node - ((r+1) % dof_node)) - 1
            for c in range(0,dof_quad4):
                ct = (connectivity[e][c // dof_node] * dof_node - ((c+1) % dof_node)) -1
                K[rt][ct] = K[rt][ct] + Ke[e][r][c]

def set_boundary_condition():
    for r in range(0,dof_total):
        for c in range(0,dof_total):
            Kc[r][c] = K[r][c]
    for r in range(0,dof_total):
        if Um[r] == True:
            for rr in range(0,dof_total):
                if rr != r:
                    F[rr] = F[rr] - Kc[rr][r] * U[r]
            for rr in range(0,dof_total):
                Kc[rr][r] = 0.0
            for cc in range(0,dof_total):
                Kc[r][cc] = 0.0
            Kc[r][r] = 1.0
            F[r] = U[r]

def solve():
    pivot = 0
    p = 0
    for r in range(0,dof_total):
        pivot = Kc[r][r]
        for c in range(r,dof_total):
            Kc[r][c] = Kc[r][c] / pivot
        F[r] = F[r] / pivot
        for rr in range(r+1, dof_total):
            p = Kc[rr][r]
            for cc in range(r,dof_total):
                Kc[rr][cc] = Kc[rr][cc] - p * Kc[r][cc]
            F[rr] = F[rr] - p * F[r]
    for r in range(dof_total-1,-1,-1):
        U[r] = F[r]
        if r != dof_total-1:
            for c in range(r+1,dof_total):
                U[r] = U[r] - Kc[r][c] * U[c]

def make_reaction():
    for r in range(dof_total):
        Fr[r] = 0.0
        for c in range(0,dof_total):
            Fr[r] = Fr[r] + K[r][c] * U[c]

def make_strain_element():
    Ue = [0.0 for _ in range(dof_quad4)]
    for e in range(0,elements):
        for ip in range(0,integral_points):
            for n in range(0,nodes_quad4):
                Ue[n * 2] = U[connectivity[e][n] * 2 - 2]
                Ue[n * 2 + 1] = U[connectivity[e][n] * 2 - 1]
            for r in range(0,components):
                strain_ip[e][ip][r] = 0
                for c in range(0,dof_quad4):
                    strain_ip[e][ip][r] = strain_ip[e][ip][r] + B[e][ip][r][c] * Ue[c]

def make_stress_element():
    for e in range(0,elements):
        for ip in range(0,integral_points):
            for r in range(0,components):
                stress_ip[e][ip][r] = 0
                for c in range(0,components):
                    stress_ip[e][ip][r] = stress_ip[e][ip][r] + D[r][c] * strain_ip[e][ip][c]

main()
タイトルとURLをコピーしました