はじめに

こんな図をmatplotlibで作ってみました.「matplotlibでこんなことやっている人もいるんだ」という事例紹介です.このような説明図は作るのが面倒くさく時間がかかりますが,一回作っておくと色々使い回しができて便利です.また図全体を使わなくても,いくつか作例を持っていると,「こういう場合はこうする」というTipsとしても役に立ちます.

たわみ線,モーメント図,応力分布は一応理論通りに計算したものを描いています.
引数の指定が少し煩雑ですが,分布荷重(片矢印)を描くには,annotateが便利です.

Strip Method for slabs Circular hole model under vertical compressive stress field

作り方

下の図に示すように,方眼紙を作り,その中に必要事項を書き込んでいきます.昔なら定規と鉛筆で描いて切り貼りで原稿を作っていたものをmatplotlibにやらせて画像を作成しているだけです.描き終わったら方眼紙目盛りを消して画像化し,ImageMagickで余白処理をして完成です.

Strip Methodの図のプログラム

モジュール読み込み

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as tick

フォントサイズ,作図範囲の指定

fsz=20   # font size
xmin=-2
xmax=11
dx=1
ymin=-5
ymax=7
dy=1

グラフの全体像指定

iflagは方眼紙を作成するかのインデックス.
作業中はiflag=1として方眼紙を描画し,完成したと思ったらiflag=0としてaxis('off')にします.アスペクト比は1(equal)に指定しておきます.

fig=plt.figure(figsize=(8,8),facecolor='w')
plt.rcParams['font.size']=fsz
plt.rcParams['font.family']='sans-serif'

iflag=0
plt.xlim([xmin,xmax])
plt.ylim([ymin,ymax])
if iflag==0:
    plt.axis('off')
else:
    plt.xlabel('x_axis')
    plt.ylabel('y_axis')
    plt.xticks(np.arange(xmin,xmax+dx,dx))
    plt.yticks(np.arange(ymin,ymax+dy,dy))
    plt.grid(color='#999999',linestyle='solid')
plt.gca().set_aspect('equal',adjustable='box')

板の描画

fill(hatch='//')でハッチングし,その中に板の図を上書きします.板はfillにより白で塗りつぶし,edgeを描画します.またplotにより板の中心線(十字線)を描画します.

# draw plate including boundary condition (hatch)
lx=5; ly=4; dd=0.5
plt.fill([-dd,lx+dd,lx+dd,-dd],[-dd,-dd,ly+dd,ly+dd],fill=False, hatch='//',lw=0)
plt.fill([0,lx,lx,0],[0,0,ly,ly],facecolor='#ffffff',edgecolor='#000000',lw=3)
plt.plot([0,lx],[ly/2,ly/2],color='#000000',lw=1)
plt.plot([lx/2,lx/2],[0,ly],color='#000000',lw=1)

たわみ線の描画

一応両端固定梁のたわみ式を入力.たわみを計算する点xx,yyはnumpy.linspaceでベクトルとして指定します.これをたわみ式に代入すればたわみのベクトルが得られます.ここがPythonの便利なところの一つですね.

# draw deflection curve
p=1; E=1; I=1
xx=np.linspace(0,lx,21)
y=p*lx**4/24/E/I*((xx/lx)**2-2*(xx/lx)**3+(xx/lx)**4)
y=-y/np.max(y)*0.5+ly/2
plt.plot(xx,y,color='#000000',lw=1)
yy=np.linspace(0,ly,21)
x=p*ly**4/24/E/I*((yy/ly)**2-2*(yy/ly)**3+(yy/ly)**4)
x=x/np.max(x)*0.5+lx/2
plt.plot(x,yy,color='#000000',lw=1)
plt.text(4,1.4,r'$\delta_x$',va='center',ha='center',fontsize=fsz,rotation=0)
plt.text(3.3,3,r'$\delta_y$',va='center',ha='center',fontsize=fsz,rotation=90)

モーメント図描画

一応両端固定梁のモーメントの式を入力.モーメントを計算する点は,前述たわみを計算した点と同じxxおよびyyです.

