🪄

PDBファイルの糖鎖位置合わせ

に公開

データベースからダウンロードしてきたとあるタンパク質の一部です。

タンパク質本体から、1) GlcNAc-GlcNAc-Manの3つの糖、2) アセチルグルコサミンが伸びています。
PDBファイルから該当箇所を一部抜き出します。
1)

ATOM   2751  C5  NAG F   2     122.975 143.965 148.767  1.00 75.59           C
ATOM   2752  C6  NAG F   2     122.358 143.644 150.123  1.00 75.59           C
ATOM   2753  C7  NAG F   2     122.707 145.556 143.723  1.00 75.59           C
ATOM   2754  C8  NAG F   2     123.484 146.265 142.655  1.00 75.59           C
ATOM   2755  N2  NAG F   2     123.396 145.238 144.813  1.00 75.59           N
ATOM   2756  O3  NAG F   2     123.503 147.164 146.913  1.00 75.59           O
ATOM   2757  O4  NAG F   2     123.848 146.064 149.510  1.00 75.59           O
ATOM   2758  O5  NAG F   2     122.224 143.325 147.739  1.00 75.59           O
ATOM   2759  O6  NAG F   2     121.040 144.201 150.188  1.00 75.59           O
ATOM   2760  O7  NAG F   2     121.525 145.287 143.597  1.00 75.59           O
ATOM   2761  C1  BMA F   3     123.097 147.020 150.274  1.00 97.22           C
ATOM   2762  C2  BMA F   3     123.656 147.051 151.689  1.00 97.22           C
ATOM   2763  C3  BMA F   3     122.850 148.034 152.525  1.00 97.22           C
ATOM   2772  C1  NAG A 404      88.555 146.492 149.278  1.00 57.75           C
ATOM   2773  C2  NAG A 404      87.820 145.854 150.451  1.00 57.75           C
ATOM   2774  C3  NAG A 404      88.459 146.305 151.753  1.00 57.75           C
ATOM   2775  C4  NAG A 404      89.933 145.940 151.735  1.00 57.75           C
ATOM   2776  C5  NAG A 404      90.594 146.574 150.520  1.00 57.75           C
ATOM   2777  C6  NAG A 404      92.060 146.177 150.432  1.00 57.75           C
ATOM   2778  C7  NAG A 404      85.470 145.354 150.143  1.00 57.75           C
ATOM   2779  C8  NAG A 404      84.107 145.932 149.903  1.00 57.75           C
ATOM   2780  N2  NAG A 404      86.421 146.229 150.450  1.00 57.75           N
ATOM   2781  O3  NAG A 404      87.819 145.642 152.846  1.00 57.75           O
ATOM   2782  O4  NAG A 404      90.553 146.423 152.931  1.00 57.75           O
ATOM   2783  O5  NAG A 404      89.936 146.142 149.332  1.00 57.75           O
ATOM   2784  O6  NAG A 404      92.145 144.807 150.026  1.00 57.75           O
ATOM   2785  O7  NAG A 404      85.691 144.158 150.064  1.00 57.75           O

これをdoGlycansで処理します。

B=P.74/HD22:-(ND2,C1,<Aa>)-4YB
A=P.143/HD22:-(ND2,C1,<Af>)-4YB-(O4,C1,<Ff>)-4YB-(O4,C1,<a>)-0MA

Aa=ND2,C1,[OD1 CG ND2 C1 250.0  CG ND2 C1 C2 180]
Af=ND2,C1,[OD1 CG ND2 C1 250.0  CG ND2 C1 C2 150]
Ff= O4,C1,[C3 C4 O4 C1   120.0 C4 O4 C1 C2 180.0]
a = O4,C1,[C3 C4 O4 C1   120.0 C4 O4 C1 C2 120.0]

二面角は適当です。すると該当糖鎖はこうつきました。


異常に近づいていますね。もうちょっとマシな配置にしたいですが、doglycansのインプットの2面角はこれだけでも8か所あります。2面角の決め方、動き方を完全に理解していても毎回元のPDBファイルから該当箇所の2面角を測定してインプットファイルに反映するのは面倒です。
ていうかdoglycansの実行中にDihedralとtorsionがこれくらいでRotationしました、ていう出力があるのですが、ここの入力の数値を変えても反映されてる気配がなくて。。。

