##はじめに
###この記事で書くこと
前回は、一番基本的な使い方ということで、ElementとCompositionについて書いた。
この記事では、Materials Projectからデータを引っ張ってくる方法について学ぶ。この記事を読む人がMaterials Projectについて知らないことはないと思うので、説明は割愛するが、簡単に言えば、計算ベースの無機材料データベースだ。
今回の記事では、このデータベースから、化合物情報を検索し、手元で扱えるように持ってくることを目指す。
###使用するバージョン
pymatgen 2022.0.8
2020.x.xくらいのバージョンだと関数の呼び出し方が違っていてエラーが起きるかもしれないので注意。
##実際の操作
###前準備 -Materials Projectへの登録とAPIキーの入手-
pymatgenを使ってデータを持ってくるには、まず (i) Materials Projectへの登録 を行った後、(ii) APIキーの取得 という手順を踏む。(i)については、特別なことは何もなく、上に示したプロジェクトのページからメールアドレスで登録するか、GoogleやGithubのアカウントでログインする。
(ii)については、少し説明を加える。Mterials Projectにログインすると、上の方に、下図のようなバーがあるのがわかると思う。
この中の、'API'と書かれたところをクリックすると、文字だらけのページに遷移する。その中から、'API keys'という見出しの部分に、以下のような文言がある:
To use the MAPI, you need to first have an API key. We have detected that you already have the following API key generated:
この直後に、ランダムなような文字列が、(多分、赤字で)表示されていると思う。これがAPIキーと呼ばれるものだ。pymatgenからデータベースにアクセスするときは、このキーを使うので、どこかにコピって置いておくか、このページをそのままにしておく。
###pymatgenを使ったデータの取得
それでは、早速データをとってこよう。ここでは、MPResterというものを使う
#MPRester:Materials Projectからデータを持ってくるためのモジュール
from pymatgen.ext.matproj import MPRester
#APIは自分のやつを持ってくる
API_KEY='さっきメモった文字列'
基本的な考え方は、「検索クエリをかけると、ヒットした結果を返してくる」というものになる。しばらく弄っている感じ、無機化合物を検索する場合に思いつく検索方法は、大体実装されていると考えてよさそう。例えば、組成式で検索をかけるなら、以下のようになる。
#特定の組成式で化合物を検索するパターン
with MPRester(API_KEY) as m:
mats = m.get_data('BaTiO3')
すると、matsという変数に、検索結果のデータが収納された。要するに、
with MPRester(API_KEY) as m:
mats = m.get_data(検索文字列)
という書式で、検索することができる。ここでまず気になるのは、このmatsという変数の中に、どのようにデータが格納されていて、どうやってデータを取り出すか、ということだ。まずは型を確認する。
print(type(mats))
#出力結果:<class 'list'>
なるほど、リスト型でデータが格納されたらしい。ということは、中のデータを取り出すには、例えば以下のようにしてやればいい。
display(mats[0])
#出力結果:
#{'energy': -31.02144054,
# 'energy_per_atom': -6.204288108,
# 'volume': 85.16422489125648,
# 'formation_energy_per_atom': -1.6848189356666665,
# 'nsites': 5,
# 'unit_cell_formula': {'Ba': 1.0, 'Ti': 1.0, 'O': 3.0},
# 'pretty_formula': 'BaTiO3',
# 'is_hubbard': False,
# 'elements': ['Ba', 'Ti', 'O'],
# 'nelements': 3,
# 以下略
print(type(mats[0]))
#出力結果:<class 'dict'>
ぱっと見で、これが何がしか1種類の化合物の情報の羅列であることがわかる。さらに、型が辞書型である。つまり、matsというリストには、化合物1つにつき1つの辞書でデータが格納されていることになる。ということは、さらに以下のようにすれば、ある化合物の、特定の特性を読み出すことができる。
print(mats[0]['pretty_formula'])
#出力結果:BaTiO3
さて、実際の使用を考えると、検索結果を一覧して、「今、自分が欲しい化合物」がどれかを調べるには、組成式、晶系、空間群くらいがわかればよいだろう。また、元のmats(リスト)の何番目を指定すれば、そのデータが手に入るか、も気になるところ。というわけで、以下のようなコードを走らせて、ここら辺の情報を一覧する。
for i, mat in enumerate(mats):
print(i, mat['pretty_formula'], mat['spacegroup']['crystal_system'], mat['spacegroup']['symbol'])
#出力結果:
#0 BaTiO3 tetragonal P4/mmm
#1 BaTiO3 tetragonal P4mm
#2 BaTiO3 orthorhombic Amm2
#3 BaTiO3 orthorhombic Amm2
#4 BaTiO3 cubic Pm-3m
#5 BaTiO3 orthorhombic C222_1
#6 BaTiO3 orthorhombic Amm2
#7 BaTiO3 hexagonal P6_3/mmc
#8 BaTiO3 trigonal R3m
#9 BaTiO3 orthorhombic Amm2
#10 BaTiO3 cubic Pm-3m
これで、matsに格納されているデータを頭から順番に見て、組成式、晶系、空間群を得ることができた。(今のケースでは、組成式は全部同じだけど。)この中から1つピックアップして、情報を取り出してみることにする。せっかくなので、室温強誘電のBaTiO3を選ぶことにする。これは空間群P4mmなので、mats[1]の化合物である。
ferroel_BTO = mats[1]
print(ferroel_BTO['band_gap'])
print(ferroel_BTO['density'])
display(ferroel_BTO['diel'])
#出力結果:
#1.8297999999999996
#5.748582891575844
#{'e_electronic': [[6.267598999999999, 0.0, 0.0],
# [0.0, 6.267598999999999, 0.0],
# [0.0, 0.0, 5.163823999999999]],
# 'e_total': [[7.6526499999999995, 0.0, 0.0],
# [0.0, 7.6526499999999995, 1.3552527156068805e-20],
# [0.0, 1.3552527156068805e-20, 19.243076999999996]],
# 'n': 2.428924453333203,
# 'poly_electronic': 5.899673999999998,
# 'poly_total': 11.516125666666666}
代表的な物性ということで、バンドギャップ、密度、誘電率テンソルを持ってきてみたが、実測とは結構ズレているので、使い方には注意が必要という印象。さて、ここまで動かしてみて、「Materials Projectのサイトよりもデータ少なくない?DoSは?」という感想を持った。調べてみたら、ちゃんと表示する方法はある、、、が、長くなりそうなので、それは別のチュートリアルに書くことにする。
###cifの取得方法
すでに気づいている人は気づいていると思うが、持ってきたデータの中には、cifのデータが入っている。cifを保存できれば、構造の情報は全て持ってこれると言っても過言ではない。
display(ferroel_BTO['cif'])
長いので、出力結果は省略するが、よく見るcifの内容が出力される。これを、保存しておいて使いたいので、以下のようなコードを走らせよう。ここはpymatgenの使い方というより、一般的なPythonの使い方なので、詳しくは省略するが、path_output_dirで指定したフォルダの中に、cifを保存している。(あえて新しいフォルダを作るのは、cifが増えたときに収集がつかなくなるから。)
#フォルダを扱うためのモジュール
import os
#cifを保存する先のフォルダの名前を指定する
path_output_dir = "./cif/"
#その名前のフォルダが存在しなければ新しく作成し、あればそこに保存する。
if not os.path.exists(path_output_dir):
os.makedirs(path_output_dir)
with open(path_output_dir+ferroel_BTO['material_id']+".cif", mode="w") as f:
f.write(ferroel_BTO['cif'])
cifから色々とデータを取り出すのは、また次のチュートリアルで書くことにする。
###他の検索の仕方
####化合物に含まれる元素で検索
「合成してみたら知らないXRDピークが出てきた。これはなんや。」といった場合に、参考データを持ってくるのに使える。こっちの例なら、一覧に組成式を表示している意味も出てくる。
#含まれる元素で化合物を検索するパターン
with MPRester(API_KEY) as m:
mat_sys_BZO = m.get_data('Ba-Zr-O')
#組成式、晶系、空間群を表示する
for i, mat in enumerate(mat_sys_BZO):
print(i, mat['pretty_formula'], mat['spacegroup']['crystal_system'], mat['spacegroup']['symbol'])
#出力結果:
#0 Ba6ZrO8 cubic Fm-3m
#1 BaZrO3 cubic Pm-3m
#2 Ba2Zr7O16 trigonal R-3
#3 Ba4ZrO6 trigonal R-3c
#4 Ba2ZrO4 tetragonal I4/mmm
#5 Ba2ZrO6 tetragonal P4/mmm
#6 BaZrO3 tetragonal I4/mcm
#7 Ba3Zr2O7 tetragonal I4/mmm
####ワイルドカードを使って検索
例えば、新しく材料を合成する際、候補を探してくる時などに使える。「*」(アスタリスク)がワイルドカードで、以下の例では、一般式ABO3を持つ化合物を検索している。
#ワイルドカードを使って検索するパターン
with MPRester(API_KEY) as m:
ABO3 = m.get_data('**O3')
#それぞれの化合物のデータがリストに格納される
print(len(ABO3))
for i, mat in enumerate(ABO3[:5]):
print(i, mat['pretty_formula'], mat['spacegroup']['crystal_system'], mat['spacegroup']['symbol'])
#出力結果:
#2509
#0 AcAlO3 cubic Pm-3m
#1 AcBO3 cubic Pm-3m
#2 AcCrO3 cubic Pm-3m
#3 AcCuO3 cubic Pm-3m
#4 AcFeO3 cubic Pm-3m
格納されたデータの量が多い(len(ABO3)で示した通り、2509件ヒットしてる)ので、最初の5つだけ表示している。ワイルドカードに添字をつけることも可能で、「Fe2」などとすれば、一般式FeX2を持つ化合物を検索することができる。ちなみに、「化合物に含まれる元素を使って検索」する場合でもワイルドカードを使うことができ、例えば「-Fe-O」などのようにクエリをかける。
公式ドキュメントによると、さらに高度な検索オプションを使用することができるが、以上のような検索ができれば、データをざっくり持ってくることに支障はない。(それに、そこに手を出すと、この記事が長くなりすぎる。)というわけで、この記事はここまでにする。また必要になったら、別記事として書くかもしれない。