Materials Projectとの連携から実践的ワークフローまで
この最終章を学ぶことで、以下の実践的スキルを習得できます:
pymatgen(Python Materials Genomics)は、材料科学のための包括的なPythonライブラリです。 MIT Materials Project研究グループによって開発され、結晶構造解析、第一原理計算、Materials Informatics研究で広く使われています。
# ターミナル/コマンドプロンプトで実行
# 基本的なインストール
pip install pymatgen
# Materials Project API連携用(推奨)
pip install mp-api
# 可視化ライブラリも一緒にインストール
pip install matplotlib numpy scipy plotly
# インストール確認
python -c "import pymatgen; print(pymatgen.__version__)"
# 出力例: 2024.10.3
Materials Projectのデータベースにアクセスするには、無料のAPIキーが必要です:
abc123xyz...)をコピーセキュリティ上の注意:APIキーは秘密情報です。GitHubなどにコミットしないでください。
シリコン(Si)のダイヤモンド構造を作成し、基本情報を取得します:
from pymatgen.core import Structure, Lattice
import numpy as np
# ダイヤモンド構造のシリコンを作成
# 格子ベクトルの定義(立方晶、a = 5.43 Å)
a = 5.43
lattice = Lattice.cubic(a)
# 原子の種類と分数座標
species = ['Si'] * 8
coords = [
[0.00, 0.00, 0.00], # FCC格子点
[0.50, 0.50, 0.00],
[0.50, 0.00, 0.50],
[0.00, 0.50, 0.50],
[0.25, 0.25, 0.25], # 四面体空隙
[0.75, 0.75, 0.25],
[0.75, 0.25, 0.75],
[0.25, 0.75, 0.75]
]
# Structureオブジェクトの作成
si_structure = Structure(lattice, species, coords)
print("=== シリコン(Si)の結晶構造情報 ===\n")
# 基本情報の取得
print(f"化学組成: {si_structure.composition}")
print(f"化学式: {si_structure.formula}")
print(f"単位格子の体積: {si_structure.volume:.4f} ų")
print(f"密度: {si_structure.density:.4f} g/cm³")
print(f"原子数: {len(si_structure)}")
# 格子定数の取得
print(f"\n格子定数:")
print(f" a = {si_structure.lattice.a:.4f} Å")
print(f" b = {si_structure.lattice.b:.4f} Å")
print(f" c = {si_structure.lattice.c:.4f} Å")
print(f" α = {si_structure.lattice.alpha:.2f}°")
print(f" β = {si_structure.lattice.beta:.2f}°")
print(f" γ = {si_structure.lattice.gamma:.2f}°")
# 各原子の情報
print(f"\n原子座標:")
print(f"{'原子':<8} {'分数座標 (x, y, z)':<30} {'デカルト座標 (Å)':<25}")
print("-" * 65)
for i, site in enumerate(si_structure):
species = site.species_string
frac_coords = site.frac_coords
cart_coords = site.coords
print(f"{species:<8} ({frac_coords[0]:6.3f}, {frac_coords[1]:6.3f}, {frac_coords[2]:6.3f}) "
f"({cart_coords[0]:6.3f}, {cart_coords[1]:6.3f}, {cart_coords[2]:6.3f})")
# 最近接原子間距離
print(f"\n最近接原子間距離:")
neighbors = si_structure.get_neighbors(si_structure[0], 3.0)
for neighbor, distance in neighbors:
print(f" {distance:.4f} Å")
CIF(Crystallographic Information File)は、結晶構造データの標準フォーマットです。 世界中のデータベース(ICSD、COD、Materials Projectなど)がCIF形式でデータを提供しています。
from pymatgen.core import Structure
from pymatgen.io.cif import CifWriter
import os
# 前のコード例で作成したsi_structureを使用
# 1. CIFファイルとして保存
cif_filename = "Si_diamond.cif"
cif_writer = CifWriter(si_structure)
cif_writer.write_file(cif_filename)
print(f"=== CIFファイルの書き出し ===")
print(f"ファイル名: {cif_filename}")
print(f"ファイルサイズ: {os.path.getsize(cif_filename)} bytes\n")
# CIFファイルの内容を表示
print("=== CIFファイルの内容(抜粋) ===")
with open(cif_filename, 'r') as f:
lines = f.readlines()
for line in lines[:30]: # 最初の30行を表示
print(line.rstrip())
# 2. CIFファイルの読み込み
loaded_structure = Structure.from_file(cif_filename)
print("\n=== CIFファイルから読み込んだ構造 ===")
print(f"化学式: {loaded_structure.formula}")
print(f"空間群: {loaded_structure.get_space_group_info()}")
print(f"格子定数 a: {loaded_structure.lattice.a:.4f} Å")
print(f"原子数: {len(loaded_structure)}")
# 3. オンラインデータベースからCIFファイルを取得する例
# 例: Crystallography Open Database (COD)
# 以下はCODからのダウンロード例(実際にはwgetやcurlを使用)
print("\n=== オンラインデータベースからの取得例 ===")
print("Crystallography Open Database (COD):")
print(" URL: http://www.crystallography.net/cod/")
print(" 例: Si (COD ID: 9008565)")
print(" ダウンロード: wget http://www.crystallography.net/cod/9008565.cif")
print("\nInorganic Crystal Structure Database (ICSD):")
print(" URL: https://icsd.fiz-karlsruhe.de/")
print(" 注: ライセンスが必要(大学・研究機関経由でアクセス可能な場合が多い)")
# 4. 構造の比較
print("\n=== 元の構造と読み込んだ構造の比較 ===")
print(f"同一性チェック: {si_structure == loaded_structure}")
print(f"格子定数の差 (Å): {abs(si_structure.lattice.a - loaded_structure.lattice.a):.6f}")
# クリーンアップ
# os.remove(cif_filename) # 必要に応じてコメントアウトを外す
Materials Projectは、機械学習と第一原理計算を組み合わせた世界最大級の材料データベースです。 14万以上の無機材料の構造、物性、電子状態などのデータが無料で公開されています。
APIキーを使ってシリコンの結晶構造を取得します:
from mp_api.client import MPRester
from pymatgen.core import Structure
# APIキーの設定(ここでは環境変数から取得する方法を推奨)
import os
# 方法1: 環境変数から取得(推奨)
API_KEY = os.environ.get('MP_API_KEY', None)
# 方法2: 直接指定(テスト用のみ、本番環境では使わない)
if API_KEY is None:
print("警告: MP_API_KEYが環境変数に設定されていません。")
print("以下のコードはダミーの例です。実際に実行するにはAPIキーが必要です。\n")
# API_KEY = "your_api_key_here" # 実際のAPIキーに置き換え
def get_structure_from_mp(api_key=None, demo_mode=True):
"""
Materials Projectから構造を取得
Parameters:
-----------
api_key : str
Materials Project APIキー
demo_mode : bool
デモモード(APIキーなしでダミーデータを返す)
"""
if demo_mode or api_key is None:
print("=== デモモード: ローカルでSi構造を作成 ===\n")
# ダミーのSi構造を返す
lattice = Lattice.cubic(5.43)
species = ['Si'] * 8
coords = [
[0.00, 0.00, 0.00], [0.50, 0.50, 0.00],
[0.50, 0.00, 0.50], [0.00, 0.50, 0.50],
[0.25, 0.25, 0.25], [0.75, 0.75, 0.25],
[0.75, 0.25, 0.75], [0.25, 0.75, 0.75]
]
structure = Structure(lattice, species, coords)
print(f"Materials Project ID (例): mp-149")
print(f"化学式: {structure.formula}")
print(f"空間群: Fd-3m (227)")
return structure
# 実際のAPI使用
with MPRester(api_key) as mpr:
print("=== Materials Projectから構造を取得 ===\n")
# 方法1: Material IDを指定して取得
mp_id = "mp-149" # Si (シリコン)
structure = mpr.get_structure_by_material_id(mp_id)
print(f"Material ID: {mp_id}")
print(f"化学式: {structure.formula}")
print(f"空間群: {structure.get_space_group_info()}")
print(f"格子定数 a: {structure.lattice.a:.4f} Å")
print(f"密度: {structure.density:.4f} g/cm³")
# 方法2: 化学式で検索
print("\n=== 化学式による検索例 ===")
docs = mpr.materials.summary.search(
formula="Fe2O3",
fields=["material_id", "formula_pretty", "symmetry"]
)
print(f"Fe2O3の検索結果: {len(docs)}件")
for doc in docs[:3]: # 最初の3件を表示
print(f" - {doc.material_id}: {doc.formula_pretty}, "
f"空間群 {doc.symmetry.symbol}")
return structure
# 実行(デモモード)
si_structure_mp = get_structure_from_mp(api_key=API_KEY, demo_mode=(API_KEY is None))
print("\n=== 取得した構造の詳細 ===")
print(si_structure_mp)
# APIキーの取得方法を案内
if API_KEY is None:
print("\n" + "="*60)
print("Materials Project APIキーの取得方法:")
print("1. https://next-gen.materialsproject.org/ にアクセス")
print("2. 右上の 'Sign Up' から無料アカウントを作成")
print("3. ログイン後、'API' セクションでAPIキーを生成")
print("4. 環境変数に設定:")
print(" export MP_API_KEY='your_api_key_here' # Linux/Mac")
print(" set MP_API_KEY=your_api_key_here # Windows")
print("="*60)
バンドギャップや結晶系などの条件で材料を検索します:
from mp_api.client import MPRester
import pandas as pd
def search_materials(api_key=None, demo_mode=True):
"""
Materials Projectで条件を指定して材料を検索
"""
if demo_mode or api_key is None:
print("=== デモモード: サンプル検索結果 ===\n")
# ダミーの検索結果
data = {
'material_id': ['mp-149', 'mp-13', 'mp-66'],
'formula': ['Si', 'GaAs', 'Ge'],
'band_gap (eV)': [1.15, 1.42, 0.74],
'formation_energy (eV/atom)': [-0.31, -0.48, -0.28],
'space_group': ['Fd-3m', 'F-43m', 'Fd-3m']
}
df = pd.DataFrame(data)
print("検索条件: バンドギャップ 0.5 - 2.0 eV の半導体")
print(df.to_string(index=False))
return df
# 実際のAPI使用
with MPRester(api_key) as mpr:
print("=== Materials Projectで材料を検索 ===\n")
# 例1: バンドギャップで検索
print("検索条件1: バンドギャップ 1.0 - 2.0 eV の半導体")
docs = mpr.materials.summary.search(
band_gap=(1.0, 2.0),
num_elements=(1, 2), # 1-2元素系
fields=["material_id", "formula_pretty", "band_gap",
"formation_energy_per_atom", "symmetry"]
)
results = []
for doc in docs[:10]: # 最初の10件
results.append({
'material_id': doc.material_id,
'formula': doc.formula_pretty,
'band_gap (eV)': f"{doc.band_gap:.3f}",
'formation_energy (eV/atom)': f"{doc.formation_energy_per_atom:.3f}",
'space_group': doc.symmetry.symbol
})
df = pd.DataFrame(results)
print(df.to_string(index=False))
# 例2: 特定の元素を含む材料の検索
print("\n検索条件2: Liを含むイオン伝導体候補")
docs = mpr.materials.summary.search(
elements=["Li", "O"], # LiとOを含む
num_elements=(2, 4),
fields=["material_id", "formula_pretty", "symmetry"]
)
print(f"検索結果: {len(docs)}件")
for doc in docs[:5]:
print(f" {doc.material_id}: {doc.formula_pretty}")
return df
# 実行(デモモード)
API_KEY = os.environ.get('MP_API_KEY', None)
search_results = search_materials(api_key=API_KEY, demo_mode=(API_KEY is None))
超格子(supercell)は、単位格子を整数倍に拡張した構造です。 第一原理計算やドーピング研究で頻繁に使用されます。
from pymatgen.core import Structure, Lattice
import numpy as np
# シリコン構造を作成(前のコード例と同じ)
a = 5.43
lattice = Lattice.cubic(a)
species = ['Si'] * 8
coords = [
[0.00, 0.00, 0.00], [0.50, 0.50, 0.00],
[0.50, 0.00, 0.50], [0.00, 0.50, 0.50],
[0.25, 0.25, 0.25], [0.75, 0.75, 0.25],
[0.75, 0.25, 0.75], [0.25, 0.75, 0.75]
]
si_structure = Structure(lattice, species, coords)
print("=== 元の単位格子 ===")
print(f"化学式: {si_structure.formula}")
print(f"原子数: {len(si_structure)}")
print(f"体積: {si_structure.volume:.4f} ų")
print(f"格子定数: a = {si_structure.lattice.a:.4f} Å\n")
# 2×2×2 超格子の作成
supercell_2x2x2 = si_structure.copy()
supercell_2x2x2.make_supercell([2, 2, 2])
print("=== 2×2×2 超格子 ===")
print(f"化学式: {supercell_2x2x2.formula}")
print(f"原子数: {len(supercell_2x2x2)}")
print(f"体積: {supercell_2x2x2.volume:.4f} ų")
print(f"格子定数: a = {supercell_2x2x2.lattice.a:.4f} Å")
print(f"体積比(超格子/単位格子): {supercell_2x2x2.volume / si_structure.volume:.1f}\n")
# 3×1×1 超格子(異方的拡張)
supercell_3x1x1 = si_structure.copy()
supercell_3x1x1.make_supercell([3, 1, 1])
print("=== 3×1×1 超格子(異方的拡張) ===")
print(f"原子数: {len(supercell_3x1x1)}")
print(f"格子定数:")
print(f" a = {supercell_3x1x1.lattice.a:.4f} Å")
print(f" b = {supercell_3x1x1.lattice.b:.4f} Å")
print(f" c = {supercell_3x1x1.lattice.c:.4f} Å\n")
# 変換行列を使った超格子作成
# 例: 対角化されていない変換(斜め方向への拡張)
transformation_matrix = [
[2, 0, 0],
[0, 2, 0],
[1, 1, 2]
]
supercell_custom = si_structure.copy()
supercell_custom.make_supercell(transformation_matrix)
print("=== カスタム変換行列による超格子 ===")
print(f"変換行列:")
print(np.array(transformation_matrix))
print(f"\n原子数: {len(supercell_custom)}")
print(f"体積: {supercell_custom.volume:.4f} ų")
# 超格子内の特定原子の置換(ドーピングの例)
print("\n=== ドーピング例: 1つのSiをPに置換 ===")
doped_structure = supercell_2x2x2.copy()
doped_structure[0] = 'P' # 最初のSi原子をPに置換
print(f"置換前の化学式: {supercell_2x2x2.formula}")
print(f"置換後の化学式: {doped_structure.formula}")
print(f"ドーピング濃度: 1 / {len(doped_structure)} = "
f"{1/len(doped_structure)*100:.2f}%")
スラブモデルは表面や界面を研究するための構造です。 バルク結晶を特定の結晶面で切断し、真空層を挿入します。
from pymatgen.core.surface import SlabGenerator
from pymatgen.core import Structure, Lattice
# シリコン構造を作成
a = 5.43
lattice = Lattice.cubic(a)
species = ['Si'] * 8
coords = [
[0.00, 0.00, 0.00], [0.50, 0.50, 0.00],
[0.50, 0.00, 0.50], [0.00, 0.50, 0.50],
[0.25, 0.25, 0.25], [0.75, 0.75, 0.25],
[0.75, 0.25, 0.75], [0.25, 0.75, 0.75]
]
si_structure = Structure(lattice, species, coords)
print("=== スラブモデルの生成 ===\n")
# (111)面のスラブを生成
miller_index = (1, 1, 1)
min_slab_size = 10.0 # スラブの最小厚さ (Å)
min_vacuum_size = 15.0 # 真空層の最小厚さ (Å)
slabgen = SlabGenerator(
si_structure,
miller_index,
min_slab_size,
min_vacuum_size,
center_slab=True # スラブを真空層の中央に配置
)
# すべての対称的に異なるスラブを生成
slabs = slabgen.get_slabs()
print(f"(111)面のスラブモデル: {len(slabs)}種類の終端を検出\n")
for i, slab in enumerate(slabs[:3], 1): # 最初の3つを表示
print(f"--- スラブ {i} ---")
print(f"原子数: {len(slab)}")
print(f"スラブ厚さ: {slab.get_slab_thickness():.4f} Å")
print(f"c軸長(真空含む): {slab.lattice.c:.4f} Å")
print(f"a, b軸長: {slab.lattice.a:.4f}, {slab.lattice.b:.4f} Å")
# 表面原子と内部原子の区別
surface_sites = slab.get_surface_sites()
print(f"表面原子数: {len(surface_sites['top']) + len(surface_sites['bottom'])}")
print()
# 異なるミラー指数でのスラブ比較
print("=== 異なる結晶面でのスラブ生成 ===\n")
miller_indices = [(1, 0, 0), (1, 1, 0), (1, 1, 1)]
for hkl in miller_indices:
slabgen = SlabGenerator(si_structure, hkl, 10.0, 15.0)
slabs = slabgen.get_slabs()
print(f"({hkl[0]}{hkl[1]}{hkl[2]})面:")
print(f" 対称的に異なる終端の数: {len(slabs)}")
if len(slabs) > 0:
first_slab = slabs[0]
print(f" 代表的なスラブの原子数: {len(first_slab)}")
print(f" スラブ厚さ: {first_slab.get_slab_thickness():.4f} Å")
print()
# スラブをCIFファイルとして保存(例)
if len(slabs) > 0:
slab_to_save = slabs[0]
from pymatgen.io.cif import CifWriter
cif_writer = CifWriter(slab_to_save)
filename = f"Si_111_slab.cif"
cif_writer.write_file(filename)
print(f"スラブモデルを保存しました: {filename}")
from pymatgen.core import Structure, Lattice
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
import numpy as np
# いくつかの典型的な結晶構造を定義
def create_sample_structures():
"""サンプル構造を作成"""
structures = {}
# 1. シリコン(ダイヤモンド構造)
a = 5.43
lattice = Lattice.cubic(a)
species = ['Si'] * 8
coords = [
[0.00, 0.00, 0.00], [0.50, 0.50, 0.00],
[0.50, 0.00, 0.50], [0.00, 0.50, 0.50],
[0.25, 0.25, 0.25], [0.75, 0.75, 0.25],
[0.75, 0.25, 0.75], [0.25, 0.75, 0.75]
]
structures['Si (Diamond)'] = Structure(lattice, species, coords)
# 2. NaCl(岩塩構造)
a = 5.64
lattice = Lattice.cubic(a)
species = ['Na', 'Na', 'Na', 'Na', 'Cl', 'Cl', 'Cl', 'Cl']
coords = [
[0.0, 0.0, 0.0], [0.5, 0.5, 0.0],
[0.5, 0.0, 0.5], [0.0, 0.5, 0.5],
[0.5, 0.0, 0.0], [0.0, 0.5, 0.0],
[0.0, 0.0, 0.5], [0.5, 0.5, 0.5]
]
structures['NaCl (Rock salt)'] = Structure(lattice, species, coords)
# 3. 鉄(BCC)
a = 2.87
lattice = Lattice.cubic(a)
species = ['Fe', 'Fe']
coords = [[0.0, 0.0, 0.0], [0.5, 0.5, 0.5]]
structures['Fe (BCC)'] = Structure(lattice, species, coords)
return structures
# 構造を作成
structures = create_sample_structures()
print("=== 対称性解析 ===\n")
for name, structure in structures.items():
print(f"--- {name} ---")
# SpacegroupAnalyzerの作成
sga = SpacegroupAnalyzer(structure)
# 基本的な対称性情報
space_group = sga.get_space_group_symbol()
space_group_number = sga.get_space_group_number()
crystal_system = sga.get_crystal_system()
point_group = sga.get_point_group_symbol()
print(f"空間群: {space_group} (No. {space_group_number})")
print(f"結晶系: {crystal_system}")
print(f"点群: {point_group}")
# Wyckoff位置の取得
symmetrized_structure = sga.get_symmetrized_structure()
print(f"\nWyckoff位置:")
for i, equiv_sites in enumerate(symmetrized_structure.equivalent_sites):
species = equiv_sites[0].species_string
wyckoff = symmetrized_structure.wyckoff_symbols[i]
multiplicity = len(equiv_sites)
# 代表的な座標(最初の等価サイト)
representative_coords = equiv_sites[0].frac_coords
print(f" {species:4s} {wyckoff:3s} (×{multiplicity:2d}): "
f"({representative_coords[0]:6.3f}, "
f"{representative_coords[1]:6.3f}, "
f"{representative_coords[2]:6.3f})")
# 対称操作の数
symmetry_ops = sga.get_symmetry_operations()
print(f"\n対称操作の数: {len(symmetry_ops)}")
# 原始格子への変換
primitive = sga.get_primitive_standard_structure()
print(f"原始格子の原子数: {len(primitive)} (元の構造: {len(structure)})")
print("\n" + "="*60 + "\n")
# 高度な対称性解析
print("=== 高度な対称性解析(Si) ===\n")
si_structure = structures['Si (Diamond)']
sga = SpacegroupAnalyzer(si_structure)
# 対称操作の詳細
print("対称操作の例(最初の5つ):")
symmetry_ops = sga.get_symmetry_operations()
for i, symmop in enumerate(symmetry_ops[:5], 1):
print(f"\n操作 {i}:")
print("回転行列:")
print(symmop.rotation_matrix)
print(f"並進ベクトル: {symmop.translation_vector}")
# 国際表記とシェーンフリース記号
print(f"\n国際記号: {sga.get_space_group_symbol()}")
print(f"点群(国際記号): {sga.get_point_group_symbol()}")
# 結晶系の詳細情報
conventional = sga.get_conventional_standard_structure()
print(f"\n慣用単位格子:")
print(f" 格子定数: a={conventional.lattice.a:.4f} Å")
print(f" 原子数: {len(conventional)}")
from pymatgen.core import Structure, Lattice
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
# NaCl構造を作成(視覚的に分かりやすい)
a = 5.64
lattice = Lattice.cubic(a)
species = ['Na', 'Na', 'Na', 'Na', 'Cl', 'Cl', 'Cl', 'Cl']
coords = [
[0.0, 0.0, 0.0], [0.5, 0.5, 0.0],
[0.5, 0.0, 0.5], [0.0, 0.5, 0.5],
[0.5, 0.0, 0.0], [0.0, 0.5, 0.0],
[0.0, 0.0, 0.5], [0.5, 0.5, 0.5]
]
nacl_structure = Structure(lattice, species, coords)
# 2×2×2 超格子を作成(可視化を豊かにする)
nacl_supercell = nacl_structure.copy()
nacl_supercell.make_supercell([2, 2, 2])
print("=== 結晶構造の3D可視化 ===\n")
print(f"構造: NaCl (岩塩構造)")
print(f"超格子サイズ: 2×2×2")
print(f"原子数: {len(nacl_supercell)}\n")
# 3Dプロット
fig = plt.figure(figsize=(14, 6))
# サブプロット1: 球棒モデル
ax1 = fig.add_subplot(121, projection='3d')
# 元素ごとに色を分ける
colors = {'Na': 'blue', 'Cl': 'green'}
sizes = {'Na': 100, 'Cl': 150}
for site in nacl_supercell:
species = site.species_string
x, y, z = site.coords
ax1.scatter(x, y, z,
c=colors[species],
s=sizes[species],
alpha=0.6,
edgecolors='black',
linewidths=0.5)
# 単位格子の枠を描画
lattice_matrix = nacl_supercell.lattice.matrix
# 単位格子の頂点
vertices = np.array([
[0, 0, 0],
[1, 0, 0],
[1, 1, 0],
[0, 1, 0],
[0, 0, 1],
[1, 0, 1],
[1, 1, 1],
[0, 1, 1]
])
# デカルト座標に変換
vertices_cart = vertices @ lattice_matrix
# 格子の辺を描画
edges = [
[0, 1], [1, 2], [2, 3], [3, 0], # 下面
[4, 5], [5, 6], [6, 7], [7, 4], # 上面
[0, 4], [1, 5], [2, 6], [3, 7] # 縦の辺
]
for edge in edges:
points = vertices_cart[edge]
ax1.plot3D(*points.T, 'k-', linewidth=1.5, alpha=0.3)
ax1.set_xlabel('X (Å)', fontweight='bold')
ax1.set_ylabel('Y (Å)', fontweight='bold')
ax1.set_zlabel('Z (Å)', fontweight='bold')
ax1.set_title('NaCl 構造(2×2×2 超格子)', fontweight='bold', fontsize=12)
# 凡例
from matplotlib.lines import Line2D
legend_elements = [
Line2D([0], [0], marker='o', color='w',
markerfacecolor='blue', markersize=10, label='Na'),
Line2D([0], [0], marker='o', color='w',
markerfacecolor='green', markersize=12, label='Cl')
]
ax1.legend(handles=legend_elements, loc='upper right')
# サブプロット2: 単位格子のみ(詳細表示)
ax2 = fig.add_subplot(122, projection='3d')
for site in nacl_structure:
species = site.species_string
x, y, z = site.coords
ax2.scatter(x, y, z,
c=colors[species],
s=sizes[species]*2,
alpha=0.8,
edgecolors='black',
linewidths=1)
# 原子ラベルを追加
ax2.text(x, y, z, f' {species}', fontsize=9)
# 単位格子の枠
vertices = np.array([
[0, 0, 0], [a, 0, 0], [a, a, 0], [0, a, 0],
[0, 0, a], [a, 0, a], [a, a, a], [0, a, a]
])
for edge in edges:
points = vertices[edge]
ax2.plot3D(*points.T, 'k-', linewidth=2)
ax2.set_xlabel('X (Å)', fontweight='bold')
ax2.set_ylabel('Y (Å)', fontweight='bold')
ax2.set_zlabel('Z (Å)', fontweight='bold')
ax2.set_title('NaCl 単位格子', fontweight='bold', fontsize=12)
ax2.legend(handles=legend_elements, loc='upper right')
plt.tight_layout()
plt.savefig('nacl_structure_3d.png', dpi=150, bbox_inches='tight')
plt.show()
print("可視化を保存しました: nacl_structure_3d.png\n")
# ボンドの描画(最近接原子間の線)
print("=== 最近接原子間距離の可視化 ===")
fig = plt.figure(figsize=(10, 8))
ax = fig.add_subplot(111, projection='3d')
# 原子を描画
for site in nacl_structure:
species = site.species_string
x, y, z = site.coords
ax.scatter(x, y, z, c=colors[species], s=sizes[species]*2,
alpha=0.8, edgecolors='black', linewidths=1)
# 最近接ボンドを描画
bond_cutoff = 3.0 # Å(この距離以下をボンドとする)
for i, site1 in enumerate(nacl_structure):
for j, site2 in enumerate(nacl_structure):
if i >= j:
continue
distance = site1.distance(site2)
if distance < bond_cutoff:
# Na-Cl間のボンドのみ描画(同種原子間は除外)
if site1.species_string != site2.species_string:
x_vals = [site1.coords[0], site2.coords[0]]
y_vals = [site1.coords[1], site2.coords[1]]
z_vals = [site1.coords[2], site2.coords[2]]
ax.plot3D(x_vals, y_vals, z_vals,
'gray', linewidth=2, alpha=0.5)
# 単位格子の枠
for edge in edges:
points = vertices[edge]
ax.plot3D(*points.T, 'k-', linewidth=2)
ax.set_xlabel('X (Å)', fontweight='bold')
ax.set_ylabel('Y (Å)', fontweight='bold')
ax.set_zlabel('Z (Å)', fontweight='bold')
ax.set_title('NaCl 構造(ボンド表示)', fontweight='bold', fontsize=14)
ax.legend(handles=legend_elements, loc='upper right')
plt.tight_layout()
plt.savefig('nacl_structure_bonds.png', dpi=150, bbox_inches='tight')
plt.show()
print("ボンド可視化を保存しました: nacl_structure_bonds.png")
ここまで学んだ技術を統合し、Materials Projectからデータを取得して解析・可視化する実践的なワークフローを示します。
アルミニウム(Al)のFCC構造を作成し、以下の情報を取得しなさい:
また、この構造をCIFファイルとして保存しなさい。
FCC構造の原子位置は (0,0,0), (0.5,0.5,0), (0.5,0,0.5), (0,0.5,0.5) です。
Lattice.cubic()とStructureクラスを使用します。
対称性情報はSpacegroupAnalyzerで取得できます。
シリコン(Si)の2×2×2超格子を作成し、以下の操作を行いなさい:
make_supercell([2,2,2])で超格子を作成します。
原子の置換はstructure[index] = 'P'のように行います。
ドーピング濃度は 置換原子数/全原子数 × 100% で計算します。
以下の構造について、空間群番号、結晶系、Wyckoff位置を調べなさい:
それぞれの構造で対称操作の数も報告しなさい。
SpacegroupAnalyzerクラスを使用します。
get_space_group_number()、get_crystal_system()、
get_symmetrized_structure()などのメソッドが有用です。
以下の手順で、興味のある材料の完全な解析を実施しなさい(APIキーがある場合):
APIキーがない場合は、CIFファイルをオンラインデータベース(COD、ICSDなど)から ダウンロードして同様の解析を行いなさい。
matplotlib の savefig() で各グラフを画像として保存し、
PyPDF2 や reportlab などのライブラリでPDFにまとめることができます。
あるいは、Jupyter Notebookで実行し、「File → Download as → PDF」で
直接PDFとして保存する方法もあります。
この5章構成の結晶学入門シリーズを通じて、以下を習得しました:
1. 材料物性との関係(MS Dojo 他シリーズ)
2. 計算材料科学(第一原理計算)
3. Materials Informatics(MI)
4. 高度な結晶学
5. 材料データベース活用
結晶学とpymatgenの学習を継続するための推奨リソース:
結晶学は、材料科学の最も基盤的な学問領域の一つです。 原子がどのように配列しているかを理解することは、材料の性質を理解し、 新しい材料を設計する上で不可欠です。
本シリーズでは、理論的な基礎からpymatgenを使った実践的な計算技術まで、 体系的に学習しました。ここで習得した知識とスキルは、 Materials Informatics、第一原理計算、実験データ解析など、 あらゆる材料研究の基盤となります。
これからは、興味のある材料や研究テーマに対して、 このシリーズで学んだ技術を積極的に応用してください。 pymatgenとMaterials Projectは強力なツールであり、 あなたの研究を加速させる可能性を秘めています。
継続的な学習と実践が、真のスキル習得への道です。 本シリーズが、あなたの材料科学研究の新たなスタートとなることを願っています。