autooperator

   
① phenix.mtz2map により input.mtz から map.ccp4 を作成

 ② auto_rebox.py により map.ccp4 を立方体(cubic_map.ccp4)に再グリッドし、その際の平行移動シフト(shift_vector)を出力

 ③ auto_operator.py により、MTZとPDBの対称性から再配列演算子(OPERATOR)とその逆(INVERSE_OPERATOR)を算出

 ④ phenix.pdbtools により、入力PDBに対して OPERATOR を適用し、さらに sites.translate で -shift_vector を反映して shifted_model.pdb を作成

 ⑤ (shifted_model.pdb を用いて任意のモデル精密化を実施)

 ⑥ 精密化後のモデル(refined_model.pdb)に対して、sites.translate(+shift_vector)および逆の再配列演算子 INVERSE_OPERATOR を適用し、元の座標系に戻して final_model.pdb として出力

auto_operator.py

#!/usr/bin/env phenix.python
import sys
from iotbx import mtz, pdb
from cctbx import crystal

def main():
  if len(sys.argv) < 3:
    print("Usage: auto_operator.py  ")
    sys.exit(1)
  mtz_file = sys.argv[1]
  pdb_file = sys.argv[2]

  # MTZファイルから結晶対称性情報を取得
  mtz_obj = mtz.object(mtz_file)
  mtz_sym = mtz_obj.crystal_symmetry()

  # PDBファイルから結晶対称性情報を取得
  pdb_inp = pdb.input(file_name=pdb_file)
  pdb_sym = pdb_inp.crystal_symmetry()

  # もし両者が一致していれば、変換は不要(identity operator)
  if mtz_sym.unit_cell() == pdb_sym.unit_cell() and \
     mtz_sym.space_group().type().lookup_symbol() == pdb_sym.space_group().type().lookup_symbol():
    operator_str = "a,b,c"
  else:
    try:
      # PDBからMTZへの変換演算子を自動算出
      op = pdb_sym.change_of_basis_op(mtz_sym)
      operator_str = op.as_string()
    except Exception as e:
      print("自動算出に失敗しました: ", e)
      operator_str = "a,b,c"

  # 逆変換演算子を算出
  op = crystal.change_of_basis_op(operator_str)
  inv_op = op.inverse()
  inverse_operator_str = inv_op.as_string()

  print("Determined change-of-basis operator: {}".format(operator_str))
  print("Inverse operator: {}".format(inverse_operator_str))

if __name__=="__main__":
  main()

auto_rebox.py

#!/usr/bin/env phenix.python
import sys, math
from iotbx import ccp4_map
from cctbx import maptbx
from scitbx.array_family import flex

def main():
    if len(sys.argv) < 3:
        print("Usage: auto_rebox.py input_map.ccp4 output_cubic_map.ccp4")
        sys.exit(1)
    input_map = sys.argv[1]
    output_map = sys.argv[2]
    
    # 元のマップ読み込み
    map_reader = ccp4_map.map_reader(input_map)
    map_data = map_reader.map_data.as_double()  # 3D flex.double array
    header = map_reader.header
    # 単位セルパラメータ (a,b,c,alpha,beta,gamma)
    a, b, c, alpha, beta, gamma = header.unit_cell_parameters
    nx, ny, nz = map_data.all()
    
    # 各軸の格子間隔
    spacing_x = a / nx
    spacing_y = b / ny
    spacing_z = c / nz
    # 解像度として最大の格子間隔を採用(保守的に)
    spacing = max(spacing_x, spacing_y, spacing_z)
    
    # 立方体とするため,新たなボックスサイズをL = max(a,b,c)に設定
    L = max(a, b, c)
    new_n = int(math.ceil(L / spacing))
    new_grid = (new_n, new_n, new_n)
    
    # 元のマップの物理中心を計算
    # 元のオフセット(header.origin)は、一般に物理的な原点位置(Å)として与えられている
    orig_origin = header.origin  # タプル (ox, oy, oz)
    center_phys = (orig_origin[0] + (nx*spacing_x)/2.0,
                   orig_origin[1] + (ny*spacing_y)/2.0,
                   orig_origin[2] + (nz*spacing_z)/2.0)
    
    # 新たな立方体マップのグリッド間隔は元と同じとし,物理中心を保つよう新オリジンを決定
    new_spacing = spacing
    new_origin = (center_phys[0] - new_n/2.0*new_spacing,
                  center_phys[1] - new_n/2.0*new_spacing,
                  center_phys[2] - new_n/2.0*new_spacing)
    
    # 新たな立方体マップ用の空配列を作成
    new_map = flex.double(flex.grid(new_grid))
    
    # 新たなグリッド各点の物理座標を計算し、元のマップから補間して新マップに格納(trilinear補間)
    for i in range(new_n):
        for j in range(new_n):
            for k in range(new_n):
                # 新グリッド点の物理座標
                x = new_origin[0] + i * new_spacing
                y = new_origin[1] + j * new_spacing
                z = new_origin[2] + k * new_spacing
                # 対応する元のグリッド内の位置(連続値)
                fx = (x - orig_origin[0]) / spacing_x
                fy = (y - orig_origin[1]) / spacing_y
                fz = (z - orig_origin[2]) / spacing_z
                # maptbxの補間関数を利用(trilinear補間)
                val = maptbx.interpolate_map_values(map_data, (fx, fy, fz))
                new_map[i,j,k] = val

    # 新たなマップヘッダー:立方体ユニットセル(L, L, L, 90,90,90)と新グリッド,新オリジンを設定
    new_unit_cell = (L, L, L, 90, 90, 90)
    new_header = header.customized_copy(unit_cell_parameters=new_unit_cell, origin=new_origin, grid=new_grid)
    
    # 新たな立方体マップを出力
    from iotbx import ccp4_map_writer
    ccp4_map_writer.write_ccp4_map_file(file_name=output_map, map_data=new_map, header=new_header)
    
    # 適用されたシフトは new_origin - orig_origin (物理座標での平行移動)
    shift_vector = (new_origin[0] - orig_origin[0],
                    new_origin[1] - orig_origin[1],
                    new_origin[2] - orig_origin[2])
    print("Applied shift vector (Å): {} {} {}".format(*shift_vector))

