draw_unit_cells.py ダウンロード/コピー

draw_unit_cells.py をダウンロード

draw_unit_cells.py
draw_unit_cells.py
  1"""
  2単位格子および超格子を描画するためのスクリプト。
  3
  4概要:
  5    結晶学における単位格子(unit cell)や、その繰り返しからなる超格子(supercell)を3Dで可視化します。
  6
  7詳細説明:
  8    格子定数と角度を入力として、3D空間における単位格子を構築し、指定された数だけ繰り返して描画します。
  9    基本ベクトルを計算し、平行六面体を積み重ねることで超格子の形状を表現します。
 10
 11関連リンク: draw_unit_cells_usage
 12"""
 13
 14import numpy as np
 15import matplotlib.pyplot as plt
 16from mpl_toolkits.mplot3d import Axes3D
 17
 18#===================
 19# デフォルト設定
 20#===================
 21ELEV_DEF = 5.90
 22AZIM_DEF = -67.55
 23AXIS_FONT_SIZE_DEF = 24
 24DRAW_LATTICE_VECTORS_DEF = False
 25
 26def lattice_vectors(a, b, c, alpha, beta, gamma):
 27    """
 28    概要:
 29        結晶の格子定数と角度から基本格子ベクトルを計算します。
 30
 31    詳細説明:
 32        直交座標系における3つの基本格子ベクトル va, vb, vc を導出します。
 33        va はX軸に沿い、vb はXY平面にあり、vc は3次元空間に配置されるように定義されます。
 34
 35    引数:
 36        :param a: 格子定数aの長さ。
 37        :type a: float
 38        :param b: 格子定数bの長さ。
 39        :type b: float
 40        :param c: 格子定数cの長さ。
 41        :type c: float
 42        :param alpha: 角度α(度)。vb と vc のなす角。
 43        :type alpha: float
 44        :param beta: 角度β(度)。va と vc のなす角。
 45        :type beta: float
 46        :param gamma: 角度γ(度)。va と vb のなす角。
 47        :type gamma: float
 48    戻り値:
 49        :returns: 3つの基本格子ベクトル va, vb, vc。
 50        :rtype: tuple[numpy.ndarray, numpy.ndarray, numpy.ndarray]
 51    """
 52    alpha_r, beta_r, gamma_r = np.radians([alpha, beta, gamma])
 53    va = np.array([a, 0, 0])
 54    vb = np.array([b * np.cos(gamma_r), b * np.sin(gamma_r), 0])
 55    cx = c * np.cos(beta_r)
 56    cy = c * (np.cos(alpha_r) - np.cos(beta_r) * np.cos(gamma_r)) / np.sin(gamma_r)
 57    cz = np.sqrt(max(0, c**2 - cx**2 - cy**2))
 58    vc = np.array([cx, cy, cz])
 59    return va, vb, vc
 60
 61def set_equal_aspect(ax, points):
 62    """
 63    概要:
 64        Matplotlib 3Dプロットのアスペクト比を全ての軸で等しく設定します。
 65
 66    引数:
 67        :param ax: 3DプロットのAxesオブジェクト。
 68        :type ax: matplotlib.axes.Axes
 69        :param points: 表示範囲を決定するための点群。
 70        :type points: numpy.ndarray
 71    戻り値:
 72        :returns: None
 73        :rtype: None
 74    """
 75    xlim = [np.min(points[:,0]), np.max(points[:,0])]
 76    ylim = [np.min(points[:,1]), np.max(points[:,1])]
 77    zlim = [np.min(points[:,2]), np.max(points[:,2])]
 78    max_range = max(
 79        xlim[1] - xlim[0],
 80        ylim[1] - ylim[0],
 81        zlim[1] - zlim[0]
 82    ) / 2
 83
 84    mid_x = np.mean(xlim)
 85    mid_y = np.mean(ylim)
 86    mid_z = np.mean(zlim)
 87
 88    ax.set_xlim(mid_x - max_range, mid_x + max_range)
 89    ax.set_ylim(mid_y - max_range, mid_y + max_range)
 90    ax.set_zlim(mid_z - max_range, mid_z + max_range)
 91    ax.set_box_aspect([1,1,1])
 92
 93def draw_cell(ax, va, vb, vc, origin, linewidth=0.5):
 94    """
 95    概要:
 96        指定された基本格子ベクトルと原点に基づいて単一の単位格子を描画します。
 97
 98    引数:
 99        :param ax: 3DプロットのAxesオブジェクト。
100        :type ax: matplotlib.axes.Axes
101        :param va: 第1基本格子ベクトル。
102        :type va: numpy.ndarray
103        :param vb: 第2基本格子ベクトル。
104        :type vb: numpy.ndarray
105        :param vc: 第3基本格子ベクトル。
106        :type vc: numpy.ndarray
107        :param origin: 描画開始点。
108        :type origin: numpy.ndarray
109        :param linewidth: 線の太さ。
110        :type linewidth: float
111    戻り値:
112        :returns: None
113        :rtype: None
114    """
115    p0 = origin
116    p1 = origin + va
117    p2 = origin + vb
118    p3 = origin + vc
119    p4 = origin + va + vb
120    p5 = origin + va + vc
121    p6 = origin + vb + vc
122    p7 = origin + va + vb + vc
123    verts = [p0, p1, p2, p3, p4, p5, p6, p7]
124
125    edges = [
126        (0,1), (0,2), (0,3),
127        (1,4), (1,5),
128        (2,4), (2,6),
129        (3,5), (3,6),
130        (4,7), (5,7), (6,7)
131    ]
132    for i,j in edges:
133        ax.plot(*zip(verts[i], verts[j]), color='black', linewidth=linewidth)
134
135def draw_supercell_plot(a, b, c, alpha, beta, gamma, nx=3, ny=3, nz=3, 
136                        elev=ELEV_DEF, azim=AZIM_DEF, 
137                        draw_lattice_vectors=DRAW_LATTICE_VECTORS_DEF, 
138                        font_size=AXIS_FONT_SIZE_DEF):
139    """
140    概要:
141        指定された格子定数と角度に基づき、超格子を描画し、画像として保存して表示します。
142
143    引数:
144        :param a: 格子定数aの長さ。
145        :type a: float
146        :param b: 格子定数bの長さ。
147        :type b: float
148        :param c: 格子定数cの長さ。
149        :type c: float
150        :param alpha: 角度α(度)。
151        :type alpha: float
152        :param beta: 角度β(度)。
153        :type beta: float
154        :param gamma: 角度γ(度)。
155        :type gamma: float
156        :param nx: X方向の単位格子の繰り返し数。
157        :type nx: int
158        :param ny: Y方向の単位格子の繰り返し数。
159        :type ny: int
160        :param nz: Z方向の単位格子の繰り返し数。
161        :type nz: int
162        :param elev: 3Dプロットの視点仰角。
163        :type elev: float
164        :param azim: 3Dプロットの視点方位角。
165        :type azim: float
166        :param draw_lattice_vectors: 基本格子ベクトルを描画するかどうかのフラグ。
167        :type draw_lattice_vectors: bool
168        :param font_size: 軸ラベルのフォントサイズ。
169        :type font_size: int
170    戻り値:
171        :returns: None
172        :rtype: None
173    """
174    va, vb, vc = lattice_vectors(a, b, c, alpha, beta, gamma)
175    fig = plt.figure(figsize=(8,6))
176    ax = fig.add_subplot(111, projection='3d')
177    ax.view_init(elev=elev, azim=azim)
178
179    # 角度確認用のイベントハンドラ
180    def on_draw(event):
181        curr_elev = ax.elev
182        curr_azim = ax.azim
183        print(f"Current elev: {curr_elev:.2f}, azim: {curr_azim:.2f}")
184    fig.canvas.mpl_connect('draw_event', on_draw)
185
186    # 超格子の全セルの描画(細い黒線)
187    for i in range(nx):
188        for j in range(ny):
189            for k in range(nz):
190                offset = i * va + j * vb + k * vc
191                draw_cell(ax, va, vb, vc, offset, linewidth=0.5)
192
193    # 原点にあるセルの強調(太い黒線)
194    draw_cell(ax, va, vb, vc, np.array([0,0,0]), linewidth=2)
195
196    # 基本格子ベクトルの描画(オプション)
197    if draw_lattice_vectors:
198        for vec, label in zip([va, vb, vc], ['a', 'b', 'c']):
199            ax.quiver(0, 0, 0, *vec, color='blue', linewidth=2, arrow_length_ratio=0.1)
200            ax.text(*vec * 1.05, label, fontsize=font_size, color='blue')
201
202    # 不要な座標軸やグリッドを非表示にする
203    ax.set_xticks([]); ax.set_yticks([]); ax.set_zticks([])
204    ax.set_xticklabels([]); ax.set_yticklabels([]); ax.set_zticklabels([])
205    ax.set_xlabel(''); ax.set_ylabel(''); ax.set_zlabel('')
206    ax.grid(False)
207    ax.xaxis.pane.set_visible(False)
208    ax.yaxis.pane.set_visible(False)
209    ax.zaxis.pane.set_visible(False)
210    ax.xaxis.line.set_color((0,0,0,0))
211    ax.yaxis.line.set_color((0,0,0,0))
212    ax.zaxis.line.set_color((0,0,0,0))
213    ax.set_facecolor((1,1,1,0))
214
215    # 全格子点の計算と描画(小さな●)
216    all_points = []
217    for i in range(nx+1):
218        for j in range(ny+1):
219            for k in range(nz+1):
220                all_points.append(i*va + j*vb + k*vc)
221    all_points = np.array(all_points)
222    ax.scatter(all_points[:,0], all_points[:,1], all_points[:,2], color='black', s=20)
223
224    # アスペクト比の調整
225    set_equal_aspect(ax, all_points)
226
227    plt.tight_layout()
228    plt.savefig("unit_cells.png", dpi=300, bbox_inches='tight', transparent=True)
229    plt.show()
230
231def main():
232    """
233    概要:
234        メインの実行フロー。特定のパラメータで超格子を描画します。
235
236    戻り値:
237        :returns: None
238        :rtype: None
239    """
240    params = {
241        'a': 5.0, 'b': 5.5, 'c': 4.5,
242        'alpha': 80, 'beta': 70, 'gamma': 100,
243        'nx': 3, 'ny': 3, 'nz': 3
244    }
245    
246    print(f"Drawing supercell: {params['nx']}x{params['ny']}x{params['nz']}")
247    draw_supercell_plot(**params)
248
249if __name__ == "__main__":
250    main()