結晶構造を記述する普遍的な記法を学ぶ
この章を学ぶことで、以下の知識とスキルを習得できます:
結晶学において、ミラー指数(Miller indices)は結晶内の面や方向を表す標準的な記法です。 1839年にイギリスの鉱物学者ウィリアム・ハロウズ・ミラー(William Hallowes Miller)によって提案されました。
結晶中には無限の平面と方向が存在します。これらを一意に、かつ簡潔に表現する方法が必要です:
ミラー指数 (hkl) は以下の手順で求められます:
これは(100)面の2倍の密度で並ぶ平面であることを意味します。
結晶軸との切片からミラー指数を自動計算するプログラム:
import numpy as np
from fractions import Fraction
def calculate_miller_indices(intercepts):
"""
切片からミラー指数を計算する
Parameters:
-----------
intercepts : tuple of float
(a軸切片, b軸切片, c軸切片)
無限大の場合は np.inf を使用
Returns:
--------
tuple : ミラー指数 (h, k, l)
"""
# 逆数を計算(無限大の逆数は0)
reciprocals = []
for intercept in intercepts:
if np.isinf(intercept):
reciprocals.append(0)
else:
reciprocals.append(1 / intercept)
# 分数として扱い、最小公倍数を見つける
fractions = [Fraction(r).limit_denominator(100) for r in reciprocals]
# 分母の最小公倍数を計算
denominators = [f.denominator for f in fractions]
lcm = np.lcm.reduce(denominators)
# 整数化
h, k, l = [int(f * lcm) for f in fractions]
# 最大公約数で簡約化
gcd = np.gcd.reduce([abs(h), abs(k), abs(l)])
if gcd > 0:
h, k, l = h // gcd, k // gcd, l // gcd
return (h, k, l)
# テスト例
print("=== ミラー指数の計算 ===\n")
# (111)面:すべての軸で切片が1
intercepts_111 = (1, 1, 1)
hkl = calculate_miller_indices(intercepts_111)
print(f"切片 {intercepts_111} → ミラー指数 {hkl}")
# (100)面:a軸のみで切片、他は平行
intercepts_100 = (1, np.inf, np.inf)
hkl = calculate_miller_indices(intercepts_100)
print(f"切片 {intercepts_100} → ミラー指数 {hkl}")
# (110)面:a軸とb軸で切片、c軸に平行
intercepts_110 = (1, 1, np.inf)
hkl = calculate_miller_indices(intercepts_110)
print(f"切片 {intercepts_110} → ミラー指数 {hkl}")
# (210)面:a軸で1/2、b軸で1の切片
intercepts_210 = (0.5, 1, np.inf)
hkl = calculate_miller_indices(intercepts_210)
print(f"切片 {intercepts_210} → ミラー指数 {hkl}")
# (123)面:異なる切片
intercepts_123 = (1, 0.5, 0.333333)
hkl = calculate_miller_indices(intercepts_123)
print(f"切片 {intercepts_123} → ミラー指数 {hkl}")
=== ミラー指数の計算 ===
切片 (1, 1, 1) → ミラー指数 (1, 1, 1)
切片 (1, inf, inf) → ミラー指数 (1, 0, 0)
切片 (1, 1, inf) → ミラー指数 (1, 1, 0)
切片 (0.5, 1, inf) → ミラー指数 (2, 1, 0)
切片 (1, 0.5, 0.333333) → ミラー指数 (1, 2, 3)
代表的な結晶面を可視化して、ミラー指数の意味を理解します:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
def plot_lattice_plane(hkl, ax, color='cyan', alpha=0.6):
"""
ミラー指数で指定された結晶面を描画
Parameters:
-----------
hkl : tuple
ミラー指数 (h, k, l)
ax : Axes3D
matplotlib の3D軸
color : str
面の色
alpha : float
透明度
"""
h, k, l = hkl
# 切片を計算(0の場合は大きな値に設定)
intercepts = []
for index in [h, k, l]:
if index == 0:
intercepts.append(10) # 無限大の代わりに大きな値
else:
intercepts.append(1 / index)
# 面を構成する頂点を計算
vertices = []
# ケースごとに頂点を設定
if h != 0 and k != 0 and l != 0:
# (111)タイプ:3つの切片を持つ
vertices = [
[intercepts[0], 0, 0],
[0, intercepts[1], 0],
[0, 0, intercepts[2]]
]
elif h != 0 and k != 0 and l == 0:
# (110)タイプ:2つの切片、c軸に平行
vertices = [
[intercepts[0], 0, 0],
[intercepts[0], 0, 2],
[0, intercepts[1], 2],
[0, intercepts[1], 0]
]
elif h != 0 and k == 0 and l == 0:
# (100)タイプ:1つの切片、他の軸に平行
vertices = [
[intercepts[0], 0, 0],
[intercepts[0], 2, 0],
[intercepts[0], 2, 2],
[intercepts[0], 0, 2]
]
# 面を描画
if len(vertices) > 0:
poly = Poly3DCollection([vertices], alpha=alpha, facecolor=color, edgecolor='black', linewidth=2)
ax.add_collection3d(poly)
def plot_crystal_axes():
"""立方晶の結晶軸と主要面を表示"""
fig = plt.figure(figsize=(15, 5))
planes = [
((1, 0, 0), 'cyan', '(100)面'),
((1, 1, 0), 'yellow', '(110)面'),
((1, 1, 1), 'magenta', '(111)面')
]
for idx, (hkl, color, title) in enumerate(planes):
ax = fig.add_subplot(1, 3, idx + 1, projection='3d')
# 結晶軸を描画
ax.quiver(0, 0, 0, 1.5, 0, 0, color='red', arrow_length_ratio=0.1, linewidth=2, label='a軸')
ax.quiver(0, 0, 0, 0, 1.5, 0, color='green', arrow_length_ratio=0.1, linewidth=2, label='b軸')
ax.quiver(0, 0, 0, 0, 0, 1.5, color='blue', arrow_length_ratio=0.1, linewidth=2, label='c軸')
# 結晶面を描画
plot_lattice_plane(hkl, ax, color=color, alpha=0.6)
# 軸ラベル
ax.set_xlabel('a', fontsize=12, fontweight='bold')
ax.set_ylabel('b', fontsize=12, fontweight='bold')
ax.set_zlabel('c', fontsize=12, fontweight='bold')
ax.set_xlim([0, 1.5])
ax.set_ylim([0, 1.5])
ax.set_zlim([0, 1.5])
ax.set_title(title, fontsize=14, fontweight='bold')
ax.legend(loc='upper left', fontsize=8)
# グリッドを追加
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('miller_indices_planes.png', dpi=150, bbox_inches='tight')
plt.show()
print("主要な結晶面の可視化を保存しました: miller_indices_planes.png")
# 実行
plot_crystal_axes()
ミラー指数 (hkl) で指定される結晶面の面間隔 dhklは、 X線回折実験で観測されるブラッグピークの位置を決定する重要なパラメータです。
立方晶(a = b = c、α = β = γ = 90°)では、面間隔は非常に簡潔な式で表されます:
ここで、aは格子定数、h, k, lはミラー指数です。
import numpy as np
import matplotlib.pyplot as plt
def cubic_d_spacing(a, h, k, l):
"""
立方晶系の面間隔を計算
Parameters:
-----------
a : float
格子定数 (Å)
h, k, l : int
ミラー指数
Returns:
--------
float : 面間隔 d_hkl (Å)
"""
return a / np.sqrt(h**2 + k**2 + l**2)
# シリコン(立方晶、a = 5.431 Å)での計算
a_Si = 5.431
print("=== シリコン(Si)の面間隔 ===")
print(f"格子定数 a = {a_Si} Å\n")
# 主要な面の面間隔を計算
planes = [
(1, 0, 0), (1, 1, 0), (1, 1, 1),
(2, 0, 0), (2, 2, 0), (3, 1, 1),
(2, 2, 2), (4, 0, 0), (3, 3, 1)
]
results = []
for hkl in planes:
h, k, l = hkl
d = cubic_d_spacing(a_Si, h, k, l)
results.append((hkl, d))
print(f"({h}{k}{l})面: d = {d:.4f} Å")
# グラフで可視化
fig, ax = plt.subplots(figsize=(12, 6))
hkl_labels = [f"({h}{k}{l})" for (h, k, l), d in results]
d_values = [d for hkl, d in results]
bars = ax.bar(range(len(results)), d_values, color='skyblue', edgecolor='navy', linewidth=1.5)
ax.set_xticks(range(len(results)))
ax.set_xticklabels(hkl_labels, rotation=45, ha='right')
ax.set_ylabel('面間隔 d (Å)', fontsize=12, fontweight='bold')
ax.set_xlabel('ミラー指数', fontsize=12, fontweight='bold')
ax.set_title('シリコン(Si)の結晶面ごとの面間隔', fontsize=14, fontweight='bold')
ax.grid(axis='y', alpha=0.3)
# 値をバーの上に表示
for i, (bar, d) in enumerate(zip(bars, d_values)):
ax.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 0.05,
f'{d:.3f}', ha='center', va='bottom', fontsize=9)
plt.tight_layout()
plt.savefig('si_d_spacing.png', dpi=150, bbox_inches='tight')
plt.show()
print("\nグラフを保存しました: si_d_spacing.png")
正方晶(a = b ≠ c、α = β = γ = 90°)では、c軸方向の格子定数が異なるため式が変わります:
def tetragonal_d_spacing(a, c, h, k, l):
"""
正方晶系の面間隔を計算
Parameters:
-----------
a : float
a軸(= b軸)の格子定数 (Å)
c : float
c軸の格子定数 (Å)
h, k, l : int
ミラー指数
Returns:
--------
float : 面間隔 d_hkl (Å)
"""
return 1 / np.sqrt((h**2 + k**2) / a**2 + l**2 / c**2)
# TiO2 ルチル(正方晶、a = 4.594 Å, c = 2.958 Å)
a_TiO2 = 4.594
c_TiO2 = 2.958
print("=== TiO2(ルチル)の面間隔 ===")
print(f"格子定数 a = {a_TiO2} Å, c = {c_TiO2} Å")
print(f"c/a 比 = {c_TiO2/a_TiO2:.3f}\n")
planes_tetragonal = [
(1, 0, 0), (0, 0, 1), (1, 1, 0),
(1, 0, 1), (1, 1, 1), (2, 0, 0),
(2, 1, 0), (2, 1, 1), (2, 2, 0)
]
# 比較:立方晶と仮定した場合(誤った計算)
print("正方晶として正しく計算した場合 vs 立方晶と誤って仮定した場合:\n")
for hkl in planes_tetragonal[:5]:
h, k, l = hkl
d_correct = tetragonal_d_spacing(a_TiO2, c_TiO2, h, k, l)
d_wrong = cubic_d_spacing(a_TiO2, h, k, l) # 誤った仮定
error = abs(d_correct - d_wrong) / d_correct * 100
print(f"({h}{k}{l})面:")
print(f" 正しい d = {d_correct:.4f} Å")
print(f" 誤った d = {d_wrong:.4f} Å (誤差: {error:.2f}%)\n")
結晶系を誤って仮定すると、面間隔の計算が大きく間違います。 特にc軸が大きく異なる正方晶を立方晶と扱うと、(001)面などのc軸に関わる面で顕著な誤差が生じます。
六方晶(a = b ≠ c、α = β = 90°、γ = 120°)は、しばしば4軸指数 (hkil)を使用します。 ここで、i = -(h+k) という関係があります。
def hexagonal_d_spacing(a, c, h, k, l):
"""
六方晶系の面間隔を計算
Parameters:
-----------
a : float
a軸(= b軸)の格子定数 (Å)
c : float
c軸の格子定数 (Å)
h, k, l : int
ミラー指数(3軸表記)
Returns:
--------
float : 面間隔 d_hkl (Å)
"""
return 1 / np.sqrt((4/3) * (h**2 + h*k + k**2) / a**2 + l**2 / c**2)
def miller_to_miller_bravais(h, k, l):
"""
3軸ミラー指数を4軸ミラー・ブラベー指数に変換
Parameters:
-----------
h, k, l : int
3軸ミラー指数
Returns:
--------
tuple : 4軸指数 (h, k, i, l) ただし i = -(h+k)
"""
i = -(h + k)
return (h, k, i, l)
# α-Al2O3(コランダム、六方晶、a = 4.759 Å, c = 12.991 Å)
a_Al2O3 = 4.759
c_Al2O3 = 12.991
print("=== α-Al2O3(コランダム)の面間隔 ===")
print(f"格子定数 a = {a_Al2O3} Å, c = {c_Al2O3} Å")
print(f"c/a 比 = {c_Al2O3/a_Al2O3:.3f}\n")
planes_hexagonal = [
(1, 0, 0), (0, 0, 1), (1, 1, 0),
(1, 0, 1), (1, 1, 2), (2, 0, 0),
(1, 0, 4), (2, 1, 0), (0, 0, 6)
]
print(f"{'3軸 (hkl)':<15} {'4軸 (hkil)':<20} {'d (Å)':<10}")
print("-" * 50)
for hkl in planes_hexagonal:
h, k, l = hkl
d = hexagonal_d_spacing(a_Al2O3, c_Al2O3, h, k, l)
hkil = miller_to_miller_bravais(h, k, l)
# 負の値を表示
hkil_str = "("
for idx in hkil:
if idx < 0:
hkil_str += f"{idx}"
else:
hkil_str += f"{idx}"
hkil_str += ")"
print(f"({h}{k}{l}){'':<12} {hkil_str:<20} {d:.4f}")
六方晶では4軸指数 (hkil) を使うと、結晶の6回対称性が記法に反映されます。 例えば、{10\(\bar{1}\)0}は6つの等価な面を表し、これが4軸表記で明確になります。
結晶の対称性により、結晶学的に等価な面や方向が存在します。 これらをまとめて表記するために、波括弧 {hkl} や山括弧 <uvw> を使います。
立方晶では、以下のような等価関係があります:
| 表記 | 意味 | 例:{100}の等価な面 |
|---|---|---|
| {100} | 等価な面の集合 | (100), (010), (001), (\(\bar{1}\)00), (0\(\bar{1}\)0), (00\(\bar{1}\)) |
| {110} | 等価な面の集合 | (110), (101), (011), (\(\bar{1}\)10), (\(\bar{1}\)0\(\bar{1}\)), など12面 |
| {111} | 等価な面の集合 | (111), (\(\bar{1}\)11), (1\(\bar{1}\)1), など8面 |
from itertools import permutations, product
def generate_equivalent_planes(h, k, l, crystal_system='cubic'):
"""
対称性を考慮して等価な面をすべて生成
Parameters:
-----------
h, k, l : int
基準となるミラー指数
crystal_system : str
結晶系 ('cubic', 'tetragonal', 'hexagonal')
Returns:
--------
set : 等価な面の集合
"""
planes = set()
if crystal_system == 'cubic':
# 立方晶:符号と置換の全組み合わせ
for perm in permutations([abs(h), abs(k), abs(l)]):
for signs in product([1, -1], repeat=3):
plane = tuple(s * p for s, p in zip(signs, perm))
if plane != (0, 0, 0): # (000)は除外
planes.add(plane)
elif crystal_system == 'tetragonal':
# 正方晶:a, b軸は等価、c軸は独立
# h, k の置換と符号変更のみ
for h_sign, k_sign, l_sign in product([1, -1], repeat=3):
planes.add((h_sign * h, k_sign * k, l_sign * l))
planes.add((k_sign * k, h_sign * h, l_sign * l)) # h, k の置換
elif crystal_system == 'hexagonal':
# 六方晶:より複雑な対称性(簡略版)
# 6回回転対称性を考慮
for l_sign in [1, -1]:
planes.add((h, k, l_sign * l))
planes.add((k, -(h+k), l_sign * l))
planes.add((-(h+k), h, l_sign * l))
return sorted(planes)
# 立方晶での等価な面
print("=== 立方晶の等価な面 ===\n")
for base_plane in [(1, 0, 0), (1, 1, 0), (1, 1, 1)]:
h, k, l = base_plane
equiv = generate_equivalent_planes(h, k, l, 'cubic')
print(f"{{{h}{k}{l}}} の等価な面 ({len(equiv)} 個):")
# 見やすく整形して表示
for i in range(0, len(equiv), 6):
planes_str = ', '.join([f"({p[0]:2}{p[1]:2}{p[2]:2})" for p in equiv[i:i+6]])
print(f" {planes_str}")
print()
# 正方晶での等価な面
print("=== 正方晶の等価な面 ===\n")
base_plane = (1, 1, 0)
equiv_tetra = generate_equivalent_planes(*base_plane, 'tetragonal')
print(f"{{{base_plane[0]}{base_plane[1]}{base_plane[2]}}} の等価な面(正方晶)({len(equiv_tetra)} 個):")
for plane in equiv_tetra:
print(f" ({plane[0]:2}{plane[1]:2}{plane[2]:2})")
結晶方向は[uvw]で表され、原点から座標 (u·a, v·b, w·c) へのベクトルを意味します。
立方晶では、(hkl)面に垂直な方向は[hkl]になります。 ただし、これは立方晶特有の性質で、他の結晶系では一般に成り立ちません。
def plot_crystal_directions():
"""立方晶の主要な結晶方向を可視化"""
fig = plt.figure(figsize=(15, 5))
directions = [
([1, 0, 0], 'red', '[100]'),
([1, 1, 0], 'green', '[110]'),
([1, 1, 1], 'blue', '[111]')
]
for idx, (uvw, color, title) in enumerate(directions):
ax = fig.add_subplot(1, 3, idx + 1, projection='3d')
# 立方体の枠を描画
r = [0, 1]
for s, e in combinations(np.array(list(product(r, r, r))), 2):
if np.sum(np.abs(s - e)) == 1:
ax.plot3D(*zip(s, e), color='gray', alpha=0.3, linewidth=1)
# 方向ベクトルを描画
ax.quiver(0, 0, 0, uvw[0], uvw[1], uvw[2],
color=color, arrow_length_ratio=0.15, linewidth=3,
label=f'{title} 方向')
# 軸ラベル
ax.set_xlabel('a', fontsize=12, fontweight='bold')
ax.set_ylabel('b', fontsize=12, fontweight='bold')
ax.set_zlabel('c', fontsize=12, fontweight='bold')
ax.set_xlim([0, 1.5])
ax.set_ylim([0, 1.5])
ax.set_zlim([0, 1.5])
ax.set_title(title + ' 結晶方向', fontsize=14, fontweight='bold')
ax.legend(loc='upper left')
ax.grid(True, alpha=0.3)
plt.tight_layout()
plt.savefig('crystal_directions.png', dpi=150, bbox_inches='tight')
plt.show()
print("結晶方向の可視化を保存しました: crystal_directions.png")
from itertools import combinations, product
# 実行
plot_crystal_directions()
ミラー指数は材料科学のあらゆる分野で使われます。 ここでは、実際の材料を例に面間隔の計算と応用を学びます。
X線回折(XRD)では、ブラッグの法則により特定の角度でピークが観測されます:
ここで、λはX線の波長、θはブラッグ角、nは反射の次数(通常1)です。 面間隔 dhkl を知ることで、観測されたピークがどの結晶面からの反射かを特定できます。
def bragg_angle(d_hkl, wavelength, n=1):
"""
ブラッグの法則から回折角を計算
Parameters:
-----------
d_hkl : float
面間隔 (Å)
wavelength : float
X線波長 (Å)
n : int
反射の次数(通常1)
Returns:
--------
float : ブラッグ角 θ (度)、Noneの場合は回折不可
"""
sin_theta = n * wavelength / (2 * d_hkl)
if abs(sin_theta) > 1:
return None # 回折条件を満たさない
return np.degrees(np.arcsin(sin_theta))
def predict_xrd_pattern(material_name, a, c=None, crystal_system='cubic',
wavelength=1.5406, max_hkl=3):
"""
材料のXRD回折パターンを予測
Parameters:
-----------
material_name : str
材料名
a : float
格子定数 a (Å)
c : float, optional
格子定数 c (Å)(正方晶・六方晶の場合)
crystal_system : str
結晶系
wavelength : float
X線波長 (Å)、Cu Kα線がデフォルト
max_hkl : int
計算する最大のミラー指数
"""
print(f"\n=== {material_name} のXRD回折パターン予測 ===")
print(f"結晶系: {crystal_system}")
print(f"格子定数: a = {a:.4f} Å" + (f", c = {c:.4f} Å" if c else ""))
print(f"X線波長: {wavelength:.4f} Å (Cu Kα)\n")
print(f"{'(hkl)':<10} {'d (Å)':<12} {'2θ (度)':<12} {'強度目安':<10}")
print("-" * 55)
results = []
# ミラー指数の組み合わせを生成
for h in range(max_hkl + 1):
for k in range(h, max_hkl + 1):
for l in range(k, max_hkl + 1):
if h == 0 and k == 0 and l == 0:
continue
# 面間隔を計算
if crystal_system == 'cubic':
d = cubic_d_spacing(a, h, k, l)
elif crystal_system == 'tetragonal':
d = tetragonal_d_spacing(a, c, h, k, l)
elif crystal_system == 'hexagonal':
d = hexagonal_d_spacing(a, c, h, k, l)
# ブラッグ角を計算
theta = bragg_angle(d, wavelength)
if theta is not None and theta < 90:
# 多重度(等価な面の数)を考慮した相対強度の簡易推定
multiplicity = len(generate_equivalent_planes(h, k, l, crystal_system))
intensity = multiplicity / (h**2 + k**2 + l**2) # 簡易的な構造因子
results.append(((h, k, l), d, 2 * theta, intensity))
# 2θ の小さい順にソート
results.sort(key=lambda x: x[2])
# 上位10ピークを表示
for i, ((h, k, l), d, two_theta, intensity) in enumerate(results[:10]):
# 強度を視覚化
intensity_bar = '█' * int(intensity * 10)
print(f"({h}{k}{l}){'':<8} {d:8.4f} {two_theta:8.2f} {intensity_bar}")
# 実行:代表的な材料のXRDパターン予測
# 1. シリコン(Si、立方晶)
predict_xrd_pattern('Silicon (Si)', a=5.4310, crystal_system='cubic', max_hkl=3)
# 2. 金(Au、立方晶)
predict_xrd_pattern('Gold (Au)', a=4.0782, crystal_system='cubic', max_hkl=2)
# 3. TiO2 ルチル(正方晶)
predict_xrd_pattern('TiO2 (Rutile)', a=4.594, c=2.958,
crystal_system='tetragonal', max_hkl=2)
# 4. α-Al2O3(六方晶)
predict_xrd_pattern('α-Al2O3 (Corundum)', a=4.759, c=12.991,
crystal_system='hexagonal', max_hkl=2)
このプログラムは、実験で得られたXRDパターンと理論計算を比較するための基礎ツールです。 実際の解析では、原子の配置による構造因子や温度因子も考慮する必要がありますが、 ミラー指数と面間隔の理解は全ての基礎となります。
材料の物性は結晶面により大きく異なります。例えば:
立方晶の結晶で、以下の切片を持つ結晶面のミラー指数を求めなさい:
銅(Cu)は面心立方構造(fcc)で、格子定数 a = 3.615 Å です。 以下の面の面間隔を計算しなさい:
また、Cu Kα線(λ = 1.5406 Å)を用いたXRD測定で、これらの面からの回折ピークは 何度(2θ)に現れるか計算しなさい。
面間隔:
ブラッグ角:(λ = 2d sinθ より)
立方晶において、{110} に含まれる等価な面をすべて列挙しなさい。 また、これらの面が何個あるか答えなさい。
答え:12個
等価な面:
これらは立方晶の対称性(24個の対称操作)により等価となり、 同じ物理的性質を持ちます。
正方晶の材料(例:ZrO2、a = 3.64 Å、c = 5.27 Å)について、 以下を実行するPythonプログラムを作成しなさい:
コード例4のtetragonal_d_spacing関数を使用し、
3重ループでh, k, lを1から3まで変化させて計算します。
結果はリストに保存し、sorted()関数でソートできます。
この章では、結晶学における最も重要な記法であるミラー指数について学びました:
次章では、X線回折の原理とブラッグの法則を詳しく学び、 ミラー指数と面間隔の知識を実際の構造解析に応用します。