RDKitのxyz2molでケモインフォマティクスと計算化学の橋渡し

02_ケモインフォマティクス

我々が化合物を扱う際に用いている構造式は,人間の目にはわかりやすいですがコンピュータにはわかりにくいものです.そのためコンピュータにわかりやすい形式で化合物を表現してあげることが重要となりますが,用途によって望ましい形式が変わってきます.

例えばケモインフォマティクスのように分子をグラフ構造として扱う場合には,原子間の結合情報が重要となります.一方で第一原理(ab initio)計算では,原子の位置が重要であり結合情報は不要です.そのため計算化学からケモインフォマティクス分野へと化合物情報を渡す際には,原子間の結合情報を推定する必要があります.

今回はRDKitの2022.09のアップデートから追加された「xyz2mol」について見ていきたいと思います.その名の通り,XYZ形式をRDKitのMOLオブジェクトへと変換するメソッドになります.RDKitでは2019.09のアップデートでMOLオブジェクトからXYZ形式への書き出しに対応しましたから,今回のアップデートで双方向の変換が可能となりました.

ケモインフォマティクスで使われる様々なファイル形式については,「ケモインフォマティクスで使われるファイル形式と化合物の描画方法」という記事にまとめています.参照してみてください.
ケモインフォマティクスで使われるファイル形式と化合物の描画方法
ケモインフォマティクスにおいてコンピュータで化合物を扱うためには,コンピュータが理解しやすい形式で化合物情報を伝える必要があります. 通常我々が使っている構造式は,人間の眼には視覚的にわかりやすい表現方法ですが,コンピュータにとってはわかりにくいため,別の表現方法が必要になります...

XYZファイルとは

XYZファイルとは拡張子が「.xyz」となるファイル形式で,各原子の3次元空間における座標をオングストローム単位で記述することで,分子の構造を表現したものになります.直交座標系のXYZ軸に沿った原子の位置を示すことから,デカルト座標系(Cartesian coordinate)とも呼びます.

特に計算化学の分野でよく使われる入力形式ですが,標準化された規格がないため,ソフトウェアによってそのフォーマットがやや異なるのが難点になります.

原子の座標を与えれば,原子間の距離がわかりますので,どの原子間に「結合」が存在するかを推定することが可能です.そのためXYZ形式のみで分子構造を完全に表現できると言えます.

通常,分子軌道法やDFT計算のインプットファイルにはXYZ形式のみの指定で十分です.一方で分子力学法計算では,各原子に力場パラメータを当てはめる際に結合種類の情報が必要になりますので,結合リストの情報が必要になります.

基本フォーマット

XYZ形式で一般的に広く用いられるフォーマットは下記のようになります.今回扱うRDKitでの入出力もこのフォーマットに対応しています.

分子内の原子数
コメント行
元素記号 x座標 y座標 z座標
元素記号 x座標 y座標 z座標
・・・

中心をなすのが以下の

元素記号 (空白) x座標 (空白) y座標 (空白) z座標

という部分です.

XYZ形式とZ-マトリックスは分子の立体構造を表す入力フォーマット」という記事ではXYZ形式やZ-マトリックスといった量子化学計算でよく使われるファイルフォーマットについて説明しています.参照してみてください.
XYZ形式とZ-マトリックスは分子の立体構造を表す入力フォーマット
ほとんどの分子は3次元空間に広がった立体構造を有していますので,分子の3次元構造を表記することが重要になります.そのために使われる表現方法として大きく 直交座標系に沿った表現 内部座標に基づいた表現 の2つの座標系の取り方が考えられます.今回はこの2つの方法について見ていきたいと...

xyz2molとは

もともとはコペンハーゲン大学のJan H. Jensenの研究グループにより開発されたpythonライブラリで,xyzファイルをもとに原子間の結合情報を推定し分子グラフを構築します.

こちらの論文に書かれているアルゴリズムを実装したもになります.
Universal Structure Conversion Method for Organic Molecules: From Atomic Connectivity to Three-Dimensional Geometry
Yeonjoon Kim, Woo Youn Kim Bull. Korean Chem. Soc. 2015, 36, 1769-1777.

分子のグラフ構造が得られれば,それをもとにsmilesへと変換したり,RDKitのMOLオブジェクトへは容易に変換可能です.

この度,Google Summer of Code 2022の成果を受けて,2022.09バージョンからRDKitに統合されました.