もとのデータベースから落としてきたファイルには一応CNO原子の位置は正しく記録されているので、いっそこの座標を拾ってきて上書きするというのはどうかと思いつきます。そこで、doglycansで出力されたPDBファイルを見てみます。

ATOM   5540  O6  4YB A 337     124.110 143.027 148.551  1.00  0.00           O
ATOM   5541  H6O 4YB A 337     123.868 142.597 147.726  1.00  0.00           H
ATOM   5542  C4  4YB A 337     127.556 144.075 147.289  1.00  0.00           C
ATOM   5543  H4  4YB A 337     127.221 144.915 146.677  1.00  0.00           H
ATOM   5544  C3  4YB A 337     128.672 143.306 146.543  1.00  0.00           C
ATOM   5545  H3  4YB A 337     129.063 142.526 147.200  1.00  0.00           H
ATOM   5546  O3  4YB A 337     129.761 144.238 146.255  1.00  0.00           O
ATOM   5547  H3O 4YB A 337     130.532 143.769 145.918  1.00  0.00           H
ATOM   5548  C2  4YB A 337     128.117 142.636 145.254  1.00  0.00           C
ATOM   5549  H2  4YB A 337     127.779 143.428 144.581  1.00  0.00           H
ATOM   5550  N2  4YB A 337     129.165 141.828 144.584  1.00  0.00           N
ATOM   5551  H2N 4YB A 337     129.369 140.920 144.966  1.00  0.00           H
ATOM   5552  C2N 4YB A 337     129.695 142.154 143.401  1.00  0.00           C
ATOM   5553  O2N 4YB A 337     129.434 143.212 142.840  1.00  0.00           O
ATOM   5554  CME 4YB A 337     130.694 141.147 142.796  1.00  0.00           C
ATOM   5555  H3M 4YB A 337     130.375 140.122 142.994  1.00  0.00           H
ATOM   5556  H2M 4YB A 337     130.759 141.288 141.716  1.00  0.00           H
ATOM   5557  H1M 4YB A 337     131.680 141.308 143.234  1.00  0.00           H
ATOM   5558  O4  4YB A 337     128.099 144.585 148.526  1.00  0.00           O
ATOM   5559  C1  0MA A 338     127.996 146.001 148.539  1.00  0.00           C
ATOM   5560  H1  0MA A 338     128.972 146.480 148.616  1.00  0.00           H
ATOM   5561  C2  0MA A 338     127.119 146.408 149.757  1.00  0.00           C
ATOM   5562  H2  0MA A 338     127.517 145.943 150.661  1.00  0.00           H
ATOM   5563  C3  0MA A 338     125.658 145.949 149.552  1.00  0.00           C
ATOM   5564  H3  0MA A 338     125.637 144.857 149.531  1.00  0.00           H

水素原子が増えたうえ、CNO原子も順番と名前が変わってしまっているため、単純にPDBファイルの対応行をとってきて座標のカラムを置き換えるだけとはいきません。水素原子の座標を推定しつつ、きちんと化学結合をたどって化学的に等価な原子なら座標を置換するという手順を踏まないといけません。

そこでRDKitをフル活用してやってみます。

一応、DoGlycansで正しく原子が付き元のデータベースのPDBの原子を再構築できるような、普通の糖鎖であることを前提としています。

大まかな流れ

  1. RCSBからダウンロードしたpdbファイルを処理していき、DoGlycansで糖を付けたタンパク質のpdbファイルを得ます。
  2. もとのPDBファイルと生成したPDBファイル、あと比較するべき残基の対応を書いたファイルを作っておきます。
  3. もとのPDBファイルには水素がついていませんので、3次元構造を考慮して水素を付けます。
  4. 対応する残基+αの原子を検索、それだけの分子を作ります。
  5. 分子構造の比較により、元のPDBファイルと生成したPDBファイルで対応する原子ペアを得ます。
  6. その原子ペア情報を使って、生成したPDBファイルの座標をもとのPDBファイルからとってきます。
  7. 結果を書き出し。

詳細

インターフェース

  • CNOなど水素以外の原子の座標が正しく記録されたPDBファイルを用意する。これを orig.pdb とする。
  • DoGlycanで処理してできたPDBファイルを ProtG.pdb とする。このファイル中の座標を orig.pdb のデータで上書きしたい。
  • orig.pdbProtG.pdb でどの残基同士が対応するか、また一続きの糖鎖はどれかを指定したファイルを用意、residue_list.txt とします。