# draw moment diagram
y0=-3; x0=8.5
mx=p*lx**2/2*(-1/6+xx/lx-xx**2/lx**2)
my=p*ly**2/2*(-1/6+yy/ly-yy**2/ly**2)
mx=mx/np.min(mx)*1.0+y0
my=-my/np.min(my)*1.0+x0
plt.plot(xx,mx,color='#000000',lw=1)
plt.fill_between(xx,mx,y0,color='#aaaaaa')
plt.plot([0,0,lx,lx],[y0+1,y0,y0,y0+1],color='#000000',lw=1)
plt.plot(my,yy,color='#000000',lw=1)
plt.fill_betweenx(yy,my,x0,color='#aaaaaa')
plt.plot([x0-1,x0,x0,x0-1],[0,0,ly,ly],color='#000000',lw=1)

寸法線の描画

annotateで両矢印の寸法線を描画します.両矢印なので,変数arstにarrowpropsのarrowstyleを格納します.また矢印を目標点にピッチリ合わせたいので,shrinkA=0,shrinkB=0です. ここで現時点でどうしてもわからないのが, 両矢印の線の太さの制御方法です . 矢の部分はhead_width,head_lengthで指定できるのですけどね.

(追記)

矢印の線の太さの制御方法を見つけました。arrowprops=dict(..., lw=0.5) で良いようです。、

arrowprops=dict(arrowstyle=arst,connectionstyle='arc3',facecolor=col,edgecolor=col,shrinkA=0,shrinkB=0,lw=0.5))

https://stackoverflow.com/questions/44489900/arrow-properties-in-matplotlib-annotate

# draw dimension lines
col='#333333'
arst='<->,head_width=3,head_length=10'
ss=r'$l_x$'
x1=0; y1=ly+1; x2=lx; y2=y1; xs=lx/2; ys=y1+0.5; rt=0
plt.annotate('',
    xy=(x1,y1), xycoords='data',
    xytext=(x2,y2), textcoords='data', fontsize=0,
    arrowprops=dict(arrowstyle=arst,connectionstyle='arc3',facecolor=col,edgecolor=col,shrinkA=0,shrinkB=0))
plt.text(xs,ys,ss,ha='center',va='center',rotation=rt)
ss=r'$l_y$'
x1=-1; y1=0; x2=x1; y2=ly; xs=x1-0.5; ys=ly/2; rt=90
plt.annotate('',
    xy=(x1,y1), xycoords='data',
    xytext=(x2,y2), textcoords='data', fontsize=0,
    arrowprops=dict(arrowstyle=arst,connectionstyle='arc3',facecolor=col,edgecolor=col,shrinkA=0,shrinkB=0))
plt.text(xs,ys,ss,ha='center',va='center',rotation=rt)

分布荷重の描画

annotateの片矢印で分布荷重を描画します.矢印を描画する箇所は,水平方向が0から5まで0.5ピッチ,y方向が0から4まで0.5ピッチで,numpy.arangeで指定しています.片矢印なのでarrowpropsのarrowstyleは指定せず,arrowpropsの中に個別にshrink,width,headwidth,headlengthを指定します.

# draw loads
col='#000000'
xx1=np.arange(0,5+0.5,0.5)
wx=0.5
for x1 in xx1:
    x2=x1;y1=y0+1.5; y2=y1+wx; sv=0
    plt.annotate('',
        xy=(x1,y1), xycoords='data',
        xytext=(x2,y2), textcoords='data', fontsize=0,
        arrowprops=dict(shrink=sv,width=1,headwidth=5,headlength=8,
                        connectionstyle='arc3',facecolor=col,edgecolor=col))
wy=0.8
yy1=np.arange(0,4+0.5,0.5)
for y1 in yy1:
    x1=x0-1.5; x2=x1-wy; y2=y1; sv=0
    plt.annotate('',
        xy=(x1,y1), xycoords='data',
        xytext=(x2,y2), textcoords='data', fontsize=0,
        arrowprops=dict(shrink=sv,width=1,headwidth=5,headlength=8,
                        connectionstyle='arc3',facecolor=col,edgecolor=col))

その他情報の描画

textで情報を描画します.

# draw text information
ss=r'$w_x$'; xs=lx/2  ; ys=y0+1.2; rt=0 ; plt.text(xs,ys,ss,ha='center',va='center',rotation=rt)
ss=r'$w_y$'; xs=x0-1.2; ys=ly/2  ; rt=90; plt.text(xs,ys,ss,ha='center',va='center',rotation=rt)

ss='Moment $M_x$'; xs=lx/2  ; ys=y0+0.4;rt=0 ; plt.text(xs,ys,ss,ha='center',va='center',rotation=rt,fontsize=fsz-2)
ss='Moment $M_y$'; xs=x0-0.4; ys=ly/2  ;rt=90; plt.text(xs,ys,ss,ha='center',va='center',rotation=rt,fontsize=fsz-2)