Open Babelを使って化学情報フォーマットを変換」という記事ではXYZ形式に留まらず多様な化学系フォーマットの変換に対応しているOpen Babelというソフトについて解説しています.参照してみてください.
Open Babelを使って化学情報のファイル形式を変換
ケモインフォマティクスではコンピュータで化学構造を扱います.我々が普段扱っている構造式はコンピュータに優しくないため,必然的に別のフォーマットに変換して処理させることになります. 「MOLファイル・SDFとはどんな化学情報ファイルなのか?」という記事では「コネクションテーブル」に...

準備

今回は以下の環境でコードを実行しています.計算に用いるCPUやメモリは各自の環境に合わせて適宜変更してください.

import sys
import numpy as np

from rdkit import rdBase, Chem
from rdkit.Chem import AllChem, Draw, rdDetermineBonds
from rdkit.Chem.Draw import IPythonConsole

import psi4
import py3Dmol

print(f'python version:\n{sys.version}')
print(f'numpy version: {np.__version__}')
print(f'rdkit version: {rdBase.rdkitVersion}')
print(f'psi4 version: {psi4.__version__}')

psi4.set_num_threads(8)
psi4.set_memory('16GB')

# python version:
# 3.10.6 | packaged by conda-forge | (main, Aug 22 2022, 20:38:29) [Clang 13.0.1 ]
# numpy version: 1.23.4
# rdkit version: 2022.09.2
# psi4 version: 1.7

xyz2molの使い方

Chem.MolFromXYZFile(xyz-file)
Chem.MolFromXYZBlock(xyz-blocck)
Cheem.rdDetermineBonds.DetermineBonds(mol)

既に有志の方々による2022.09のリリースノート翻訳に簡単な使い方の説明が記されています.基本的な使い方としては,

  1. MolFromXYZFile/Blockで原子位置を保持したMOLオブジェクトを作成
  2. DetermineBondsで原子間の結合を推定

という2段階工程になります.

今回はエタノールアミンを例としてみましょう.

まず

  1. smilesからRDKitのMOLオブジェクトを作成
  2. 水素原子の付加
  3. 立体構造の生成

を行い,xyz形式にて出力します.

mol = Chem.MolFromSmiles('NCCO')
mol_h = Chem.AddHs(mol)
AllChem.EmbedMolecule(mol_h)
xyz = Chem.MolToXYZBlock(mol_h)
print(xyz)

この段階でのXYZ形式は以下の通りでした.

11

N      1.535699   -0.024779   -0.379784
C      0.355985    0.237892    0.404670
C     -0.875027   -0.439099   -0.136693
O     -1.981254   -0.150007    0.659909
H      2.114964    0.851773   -0.481723
H      2.182283   -0.723676    0.082840
H      0.549831   -0.206596    1.420569
H      0.180192    1.315725    0.589495
H     -1.123522   -0.146303   -1.159337
H     -0.748728   -1.541147   -0.105222
H     -2.190422    0.826217    0.656805

続いて作成したXYZブロックをもとに,RDKitのMOLオブジェクト作成を試みます.

mol2 = Chem.MolFromXYZBlock(xyz)
print(Chem.MolToSmiles(mol2))
# C.C.N.O.[HH].[HH].[HH].[HH].[HH].[HH].[HH]

SMILESからもわかるように,この段階では結合情報がありません.

Mol from xyz

結合情報がないことはMOLブロックからもわかります.

     RDKit          3D

 11  0  0  0  0  0  0  0  0  0999 V2000
    1.5357   -0.0248   -0.3798 N   0  0  0  0  0  0  0  0  0  0  0  0
    0.3560    0.2379    0.4047 C   0  0  0  0  0  0  0  0  0  0  0  0
   -0.8750   -0.4391   -0.1367 C   0  0  0  0  0  0  0  0  0  0  0  0
   -1.9813   -0.1500    0.6599 O   0  0  0  0  0  0  0  0  0  0  0  0
    2.1150    0.8518   -0.4817 H   0  0  0  0  0  0  0  0  0  0  0  0
    2.1823   -0.7237    0.0828 H   0  0  0  0  0  0  0  0  0  0  0  0
    0.5498   -0.2066    1.4206 H   0  0  0  0  0  0  0  0  0  0  0  0
    0.1802    1.3157    0.5895 H   0  0  0  0  0  0  0  0  0  0  0  0
   -1.1235   -0.1463   -1.1593 H   0  0  0  0  0  0  0  0  0  0  0  0
   -0.7487   -1.5411   -0.1052 H   0  0  0  0  0  0  0  0  0  0  0  0
   -2.1904    0.8262    0.6568 H   0  0  0  0  0  0  0  0  0  0  0  0
M  END

続いてDetermineBondsメソッドを用いることで原子間の結合情報を推定します.

rdDetermineBonds.DetermineBonds(mol2)
print(Chem.MolToMolBlock(mol2))

