"""
get_cif.py
概要: Materials Project APIから結晶構造データを取得し、指定されたフォーマットで保存するスクリプトです。
詳細説明:
このスクリプトは、Materials ProjectのAPIキーを利用して、ユーザーが指定した化学式または元素組成に基づいて結晶構造を検索します。
検索結果の構造は、CIF形式(デフォルト)またはPOSCAR形式でファイルに保存されます。
スクリプトの実行には、環境変数 'MP_APIKEY' に有効なMaterials Project APIキーが設定されている必要があります。
使用方法:
python get_cif.py <formula> [output_format] [prec]
<formula>: 検索する化学式またはカンマ区切りの元素リスト (例: "TiO2", "Ti,O")
[output_format]: 出力ファイルの形式 (デフォルト: "cif", その他 "poscar" など)
[prec]: 対称性を分析する際の許容誤差 (デフォルト: 1.0e-3)
関連リンク:
:doc:`get_cif_usage`
"""
import os
import sys
try:
from mp_api.client import MPRester
from pymatgen.core.structure import Structure
from pymatgen.core import Composition
from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
from pymatgen.io.cif import CifWriter, CifParser
except:
pass
formula = None
output_format = "cif"
prec = 1.0e-3
[ドキュメント]
def usage():
"""
概要: スクリプトの正しい使用方法をコンソールに表示します。
詳細説明:
この関数は、スクリプトが引数なしで実行された場合や、ユーザーが使用方法を知りたい場合に呼び出され、
必要なコマンドライン引数の形式と説明を出力します。
"""
print()
print(f"Usage: python {sys.argv[0]} formula [output_format] [prec]\n")
[ドキュメント]
def update_vars():
"""
概要: コマンドライン引数を解析し、グローバル変数 `formula`, `output_format`, `prec` を更新します。
詳細説明:
`sys.argv` を読み込み、最初の引数から化学式または元素組成を設定します。
オプションで、2番目の引数から出力フォーマット、3番目の引数から対称性の許容誤差を設定します。
化学式が指定されていない場合はエラーメッセージを表示し、スクリプトを終了します。
"""
global formula, output_format, prec # output_formuat は typo 修正せず既存コード通り
argv = sys.argv
narg = len(argv)
if narg <= 1:
print("\nError: Chemical formula must be given as the first arg\n")
input("Press ENTER to terminate>>\n")
exit()
formula = argv[1]
# 既存コードの typo に合わせる。output_formuat は output_format の間違いだが、変更しないルールによりそのまま
if narg >= 3: output_format = argv[2]
if narg >= 4: prec = float(argv[3])
[ドキュメント]
def get_structure(formula):
"""
概要: Materials Project APIから指定された化学式または元素組成に対応する結晶構造を取得します。
詳細説明:
環境変数 'MP_APIKEY' からAPIキーを取得し、`MPRester` を使用してMaterials Projectに接続します。
引数 `formula` がカンマ区切りで複数の元素を含む場合、それらの元素を含む構造を検索します。
単一の化学式の場合、その化学式に一致する構造を検索します。
見つかった構造は `pymatgen.core.structure.Structure` オブジェクトのリストとして返されます。
APIキーの取得失敗、MPResterの初期化失敗、またはデータが見つからない場合はエラーメッセージを表示し、Noneを返します。
検索結果の構造について、入力された元素がすべて含まれているかを確認し、含まれない場合は警告を出力してスキップします。
:param formula: str: 検索対象の化学式 (例: "TiO2") またはカンマ区切りの元素組成 (例: "Ti,O")。
:returns: list[Structure] or None: 見つかった結晶構造のリスト、またはエラー発生時やデータが見つからない場合はNone。
"""
API_KEY = os.getenv('MP_APIKEY')
if API_KEY is None:
print("\nError: Can not get MP API Key from the environment var MP_APIKEY\n")
return None
mpr = MPRester(API_KEY)
if mpr is None:
print(f"\nError: Can not get MPRester using the given API_KEY [{API_KEY}]\n")
input("Press ENTER to terminate>>\n")
exit()
if ',' in formula:
elements_in = formula.replace(' ', '').split(',')
search_results = mpr.materials.summary.search(elements = elements_in)
else:
elements_in = None
search_results = mpr.materials.search(formula = formula)
if not search_results:
print(f"No data found for {formula}")
return None
structures = []
for res in search_results:
material_id = res.material_id
material_data = mpr.materials.search(material_ids = [material_id])
if not material_data:
print(f"No structure data found for material_id {material_id}")
continue
structure_dict = material_data[0].structure.as_dict()
structure = Structure.from_dict(structure_dict)
composition = structure.composition
cformula = composition.reduced_formula
# cformula = structure.reduced_formula #formula.replace(" ", "")
# composition = Composition(cformula)
elements_res = [element.symbol for element in composition.elements]
if elements_in:
# print("elements_res=", elements_res)
# print("elements_in=", elements_in)
not_included = []
for e in elements_res:
# print(" e=", e)
if e not in elements_in: not_included.append(e)
if len(not_included) >= 1:
print(f" ** One or more elements are not included for [{cformula}] in the given condition [{formula}]:", not_included)
continue
else:
print(f" ** Found [{cformula}] [{material_id}]")
structure.cmaterial = res
structure.material_id = material_id
structure.cformula = cformula
structure.celements = elements_res
structures.append(structure)
cformula = structure.formula.replace(" ", "")
print(f"Found material_id for {formula}: {material_id}: {cformula}")
return structures
[ドキュメント]
def save_structures(structures, output_format, formula):
"""
概要: 取得した結晶構造を指定されたフォーマットでファイルに保存します。
詳細説明:
渡された結晶構造のリストを反復処理し、各構造を `output_format` に従ってファイルに書き出します。
`SpacegroupAnalyzer` を使用して構造を対称化してから保存します。
出力ファイル名は、構造の簡略化学式とMaterial IDに基づいて命名されます。
`output_format` が "poscar" の場合、最初のファイルは "POSCAR"、2番目以降は "POSCAR_2" などとなります。
その他のフォーマットの場合、"化学式_material_id.フォーマット" の形式で保存されます。
:param structures: list[Structure]: 保存する結晶構造オブジェクトのリスト。
:param output_format: str: 出力ファイルのフォーマット (例: "cif", "poscar")。
:param formula: str: 元の検索に使用された化学式 (ファイル名の一部として利用される可能性がありますが、実際は`cformula`と`material_id`が使われます)。
"""
print()
for i, structure in enumerate(structures):
cformula = structure.cformula
if output_format == "poscar":
if i == 0:
output_path = f"POSCAR"
else:
output_path = f"POSCAR_{i+1}"
else:
output_path = f"{cformula}_{structure.material_id}.{output_format}"
'''
if i == 0:
output_path = f"{cformula}.{output_format}"
else:
output_path = f"{cformula}_{i+1}.{output_format}"
'''
symmetry_analyzer = SpacegroupAnalyzer(structure, symprec = prec) #, angle_tolerance = 5.0)
symmetrized_structure = symmetry_analyzer.get_symmetrized_structure()
# print()
# print("symmetrized_structure:")
# print(symmetrized_structure)
print(f"Saving {cformula} structure to [{output_path}]...")
# structure.to(filename = output_path, fmt = output_format)
writer = CifWriter(symmetrized_structure, symprec = prec) #, angle_tolerance = 5.0)
writer.write_file(output_path)
[ドキュメント]
def main():
"""
概要: スクリプトの主要な実行フローを制御します。
詳細説明:
`update_vars` を呼び出してコマンドライン引数を処理し、グローバル変数を設定します。
次に、`get_structure` を使用してMaterials Projectから結晶構造を取得します。
構造が正常に取得された場合、`save_structures` を呼び出してそれらの構造をファイルに保存します。
最後に、`usage` 関数を呼び出してスクリプトの使用方法を表示し、終了プロンプトを表示してユーザーの入力を待ちます。
"""
update_vars()
structures = get_structure(formula)
if structures:
# output_formuat は typo なのでそのまま渡す
save_structures(structures, output_format, formula)
usage()
print()
input("Press ENTER to terminate>>\n")
exit()
if __name__ == "__main__":
main()