2023年第3回デジラボ(後半)シェルのたわみを最小にするブレース・面材配置最適化を体験しよう
こんにちは.2023年度第3回のデジラボを担当する北九州市立大学藤田研究室所属の,M2清水,M2新田,B4小林です.
今回は,「シェルのたわみを最小にするブレース面材配置最適化」というテーマのもと話を進めていきます.
前半ではシェルのたわみを最小にするブレース・面材配置の実験を行い、各班で鉛直方向の変形を計測し、考察しました。後半ではOpensees for Grasshopperを使用して構造解析を行い、最適化プラグインの一つであるWallaceiを使用して最適化を行います。
概要
前半で作成したアクリル板を使用したシェルの載荷実験を,Grasshopperを使用してモデリングし,藤田慎之輔先生が開発した,有限要素法に基づく構造解析ソフトウェアであるOpenSeesをRhinocerosとGrasshopperという3Dモデリング及びパラメトリックデザインツールと連携させるプラグインである,「Opensees for Grasshopper 」を使用して構造解析をします.その後,Grasshopperの最適化プラグインであるWallaceiを使用して最適化を実施します.
解析モデル作成
早速モデル作成の説明にいきたいと思います。
①制御点を作成
import ghpythonlib.components as gh
P=[]#シェルの座標を格納
x=X/(n-1)#スパン/分割数=一つのグリッドの長さ
y=Y/(n-1)
for i in range(n):
p=[]
for j in range(n):
z=0
if (i==0 or i==4 or j==4 or j==0):
if (i==1 or i==3 or j==1 or j==3):
z=z1
elif (i==2 or j==2):
z=z2
elif (i==1 or i==3) and (j==1 or j==3):
z=z3
elif (i==2 and j==2):
z=z4
else:
z=z5
p.append(gh.ConstructPoint(x*i,y*j,z))
print(p)
P.append(p)
制御点は制御点ネット分割数を4、X,Y方向のスパンを0.5mとして作成していきます。今回はシェルに対象性を持たせるために制御点のz座標をz1~z5に区分しています。
また、出力がGh-Pythonの多重リストなってしまい、そのままだとGrasshopper側で認識ができないため、Setsタブ内のListグループにある「Convert list and tree」コンポーネントを使ってTreeに変換します。
➁制御点をもとに節点を作成
Pを制御点として引数にとり、節点を作成します。
マトリクス形式になった点群データを、Openseesタブ内のUtilityグループ内にある「Rational B-spline Surface」コンポーネントのPにつなぎます。U,Vには曲面上の点の出力数を指定しますが、ここでは8としています。DにはBスプライン曲面の次数を入力しますが、入力を省略した場合のデフォルト値は3です。
構造解析では、曲面は複数の面(有限要素)に分割して解析を行いますが、その要素の形状はなるべく均一であることが望ましいです。先ほどの状態では、制御点数(5)-1≠次数D(3)となっているため、曲面上の点のx,y座標は均等出力されません。なので、制御点数-1=次数Dとなるようにします。
③節点をもとにブレース面材を配置
import rhinoscriptsyntax as rs
import Rhino.Geometry as rg
from clr import AddReference as addr
addr("Grasshopper")
from System import Object
from Grasshopper import DataTree
from Grasshopper.Kernel.Data import GH_Path
import ghpythonlib.components as gc
def dataTreeToList(aTree):
theList = []
for i in range(aTree.BranchCount ):
thisListPart = []
thisBranch = aTree.Branch(i)
for j in range(len(thisBranch)):
thisListPart.append( thisBranch[j] )
theList.append(thisListPart)
return theList
P=dataTreeToList(P)#TreeをPythonで扱うためにListに変換
blace1,blace2=[],[]
pitchx=X/divx #X方向の点群のピッチ
pitchy=Y/divy #Y方向の点群のピッチ
#面を作成
shell=[]
N=len(P)-2#全体の格子点最大値
n=len(x)#1/4領域の開口部数
for i in range(n):#X軸について対称コピー
x.append(N-x[i])
y.append(y[i])
n=len(x)#1/2領域の開口部数
for i in range(n):#Y軸について対称コピー
x.append(x[i])
y.append(N-y[i])
for j in range(len(P)-1):
for i in range(len(P)-1):
flag=0#開口部なら1以上,そうでなければ0となる判定用整数
for k in range(len(x)):
if i==x[k] and j==y[k]:flag+=1
if flag==0:
p1=rs.coerce3dpoint(P[i][j])#GuidをPoint3dに変換
p2=rs.coerce3dpoint(P[i+1][j])#GuidをPoint3dに変換
p3=rs.coerce3dpoint(P[i+1][j+1])#GuidをPoint3dに変換
p4=rs.coerce3dpoint(P[i][j+1])#GuidをPoint3dに変換
shell.append(gc.x4PointSurface(p1,p2,p3,p4))#4点から面を作成
#ブレース
for j in range(len(P[0])-1):
for i in range(len(P[0])-1):
p1=rs.coerce3dpoint(P[i][j])
p2=rs.coerce3dpoint(P[i+1][j+1])
blace1.append(gc.Line(p1, p2))
print(p1)
for j in range(len(P[0])):
for i in range(len(P[0])-1):
if 1<=j:
p1=rs.coerce3dpoint(P[i][j])
p2=rs.coerce3dpoint(P[i+1][j-1])
blace2.append(gc.Line(p1, p2))
#骨
beam=[]
for i in range(len(P[0])):
for j in range(len(P[0])-1):
p1=rs.coerce3dpoint(P[i][j])
p2=rs.coerce3dpoint(P[i][j+1])
beam.append(gc.Line(p1, p2))
for i in range(len(P[0])-1):
for j in range(len(P[0])):
p1=rs.coerce3dpoint(P[i][j])
p2=rs.coerce3dpoint(P[i+1][j])
beam.append(gc.Line(p1, p2))
ここでは節点をもとに全てのブレース,面材を配置します。
④0-4変数で部材を選択(コピペ推奨)
surf,blace,sec=[],[],[]
for i in range(len(num)):
if i==0:
if num[i]==1:
surf.append(shell[0])
surf.append(shell[div-2])
surf.append(shell[(div-2)*(div-1)])
surf.append(shell[(div-2)*(div)])
elif num[i]==2:
blace.append(blace2[0])
blace.append(blace1[div-2])
blace.append(blace1[(div-2)*(div-1)])
blace.append(blace2[(div-2)*(div)])
elif num[i]==3:
blace.append(blace1[0])
blace.append(blace2[div-2])
blace.append(blace2[(div-2)*(div-1)])
blace.append(blace1[(div-2)*(div)])
elif num[i]==4:
blace.append(blace2[0])
blace.append(blace2[div-2])
blace.append(blace2[(div-2)*(div-1)])
blace.append(blace2[(div-2)*(div)])
blace.append(blace1[0])
blace.append(blace1[div-2])
blace.append(blace1[(div-2)*(div-1)])
blace.append(blace1[(div-2)*(div)])
elif i==1:
print(i)
if num[i]==1:
surf.append(shell[1])
surf.append(shell[div-3])
surf.append(shell[(div-2)*(div-1)+1])
surf.append(shell[(div-2)*(div)-1])
elif num[i]==2:
blace.append(blace1[1])
blace.append(blace2[div-3])
blace.append(blace2[(div-2)*(div-1)+1])
blace.append(blace1[(div-2)*(div)-1])
elif num[i]==3:
blace.append(blace2[1])
blace.append(blace1[div-3])
blace.append(blace1[(div-2)*(div-1)+1])
blace.append(blace2[(div-2)*(div)-1])
elif num[i]==4:
blace.append(blace1[1])
blace.append(blace1[div-3])
blace.append(blace1[(div-2)*(div-1)+1])
blace.append(blace1[(div-2)*(div)-1])
blace.append(blace2[1])
blace.append(blace2[div-3])
blace.append(blace2[(div-2)*(div-1)+1])
blace.append(blace2[(div-2)*(div)-1])
elif i==2:
print(i)
if num[i]==1:
surf.append(shell[div-1])
surf.append(shell[2*div-3])
surf.append(shell[(div-2)*(div-1)-1])
surf.append(shell[(div-2)*(div-2)-1])
elif num[i]==2:
blace.append(blace2[div-1])
blace.append(blace1[2*div-3])
blace.append(blace2[(div-2)*(div-1)-1])
blace.append(blace1[(div-2)*(div-2)-1])
elif num[i]==3:
blace.append(blace1[div-1])
blace.append(blace2[2*div-3])
blace.append(blace1[(div-2)*(div-1)-1])
blace.append(blace2[(div-2)*(div-2)-1])
elif num[i]==4:
blace.append(blace1[div-1])
blace.append(blace1[2*div-3])
blace.append(blace1[(div-2)*(div-1)-1])
blace.append(blace1[(div-2)*(div-2)-1])
blace.append(blace2[div-1])
blace.append(blace2[2*div-3])
blace.append(blace2[(div-2)*(div-1)-1])
blace.append(blace2[(div-2)*(div-2)-1])
elif i==3:
print(i)
if num[i]==1:
surf.append(shell[div+4])
surf.append(shell[div])
surf.append(shell[5*(div-1)+1])
surf.append(shell[5*(div-1)+5])
elif num[i]==2:
blace.append(blace2[div+4])
blace.append(blace1[div])
blace.append(blace2[5*(div-1)+1])
blace.append(blace1[5*(div-1)+5])
elif num[i]==3:
blace.append(blace1[div+4])
blace.append(blace2[div])
blace.append(blace1[5*(div-1)+1])
blace.append(blace2[5*(div-1)+5])
elif num[i]==4:
blace.append(blace1[div+4])
blace.append(blace1[div])
blace.append(blace1[5*(div-1)+1])
blace.append(blace1[5*(div-1)+5])
blace.append(blace2[div+4])
blace.append(blace2[div])
blace.append(blace2[5*(div-1)+1])
blace.append(blace2[5*(div-1)+5])
elif i==4:
print(i)
if num[i]==1:
surf.append(shell[2])
surf.append(shell[4])
surf.append(shell[6*(div-1)+2])
surf.append(shell[6*(div-1)+4])
elif num[i]==2:
blace.append(blace2[2])
blace.append(blace1[4])
blace.append(blace1[6*(div-1)+2])
blace.append(blace2[6*(div-1)+4])
elif num[i]==3:
blace.append(blace1[2])
blace.append(blace2[4])
blace.append(blace2[6*(div-1)+2])
blace.append(blace1[6*(div-1)+4])
elif num[i]==4:
blace.append(blace1[2])
blace.append(blace2[4])
blace.append(blace2[6*(div-1)+2])
blace.append(blace1[6*(div-1)+4])
blace.append(blace2[2])
blace.append(blace1[4])
blace.append(blace1[6*(div-1)+2])
blace.append(blace2[6*(div-1)+4])
elif i==5:
print(i)
if num[i]==1:
surf.append(shell[2*(div-1)])
surf.append(shell[2*(div-1)+6])
surf.append(shell[4*(div-1)])
surf.append(shell[4*(div-1)+6])
elif num[i]==2:
blace.append(blace2[2*(div-1)])
blace.append(blace1[2*(div-1)+6])
blace.append(blace1[4*(div-1)])
blace.append(blace2[4*(div-1)+6])
elif num[i]==3:
blace.append(blace1[2*(div-1)])
blace.append(blace2[2*(div-1)+6])
blace.append(blace2[4*(div-1)])
blace.append(blace1[4*(div-1)+6])
elif num[i]==4:
blace.append(blace2[2*(div-1)])
blace.append(blace1[2*(div-1)+6])
blace.append(blace1[4*(div-1)])
blace.append(blace2[4*(div-1)+6])
blace.append(blace1[2*(div-1)])
blace.append(blace2[2*(div-1)+6])
blace.append(blace2[4*(div-1)])
blace.append(blace1[4*(div-1)+6])
elif i==6:
print(i)
if num[i]==1:
surf.append(shell[2*(div-1)+1])
surf.append(shell[2*(div-1)+5])
surf.append(shell[4*(div-1)+1])
surf.append(shell[4*(div-1)+5])
elif num[i]==2:
blace.append(blace2[2*(div-1)+1])
blace.append(blace1[2*(div-1)+5])
blace.append(blace1[4*(div-1)+1])
blace.append(blace2[4*(div-1)+5])
elif num[i]==3:
blace.append(blace1[2*(div-1)+1])
blace.append(blace2[2*(div-1)+5])
blace.append(blace2[4*(div-1)+1])
blace.append(blace1[4*(div-1)+5])
elif num[i]==4:
blace.append(blace2[2*(div-1)+1])
blace.append(blace1[2*(div-1)+5])
blace.append(blace1[4*(div-1)+1])
blace.append(blace2[4*(div-1)+5])
blace.append(blace1[2*(div-1)+1])
blace.append(blace2[2*(div-1)+5])
blace.append(blace2[4*(div-1)+1])
blace.append(blace1[4*(div-1)+5])
elif i==7:
print(i)
if num[i]==1:
surf.append(shell[2*(div-1)+2])
surf.append(shell[2*(div-1)+4])
surf.append(shell[4*(div-1)+2])
surf.append(shell[4*(div-1)+4])
elif num[i]==2:
blace.append(blace2[2*(div-1)+2])
blace.append(blace1[2*(div-1)+4])
blace.append(blace1[4*(div-1)+2])
blace.append(blace2[4*(div-1)+4])
elif num[i]==3:
blace.append(blace1[2*(div-1)+2])
blace.append(blace2[2*(div-1)+4])
blace.append(blace2[4*(div-1)+2])
blace.append(blace1[4*(div-1)+4])
elif num[i]==4:
blace.append(blace2[2*(div-1)+2])
blace.append(blace1[2*(div-1)+4])
blace.append(blace1[4*(div-1)+2])
blace.append(blace2[4*(div-1)+4])
blace.append(blace1[2*(div-1)+2])
blace.append(blace2[2*(div-1)+4])
blace.append(blace2[4*(div-1)+2])
blace.append(blace1[4*(div-1)+4])
elif i==8:
print(i)
if num[i]==1:
surf.append(shell[1*(div-1)+2])
surf.append(shell[1*(div-1)+4])
surf.append(shell[5*(div-1)+2])
surf.append(shell[5*(div-1)+4])
elif num[i]==2:
blace.append(blace2[1*(div-1)+2])
blace.append(blace1[1*(div-1)+4])
blace.append(blace1[5*(div-1)+2])
blace.append(blace2[5*(div-1)+4])
elif num[i]==3:
blace.append(blace1[1*(div-1)+2])
blace.append(blace2[1*(div-1)+4])
blace.append(blace2[5*(div-1)+2])
blace.append(blace1[5*(div-1)+4])
elif num[i]==4:
blace.append(blace2[1*(div-1)+2])
blace.append(blace1[1*(div-1)+4])
blace.append(blace1[5*(div-1)+2])
blace.append(blace2[5*(div-1)+4])
blace.append(blace1[1*(div-1)+2])
blace.append(blace2[1*(div-1)+4])
blace.append(blace2[5*(div-1)+2])
blace.append(blace1[5*(div-1)+4])
elif i==9:
print(i)
if num[i]==1:
surf.append(shell[3])
surf.append(shell[3*(div-1)])
surf.append(shell[4*(div-1)-1])
surf.append(shell[6*(div-1)+3])
elif num[i]==2:
blace.append(blace2[3])
blace.append(blace1[3*(div-1)])
blace.append(blace1[4*(div-1)-1])
blace.append(blace2[6*(div-1)+3])
blace.append(blace1[3])
blace.append(blace2[3*(div-1)])
blace.append(blace2[4*(div-1)-1])
blace.append(blace1[6*(div-1)+3])
elif i==10:
print(i)
if num[i]==1:
surf.append(shell[1*(div-1)+3])
surf.append(shell[3*(div-1)+1])
surf.append(shell[4*(div-1)-2])
surf.append(shell[5*(div-1)+3])
elif num[i]==2:
blace.append(blace1[1*(div-1)+3])
blace.append(blace1[3*(div-1)+1])
blace.append(blace1[4*(div-1)-2])
blace.append(blace1[5*(div-1)+3])
blace.append(blace2[1*(div-1)+3])
blace.append(blace2[3*(div-1)+1])
blace.append(blace2[4*(div-1)-2])
blace.append(blace2[5*(div-1)+3])
elif i==11:
print(i)
if num[i]==1:
surf.append(shell[2*(div-1)+3])
surf.append(shell[3*(div-1)+2])
surf.append(shell[4*(div-1)-3])
surf.append(shell[4*(div-1)+3])
elif num[i]==2:
blace.append(blace1[2*(div-1)+3])
blace.append(blace1[3*(div-1)+2])
blace.append(blace1[4*(div-1)-3])
blace.append(blace1[4*(div-1)+3])
blace.append(blace2[2*(div-1)+3])
blace.append(blace2[3*(div-1)+2])
blace.append(blace2[4*(div-1)-3])
blace.append(blace2[4*(div-1)+3])
else:
surf.append(shell[24])
thick = []
for i in range(len(surf)):
thick.append(0.0005)
for i in range(len(num)):
if num[i]==1 or i==24:
surf.append(shell[i])
elif num[i]==2:
blace.append(blace1[i])
elif num[i]==3:
blace.append(blace2[i])
elif num[i]==4:
blace.append(blace1[i])
blace.append(blace2[i])
thick = []
for i in range(len(surf)):
thick.append(0.0005)
⑤Openseesを用いた弾性範囲での構造解析
以上で作成した解析モデルをOpenSees for Grasshopperで解析可能なモデルにするために,以下の種々の条件を設定して,コンポーネントを組んでいきます.
部材断面・材料特性を設定する.今回使用する材料は,アクリル板を使用します.アクリル板の弾性係数:2.4×10⁶[kN/m²],ポアソン比:0.3,密度:1400[kN/m³]とし,断面は幅1.5mm,厚み10mmに設定します.
境界条件は,下の画像のようにスタイロに穴をあけてはめ込んでいるので,x,y,z方向のみ支持して,回転は許容するためピン支持としている.
荷重は,頂点に載荷実験で使用したものと同様の荷重を設定します.実験では,315gを頂部の面に載荷したので,解析用の荷重に整理すると0.06[kN/m²]となります.
⑥Wallaceiを用いて最適化
いよいよ最適化をしてきます。
多目的最適化やWallaceiについての説明は過去のデジラボで詳しく説明されていたので、こちらをご覧ください。
以下は定式化です.最適化の目的関数はブレースの金額とシェルのたわみとし,これら二つの目的を最小化する最適化問題を考えます.制約条件は,たわみ(δ)の上限値としてδ≦3cmであることと,ブレースと面材の接合部数は一つのピンに対して付与するブレース・面材などの要素数(P)が3つ以内が限界であるため,P≦3とする.(※実際には,Wallaceiは制約を指定することができないため,ペナルティとして目的関数に大きな値を加えることで,自動的に排除されるように設定している).ブレースの数をm,面の数をnとすると,
Minimize δ
15m+56n
subject to δ ≦ 3
P ≦ 3
最適化結果
以上の条件で最適化を回していきます.最適化の方は以下の動画を参照してみるとわかりやすいと思います.
https://www.youtube.com/watch?v=uU1cESSD3Gc
以上をもって,「2023年第3回デジラボ(後半)シェルのたわみを最小にするブレース・面材配置最適化を体験しよう」を終了します.
構造解析用Rh/Gh
参考文献
Shape optimization of shell structures with pinned support at 4 corners (notion.site)
2020年度第4回デジラボ:Grasshopperのコンポーネント"Wallacei"|fujitalab (note.com)