MOLブロックに結合情報が賦与されました.

    RDKit          3D

 11 10  0  0  0  0  0  0  0  0999 V2000
    1.5357   -0.0248   -0.3798 N   0  0  0  0  0  0  0  0  0  0  0  0
    0.3560    0.2379    0.4047 C   0  0  1  0  0  0  0  0  0  0  0  0
   -0.8750   -0.4391   -0.1367 C   0  0  1  0  0  0  0  0  0  0  0  0
   -1.9813   -0.1500    0.6599 O   0  0  0  0  0  0  0  0  0  0  0  0
    2.1150    0.8518   -0.4817 H   0  0  0  0  0  0  0  0  0  0  0  0
    2.1823   -0.7237    0.0828 H   0  0  0  0  0  0  0  0  0  0  0  0
    0.5498   -0.2066    1.4206 H   0  0  0  0  0  0  0  0  0  0  0  0
    0.1802    1.3157    0.5895 H   0  0  0  0  0  0  0  0  0  0  0  0
   -1.1235   -0.1463   -1.1593 H   0  0  0  0  0  0  0  0  0  0  0  0
   -0.7487   -1.5411   -0.1052 H   0  0  0  0  0  0  0  0  0  0  0  0
   -2.1904    0.8262    0.6568 H   0  0  0  0  0  0  0  0  0  0  0  0
  1  2  1  0
  1  5  1  0
  1  6  1  0
  2  3  1  0
  2  7  1  1
  2  8  1  0
  3  4  1  0
  3  9  1  6
  3 10  1  0
  4 11  1  0
M  END

具体例:RDKitとpsi4の連携

XYZ形式は計算化学の分野で汎用されるフォーマットですので,今度はpythonの量子化学計算用ライブラリであるpsi4を用いて構造最適化を行い,その構造を基にRDKitのMOLオブジェクトを作成してみます.

分子としては以下のようにビフェニルを母骨格としてオルト位が塩素原子で置換された化合物を用い,塩素原子の置換程度による二面角の変化を調べてみます.

Cl biphenyl

以下のコードでは

  1. psi4を用いて各分子をHF/STO-3Gレベルで構造最適化,振動数計算を行い安定構造であることを確認
  2. 最適化構造をxyz形式で保存
  3. 保存したxyzファイルからRDKitを用いてMOLオブジェクトを作成
  4. 芳香環を鋳型として各分子の座標を合わせる
  5. py3Dmolを用いて立体構造を描画

の順番で処理をしていきます.

psi4を用いた構造最適化・振動数計算

psi4.geometry(structure)
psi4.optimize(level-of-theory, molecule)
psi4.frequency(level-of-theory, molecule, return_wfn=bool)
psi4.Molecule.save_string_xyz_file()

本ブログでは「計算化学にpythonとpsi4で入門」という記事からはじめて,psi4の使い方を取り上げています.構造最適化,振動数計算については以下の記事でそれぞれ解説していますので,詳細を知りたい方は参照してみてください.

以下のコードでは

  1. psi4のMoleculeオブジェクトを作成
  2. HF/STO-3Gレベルで構造最適化
  3. 最適化構造を用いてHF/STO-3Gレベルで振動数計算
  4. 負の振動数がないことを確認
  5. 最適化構造をxyzファイルに保存

という順番で処理を行っています.

biphenyl = psi4.geometry('''
0 1
 C                 -1.60677167    0.57674840    0.00000000
 C                 -0.20435967    0.57674840    0.00000000
 C                  0.49918233    1.77936240    0.00000000
 C                 -0.18689967    2.99365840   -0.00276100
 C                 -1.58155267    3.00175740   -0.00410600
 C                 -2.28951367    1.80169940   -0.00129900
 C                 -2.34720267   -0.68369560    0.00125500
 C                 -1.91583367   -1.76170960   -0.78523500
 C                 -2.62398167   -2.96161460   -0.78348600
 C                 -3.76696067   -3.10067760    0.00359000
 C                 -4.20140767   -2.03351860    0.78943600
 C                 -3.49817067   -0.83068560    0.78885600
 H                  0.34069133   -0.37912460    0.00768300
 H                  1.59903833    1.76928540    0.00327200
 H                  0.37006933    3.94170340   -0.00385900
 H                 -2.12584767    3.95754040   -0.00849600
 H                 -3.38981967    1.81245940   -0.00794900
 H                 -1.01781667   -1.65416360   -1.41200100
 H                 -2.27983967   -3.80040660   -1.40621400
 H                 -4.32385867   -4.04876460    0.00450100
 H                 -5.10115267   -2.14040360    1.41300000
 H                 -3.84113667    0.00700840    1.41453700
''')