前述のように原子数も順番も名前も違うため、「3次元座標で差がある原子・残基を検出して自動的にフィットする残基を検出」ができないので、参照用のPDBファイル、修正したいPDBファイルの他に修正対象の残基名リストを渡してやる必要があります。

residule_list.txtは以下のようなフォーマットにします。

residue_list.txt
335 A 404 g1
336 F 1 g2
337 F 2 g2
338 F 3 g2

これは、

  • ProtG.pdb の 335番目の残基が orig.pdb の Chain A の404番目の残基に相当して1つのかたまり g1 であること、
  • ProtG.pdb の 336~338番目の残基が orig.pdb のChain Fの1~3番目の残基に相当してまた別の1つの塊 g2 であること

を書いています。

この準備をしたのち、

python adjust_residue.py ProtG.pdb orig.pdb residue_list.txt

として実行するようなpythonコードを書いていきます。

main部

引数を3つ受け取るスクリプトファイルです。 main 関数の書き方をしています。
あと必要なライブラリをインポートします。

adjust_residue.pyの枠組み
import sys
import numpy as np
from rdkit import Chem
from rdkit.Chem import AllChem
from rdkit.Chem import rdFMCS  # 分子構造から原子対応をとるためのモジュール

def main():
    if len(sys.argv) != 4:
        print(f"usage: {sys.argv[0]} protG.PDB orig.PDB residue_list")
        return
    """
    (処理を書くところ)
    """

if __name__ == "__main__":
    main()

以下、「処理を書くところ」を埋めていきます。

ファイル読み込み

コマンドライン引数で渡したファイルを読み込みます。

まず、 sys.argv[1] のpdbファイルは座標を修正したい ProtG.pdb です。水素原子をそのままにしておきたいですし、残基同士が重なっているため結合情報も自動で検出してもらっては困ります。なのでいろいろなオプションを False にします。
一方、 sys.argv[2] のpdbファイルは座標の参考になる orig.pdb です。水素原子はありませんので、読み込み後 AddHs を適用します。そして参考用の座標データを取得します。

adjust_residue.py
    mod_mol = Chem.MolFromPDBFile(sys.argv[1], sanitize=False, removeHs=False, proximityBonding=False)
    ref_mol = Chem.MolFromPDBFile(sys.argv[2])
    ref_mol = Chem.AddHs(ref_mol, addCoords=True, addResidueInfo=True)
    ref_coord = np.array(ref_mol.GetConformer().GetPositions())

続いて、残基の対応を記録したファイルを読み込みます。

adjust_residue.py
    res_list_dict = {}
    with open(sys.argv[3], "r") as f:
        for L in f.readlines():
            cols = L.strip().split()
            g = cols[3]
            if not g in res_list_dict.keys():
                res_list_dict[g] = []
            res_list_dict[g].append((cols[0], cols[1], cols[2]))

例として、このようなデータが得られます。

res_list_dict
{'g1': [('335', 'A', '404')],
 'g2': [('336', 'F', '1'), ('337', 'F', '2'), ('338', 'F', '3')]}

処理全体の枠組み

糖鎖グループごとに処理をします。各糖鎖ごとに、orig.pdbProtG.pdb からRDkitのMolオブジェクトとして取り出し、分子構造のマッチング、 ProtG.pdb の座標を更新します。すべての糖鎖で処理ができれば、 modified.pdb に書き出します。まとめると、main関数は以下のような構造となります。

adjust_residue.py
def main():
    """(先ほどの読み込み部)"""
    for _, res_list in res_list_dict.items():
        """ orig.pdb から糖鎖スニペット ref_glycomol の作成 """
        """ ProtG.pdb から糖鎖スニペット mod_glycomol の作成 """
        """ 分子構造マッチング """
        """ mod_mol の座標の上書き """
    Chem.MolToPDBFile(mod_mol, "modified.pdb")

これからループ内の各部分を深堀します。

元となる ref_mol から糖鎖スニペットの作成

まず原子インデックスを格納する配列と糖鎖部分だけの分子オブジェクトを用意します。

adjust_residue.py
        ref_atom_idx = []
        ref_glycomol = Chem.RWMol()