ss=r'$-\frac{w_x\cdot l_x{}^2}{12}$'; xs=lx; ys=y0+1.1;rt=0
plt.text(xs,ys,ss,ha='left',va='center',rotation=rt)
ss=r'$\frac{w_x\cdot l_x{}^2}{24}$'; xs=lx/2; ys=y0-1.0;rt=0
plt.text(xs,ys,ss,ha='center',va='center',rotation=rt)

ss=r'$-\frac{w_y\cdot l_y{}^2}{12}$'; xs=x0-1; ys=ly;rt=90
plt.text(xs,ys,ss,ha='center',va='bottom',rotation=rt)
ss=r'$\frac{w_y\cdot l_y{}^2}{24}$'; xs=x0+1.0; ys=ly/2;rt=90
plt.text(xs,ys,ss,ha='center',va='center',rotation=rt)

ss='Fixed'
xs=lx/4; ys=ly-0.05; rt=0; plt.text(xs,ys,ss,ha='center',va='top',rotation=rt,fontsize=fsz-2)
xs=lx/4; ys=0+0.05; rt=0; plt.text(xs,ys,ss,ha='center',va='bottom',rotation=rt,fontsize=fsz-2)
xs=0+0.05; ys=ly/4*3; rt=90; plt.text(xs,ys,ss,ha='left',va='center',rotation=rt,fontsize=fsz-2)
xs=lx-0.05; ys=ly/4*3; rt=90; plt.text(xs,ys,ss,ha='right',va='center',rotation=rt,fontsize=fsz-2)

ss='Strip method for All edges fixed rectangular plate\nunder uniformly distributed load $w$'
xs=0.5*(xmin+xmax); ys=ymax; rt=0
plt.text(xs,ys,ss,ha='center',va='top',rotation=rt,fontsize=fsz-2)

図の保存

plt.tight_layout()
fnameF='fig_strip.png'
plt.savefig(fnameF, dpi=200, bbox_inches="tight", pad_inches=0.1)
plt.show()
plt.close()

Circular Holeの図のプログラム

やり方は上の図と同じなので,プログラムだけを載せておきます.

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as tick

fsz=20
xmin=-2
xmax=6
dx=1
ymin=-4
ymax=7
dy=1

fig=plt.figure(figsize=(8,11),facecolor='w')
plt.rcParams['font.size']=fsz
plt.rcParams['font.family']='sans-serif'

iflag=0
plt.xlim([xmin,xmax])
plt.ylim([ymin,ymax])
if iflag==0:
    plt.axis('off')
else:
    plt.xlabel('x_axis')
    plt.ylabel('y_axis')
    plt.xticks(np.arange(xmin,xmax+dx,dx))
    plt.yticks(np.arange(ymin,ymax+dy,dy))
    plt.grid(color='#999999',linestyle='solid')
plt.gca().set_aspect('equal',adjustable='box')

# draw circular hole
r=1
theta=np.linspace(0,2*np.pi,360)
x=r*np.cos(theta)
y=r*np.sin(theta)
plt.fill(x,y,facecolor='#eeeeee',edgecolor='#000000',lw=2) # circular hole
arst='<->,head_width=3,head_length=10'
x1=0;y1=0;x2=1;y2=0
plt.annotate('',
    xy=(x1,y1), xycoords='data',
    xytext=(x2,y2), textcoords='data', fontsize=0,
    arrowprops=dict(arrowstyle=arst,connectionstyle='arc3',facecolor='#555555',edgecolor='#555555',shrinkA=0,shrinkB=0))
plt.text(0.5,-0.2,r'$r$',ha='center',va='center',rotation=0)
plt.text(-0.15,-0.15,r'$o$',ha='center',va='center',rotation=0)

# axis for sig_x including tick marks
ds=0.1;dds=0.1
plt.plot([0,0,2],[4,1,1],color='#000000',lw=1)
plt.hlines([2,3,4],-ds,ds,color='#000000',linestyle='solid')
plt.vlines([0.5,1,1.5,2],1-ds,1+ds,color='#000000',linestyle='solid')
plt.text(0,4+dds,r'$y/r$',va='bottom',ha='center',color='#000000')
plt.text(2+dds,1,r'$\sigma_x/q$',va='center',ha='left',color='#000000')