Cl_biphenyl = psi4.geometry('''
0 1
 C                 -1.60677167    0.57674840    0.00000000
 C                 -0.20435967    0.57674840    0.00000000
 C                  0.49918233    1.77936240    0.00000000
 C                 -0.18689967    2.99365840   -0.00276100
 C                 -1.58155267    3.00175740   -0.00410600
 C                 -2.28951367    1.80169940   -0.00129900
 C                 -2.34720267   -0.68369560    0.00125500
 C                 -1.91583367   -1.76170960   -0.78523500
 C                 -2.62398167   -2.96161460   -0.78348600
 C                 -3.76696067   -3.10067760    0.00359000
 C                 -4.20140767   -2.03351860    0.78943600
 C                 -3.49817067   -0.83068560    0.78885600
 H                  0.34069133   -0.37912460    0.00768300
 H                  1.59903833    1.76928540    0.00327200
 H                  0.37006933    3.94170340   -0.00385900
 H                 -2.12584767    3.95754040   -0.00849600
 H                 -3.38981967    1.81245940   -0.00794900
 H                 -1.01781667   -1.65416360   -1.41200100
 H                 -2.27983967   -3.80040660   -1.40621400
 H                 -4.32385867   -4.04876460    0.00450100
 H                 -5.10115267   -2.14040360    1.41300000
 Cl                -4.04672734    0.50916334    1.78960093
''')

Cl2_biphenyl = psi4.geometry('''
0 1
 C                 -1.60677167    0.57674840    0.00000000
 C                 -0.20435967    0.57674840    0.00000000
 C                  0.49918233    1.77936240    0.00000000
 C                 -0.18689967    2.99365840   -0.00276100
 C                 -1.58155267    3.00175740   -0.00410600
 C                 -2.28951367    1.80169940   -0.00129900
 C                 -2.34720267   -0.68369560    0.00125500
 C                 -1.91583367   -1.76170960   -0.78523500
 C                 -2.62398167   -2.96161460   -0.78348600
 C                 -3.76696067   -3.10067760    0.00359000
 C                 -4.20140767   -2.03351860    0.78943600
 C                 -3.49817067   -0.83068560    0.78885600
 H                  0.34069133   -0.37912460    0.00768300
 H                  1.59903833    1.76928540    0.00327200
 H                  0.37006933    3.94170340   -0.00385900
 H                 -2.12584767    3.95754040   -0.00849600
 H                 -3.38981967    1.81245940   -0.00794900
 H                 -2.27983967   -3.80040660   -1.40621400
 H                 -4.32385867   -4.04876460    0.00450100
 H                 -5.10115267   -2.14040360    1.41300000
 Cl                -4.04672734    0.50916334    1.78960093
 Cl                -0.47950275   -1.58969543   -1.78771423
''')

Cl4_biphenyl = psi4.geometry('''
0 1
 C                 -1.60677167    0.57674840    0.00000000
 C                 -0.20435967    0.57674840    0.00000000
 C                  0.49918233    1.77936240    0.00000000
 C                 -0.18689967    2.99365840   -0.00276100
 C                 -1.58155267    3.00175740   -0.00410600
 C                 -2.28951367    1.80169940   -0.00129900
 C                 -2.34720267   -0.68369560    0.00125500
 C                 -1.91583367   -1.76170960   -0.78523500
 C                 -2.62398167   -2.96161460   -0.78348600
 C                 -3.76696067   -3.10067760    0.00359000
 C                 -4.20140767   -2.03351860    0.78943600
 C                 -3.49817067   -0.83068560    0.78885600
 H                  1.59903833    1.76928540    0.00327200
 H                  0.37006933    3.94170340   -0.00385900
 H                 -2.12584767    3.95754040   -0.00849600
 H                 -2.27983967   -3.80040660   -1.40621400
 H                 -4.32385867   -4.04876460    0.00450100
 H                 -5.10115267   -2.14040360    1.41300000
 Cl                -4.04672734    0.50916334    1.78960093
 Cl                -0.47950275   -1.58969543   -1.78771423
 Cl                -4.04939739    1.81890948   -0.01193534
 Cl                 0.66742196   -0.95212235    0.01228857
''')


def check_img(frequencies: np.ndarray) -> bool:
    if np.sum(frequencies < 0) == 0:
        return True
    else:
        return False


mols = [biphenyl, Cl_biphenyl, Cl2_biphenyl, Cl4_biphenyl]