if __name__=="__main__":
    main()

 

auto_transform.sh

#!/bin/bash
# 使用例:
#   ./auto_transform.sh input.mtz input.pdb refined_model.pdb
# ※ refined_model.pdb は、shifted_model.pdbを用いて精密化済みのモデルファイルとします。

if [ "$#" -ne 3 ]; then
    echo "Usage: $0   "
    exit 1
fi

MTZ_FILE=$1
PDB_FILE=$2
REFINED_MODEL=$3

echo "=== Step 1: MTZファイルから電子密度マップ (map.ccp4) を作成 ==="
phenix.mtz2map ${MTZ_FILE} d_min=2.0 keep_origin=True map_file=map.ccp4

echo "=== Step 2: マップを立方体 (cubic_map.ccp4) に再グリッド ==="
phenix.python auto_rebox.py map.ccp4 cubic_map.ccp4 > rebox_info.txt
# 出力例の行:
# Applied shift vector (Å): 5.0  -3.2  2.7
SHIFT_LINE=$(grep "Applied shift vector" rebox_info.txt)
Sx=$(echo $SHIFT_LINE | awk '{print $4}')
Sy=$(echo $SHIFT_LINE | awk '{print $5}')
Sz=$(echo $SHIFT_LINE | awk '{print $6}')
echo "適用されたシフトベクトル: ${Sx} ${Sy} ${Sz} (Å)"

echo "=== Step 3: PDBファイルの座標を変換(再配列演算子+平行移動) ==="
# 自動で再配列演算子を算出
OP_INFO=$(phenix.python auto_operator.py ${MTZ_FILE} ${PDB_FILE})
OPERATOR=$(echo "$OP_INFO" | grep "Determined change-of-basis operator:" | awk '{print $5}')
INVERSE_OPERATOR=$(echo "$OP_INFO" | grep "Inverse operator:" | awk '{print $3}')

echo "算出された変換演算子: ${OPERATOR}"
echo "算出された逆変換演算子: ${INVERSE_OPERATOR}"

# ※ここでは、MTZ→map変換時にkeep_origin=Trueなら座標系は一致していると仮定し、
# さらにauto_rebox.pyで立方体化した際に適用された平行移動 (shift_vector) を反映します。
# そのため、PDB座標に対しては「change_of_basis」オプションで再配列演算子を(必要なら)適用し、
# さらに sites.translate で -shift_vector を適用します。
phenix.pdbtools ${PDB_FILE} change_of_basis="${OPERATOR}" sites.translate="-${Sx},-${Sy},-${Sz}" out=shifted_model.pdb

echo "shifted_model.pdb を作成しました。"
echo "=== Step 4: (ここで shifted_model.pdb を用いてモデル精密化を実施してください) ==="
echo "    ※精密化後のモデルは、引数で指定された ${REFINED_MODEL} とします。"

echo "=== Step 5: 精密化後のモデルを元の座標系に戻す ==="
# 逆変換:まず sites.translate で +shift_vector を適用し、その後逆の再配列演算子を適用
phenix.pdbtools ${REFINED_MODEL} sites.translate="${Sx},${Sy},${Sz}" change_of_basis="${INVERSE_OPERATOR}" out=final_model.pdb

echo "最終モデル (元のMTZ座標系に戻したもの) は final_model.pdb に出力されました。"

 

./auto_transform.sh input.mtz input.pdb refined_model.pdb

 

 

dfee

 

 

dfe

コメント