# axis for sig_y including tick marks
plt.plot([1,1,4],[-2,0,0],color='#000000',lw=1)
plt.hlines([-0.5,-1,-1.5,-2],1-ds,1+ds,color='#000000',linestyle='solid')
plt.vlines([2,3,4],-ds,ds,color='#000000',linestyle='solid')
plt.text(4+dds,0,r'$x/r$',va='center',ha='left',color='#000000')
plt.text(1,-2-dds,r'$\sigma_y/q$',va='top',ha='center',color='#000000')

# draw digit
for i in range(1,4):
    plt.text(-2.5*ds,1+float(i),str(i+1),va='center',ha='center',fontsize=fsz-3)
    plt.text(0.5*float(i),1-2.5*ds,str(i),va='center',ha='center',fontsize=fsz-3)
    plt.text(1+float(i),2.5*ds,str(i+1),va='center',ha='center',fontsize=fsz-3)
    plt.text(1-2.5*ds,-0.5*float(i),str(i),va='center',ha='center',fontsize=fsz-3)

# draw load
xx1=np.arange(-1.5,1.5+0.25,0.25)
col='#000000'; sv=0
sp=0.5
for x1 in xx1:
    x2=x1
    y1=-2.6; y2=y1-sp
    plt.annotate('',
        xy=(x1,y1), xycoords='data',
        xytext=(x2,y2), textcoords='data', fontsize=0,
        arrowprops=dict(shrink=sv,width=1,headwidth=5,headlength=8,
                        connectionstyle='arc3',facecolor=col,edgecolor=col))
    y1=4.6; y2=y1+sp
    plt.annotate('',
        xy=(x1,y1), xycoords='data',
        xytext=(x2,y2), textcoords='data', fontsize=0,
        arrowprops=dict(shrink=sv,width=1,headwidth=5,headlength=8,
                        connectionstyle='arc3',facecolor=col,edgecolor=col))
plt.text(1.75, 4.9,r'$q$ (compression)',va='center',ha='left',color='#000000')
plt.text(1.75,-2.9,r'$q$ (compression)',va='center',ha='left',color='#000000')

# draw stress distribution
tt=np.arange(1,4+0.1,0.1)
sigx=r**2/2/tt**2-3*r**4/2/tt**4
sigy=1+r**2/2/tt**2+3*r**4/2/tt**4
plt.fill_betweenx(tt,0,-0.5*sigx,facecolor='#cccccc')
plt.fill_between(tt,0,-0.5*sigy,facecolor='#cccccc')
plt.plot(-0.5*sigx,tt,color='#555555',lw=2)
plt.plot(tt,-0.5*sigy,color='#555555',lw=2)
plt.text(2.5,-0.3,'Compression',va='center',ha='center',color='#000000',fontsize=fsz-3)

# annotation
ss='Tension force\n$T=-0.1925\cdot r\cdot q$'
x1=0.2; y1=1.1; x2=x1+0.25; y2=y1+1
sv=0
plt.annotate(ss,
    xy=(x1,y1), xycoords='data',
    xytext=(x2,y2), textcoords='data', fontsize=fsz-3,
    arrowprops=dict(shrink=sv,width=0.3,headwidth=4,headlength=8,
                    connectionstyle='arc3',facecolor=col,edgecolor=col))
ss=r'$\sqrt{3}\cdot r$'
x1=0; y1=np.sqrt(3); x2=x1-0.5; y2=y1
sv=0
plt.annotate(ss,
    xy=(x1,y1), xycoords='data',
    xytext=(x2,y2), textcoords='data', fontsize=fsz-4,
    ha='right', va='center',
    arrowprops=dict(shrink=sv,width=0.3,headwidth=4,headlength=8,
                    connectionstyle='arc3',facecolor=col,edgecolor=col))

plt.tight_layout()
fnameF='fig_hole.png'
plt.savefig(fnameF, dpi=200, bbox_inches="tight", pad_inches=0.1)
plt.show()
plt.close()

ImageMagickによる余白処理

報告書に貼り込むため,ImageMagickで余白処理をして完成.
ここでは.trimで余白を削除した後,周囲に10pxの白色の余白を付与している.

convert fig_strip.png -trim -bordercolor white -border 10x10 fig_strip.png
convert fig_hole.png -trim -bordercolor white -border 10x10 fig_hole.png

以 上

Sign up for free and join this conversation.
Sign Up
If you already have a Qiita account log in.