for i, mol in enumerate(mols):
    print('----------------------------------------')
    psi4.set_output_file(f'mol_{i+1}.log')
    print(f'## start optimization of No.{i + 1} ##')
    print(f'initial structure:\n{mol.save_string_xyz()}\n')
    e, wfn = psi4.optimize('hf/sto-3g', molecule=mol, return_wfn=True)
    print(f'optimized structure:\n{mol.save_string_xyz()}\n')
    print('## start freq of No.{i + 1} ##')
    e, wfn = psi4.frequency('hf/sto-3g', molecule=mol,
                            ref_gradient=wfn.gradient(),
                            return_wfn=True)

    if not check_img(np.array(wfn.frequencies())):
            print('##################################')
            print('##### OPTIMIZATION FAILED !! #####')
            print('##################################')
            break

    with open(f'optimized_{i + 1}.xyz', 'w') as file:
            print(mol.save_string_xyz_file(), file=file)

これでそれぞれの分子の最適化構造を収めたxyzファイルが得られましたので,続いてRDKitによる読み込みを行ってみます.

RDKitを用いてxyzファイルの読み込みとpy3Dmolを用いた立体構造の重ね合わせ表示

AllChem.AlignMol(prbMol, refMol, atomMap)

今回のように共通の母骨格を有する異なる分子の重ね合わせにはRDKitのAllChem.AlignMolが便利です.AlignMolメソッドではコンフォマーの重ね合わせと同様にatomMapをオプションに与えます.異なる点は分子が異なるため,鋳型とマッチする原子の番号の対応を与える必要があります.下のコードではzipを用いています.

鋳型にする部分構造は片方の芳香環(原子番号0から5)を使い,最初の分子である無置換のビフェニルを基準に残りの分子を重ね合わせていきます.3次元構造の表示にはpy3Dmolを用います.

スキャホールド・ホッピングした分子群の重ね合わせなど,共通の母骨格を持たない場合にはOpen3D ALIGNが使えます.「RDKitでOpen3DALIGNを用いた立体構造の重ね合わせ」という記事で説明していますので,参照してみてください.
## XYZファイルからMOLオブジェクトの作成
import glob
files = glob.glob('*.xyz')
mols = []
for file in files:
    mol = Chem.MolFromXYZFile(file)
    rdDetermineBonds.DetermineBonds(mol)
    mols.append(mol)

## 片方の芳香環を鋳型とした立体構造の重ね合わせ
for mol in mols[1:]:
    AllChem.AlignMol(prbMol=mol,
                    refMol=mols[0],
                    atomMap=list(zip(range(5), range(5)))
                    )

## py3Dmolによる描画
view = py3Dmol.view(width=400, height=400)
for mol in mols:
    view.addModel(Chem.MolToMolBlock(mol), 'sdf')

view.setStyle({'stick': {}})
view.zoomTo()
view.setBackgroundColor('0xeeeeee')
view.show()

重ね合わせた結果は下のようになりました.塩素原子の数が増えるにつれて二面角が直角に近づいていくのが見てとれます.

Py3dmol overlaid

RDKitを用いて制約付きで立体構造を生成する」という記事で説明したように,rdMolTransforms.GetDihedralDegを用いて実際に各分子の二面角を取得してみます.

from rdkit.Chem import rdMolTransforms
for i, mol in enumerate(mols):
    print(f'dihedral angle of {i + 1}: {rdMolTransforms.GetDihedralDeg(mol.GetConformer(-1),1, 0, 6, 11): .1f}')

塩素原子の数が増えるにつれて二面角が小さくなることを数値の面からも確認できました.

dihedral angle of 1:  141.3
dihedral angle of 2:  125.3
dihedral angle of 3:  108.5
dihedral angle of 4:  90.1

終わりに

今回は「RDKitのxyz2molでケモインフォマティクスと計算化学の橋渡し」という話題について,2022.09のアップデートからRDKitに追加されたxyz2molに関し

  • xyzファイルとはなにか
  • xyz2molとはなにか
    − RDKitのxyz2molの使い方
  • xyz2molを用いてpsi4とrdkit間でのデータ共有例

などについて,psi4の計算方法やRDKitのMOLオブジェクトの細かい使い方にも触れながら見てきました.

化合物をコンピュータで扱う際,目的によって適切なファイル形式が異なります.一方で人間が化合物を扱う際は,今回の最後の例でも見たように実際に眼で確認することが最もわかりやすいです.py3Dmolは分子の立体構造をJupyter Notebook上で確認することが可能なライブラリーです.引き続き使い方に関し,理解を深めていくのがよいと思います.

>>次の記事:「py3Dmolを使って化学構造をJupyter上で美しく表示する

コメント

タイトルとURLをコピーしました