|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| import argparse
|
| import json
|
| import os
|
|
|
| from colabdesign.mpnn import clear_mem, mk_mpnn_model
|
| from ml_collections import ConfigDict
|
|
|
| from pxdbench.tools.biopython_utils import get_interface_residue_id, hotspot_residues
|
| from pxdbench.utils import extract_chain_sequence, seed_everything
|
|
|
|
|
| def get_pdb_basename(pdb_path: str):
|
| assert pdb_path.endswith(".pdb")
|
| basename = os.path.basename(pdb_path)
|
| return basename[:-4]
|
|
|
|
|
| def design_monomer(
|
| pdb_dir: str,
|
| pdb_names: list[str],
|
| num_samples: int,
|
| mpnn_cfg: ConfigDict,
|
| if_print=True,
|
| ):
|
| """
|
| Design sequences for monomer proteins using ProteinMPNN.
|
|
|
| Args:
|
| pdb_dir (str): Directory containing input PDB files.
|
| pdb_names (list[str]): List of PDB base names (without '.pdb') to process.
|
| num_samples (int): Number of sequences to generate per PDB.
|
| mpnn_cfg (ConfigDict): ProteinMPNN configuration (temperature, weights, etc.).
|
| if_print (bool, optional): Whether to print progress. Defaults to True.
|
|
|
| Returns:
|
| list[dict]: List of design results with keys 'name' (PDB name), 'seq_idx' (sequence index),
|
| and 'sequence' (designed amino acid sequence).
|
| """
|
| clear_mem()
|
| mpnn_model = mk_mpnn_model(
|
| backbone_noise=0.0,
|
| model_name="v_48_020",
|
| weights=mpnn_cfg.weights,
|
| )
|
|
|
| final_result = []
|
|
|
| for name in pdb_names:
|
| pdb_path = os.path.join(pdb_dir, name + ".pdb")
|
|
|
| mpnn_model.prep_inputs(
|
| pdb_filename=pdb_path,
|
| chain="A",
|
| )
|
|
|
| if if_print:
|
| print(f"{pdb_path} is done")
|
|
|
| temperature = mpnn_cfg.temperature
|
| if isinstance(temperature, str):
|
| if temperature == "auto":
|
| temperature = 0.0001 if num_samples > 1 else 0.1
|
| print(
|
| f"Use temperature {temperature} for num_samples being {num_samples}"
|
| )
|
| else:
|
| temperature = float(temperature)
|
| else:
|
| raise ValueError(f"Unknown temperature {temperature}")
|
|
|
|
|
| mpnn_sequences = mpnn_model.sample(
|
| temperature=temperature,
|
| num=num_samples,
|
| batch=1,
|
| )
|
|
|
|
|
| for i, seq in enumerate(mpnn_sequences["seq"]):
|
| final_result.append(
|
| {"name": name, "seq_idx": i, "sequence": seq.split("/")[-1]}
|
| )
|
|
|
| if if_print:
|
| print("finished all Sequence Design")
|
| return final_result
|
|
|
|
|
| def design_binder(
|
| pdb_dir: str,
|
| pdb_names: list[str],
|
| num_samples: int,
|
| binder_chains: list[str],
|
| cond_chains: list[str],
|
| mpnn_cfg: ConfigDict,
|
| if_print=True,
|
| ):
|
| """
|
| Design sequences for binder proteins using ProteinMPNN.
|
|
|
| Args:
|
| pdb_dir (str): Directory containing input PDB files.
|
| pdb_names (list[str]): List of PDB base names (without '.pdb') to process.
|
| num_samples (int): Number of sequences to generate per PDB.
|
| binder_chains (list[str]): List of binder chain IDs.
|
| cond_chains (list[str]): List of conditional chain IDs.
|
| mpnn_cfg (ConfigDict): ProteinMPNN configuration (temperature, weights, etc.).
|
| if_print (bool, optional): Whether to print progress. Defaults to True.
|
|
|
| Returns:
|
| list[dict]: List of design results with keys 'name' (PDB name), 'seq_idx' (sequence index),
|
| and 'sequence' (designed amino acid sequence).
|
| """
|
|
|
| clear_mem()
|
| mpnn_model = mk_mpnn_model(
|
| backbone_noise=0.0,
|
| model_name="v_48_020",
|
| weights=mpnn_cfg.weights,
|
| )
|
|
|
| final_result = []
|
|
|
| for name in pdb_names:
|
| pdb_path = os.path.join(pdb_dir, name + ".pdb")
|
|
|
| if len(binder_chains) > 1:
|
| raise ValueError(f"Only support one-chain binders, but got {binder_chains}")
|
| if mpnn_cfg.fix_interface:
|
| interacting_residues = hotspot_residues(
|
| pdb_path=pdb_path, binder_chain=binder_chains[0]
|
| )
|
| if len(interacting_residues) > 0:
|
| fix_pos = get_interface_residue_id(
|
| interacting_residues=interacting_residues,
|
| binder_chain=binder_chains[0],
|
| )
|
| else:
|
| fix_pos = ",".join(cond_chains)
|
|
|
| else:
|
| fix_pos = ",".join(cond_chains)
|
| mpnn_model.prep_inputs(
|
| pdb_filename=pdb_path,
|
| chain=",".join(cond_chains + binder_chains),
|
| fix_pos=fix_pos,
|
| rm_aa=mpnn_cfg.rm_aa,
|
| )
|
|
|
| if if_print:
|
| print(f"{pdb_path} is done")
|
|
|
| temperature = mpnn_cfg.temperature
|
| if isinstance(temperature, str):
|
| if temperature == "auto":
|
| temperature = 0.0001 if num_samples > 1 else 0.1
|
| print(
|
| f"Use temperature {temperature} for num_samples being {num_samples}"
|
| )
|
| else:
|
| temperature = float(temperature)
|
| else:
|
| raise ValueError(f"Unknown temperature {temperature}")
|
|
|
|
|
| mpnn_sequences = mpnn_model.sample(
|
| temperature=temperature,
|
| num=num_samples,
|
| batch=1,
|
| )
|
|
|
|
|
| for i, seq in enumerate(mpnn_sequences["seq"]):
|
| final_result.append(
|
| {"name": name, "seq_idx": i, "sequence": seq.split("/")[-1]}
|
| )
|
|
|
| if if_print:
|
| print("finished all Sequence Design")
|
|
|
| return final_result
|
|
|
|
|
| def get_gt_sequence(pdb_dir: str, pdb_names: list[str], binder_chain="B"):
|
| """
|
| Get ground truth sequences for binder proteins from PDB files.
|
|
|
| Args:
|
| pdb_dir (str): Directory containing input PDB files.
|
| pdb_names (list[str]): List of PDB base names (without '.pdb') to process.
|
| binder_chain (str, optional): Chain ID of the binder protein. Defaults to "B".
|
|
|
| Returns:
|
| list[dict]: List of design results with keys 'name' (PDB name), 'seq_idx' (sequence index),
|
| and 'sequence' (ground truth amino acid sequence).
|
| """
|
|
|
| final_result = []
|
| for name in pdb_names:
|
| sequence = []
|
| result = {}
|
| pdb_path = os.path.join(pdb_dir, name + ".pdb")
|
| seq = extract_chain_sequence(pdb_path, chain_id=binder_chain)
|
| sequence.append(seq)
|
| result["name"] = name
|
| result["sequences"] = sequence
|
| final_result.append({"name": name, "seq_idx": 0, "sequence": sequence[0]})
|
|
|
| return final_result
|
|
|
|
|
| def main():
|
| parser = argparse.ArgumentParser(description="ProteinMPNN Sequence Design")
|
| parser.add_argument("--input", type=str, required=True, help="Input JSON file")
|
| parser.add_argument("--output", type=str, required=True, help="Output JSON file")
|
| parser.add_argument("--seed", type=int, default=None)
|
|
|
| args = parser.parse_args()
|
|
|
| with open(args.input, "r") as f:
|
| input_data = json.load(f)
|
|
|
| if args.seed is not None:
|
| seed_everything(args.seed, deterministic=False)
|
| design_type = input_data["design_type"]
|
|
|
| try:
|
| if design_type == "monomer":
|
| result = design_monomer(
|
| pdb_dir=input_data["pdb_dir"],
|
| pdb_names=input_data["pdb_names"],
|
| num_samples=input_data["num_samples"],
|
| mpnn_cfg=ConfigDict(input_data["mpnn_cfg"]),
|
| if_print=True,
|
| )
|
| elif design_type == "binder":
|
| result = design_binder(
|
| pdb_dir=input_data["pdb_dir"],
|
| pdb_names=input_data["pdb_names"],
|
| num_samples=input_data["num_samples"],
|
| binder_chains=input_data["binder_chains"],
|
| cond_chains=input_data["cond_chains"],
|
| mpnn_cfg=ConfigDict(input_data["mpnn_cfg"]),
|
| if_print=True,
|
| )
|
| elif design_type == "gt":
|
| result = get_gt_sequence(
|
| pdb_dir=input_data["pdb_dir"],
|
| pdb_names=input_data["pdb_names"],
|
| binder_chain=input_data.get("binder_chain", "B"),
|
| )
|
| else:
|
| raise ValueError(f"Unknown design type: {design_type}")
|
|
|
| with open(args.output, "w") as f:
|
| json.dump(result, f)
|
|
|
| print(f"Successfully completed {design_type} design")
|
|
|
| except Exception as e:
|
| print(f"Error: {str(e)}")
|
| import traceback
|
|
|
| traceback.print_exc()
|
| exit(1)
|
|
|
|
|
| if __name__ == "__main__":
|
| main()
|
|
|