プログラム2

import os
import subprocess
import json
import numpy as np
import sys

def run_command(cmd):
    ret = subprocess.run(cmd, shell=True)
    if ret.returncode != 0:
        raise RuntimeError(f"Command failed: {cmd}")

def read_pdb_coordinates(pdb_file_path):
    coords = []
    atom_lines = []
    with open(pdb_file_path, 'r') as f:
        for line in f:
            if line.startswith("ATOM") or line.startswith("HETATM"):
                x = float(line[30:38])
                y = float(line[38:46])
                z = float(line[46:54])
                coords.append([x, y, z])
                atom_lines.append(line)
    return np.array(coords), atom_lines

def write_pdb_coordinates(atom_lines, coords, output_path):
    with open(output_path, 'w') as f:
        for line, c in zip(atom_lines, coords):
            new_line = (line[:30] +
                        f"{c[0]:8.3f}{c[1]:8.3f}{c[2]:8.3f}" +
                        line[54:])
            f.write(new_line)

def create_transformation(pixel_size_x, pixel_size_y, pixel_size_z, origin_shift):
    scale_x = 1.0 / pixel_size_x
    scale_y = 1.0 / pixel_size_y
    scale_z = 1.0 / pixel_size_z
    S = np.eye(4)
    S[0,0] = scale_x
    S[1,1] = scale_y
    S[2,2] = scale_z
    
    T = np.eye(4)
    T[0,3] = origin_shift[0]
    T[1,3] = origin_shift[1]
    T[2,3] = origin_shift[2]
    
    final_transform = S @ T
    return final_transform

def apply_transformation(coords, matrix):
    N = coords.shape[0]
    hom_coords = np.ones((N,4))
    hom_coords[:,:3] = coords
    transformed = hom_coords @ matrix
    return transformed[:,:3]

def invert_transformation(matrix):
    R = matrix[:3,:3]
    t = matrix[:3,3]
    R_inv = R.T
    t_inv = -R_inv @ t
    M_inv = np.eye(4)
    M_inv[:3,:3] = R_inv
    M_inv[:3,3] = t_inv
    return M_inv

def forward_procedure(
    mtz_file, pdb_file, phenix_path, output_map, output_transformed_pdb, 
    pixel_size_x, pixel_size_y, pixel_size_z, origin_shift, log_json
):
    # Step 1: Convert MTZ to map using phenix.mtz2map
    cmd_mtz2map = f"{phenix_path}/phenix.mtz2map {mtz_file}"
    run_command(cmd_mtz2map)
    
    # Assume the output from mtz2map is something like {basename}.map
    # If mtz_file is input.mtz, phenix.mtz2map typically generates something like input.map
    base = os.path.splitext(mtz_file)[0]
    input_map = f"{base}.map"
    
    # Step 2: Resample map to uniform pixel size using phenix.map_resample
    cmd_resample = f"{phenix_path}/phenix.map_resample {input_map} {output_map} pixel_size=1.0"
    run_command(cmd_resample)
    
    # The user must record pixel sizes and origin shift used or determined.
    # Here we assume pixel_size_x, pixel_size_y, pixel_size_z, and origin_shift are known.
    # In practice, one would parse phenix.map_resample output for these values.
    # We record them so we can invert later.
    
    # Step 3: Read original pdb coordinates
    original_coords, atom_lines = read_pdb_coordinates(pdb_file)
    
    # Step 4: Create forward transform
    T = create_transformation(pixel_size_x, pixel_size_y, pixel_size_z, origin_shift)
    
    # Step 5: Apply transformation to PDB coordinates
    transformed_coords = apply_transformation(original_coords, T)
    write_pdb_coordinates(atom_lines, transformed_coords, output_transformed_pdb)
    
    # Step 6: Save transformation data to JSON for later reversal
    data = {
        "mtz_file": mtz_file,
        "pdb_file": pdb_file,
        "phenix_path": phenix_path,
        "input_map": input_map,
        "resampled_map": output_map,
        "pixel_size_x": pixel_size_x,
        "pixel_size_y": pixel_size_y,
        "pixel_size_z": pixel_size_z,
        "origin_shift": origin_shift,
        "transform_matrix": T.tolist(),
        "transformed_pdb": output_transformed_pdb
    }
    with open(log_json, 'w') as f:
        json.dump(data, f, indent=4)

def backward_procedure(
    built_in_uniform_space_pdb, log_json, output_restored_pdb
):
    # Step 1: Load transformation data
    with open(log_json, 'r') as f:
        data = json.load(f)
    T_list = data["transform_matrix"]
    T = np.array(T_list)
    
    # Step 2: Invert transform
    T_inv = invert_transformation(T)
    
    # Step 3: Read newly built model in uniform space
    new_coords, new_atom_lines = read_pdb_coordinates(built_in_uniform_space_pdb)
    
    # Step 4: Apply inverse transform
    restored_coords = apply_transformation(new_coords, T_inv)
    write_pdb_coordinates(new_atom_lines, restored_coords, output_restored_pdb)


if __name__ == "__main__":
    # Example usage:
    # Forward:
    # python script.py forward input.mtz original.pdb /path/to/phenix output_resampled.map transformed.pdb 1.2 1.2 1.5 "-5.0 3.0 -2.0" transform_log.json
    #
    # Backward:
    # python script.py backward built_in_uniform_space.pdb transform_log.json restored_original_frame.pdb
    
    mode = sys.argv[1]
    if mode == "forward":
        mtz_file = sys.argv[2]
        pdb_file = sys.argv[3]
        phenix_path = sys.argv[4]
        output_map = sys.argv[5]
        output_transformed_pdb = sys.argv[6]
        pixel_size_x = float(sys.argv[7])
        pixel_size_y = float(sys.argv[8])
        pixel_size_z = float(sys.argv[9])
        origin_shift_str = sys.argv[10].strip()
        origin_shift = [float(x) for x in origin_shift_str.split()]
        log_json = sys.argv[11]
        forward_procedure(mtz_file, pdb_file, phenix_path, output_map, output_transformed_pdb,
                          pixel_size_x, pixel_size_y, pixel_size_z, origin_shift, log_json)
    elif mode == "backward":
        built_in_uniform_space_pdb = sys.argv[2]
        log_json = sys.argv[3]
        output_restored_pdb = sys.argv[4]
        backward_procedure(built_in_uniform_space_pdb, log_json, output_restored_pdb)
    else:
        raise ValueError("Mode must be either 'forward' or 'backward'.")

コメント