目標
POSCARファイルから,炭酸イオンを含むどうかを判定するアルゴリズムを作成する.
前準備
0-1.元素種C, Oを含むPOSCARファイルから,POSCAR.nnlistを作成し,CとOの結合距離の分布図を描く.
0-2.1.より炭酸イオンのCO間の結合距離の最大値を推定する.(注)
(注)アルゴリズムには直接関係ないため,必須でない.
結合探索アルゴリズム
1.POSCARファイルが元素種C, Oを含む
2.POSCAR.nnlistにおいて,原子Cから0-2.のCO結合距離以内に,原子Cに対するNeigborsListに原子Oを3つ以上含む,中心原子Cが存在する.
####### 条件1:原子Cから原子Oに対して,結合手がちょうど3本生えているか? #######
3.2.の中心原子Cに対して,
1番近い原子がOであり,かつ2番目に近い原子もOであり,かつ3番目に近い原子もOである,中心原子Cが存在する.
4.2.の中心原子Cに対して,4番目に近い原子が
存在しない場合 → 条件1をクリア.
存在する場合 → 5.に進む.
5.2.の中心原子Cに対して,4番目に近い原子とCとの距離が,3番目に近い原子(:酸素O ∵手順3.)とのCO距離より大きい.
→ 条件1をクリア.
####### 条件2:C周りの3つのOすべてが,CO結合距離内において,中心のC以外と結合していないか? #######
6.3.の3つの原子O全てに対して,3.の中心の原子Cとの距離以内に,中心原子C以外の別の原子が存在しない.
→ 条件2をクリア.
→ 「条件1かつ条件2を満たす.⇔ 炭酸イオンを含む」.?
前準備ためのプログラムの実行(流れ)
- 0-1-1.cif/ディレクトリ内の,元素種C, Oを含むPOSCARファイルパスをリストで取得.
元素種C,Oを含むPOSCARファイルのパスのみ抽出(ログ) #Python3 - Qiita
- 0-1-2.0-1-1.で得たパスリストに対して,
poscar2nnlistを用いて,5Å以内のネイバーリスト(:POSCAR.nnlist)を作成.
POSCARからneighbors listの作成(ログ) #Python3 - Qiita
-
0-1-3.0-1-2.で得たPOSCAR.nnlistから,原子Cと原子Oの距離を抽出し,NumPyのndarrayに格納.
(注)POSCAR.nnlistはPandasのDataFrame化して処理する. -
0-1-4.0-1-3.で得たndarrayを1次元にflattenし,ヒストグラムを描画.
- 0-2.0-1-4.より炭酸イオンのCO間の結合距離の最大値を推定する.
ヒストグラムから,炭素酸素間結合距離の閾値として,1.6Åとするのが妥当だと考えられる.
一方で,炭酸イオンの熱化学半径は1.64Åと書かれている.
これらより,目安となる炭素酸素間結合距離の最大値を1.65Åと仮定して進める.
アルゴリズムの実装
pwd
/mnt/ssd_elecom_c2c_960gb/scripts
mkdir algorithm_bond_search
cd algorithm_bond_search
まず,サンプルのPOSCAR.nnlistファイルをダウンロードする.
sudo git clone https://github.com/k-morii-toridai/sample_test_files.git
ls -CF sample_test_files/
1000033.cif* POSCAR* POSCAR.nnlist*
続いて,自作のファイル変換パッケージをダウンロードする.
sudo git clone https://github.com/k-morii-toridai/package_file_conversion.git
ls -CF package_file_conversion/
__init__.py* __pycache__/ df2poscar.py* nnlist2df.py* poscar2df.py* textfile2df.py*
炭酸イオンを含むか判定のalgolithmを作成した.
import numpy as np
def filter_2(df_nnlist):
"""
2.POSCAR.nnlistにおいて,原子Cから0-2.のCO結合距離以内に,原子Oを3つ以上含む,中心原子Cが存在するかどうか判定.
→ 存在する場合、True値,{中心原子Cの'central_atom_id': そのneighborsの'central_atom_id'}の辞書の2つを返す.
→ 存在しない場合,False値,空の辞書を返す.
Usage:
------
bool_2, dict_2 = filter_2(df_nnlist=df_nnlist)
Parameters:
-----------
df_nnlist: pd.DataFrame
Returns:
--------
bool_2: bool
dict_2: dict
"""
df_nnlist_group_dict = df_nnlist[df_nnlist['central_atom_symbol'] == 'C'].groupby('central_atom_id').groups
df_nnlist_central_atom_ids = np.array(list(df_nnlist_group_dict.keys()))
bool_list = []
for key in df_nnlist_central_atom_ids:
bool_list.append(df_nnlist.iloc[df_nnlist_group_dict[key]]['neighboring_atom_symbol'].tolist().count('O') >= 3)
df_nnlist_central_atom_ids_fillterd = df_nnlist_central_atom_ids[bool_list]
bool_filter_2 = len(df_nnlist_central_atom_ids_fillterd) >= 1
filtered_df_nnlist_group_dict = {key: df_nnlist_group_dict[key] for key in df_nnlist_group_dict.keys() if key in df_nnlist_central_atom_ids_fillterd}
return bool_filter_2, filtered_df_nnlist_group_dict
def filter_3(df_nnlist, dict_2):
"""
3.2.の中心原子Cに対して,1番近い原子がOであり,かつ2番目に近い原子もOであり,かつ3番目に近い原子もOである,中心原子Cが存在するかどうか判定.
→ 存在する場合,True値,{中心原子Cの'central_atom_id': そのneighborsの'central_atom_id'}の辞書の2つを返す.
→ 存在しない場合,False値,空の辞書を返す.
Usage:
------
bool_3, dict_3 = filter_3(df_nnlist=df_nnlist, dict_2=dict_2)
Parameters:
-----------
df_nnlist: pd.DataFrame
dict_2: dict
Returns:
--------
bool_3: bool
dict_3: dict
"""
bool_list_3 = []
for k, v in dict_2.items():
bool_list_3.append(set(df_nnlist.iloc[dict_2[k]].sort_values(by='rel_distance')['neighboring_atom_symbol'].tolist()[1:4]) == {'O'})
df_nnlist_central_atom_ids_fillterd_3 = np.array(list(dict_2.keys()))[bool_list_3]
filtered_3_df_nnlist_group_dict = {key: dict_2[key] for key in dict_2.keys() if key in df_nnlist_central_atom_ids_fillterd_3}
bool_filter_3 = len(df_nnlist_central_atom_ids_fillterd_3) >= 1
return bool_filter_3, filtered_3_df_nnlist_group_dict
def filter_4(df_nnlist, dict_3):
"""
4.2.の中心原子Cに対して4番目に近い原子が存在しないかを判定.
→ 存在しない場合,True値,{中心原子Cの'central_atom_id': そのneighborsの'central_atom_id'}の辞書の2つを返す.
→ 存在する場合,False値,空の辞書の2つを返す.
Usage:
------
bool_4, dict_4 = filter_4(df_nnlist=df_nnlist, dict_3=dict_3)
Parameters:
-----------
df_nnlist: pd.DataFrame
dict_3: dict
Returns:
--------
bool_4: bool
dict_4: dict
"""
bool_list_4 = []
for k, v in dict_3.items():
bool_list_4.append(len(df_nnlist.iloc[dict_3[k]].sort_values(by='rel_distance')['neighboring_atom_symbol'].tolist()) == 4)
df_nnlist_central_atom_ids_fillterd_4 = np.array(list(dict_3.keys()))[bool_list_4]
filtered_4_df_nnlist_group_dict = {key: dict_3[key] for key in dict_3.keys() if key in df_nnlist_central_atom_ids_fillterd_4}
bool_filter_4 = len(df_nnlist_central_atom_ids_fillterd_4) >= 1
return bool_filter_4, filtered_4_df_nnlist_group_dict
def filter_5(df_nnlist, dict_3):
"""
5.2.の中心原子Cに対して4番目に近い原子が,Cに3番目に近い原子OとCのCO距離より大きいCが存在するどうかを判定.
→ 存在する場合,True値,{中心原子Cの'central_atom_id': そのneighborsの'central_atom_id'}の辞書の2つを返す.
→ 存在しない場合,False値,空の辞書を返す.
Usage:
------
bool_5, dict_5 = filter_5(df_nnlist=df_nnlist, dict_3=dict_3)
Parameters:
-----------
df_nnlist: pd.DataFrame
dict_3: dict
Returns:
--------
bool_5: bool
dict_5: dict
"""
bool_list_5 = []
for k, v in dict_3.items():
third_CO_bond_dist = df_nnlist.iloc[dict_3[k]].sort_values(by='rel_distance')['rel_distance'].tolist()[3]
forth_dist = df_nnlist.iloc[dict_3[k]].sort_values(by='rel_distance')['rel_distance'].tolist()[4]
bool_list_5.append(third_CO_bond_dist < forth_dist)
df_nnlist_central_atom_ids_fillterd_5 = np.array(list(dict_3.keys()))[bool_list_5]
filtered_5_df_nnlist_group_dict = {key: dict_3[key] for key in dict_3.keys() if key in df_nnlist_central_atom_ids_fillterd_5}
bool_filter_5 = len(df_nnlist_central_atom_ids_fillterd_5) >= 1
return bool_filter_5, filtered_5_df_nnlist_group_dict
def filter_6(df_nnlist, dict_3):
"""
6.3.の3つの原子O全てに対して,3.の中心の原子Cとの距離以内に,中心原子C以外の別の原子が存在しないかどうかを判定.
→ 存在しない場合,True値,中心原子Cの'central_atom_id'のndarrayの2つを返す.
→ 存在する場合,False値,空のndarrayを返す.
Usage:
------
bool_6, C_ids = filter_6(df_nnlist=df_nnlist, dict_3=dict_3)
Parameters:
-----------
df_nnlist: pd.DataFrame
dict_3: dict
Returns:
--------
bool_6: bool
C_ids: ndarray
"""
bool_list_6 = []
for k, v in dict_3.items():
# C周りのO3つのindex
indices = df_nnlist.iloc[dict_3[k]].sort_values(by='rel_distance').index[1:4]
O_ids = df_nnlist.iloc[indices].apply(lambda row: row['neighboring_atom_id'], axis=1).tolist()
bool_list_temp = []
for O_id in O_ids:
bool_temp = df_nnlist[df_nnlist['central_atom_id'] == O_id].sort_values('rel_distance')['neighboring_atom_symbol'].tolist()[1] == 'C'
bool_list_temp.append(bool_temp)
if set(bool_list_temp) == {True}:
bool_list_6.append(True)
else:
bool_list_6.append(False)
C_ids = np.array(list(dict_3.keys()))[bool_list_6]
bool_filter_6 = bool_list_6.count(True) >= 1
return bool_filter_6, C_ids
def concat_filter(df_nnlist):
"""
filter_2()~filter_6()の関数を用いて,POSCAR.nnlistを用いて,POSCARファイルに炭酸イオンを含むかどうかの判定algolismを作成.
→ True値が返された場合,炭酸イオンを含む.
→ False値が返され場合,炭酸イオンを含まない.
Usage:
------
concat_filter(df_nnlist=df_nnlist)
Parameters:
-----------
df_nnlist: pd.DataFrame
Returns:
--------
bool: True or False
"""
bool_2, dict_2 = filter_2(df_nnlist=df_nnlist)
if bool_2:
bool_3, dict_3 = filter_3(df_nnlist=df_nnlist, dict_2=dict_2)
if bool_3:
bool_4, dict_4 = filter_4(df_nnlist=df_nnlist, dict_3=dict_3)
if bool_4:
return True
else:
bool_5, dict_5 = filter_5(df_nnlist=df_nnlist, dict_3=dict_3)
if bool_5:
bool_6, C_ids = filter_6(df_nnlist=df_nnlist, dict_3=dict_3)
if bool_6:
return True
else:
return False
else:
return False
else:
return False
else:
return False
if __name__ == '__main__':
from package_file_conversion.nnlist2df import nnlist2df
nnlist_path = 'sample_test_files/1000033/nnlist_5/POSCAR.nnlist'
df_nnlist = nnlist2df(nnlist_path=nnlist_path)
print(f"concat_filter(df_nnlist=df_nnlist): {concat_filter(df_nnlist=df_nnlist)}")
concat_filter(df_nnlist=df_nnlist)
炭酸イオンを含むと判定した場合は,True値を
含まないと判定した場合は,False値を返す.