FEMの勉強2

前回、以下の本の1次元FEMプログラム(VBA)をPython化してみましたが、今度は2次元FEMをPython化してみました。

 

[商品価格に関しましては、リンクが作成された時点と現時点で情報が変更されている場合がございます。]

ExcelとVBAによる実用有限要素法入門 [ 白井豊 ]

価格:2592円(税込、送料無料) (2017/4/16時点)

 
本編の一部とExcel(VBA)はこちらからダウンロードできます。

http://souzousha.iinaa.net/www/FEM/Download.htm
 

# -*- coding: utf-8 -*-

import math

class Material:
    def __init__(self,no):
        self.no = no
        self.young = .0
        self.poisson = .0
        self.thickness = .0

class Element:
    def __init__(self,no):
        self.no = no
        self.node = [0,0,0] # 節点番号
        self.x = [.0,.0,.0] # 節点番号のX座標
        self.y = [.0,.0,.0] 
        self.u = [.0 for i in range(6)] # 節点番号の変位
        self.elNode = [0,0,0,0,0,0]
        self.matNo = 0
        self.area = .0
        self.Bmat = [[.0 for i in range(6)] for j in range(3)]
        self.Smat = [[.0 for i in range(6)] for j in range(3)]
        self.Dmat = [[.0 for i in range(3)] for j in range(3)]
        self.Kmat = [[.0 for i in range(6)] for j in range(6)]
        self.stress = [.0 for i in range(9)]   # 要素の応力

    def setDmat(self,mat):
        a = mat.young / (1+mat.poisson) / (1-mat.poisson)
        b = a * mat.poisson
        c = 0.5 * mat.young / (1+mat.poisson)
        self.Dmat = [[a,b,0,],
                     [b,a,0,],
                     [0,0,c]]

    def setBmat(self):
        self.elNode = [2*self.node[0],2*self.node[0]+1,
                       2*self.node[1],2*self.node[1]+1,
                       2*self.node[2],2*self.node[2]+1]
        Ai = self.y[1] - self.y[2]
        Aj = self.y[2] - self.y[0]
        Ak = self.y[0] - self.y[1]
        Bi = self.x[2] - self.x[1]
        Bj = self.x[0] - self.x[2]
        Bk = self.x[1] - self.x[0]
        dl = Bk * Aj - Ak * Bj
        self.area = 0.5 * dl
        self.Bmat = [[Ai/dl,    0,Aj/dl,    0,Ak/dl,    0],
                     [    0,Bi/dl,    0,Bj/dl,    0,Bk/dl],
                     [Bi/dl,Ai/dl,Bj/dl,Aj/dl,Bk/dl,Ak/dl]]
                     
    def setSmat(self):
        for i in range(3):
            for j in range(6):
                s = 0
                for k in range(3):
                    s += self.Dmat[i][k]*self.Bmat[k][j]
                self.Smat[i][j] = s
        
    def setKmat(self,mat): #要素剛性行列の設定
        v = mat.thickness * self.area
        for i in range(6):
            for j in range(6):
                s = 0
                for k in range(3):
                    s += self.Bmat[k][i]*self.Smat[k][j]
                self.Kmat[i][j] = v * s

    def calcStress(self):
        for i in range(3):
            s = 0
            for j in range(6):
                s += self.Smat[i][j] * self.u[j]
            self.stress[i] = s
        cc = 0.5 * (self.stress[0] + self.stress[1])
        ss = 0.5 * (self.stress[0] - self.stress[1])
        cr = math.sqrt(ss*ss+self.stress[2]*self.stress[2])
        self.stress[3] = cc + cr
        self.stress[4] = cc - cr
        self.stress[5] = cr
        if cr <= ss:
            self.stress[6]  = 0
        else:
            wk = abs((ss+cr)/(ss-cr))
            self.stress[6] = 57.29577951 * math.atan(math.sqrt(wk))
            if self.stress[6] < 0:
                self.stress[6] = -self.stress[6]
            self.stress[6] = 90 - self.stress[6]
        self.stress[7] = math.sqrt(self.stress[3]*self.stress[3]
                                  - self.stress[3] * self.stress[4]
                                  + self.stress[4] * self.stress[4])
        if self.stress[4] > 0:
            self.stress[8] = self.stress[3]
        elif self.stress[3] > 0:
            self.stress[8] = self.stress[3] - self.stress[4]
        else:
            self.stress[8] = - self.stress[4]