ref_mol の該当する原子を分子構造マッチング用分子 ref_glycomol に追加しつつ、その ref_mol での AtmIdxref_atom_idx に順番を保って追加します。これで、分子構造で対応をとりつつ、元の ref_mol でのインデックスを得て座標を得ることができます。

ref_mol での原子をひとつづつ確認していきます。
指定したChain ID、残基番号でなければスキップします。

adjust_residue.py
        for a in ref_mol.GetAtoms():
            resinfo = a.GetPDBResidueInfo()
            if not resinfo.GetChainId() in ref_chains:
                continue
            if not f"{resinfo.GetResidueNumber()}" in ref_resids:
                continue

この if に該当しなければ対象の原子です。 ref_glycomol に追加する用の原子オブジェクトを生成し、追加します。その時同時に ref_atom_idx にも追加し、  ref_glycomol での AtmIdx をインデックスとして渡せば ref_mol での AtmIdx が得られるようにします。

adjust_residue.py
            new_atom = Chem.Atom(a.GetAtomicNum())
            new_atom.SetMonomerInfo(a.GetMonomerInfo())
            ref_glycomol.AddAtom(new_atom)
            ref_atom_idx.append(a.GetIdx())

続いてref_glycomol に結合を追加します。いま処理している原子 a が関わる結合をループして舐めていきますが、その時に結合先原子もすでに ref_glycomol にある結合だけ処理するようにします。こうすれば同じ結合を2回追加することはないと思います。

adjust_residue.py
            for bond in a.GetBonds():
                a1, a2 = bond.GetBeginAtom(), bond.GetEndAtom()
                if (not a2.GetIdx() in ref_atom_idx) or (not a1.GetIdx() in ref_atom_idx):
                    continue
                a1_newidx = ref_atom_idx.index(a1.GetIdx())
                a2_newidx = ref_atom_idx.index(a2.GetIdx())
                ref_glycomol.AddBond(a1_newidx, a2_newidx, bond.GetBondType())

これで ref_mol の原子チェックのループが終わりました。これを抜けたあと、 ref_glycomol の後処理をします。

分子スニペットの拡張

いろいろやって分かったのですが、doglycansの処理により、糖鎖の残基だけでなく、糖鎖が結合したアミノ酸残基の ND2 とさらにそれが付いたカルボニル基も回転しています。つまり、

(アミノ酸)-CO-NH-(糖鎖)

となったときのこの CO-NH も対象に加えないといけません。ということで、元の分子を見て結合3つ分拡張してやります。拡張する関数 expand_snippet を以下のように定義します。つまり、分子スニペットの原子を見て、結合先原子がまだスニペット(正確には index list)にいなければ追加するという処理を3回やります。
ただindex listにそのまま追加するとループ中に今しがた追加した原子について結合を調べ追加するという処理をしてしまい、1回のループで ref_mol の全体が入ってしまうので、ループはコピーしてから行います。あと、なんかアミノ酸中の C=O 結合がなんか orig.pdb だと単結合、 ProtG.pdb だと2重結合ということでもう単結合固定で追加していきます。

adjust_residue.py
def expand_snippet(molobj, snippetobj, index_list, N):
    for _ in range(N):
        for a1_idx, ai in enumerate(index_list.copy()):
            a = molobj.GetAtomWithIdx(ai)
            for na in a.GetNeighbors():
                if na.GetIdx() in index_list:
                    continue
                nai = na.GetIdx()
                new_atom = Chem.Atom(na.GetAtomicNum())
                new_atom.SetMonomerInfo(na.GetMonomerInfo())
                snippetobj.AddAtom(new_atom)
                index_list.append(nai)
                a2_idx = len(index_list) - 1
                snippetobj.AddBond(a1_idx, a2_idx, Chem.BondType.SINGLE)

この関数を使って結合3つ分拡張します。続いて編集可能な RWMol のオブジェクトだった ref_glycomol をラスタライズします。

adjust_residue.py
        expand_snippet(ref_mol, ref_glycomol, ref_atom_idx, 3)
        ref_glycomol = ref_glycomol.GetMol()

これで ref_mol から糖鎖部分を抜き出したオブジェクトを作れました。

この節は2重ループ(正確には3重ループ)中のコードをパートごとに分けて書いていますので、全体の流れが分かりにくいと思います。この記事の最後にプログラム全体をあげていますので、参考にしてください。

