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

get_cif.py をダウンロード

get_cif.py
get_cif.py
  1"""
  2概要: Materials Project APIから結晶構造データを取得し、指定されたフォーマットで保存するスクリプトです。
  3
  4詳細説明:
  5    このスクリプトは、Materials ProjectのAPIキーを利用して、ユーザーが指定した化学式または元素組成に基づいて結晶構造を検索します。
  6    検索結果の構造は、CIF形式(デフォルト)またはPOSCAR形式でファイルに保存されます。
  7    スクリプトの実行には、環境変数 MP_APIKEY に有効なMaterials Project APIキーが設定されている必要があります。
  8
  9    使用方法:
 10        コマンドラインから以下のように実行します。
 11        python get_cif.py formula [output_format] [prec]
 12
 13        formula: 検索する化学式またはカンマ区切りの元素リスト (例: "TiO2", "Ti,O")
 14        output_format: 出力ファイルの形式 (デフォルト: "cif", その他 "poscar" など)
 15        prec: 対称性を分析する際の許容誤差 (デフォルト: 1.0e-3)
 16
 17関連リンク:
 18    get_cif_usage
 19"""
 20
 21import os
 22import sys
 23
 24try:
 25    from mp_api.client import MPRester
 26    from pymatgen.core.structure import Structure
 27    from pymatgen.core import Composition
 28    from pymatgen.symmetry.analyzer import SpacegroupAnalyzer
 29    from pymatgen.io.cif import CifWriter, CifParser
 30except:
 31    pass
 32
 33formula = None
 34output_format = "cif"
 35prec = 1.0e-3
 36
 37
 38def usage():
 39    """
 40    概要: スクリプトの正しい使用方法をコンソールに表示します。
 41
 42    詳細説明:
 43        この関数は、スクリプトが引数なしで実行された場合や、ユーザーが使用方法を知りたい場合に呼び出され、
 44        必要なコマンドライン引数の形式と説明を出力します。
 45    """
 46    print()
 47    print(f"Usage: python {sys.argv[0]} formula [output_format] [prec]\n")
 48
 49def update_vars():
 50    """
 51    概要: コマンドライン引数を解析し、グローバル変数 formula, output_format, prec を更新します。
 52
 53    詳細説明:
 54        sys.argv を読み込み、最初の引数から化学式または元素組成を設定します。
 55        オプションで、2番目の引数から出力フォーマット、3番目の引数から対称性の許容誤差を設定します。
 56        化学式が指定されていない場合はエラーメッセージを表示し、スクリプトを終了します。
 57    """
 58    global formula, output_format, prec # output_formuat は typo 修正せず既存コード通り
 59
 60    argv = sys.argv
 61    narg = len(argv)
 62    if narg <= 1:
 63        print("\nError: Chemical formula must be given as the first arg\n")
 64        input("Press ENTER to terminate>>\n")
 65        exit()
 66
 67    formula = argv[1]
 68    # 既存コードの typo に合わせる。output_formuat は output_format の間違いだが、変更しないルールによりそのまま
 69    if narg >= 3: output_format = argv[2]
 70    if narg >= 4: prec = float(argv[3])
 71
 72
 73def get_structure(formula):
 74    """
 75    概要: Materials Project APIから指定された化学式または元素組成に対応する結晶構造を取得します。
 76
 77    詳細説明:
 78        環境変数 MP_APIKEY からAPIキーを取得し、MPRester を使用してMaterials Projectに接続します。
 79        引数 formula がカンマ区切りで複数の元素を含む場合、それらの元素を含む構造を検索します。
 80        単一の化学式の場合、その化学式に一致する構造を検索します。
 81        見つかった構造は pymatgen.core.structure.Structure オブジェクトのリストとして返されます。
 82        APIキーの取得失敗、MPResterの初期化失敗、またはデータが見つからない場合はエラーメッセージを表示し、Noneを返します。
 83        検索結果の構造について、入力された元素がすべて含まれているかを確認し、含まれない場合は警告を出力してスキップします。
 84
 85    引数:
 86        :param formula: 検索対象の化学式 (例: "TiO2") またはカンマ区切りの元素組成 (例: "Ti,O")。
 87        :type formula: str
 88
 89    戻り値:
 90        :returns: 見つかった結晶構造のリスト、またはエラー発生時やデータが見つからない場合はNone。
 91        :rtype: list or None
 92    """
 93    API_KEY = os.getenv('MP_APIKEY')
 94    if API_KEY is None:
 95        print("\nError: Can not get MP API Key from the environment var MP_APIKEY\n")
 96        return None
 97
 98
 99    mpr = MPRester(API_KEY)