class Fem2D():
    
    def setNodeData(self):
        '''節点データ設定'''
        f = open('node.txt')
        lines = f.readlines()
        f.close()
        lines = lines[1:]
        
        self.x = []
        self.y = []
        self.force = []
        self.nodeCond = []
        self.disp = []
        
        n = 0
        for line in lines:
            line = line.strip('\n')
            d = line.split('\t')
            d = [x if x is not '' else 0 for x in d] + [0,0,0,0]
            
            self.x.append(float(d[1]))
            self.y.append(float(d[2]))
            self.nodeCond.append(int(d[3]))
            self.nodeCond.append(int(d[4]))
            self.disp.append(float(d[5]))
            self.disp.append(float(d[6]))
            self.force.append(float(d[7]))
            self.force.append(float(d[8]))
            n += 1
        self.numberOfNode = n
    
    def setMaterialData(self):
        '''材料データ設定'''
        self.mats = []
        m = Material(0)
        m.young = 20000.0
        m.poisson = 0.3
        m.thickness = 1.0
        self.mats.append(m)
        
    def setElementData(self):
        '''要素データ設定'''
        f = open('elem.txt')
        lines = f.readlines()
        lines = lines[1:]
        f.close()
        
        self.elems = []
        for line in lines:
            d = line.split('\t')
            no = int(d[0])-1
            e = Element(no)
            n0 = int(d[1])-1
            n1 = int(d[2])-1
            n2 = int(d[3])-1
            e.node = [n0,n1,n2]
            e.matNo = int(d[4])-1
            e.x[0] = self.x[n0] 
            e.x[1] = self.x[n1] 
            e.x[2] = self.x[n2] 
            e.y[0] = self.y[n0] 
            e.y[1] = self.y[n1] 
            e.y[2] = self.y[n2] 
            self.elems.append(e)
        self.numberOfElement = len(self.elems)
    
    def setElementMatrix(self):
        '''要素剛性行列設定'''
        for i in range(self.numberOfElement):
            n = self.elems[i].matNo
            self.elems[i].setDmat(self.mats[n])
            self.elems[i].setBmat()
            self.elems[i].setSmat()
            self.elems[i].setKmat(self.mats[n])

    def setBandWidth(self):
        n = 0
        for k in range(self.numberOfElement):
            for i in range(6):
                for j in range(6):
                    ii = self.elems[k].elNode[i]
                    jj = self.elems[k].elNode[j] - ii
                    if jj > n:
                        n = jj
        self.bandWidth = n
    
    def setTotalMatrix(self):
        '''全体剛性行列設定'''
        i = self.numberOfNode*2
        j = self.bandWidth+1
        self.totalMat = [[0 for a in range(j)] for b in range(i)]
        
        for k in range(self.numberOfElement):
            for i in range(6):
                for j in range(6):
                    ii = self.elems[k].elNode[i]
                    jj = self.elems[k].elNode[j]-ii
                    if jj >= 0:
                        self.totalMat[ii][jj] += self.elems[k].Kmat[i][j]
     
    def setBoundaryCondition(self):
        '''境界条件による全体剛性行列,荷重ベクトルの変更'''
        for i in range(self.numberOfNode*2):
            if self.nodeCond[i] != 0:
                self.totalMat[i][0] = 1e30
                self.force[i] = self.totalMat[i][0]*self.disp[i]
    
    def saveTotalMatrix(self,fp='matrix.txt'):
        '''全体剛性行列の保存'''
        f = open(fp,'w')
        for i in range(self.numberOfNode*2):
            for j in range(self.bandWidth+1):
                f.write(str(self.totalMat[i][j])+'\t')
            f.write('\n')          
        f.close()
        print('saveTotalMat: %s' % fp)

    def solver(self):
        '''連立方程式の解(ガウスの消去法)'''
        nfreedom = self.numberOfNode*2
        akj = ['' for i in range(nfreedom)]
        aik = ['' for i in range(nfreedom)]
        n1 = self.bandWidth + 1
        n2 = nfreedom 
        for k in range(n2):
            m = self.bandWidth + 1
            if k+self.bandWidth+1 > n2:
                m = n2 - k
            for j in range(1,m):
                akj[j] = self.totalMat[k][j]/self.totalMat[k][0]
                aik[j] = self.totalMat[k][j]
            for i in range(1,m):
                it = i + k
                im1 = i
                for j in range(i,m):
                    jt = j - im1
                    self.totalMat[it][jt] -= aik[i]*akj[j]
        for k in range(n2-1):
            self.force[k] = self.force[k]/self.totalMat[k][0]
            m = self.bandWidth + 1
            if (k+self.bandWidth+1)>n2:
                m = n2 - k
            for i in range(1,m):
                it = i + k
                self.force[it] -= self.totalMat[k][i]*self.force[k]
        self.disp[n2-1] = self.force[n2-1] / self.totalMat[n2-1][0]
        for k in range(n2-2,-1,-1):
            s = 0
            m = self.bandWidth + 1
            if (k+self.bandWidth+1)> n2:
                m = n2-k
            for j in range(1,m):
                jt = j + k
                s += self.totalMat[k][j]*self.disp[jt]
            self.disp[k] = self.force[k] - s / self.totalMat[k][0]
            
    def saveNodeDisp(self):
        '''求まった節点変位データの保存'''
        f = open('disp.txt','w')
        for i in range(self.numberOfNode):
            f.write('%d\t' % (i+1))
            f.write('%f\t' % self.disp[2*i])
            f.write('%f\n' % self.disp[2*i+1])
        f.close()
        print('saveNodeDisp: disp.txt')
    
    def setStress(self):
        '''応力の設定'''
        for i in range(self.numberOfElement):
            e = self.elems[i]
            for j in range(6):
                k = e.elNode[j]
                e.u[j] = self.disp[k]
            e.calcStress()

    def saveStress(self):
        '''求まった応力データの保存'''
        f = open('stress.txt','w')
        for i in range(self.numberOfElement):
            for j in range(9):
                f.write(str(self.elems[i].stress[j])+'\t')
            f.write('\n')
        f.close()
        print('saveStress: stress.txt')

    def main(self):    
        self.setNodeData()
        self.setMaterialData()
        self.setElementData()
        self.setElementMatrix()
        self.setBandWidth()
        self.setTotalMatrix()
        self.saveTotalMatrix()
        self.setBoundaryCondition()
        self.saveTotalMatrix('matrix2.txt')
        self.solver()
        self.saveNodeDisp()
        self.setStress()
        self.saveStress()

def main():
    fem = Fem2D()
    fem.main()

if __name__ == '__main__':
    main()

プログラミングしてみれば、理解できると思ったけど、そんなことはなかったぜ。
流れくらいは分かったけど。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です