0
0

炭酸イオンかどうかの判定アルゴリズムの作成(ログ)

Last updated at Posted at 2023-12-09

目標

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し,ヒストグラムを描画.

Histgram_of_distances_between_carbon_and_oxygen_100dpi_s5.png

Histgram_of_distances_between_carbon_and_oxygen_under200pm_100dpi_s5.png

  • 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値を返す.

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0