mod_mol から糖鎖スニペットの作成

ぶっちゃけ ref_molref_glycomolref_atom_idx をそれぞれ mod_molmod_glycomolmod_atom_idx に変えたらさっきとほとんど同じですので、省略します。
ChainIDによるチェックがないくらいでしょうか。
詳しくは最後のプログラム全体を参照してください。

あと、座標修正用に、 Conformer オブジェクトを取得しておきます。このオブジェクトの関数を使って mod_mol の原子座標を変更できます。

adjust_residue.py
        mod_coord = mod_mol.GetConformer()

分子構造マッチングと座標の上書き

マッチングに使うのは FindMCS 関数です。これは Mol オブジェクトを複数受け取り、共通部分の構造を得ることができます。
続いて、その構造をSMART表記で受け取り、SMART構造で検索できるように query オブジェクトとします。
これで元の mod_glycomolref_glycomol を検索すると当たり前にマッチしますが、そのとき原子の対応が SMARTを通して得られます。

adjust_residue.py
        mcs = rdFMCS.FindMCS([mod_glycomol, ref_glycomol])
        query = Chem.MolFromSmarts(mcs.smartsString)
        match1 = mod_glycomol.GetSubstructMatch(query)
        match2 = ref_glycomol.GetSubstructMatch(query)

match* はどうもSMARTに対応する AtmIdx の配列のようです。ということは、この match* での数字は糖鎖スニペット中でのインデックスですので、 mod_atom_idx ref_atom_idx リストを使って元のmod_molref_mol での AtmIdx を得ます。それを使って座標を上書きします。

adjust_residue.py
        for i in range(len(match1)):
            atm1_idx = match1[i]
            atm2_idx = match2[i]
            mod_atom = mod_atom_idx[atm1_idx]
            ref_atom = ref_atom_idx[atm2_idx]
            ref_atom_coord = ref_coord[ref_atom, :]
            mod_coord.SetAtomPosition(mod_atom, ref_atom_coord)

GetSubstructMatch を実行するの、元の mod_molref_mol でもいいかな・・・でも同じ配列の糖鎖が複数あったりしたら破綻するし、検索に時間かかったりするか・・・

ともあれ、これで一通りやりたいことの処理ができました。

結果

以上の内容をまとめて実装した pythonコードを実行しました。
最初に出した、原子同士が重なってしまっていた糖鎖の部分も、こんな感じで参考PDBファイルに従った位置に持ってくることができました。

左側が orig.pdb の糖鎖の部分、右側が今回のプログラムで修正した pdb の該当部分です。

コード全体

adjust_residue.py
import sys
import numpy as np
from rdkit import Chem
from rdkit.Chem import AllChem
from rdkit.Chem import rdFMCS

def expand_snippet(molobj, snippetobj, index_list, N):
    for _ in range(N):
        for a1_idx, ai in enumerate(index_list.copy()):
            a = molobj.GetAtomWithIdx(ai)
            for na in a.GetNeighbors():
                if na.GetIdx() in index_list:
                    continue
                nai = na.GetIdx()
                new_atom = Chem.Atom(na.GetAtomicNum())
                new_atom.SetMonomerInfo(na.GetMonomerInfo())
                snippetobj.AddAtom(new_atom)
                index_list.append(nai)
                a2_idx = len(index_list) - 1
                # snippetobj.AddBond(a1_idx, a2_idx, molobj.GetBondBetweenAtoms(ai, nai).GetBondType())
                snippetobj.AddBond(a1_idx, a2_idx, Chem.BondType.SINGLE)
