🔑

CAEファイルをpythonでより素早く処理する

に公開
  • 久しぶりの記事です。

今回やること

  • CAEデータをpythonで処理しようとすると、どうしても処理速度が遅いなぁ~と感じます。
  • 特に、接点やエレメントなど数が多い処理を挟むと顕著に表れてきてしまいます。実際、自分の職場で扱っているくらいの規模になると下手すると30分とかそれ以上かかってしまう時もありました。
  • これでは、いくら自動化できます!といっても遅くて使う気になってもらえません。
  • どうしたら早くできるかな~と考えていて、処理が速いらしいdataframe型で接点などを保存するようにしてみました。

dataframe型のどこが便利か?

  • 前回記事での辞書型に比べて、保存しているデータを取り出す速度は、それほど大きく変わらないです(私の主観です。調べてません)が、特定の条件を満たす値だけ抜き出したり、並び替えたりするのが明らかに早い、またデータの種類を後から増やせたりと便利です。(後でやりたいことが増えたりしたときに柔軟に対応できる。)
  • あと、NODEクラスだとかSHELLクラスだとかをいちいち作らなくてよいので面倒でないです。

プログラムの説明

  • やってることは、CAEファイル(keyword形式)を読み込んでdataframe型で保存するだけです。
class RW_File:
    def Read_Keyfile(self,path:str)->None:#Main
        RList=self.__Read_file_toList(path)
        NODE_BASE_index,NODE_BASE_List=self.__Separate_Ranges(RList,"*NODE")
        NODE_List=np.array(self.__separate_row_str_count(NODE_BASE_List[1:],self.__CountList("*NODE")))
        df_N=pd.DataFrame(NODE_List[:,:4],columns=["ID","x","y","z"])
        df_N.loc[:,["ID"]]=df_N.loc[:,["ID"]].astype(int)
        df_N.loc[:,["x","y","z"]]=df_N.loc[:,["x","y","z"]].astype(float)

    def __Read_file_toList(self,path):#ファイルをすべて読み込んでListで返す
        with open(path,"rb") as ff:
            en=chardet.detect(ff.read())
        with open(path,encoding=en["encoding"]) as f:
            # $マークがついている行以外を読み込む
            #(keyファイルは、$がついている行がコメント文であるから)
            return [i for i in f.readlines() if i.find("$")==-1]

    def __Separate_Ranges(self,Liens:list,Keyword:str,LimitWordLen=1000):
        #キーワードから必要な行を取り出す
        valuetemp=[]
        indextemp=[]
        jug=False
        for index,value in enumerate(Liens):
            if value[:1]=="*":
                jug=False
            if value[:len(Keyword)]==Keyword and len(value)<=LimitWordLen+1:
                jug=True
            if jug:
                valuetemp.append(value)
                indextemp.append(index)
        return indextemp,valuetemp

    def __separate_row_str_count(self,BaseList:list,__CountList:list)->list:
        #行を適切な文字位置で分割する
        #PARTなど必要なデータが複数行で書かれていても対応できるようにしている
        temp=[]
        newCount=[]
        for i in range(int((len(BaseList))/len(__CountList))):
            newCount.extend(__CountList)
        for index,i in enumerate(BaseList):
            temp.append([ i[newCount[index][j]:newCount[index][j+1]] for j in range(len(newCount[index])-1)])
        return temp

    def __CountList(self,keyword:str)->list:
        #各データの分割位置
        temp={"*NODE":[(0,8,24,40,56,-1)],
            "*ELEMENT_SHELL":[(0,8,16,24,32,40,48,-1)],
            "*ELEMENT_SOLID":[(0,8,16,24,32,40,48,56,64,72,80,-1)],
            "*PART":[(0,-1),(0,10,20,30,40,50,60,70,80,-1)],
            "*SET_SHELL_LIST":[(0,10,20,30,40,50,60,70,80,-1)],
            "*PART_CONTACT":[(0,-1),(0,10,20,30,40,50,60,70,80),(0,10,20,30,40,50,60,70,80)]}
        return temp[keyword]  

終わりに

  • 今回、記載しているのは接点だけですが、他のデータも上記の方法で同様にdatafarameとして保存できます。

追記

  • 上のプログラムでファイルの読み込みをしていましたが、どうにもMbatサイズになると読み込み速度が遅くて辛いため。速度向上のために一部関数(__Separate_Ranges)を見直しました。
from itertools import chain
def __Separate_Ranges(self,Lines,Keyword:str):
        
        BASE=pd.DataFrame(Lines,columns=["S"])
        keyMatch=BASE.query("S.str.startswith(@Keyword)").index
        Mark_row=BASE.query('S.str.startswith("*")').index
        AA=[(i,Mark_row[Mark_row > i][0]) for i in keyMatch]
        
        indextemp=list(chain.from_iterable([np.arange(s,e) for s,e in AA]))
        valuetemp=list(chain.from_iterable([Lines[s:e] for s,e in AA]))
        
        return indextemp,valuetemp
  • この見直しで大体40Mbatくらいのファイルなら3分くらい(まだまだ遅いですね(-_-;))で読み込めます

Discussion