100    if mpr is None:
101        print(f"\nError: Can not get MPRester using the given API_KEY [{API_KEY}]\n")
102        input("Press ENTER to terminate>>\n")
103        exit()
104
105    if ',' in formula:
106        elements_in = formula.replace(' ', '').split(',')
107        search_results = mpr.materials.summary.search(elements = elements_in)
108    else:
109        elements_in = None
110        search_results = mpr.materials.search(formula = formula)
111    if not search_results:
112        print(f"No data found for {formula}")
113        return None
114
115    structures = []
116    for res in search_results:
117        material_id = res.material_id
118
119        material_data = mpr.materials.search(material_ids = [material_id])
120        if not material_data:
121            print(f"No structure data found for material_id {material_id}")
122            continue
123
124        structure_dict = material_data[0].structure.as_dict()
125        structure   = Structure.from_dict(structure_dict)
126        composition = structure.composition
127        cformula    = composition.reduced_formula
128#        cformula = structure.reduced_formula    #formula.replace(" ", "")
129#        composition = Composition(cformula)
130        elements_res = [element.symbol for element in composition.elements]
131        if elements_in:
132#            print("elements_res=", elements_res)
133#            print("elements_in=", elements_in)
134            not_included = []
135            for e in elements_res:
136#                print("  e=", e)
137                if e not in elements_in: not_included.append(e)
138
139            if len(not_included) >= 1:
140                print(f"  ** One or more elements are not included for [{cformula}] in the given condition [{formula}]:", not_included)
141                continue
142            else:
143                print(f"  ** Found [{cformula}] [{material_id}]")
144
145        structure.cmaterial = res
146        structure.material_id = material_id
147        structure.cformula  = cformula
148        structure.celements = elements_res
149        structures.append(structure)
150        cformula = structure.formula.replace(" ", "")
151        print(f"Found material_id for {formula}: {material_id}: {cformula}")
152
153    return structures
154
155
156def save_structures(structures, output_format, formula):
157    """
158    概要: 取得した結晶構造を指定されたフォーマットでファイルに保存します。
159
160    詳細説明:
161        渡された結晶構造のリストを反復処理し、各構造を output_format に従ってファイルに書き出します。
162        SpacegroupAnalyzer を使用して構造を対称化してから保存します。
163        出力ファイル名は、構造の簡略化学式とMaterial IDに基づいて命名されます。
164        output_format が "poscar" の場合、最初のファイルは "POSCAR"、2番目以降は "POSCAR_2" などとなります。
165        その他のフォーマットの場合、"化学式_material_id.フォーマット" の形式で保存されます。
166
167    引数:
168        :param structures: 保存する結晶構造オブジェクトのリスト。
169        :type structures: list
170        :param output_format: 出力ファイルのフォーマット (例: "cif", "poscar")。
171        :type output_format: str
172        :param formula: 元の検索に使用された化学式 (ファイル名の一部として利用される可能性がありますが、実際はcformulaとmaterial_idが使われます)。
173        :type formula: str
174    """
175    print()
176    for i, structure in enumerate(structures):
177        cformula = structure.cformula
178        if output_format == "poscar":
179            if i == 0:
180                output_path = f"POSCAR"
181            else:
182                output_path = f"POSCAR_{i+1}"
183        else:
184            output_path = f"{cformula}_{structure.material_id}.{output_format}"
185            '''
186            if i == 0:
187                output_path = f"{cformula}.{output_format}"
188            else:
189                output_path = f"{cformula}_{i+1}.{output_format}"
190            '''
191
192        symmetry_analyzer = SpacegroupAnalyzer(structure, symprec = prec) #, angle_tolerance = 5.0)
193        symmetrized_structure = symmetry_analyzer.get_symmetrized_structure()
194#        print()
195#        print("symmetrized_structure:")
196#        print(symmetrized_structure)
197        
198        print(f"Saving {cformula} structure to [{output_path}]...")
199#        structure.to(filename = output_path, fmt = output_format)
200        writer = CifWriter(symmetrized_structure, symprec = prec) #, angle_tolerance = 5.0)
201        writer.write_file(output_path)
202
203
204def main():
205    """
206    概要: スクリプトの主要な実行フローを制御します。
207
208    詳細説明:
209        update_vars を呼び出してコマンドライン引数を処理し、グローバル変数を設定します。
210        次に、get_structure を使用してMaterials Projectから結晶構造を取得します。
211        構造が正常に取得された場合、save_structures を呼び出してそれらの構造をファイルに保存します。
212        最後に、usage 関数を呼び出してスクリプトの使用方法を表示し、終了プロンプトを表示してユーザーの入力を待ちます。
213    """
214    update_vars()
215
216    structures = get_structure(formula)
217    if structures:
218        # output_formuat は typo なのでそのまま渡す
219        save_structures(structures, output_format, formula)
220
221    usage()
222    print()
223    input("Press ENTER to terminate>>\n")
224    exit()
225
226
227if __name__ == "__main__":
228    main()