def main():
    """(ファイル読み込み部)"""
    if len(sys.argv) != 4:
        print(f"usage: {sys.argv[0]} protG.PDB orig.PDB residue_list")
        return

    mod_mol = Chem.MolFromPDBFile(sys.argv[1], sanitize=False, removeHs=False, proximityBonding=False)
    ref_mol = Chem.MolFromPDBFile(sys.argv[2])

    ref_mol = Chem.AddHs(ref_mol, addCoords=True, addResidueInfo=True)
    # RDkitのバージョンが新しいとAddHsは以下のようにします:
    # AddHsParam = Chem.AddHsParameters()
    # AddHsParam.addCoords = True
    # AddHsParam.addResidueInfo = True
    # ref_mol = Chem.AddHs(ref_mol, AddHsParam)
    ref_coord = np.array(ref_mol.GetConformer().GetPositions())

    res_list_dict = {}
    with open(sys.argv[3], "r") as f:
        for L in f.readlines():
            cols = L.strip().split()
            g = cols[3]
            if not g in res_list_dict.keys():
                res_list_dict[g] = []
            res_list_dict[g].append((cols[0], cols[1], cols[2]))

    for _, res_list in res_list_dict.items():
        mod_resids = [r[0] for r in res_list]
        ref_chains = [r[1] for r in res_list]
        ref_resids = [r[2] for r in res_list]

        """ orig.pdb から糖鎖スニペット ref_glycomol の作成 """
        ref_atom_idx = []
        ref_glycomol = Chem.RWMol()
        for a in ref_mol.GetAtoms():
            resinfo = a.GetPDBResidueInfo()
            if not resinfo.GetChainId() in ref_chains:
                continue
            if not f"{resinfo.GetResidueNumber()}" in ref_resids:
                continue
            new_atom = Chem.Atom(a.GetAtomicNum())
            new_atom.SetMonomerInfo(a.GetMonomerInfo())
            ref_glycomol.AddAtom(new_atom)
            ref_atom_idx.append(a.GetIdx())
            for bond in a.GetBonds():
                a1, a2 = bond.GetBeginAtom(), bond.GetEndAtom()
                if (not a2.GetIdx() in ref_atom_idx) or (not a1.GetIdx() in ref_atom_idx):
                    continue
                a1_newidx = ref_atom_idx.index(a1.GetIdx())
                a2_newidx = ref_atom_idx.index(a2.GetIdx())
                ref_glycomol.AddBond(a1_newidx, a2_newidx, bond.GetBondType())
        expand_snippet(ref_mol, ref_glycomol, ref_atom_idx, 3)
        ref_glycomol = ref_glycomol.GetMol()

        """ ProtG.pdb から糖鎖スニペット mod_glycomol の作成 """
        mod_atom_idx = []
        mod_first_atom = None
        mod_glycomol = Chem.RWMol()
        for a in mod_mol.GetAtoms():
            resinfo = a.GetPDBResidueInfo()
            if not f"{resinfo.GetResidueNumber()}" in mod_resids:
                continue
            new_atom = Chem.Atom(a.GetAtomicNum())
            new_atom.SetMonomerInfo(a.GetMonomerInfo())
            mod_glycomol.AddAtom(new_atom)
            mod_atom_idx.append(a.GetIdx())
            for bond in a.GetBonds():
                a1, a2 = bond.GetBeginAtom(), bond.GetEndAtom()
                if a1.GetIdx() > a2.GetIdx():
                    continue
                if (not a2.GetIdx() in mod_atom_idx) or (not a1.GetIdx() in mod_atom_idx):
                    continue
                a1_newidx = mod_atom_idx.index(a1.GetIdx())
                a2_newidx = mod_atom_idx.index(a2.GetIdx())
                mod_glycomol.AddBond(a1_newidx, a2_newidx, bond.GetBondType())

        expand_snippet(mod_mol, mod_glycomol, mod_atom_idx, 3)
        mod_glycomol = mod_glycomol.GetMol()
        mod_coord = mod_mol.GetConformer()

        """ 分子構造マッチング """
        mcs = rdFMCS.FindMCS([mod_glycomol, ref_glycomol])
        query = Chem.MolFromSmarts(mcs.smartsString)
        match1 = mod_glycomol.GetSubstructMatch(query)
        match2 = ref_glycomol.GetSubstructMatch(query)

        """ mod_mol の座標の上書き """
        for i in range(len(match1)):
            atm1_idx = match1[i]
            atm2_idx = match2[i]
            mod_atom = mod_atom_idx[atm1_idx]
            ref_atom = ref_atom_idx[atm2_idx]
            ref_atom_coord = ref_coord[ref_atom, :]
            mod_coord.SetAtomPosition(mod_atom, ref_atom_coord)
    Chem.MolToPDBFile(mod_mol, "modified.pdb")

if __name__ == "__main__":
    main()

さいごに

これでやりたいこと(糖鎖も含めて絡み合ったタンパク質複合体)ができるかも?

Discussion