diff --git a/CHANGELOG.txt b/CHANGELOG.txt index 68c4c91e887ac527fb44c15547142349a8864f04..01e85e9cf5e605b142e1f032f30eb99e1776bf61 100644 --- a/CHANGELOG.txt +++ b/CHANGELOG.txt @@ -1,3 +1,23 @@ +Changes in Release 2.5.0 +-------------------------------------------------------------------------------- + + * A default compound library is set and the rule-base processor is set as the + default processor when the ost module is imported. As a result, OpenStructure + will behave more like a script run with the `ost` command in python scripts, + Jupyter notebooks, ipython sessions, etc. + * Global chain mapping and assignment based on RMSD only are available for + ligand scoring (both with the action and module). + * Additional chain and residue properties are read from mmCIF allowing basic + mapping between label and author chain and residue numbers. + * USalign support - as binding that calls external USalign executable or + direct injection of macromolecules on the C++ level. + * Added functionality in compare-structures action: backbone only lDDT, + flag to disable stereochemistry checks for lDDT, TMscore (including + associated chain mapping) computed by USalign, make residues/atoms uniquely + identifiable (also considering residue number insertion codes). + * Read atom charges if present from PDB/mmCIF/SDF files + * Several bug fixes and improvements. + Changes in Release 2.4.0 -------------------------------------------------------------------------------- diff --git a/CMakeLists.txt b/CMakeLists.txt index 83aa5d857cd44b803c1de242cfeb5cdbe018624b..aa7daa5413e1039eb3ec3181d824caa050686f3a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,7 +14,7 @@ cmake_policy(SET CMP0060 NEW) project(OpenStructure CXX C) set (CMAKE_EXPORT_COMPILE_COMMANDS 1) set (OST_VERSION_MAJOR 2) -set (OST_VERSION_MINOR 4) +set (OST_VERSION_MINOR 5) set (OST_VERSION_PATCH 0) set (OST_VERSION_STRING ${OST_VERSION_MAJOR}.${OST_VERSION_MINOR}.${OST_VERSION_PATCH} ) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${PROJECT_SOURCE_DIR}/cmake_support) diff --git a/actions/ost-compare-ligand-structures b/actions/ost-compare-ligand-structures index ee7575218cf7638617f91c89a07870463f1fcb7f..176cd5e1f6028c096d9b1bdc3dbd4b6f3fdce673 100644 --- a/actions/ost-compare-ligand-structures +++ b/actions/ost-compare-ligand-structures @@ -36,12 +36,12 @@ options, this is a dictionary with three keys: * "model_ligands": A list of ligands in the model. If ligands were provided explicitly with --model-ligands, elements of the list will be the paths to - the ligand SDF file(s). Otherwise, they will be the chain name and residue - number of the ligand, separated by a dot. + the ligand SDF file(s). Otherwise, they will be the chain name, residue + number and insertion code of the ligand, separated by a dot. * "reference_ligands": A list of ligands in the reference. If ligands were provided explicitly with --reference-ligands, elements of the list will be - the paths to the ligand SDF file(s). Otherwise, they will be the chain name - and residue number of the ligand, separated by a dot. + the paths to the ligand SDF file(s). Otherwise, they will be the chain name, + residue number and insertion code of the ligand, separated by a dot. * "status": SUCCESS if everything ran through. In case of failure, the only content of the JSON output will be \"status\" set to FAILURE and an additional key: "traceback". @@ -57,11 +57,8 @@ import os import traceback import ost -from ost import conop from ost import io from ost.mol.alg import ligand_scoring -from ost.mol.alg import Molck, MolckSettings - def _ParseArgs(): @@ -170,6 +167,32 @@ def _ParseArgs(): action="store_true", help=("Allow incomplete target ligands.")) + parser.add_argument( + "-gcm", + "--global-chain-mapping", + dest="global_chain_mapping", + default=False, + action="store_true", + help=("Use a global chain mapping.")) + + parser.add_argument( + "-c", + "--chain-mapping", + nargs="+", + dest="chain_mapping", + help=("Custom mapping of chains between the reference and the model. " + "Each separate mapping consist of key:value pairs where key " + "is the chain name in reference and value is the chain name in " + "model. Only has an effect if global-chain-mapping flag is set.")) + + parser.add_argument( + "-ra", + "--rmsd-assignment", + dest="rmsd_assignment", + default=False, + action="store_true", + help=("Use RMSD for ligand assignment.")) + parser.add_argument( "--lddt-pli", dest="lddt_pli", @@ -199,10 +222,10 @@ def _ParseArgs(): help=("lDDT inclusion radius for lDDT-PLI.")) parser.add_argument( - "--lddt-bs-radius", - dest="lddt_bs_radius", + "--lddt-lp-radius", + dest="lddt_lp_radius", default=4.0, - help=("lDDT inclusion radius for lDDT-BS.")) + help=("lDDT inclusion radius for lDDT-LP.")) parser.add_argument( '-v', @@ -212,6 +235,16 @@ def _ParseArgs(): default=3, help="Set verbosity level. Defaults to 3 (INFO).") + parser.add_argument( + "--n-max-naive", + dest="n_max_naive", + required=False, + default=12, + type=int, + help=("If number of chains in model and reference are below or equal " + "that number, the global chain mapping will naively enumerate " + "all possible mappings. A heuristic is used otherwise.")) + return parser.parse_args() @@ -234,12 +267,9 @@ def _LoadStructure(structure_path, format="auto", fault_tolerant=False): f"{structure_path}.") format = ext[-1].lower() - # increase loglevel, as we would pollute the info log with weird stuff - ost.PushVerbosityLevel(ost.LogLevel.Error) # Load the structure if format in ["mmcif", "cif"]: - entity, seqres = io.LoadMMCIF(structure_path, seqres=True, - fault_tolerant=fault_tolerant) + entity = io.LoadMMCIF(structure_path, fault_tolerant=fault_tolerant) if len(entity.residues) == 0: raise Exception(f"No residues found in file: {structure_path}") elif format == "pdb": @@ -250,22 +280,11 @@ def _LoadStructure(structure_path, format="auto", fault_tolerant=False): raise Exception(f"Unknown/ unsupported file extension found for " f"file {structure_path}.") - # Molck it - molck_settings = MolckSettings(rm_unk_atoms=True, - rm_non_std=False, - rm_hyd_atoms=True, - rm_oxt_atoms=False, - rm_zero_occ_atoms=False, - colored=False, - map_nonstd_res=False, - assign_elem=True) - # Cleanup a copy of the structures - Molck(entity, conop.GetDefaultLib(), molck_settings) - - # restore old loglevel and return - ost.PopVerbosityLevel() - entity.SetName(structure_path) - return entity + # Remove hydrogens + cleaned_entity = ost.mol.CreateEntityFromView(entity.Select("ele != H"), + include_exlusive_atoms=False) + cleaned_entity.SetName(structure_path) + return cleaned_entity def _LoadLigands(ligands): @@ -307,8 +326,23 @@ def _Validate(structure, ligands, legend, fault_tolerant=False): raise ValueError(msg) +def _QualifiedResidueNotation(r): + """Return a parsable string of the residue in the format: + ChainName.ResidueNumber.InsertionCode.""" + resnum = r.number + return "{cname}.{rnum}.{ins_code}".format( + cname=r.chain.name, + rnum=resnum.num, + ins_code=resnum.ins_code.strip("\u0000"), + ) + + def _Process(model, model_ligands, reference, reference_ligands, args): + mapping = None + if args.chain_mapping is not None: + mapping = {x.split(':')[0]: x.split(':')[1] for x in args.chain_mapping} + scorer = ligand_scoring.LigandScorer( model=model, target=reference, @@ -318,34 +352,65 @@ def _Process(model, model_ligands, reference, reference_ligands, args): check_resnames=args.enforce_consistency, rename_ligand_chain=True, substructure_match=args.substructure_match, + global_chain_mapping=args.global_chain_mapping, + rmsd_assignment=args.rmsd_assignment, radius=args.radius, lddt_pli_radius=args.lddt_pli_radius, - lddt_bs_radius=args.lddt_bs_radius + lddt_lp_radius=args.lddt_lp_radius, + n_max_naive=args.n_max_naive, + custom_mapping=mapping ) out = dict() if model_ligands is not None: # Replace model ligand by path - assert len(model_ligands) == len(scorer.model_ligands) - # Map ligand => path - model_ligands_map = {k: v for k, v in zip(scorer.model_ligands, - args.model_ligands)} - out["model_ligands"] = args.model_ligands + if len(model_ligands) == len(scorer.model_ligands): + # Map ligand => path + model_ligands_map = {k: v for k, v in zip(scorer.model_ligands, + args.model_ligands)} + out["model_ligands"] = args.model_ligands + elif len(model_ligands) < len(scorer.model_ligands): + # Multi-ligand SDF files were given + # Map ligand => path:idx + out["model_ligands"] = [] + for ligand, filename in zip(model_ligands, args.model_ligands): + assert isinstance(ligand, ost.mol.EntityHandle) + for i, residue in enumerate(ligand.residues): + out["model_ligands"].append(f"{filename}:{i}") + else: + # This should never happen and would be a bug + raise RuntimeError("Fewer ligands in the model scorer " + "(%d) than given (%d)" % ( + len(scorer.model_ligands), len(model_ligands))) else: - model_ligands_map = {l: "%s.%s" % (l.chain.name, l.number) + model_ligands_map = {l: _QualifiedResidueNotation(l) for l in scorer.model_ligands} out["model_ligands"] = list(model_ligands_map.values()) if reference_ligands is not None: # Replace reference ligand by path - assert len(reference_ligands) == len(scorer.target_ligands) - # Map ligand => path - reference_ligands_map = {k: v for k, v in zip(scorer.target_ligands, - args.reference_ligands)} - out["reference_ligands"] = args.reference_ligands + if len(reference_ligands) == len(scorer.target_ligands): + # Map ligand => path + reference_ligands_map = {k: v for k, v in zip(scorer.target_ligands, + args.reference_ligands)} + out["reference_ligands"] = args.reference_ligands + elif len(reference_ligands) < len(scorer.target_ligands): + # Multi-ligand SDF files were given + # Map ligand => path:idx + out["reference_ligands"] = [] + for ligand, filename in zip(reference_ligands, args.reference_ligands): + assert isinstance(ligand, ost.mol.EntityHandle) + for i, residue in enumerate(ligand.residues): + out["reference_ligands"].append(f"{filename}:{i}") + else: + # This should never happen and would be a bug + raise RuntimeError("Fewer ligands in the reference scorer " + "(%d) than given (%d)" % ( + len(scorer.target_ligands), len(reference_ligands))) + else: - reference_ligands_map = {l: "%s.%s" % (l.chain.name, l.number) + reference_ligands_map = {l: _QualifiedResidueNotation(l) for l in scorer.target_ligands} out["reference_ligands"] = list(reference_ligands_map.values()) @@ -361,14 +426,14 @@ def _Process(model, model_ligands, reference, reference_ligands, args): lddt_pli["transform"] = [transform_data[i:i + 4] for i in range(0, len(transform_data), 4)] - lddt_pli["bs_ref_res"] = [r.qualified_name for r in + lddt_pli["bs_ref_res"] = [_QualifiedResidueNotation(r) for r in lddt_pli["bs_ref_res"]] - lddt_pli["bs_ref_res_mapped"] = [r.qualified_name for r in + lddt_pli["bs_ref_res_mapped"] = [_QualifiedResidueNotation(r) for r in lddt_pli["bs_ref_res_mapped"]] - lddt_pli["bs_mdl_res_mapped"] = [r.qualified_name for r in + lddt_pli["bs_mdl_res_mapped"] = [_QualifiedResidueNotation(r) for r in lddt_pli["bs_mdl_res_mapped"]] lddt_pli["inconsistent_residues"] = ["%s-%s" %( - x.qualified_name, y.qualified_name) for x,y in lddt_pli[ + _QualifiedResidueNotation(x), _QualifiedResidueNotation(y)) for x,y in lddt_pli[ "inconsistent_residues"]] out["lddt_pli"][model_key] = lddt_pli @@ -383,14 +448,14 @@ def _Process(model, model_ligands, reference, reference_ligands, args): transform_data = rmsd["transform"].data rmsd["transform"] = [transform_data[i:i + 4] for i in range(0, len(transform_data), 4)] - rmsd["bs_ref_res"] = [r.qualified_name for r in + rmsd["bs_ref_res"] = [_QualifiedResidueNotation(r) for r in rmsd["bs_ref_res"]] - rmsd["bs_ref_res_mapped"] = [r.qualified_name for r in + rmsd["bs_ref_res_mapped"] = [_QualifiedResidueNotation(r) for r in rmsd["bs_ref_res_mapped"]] - rmsd["bs_mdl_res_mapped"] = [r.qualified_name for r in + rmsd["bs_mdl_res_mapped"] = [_QualifiedResidueNotation(r) for r in rmsd["bs_mdl_res_mapped"]] rmsd["inconsistent_residues"] = ["%s-%s" %( - x.qualified_name, y.qualified_name) for x,y in rmsd[ + _QualifiedResidueNotation(x), _QualifiedResidueNotation(y)) for x,y in rmsd[ "inconsistent_residues"]] out["rmsd"][model_key] = rmsd diff --git a/actions/ost-compare-structures b/actions/ost-compare-structures index e88068505e10a7c1b1293b2570dafc0fc995933f..48d33839e43ff0dba2fb1f5e259ea95bce9d9d3d 100644 --- a/actions/ost-compare-structures +++ b/actions/ost-compare-structures @@ -17,7 +17,7 @@ Loads the structures and performs basic cleanup: The cleaned structures are optionally dumped using -d/--dump-structures Output is written in JSON format (default: out.json). In case of no additional -options, this is a dictionary with 8 keys: +options, this is a dictionary with 8 keys describing model/reference comparison: * "reference_chains": Chain names of reference * "model_chains": Chain names of model @@ -36,7 +36,7 @@ options, this is a dictionary with 8 keys: format. * "inconsistent_residues": List of strings that represent name mismatches of aligned residues in form - <trg_cname>.<trg_rname><trg_rnum>-<mdl_cname>.<mdl_rname><mdl_rnum>. + <trg_cname>.<trg_rnum>.<trg_ins_code>-<mdl_cname>.<mdl_rnum>.<mdl_ins_code>. Inconsistencies may lead to corrupt results but do not abort the program. Program abortion in these cases can be enforced with -ec/--enforce-consistency. @@ -44,6 +44,20 @@ options, this is a dictionary with 8 keys: content of the JSON output will be \"status\" set to FAILURE and an additional key: "traceback". +The following additional keys store relevant input parameters to reproduce +results: + + * "model" + * "reference" + * "fault_tolerant" + * "model_biounit" + * "reference_biounit" + * "residue_number_alignment" + * "enforce_consistency" + * "cad_exec" + * "usalign_exec" + * "lddt_no_stereochecks" + The pairwise sequence alignments are computed with Needleman-Wunsch using BLOSUM62 (NUC44 for nucleotides). Many benchmarking scenarios preprocess the structures to ensure matching residue numbers (CASP/CAMEO). In these cases, @@ -216,13 +230,36 @@ def _ParseArgs(): default=False, action="store_true", help=("Compute per-residue lDDT scores with default parameterization " - "and store as key \"local_lddt\". Score for model residue with " - "number 42 in chain X can be extracted with: " - "data[\"local_lddt\"][\"X\"][\"42\"]. If there is an insertion " - "code, lets say A, the last key becomes \"42A\" Stereochemical " - "irregularities affecting lDDT are reported as keys " - "\"model_clashes\", \"model_bad_bonds\", \"model_bad_angles\" " - "and the respective reference counterparts.")) + "and store as key \"local_lddt\". Score for each residue is " + "accessible by key <chain_name>.<resnum>.<resnum_inscode>. " + "Residue with number 42 in chain X can be extracted with: " + "data[\"local_lddt\"][\"X.42.\"]. If there is an insertion " + "code, lets say A, the residue key becomes \"X.42.A\". " + "Stereochemical irregularities affecting lDDT are reported as " + "keys \"model_clashes\", \"model_bad_bonds\", " + "\"model_bad_angles\" and the respective reference " + "counterparts. Atoms specified in there follow the following " + "format: <chain_name>.<resnum>.<resnum_inscode>.<atom_name>")) + + parser.add_argument( + "--bb-lddt", + dest="bb_lddt", + default=False, + action="store_true", + help=("Compute global lDDT score with default parameterization and " + "store as key \"bb_lddt\". lDDT in this case is only computed on " + "backbone atoms: CA for peptides and C3' for nucleotides")) + + parser.add_argument( + "--bb-local-lddt", + dest="bb_local_lddt", + default=False, + action="store_true", + help=("Compute per-residue lDDT scores with default parameterization " + "and store as key \"bb_local_lddt\". lDDT in this case is only " + "computed on backbone atoms: CA for peptides and C3' for " + "nucleotides. Per-residue scores are accessible as described for " + "local_lddt.")) parser.add_argument( "--cad-score", @@ -240,12 +277,10 @@ def _ParseArgs(): default=False, action="store_true", help=("Compute local CAD's atom-atom (AA) scores and store as key " - "\"local_cad_score\". Score for model residue with number 42 in " - "chain X can be extracted with: " - "data[\"local_cad_score\"][\"X\"][\"42\"]. " - "--residue-number-alignments must be enabled to compute this " - "score. Requires voronota_cadscore executable in PATH. " - "Alternatively you can set cad-exec.")) + "\"local_cad_score\". Per-residue scores are accessible as " + "described for local_lddt. --residue-number-alignments must be " + "enabled to compute this score. Requires voronota_cadscore " + "executable in PATH. Alternatively you can set cad-exec.")) parser.add_argument( "--cad-exec", @@ -255,6 +290,13 @@ def _ParseArgs(): "https://github.com/kliment-olechnovic/voronota). Searches PATH " "if not set.")) + parser.add_argument( + "--usalign-exec", + dest="usalign_exec", + default=None, + help=("Path to USalign executable to compute TM-score. If not given, " + "an OpenStructure internal copy of USalign code is used.")) + parser.add_argument( "--qs-score", dest="qs_score", @@ -321,8 +363,36 @@ def _ParseArgs(): "None. Model interface residues are available as key " "\"model_interface_residues\", reference interface residues as " "key \"reference_interface_residues\". Residues are represented " - "as string in form <num><inscode>. The respective scores are " - "available as keys \"patch_qs\" and \"patch_dockq\"")) + "as string in form <chain_name>.<resnum>.<resnum_inscode>. " + "The respective scores are available as keys \"patch_qs\" and " + "\"patch_dockq\"")) + + parser.add_argument( + "--tm-score", + dest="tm_score", + default=False, + action="store_true", + help=("Computes TM-score with the USalign tool. Also computes a " + "chain mapping in case of complexes that is stored in the " + "same format as the default mapping. TM-score and the mapping " + "are available as keys \"tm_score\" and \"usalign_mapping\"")) + + parser.add_argument( + "--lddt-no-stereochecks", + dest="lddt_no_stereochecks", + default=False, + action="store_true", + help=("Disable stereochecks for lDDT computation")) + + parser.add_argument( + "--n-max-naive", + dest="n_max_naive", + required=False, + default=12, + type=int, + help=("If number of chains in model and reference are below or equal " + "that number, the chain mapping will naively enumerate all " + "possible mappings. A heuristic is used otherwise.")) return parser.parse_args() @@ -428,29 +498,47 @@ def _GetInconsistentResidues(alns): r1 = col.GetResidue(0) r2 = col.GetResidue(1) if r1.IsValid() and r2.IsValid() and r1.GetName() != r2.GetName(): - lst.append((r1, r2)) - lst = [f"{x[0].GetQualifiedName()}-{x[1].GetQualifiedName()}" for x in lst] + ch_1 = r1.GetChain().name + num_1 = r1.number.num + ins_code_1 = r1.number.ins_code.strip("\u0000") + id_1 = f"{ch_1}.{num_1}.{ins_code_1}" + ch_2 = r2.GetChain().name + num_2 = r2.number.num + ins_code_2 = r2.number.ins_code.strip("\u0000") + id_2 = f"{ch_2}.{num_2}.{ins_code_2}" + lst.append(f"{id_1}-{id_2}") return lst def _LocalScoresToJSONDict(score_dict): - """ Score for model residue with number rnum in chain X can be extracted - with: data["local_cad_score"]["X"][rnum]. Convert ResNum object to str for - JSON serialization + """ Convert ResNums to str for JSON serialization """ json_dict = dict() for ch, ch_scores in score_dict.items(): - json_dict[ch] = {str(rnum): s for rnum, s in ch_scores.items()} + for num, s in ch_scores.items(): + ins_code = num.ins_code.strip("\u0000") + json_dict[f"{ch}.{num.num}.{ins_code}"] = s return json_dict -def _InterfaceResiduesToJSONDict(interface_dict): - """ Interface residues are stored as - data["model_interface_residues"]["A"][rnum1, rnum2,...]. Convert ResNum - object to str for JSON serialization. +def _InterfaceResiduesToJSONList(interface_dict): + """ Convert ResNums to str for JSON serialization. + + Changes in this function will affect _PatchScoresToJSONList """ - json_dict = dict() + json_list = list() for ch, ch_nums in interface_dict.items(): - json_dict[ch] = [str(rnum) for rnum in ch_nums] - return json_dict + for num in ch_nums: + ins_code = num.ins_code.strip("\u0000") + json_list.append(f"{ch}.{num.num}.{ins_code}") + return json_list + +def _PatchScoresToJSONList(interface_dict, score_dict): + """ Creates List of patch scores that are consistent with interface residue + lists + """ + json_list = list() + for ch, ch_nums in interface_dict.items(): + json_list += score_dict[ch] + return json_list def _Process(model, reference, args): @@ -461,7 +549,10 @@ def _Process(model, reference, args): scorer = scoring.Scorer(model, reference, resnum_alignments = args.residue_number_alignment, cad_score_exec = args.cad_exec, - custom_mapping = mapping) + custom_mapping = mapping, + usalign_exec = args.usalign_exec, + lddt_no_stereochecks = args.lddt_no_stereochecks, + n_max_naive = args.n_max_naive) ir = _GetInconsistentResidues(scorer.aln) if len(ir) > 0 and args.enforce_consistency: @@ -490,6 +581,12 @@ def _Process(model, reference, args): out["reference_bad_bonds"] = [x.ToJSON() for x in scorer.target_bad_bonds] out["reference_bad_angles"] = [x.ToJSON() for x in scorer.target_bad_angles] + if args.bb_lddt: + out["bb_lddt"] = scorer.bb_lddt + + if args.bb_local_lddt: + out["bb_local_lddt"] = _LocalScoresToJSONDict(scorer.bb_local_lddt) + if args.cad_score: out["cad_score"] = scorer.cad_score @@ -525,11 +622,17 @@ def _Process(model, reference, args): if args.patch_scores: out["model_interface_residues"] = \ - _InterfaceResiduesToJSONDict(scorer.model_interface_residues) + _InterfaceResiduesToJSONList(scorer.model_interface_residues) out["reference_interface_residues"] = \ - _InterfaceResiduesToJSONDict(scorer.target_interface_residues) - out["patch_qs"] = scorer.patch_qs - out["patch_dockq"] = scorer.patch_dockq + _InterfaceResiduesToJSONList(scorer.target_interface_residues) + out["patch_qs"] = _PatchScoresToJSONList(scorer.model_interface_residues, + scorer.patch_qs) + out["patch_dockq"] = _PatchScoresToJSONList(scorer.model_interface_residues, + scorer.patch_dockq) + + if args.tm_score: + out["tm_score"] = scorer.tm_score + out["usalign_mapping"] = scorer.usalign_mapping if args.dump_structures: try: @@ -556,16 +659,17 @@ def _Process(model, reference, args): "fails in this case.") else: raise - - - - return out def _Main(): args = _ParseArgs() try: + compute_cad = args.cad_score or args.local_cad_score + if compute_cad and not args.residue_number_alignment: + raise RuntimeError("Only support CAD score when residue numbers in " + "model and reference match. Use -rna flag if " + "this is the case.") reference = _LoadStructure(args.reference, sformat=args.reference_format, bu_idx=args.reference_biounit, @@ -575,6 +679,18 @@ def _Main(): bu_idx=args.model_biounit, fault_tolerant = args.fault_tolerant) out = _Process(model, reference, args) + + # append input arguments + out["model"] = args.model + out["reference"] = args.reference + out["fault_tolerant"] = args.fault_tolerant + out["model_biounit"] = args.model_biounit + out["reference_biounit"] = args.reference_biounit + out["residue_number_alignment"] = args.residue_number_alignment + out["enforce_consistency"] = args.enforce_consistency + out["cad_exec"] = args.cad_exec + out["usalign_exec"] = args.usalign_exec + out["lddt_no_stereochecks"] = args.lddt_no_stereochecks out["status"] = "SUCCESS" with open(args.output, 'w') as fh: json.dump(out, fh, indent=4, sort_keys=False) diff --git a/docker/Dockerfile b/docker/Dockerfile index 1975e401be9e21d7bcbee708b8ab5b5d068ebc5b..e019bcef45d8742129ea99819072b09b9baea6eb 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -2,7 +2,7 @@ FROM ubuntu:22.04 # ARGUMENTS ########### -ARG OPENSTRUCTURE_VERSION="2.4.0" +ARG OPENSTRUCTURE_VERSION="2.5.0" ARG SRC_FOLDER="/usr/local/src" ARG CPUS_FOR_MAKE=2 ARG OPENMM_VERSION="7.7.0" diff --git a/docker/README.rst b/docker/README.rst index 4267e29f0ef349a734164966c1bdab52e4625bfc..3e74a8020493c7e4c0f7ec8bf0d75d4b7e87b884 100644 --- a/docker/README.rst +++ b/docker/README.rst @@ -105,21 +105,10 @@ Here is an example run of the compare-structures action: docker run --rm -v $(pwd):/home <IMAGE NAME> compare-structures \ --model model.pdb \ - --reference reference.pdb \ - --output output.json \ - --qs-score \ - --residue-number-alignment \ + --reference reference.cif \ + --output scores.json \ --lddt \ - --structural-checks \ - --consistency-checks \ - --inclusion-radius 15.0 \ - --bond-tolerance 15.0 \ - --angle-tolerance 15.0 \ - --molck \ - --remove oxt hyd unk \ - --clean-element-column \ - --map-nonstandard-residues - + --local-lddt In order to see all available options for this action run: @@ -127,6 +116,15 @@ In order to see all available options for this action run: docker run --rm <IMAGE NAME> compare-structures -h +CASP15 used lDDT for RNA scoring. lDDT runs stereochemistry checks by default, +removing sidechains if they have problematic stereochemistry. This gives lower +lDDT scores. The full residue is removed if the backbone has problematic +stereochemistry resulting in an lDDT score of 0.0 for that particular residue. +Stereochemistry checks for RNA were not yet available in CASP15. To reproduce +these results, use the ``--lddt-no-stereochecks`` flag. This disables +stereochemistry checks for lDDT computation but stereochemical irregularities +are still reported in the output. + Scripts ####### @@ -188,3 +186,65 @@ Eg. to run molck type: .. note:: Note how the options to the command are specified after the image name. + +.. _docker_compound_lib: + +The Compound Library +-------------------- + +At build time of the container, a :class:`~ost.conop.CompoundLib` is generated. +Compound libraries contain information on chemical compounds, such as their +connectivity, chemical class and one-letter-code. The compound library has +several uses, but the most important one is to provide the connectivy +information for the rule-based processor. + +The compound library is generated with the components.cif dictionary provided by +the PDB. As the PDB updates regularly, the compound library shipped with the +container is quickly outdated. For most use cases, this is not problematic. +However, if you rely on correct connectivity information of the latest and +greatest compounds, you have to keep the compound library up to date manually. + +If you work with ligands or non standard residues, or simply if you download +files from the PDB, it is recommended to generate your own compound library and +mount it into the container. + +The simplest way to create a compound library is to use the +:program:`chemdict_tool` available in the container. The program allows you +to import the chemical description of the compounds from a mmCIF dictionary, +e.g. the components.cif dictionary provided by the PDB. +The latest dictionary can be downloaded from the +`wwPDB site <http://www.wwpdb.org/ccd.html>`_. +The files are rather large, it is therefore recommended to download the +gzipped version. + +After downloading the file use :program:`chemdict_tool` in the container to +convert the mmCIF dictionary into our internal format: + +.. code-block:: bash + + docker run --rm -v $(pwd):/home --entrypoint chemdict_tool <IMAGE_NAME> \ + create components.cif.gz compounds.chemlib + +To run a script with the updated compound library, use the -v option for +mounting/overriding, and the --env option to set the ``OST_COMPOUNDS_CHEMLIB`` +environment variable inside the container, pointing to the path of the +mounted file: + +.. code-block:: bash + + docker run --rm -v /home/<USER>:/home \ + -v <COMPLIB_DIR_LOCALHOST>/compounds.chemlib:/compounds.chemlib \ + --env OST_COMPOUNDS_CHEMLIB=/compounds.chemlib \ + <IMAGE_NAME> script.py pdbs/struct.pdb + +with COMPLIB_DIR_LOCALHOST being the directory that contains the newly generated +compound library with name compounds.chemlib. + +You can check whether the default lib is successfully overriden by looking at the +output when running a Python script with following code in the container: + +.. code-block:: python + + from ost import conop + lib = conop.GetDefaultLib() + print(lib.GetCreationDate()) diff --git a/docker/test_docker.py b/docker/test_docker.py index 38fae4a6093ba8b437d1ca91e63992a6f12a068b..b6a68c05e1526dced1dafb8e73f07ab0510e03b4 100644 --- a/docker/test_docker.py +++ b/docker/test_docker.py @@ -1,19 +1,33 @@ import ost -from ost.mol.alg import qsscoring +from ost.mol.alg import scoring, qsscoring +from ost import conop + +lib = conop.GetDefaultLib() +if lib is not None: + print("You have a valid compound library, last updated on " + + lib.GetCreationDate()) +else: + ost.LogError("No compound library set as default!") + print("The compound library is not working properly!") # load two biounits to compare ent_full = ost.io.LoadPDB('3ia3', remote=True) ent_1 = ent_full.Select('cname=A,D') ent_2 = ent_full.Select('cname=B,C') -# get score -ost.PushVerbosityLevel(3) + +# get scores +ost.PushVerbosityLevel(3) + try: - qs_scorer = qsscoring.QSscorer(ent_1, ent_2) - ost.LogScript('QSscore:', str(qs_scorer.global_score)) - ost.LogScript('Chain mapping used:', str(qs_scorer.chain_mapping)) -except qsscoring.QSscoreError as ex: + scorer = scoring.Scorer(ent_1, ent_2) + ost.LogScript('lDDT:', str(scorer.lddt)) + ost.LogScript('QSscore:', str(scorer.qs_global)) + ost.LogScript('Chain mapping used:', str(scorer.mapping.GetFlatMapping())) +except Exception as ex: # default handling: report failure and set score to 0 - ost.LogError('QSscore failed:', str(ex)) + ost.LogError('Scoring failed:', str(ex)) qs_score = 0 + print("OST is not working properly!") +else: + print("OST is working!") -print("OST is working!") diff --git a/modules/base/doc/testutils.rst b/modules/base/doc/testutils.rst index df61f56e7302da580e9b64d1b4235564057a1370..dabf827e63eddd10b844a894c2d2de2dfcfa099f 100644 --- a/modules/base/doc/testutils.rst +++ b/modules/base/doc/testutils.rst @@ -6,4 +6,4 @@ .. autofunction:: ost.testutils.RunTests -.. autofunction:: ost.testutils.SetDefaultCompoundLib +.. autofunction:: ost.testutils.DefaultCompoundLibIsSet diff --git a/modules/base/pymod/__init__.py.in b/modules/base/pymod/__init__.py.in index 7b39730af57d4ba86e47dabb940dce9030360902..0dff2fd3797a697094017a346fef9661d84138ac 100644 --- a/modules/base/pymod/__init__.py.in +++ b/modules/base/pymod/__init__.py.in @@ -16,12 +16,13 @@ # along with this library; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA #------------------------------------------------------------------------------ - +import os __all__=['CharList','Correl', 'FileLogSink', 'FloatList', 'FloatMatrix', 'FloatMatrix3', 'FloatMatrix4', 'GetCurrentLogSink', 'GetPrefixPath', 'GetSharedDataPath', 'GetVerbosityLevel', 'Histogram', 'IntList', 'LogDebug', 'LogError', 'LogInfo', 'LogScript', 'LogSink', 'LogTrace', 'LogVerbose', 'LogWarning', 'Max', 'Mean', 'Median', 'Min', 'MultiLogSink', 'PopLogSink', 'PopVerbosityLevel', 'PushLogSink', 'PushVerbosityLevel', 'Range', 'SetPrefixPath', 'StdDev', 'StreamLogSink', 'StringList', 'StringLogSink', 'Units', 'VERSION', 'VERSION_MAJOR', 'VERSION_MINOR', 'VERSION_PATCH', 'WITH_NUMPY', 'conop', 'geom', 'io', 'mol', 'seq', 'stutil' @ALL_ADDITIONAL_MODULES@] from ._ost_base import * from .stutil import * +from ost import conop class StreamLogSink(LogSink): def __init__(self, stream): @@ -29,3 +30,104 @@ class StreamLogSink(LogSink): self._stream=stream def LogMessage(self, message, level): self._stream.write(message) + + +def _GetPrefixFromPythonPath(): + """Get the prefix from this file's path""" + prefix_path = __file__ + for _ in range(5): + prefix_path = os.path.dirname(prefix_path) + return prefix_path + + +def _SetupOstPrefix(): + """ This function attempts to set the OST prefix path if $OST_ROOT was not + set already. + + It does so by assuming that the __init__.py file is located under + $OST_ROOT/lib(64)?/pythonX.XX/site-packages/ost. This might not be the case + in all settings (conda, pip?). In that case this function should be + adapted. + """ + + try: + # $OST_ROOT was set + # Note: this doesn't mean set to a valid prefix + GetPrefixPath() + except RuntimeError: + # Setup from this file's path + prefix_path = _GetPrefixFromPythonPath() + SetPrefixPath(prefix_path) + + +def _TrySetCompoundsLib(path): + """Tries to set the compound lib to 'path', or raises a ValueError.""" + if path is not None and os.path.exists(path): + compound_lib = conop.CompoundLib.Load(path) + if compound_lib is not None: + conop.SetDefaultLib(compound_lib) + return + raise ValueError("Could not load %s" % path) + + +def _SetupCompoundsLib(): + """ This function sets up the compound lib. + + By order of priority, the following heuristics are used: + + 1. The $OST_COMPOUNDS_CHEMLIB environment variable + 2. The 'compounds.chemlib' file in the shared path under the folder pointed + by the $OST_ROOT environment variable + 3. The 'compounds.chemlib' file in the shared path under the OST prefix where + the ost python module is installed. + + If no compound library can be loaded with any of these strategies, a warning + message is issued. + + """ + # Try with the $OST_COMPOUNDS_CHEMLIB environment variable + compound_lib_path = os.getenv("OST_COMPOUNDS_CHEMLIB") + if compound_lib_path: + compound_lib = conop.CompoundLib.Load(compound_lib_path) + if compound_lib is None: + raise RuntimeError("Could not load $OST_COMPOUNDS_CHEMLIB as a compound " + "library: '%s'." % compound_lib_path) + else: + conop.SetDefaultLib(compound_lib) + return + + # Try from GetSharedDataPath() - requires $OST_ROOT to be set. + try: + compound_lib_path = os.path.join(GetSharedDataPath(), 'compounds.chemlib') + _TrySetCompoundsLib(compound_lib_path) + except (RuntimeError, ValueError): + pass + else: + return + + # If OST_ROOT was pointing to a root with no compound lib or to an invalid + # root, try + try: + old_prefix = GetPrefixPath() # This is set by now and won't raise + prefix_path = _GetPrefixFromPythonPath() + SetPrefixPath(prefix_path) + compound_lib_path = os.path.join(GetSharedDataPath(), 'compounds.chemlib') + _TrySetCompoundsLib(compound_lib_path) + except (RuntimeError, ValueError): + SetPrefixPath(old_prefix) + else: + # If we're here, OST root was set but the prefix did not contain a compound + # lib, but the root obtained from the python path contained one. + # Most likely OST_ROOT was set incorrectly + LogWarning("$OST_ROOT is pointing to an invalid OST prefix: '%s'" % ( + os.getenv("OST_ROOT"))) + return + + LogWarning("Compound library not available. Some functionality may not " \ + "work as expected.") + + +# Setup OST +PushVerbosityLevel(LogLevel.Script) +_SetupOstPrefix() +_SetupCompoundsLib() diff --git a/modules/base/pymod/testutils.py b/modules/base/pymod/testutils.py index 4b701fe08fa6ae71e1648bd1330a2a907c777bb9..ea21d9b9ec4d75ed157487c29ac124feece8fd03 100644 --- a/modules/base/pymod/testutils.py +++ b/modules/base/pymod/testutils.py @@ -1,5 +1,13 @@ +import inspect +import sys +import unittest + +from ost import xmlrunner +from ost.conop import GetDefaultLib + + def RunTests(): - ''' + """ This function behaves as a custom TestLoader for python unittests. With no system arguments, the default unittest TestRunner is used. @@ -31,20 +39,15 @@ def RunTests(): from ost import testutils testutils.RunTests() - ''' - import unittest - import sys + """ try: - if len(sys.argv)>1 and sys.argv[1]=='xml': - import inspect - import types + if len(sys.argv) > 1 and sys.argv[1] == 'xml': import __main__ - from ost import xmlrunner for name, obj in inspect.getmembers(__main__): if (isinstance(obj, type) and issubclass(obj, unittest.TestCase)): suite = unittest.TestLoader().loadTestsFromTestCase(obj) - stream = open('PYTEST-%s.xml'%name, 'w') + stream = open('PYTEST-%s.xml' % name, 'w') xmlrunner.XMLTestRunner(stream).run(suite) stream.close() @@ -54,54 +57,15 @@ def RunTests(): print(e) -def SetDefaultCompoundLib(): - ''' - This function tries to ensure that a default compound library is set. - When calling scripts with ``ost`` this is not needed, but since unit tests are - called with ``python`` we need to ensure that it is set. The function is only - expected to succeed (and return True) if ``COMPOUND_LIB`` was set when - :ref:`configuring the compilation <cmake-flags>`. - - It tries the following: - - - get it with :func:`ost.conop.GetDefaultLib` - - look for ``compounds.chemlib`` in ``$OST_ROOT/share/openstructure`` - - if ``OST_ROOT`` not set in the above, try to guess it based on the path of - the ``conop`` module - - To use this check modify the :func:`RunTests` call to read: - - .. code-block:: python - - if __name__ == "__main__": - from ost import testutils - if testutils.SetDefaultCompoundLib(): - testutils.RunTests() - else: - print 'No compound library available. Ignoring test_XXX.py tests.' +def DefaultCompoundLibIsSet(): + """ + This function checks if a default compound library is set. :return: True, if a compound library was found and set to be accessed with :func:`ost.conop.GetDefaultLib`. False otherwise. - ''' - import os - from ost import conop, GetSharedDataPath, SetPrefixPath + """ # check if already there - if conop.GetDefaultLib(): + if GetDefaultLib(): return True else: - # try to get the shared data path? - try: - shared_data_path = GetSharedDataPath() - except Exception as e: - SetPrefixPath(os.path.abspath(os.path.join(conop.__path__[0], os.pardir, - os.pardir, os.pardir, - os.pardir, os.pardir))) - shared_data_path = GetSharedDataPath() - # look for compounds.chemlib in there - compound_lib_path = os.path.join(shared_data_path, 'compounds.chemlib') - if os.path.exists(compound_lib_path): - compound_lib = conop.CompoundLib.Load(compound_lib_path) - conop.SetDefaultLib(compound_lib) - return True - else: - return False + return False diff --git a/modules/bindings/doc/tmtools.rst b/modules/bindings/doc/tmtools.rst index 823a35eca537d4a4b7b2d0d98d5e435c2fca3734..cc08e010d98a2b91cfc69841aad103df4422c295 100644 --- a/modules/bindings/doc/tmtools.rst +++ b/modules/bindings/doc/tmtools.rst @@ -5,7 +5,7 @@ :synopsis: Sequence dependent and independent structure superposition The :mod:`~ost.bindings.tmtools` module provides access to the structural -superposition programs TMscore, Tmalign and MMalign developed by Y. Zhang +superposition programs TMscore and Tmalign developed by Y. Zhang and J. Skolnick. These programs superpose a model onto a reference structure, using the positions of the Calpha atoms only. While at their core, these programs essentially use the same algorithm, they differ on how the Calphas are @@ -17,15 +17,15 @@ Citation: Yang Zhang and Jeffrey Skolnick, Proteins 2004 57: 702-710 Y. Zhang and J. Skolnick, Nucl. Acids Res. 2005 33, 2302-9 -Besides using the standalone TM-align program, ost also provides a wrapper -around TM-align as published in: +Besides using the standalone TM-align program, ost also provides wrappers +around USalign as published in: - Sha Gong, Chengxin Zhang, Yang Zhang, Bioinformatics 2019 - -The advantage is that no intermediate files must be generated, a wrapper on the -c++ layer is used instead. However, only the basic TM-align superposition -functionality is available. + Chengxin Zhang, Morgan Shine, Anna Marie Pyle, Yang Zhang + (2022) Nat Methods +USalign can be used as external standalone tool. Alternatively, ost allows to +directly inject structural data in the USAlign c++ layer. The advantage of that +is that no intermediate files must be generated. Distance measures used by TMscore @@ -78,15 +78,27 @@ Usage of TMscore .. autoclass:: ost.bindings.tmtools.TMScoreResult +Usage of USalign +-------------------------------------------------------------------------------- + +For higher order complexes, ost provides access to USalign. This corresponds to +calling USalign with the preferred way of comparing full biounits: + +.. code-block:: bash + + USalign mdl.pdb ref.pdb -mm 1 -ter 0 + +.. autofunction:: ost.bindings.tmtools.USAlign + -TMalign C++ wrapper +C++ wrappers -------------------------------------------------------------------------------- .. currentmodule:: ost.bindings -Instead of calling the TMalign executable, ost also provides a wrapper around -its C++ implementation. The advantage is that no intermediate files need to be -generated in order to call the executable. +Instead of calling the external executables, ost also provides a wrapper around +the USalign c++ implementation which is shipped with the ost source code. +The advantage is that no intermediate files need to be generated. .. code-block:: python @@ -106,6 +118,7 @@ generated in order to call the executable. :param rmsd: RMSD of the superposed residues :param tm_score: TMScore of the superposed residues + :param tm_score_swapped: TMScore when reference is swapped :param aligned_length: Number of superposed residues :param transform: Transformation matrix to superpose first chain onto reference @@ -118,13 +131,16 @@ generated in order to call the executable. .. method:: WrappedTMAlign(chain1, chain2, [fast=False]) - Takes two chain views and runs TMalign with *chain2* as reference. - The positions and sequences are directly extracted from the chain + Takes two chain views and runs TMalign from USAlign with *chain2* as + reference. The positions and sequences are directly extracted from the chain residues for every residue that fulfills: - * peptide linking + * peptide linking and valid CA atom OR nucleotide linking and valid C3' + atom * valid one letter code(no '?') - * valid CA atom + + The function automatically identifies whether the chains consist of peptide + or RNA residues. An error is raised if the two types are mixed. :param chain1: Chain from which position and sequence are extracted to run TMalign. @@ -137,21 +153,71 @@ generated in order to call the executable. :rtype: :class:`ost.bindings.TMAlignResult` -.. method:: WrappedTMAlign(pos1, pos2, seq1, seq2 [fast=False]) +.. method:: WrappedTMAlign(pos1, pos2, seq1, seq2 [fast=False, rna=False]) Similar as described above, but directly feeding in raw data. - :param pos1: CA positions of the first chain - :param pos2: CA positions of the second chain, this is the reference. + :param pos1: CA/C3' positions of the first chain + :param pos2: CA/C3' positions of the second chain, this is the reference. :param seq1: Sequence of first chain :param seq2: Sequence of second chain :param fast: Whether to apply the *fast* flag to TMAlign + :param rna: Whether to treat as RNA :type pos1: :class:`ost.geom.Vec3List` :type pos2: :class:`ost.geom.Vec3List` :type seq1: :class:`ost.seq.SequenceHandle` :type seq2: :class:`ost.seq.SequenceHandle` :type fast: :class:`bool` + :type rna: :class:`bool` :rtype: :class:`ost.bindings.TMAlignResult` :raises: :class:`ost.Error` if pos1 and seq1, pos2 and seq2 respectively are not consistent in size. +For higher order complexes, ost provides access to the MMalign functionality +from USalign. + +.. class:: MMAlignResult(rmsd, tm_score, transform, aligned_length, alignments,\ + ent1_mapped_chains, ent2_mapped_chains) + + All parameters of the constructor are available as attributes of the class + + :param rmsd: RMSD of the superposed residues + :param tm_score: TMScore of the superposed residues + :param tm_score_swapped: TMScore when reference is swapped + :param aligned_length: Number of superposed residues + :param transform: Transformation matrix to superpose mdl onto reference + :param alignments: Alignments of all mapped chains, with first sequence + being from ent1 and second sequence from ent2 + :param ent1_mapped_chains: All mapped chains from ent1 + :param ent2_mapped_chains: The respective mapped chains from ent2 + :type rmsd: :class:`float` + :type tm_score: :class:`float` + :type aligned_length: :class:`int` + :type transform: :class:`geom.Mat4` + :type alignments: :class:`ost.seq.AlignmentList` + :type ent1_mapped_chains: :class:`ost.StringList` + :type ent2_mapped_chains: :class:`ost.StringList` + +.. method:: WrappedMMAlign(ent1, ent2, [fast=False]) + + Takes two entity views and runs MMalign with *ent2* as reference. + The positions and sequences are directly extracted from the chain + residues for every residue that fulfills: + + * peptide linking and valid CA atom OR nucleotide linking and valid C3' + atom + * valid one letter code(no '?') + + The function automatically identifies whether the chains consist of peptide + or RNA residues. An error is raised if the two types are mixed in the same + chain. + + :param ent1: Entity from which position and sequence are extracted + to run MMalign. + :param ent2: Entity from which position and sequence are extracted + to run MMalign, this is the reference. + :param fast: Whether to apply the *fast* flag to MMAlign + :type ent1: :class:`ost.mol.EntityView` + :type ent2: :class:`ost.mol.EntityView` + :type fast: :class:`bool` + :rtype: :class:`ost.bindings.MMAlignResult` diff --git a/modules/bindings/pymod/export_tmalign.cc b/modules/bindings/pymod/export_tmalign.cc index aefe33ec7de09fb20ab79558e1f685e2bff0f1cb..060421d01230bb5a35e27f3a6b4ec3c09bd889b6 100644 --- a/modules/bindings/pymod/export_tmalign.cc +++ b/modules/bindings/pymod/export_tmalign.cc @@ -26,9 +26,9 @@ ost::bindings::TMAlignResult WrapTMAlignPos(const geom::Vec3List& pos_one, const geom::Vec3List& pos_two, const ost::seq::SequenceHandle& seq1, const ost::seq::SequenceHandle& seq2, - bool fast) { + bool fast, bool rna) { - return ost::bindings::WrappedTMAlign(pos_one, pos_two, seq1, seq2, fast); + return ost::bindings::WrappedTMAlign(pos_one, pos_two, seq1, seq2, fast, rna); } ost::bindings::TMAlignResult WrapTMAlignView(const ost::mol::ChainView& chain1, @@ -38,11 +38,19 @@ ost::bindings::TMAlignResult WrapTMAlignView(const ost::mol::ChainView& chain1, return ost::bindings::WrappedTMAlign(chain1, chain2, fast); } +ost::bindings::MMAlignResult WrapMMAlignView(const ost::mol::EntityView& ent1, + const ost::mol::EntityView& ent2, + bool fast) { + + return ost::bindings::WrappedMMAlign(ent1, ent2, fast); +} + void export_TMAlign() { - class_<ost::bindings::TMAlignResult>("TMAlignResult", init<Real, Real, int, const geom::Mat4&, + class_<ost::bindings::TMAlignResult>("TMAlignResult", init<Real, Real, Real, int, const geom::Mat4&, const ost::seq::AlignmentHandle&>()) .add_property("rmsd", make_function(&ost::bindings::TMAlignResult::GetRMSD)) .add_property("tm_score", make_function(&ost::bindings::TMAlignResult::GetTMScore)) + .add_property("tm_score_swapped", make_function(&ost::bindings::TMAlignResult::GetTMScoreSwapped)) .add_property("aligned_length", make_function(&ost::bindings::TMAlignResult::GetAlignedLength)) .add_property("transform", make_function(&ost::bindings::TMAlignResult::GetTransform, return_value_policy<reference_existing_object>())) @@ -50,9 +58,31 @@ void export_TMAlign() { return_value_policy<reference_existing_object>())) ; + class_<ost::bindings::MMAlignResult>("MMAlignResult", init<Real, Real, Real, int, const geom::Mat4&, + const ost::seq::AlignmentList&, + const std::vector<String>&, + const std::vector<String>&>()) + .add_property("rmsd", make_function(&ost::bindings::MMAlignResult::GetRMSD)) + .add_property("tm_score", make_function(&ost::bindings::MMAlignResult::GetTMScore)) + .add_property("tm_score_swapped", make_function(&ost::bindings::MMAlignResult::GetTMScoreSwapped)) + .add_property("transform", make_function(&ost::bindings::MMAlignResult::GetTransform, + return_value_policy<reference_existing_object>())) + .add_property("aligned_length", make_function(&ost::bindings::MMAlignResult::GetAlignedLength)) + .add_property("alignments", make_function(&ost::bindings::MMAlignResult::GetAlignments, + return_value_policy<reference_existing_object>())) + .add_property("ent1_mapped_chains", make_function(&ost::bindings::MMAlignResult::GetEnt1MappedChains, + return_value_policy<reference_existing_object>())) + .add_property("ent2_mapped_chains", make_function(&ost::bindings::MMAlignResult::GetEnt2MappedChains, + return_value_policy<reference_existing_object>())) + ; + + def("WrappedTMAlign", &WrapTMAlignPos, (arg("pos1"), arg("pos2"), arg("seq1"), arg("seq2"), - arg("fast")=false)); + arg("fast")=false, arg("rna")=false)); def("WrappedTMAlign", &WrapTMAlignView, (arg("chain1"), arg("chain2"), arg("fast")=false)); + + def("WrappedMMAlign", &WrapMMAlignView, (arg("ent1"), arg("ent2"), + arg("fast")=false)); } diff --git a/modules/bindings/pymod/tmtools.py b/modules/bindings/pymod/tmtools.py index 94fc4ceed5b88113842fbebaa63995ae7469f7d7..29dcb8c408522859f91c87b8f5c6c363675b36ee 100644 --- a/modules/bindings/pymod/tmtools.py +++ b/modules/bindings/pymod/tmtools.py @@ -55,7 +55,8 @@ def _CleanupFiles(dir_name): def _ParseTmAlign(lines,lines_matrix): info_line=lines[12].split(',') aln_length=int(info_line[0].split('=')[1].strip()) - rmsd=float(info_line[1].split('=')[1].strip()) + rmsd=float(info_line[1].split('=')[1].strip()) + tm_score_swapped=float(lines[13].split('=')[1].split('(')[0].strip()) tm_score=float(lines[14].split('=')[1].split('(')[0].strip()) tf1=[float(i.strip()) for i in lines_matrix[2].split()] tf2=[float(i.strip()) for i in lines_matrix[3].split()] @@ -69,7 +70,92 @@ def _ParseTmAlign(lines,lines_matrix): alignment = seq.CreateAlignment() alignment.AddSequence(seq2) alignment.AddSequence(seq1) - return ost.bindings.TMAlignResult(rmsd, tm_score, aln_length, tf, alignment) + return ost.bindings.TMAlignResult(rmsd, tm_score, tm_score_swapped, + aln_length, tf, alignment) + +def _ParseUSAlign(lines,lines_matrix): + + # stuff that is immediately parsed + rmsd = None + tm_score = None + tm_score_swapped = None + aligned_length = None + + # first goes into intermediate data structures + aln_data = list() + mapping_data1 = list() + mapping_data2 = list() + in_aln = False + + for line in lines: + if in_aln: + if len(line.strip()) == 0: + in_aln = False + else: + aln_data.append(line.strip('*')) + elif line.startswith("Name of Structure_1:"): + tmp = [item.strip() for item in line.split()[3].split(':')[1:]] + for item in tmp: + if len(item) > 0: + mapping_data1.append(item.split(',')[1]) + else: + mapping_data1.append("") + elif line.startswith("Name of Structure_2:"): + tmp = [item.strip() for item in line.split()[3].split(':')[1:]] + for item in tmp: + if len(item) > 0: + mapping_data2.append(item.split(',')[1]) + else: + mapping_data2.append("") + elif line.startswith("Aligned length="): + data = [item.strip() for item in line.split(',')] + for item in data: + if item.startswith("Aligned length="): + aligned_length = int(item.split("=")[1]) + elif item.startswith("RMSD="): + rmsd = float(item.split("=")[1]) + elif line.startswith("TM-score="): + if "(normalized by length of Structure_1" in line: + tm_score_swapped = float(line.split('(')[0].split('=')[1].strip()) + elif "(normalized by length of Structure_2" in line: + tm_score = float(line.split('(')[0].split('=')[1].strip()) + elif line.startswith("(\":\" denotes residue pairs of"): + in_aln = True + + assert(len(aln_data)==3) + aln_sequences1 = aln_data[0].split('*') + aln_sequences2 = aln_data[2].split('*') + + # do mapping/aln data + alns = ost.seq.AlignmentList() + ent1_mapped_chains = ost.StringList() + ent2_mapped_chains = ost.StringList() + assert(len(mapping_data1) == len(mapping_data2)) + assert(len(aln_sequences1) == len(aln_sequences2)) + assert(len(mapping_data1) == len(aln_sequences1)) + for a, b, c, d in zip(mapping_data1, mapping_data2, + aln_sequences1, aln_sequences2): + if len(a) > 0 and len(b) > 0: + ent1_mapped_chains.append(a) + ent2_mapped_chains.append(b) + assert(len(c) == len(d)) + aln = seq.CreateAlignment() + aln.AddSequence(seq.CreateSequence(a, c)) + aln.AddSequence(seq.CreateSequence(b, d)) + alns.append(aln) + + # parse transformation matrix + tf1=[float(i.strip()) for i in lines_matrix[2].split()[1:]] + tf2=[float(i.strip()) for i in lines_matrix[3].split()[1:]] + tf3=[float(i.strip()) for i in lines_matrix[4].split()[1:]] + mat = geom.Mat4(tf1[1], tf1[2], tf1[3], tf1[0], + tf2[1], tf2[2], tf2[3], tf2[0], + tf3[1], tf3[2], tf3[3], tf3[0], + 0.0, 0.0, 0.0, 1.0) + + return ost.bindings.MMAlignResult(rmsd, tm_score, tm_score_swapped, + aligned_length, mat, alns, + ent1_mapped_chains, ent2_mapped_chains) def _RunTmAlign(tmalign, tmp_dir): model1_filename=os.path.join(tmp_dir, 'model01.pdb') @@ -91,6 +177,21 @@ def _RunTmAlign(tmalign, tmp_dir): matrix_file.close() return _ParseTmAlign(lines,lines_matrix) +def _RunUSAlign(usalign, tmp_dir): + model1_filename=os.path.join(tmp_dir, 'model01.pdb') + model2_filename=os.path.join(tmp_dir, 'model02.pdb') + mat_filename = os.path.join(tmp_dir, "mat.txt") + usalign_path=settings.Locate('USalign', explicit_file_name=usalign) + command = f"{usalign_path} {model1_filename} {model2_filename} -mm 1 -ter 0 -m {mat_filename}" + ps=subprocess.Popen(command, shell=True, stdout=subprocess.PIPE) + stdout,_=ps.communicate() + lines=stdout.decode().splitlines() + if (len(lines))<22: + _CleanupFiles(tmp_dir) + raise RuntimeError("USalign superposition failed") + with open(mat_filename) as fh: + lines_matrix = fh.readlines() + return _ParseUSAlign(lines,lines_matrix) class TMScoreResult: """ @@ -216,3 +317,31 @@ def TMScore(model1, model2, tmscore=None): model1.handle.EditXCS().ApplyTransform(result.transform) _CleanupFiles(tmp_dir_name) return result + + +def USAlign(model1, model2, usalign=None): + """ + Performs a sequence independent superposition of model1 onto model2, the + reference. Can deal with multimeric complexes and RNA. + + Creates temporary model files on disk and runs USalign with: + ``USalign model1.pdb model2.pdb -mm 1 -ter 0 -m rotmat.txt`` + + :param model1: The model structure. If the superposition is successful, will + be superposed onto the reference structure + :type model1: :class:`~ost.mol.EntityView` or :class:`~ost.mol.EntityHandle` + :param model2: The reference structure + :type model2: :class:`~ost.mol.EntityView` or :class:`~ost.mol.EntityHandle` + :param usalign: If not None, the path to the USalign executable. Searches + for executable with name ``USalign`` in PATH if not given. + :returns: The result of the superposition + :rtype: :class:`ost.bindings.MMAlignResult` + + :raises: :class:`~ost.settings.FileNotFound` if executable could not be located. + :raises: :class:`RuntimeError` if the superposition failed + """ + tmp_dir_name=_SetupFiles((model1, model2)) + result=_RunUSAlign(usalign, tmp_dir_name) + model1.handle.EditXCS().ApplyTransform(result.transform) + _CleanupFiles(tmp_dir_name) + return result diff --git a/modules/bindings/src/tmalign/BLOSUM.h b/modules/bindings/src/USalign/BLOSUM.h similarity index 100% rename from modules/bindings/src/tmalign/BLOSUM.h rename to modules/bindings/src/USalign/BLOSUM.h diff --git a/modules/bindings/src/USalign/Dockerfile b/modules/bindings/src/USalign/Dockerfile new file mode 100644 index 0000000000000000000000000000000000000000..26c4bf271e3c2a0970af20bd84cd432a68349f4e --- /dev/null +++ b/modules/bindings/src/USalign/Dockerfile @@ -0,0 +1,25 @@ +FROM gcc:12.2 as build +COPY . /usr/src/usalign +WORKDIR /usr/src/usalign +RUN make -j +RUN strip qTMclust USalign TMalign TMscore MMalign se pdb2xyz xyz_sfetch pdb2fasta pdb2ss NWalign HwRMSD cif2pdb + +# Don't use alpine since we need ubuntu's support +FROM ubuntu:latest +RUN mkdir /usr/bin/usalign +WORKDIR /usr/bin/usalign +COPY --from=build /usr/src/usalign/qTMclust /usr/bin/usalign/ +COPY --from=build /usr/src/usalign/USalign /usr/bin/usalign/ +COPY --from=build /usr/src/usalign/TMalign /usr/bin/usalign/ +COPY --from=build /usr/src/usalign/TMscore /usr/bin/usalign/ +COPY --from=build /usr/src/usalign/MMalign /usr/bin/usalign/ +COPY --from=build /usr/src/usalign/se /usr/bin/usalign/ +COPY --from=build /usr/src/usalign/pdb2xyz /usr/bin/usalign/ +COPY --from=build /usr/src/usalign/xyz_sfetch /usr/bin/usalign/ +COPY --from=build /usr/src/usalign/pdb2fasta /usr/bin/usalign/ +COPY --from=build /usr/src/usalign/pdb2ss /usr/bin/usalign/ +COPY --from=build /usr/src/usalign/NWalign /usr/bin/usalign/ +COPY --from=build /usr/src/usalign/HwRMSD /usr/bin/usalign/ +COPY --from=build /usr/src/usalign/cif2pdb /usr/bin/usalign/ + +CMD "/bin/bash" diff --git a/modules/bindings/src/tmalign/HwRMSD.cpp b/modules/bindings/src/USalign/HwRMSD.cpp similarity index 100% rename from modules/bindings/src/tmalign/HwRMSD.cpp rename to modules/bindings/src/USalign/HwRMSD.cpp diff --git a/modules/bindings/src/tmalign/HwRMSD.h b/modules/bindings/src/USalign/HwRMSD.h similarity index 96% rename from modules/bindings/src/tmalign/HwRMSD.h rename to modules/bindings/src/USalign/HwRMSD.h index 8a29399cdfaf9c8b55e3767508f3ff6d78a4cad4..8e0d0b2e453986055251221831152b671748bbca 100644 --- a/modules/bindings/src/tmalign/HwRMSD.h +++ b/modules/bindings/src/USalign/HwRMSD.h @@ -140,7 +140,7 @@ int HwRMSD_main(double **xa, double **ya, const char *seqx, const char *seqy, if (n_ali8_tmp==0) { - cerr<<"WARNING! zero aligned residue in iteration "<<iter<<endl; + //cerr<<"WARNING! zero aligned residue in iteration "<<iter<<endl; if (xlen>=ylen) seqxA_tmp=(string)(seqx); if (xlen<=ylen) seqyA_tmp=(string)(seqy); if (xlen<ylen) diff --git a/modules/bindings/src/tmalign/Kabsch.h b/modules/bindings/src/USalign/Kabsch.h similarity index 100% rename from modules/bindings/src/tmalign/Kabsch.h rename to modules/bindings/src/USalign/Kabsch.h diff --git a/modules/bindings/src/tmalign/MMalign.cpp b/modules/bindings/src/USalign/MMalign.cpp similarity index 81% rename from modules/bindings/src/tmalign/MMalign.cpp rename to modules/bindings/src/USalign/MMalign.cpp index 6cc485647ec95d9f6c581148fa9dd059981d6518..816798b248be6b67657bc046865b76e3f79cd814 100644 --- a/modules/bindings/src/tmalign/MMalign.cpp +++ b/modules/bindings/src/USalign/MMalign.cpp @@ -9,7 +9,7 @@ void print_version() cout << "\n" " **********************************************************************\n" -" * MM-align (Version 20200519): complex structure alignment *\n" +" * MM-align (Version 20220412): complex structure alignment *\n" " * References: S Mukherjee, Y Zhang. Nucl Acids Res 37(11):e83 (2009) *\n" " * Please email comments and suggestions to yangzhanglab@umich.edu *\n" " **********************************************************************" @@ -440,36 +440,34 @@ int main(int argc, char *argv[]) t2 = clock(); float diff = ((float)t2 - (float)t1)/CLOCKS_PER_SEC; - printf("Total CPU time is %5.2f seconds\n", diff); + printf("#Total CPU time is %5.2f seconds\n", diff); return 0; } /* declare TM-score tables */ int chain1_num=xa_vec.size(); int chain2_num=ya_vec.size(); - double **TM1_mat; - double **TM2_mat; + vector<string> tmp_str_vec(chain2_num,""); double **TMave_mat; double **ut_mat; // rotation matrices for all-against-all alignment int ui,uj,ut_idx; - NewArray(&TM1_mat,chain1_num,chain2_num); - NewArray(&TM2_mat,chain1_num,chain2_num); NewArray(&TMave_mat,chain1_num,chain2_num); NewArray(&ut_mat,chain1_num*chain2_num,4*3); - vector<string> tmp_str_vec(chain2_num,""); vector<vector<string> >seqxA_mat(chain1_num,tmp_str_vec); vector<vector<string> > seqM_mat(chain1_num,tmp_str_vec); vector<vector<string> >seqyA_mat(chain1_num,tmp_str_vec); - tmp_str_vec.clear(); + + double maxTMmono=-1; + int maxTMmono_i,maxTMmono_j; /* get all-against-all alignment */ + if (len_aa+len_na>500) fast_opt=true; for (i=0;i<chain1_num;i++) { xlen=xlen_vec[i]; if (xlen<3) { - for (j=0;j<chain2_num;j++) - TM1_mat[i][j]=TM2_mat[i][j]=TMave_mat[i][j]=-1; + for (j=0;j<chain2_num;j++) TMave_mat[i][j]=-1; continue; } seqx = new char[xlen+1]; @@ -489,14 +487,14 @@ int main(int argc, char *argv[]) if (mol_vec1[i]*mol_vec2[j]<0) //no protein-RNA alignment { - TM1_mat[i][j]=TM2_mat[i][j]=TMave_mat[i][j]=-1; + TMave_mat[i][j]=-1; continue; } ylen=ylen_vec[j]; if (ylen<3) { - TM1_mat[i][j]=TM2_mat[i][j]=TMave_mat[i][j]=-1; + TMave_mat[i][j]=-1; continue; } seqy = new char[ylen+1]; @@ -530,18 +528,22 @@ int main(int argc, char *argv[]) seqM, seqxA, seqyA, rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, xlen, ylen, sequence, Lnorm_tmp, d0_scale, - 0, false, true, false, true, + 0, false, true, false, fast_opt, mol_vec1[i]+mol_vec2[j],TMcut); /* store result */ for (ui=0;ui<3;ui++) for (uj=0;uj<3;uj++) ut_mat[ut_idx][ui*3+uj]=u0[ui][uj]; for (uj=0;uj<3;uj++) ut_mat[ut_idx][9+uj]=t0[uj]; - TM1_mat[i][j]=TM2; // normalized by chain1 - TM2_mat[i][j]=TM1; // normalized by chain2 seqxA_mat[i][j]=seqxA; seqyA_mat[i][j]=seqyA; TMave_mat[i][j]=TM4*Lnorm_tmp; + if (TMave_mat[i][j]>maxTMmono) + { + maxTMmono=TMave_mat[i][j]; + maxTMmono_i=i; + maxTMmono_j=j; + } /* clean up */ seqM.clear(); @@ -568,8 +570,7 @@ int main(int argc, char *argv[]) if (total_score<=0) PrintErrorAndQuit("ERROR! No assignable chain"); /* refine alignment for large oligomers */ - int aln_chain_num=0; - for (i=0;i<chain1_num;i++) aln_chain_num+=(assign1_list[i]>=0); + int aln_chain_num=count_assign_pair(assign1_list,chain1_num); bool is_oligomer=(aln_chain_num>=3); if (aln_chain_num==2) // dimer alignment { @@ -617,22 +618,90 @@ int main(int argc, char *argv[]) DeleteArray(&xcentroids, chain1_num); DeleteArray(&ycentroids, chain2_num); } - if (len_aa+len_na>1000) fast_opt=true; + + /* store initial assignment */ + int init_pair_num=count_assign_pair(assign1_list,chain1_num); + int *assign1_init, *assign2_init; + assign1_init=new int[chain1_num]; + assign2_init=new int[chain2_num]; + double **TMave_init; + NewArray(&TMave_init,chain1_num,chain2_num); + vector<vector<string> >seqxA_init(chain1_num,tmp_str_vec); + vector<vector<string> >seqyA_init(chain1_num,tmp_str_vec); + vector<string> sequence_init; + copy_chain_assign_data(chain1_num, chain2_num, sequence_init, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, TMave_mat, + seqxA_init, seqyA_init, assign1_init, assign2_init, TMave_init); /* perform iterative alignment */ - for (int iter=0;iter<1;iter++) + double max_total_score=0; // ignore old total_score because previous + // score was from monomeric chain superpositions + int max_iter=5-(int)((len_aa+len_na)/200); + if (max_iter<2) max_iter=2; + MMalign_iter(max_total_score, max_iter, xa_vec, ya_vec, seqx_vec, seqy_vec, + secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, + xa, ya, seqx, seqy, secx, secy, len_aa, len_na, chain1_num, chain2_num, + TMave_mat, seqxA_mat, seqyA_mat, assign1_list, assign2_list, sequence, + d0_scale, fast_opt); + + /* sometime MMalign_iter is even worse than monomer alignment */ + if (max_total_score<maxTMmono) { - total_score=MMalign_search(xa_vec, ya_vec, seqx_vec, seqy_vec, + copy_chain_assign_data(chain1_num, chain2_num, sequence, + seqxA_init, seqyA_init, assign1_init, assign2_init, TMave_init, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, TMave_mat); + for (i=0;i<chain1_num;i++) + { + if (i!=maxTMmono_i) assign1_list[i]=-1; + else assign1_list[i]=maxTMmono_j; + } + for (j=0;j<chain2_num;j++) + { + if (j!=maxTMmono_j) assign2_list[j]=-1; + else assign2_list[j]=maxTMmono_i; + } + sequence[0]=seqxA_mat[maxTMmono_i][maxTMmono_j]; + sequence[1]=seqyA_mat[maxTMmono_i][maxTMmono_j]; + max_total_score=maxTMmono; + MMalign_iter(max_total_score, max_iter, xa_vec, ya_vec, seqx_vec, seqy_vec, secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, - xa, ya, seqx, seqy, secx, secy, len_aa, len_na, - chain1_num, chain2_num, TM1_mat, TM2_mat, TMave_mat, - seqxA_mat, seqyA_mat, assign1_list, assign2_list, sequence, - d0_scale, true); - total_score=enhanced_greedy_search(TMave_mat, assign1_list, - assign2_list, chain1_num, chain2_num); - if (total_score<=0) PrintErrorAndQuit("ERROR! No assignable chain"); + xa, ya, seqx, seqy, secx, secy, len_aa, len_na, chain1_num, chain2_num, + TMave_mat, seqxA_mat, seqyA_mat, assign1_list, assign2_list, sequence, + d0_scale, fast_opt); } + /* perform cross chain alignment + * in some cases, this leads to dramatic improvement, esp for homodimer */ + int iter_pair_num=count_assign_pair(assign1_list,chain1_num); + if (iter_pair_num>=init_pair_num) copy_chain_assign_data( + chain1_num, chain2_num, sequence_init, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, TMave_mat, + seqxA_init, seqyA_init, assign1_init, assign2_init, TMave_init); + double max_total_score_cross=max_total_score; + + //if (init_pair_num!=2 && is_oligomer==false) MMalign_cross( + //max_total_score_cross, max_iter, xa_vec, ya_vec, seqx_vec, seqy_vec, + //secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, + //xa, ya, seqx, seqy, secx, secy, len_aa, len_na, chain1_num, chain2_num, + //TMave_init, seqxA_init, seqyA_init, assign1_init, assign2_init, sequence_init, + //d0_scale, true); + //else + if (len_aa+len_na<10000) + { + MMalign_dimer(max_total_score_cross, xa_vec, ya_vec, seqx_vec, seqy_vec, + secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, + xa, ya, seqx, seqy, secx, secy, len_aa, len_na, chain1_num, chain2_num, + TMave_init, seqxA_init, seqyA_init, assign1_init, assign2_init, + sequence_init, d0_scale, fast_opt); + if (max_total_score_cross>max_total_score) + { + max_total_score=max_total_score_cross; + copy_chain_assign_data(chain1_num, chain2_num, sequence, + seqxA_init, seqyA_init, assign1_init, assign2_init, TMave_init, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, TMave_mat); + } + } + /* final alignment */ if (outfmt_opt==0) print_version(); MMalign_final(xname.substr(dir1_opt.size()), yname.substr(dir2_opt.size()), @@ -641,7 +710,7 @@ int main(int argc, char *argv[]) xa_vec, ya_vec, seqx_vec, seqy_vec, secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, xa, ya, seqx, seqy, secx, secy, len_aa, len_na, - chain1_num, chain2_num, TM1_mat, TM2_mat, TMave_mat, + chain1_num, chain2_num, TMave_mat, seqxA_mat, seqM_mat, seqyA_mat, assign1_list, assign2_list, sequence, d0_scale, m_opt, o_opt, outfmt_opt, ter_opt, split_opt, a_opt, d_opt, fast_opt, full_opt, mirror_opt, resi_vec1, resi_vec2); @@ -649,13 +718,18 @@ int main(int argc, char *argv[]) /* clean up everything */ delete [] assign1_list; delete [] assign2_list; - DeleteArray(&TM1_mat, chain1_num); - DeleteArray(&TM2_mat, chain1_num); DeleteArray(&TMave_mat,chain1_num); DeleteArray(&ut_mat, chain1_num*chain2_num); vector<vector<string> >().swap(seqxA_mat); vector<vector<string> >().swap(seqM_mat); vector<vector<string> >().swap(seqyA_mat); + vector<string>().swap(tmp_str_vec); + + delete [] assign1_init; + delete [] assign2_init; + DeleteArray(&TMave_init,chain1_num); + vector<vector<string> >().swap(seqxA_init); + vector<vector<string> >().swap(seqyA_init); vector<vector<vector<double> > >().swap(xa_vec); // structure of complex1 vector<vector<vector<double> > >().swap(ya_vec); // structure of complex2 @@ -672,9 +746,11 @@ int main(int argc, char *argv[]) vector<string>().swap(chain1_list); vector<string>().swap(chain2_list); vector<string>().swap(sequence); + vector<string>().swap(resi_vec1); // residue index for chain1 + vector<string>().swap(resi_vec2); // residue index for chain2 t2 = clock(); float diff = ((float)t2 - (float)t1)/CLOCKS_PER_SEC; - printf("Total CPU time is %5.2f seconds\n", diff); + printf("#Total CPU time is %5.2f seconds\n", diff); return 0; } diff --git a/modules/bindings/src/USalign/MMalign.h b/modules/bindings/src/USalign/MMalign.h new file mode 100644 index 0000000000000000000000000000000000000000..4b480da62dd30b3c9d32a3b602d941166b7368df --- /dev/null +++ b/modules/bindings/src/USalign/MMalign.h @@ -0,0 +1,3040 @@ +#include <cfloat> +#include "se.h" + +/* count the number of nucleic acid chains (na_chain_num) and + * protein chains (aa_chain_num) in a complex */ +int count_na_aa_chain_num(int &na_chain_num,int &aa_chain_num, + const vector<int>&mol_vec) +{ + na_chain_num=0; + aa_chain_num=0; + for (size_t i=0;i<mol_vec.size();i++) + { + if (mol_vec[i]>0) na_chain_num++; + else aa_chain_num++; + } + return na_chain_num+aa_chain_num; +} + +/* adjust chain assignment for dimer-dimer alignment + * return true if assignment is adjusted */ +bool adjust_dimer_assignment( + const vector<vector<vector<double> > >&xa_vec, + const vector<vector<vector<double> > >&ya_vec, + const vector<int>&xlen_vec, const vector<int>&ylen_vec, + const vector<int>&mol_vec1, const vector<int>&mol_vec2, + int *assign1_list, int *assign2_list, + const vector<vector<string> >&seqxA_mat, + const vector<vector<string> >&seqyA_mat) +{ + /* check currently assigned chains */ + int i1,i2,j1,j2; + i1=i2=j1=j2=-1; + int chain1_num=xa_vec.size(); + int i,j; + for (i=0;i<chain1_num;i++) + { + if (assign1_list[i]>=0) + { + if (i1<0) + { + i1=i; + j1=assign1_list[i1]; + } + else + { + i2=i; + j2=assign1_list[i2]; + } + } + } + + /* normalize d0 by L */ + int xlen=xlen_vec[i1]+xlen_vec[i2]; + int ylen=ylen_vec[j1]+ylen_vec[j2]; + int mol_type=mol_vec1[i1]+mol_vec1[i2]+ + mol_vec2[j1]+mol_vec2[j2]; + double D0_MIN, d0, d0_search; + double Lnorm=getmin(xlen,ylen); + parameter_set4final(getmin(xlen,ylen), D0_MIN, Lnorm, d0, + d0_search, mol_type); + + double **xa,**ya, **xt; + NewArray(&xa, xlen, 3); + NewArray(&ya, ylen, 3); + NewArray(&xt, xlen, 3); + + double RMSD = 0; + double dd = 0; + double t[3]; + double u[3][3]; + size_t L_ali=0; // index of residue in aligned region + size_t r=0; // index of residue in full alignment + + /* total score using current assignment */ + L_ali=0; + i=j=-1; + for (r=0;r<seqxA_mat[i1][j1].size();r++) + { + i+=(seqxA_mat[i1][j1][r]!='-'); + j+=(seqyA_mat[i1][j1][r]!='-'); + if (seqxA_mat[i1][j1][r]=='-' || seqyA_mat[i1][j1][r]=='-') continue; + xa[L_ali][0]=xa_vec[i1][i][0]; + xa[L_ali][1]=xa_vec[i1][i][1]; + xa[L_ali][2]=xa_vec[i1][i][2]; + ya[L_ali][0]=ya_vec[j1][j][0]; + ya[L_ali][1]=ya_vec[j1][j][1]; + ya[L_ali][2]=ya_vec[j1][j][2]; + L_ali++; + } + i=j=-1; + for (r=0;r<seqxA_mat[i2][j2].size();r++) + { + i+=(seqxA_mat[i2][j2][r]!='-'); + j+=(seqyA_mat[i2][j2][r]!='-'); + if (seqxA_mat[i2][j2][r]=='-' || seqyA_mat[i2][j2][r]=='-') continue; + xa[L_ali][0]=xa_vec[i2][i][0]; + xa[L_ali][1]=xa_vec[i2][i][1]; + xa[L_ali][2]=xa_vec[i2][i][2]; + ya[L_ali][0]=ya_vec[j2][j][0]; + ya[L_ali][1]=ya_vec[j2][j][1]; + ya[L_ali][2]=ya_vec[j2][j][2]; + L_ali++; + } + + Kabsch(xa, ya, L_ali, 1, &RMSD, t, u); + do_rotation(xa, xt, L_ali, t, u); + + double total_score1=0; + for (r=0;r<L_ali;r++) + { + dd=dist(xt[r],ya[r]); + total_score1+=1/(1+dd/d0*d0); + } + total_score1/=Lnorm; + + /* total score using reversed assignment */ + L_ali=0; + i=j=-1; + for (r=0;r<seqxA_mat[i1][j2].size();r++) + { + i+=(seqxA_mat[i1][j2][r]!='-'); + j+=(seqyA_mat[i1][j2][r]!='-'); + if (seqxA_mat[i1][j2][r]=='-' || seqyA_mat[i1][j2][r]=='-') continue; + xa[L_ali][0]=xa_vec[i1][i][0]; + xa[L_ali][1]=xa_vec[i1][i][1]; + xa[L_ali][2]=xa_vec[i1][i][2]; + ya[L_ali][0]=ya_vec[j2][j][0]; + ya[L_ali][1]=ya_vec[j2][j][1]; + ya[L_ali][2]=ya_vec[j2][j][2]; + L_ali++; + } + i=j=-1; + for (r=0;r<seqxA_mat[i2][j1].size();r++) + { + i+=(seqxA_mat[i2][j1][r]!='-'); + j+=(seqyA_mat[i2][j1][r]!='-'); + if (seqxA_mat[i2][j1][r]=='-' || seqyA_mat[i2][j1][r]=='-') continue; + xa[L_ali][0]=xa_vec[i2][i][0]; + xa[L_ali][1]=xa_vec[i2][i][1]; + xa[L_ali][2]=xa_vec[i2][i][2]; + ya[L_ali][0]=ya_vec[j1][j][0]; + ya[L_ali][1]=ya_vec[j1][j][1]; + ya[L_ali][2]=ya_vec[j1][j][2]; + L_ali++; + } + + Kabsch(xa, ya, L_ali, 1, &RMSD, t, u); + do_rotation(xa, xt, L_ali, t, u); + + double total_score2=0; + for (r=0;r<L_ali;r++) + { + dd=dist(xt[r],ya[r]); + total_score2+=1/(1+dd/d0*d0); + } + total_score2/=Lnorm; + + /* swap chain assignment */ + if (total_score1<total_score2) + { + assign1_list[i1]=j2; + assign1_list[i2]=j1; + assign2_list[j1]=i2; + assign2_list[j2]=i1; + } + + /* clean up */ + DeleteArray(&xa, xlen); + DeleteArray(&ya, ylen); + DeleteArray(&xt, xlen); + return total_score1<total_score2; +} + +/* count how many chains are paired */ +int count_assign_pair(int *assign1_list,const int chain1_num) +{ + int pair_num=0; + int i; + for (i=0;i<chain1_num;i++) pair_num+=(assign1_list[i]>=0); + return pair_num; +} + + +/* assign chain-chain correspondence */ +double enhanced_greedy_search(double **TMave_mat,int *assign1_list, + int *assign2_list, const int chain1_num, const int chain2_num) +{ + double total_score=0; + double tmp_score=0; + int i,j; + int maxi=0; + int maxj=0; + + /* initialize parameters */ + for (i=0;i<chain1_num;i++) assign1_list[i]=-1; + for (j=0;j<chain2_num;j++) assign2_list[j]=-1; + + /* greedy assignment: in each iteration, the highest chain pair is + * assigned, until no assignable chain is left */ + while(1) + { + tmp_score=-1; + for (i=0;i<chain1_num;i++) + { + if (assign1_list[i]>=0) continue; + for (j=0;j<chain2_num;j++) + { + if (assign2_list[j]>=0 || TMave_mat[i][j]<=0) continue; + if (TMave_mat[i][j]>tmp_score) + { + maxi=i; + maxj=j; + tmp_score=TMave_mat[i][j]; + } + } + } + if (tmp_score<=0) break; // error: no assignable chain + assign1_list[maxi]=maxj; + assign2_list[maxj]=maxi; + total_score+=tmp_score; + } + if (total_score<=0) return total_score; // error: no assignable chain + //cout<<"assign1_list={"; + //for (i=0;i<chain1_num;i++) cout<<assign1_list[i]<<","; cout<<"}"<<endl; + //cout<<"assign2_list={"; + //for (j=0;j<chain2_num;j++) cout<<assign2_list[j]<<","; cout<<"}"<<endl; + + /* iterative refinemnt */ + double delta_score; + int *assign1_tmp=new int [chain1_num]; + int *assign2_tmp=new int [chain2_num]; + for (i=0;i<chain1_num;i++) assign1_tmp[i]=assign1_list[i]; + for (j=0;j<chain2_num;j++) assign2_tmp[j]=assign2_list[j]; + int old_i=-1; + int old_j=-1; + + for (int iter=0;iter<getmin(chain1_num,chain2_num)*5;iter++) + { + delta_score=-1; + for (i=0;i<chain1_num;i++) + { + old_j=assign1_list[i]; + for (j=0;j<chain2_num;j++) + { + // attempt to swap (i,old_j=assign1_list[i]) with (i,j) + if (j==assign1_list[i] || TMave_mat[i][j]<=0) continue; + old_i=assign2_list[j]; + + assign1_tmp[i]=j; + if (old_i>=0) assign1_tmp[old_i]=old_j; + assign2_tmp[j]=i; + if (old_j>=0) assign2_tmp[old_j]=old_i; + + delta_score=TMave_mat[i][j]; + if (old_j>=0) delta_score-=TMave_mat[i][old_j]; + if (old_i>=0) delta_score-=TMave_mat[old_i][j]; + if (old_i>=0 && old_j>=0) delta_score+=TMave_mat[old_i][old_j]; + + if (delta_score>0) // successful swap + { + assign1_list[i]=j; + if (old_i>=0) assign1_list[old_i]=old_j; + assign2_list[j]=i; + if (old_j>=0) assign2_list[old_j]=old_i; + total_score+=delta_score; + break; + } + else + { + assign1_tmp[i]=assign1_list[i]; + if (old_i>=0) assign1_tmp[old_i]=assign1_list[old_i]; + assign2_tmp[j]=assign2_list[j]; + if (old_j>=0) assign2_tmp[old_j]=assign2_list[old_j]; + } + } + if (delta_score>0) break; + } + if (delta_score<=0) break; // cannot swap any chain pair + } + + /* clean up */ + delete[]assign1_tmp; + delete[]assign2_tmp; + return total_score; +} + +double calculate_centroids(const vector<vector<vector<double> > >&a_vec, + const int chain_num, double ** centroids) +{ + int L=0; + int c,r; // index of chain and residue + for (c=0; c<chain_num; c++) + { + centroids[c][0]=0; + centroids[c][1]=0; + centroids[c][2]=0; + L=a_vec[c].size(); + for (r=0; r<L; r++) + { + centroids[c][0]+=a_vec[c][r][0]; + centroids[c][1]+=a_vec[c][r][1]; + centroids[c][2]+=a_vec[c][r][2]; + } + centroids[c][0]/=L; + centroids[c][1]/=L; + centroids[c][2]/=L; + //cout<<centroids[c][0]<<'\t' + //<<centroids[c][1]<<'\t' + //<<centroids[c][2]<<endl; + } + + vector<double> d0_vec(chain_num,-1); + int c2=0; + double d0MM=0; + for (c=0; c<chain_num; c++) + { + for (c2=0; c2<chain_num; c2++) + { + if (c2==c) continue; + d0MM=sqrt(dist(centroids[c],centroids[c2])); + if (d0_vec[c]<=0) d0_vec[c]=d0MM; + else d0_vec[c]=getmin(d0_vec[c], d0MM); + } + } + d0MM=0; + for (c=0; c<chain_num; c++) d0MM+=d0_vec[c]; + d0MM/=chain_num; + d0_vec.clear(); + //cout<<d0MM<<endl; + return d0MM; +} + +/* calculate MMscore of aligned chains + * MMscore = sum(TMave_mat[i][j]) * sum(1/(1+dij^2/d0MM^2)) + * / (L* getmin(chain1_num,chain2_num)) + * dij is the centroid distance between chain pair i and j + * d0MM is scaling factor. TMave_mat[i][j] is the TM-score between + * chain pair i and j multiple by getmin(Li*Lj) */ +double calMMscore(double **TMave_mat,int *assign1_list, + const int chain1_num, const int chain2_num, double **xcentroids, + double **ycentroids, const double d0MM, double **r1, double **r2, + double **xt, double t[3], double u[3][3], const int L) +{ + int Nali=0; // number of aligned chain + int i,j; + double MMscore=0; + for (i=0;i<chain1_num;i++) + { + j=assign1_list[i]; + if (j<0) continue; + + r1[Nali][0]=xcentroids[i][0]; + r1[Nali][1]=xcentroids[i][1]; + r1[Nali][2]=xcentroids[i][2]; + + r2[Nali][0]=ycentroids[j][0]; + r2[Nali][1]=ycentroids[j][1]; + r2[Nali][2]=ycentroids[j][2]; + + Nali++; + MMscore+=TMave_mat[i][j]; + } + MMscore/=L; + + double RMSD = 0; + double TMscore=0; + if (Nali>=3) + { + /* Kabsch superposition */ + Kabsch(r1, r2, Nali, 1, &RMSD, t, u); + do_rotation(r1, xt, Nali, t, u); + + /* calculate pseudo-TMscore */ + double dd=0; + for (i=0;i<Nali;i++) + { + dd=dist(xt[i], r2[i]); + TMscore+=1/(1+dd/(d0MM*d0MM)); + } + } + else if (Nali==2) + { + double dd=dist(r1[0],r2[0]); + TMscore=1/(1+dd/(d0MM*d0MM)); + } + else TMscore=1; // only one aligned chain. + TMscore/=getmin(chain1_num,chain2_num); + MMscore*=TMscore; + return MMscore; +} + +/* check if this is alignment of heterooligomer or homooligomer + * return het_deg, which ranges from 0 to 1. + * The larger the value, the more "hetero"; + * Tthe smaller the value, the more "homo" */ +double check_heterooligomer(double **TMave_mat, const int chain1_num, + const int chain2_num) +{ + double het_deg=0; + double min_TM=-1; + double max_TM=-1; + int i,j; + for (i=0;i<chain1_num;i++) + { + for (j=0;j<chain2_num;j++) + { + if (min_TM<0 || TMave_mat[i][j] <min_TM) min_TM=TMave_mat[i][j]; + if (max_TM<0 || TMave_mat[i][j]>=max_TM) max_TM=TMave_mat[i][j]; + } + } + het_deg=(max_TM-min_TM)/max_TM; + //cout<<"min_TM="<<min_TM<<endl; + //cout<<"max_TM="<<max_TM<<endl; + return het_deg; +} + +/* reassign chain-chain correspondence, specific for homooligomer */ +double homo_refined_greedy_search(double **TMave_mat,int *assign1_list, + int *assign2_list, const int chain1_num, const int chain2_num, + double **xcentroids, double **ycentroids, const double d0MM, + const int L, double **ut_mat) +{ + double MMscore_max=0; + double MMscore=0; + int i,j; + int c1,c2; + int max_i=-1; // the chain pair whose monomer u t yields highest MMscore + int max_j=-1; + + int chain_num=getmin(chain1_num,chain2_num); + int *assign1_tmp=new int [chain1_num]; + int *assign2_tmp=new int [chain2_num]; + double **xt; + NewArray(&xt, chain1_num, 3); + double t[3]; + double u[3][3]; + int ui,uj,ut_idx; + double TMscore=0; // pseudo TM-score + double TMsum =0; + double TMnow =0; + double TMmax =0; + double dd=0; + + size_t total_pair=chain1_num*chain2_num; // total pair + double *ut_tmc_mat=new double [total_pair]; // chain level TM-score + vector<pair<double,int> > ut_tm_vec(total_pair,make_pair(0.0,0)); // product of both + + for (c1=0;c1<chain1_num;c1++) + { + for (c2=0;c2<chain2_num;c2++) + { + if (TMave_mat[c1][c2]<=0) continue; + ut_idx=c1*chain2_num+c2; + for (ui=0;ui<3;ui++) + for (uj=0;uj<3;uj++) u[ui][uj]=ut_mat[ut_idx][ui*3+uj]; + for (uj=0;uj<3;uj++) t[uj]=ut_mat[ut_idx][9+uj]; + + do_rotation(xcentroids, xt, chain1_num, t, u); + + for (i=0;i<chain1_num;i++) assign1_tmp[i]=-1; + for (j=0;j<chain2_num;j++) assign2_tmp[j]=-1; + + + for (i=0;i<chain1_num;i++) + { + for (j=0;j<chain2_num;j++) + { + ut_idx=i*chain2_num+j; + ut_tmc_mat[ut_idx]=0; + ut_tm_vec[ut_idx].first=-1; + ut_tm_vec[ut_idx].second=ut_idx; + if (TMave_mat[i][j]<=0) continue; + dd=dist(xt[i],ycentroids[j]); + ut_tmc_mat[ut_idx]=1/(1+dd/(d0MM*d0MM)); + ut_tm_vec[ut_idx].first= + ut_tmc_mat[ut_idx]*TMave_mat[i][j]; + //cout<<"TM["<<ut_idx<<"]="<<ut_tm_vec[ut_idx].first<<endl; + } + } + //cout<<"sorting "<<total_pair<<" chain pairs"<<endl; + + /* initial assignment */ + assign1_tmp[c1]=c2; + assign2_tmp[c2]=c1; + TMsum=TMave_mat[c1][c2]; + TMscore=ut_tmc_mat[c1*chain2_num+c2]; + + /* further assignment */ + sort(ut_tm_vec.begin(), ut_tm_vec.end()); // sort in ascending order + for (ut_idx=total_pair-1;ut_idx>=0;ut_idx--) + { + j=ut_tm_vec[ut_idx].second % chain2_num; + i=int(ut_tm_vec[ut_idx].second / chain2_num); + if (TMave_mat[i][j]<=0) break; + if (assign1_tmp[i]>=0 || assign2_tmp[j]>=0) continue; + assign1_tmp[i]=j; + assign2_tmp[j]=i; + TMsum+=TMave_mat[i][j]; + TMscore+=ut_tmc_mat[i*chain2_num+j]; + //cout<<"ut_idx="<<ut_tm_vec[ut_idx].second + //<<"\ti="<<i<<"\tj="<<j<<"\ttm="<<ut_tm_vec[ut_idx].first<<endl; + } + + /* final MMscore */ + MMscore=(TMsum/L)*(TMscore/chain_num); + if (max_i<0 || max_j<0 || MMscore>MMscore_max) + { + max_i=c1; + max_j=c2; + MMscore_max=MMscore; + for (i=0;i<chain1_num;i++) assign1_list[i]=assign1_tmp[i]; + for (j=0;j<chain2_num;j++) assign2_list[j]=assign2_tmp[j]; + //cout<<"TMsum/L="<<TMsum/L<<endl; + //cout<<"TMscore/chain_num="<<TMscore/chain_num<<endl; + //cout<<"MMscore="<<MMscore<<endl; + //cout<<"assign1_list={"; + //for (i=0;i<chain1_num;i++) + //cout<<assign1_list[i]<<","; cout<<"}"<<endl; + //cout<<"assign2_list={"; + //for (j=0;j<chain2_num;j++) + //cout<<assign2_list[j]<<","; cout<<"}"<<endl; + } + } + } + + /* clean up */ + delete[]assign1_tmp; + delete[]assign2_tmp; + delete[]ut_tmc_mat; + ut_tm_vec.clear(); + DeleteArray(&xt, chain1_num); + return MMscore; +} + +/* reassign chain-chain correspondence, specific for heterooligomer */ +double hetero_refined_greedy_search(double **TMave_mat,int *assign1_list, + int *assign2_list, const int chain1_num, const int chain2_num, + double **xcentroids, double **ycentroids, const double d0MM, const int L) +{ + double MMscore_old=0; + double MMscore=0; + int i,j; + + double **r1; + double **r2; + double **xt; + int chain_num=getmin(chain1_num,chain2_num); + NewArray(&r1, chain_num, 3); + NewArray(&r2, chain_num, 3); + NewArray(&xt, chain_num, 3); + double t[3]; + double u[3][3]; + + /* calculate MMscore */ + MMscore=MMscore_old=calMMscore(TMave_mat, assign1_list, chain1_num, + chain2_num, xcentroids, ycentroids, d0MM, r1, r2, xt, t, u, L); + //cout<<"MMscore="<<MMscore<<endl; + //cout<<"TMave_mat="<<endl; + //for (i=0;i<chain1_num;i++) + //{ + //for (j=0; j<chain2_num; j++) + //{ + //if (j<chain2_num-1) cout<<TMave_mat[i][j]<<'\t'; + //else cout<<TMave_mat[i][j]<<endl; + //} + //} + + /* iteratively refine chain assignment. in each iteration, attempt + * to swap (i,old_j=assign1_list[i]) with (i,j) */ + double delta_score=-1; + int *assign1_tmp=new int [chain1_num]; + int *assign2_tmp=new int [chain2_num]; + for (i=0;i<chain1_num;i++) assign1_tmp[i]=assign1_list[i]; + for (j=0;j<chain2_num;j++) assign2_tmp[j]=assign2_list[j]; + int old_i=-1; + int old_j=-1; + + //cout<<"assign1_list={"; + //for (i=0;i<chain1_num;i++) cout<<assign1_list[i]<<","; cout<<"}"<<endl; + //cout<<"assign2_list={"; + //for (j=0;j<chain2_num;j++) cout<<assign2_list[j]<<","; cout<<"}"<<endl; + + for (int iter=0;iter<chain1_num*chain2_num;iter++) + { + delta_score=-1; + for (i=0;i<chain1_num;i++) + { + old_j=assign1_list[i]; + for (j=0;j<chain2_num;j++) + { + if (j==assign1_list[i] || TMave_mat[i][j]<=0) continue; + old_i=assign2_list[j]; + + assign1_tmp[i]=j; + if (old_i>=0) assign1_tmp[old_i]=old_j; + assign2_tmp[j]=i; + if (old_j>=0) assign2_tmp[old_j]=old_i; + + MMscore=calMMscore(TMave_mat, assign1_tmp, chain1_num, + chain2_num, xcentroids, ycentroids, d0MM, + r1, r2, xt, t, u, L); + + //cout<<"(i,j,old_i,old_j,MMscore)=("<<i<<","<<j<<"," + //<<old_i<<","<<old_j<<","<<MMscore<<")"<<endl; + + if (MMscore>MMscore_old) // successful swap + { + assign1_list[i]=j; + if (old_i>=0) assign1_list[old_i]=old_j; + assign2_list[j]=i; + if (old_j>=0) assign2_list[old_j]=old_i; + delta_score=(MMscore-MMscore_old); + MMscore_old=MMscore; + //cout<<"MMscore="<<MMscore<<endl; + break; + } + else + { + assign1_tmp[i]=assign1_list[i]; + if (old_i>=0) assign1_tmp[old_i]=assign1_list[old_i]; + assign2_tmp[j]=assign2_list[j]; + if (old_j>=0) assign2_tmp[old_j]=assign2_list[old_j]; + } + } + } + //cout<<"iter="<<iter<<endl; + //cout<<"assign1_list={"; + //for (i=0;i<chain1_num;i++) cout<<assign1_list[i]<<","; cout<<"}"<<endl; + //cout<<"assign2_list={"; + //for (j=0;j<chain2_num;j++) cout<<assign2_list[j]<<","; cout<<"}"<<endl; + if (delta_score<=0) break; // cannot swap any chain pair + } + MMscore=MMscore_old; + //cout<<"MMscore="<<MMscore<<endl; + + /* clean up */ + delete[]assign1_tmp; + delete[]assign2_tmp; + DeleteArray(&r1, chain_num); + DeleteArray(&r2, chain_num); + DeleteArray(&xt, chain_num); + return MMscore; +} + +void copy_chain_data(const vector<vector<double> >&a_vec_i, + const vector<char>&seq_vec_i,const vector<char>&sec_vec_i, + const int len,double **a,char *seq,char *sec) +{ + int r; + for (r=0;r<len;r++) + { + a[r][0]=a_vec_i[r][0]; + a[r][1]=a_vec_i[r][1]; + a[r][2]=a_vec_i[r][2]; + seq[r]=seq_vec_i[r]; + sec[r]=sec_vec_i[r]; + } + seq[len]=0; + sec[len]=0; +} + +/* clear chains with L<3 */ +void clear_full_PDB_lines(vector<vector<string> > PDB_lines,const string atom_opt) +{ + int chain_i; + int Lch; + int a; + bool select_atom; + string line; + for (chain_i=0;chain_i<PDB_lines.size();chain_i++) + { + Lch=0; + for (a=0;a<PDB_lines[chain_i].size();a++) + { + line=PDB_lines[chain_i][a]; + if (atom_opt=="auto") + { + if (line[17]==' ' && (line[18]=='D'||line[18]==' ')) + select_atom=(line.compare(12,4," C3'")==0); + else select_atom=(line.compare(12,4," CA ")==0); + } + else select_atom=(line.compare(12,4,atom_opt)==0); + Lch+=select_atom; + } + if (Lch<3) + { + for (a=0;a<PDB_lines[chain_i].size();a++) + PDB_lines[chain_i][a].clear(); + PDB_lines[chain_i].clear(); + } + } + line.clear(); +} + +size_t get_full_PDB_lines(const string filename, + vector<vector<string> >&PDB_lines, const int ter_opt, + const int infmt_opt, const int split_opt, const int het_opt) +{ + size_t i=0; // resi i.e. atom index + string line; + char chainID=0; + vector<string> tmp_str_vec; + + int compress_type=0; // uncompressed file + ifstream fin; +#ifndef REDI_PSTREAM_H_SEEN + ifstream fin_gz; +#else + redi::ipstream fin_gz; // if file is compressed + if (filename.size()>=3 && + filename.substr(filename.size()-3,3)==".gz") + { + fin_gz.open("gunzip -c '"+filename+"'"); + compress_type=1; + } + else if (filename.size()>=4 && + filename.substr(filename.size()-4,4)==".bz2") + { + fin_gz.open("bzcat '"+filename+"'"); + compress_type=2; + } + else +#endif + fin.open(filename.c_str()); + + if (infmt_opt==0||infmt_opt==-1) // PDB format + { + while (compress_type?fin_gz.good():fin.good()) + { + if (compress_type) getline(fin_gz, line); + else getline(fin, line); + if (infmt_opt==-1 && line.compare(0,5,"loop_")==0) // PDBx/mmCIF + return get_full_PDB_lines(filename,PDB_lines, + ter_opt, 3, split_opt,het_opt); + if (i > 0) + { + if (ter_opt>=1 && line.compare(0,3,"END")==0) break; + else if (ter_opt>=3 && line.compare(0,3,"TER")==0) break; + } + if (split_opt && line.compare(0,3,"END")==0) chainID=0; + if (line.size()>=54 && (line[16]==' ' || line[16]=='A') && ( + (line.compare(0, 6, "ATOM ")==0) || + (line.compare(0, 6, "HETATM")==0 && het_opt==1) || + (line.compare(0, 6, "HETATM")==0 && het_opt==2 && + line.compare(17,3, "MSE")==0))) + { + if (!chainID) + { + chainID=line[21]; + PDB_lines.push_back(tmp_str_vec); + } + else if (ter_opt>=2 && chainID!=line[21]) break; + if (split_opt==2 && chainID!=line[21]) + { + chainID=line[21]; + PDB_lines.push_back(tmp_str_vec); + } + + PDB_lines.back().push_back(line); + i++; + } + } + } + else if (infmt_opt==1) // SPICKER format + { + size_t L=0; + float x,y,z; + stringstream i8_stream; + while (compress_type?fin_gz.good():fin.good()) + { + if (compress_type) fin_gz>>L>>x>>y>>z; + else fin >>L>>x>>y>>z; + if (compress_type) getline(fin_gz, line); + else getline(fin, line); + if (!(compress_type?fin_gz.good():fin.good())) break; + for (i=0;i<L;i++) + { + if (compress_type) fin_gz>>x>>y>>z; + else fin >>x>>y>>z; + i8_stream<<"ATOM "<<setw(4)<<i+1<<" CA UNK "<<setw(4) + <<i+1<<" "<<setiosflags(ios::fixed)<<setprecision(3) + <<setw(8)<<x<<setw(8)<<y<<setw(8)<<z; + line=i8_stream.str(); + i8_stream.str(string()); + PDB_lines.back().push_back(line); + } + if (compress_type) getline(fin_gz, line); + else getline(fin, line); + } + } + else if (infmt_opt==2) // xyz format + { + size_t L=0; + stringstream i8_stream; + while (compress_type?fin_gz.good():fin.good()) + { + if (compress_type) getline(fin_gz, line); + else getline(fin, line); + L=atoi(line.c_str()); + if (compress_type) getline(fin_gz, line); + else getline(fin, line); + for (i=0;i<line.size();i++) + if (line[i]==' '||line[i]=='\t') break; + if (!(compress_type?fin_gz.good():fin.good())) break; + PDB_lines.push_back(tmp_str_vec); + for (i=0;i<L;i++) + { + if (compress_type) getline(fin_gz, line); + else getline(fin, line); + i8_stream<<"ATOM "<<setw(4)<<i+1<<" CA " + <<AAmap(line[0])<<" "<<setw(4)<<i+1<<" " + <<line.substr(2,8)<<line.substr(11,8)<<line.substr(20,8); + line=i8_stream.str(); + i8_stream.str(string()); + PDB_lines.back().push_back(line); + } + } + } + else if (infmt_opt==3) // PDBx/mmCIF format + { + bool loop_ = false; // not reading following content + map<string,int> _atom_site; + int atom_site_pos; + vector<string> line_vec; + string alt_id="."; // alternative location indicator + string asym_id="."; // this is similar to chainID, except that + // chainID is char while asym_id is a string + // with possibly multiple char + string prev_asym_id=""; + string AA=""; // residue name + string atom=""; + string resi=""; + string model_index=""; // the same as model_idx but type is string + stringstream i8_stream; + while (compress_type?fin_gz.good():fin.good()) + { + if (compress_type) getline(fin_gz, line); + else getline(fin, line); + if (line.size()==0) continue; + if (loop_) loop_ = (line.size()>=2)?(line.compare(0,2,"# ")):(line.compare(0,1,"#")); + if (!loop_) + { + if (line.compare(0,5,"loop_")) continue; + while(1) + { + if (compress_type) + { + if (fin_gz.good()) getline(fin_gz, line); + else PrintErrorAndQuit("ERROR! Unexpected end of "+filename); + } + else + { + if (fin.good()) getline(fin, line); + else PrintErrorAndQuit("ERROR! Unexpected end of "+filename); + } + if (line.size()) break; + } + if (line.compare(0,11,"_atom_site.")) continue; + + loop_=true; + _atom_site.clear(); + atom_site_pos=0; + _atom_site[Trim(line.substr(11))]=atom_site_pos; + + while(1) + { + if (compress_type) getline(fin_gz, line); + else getline(fin, line); + if (line.size()==0) continue; + if (line.compare(0,11,"_atom_site.")) break; + _atom_site[Trim(line.substr(11))]=++atom_site_pos; + } + + + if (_atom_site.count("group_PDB")* + _atom_site.count("label_atom_id")* + _atom_site.count("label_comp_id")* + (_atom_site.count("auth_asym_id")+ + _atom_site.count("label_asym_id"))* + (_atom_site.count("auth_seq_id")+ + _atom_site.count("label_seq_id"))* + _atom_site.count("Cartn_x")* + _atom_site.count("Cartn_y")* + _atom_site.count("Cartn_z")==0) + { + loop_ = false; + cerr<<"Warning! Missing one of the following _atom_site data items: group_PDB, label_atom_id, label_comp_id, auth_asym_id/label_asym_id, auth_seq_id/label_seq_id, Cartn_x, Cartn_y, Cartn_z"<<endl; + continue; + } + } + + line_vec.clear(); + split(line,line_vec); + if ((line_vec[_atom_site["group_PDB"]]!="ATOM" && + line_vec[_atom_site["group_PDB"]]!="HETATM") || + (line_vec[_atom_site["group_PDB"]]=="HETATM" && + (het_opt==0 || + (het_opt==2 && line_vec[_atom_site["label_comp_id"]]!="MSE"))) + ) continue; + + alt_id="."; + if (_atom_site.count("label_alt_id")) // in 39.4 % of entries + alt_id=line_vec[_atom_site["label_alt_id"]]; + if (alt_id!="." && alt_id!="A") continue; + + atom=line_vec[_atom_site["label_atom_id"]]; + if (atom[0]=='"') atom=atom.substr(1); + if (atom.size() && atom[atom.size()-1]=='"') + atom=atom.substr(0,atom.size()-1); + if (atom.size()==0) continue; + if (atom.size()==1) atom=" "+atom+" "; + else if (atom.size()==2) atom=" "+atom+" "; // wrong for sidechain H + else if (atom.size()==3) atom=" "+atom; + else if (atom.size()>=5) continue; + + AA=line_vec[_atom_site["label_comp_id"]]; // residue name + if (AA.size()==1) AA=" "+AA; + else if (AA.size()==2) AA=" " +AA; + else if (AA.size()>=4) continue; + + if (_atom_site.count("auth_asym_id")) + asym_id=line_vec[_atom_site["auth_asym_id"]]; + else asym_id=line_vec[_atom_site["label_asym_id"]]; + if (asym_id==".") asym_id=" "; + + if (_atom_site.count("pdbx_PDB_model_num") && + model_index!=line_vec[_atom_site["pdbx_PDB_model_num"]]) + { + model_index=line_vec[_atom_site["pdbx_PDB_model_num"]]; + if (PDB_lines.size() && ter_opt>=1) break; + if (PDB_lines.size()==0 || split_opt>=1) + { + PDB_lines.push_back(tmp_str_vec); + prev_asym_id=asym_id; + } + } + + if (prev_asym_id!=asym_id) + { + if (prev_asym_id!="" && ter_opt>=2) break; + if (split_opt>=2) PDB_lines.push_back(tmp_str_vec); + } + if (prev_asym_id!=asym_id) prev_asym_id=asym_id; + + if (_atom_site.count("auth_seq_id")) + resi=line_vec[_atom_site["auth_seq_id"]]; + else resi=line_vec[_atom_site["label_seq_id"]]; + if (_atom_site.count("pdbx_PDB_ins_code") && + line_vec[_atom_site["pdbx_PDB_ins_code"]]!="?") + resi+=line_vec[_atom_site["pdbx_PDB_ins_code"]][0]; + else resi+=" "; + + i++; + i8_stream<<"ATOM " + <<setw(5)<<i<<" "<<atom<<" "<<AA<<setw(2)<<asym_id.substr(0,2) + <<setw(5)<<resi.substr(0,5)<<" " + <<setw(8)<<line_vec[_atom_site["Cartn_x"]].substr(0,8) + <<setw(8)<<line_vec[_atom_site["Cartn_y"]].substr(0,8) + <<setw(8)<<line_vec[_atom_site["Cartn_z"]].substr(0,8); + PDB_lines.back().push_back(i8_stream.str()); + i8_stream.str(string()); + } + _atom_site.clear(); + line_vec.clear(); + alt_id.clear(); + asym_id.clear(); + AA.clear(); + } + + if (compress_type) fin_gz.close(); + else fin.close(); + line.clear(); + return PDB_lines.size(); +} + +void output_dock(const vector<string>&chain_list, const int ter_opt, + const int split_opt, const int infmt_opt, const string atom_opt, + const int mirror_opt, double **ut_mat, const string&fname_super) +{ + size_t i; + int chain_i,a; + string name; + int chainnum; + double x[3]; // before transform + double x1[3]; // after transform + string line; + vector<vector<string> >PDB_lines; + int m=0; + double t[3]; + double u[3][3]; + int ui,uj; + stringstream buf; + string filename; + int het_opt=1; + for (i=0;i<chain_list.size();i++) + { + name=chain_list[i]; + chainnum=get_full_PDB_lines(name, PDB_lines, + ter_opt, infmt_opt, split_opt, het_opt); + if (!chainnum) continue; + clear_full_PDB_lines(PDB_lines, atom_opt); // clear chains with <3 residue + for (chain_i=0;chain_i<chainnum;chain_i++) + { + if (PDB_lines[chain_i].size()<3) continue; + buf<<fname_super<<'.'<<m<<".pdb"; + filename=buf.str(); + buf.str(string()); + for (ui=0;ui<3;ui++) for (uj=0;uj<3;uj++) u[ui][uj]=ut_mat[m][ui*3+uj]; + for (uj=0;uj<3;uj++) t[uj]=ut_mat[m][9+uj]; + for (a=0;a<PDB_lines[chain_i].size();a++) + { + line=PDB_lines[chain_i][a]; + x[0]=atof(line.substr(30,8).c_str()); + x[1]=atof(line.substr(38,8).c_str()); + x[2]=atof(line.substr(46,8).c_str()); + if (mirror_opt) x[2]=-x[2]; + transform(t, u, x, x1); + buf<<line.substr(0,30)<<setiosflags(ios::fixed) + <<setprecision(3) + <<setw(8)<<x1[0]<<setw(8)<<x1[1]<<setw(8)<<x1[2] + <<line.substr(54)<<'\n'; + } + buf<<"TER"<<endl; + ofstream fp; + fp.open(filename.c_str()); + fp<<buf.str(); + fp.close(); + buf.str(string()); + PDB_lines[chain_i].clear(); + m++; + } // chain_i + name.clear(); + PDB_lines.clear(); + } // i + vector<vector<string> >().swap(PDB_lines); + line.clear(); +} + +void parse_chain_list(const vector<string>&chain_list, + vector<vector<vector<double> > >&a_vec, vector<vector<char> >&seq_vec, + vector<vector<char> >&sec_vec, vector<int>&mol_vec, vector<int>&len_vec, + vector<string>&chainID_list, const int ter_opt, const int split_opt, + const string mol_opt, const int infmt_opt, const string atom_opt, + const int mirror_opt, const int het_opt, int &len_aa, int &len_na, + const int o_opt, vector<string>&resi_vec) +{ + size_t i; + int chain_i,r; + string name; + int chainnum; + double **xa; + int len; + char *seq,*sec; + + vector<vector<string> >PDB_lines; + vector<double> tmp_atom_array(3,0); + vector<vector<double> > tmp_chain_array; + vector<char>tmp_seq_array; + vector<char>tmp_sec_array; + //vector<string> resi_vec; + int read_resi=2; + + for (i=0;i<chain_list.size();i++) + { + name=chain_list[i]; + chainnum=get_PDB_lines(name, PDB_lines, chainID_list, + mol_vec, ter_opt, infmt_opt, atom_opt, split_opt, het_opt); + if (!chainnum) + { + cerr<<"Warning! Cannot parse file: "<<name + <<". Chain number 0."<<endl; + continue; + } + for (chain_i=0;chain_i<chainnum;chain_i++) + { + len=PDB_lines[chain_i].size(); + if (!len) + { + cerr<<"Warning! Cannot parse file: "<<name + <<". Chain length 0."<<endl; + continue; + } + else if (len<3) + { + cerr<<"Sequence is too short <3!: "<<name<<endl; + continue; + } + NewArray(&xa, len, 3); + seq = new char[len + 1]; + sec = new char[len + 1]; + len = read_PDB(PDB_lines[chain_i], xa, seq, resi_vec, read_resi); + if (mirror_opt) for (r=0;r<len;r++) xa[r][2]=-xa[r][2]; + if (mol_vec[chain_i]>0 || mol_opt=="RNA") + make_sec(seq, xa, len, sec,atom_opt); + else make_sec(xa, len, sec); // secondary structure assignment + + /* store in vector */ + tmp_chain_array.assign(len,tmp_atom_array); + vector<char>tmp_seq_array(len+1,0); + vector<char>tmp_sec_array(len+1,0); + for (r=0;r<len;r++) + { + tmp_chain_array[r][0]=xa[r][0]; + tmp_chain_array[r][1]=xa[r][1]; + tmp_chain_array[r][2]=xa[r][2]; + tmp_seq_array[r]=seq[r]; + tmp_sec_array[r]=sec[r]; + } + a_vec.push_back(tmp_chain_array); + seq_vec.push_back(tmp_seq_array); + sec_vec.push_back(tmp_sec_array); + len_vec.push_back(len); + + /* clean up */ + tmp_chain_array.clear(); + tmp_seq_array.clear(); + tmp_sec_array.clear(); + PDB_lines[chain_i].clear(); + DeleteArray(&xa, len); + delete [] seq; + delete [] sec; + } // chain_i + name.clear(); + PDB_lines.clear(); + mol_vec.clear(); + } // i + tmp_atom_array.clear(); + + if (mol_opt=="RNA") mol_vec.assign(a_vec.size(),1); + else if (mol_opt=="protein") mol_vec.assign(a_vec.size(),-1); + else + { + mol_vec.assign(a_vec.size(),0); + for (i=0;i<a_vec.size();i++) + { + for (r=0;r<len_vec[i];r++) + { + if (seq_vec[i][r]>='a' && seq_vec[i][r]<='z') mol_vec[i]++; + else mol_vec[i]--; + } + } + } + + len_aa=0; + len_na=0; + for (i=0;i<a_vec.size();i++) + { + if (mol_vec[i]>0) len_na+=len_vec[i]; + else len_aa+=len_vec[i]; + } +} + +int copy_chain_pair_data( + const vector<vector<vector<double> > >&xa_vec, + const vector<vector<vector<double> > >&ya_vec, + const vector<vector<char> >&seqx_vec, const vector<vector<char> >&seqy_vec, + const vector<vector<char> >&secx_vec, const vector<vector<char> >&secy_vec, + const vector<int> &mol_vec1, const vector<int> &mol_vec2, + const vector<int> &xlen_vec, const vector<int> &ylen_vec, + double **xa, double **ya, char *seqx, char *seqy, char *secx, char *secy, + int chain1_num, int chain2_num, + vector<vector<string> >&seqxA_mat, vector<vector<string> >&seqyA_mat, + int *assign1_list, int *assign2_list, vector<string>&sequence) +{ + int i,j,r; + for (i=0;i<sequence.size();i++) sequence[i].clear(); + sequence.clear(); + sequence.push_back(""); + sequence.push_back(""); + int mol_type=0; + int xlen=0; + int ylen=0; + for (i=0;i<chain1_num;i++) + { + j=assign1_list[i]; + if (j<0) continue; + for (r=0;r<xlen_vec[i];r++) + { + seqx[xlen]=seqx_vec[i][r]; + secx[xlen]=secx_vec[i][r]; + xa[xlen][0]= xa_vec[i][r][0]; + xa[xlen][1]= xa_vec[i][r][1]; + xa[xlen][2]= xa_vec[i][r][2]; + xlen++; + } + sequence[0]+=seqxA_mat[i][j]; + for (r=0;r<ylen_vec[j];r++) + { + seqy[ylen]=seqy_vec[j][r]; + secy[ylen]=secy_vec[j][r]; + ya[ylen][0]= ya_vec[j][r][0]; + ya[ylen][1]= ya_vec[j][r][1]; + ya[ylen][2]= ya_vec[j][r][2]; + ylen++; + } + sequence[1]+=seqyA_mat[i][j]; + mol_type+=mol_vec1[i]+mol_vec2[j]; + } + seqx[xlen]=0; + secx[xlen]=0; + seqy[ylen]=0; + secy[ylen]=0; + return mol_type; +} + +double MMalign_search( + const vector<vector<vector<double> > >&xa_vec, + const vector<vector<vector<double> > >&ya_vec, + const vector<vector<char> >&seqx_vec, const vector<vector<char> >&seqy_vec, + const vector<vector<char> >&secx_vec, const vector<vector<char> >&secy_vec, + const vector<int> &mol_vec1, const vector<int> &mol_vec2, + const vector<int> &xlen_vec, const vector<int> &ylen_vec, + double **xa, double **ya, char *seqx, char *seqy, char *secx, char *secy, + int len_aa, int len_na, int chain1_num, int chain2_num, double **TMave_mat, + vector<vector<string> >&seqxA_mat, vector<vector<string> >&seqyA_mat, + int *assign1_list, int *assign2_list, vector<string>&sequence, + double d0_scale, bool fast_opt, const int i_opt=3) +{ + double total_score=0; + int i,j; + int xlen=0; + int ylen=0; + for (i=0;i<chain1_num;i++) + { + if (assign1_list[i]<0) continue; + xlen+=xlen_vec[i]; + ylen+=ylen_vec[assign1_list[i]]; + } + if (xlen<=3 || ylen<=3) return total_score; + + seqx = new char[xlen+1]; + secx = new char[xlen+1]; + NewArray(&xa, xlen, 3); + seqy = new char[ylen+1]; + secy = new char[ylen+1]; + NewArray(&ya, ylen, 3); + + int mol_type=copy_chain_pair_data(xa_vec, ya_vec, seqx_vec, seqy_vec, + secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, + xa, ya, seqx, seqy, secx, secy, chain1_num, chain2_num, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, sequence); + + /* declare variable specific to this pair of TMalign */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + + double Lnorm_ass=len_aa+len_na; + + /* entry function for structure alignment */ + TMalign_main(xa, ya, seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + i_opt, false, true, false, fast_opt, mol_type, -1); + + /* clean up */ + delete [] seqx; + delete [] seqy; + delete [] secx; + delete [] secy; + DeleteArray(&xa,xlen); + DeleteArray(&ya,ylen); + + /* re-compute chain level alignment */ + for (i=0;i<chain1_num;i++) + { + xlen=xlen_vec[i]; + if (xlen<3) + { + for (j=0;j<chain2_num;j++) TMave_mat[i][j]=-1; + continue; + } + seqx = new char[xlen+1]; + secx = new char[xlen+1]; + NewArray(&xa, xlen, 3); + copy_chain_data(xa_vec[i],seqx_vec[i],secx_vec[i], + xlen,xa,seqx,secx); + + double **xt; + NewArray(&xt, xlen, 3); + do_rotation(xa, xt, xlen, t0, u0); + + for (j=0;j<chain2_num;j++) + { + if (mol_vec1[i]*mol_vec2[j]<0) //no protein-RNA alignment + { + TMave_mat[i][j]=-1; + continue; + } + + ylen=ylen_vec[j]; + if (ylen<3) + { + TMave_mat[i][j]=-1; + continue; + } + seqy = new char[ylen+1]; + secy = new char[ylen+1]; + NewArray(&ya, ylen, 3); + copy_chain_data(ya_vec[j],seqy_vec[j],secy_vec[j], + ylen,ya,seqy,secy); + + /* declare variable specific to this pair of TMalign */ + d0_out=5.0; + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + rmsd0 = 0.0; + Liden=0; + int *invmap = new int[ylen+1]; + + double Lnorm_ass=len_aa; + if (mol_vec1[i]+mol_vec2[j]>0) Lnorm_ass=len_na; + + /* entry function for structure alignment */ + se_main(xt, ya, seqx, seqy, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + 0, false, 2, false, mol_vec1[i]+mol_vec2[j], 1, invmap); + + /* print result */ + seqxA_mat[i][j]=seqxA; + seqyA_mat[i][j]=seqyA; + + TMave_mat[i][j]=TM4*Lnorm_ass; + if (assign1_list[i]==j) total_score+=TMave_mat[i][j]; + + /* clean up */ + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + + delete[]seqy; + delete[]secy; + DeleteArray(&ya,ylen); + delete[]invmap; + } + delete[]seqx; + delete[]secx; + DeleteArray(&xa,xlen); + DeleteArray(&xt,xlen); + } + return total_score; +} + +void MMalign_final( + const string xname, const string yname, + const vector<string> chainID_list1, const vector<string> chainID_list2, + string fname_super, string fname_lign, string fname_matrix, + const vector<vector<vector<double> > >&xa_vec, + const vector<vector<vector<double> > >&ya_vec, + const vector<vector<char> >&seqx_vec, const vector<vector<char> >&seqy_vec, + const vector<vector<char> >&secx_vec, const vector<vector<char> >&secy_vec, + const vector<int> &mol_vec1, const vector<int> &mol_vec2, + const vector<int> &xlen_vec, const vector<int> &ylen_vec, + double **xa, double **ya, char *seqx, char *seqy, char *secx, char *secy, + int len_aa, int len_na, int chain1_num, int chain2_num, + double **TMave_mat, + vector<vector<string> >&seqxA_mat, vector<vector<string> >&seqM_mat, + vector<vector<string> >&seqyA_mat, int *assign1_list, int *assign2_list, + vector<string>&sequence, const double d0_scale, const bool m_opt, + const int o_opt, const int outfmt_opt, const int ter_opt, + const int split_opt, const bool a_opt, const bool d_opt, + const bool fast_opt, const bool full_opt, const int mirror_opt, + const vector<string>&resi_vec1, const vector<string>&resi_vec2) +{ + int i,j; + int xlen=0; + int ylen=0; + for (i=0;i<chain1_num;i++) xlen+=xlen_vec[i]; + for (j=0;j<chain2_num;j++) ylen+=ylen_vec[j]; + if (xlen<=3 || ylen<=3) return; + + seqx = new char[xlen+1]; + secx = new char[xlen+1]; + NewArray(&xa, xlen, 3); + seqy = new char[ylen+1]; + secy = new char[ylen+1]; + NewArray(&ya, ylen, 3); + + int mol_type=copy_chain_pair_data(xa_vec, ya_vec, seqx_vec, seqy_vec, + secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, + xa, ya, seqx, seqy, secx, secy, chain1_num, chain2_num, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, sequence); + + /* declare variable specific to this pair of TMalign */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + + double Lnorm_ass=len_aa+len_na; + + /* entry function for structure alignment */ + TMalign_main(xa, ya, seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + 3, a_opt, false, d_opt, fast_opt, mol_type, -1); + + /* prepare full complex alignment */ + string chainID1=""; + string chainID2=""; + sequence.clear(); + sequence.push_back(""); // seqxA + sequence.push_back(""); // seqyA + sequence.push_back(""); // seqM + int aln_start=0; + int aln_end=0; + for (i=0;i<chain1_num;i++) + { + j=assign1_list[i]; + if (j<0) continue; + chainID1+=chainID_list1[i]; + chainID2+=chainID_list2[j]; + sequence[0]+=seqxA_mat[i][j]+'*'; + sequence[1]+=seqyA_mat[i][j]+'*'; + + aln_end+=seqxA_mat[i][j].size(); + seqM_mat[i][j]=seqM.substr(aln_start,aln_end-aln_start); + sequence[2]+=seqM_mat[i][j]+'*'; + aln_start=aln_end; + } + + /* prepare unaligned region */ + for (i=0;i<chain1_num;i++) + { + if (assign1_list[i]>=0) continue; + chainID1+=chainID_list1[i]; + chainID2+=':'; + string s(seqx_vec[i].begin(),seqx_vec[i].end()); + sequence[0]+=s.substr(0,xlen_vec[i])+'*'; + sequence[1]+=string(xlen_vec[i],'-')+'*'; + s.clear(); + sequence[2]+=string(xlen_vec[i],' ')+'*'; + } + for (j=0;j<chain2_num;j++) + { + if (assign2_list[j]>=0) continue; + chainID1+=':'; + chainID2+=chainID_list2[j]; + string s(seqy_vec[j].begin(),seqy_vec[j].end()); + sequence[0]+=string(ylen_vec[j],'-')+'*'; + sequence[1]+=s.substr(0,ylen_vec[j])+'*'; + s.clear(); + sequence[2]+=string(ylen_vec[j],' ')+'*'; + } + + /* print alignment */ + output_results(xname, yname, chainID1.c_str(), chainID2.c_str(), + xlen, ylen, t0, u0, TM1, TM2, TM3, TM4, TM5, rmsd0, d0_out, + sequence[2].c_str(), sequence[0].c_str(), sequence[1].c_str(), + Liden, n_ali8, L_ali, TM_ali, rmsd_ali, + TM_0, d0_0, d0A, d0B, 0, d0_scale, d0a, d0u, + (m_opt?fname_matrix:"").c_str(), outfmt_opt, ter_opt, true, + split_opt, o_opt, fname_super, + false, a_opt, false, d_opt, mirror_opt, resi_vec1, resi_vec2); + + /* clean up */ + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + delete [] seqx; + delete [] seqy; + delete [] secx; + delete [] secy; + DeleteArray(&xa,xlen); + DeleteArray(&ya,ylen); + sequence[0].clear(); + sequence[1].clear(); + sequence[2].clear(); + + if (!full_opt) return; + + cout<<"# End of alignment for full complex. The following blocks list alignments for individual chains."<<endl; + + /* re-compute chain level alignment */ + for (i=0;i<chain1_num;i++) + { + j=assign1_list[i]; + if (j<0) continue; + xlen=xlen_vec[i]; + seqx = new char[xlen+1]; + secx = new char[xlen+1]; + NewArray(&xa, xlen, 3); + copy_chain_data(xa_vec[i],seqx_vec[i],secx_vec[i], + xlen,xa,seqx,secx); + + double **xt; + NewArray(&xt, xlen, 3); + do_rotation(xa, xt, xlen, t0, u0); + + ylen=ylen_vec[j]; + if (ylen<3) + { + TMave_mat[i][j]=-1; + continue; + } + seqy = new char[ylen+1]; + secy = new char[ylen+1]; + NewArray(&ya, ylen, 3); + copy_chain_data(ya_vec[j],seqy_vec[j],secy_vec[j], + ylen,ya,seqy,secy); + + /* declare variable specific to this pair of TMalign */ + d0_out=5.0; + rmsd0 = 0.0; + Liden=0; + int *invmap = new int[ylen+1]; + seqM=""; + seqxA=""; + seqyA=""; + double Lnorm_ass=len_aa; + if (mol_vec1[i]+mol_vec2[j]>0) Lnorm_ass=len_na; + sequence[0]=seqxA_mat[i][j]; + sequence[1]=seqyA_mat[i][j]; + + /* entry function for structure alignment */ + se_main(xt, ya, seqx, seqy, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + 1, a_opt, 2, d_opt, mol_vec1[i]+mol_vec2[j], 1, invmap); + + //TM2=TM4*Lnorm_ass/xlen; + //TM1=TM4*Lnorm_ass/ylen; + //d0A=d0u; + //d0B=d0u; + + /* print result */ + output_results(xname, yname, + chainID_list1[i].c_str(), chainID_list2[j].c_str(), + xlen, ylen, t0, u0, TM1, TM2, TM3, TM4, TM5, rmsd0, d0_out, + seqM_mat[i][j].c_str(), seqxA_mat[i][j].c_str(), + seqyA_mat[i][j].c_str(), Liden, n_ali8, L_ali, TM_ali, rmsd_ali, + TM_0, d0_0, d0A, d0B, Lnorm_ass, d0_scale, d0a, d0u, + "", outfmt_opt, ter_opt, false, split_opt, 0, + "", false, a_opt, false, d_opt, 0, resi_vec1, resi_vec2); + + /* clean up */ + seqxA.clear(); + seqM.clear(); + seqyA.clear(); + sequence[0].clear(); + sequence[1].clear(); + delete[]seqy; + delete[]secy; + DeleteArray(&ya,ylen); + delete[]seqx; + delete[]secx; + DeleteArray(&xa,xlen); + DeleteArray(&xt,xlen); + delete[]invmap; + } + sequence.clear(); + return; +} + +void copy_chain_assign_data(int chain1_num, int chain2_num, + vector<string> &sequence, + vector<vector<string> >&seqxA_mat, vector<vector<string> >&seqyA_mat, + int *assign1_list, int *assign2_list, double **TMave_mat, + vector<vector<string> >&seqxA_tmp, vector<vector<string> >&seqyA_tmp, + int *assign1_tmp, int *assign2_tmp, double **TMave_tmp) +{ + int i,j; + for (i=0;i<sequence.size();i++) sequence[i].clear(); + sequence.clear(); + sequence.push_back(""); + sequence.push_back(""); + for (i=0;i<chain1_num;i++) assign1_tmp[i]=assign1_list[i]; + for (i=0;i<chain2_num;i++) assign2_tmp[i]=assign2_list[i]; + for (i=0;i<chain1_num;i++) + { + for (j=0;j<chain2_num;j++) + { + seqxA_tmp[i][j]=seqxA_mat[i][j]; + seqyA_tmp[i][j]=seqyA_mat[i][j]; + TMave_tmp[i][j]=TMave_mat[i][j]; + if (assign1_list[i]==j) + { + sequence[0]+=seqxA_mat[i][j]; + sequence[1]+=seqyA_mat[i][j]; + } + } + } + return; +} + +void MMalign_iter(double & max_total_score, const int max_iter, + const vector<vector<vector<double> > >&xa_vec, + const vector<vector<vector<double> > >&ya_vec, + const vector<vector<char> >&seqx_vec, const vector<vector<char> >&seqy_vec, + const vector<vector<char> >&secx_vec, const vector<vector<char> >&secy_vec, + const vector<int> &mol_vec1, const vector<int> &mol_vec2, + const vector<int> &xlen_vec, const vector<int> &ylen_vec, + double **xa, double **ya, char *seqx, char *seqy, char *secx, char *secy, + int len_aa, int len_na, int chain1_num, int chain2_num, double **TMave_mat, + vector<vector<string> >&seqxA_mat, vector<vector<string> >&seqyA_mat, + int *assign1_list, int *assign2_list, vector<string>&sequence, + double d0_scale, bool fast_opt) +{ + /* tmp assignment */ + double total_score; + int *assign1_tmp, *assign2_tmp; + assign1_tmp=new int[chain1_num]; + assign2_tmp=new int[chain2_num]; + double **TMave_tmp; + NewArray(&TMave_tmp,chain1_num,chain2_num); + vector<string> tmp_str_vec(chain2_num,""); + vector<vector<string> >seqxA_tmp(chain1_num,tmp_str_vec); + vector<vector<string> >seqyA_tmp(chain1_num,tmp_str_vec); + vector<string> sequence_tmp; + copy_chain_assign_data(chain1_num, chain2_num, sequence_tmp, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, TMave_mat, + seqxA_tmp, seqyA_tmp, assign1_tmp, assign2_tmp, TMave_tmp); + + for (int iter=0;iter<max_iter;iter++) + { + total_score=MMalign_search(xa_vec, ya_vec, seqx_vec, seqy_vec, + secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, + xa, ya, seqx, seqy, secx, secy, len_aa, len_na, + chain1_num, chain2_num, + TMave_tmp, seqxA_tmp, seqyA_tmp, assign1_tmp, assign2_tmp, + sequence, d0_scale, fast_opt); + total_score=enhanced_greedy_search(TMave_tmp, assign1_tmp, + assign2_tmp, chain1_num, chain2_num); + //if (total_score<=0) PrintErrorAndQuit("ERROR! No assignable chain"); + if (total_score<=max_total_score) break; + max_total_score=total_score; + copy_chain_assign_data(chain1_num, chain2_num, sequence, + seqxA_tmp, seqyA_tmp, assign1_tmp, assign2_tmp, TMave_tmp, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, TMave_mat); + } + + /* clean up everything */ + delete [] assign1_tmp; + delete [] assign2_tmp; + DeleteArray(&TMave_tmp,chain1_num); + vector<string>().swap(tmp_str_vec); + vector<vector<string> >().swap(seqxA_tmp); + vector<vector<string> >().swap(seqyA_tmp); +} + + +/* Input: vectors x, y, rotation matrix t, u, scale factor d02, and gap_open + * Output: j2i[1:len2] \in {1:len1} U {-1} + * path[0:len1, 0:len2]=1,2,3, from diagonal, horizontal, vertical */ +void NWDP_TM_dimer(bool **path, double **val, double **x, double **y, + int len1, int len2, bool **mask, + double t[3], double u[3][3], double d02, double gap_open, int j2i[]) +{ + int i, j; + double h, v, d; + + //initialization + for(i=0; i<=len1; i++) + { + //val[i][0]=0; + val[i][0]=i*gap_open; + path[i][0]=false; //not from diagonal + } + + for(j=0; j<=len2; j++) + { + //val[0][j]=0; + val[0][j]=j*gap_open; + path[0][j]=false; //not from diagonal + j2i[j]=-1; //all are not aligned, only use j2i[1:len2] + } + double xx[3], dij; + + + //decide matrix and path + for(i=1; i<=len1; i++) + { + transform(t, u, &x[i-1][0], xx); + for(j=1; j<=len2; j++) + { + d=FLT_MIN; + if (mask[i][j]) + { + dij=dist(xx, &y[j-1][0]); + d=val[i-1][j-1] + 1.0/(1+dij/d02); + } + + //symbol insertion in horizontal (= a gap in vertical) + h=val[i-1][j]; + if(path[i-1][j]) h += gap_open; //aligned in last position + + //symbol insertion in vertical + v=val[i][j-1]; + if(path[i][j-1]) v += gap_open; //aligned in last position + + + if(d>=h && d>=v) + { + path[i][j]=true; //from diagonal + val[i][j]=d; + } + else + { + path[i][j]=false; //from horizontal + if(v>=h) val[i][j]=v; + else val[i][j]=h; + } + } //for i + } //for j + + //trace back to extract the alignment + i=len1; + j=len2; + while(i>0 && j>0) + { + if(path[i][j]) //from diagonal + { + j2i[j-1]=i-1; + i--; + j--; + } + else + { + h=val[i-1][j]; + if(path[i-1][j]) h +=gap_open; + + v=val[i][j-1]; + if(path[i][j-1]) v +=gap_open; + + if(v>=h) j--; + else i--; + } + } +} + +/* +ss + * Input: secondary structure secx, secy, and gap_open + * Output: j2i[1:len2] \in {1:len1} U {-1} + * path[0:len1, 0:len2]=1,2,3, from diagonal, horizontal, vertical */ +void NWDP_TM_dimer(bool **path, double **val, const char *secx, const char *secy, + const int len1, const int len2, bool **mask, const double gap_open, int j2i[]) +{ + + int i, j; + double h, v, d; + + //initialization + for(i=0; i<=len1; i++) + { + //val[i][0]=0; + val[i][0]=i*gap_open; + path[i][0]=false; //not from diagonal + } + + for(j=0; j<=len2; j++) + { + //val[0][j]=0; + val[0][j]=j*gap_open; + path[0][j]=false; //not from diagonal + j2i[j]=-1; //all are not aligned, only use j2i[1:len2] + } + + //decide matrix and path + for(i=1; i<=len1; i++) + { + for(j=1; j<=len2; j++) + { + d=FLT_MIN; + if (mask[i][j]) + d=val[i-1][j-1] + 1.0*(secx[i-1]==secy[j-1]); + + //symbol insertion in horizontal (= a gap in vertical) + h=val[i-1][j]; + if(path[i-1][j]) h += gap_open; //aligned in last position + + //symbol insertion in vertical + v=val[i][j-1]; + if(path[i][j-1]) v += gap_open; //aligned in last position + + if(d>=h && d>=v) + { + path[i][j]=true; //from diagonal + val[i][j]=d; + } + else + { + path[i][j]=false; //from horizontal + if(v>=h) val[i][j]=v; + else val[i][j]=h; + } + } //for i + } //for j + + //trace back to extract the alignment + i=len1; + j=len2; + while(i>0 && j>0) + { + if(path[i][j]) //from diagonal + { + j2i[j-1]=i-1; + i--; + j--; + } + else + { + h=val[i-1][j]; + if(path[i-1][j]) h +=gap_open; + + v=val[i][j-1]; + if(path[i][j-1]) v +=gap_open; + + if(v>=h) j--; + else i--; + } + } +} + +//heuristic run of dynamic programing iteratively to find the best alignment +//input: initial rotation matrix t, u +// vectors x and y, d0 +//output: best alignment that maximizes the TMscore, will be stored in invmap +double DP_iter_dimer(double **r1, double **r2, double **xtm, double **ytm, + double **xt, bool **path, double **val, double **x, double **y, + int xlen, int ylen, bool **mask, double t[3], double u[3][3], int invmap0[], + int g1, int g2, int iteration_max, double local_d0_search, + double D0_MIN, double Lnorm, double d0, double score_d8) +{ + double gap_open[2]={-0.6, 0}; + double rmsd; + int *invmap=new int[ylen+1]; + + int iteration, i, j, k; + double tmscore, tmscore_max, tmscore_old=0; + int score_sum_method=8, simplify_step=40; + tmscore_max=-1; + + //double d01=d0+1.5; + double d02=d0*d0; + for(int g=g1; g<g2; g++) + { + for(iteration=0; iteration<iteration_max; iteration++) + { + NWDP_TM_dimer(path, val, x, y, xlen, ylen, mask, + t, u, d02, gap_open[g], invmap); + + k=0; + for(j=0; j<ylen; j++) + { + i=invmap[j]; + + if(i>=0) //aligned + { + xtm[k][0]=x[i][0]; + xtm[k][1]=x[i][1]; + xtm[k][2]=x[i][2]; + + ytm[k][0]=y[j][0]; + ytm[k][1]=y[j][1]; + ytm[k][2]=y[j][2]; + k++; + } + } + + tmscore = TMscore8_search(r1, r2, xtm, ytm, xt, k, t, u, + simplify_step, score_sum_method, &rmsd, local_d0_search, + Lnorm, score_d8, d0); + + + if(tmscore>tmscore_max) + { + tmscore_max=tmscore; + for(i=0; i<ylen; i++) invmap0[i]=invmap[i]; + } + + if(iteration>0) + { + if(fabs(tmscore_old-tmscore)<0.000001) break; + } + tmscore_old=tmscore; + }// for iteration + + }//for gapopen + + + delete []invmap; + return tmscore_max; +} + +void get_initial_ss_dimer(bool **path, double **val, const char *secx, + const char *secy, int xlen, int ylen, bool **mask, int *y2x) +{ + double gap_open=-1.0; + NWDP_TM_dimer(path, val, secx, secy, xlen, ylen, mask, gap_open, y2x); +} + +bool get_initial5_dimer( double **r1, double **r2, double **xtm, double **ytm, + bool **path, double **val, double **x, double **y, int xlen, int ylen, + bool **mask, int *y2x, + double d0, double d0_search, const bool fast_opt, const double D0_MIN) +{ + double GL, rmsd; + double t[3]; + double u[3][3]; + + double d01 = d0 + 1.5; + if (d01 < D0_MIN) d01 = D0_MIN; + double d02 = d01*d01; + + double GLmax = 0; + int aL = getmin(xlen, ylen); + int *invmap = new int[ylen + 1]; + + // jump on sequence1--------------> + int n_jump1 = 0; + if (xlen > 250) + n_jump1 = 45; + else if (xlen > 200) + n_jump1 = 35; + else if (xlen > 150) + n_jump1 = 25; + else + n_jump1 = 15; + if (n_jump1 > (xlen / 3)) + n_jump1 = xlen / 3; + + // jump on sequence2--------------> + int n_jump2 = 0; + if (ylen > 250) + n_jump2 = 45; + else if (ylen > 200) + n_jump2 = 35; + else if (ylen > 150) + n_jump2 = 25; + else + n_jump2 = 15; + if (n_jump2 > (ylen / 3)) + n_jump2 = ylen / 3; + + // fragment to superimpose--------------> + int n_frag[2] = { 20, 100 }; + if (n_frag[0] > (aL / 3)) + n_frag[0] = aL / 3; + if (n_frag[1] > (aL / 2)) + n_frag[1] = aL / 2; + + // start superimpose search--------------> + if (fast_opt) + { + n_jump1*=5; + n_jump2*=5; + } + bool flag = false; + for (int i_frag = 0; i_frag < 2; i_frag++) + { + int m1 = xlen - n_frag[i_frag] + 1; + int m2 = ylen - n_frag[i_frag] + 1; + + for (int i = 0; i<m1; i = i + n_jump1) //index starts from 0, different from FORTRAN + { + for (int j = 0; j<m2; j = j + n_jump2) + { + for (int k = 0; k<n_frag[i_frag]; k++) //fragment in y + { + r1[k][0] = x[k + i][0]; + r1[k][1] = x[k + i][1]; + r1[k][2] = x[k + i][2]; + + r2[k][0] = y[k + j][0]; + r2[k][1] = y[k + j][1]; + r2[k][2] = y[k + j][2]; + } + + // superpose the two structures and rotate it + Kabsch(r1, r2, n_frag[i_frag], 1, &rmsd, t, u); + + double gap_open = 0.0; + NWDP_TM_dimer(path, val, x, y, xlen, ylen, mask, + t, u, d02, gap_open, invmap); + GL = get_score_fast(r1, r2, xtm, ytm, x, y, xlen, ylen, + invmap, d0, d0_search, t, u); + if (GL>GLmax) + { + GLmax = GL; + for (int ii = 0; ii<ylen; ii++) y2x[ii] = invmap[ii]; + flag = true; + } + } + } + } + + delete[] invmap; + return flag; +} + +void get_initial_ssplus_dimer(double **r1, double **r2, double **score, + bool **path, double **val, const char *secx, const char *secy, + double **x, double **y, int xlen, int ylen, bool **mask, + int *y2x0, int *y2x, const double D0_MIN, double d0) +{ + //create score matrix for DP + score_matrix_rmsd_sec(r1, r2, score, secx, secy, x, y, xlen, ylen, + y2x0, D0_MIN,d0); + + int i,j; + for (i=0;i<xlen+1;i++) for (j=0;j<ylen+1;j++) score[i][j]=FLT_MIN; + + double gap_open=-1.0; + NWDP_TM(score, path, val, xlen, ylen, gap_open, y2x); +} + +/* Entry function for TM-align. Return TM-score calculation status: + * 0 - full TM-score calculation + * 1 - terminated due to exception + * 2-7 - pre-terminated due to low TM-score */ +int TMalign_dimer_main(double **xa, double **ya, + const char *seqx, const char *seqy, const char *secx, const char *secy, + double t0[3], double u0[3][3], + double &TM1, double &TM2, double &TM3, double &TM4, double &TM5, + double &d0_0, double &TM_0, + double &d0A, double &d0B, double &d0u, double &d0a, double &d0_out, + string &seqM, string &seqxA, string &seqyA, + double &rmsd0, int &L_ali, double &Liden, + double &TM_ali, double &rmsd_ali, int &n_ali, int &n_ali8, + const int xlen, const int ylen, + bool **mask, + const vector<string> sequence, const double Lnorm_ass, + const double d0_scale, const int i_opt, const int a_opt, + const bool u_opt, const bool d_opt, const bool fast_opt, + const int mol_type, const double TMcut=-1) +{ + double D0_MIN; //for d0 + double Lnorm; //normalization length + double score_d8,d0,d0_search,dcu0;//for TMscore search + double t[3], u[3][3]; //Kabsch translation vector and rotation matrix + double **score; // Input score table for dynamic programming + bool **path; // for dynamic programming + double **val; // for dynamic programming + double **xtm, **ytm; // for TMscore search engine + double **xt; //for saving the superposed version of r_1 or xtm + double **r1, **r2; // for Kabsch rotation + + /***********************/ + /* allocate memory */ + /***********************/ + int minlen = min(xlen, ylen); + NewArray(&score, xlen+1, ylen+1); + NewArray(&path, xlen+1, ylen+1); + NewArray(&val, xlen+1, ylen+1); + NewArray(&xtm, minlen, 3); + NewArray(&ytm, minlen, 3); + NewArray(&xt, xlen, 3); + NewArray(&r1, minlen, 3); + NewArray(&r2, minlen, 3); + + /***********************/ + /* parameter set */ + /***********************/ + parameter_set4search(xlen, ylen, D0_MIN, Lnorm, + score_d8, d0, d0_search, dcu0); + int simplify_step = 40; //for simplified search engine + int score_sum_method = 8; //for scoring method, whether only sum over pairs with dis<score_d8 + + int i; + int *invmap0 = new int[ylen+1]; + int *invmap = new int[ylen+1]; + double TM, TMmax=-1; + for(i=0; i<ylen; i++) invmap0[i]=-1; + + double ddcc=0.4; + if (Lnorm <= 40) ddcc=0.1; //Lnorm was setted in parameter_set4search + double local_d0_search = d0_search; + + //************************************************// + // get initial alignment from user's input: // + // Stick to the initial alignment // + //************************************************// + bool bAlignStick = false; + if (i_opt==3)// if input has set parameter for "-I" + { + // In the original code, this loop starts from 1, which is + // incorrect. Fortran starts from 1 but C++ should starts from 0. + for (int j = 0; j < ylen; j++)// Set aligned position to be "-1" + invmap[j] = -1; + + int i1 = -1;// in C version, index starts from zero, not from one + int i2 = -1; + int L1 = sequence[0].size(); + int L2 = sequence[1].size(); + int L = min(L1, L2);// Get positions for aligned residues + for (int kk1 = 0; kk1 < L; kk1++) + { + if (sequence[0][kk1] != '-') i1++; + if (sequence[1][kk1] != '-') + { + i2++; + if (i2 >= ylen || i1 >= xlen) kk1 = L; + else if (sequence[0][kk1] != '-') invmap[i2] = i1; + } + } + + //--------------- 2. Align proteins from original alignment + double prevD0_MIN = D0_MIN;// stored for later use + int prevLnorm = Lnorm; + double prevd0 = d0; + TM_ali = standard_TMscore(r1, r2, xtm, ytm, xt, xa, ya, xlen, ylen, + invmap, L_ali, rmsd_ali, D0_MIN, Lnorm, d0, d0_search, score_d8, + t, u, mol_type); + D0_MIN = prevD0_MIN; + Lnorm = prevLnorm; + d0 = prevd0; + TM = detailed_search_standard(r1, r2, xtm, ytm, xt, xa, ya, xlen, ylen, + invmap, t, u, 40, 8, local_d0_search, true, Lnorm, score_d8, d0); + if (TM > TMmax) + { + TMmax = TM; + for (i = 0; i<ylen; i++) invmap0[i] = invmap[i]; + } + bAlignStick = true; + } + + /******************************************************/ + /* get initial alignment with gapless threading */ + /******************************************************/ + if (!bAlignStick) + { + get_initial(r1, r2, xtm, ytm, xa, ya, xlen, ylen, invmap0, d0, + d0_search, fast_opt, t, u); + TM = detailed_search(r1, r2, xtm, ytm, xt, xa, ya, xlen, ylen, invmap0, + t, u, simplify_step, score_sum_method, local_d0_search, Lnorm, + score_d8, d0); + if (TM>TMmax) TMmax = TM; + if (TMcut>0) copy_t_u(t, u, t0, u0); + //run dynamic programing iteratively to find the best alignment + TM = DP_iter_dimer(r1, r2, xtm, ytm, xt, path, val, xa, ya, xlen, ylen, + mask, t, u, invmap, 0, 2, (fast_opt)?2:30, + local_d0_search, D0_MIN, Lnorm, d0, score_d8); + if (TM>TMmax) + { + TMmax = TM; + for (int i = 0; i<ylen; i++) invmap0[i] = invmap[i]; + if (TMcut>0) copy_t_u(t, u, t0, u0); + } + + if (TMcut>0) // pre-terminate if TM-score is too low + { + double TMtmp=approx_TM(xlen, ylen, a_opt, + xa, ya, t0, u0, invmap0, mol_type); + + if (TMtmp<0.5*TMcut) + { + TM1=TM2=TM3=TM4=TM5=TMtmp; + clean_up_after_approx_TM(invmap0, invmap, score, path, val, + xtm, ytm, xt, r1, r2, xlen, minlen); + return 2; + } + } + + /************************************************************/ + /* get initial alignment based on secondary structure */ + /************************************************************/ + get_initial_ss_dimer(path, val, secx, secy, xlen, ylen, mask, invmap); + TM = detailed_search(r1, r2, xtm, ytm, xt, xa, ya, xlen, ylen, invmap, + t, u, simplify_step, score_sum_method, local_d0_search, Lnorm, + score_d8, d0); + if (TM>TMmax) + { + TMmax = TM; + for (int i = 0; i<ylen; i++) invmap0[i] = invmap[i]; + if (TMcut>0) copy_t_u(t, u, t0, u0); + } + if (TM > TMmax*0.2) + { + TM = DP_iter_dimer(r1, r2, xtm, ytm, xt, path, val, xa, ya, + xlen, ylen, mask, t, u, invmap, 0, 2, + (fast_opt)?2:30, local_d0_search, D0_MIN, Lnorm, d0, score_d8); + if (TM>TMmax) + { + TMmax = TM; + for (int i = 0; i<ylen; i++) invmap0[i] = invmap[i]; + if (TMcut>0) copy_t_u(t, u, t0, u0); + } + } + + if (TMcut>0) // pre-terminate if TM-score is too low + { + double TMtmp=approx_TM(xlen, ylen, a_opt, + xa, ya, t0, u0, invmap0, mol_type); + + if (TMtmp<0.52*TMcut) + { + TM1=TM2=TM3=TM4=TM5=TMtmp; + clean_up_after_approx_TM(invmap0, invmap, score, path, val, + xtm, ytm, xt, r1, r2, xlen, minlen); + return 3; + } + } + + /************************************************************/ + /* get initial alignment based on local superposition */ + /************************************************************/ + //=initial5 in original TM-align + if (get_initial5_dimer( r1, r2, xtm, ytm, path, val, xa, ya, + xlen, ylen, mask, invmap, d0, d0_search, fast_opt, D0_MIN)) + { + TM = detailed_search(r1, r2, xtm, ytm, xt, xa, ya, xlen, ylen, + invmap, t, u, simplify_step, score_sum_method, + local_d0_search, Lnorm, score_d8, d0); + if (TM>TMmax) + { + TMmax = TM; + for (int i = 0; i<ylen; i++) invmap0[i] = invmap[i]; + if (TMcut>0) copy_t_u(t, u, t0, u0); + } + if (TM > TMmax*ddcc) + { + TM = DP_iter_dimer(r1, r2, xtm, ytm, xt, path, val, xa, ya, + xlen, ylen, mask, t, u, invmap, 0, 2, 2, + local_d0_search, D0_MIN, Lnorm, d0, score_d8); + if (TM>TMmax) + { + TMmax = TM; + for (int i = 0; i<ylen; i++) invmap0[i] = invmap[i]; + if (TMcut>0) copy_t_u(t, u, t0, u0); + } + } + } + else + cerr << "\n\nWarning: initial alignment from local superposition fail!\n\n" << endl; + + if (TMcut>0) // pre-terminate if TM-score is too low + { + double TMtmp=approx_TM(xlen, ylen, a_opt, + xa, ya, t0, u0, invmap0, mol_type); + + if (TMtmp<0.54*TMcut) + { + TM1=TM2=TM3=TM4=TM5=TMtmp; + clean_up_after_approx_TM(invmap0, invmap, score, path, val, + xtm, ytm, xt, r1, r2, xlen, minlen); + return 4; + } + } + + /********************************************************************/ + /* get initial alignment by local superposition+secondary structure */ + /********************************************************************/ + //=initial3 in original TM-align + get_initial_ssplus_dimer(r1, r2, score, path, val, secx, secy, xa, ya, + xlen, ylen, mask, invmap0, invmap, D0_MIN, d0); + TM = detailed_search(r1, r2, xtm, ytm, xt, xa, ya, xlen, ylen, invmap, + t, u, simplify_step, score_sum_method, local_d0_search, Lnorm, + score_d8, d0); + if (TM>TMmax) + { + TMmax = TM; + for (i = 0; i<ylen; i++) invmap0[i] = invmap[i]; + if (TMcut>0) copy_t_u(t, u, t0, u0); + } + if (TM > TMmax*ddcc) + { + TM = DP_iter_dimer(r1, r2, xtm, ytm, xt, path, val, xa, ya, + xlen, ylen, mask, t, u, invmap, 0, 2, + (fast_opt)?2:30, local_d0_search, D0_MIN, Lnorm, d0, score_d8); + if (TM>TMmax) + { + TMmax = TM; + for (i = 0; i<ylen; i++) invmap0[i] = invmap[i]; + if (TMcut>0) copy_t_u(t, u, t0, u0); + } + } + + if (TMcut>0) // pre-terminate if TM-score is too low + { + double TMtmp=approx_TM(xlen, ylen, a_opt, + xa, ya, t0, u0, invmap0, mol_type); + + if (TMtmp<0.56*TMcut) + { + TM1=TM2=TM3=TM4=TM5=TMtmp; + clean_up_after_approx_TM(invmap0, invmap, score, path, val, + xtm, ytm, xt, r1, r2, xlen, minlen); + return 5; + } + } + + /*******************************************************************/ + /* get initial alignment based on fragment gapless threading */ + /*******************************************************************/ + //=initial4 in original TM-align + get_initial_fgt(r1, r2, xtm, ytm, xa, ya, xlen, ylen, + invmap, d0, d0_search, dcu0, fast_opt, t, u); + TM = detailed_search(r1, r2, xtm, ytm, xt, xa, ya, xlen, ylen, invmap, + t, u, simplify_step, score_sum_method, local_d0_search, Lnorm, + score_d8, d0); + if (TM>TMmax) + { + TMmax = TM; + for (i = 0; i<ylen; i++) invmap0[i] = invmap[i]; + if (TMcut>0) copy_t_u(t, u, t0, u0); + } + if (TM > TMmax*ddcc) + { + TM = DP_iter_dimer(r1, r2, xtm, ytm, xt, path, val, xa, ya, + xlen, ylen, mask, t, u, invmap, 1, 2, 2, + local_d0_search, D0_MIN, Lnorm, d0, score_d8); + if (TM>TMmax) + { + TMmax = TM; + for (i = 0; i<ylen; i++) invmap0[i] = invmap[i]; + if (TMcut>0) copy_t_u(t, u, t0, u0); + } + } + + if (TMcut>0) // pre-terminate if TM-score is too low + { + double TMtmp=approx_TM(xlen, ylen, a_opt, + xa, ya, t0, u0, invmap0, mol_type); + + if (TMtmp<0.58*TMcut) + { + TM1=TM2=TM3=TM4=TM5=TMtmp; + clean_up_after_approx_TM(invmap0, invmap, score, path, val, + xtm, ytm, xt, r1, r2, xlen, minlen); + return 6; + } + } + + //************************************************// + // get initial alignment from user's input: // + //************************************************// + if (i_opt==1)// if input has set parameter for "-i" + { + for (int j = 0; j < ylen; j++)// Set aligned position to be "-1" + invmap[j] = -1; + + int i1 = -1;// in C version, index starts from zero, not from one + int i2 = -1; + int L1 = sequence[0].size(); + int L2 = sequence[1].size(); + int L = min(L1, L2);// Get positions for aligned residues + for (int kk1 = 0; kk1 < L; kk1++) + { + if (sequence[0][kk1] != '-') + i1++; + if (sequence[1][kk1] != '-') + { + i2++; + if (i2 >= ylen || i1 >= xlen) kk1 = L; + else if (sequence[0][kk1] != '-') invmap[i2] = i1; + } + } + + //--------------- 2. Align proteins from original alignment + double prevD0_MIN = D0_MIN;// stored for later use + int prevLnorm = Lnorm; + double prevd0 = d0; + TM_ali = standard_TMscore(r1, r2, xtm, ytm, xt, xa, ya, + xlen, ylen, invmap, L_ali, rmsd_ali, D0_MIN, Lnorm, d0, + d0_search, score_d8, t, u, mol_type); + D0_MIN = prevD0_MIN; + Lnorm = prevLnorm; + d0 = prevd0; + + TM = detailed_search_standard(r1, r2, xtm, ytm, xt, xa, ya, + xlen, ylen, invmap, t, u, 40, 8, local_d0_search, true, Lnorm, + score_d8, d0); + if (TM > TMmax) + { + TMmax = TM; + for (i = 0; i<ylen; i++) invmap0[i] = invmap[i]; + } + // Different from get_initial, get_initial_ss and get_initial_ssplus + TM = DP_iter_dimer(r1, r2, xtm, ytm, xt, path, val, xa, ya, + xlen, ylen, mask, t, u, invmap, 0, 2, + (fast_opt)?2:30, local_d0_search, D0_MIN, Lnorm, d0, score_d8); + if (TM>TMmax) + { + TMmax = TM; + for (i = 0; i<ylen; i++) invmap0[i] = invmap[i]; + } + } + } + + + + //*******************************************************************// + // The alignment will not be changed any more in the following // + //*******************************************************************// + //check if the initial alignment is generated appropriately + bool flag=false; + for(i=0; i<ylen; i++) + { + if(invmap0[i]>=0) + { + flag=true; + break; + } + } + if(!flag) + { + cout << "There is no alignment between the two structures! " + << "Program stop with no result!" << endl; + TM1=TM2=TM3=TM4=TM5=0; + return 1; + } + + /* last TM-score pre-termination */ + if (TMcut>0) + { + double TMtmp=approx_TM(xlen, ylen, a_opt, + xa, ya, t0, u0, invmap0, mol_type); + + if (TMtmp<0.6*TMcut) + { + TM1=TM2=TM3=TM4=TM5=TMtmp; + clean_up_after_approx_TM(invmap0, invmap, score, path, val, + xtm, ytm, xt, r1, r2, xlen, minlen); + return 7; + } + } + + //********************************************************************// + // Detailed TMscore search engine --> prepare for final TMscore // + //********************************************************************// + //run detailed TMscore search engine for the best alignment, and + //extract the best rotation matrix (t, u) for the best alignment + simplify_step=1; + if (fast_opt) simplify_step=40; + score_sum_method=8; + TM = detailed_search_standard(r1, r2, xtm, ytm, xt, xa, ya, xlen, ylen, + invmap0, t, u, simplify_step, score_sum_method, local_d0_search, + false, Lnorm, score_d8, d0); + + //select pairs with dis<d8 for final TMscore computation and output alignment + int k=0; + int *m1, *m2; + double d; + m1=new int[xlen]; //alignd index in x + m2=new int[ylen]; //alignd index in y + do_rotation(xa, xt, xlen, t, u); + k=0; + for(int j=0; j<ylen; j++) + { + i=invmap0[j]; + if(i>=0)//aligned + { + n_ali++; + d=sqrt(dist(&xt[i][0], &ya[j][0])); + if (d <= score_d8 || (i_opt == 3)) + { + m1[k]=i; + m2[k]=j; + + xtm[k][0]=xa[i][0]; + xtm[k][1]=xa[i][1]; + xtm[k][2]=xa[i][2]; + + ytm[k][0]=ya[j][0]; + ytm[k][1]=ya[j][1]; + ytm[k][2]=ya[j][2]; + + r1[k][0] = xt[i][0]; + r1[k][1] = xt[i][1]; + r1[k][2] = xt[i][2]; + r2[k][0] = ya[j][0]; + r2[k][1] = ya[j][1]; + r2[k][2] = ya[j][2]; + + k++; + } + } + } + n_ali8=k; + + Kabsch(r1, r2, n_ali8, 0, &rmsd0, t, u);// rmsd0 is used for final output, only recalculate rmsd0, not t & u + rmsd0 = sqrt(rmsd0 / n_ali8); + + + //****************************************// + // Final TMscore // + // Please set parameters for output // + //****************************************// + double rmsd; + simplify_step=1; + score_sum_method=0; + double Lnorm_0=ylen; + + + //normalized by length of structure A + parameter_set4final(Lnorm_0, D0_MIN, Lnorm, d0, d0_search, mol_type); + d0A=d0; + d0_0=d0A; + local_d0_search = d0_search; + TM1 = TMscore8_search(r1, r2, xtm, ytm, xt, n_ali8, t0, u0, simplify_step, + score_sum_method, &rmsd, local_d0_search, Lnorm, score_d8, d0); + TM_0 = TM1; + + //normalized by length of structure B + parameter_set4final(xlen+0.0, D0_MIN, Lnorm, d0, d0_search, mol_type); + d0B=d0; + local_d0_search = d0_search; + TM2 = TMscore8_search(r1, r2, xtm, ytm, xt, n_ali8, t, u, simplify_step, + score_sum_method, &rmsd, local_d0_search, Lnorm, score_d8, d0); + + double Lnorm_d0; + if (a_opt>0) + { + //normalized by average length of structures A, B + Lnorm_0=(xlen+ylen)*0.5; + parameter_set4final(Lnorm_0, D0_MIN, Lnorm, d0, d0_search, mol_type); + d0a=d0; + d0_0=d0a; + local_d0_search = d0_search; + + TM3 = TMscore8_search(r1, r2, xtm, ytm, xt, n_ali8, t0, u0, + simplify_step, score_sum_method, &rmsd, local_d0_search, Lnorm, + score_d8, d0); + TM_0=TM3; + } + if (u_opt) + { + //normalized by user assigned length + parameter_set4final(Lnorm_ass, D0_MIN, Lnorm, + d0, d0_search, mol_type); + d0u=d0; + d0_0=d0u; + Lnorm_0=Lnorm_ass; + local_d0_search = d0_search; + TM4 = TMscore8_search(r1, r2, xtm, ytm, xt, n_ali8, t0, u0, + simplify_step, score_sum_method, &rmsd, local_d0_search, Lnorm, + score_d8, d0); + TM_0=TM4; + } + if (d_opt) + { + //scaled by user assigned d0 + parameter_set4scale(ylen, d0_scale, Lnorm, d0, d0_search); + d0_out=d0_scale; + d0_0=d0_scale; + //Lnorm_0=ylen; + Lnorm_d0=Lnorm_0; + local_d0_search = d0_search; + TM5 = TMscore8_search(r1, r2, xtm, ytm, xt, n_ali8, t0, u0, + simplify_step, score_sum_method, &rmsd, local_d0_search, Lnorm, + score_d8, d0); + TM_0=TM5; + } + + /* derive alignment from superposition */ + int ali_len=xlen+ylen; //maximum length of alignment + seqxA.assign(ali_len,'-'); + seqM.assign( ali_len,' '); + seqyA.assign(ali_len,'-'); + + //do_rotation(xa, xt, xlen, t, u); + do_rotation(xa, xt, xlen, t0, u0); + + int kk=0, i_old=0, j_old=0; + d=0; + for(int k=0; k<n_ali8; k++) + { + for(int i=i_old; i<m1[k]; i++) + { + //align x to gap + seqxA[kk]=seqx[i]; + seqyA[kk]='-'; + seqM[kk]=' '; + kk++; + } + + for(int j=j_old; j<m2[k]; j++) + { + //align y to gap + seqxA[kk]='-'; + seqyA[kk]=seqy[j]; + seqM[kk]=' '; + kk++; + } + + seqxA[kk]=seqx[m1[k]]; + seqyA[kk]=seqy[m2[k]]; + Liden+=(seqxA[kk]==seqyA[kk]); + d=sqrt(dist(&xt[m1[k]][0], &ya[m2[k]][0])); + if(d<d0_out) seqM[kk]=':'; + else seqM[kk]='.'; + kk++; + i_old=m1[k]+1; + j_old=m2[k]+1; + } + + //tail + for(int i=i_old; i<xlen; i++) + { + //align x to gap + seqxA[kk]=seqx[i]; + seqyA[kk]='-'; + seqM[kk]=' '; + kk++; + } + for(int j=j_old; j<ylen; j++) + { + //align y to gap + seqxA[kk]='-'; + seqyA[kk]=seqy[j]; + seqM[kk]=' '; + kk++; + } + seqxA=seqxA.substr(0,kk); + seqyA=seqyA.substr(0,kk); + seqM =seqM.substr(0,kk); + + /* free memory */ + clean_up_after_approx_TM(invmap0, invmap, score, path, val, + xtm, ytm, xt, r1, r2, xlen, minlen); + delete [] m1; + delete [] m2; + return 0; // zero for no exception +} + +void MMalign_dimer(double & total_score, + const vector<vector<vector<double> > >&xa_vec, + const vector<vector<vector<double> > >&ya_vec, + const vector<vector<char> >&seqx_vec, const vector<vector<char> >&seqy_vec, + const vector<vector<char> >&secx_vec, const vector<vector<char> >&secy_vec, + const vector<int> &mol_vec1, const vector<int> &mol_vec2, + const vector<int> &xlen_vec, const vector<int> &ylen_vec, + double **xa, double **ya, char *seqx, char *seqy, char *secx, char *secy, + int len_aa, int len_na, int chain1_num, int chain2_num, double **TMave_mat, + vector<vector<string> >&seqxA_mat, vector<vector<string> >&seqyA_mat, + int *assign1_list, int *assign2_list, vector<string>&sequence, + double d0_scale, bool fast_opt) +{ + int i,j; + int xlen=0; + int ylen=0; + vector<int> xlen_dimer; + vector<int> ylen_dimer; + for (i=0;i<chain1_num;i++) + { + j=assign1_list[i]; + if (j<0) continue; + xlen+=xlen_vec[i]; + ylen+=ylen_vec[j]; + xlen_dimer.push_back(xlen_vec[i]); + ylen_dimer.push_back(ylen_vec[j]); + } + if (xlen<=3 || ylen<=3) return; + + bool **mask; // mask out inter-chain region + NewArray(&mask, xlen+1, ylen+1); + for (i=0;i<xlen+1;i++) for (j=0;j<ylen+1;j++) mask[i][j]=false; + for (i=0;i<xlen_dimer[0]+1;i++) mask[i][0]=true; + for (j=0;j<ylen_dimer[0]+1;j++) mask[0][j]=true; + int c,prev_xlen,prev_ylen; + prev_xlen=1; + prev_ylen=1; + for (c=0;c<xlen_dimer.size();c++) + { + for (i=prev_xlen;i<prev_xlen+xlen_dimer[c];i++) + for (j=prev_ylen;j<prev_ylen+ylen_dimer[c];j++) mask[i][j]=true; + prev_xlen+=xlen_dimer[c]; + prev_ylen+=ylen_dimer[c]; + } + vector<int>().swap(xlen_dimer); + vector<int>().swap(ylen_dimer); + + seqx = new char[xlen+1]; + secx = new char[xlen+1]; + NewArray(&xa, xlen, 3); + seqy = new char[ylen+1]; + secy = new char[ylen+1]; + NewArray(&ya, ylen, 3); + + int mol_type=copy_chain_pair_data(xa_vec, ya_vec, seqx_vec, seqy_vec, + secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, + xa, ya, seqx, seqy, secx, secy, chain1_num, chain2_num, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, sequence); + + /* declare variable specific to this pair of TMalign */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + + double Lnorm_ass=len_aa+len_na; + + TMalign_dimer_main(xa, ya, seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, mask, sequence, Lnorm_ass, d0_scale, + 1, false, true, false, fast_opt, mol_type, -1); + + /* clean up TM-align */ + delete [] seqx; + delete [] seqy; + delete [] secx; + delete [] secy; + DeleteArray(&xa,xlen); + DeleteArray(&ya,ylen); + DeleteArray(&mask,xlen+1); + + /* re-compute chain level alignment */ + total_score=0; + for (i=0;i<chain1_num;i++) + { + xlen=xlen_vec[i]; + if (xlen<3) + { + for (j=0;j<chain2_num;j++) TMave_mat[i][j]=-1; + continue; + } + seqx = new char[xlen+1]; + secx = new char[xlen+1]; + NewArray(&xa, xlen, 3); + copy_chain_data(xa_vec[i],seqx_vec[i],secx_vec[i], + xlen,xa,seqx,secx); + + double **xt; + NewArray(&xt, xlen, 3); + do_rotation(xa, xt, xlen, t0, u0); + + for (j=0;j<chain2_num;j++) + { + if (mol_vec1[i]*mol_vec2[j]<0) //no protein-RNA alignment + { + TMave_mat[i][j]=-1; + continue; + } + + ylen=ylen_vec[j]; + if (ylen<3) + { + TMave_mat[i][j]=-1; + continue; + } + seqy = new char[ylen+1]; + secy = new char[ylen+1]; + NewArray(&ya, ylen, 3); + copy_chain_data(ya_vec[j],seqy_vec[j],secy_vec[j], + ylen,ya,seqy,secy); + + /* declare variable specific to this pair of TMalign */ + d0_out=5.0; + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + rmsd0 = 0.0; + Liden=0; + int *invmap = new int[ylen+1]; + + double Lnorm_ass=len_aa; + if (mol_vec1[i]+mol_vec2[j]>0) Lnorm_ass=len_na; + + /* entry function for structure alignment */ + se_main(xt, ya, seqx, seqy, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + 0, false, 2, false, mol_vec1[i]+mol_vec2[j], 1, invmap); + + /* print result */ + seqxA_mat[i][j]=seqxA; + seqyA_mat[i][j]=seqyA; + + TMave_mat[i][j]=TM4*Lnorm_ass; + if (assign1_list[i]==j) + { + if (TM4<=0) assign1_list[i]=assign2_list[j]=-1; + else total_score+=TMave_mat[i][j]; + } + + /* clean up */ + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + + delete[]seqy; + delete[]secy; + DeleteArray(&ya,ylen); + delete[]invmap; + } + delete[]seqx; + delete[]secx; + DeleteArray(&xa,xlen); + DeleteArray(&xt,xlen); + } + return; +} + +void MMalign_cross(double & max_total_score, const int max_iter, + const vector<vector<vector<double> > >&xa_vec, + const vector<vector<vector<double> > >&ya_vec, + const vector<vector<char> >&seqx_vec, const vector<vector<char> >&seqy_vec, + const vector<vector<char> >&secx_vec, const vector<vector<char> >&secy_vec, + const vector<int> &mol_vec1, const vector<int> &mol_vec2, + const vector<int> &xlen_vec, const vector<int> &ylen_vec, + double **xa, double **ya, char *seqx, char *seqy, char *secx, char *secy, + int len_aa, int len_na, int chain1_num, int chain2_num, double **TMave_mat, + vector<vector<string> >&seqxA_mat, vector<vector<string> >&seqyA_mat, + int *assign1_list, int *assign2_list, vector<string>&sequence, + double d0_scale, bool fast_opt) +{ + /* tmp assignment */ + int *assign1_tmp, *assign2_tmp; + assign1_tmp=new int[chain1_num]; + assign2_tmp=new int[chain2_num]; + double **TMave_tmp; + NewArray(&TMave_tmp,chain1_num,chain2_num); + vector<string> tmp_str_vec(chain2_num,""); + vector<vector<string> >seqxA_tmp(chain1_num,tmp_str_vec); + vector<vector<string> >seqyA_tmp(chain1_num,tmp_str_vec); + vector<string> sequence_tmp; + copy_chain_assign_data(chain1_num, chain2_num, sequence_tmp, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, TMave_mat, + seqxA_tmp, seqyA_tmp, assign1_tmp, assign2_tmp, TMave_tmp); + + double total_score=MMalign_search(xa_vec, ya_vec, seqx_vec, seqy_vec, + secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, + xa, ya, seqx, seqy, secx, secy, len_aa, len_na, chain1_num, chain2_num, + TMave_tmp, seqxA_tmp, seqyA_tmp, assign1_tmp, assign2_tmp, sequence_tmp, + d0_scale, fast_opt, 1); + if (total_score>max_total_score) + { + copy_chain_assign_data(chain1_num, chain2_num, sequence, + seqxA_tmp, seqyA_tmp, assign1_tmp, assign2_tmp, TMave_tmp, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, TMave_mat); + max_total_score=total_score; + } + + if (max_iter) MMalign_iter( + max_total_score, max_iter, xa_vec, ya_vec, seqx_vec, seqy_vec, + secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, + xa, ya, seqx, seqy, secx, secy, len_aa, len_na, chain1_num, chain2_num, + TMave_mat, seqxA_mat, seqyA_mat, assign1_list, assign2_list, sequence, + d0_scale, fast_opt); + + /* clean up everything */ + delete [] assign1_tmp; + delete [] assign2_tmp; + DeleteArray(&TMave_tmp,chain1_num); + vector<string>().swap(tmp_str_vec); + vector<vector<string> >().swap(seqxA_tmp); + vector<vector<string> >().swap(seqyA_tmp); + vector<string>().swap(sequence_tmp); + return; +} + +/* return the number of chains that are trimmed */ +int trimComplex(vector<vector<vector<double> > >&a_trim_vec, + vector<vector<char> >&seq_trim_vec, vector<vector<char> >&sec_trim_vec, + vector<int>&len_trim_vec, + const vector<vector<vector<double> > >&a_vec, + const vector<vector<char> >&seq_vec, const vector<vector<char> >&sec_vec, + const vector<int> &len_vec, const vector<int> &mol_vec, + const int Lchain_aa_max, const int Lchain_na_max) +{ + int trim_chain_count=0; + int chain_num=a_vec.size(); + int i,j; + int r1,r2; + double dinter; + double dinter_min; + vector<pair<double,int> >dinter_vec; + vector<bool> include_vec; + vector<char> seq_empty; + vector<vector<double> > a_empty; + vector<double> xcoor(3,0); + vector<double> ycoor(3,0); + int xlen,ylen; + int Lchain_max; + double expand=2; + for (i=0;i<chain_num;i++) + { + xlen=len_vec[i]; + if (mol_vec[i]>0) Lchain_max=Lchain_na_max*expand; + else Lchain_max=Lchain_aa_max*expand; + if (Lchain_max<3) Lchain_max=3; + if (xlen<=Lchain_max || xlen<=3) + { + a_trim_vec.push_back(a_vec[i]); + seq_trim_vec.push_back(seq_vec[i]); + sec_trim_vec.push_back(sec_vec[i]); + len_trim_vec.push_back(xlen); + continue; + } + trim_chain_count++; + for (r1=0;r1<xlen;r1++) + { + xcoor[0]=a_vec[i][r1][0]; + xcoor[1]=a_vec[i][r1][1]; + xcoor[2]=a_vec[i][r1][2]; + dinter_min=FLT_MAX; + for (j=0;j<chain_num;j++) + { + if (i==j) continue; + ylen=len_vec[j]; + for (r2=0;r2<ylen;r2++) + { + ycoor[0]=a_vec[j][r2][0]; + ycoor[1]=a_vec[j][r2][1]; + ycoor[2]=a_vec[j][r2][2]; + dinter=(xcoor[0]-ycoor[0])*(xcoor[0]-ycoor[0])+ + (xcoor[1]-ycoor[1])*(xcoor[1]-ycoor[1])+ + (xcoor[2]-ycoor[2])*(xcoor[2]-ycoor[2]); + if (dinter<dinter_min) dinter_min=dinter; + } + } + dinter_vec.push_back(make_pair(dinter,r1)); + } + sort(dinter_vec.begin(),dinter_vec.end()); + include_vec.assign(xlen,false); + for (r1=0;r1<Lchain_max;r1++) + include_vec[dinter_vec[r1].second]=true; + dinter_vec.clear(); + + a_trim_vec.push_back(a_empty); + seq_trim_vec.push_back(seq_empty); + sec_trim_vec.push_back(seq_empty); + len_trim_vec.push_back(Lchain_max); + for (r1=0;r1<xlen;r1++) + { + if (include_vec[r1]==false) continue; + a_trim_vec[i].push_back(a_vec[i][r1]); + seq_trim_vec[i].push_back(seq_vec[i][r1]); + sec_trim_vec[i].push_back(sec_vec[i][r1]); + } + include_vec.clear(); + } + vector<pair<double,int> >().swap(dinter_vec); + vector<bool>().swap(include_vec); + vector<double> ().swap(xcoor); + vector<double> ().swap(ycoor); + return trim_chain_count; +} + +void writeTrimComplex(vector<vector<vector<double> > >&a_trim_vec, + vector<vector<char> >&seq_trim_vec, vector<int>&len_trim_vec, + vector<string>&chainID_list, vector<int>&mol_vec, + const string &atom_opt, string filename) +{ + int c,r; + int a=0; + string chainID; + string atom; + ofstream fp(filename.c_str()); + for (c=0;c<chainID_list.size();c++) + { + chainID=chainID_list[c]; + if (chainID.size()==1) chainID=" "+chainID; + else if (chainID.size()>2) chainID=chainID.substr(chainID.size()-2,2); + if (chainID[0]==':') chainID=" "+chainID.substr(1); + atom=atom_opt; + if (atom_opt=="auto") + { + if (mol_vec[c]>0) atom=" C3'"; + else atom=" CA "; + } + + for (r=0;r<len_trim_vec[c];r++) + fp<<"ATOM "<<resetiosflags(ios::right)<<setw(5)<<++a<<' ' + <<atom<<' '<<AAmap(seq_trim_vec[c][r])<<chainID + <<setw(4)<<r+1<<" " + <<setiosflags(ios::fixed)<<setprecision(3) + <<setw(8)<<a_trim_vec[c][r][0] + <<setw(8)<<a_trim_vec[c][r][1] + <<setw(8)<<a_trim_vec[c][r][2]<<endl; + } + fp.close(); + atom.clear(); + chainID.clear(); + return; +} + +void output_dock_rotation_matrix(const char* fname_matrix, + const vector<string>&xname_vec, const vector<string>&yname_vec, + double ** ut_mat, int *assign1_list) +{ + fstream fout; + fout.open(fname_matrix, ios::out | ios::trunc); + if (fout)// succeed + { + int i,k; + for (i=0;i<xname_vec.size();i++) + { + if (assign1_list[i]<0) continue; + fout << "------ The rotation matrix to rotate " + <<xname_vec[i]<<" to "<<yname_vec[i]<<" ------\n" + << "m t[m] u[m][0] u[m][1] u[m][2]\n"; + for (k = 0; k < 3; k++) + fout<<k<<setiosflags(ios::fixed)<<setprecision(10) + <<' '<<setw(18)<<ut_mat[i][9+k] + <<' '<<setw(14)<<ut_mat[i][3*k+0] + <<' '<<setw(14)<<ut_mat[i][3*k+1] + <<' '<<setw(14)<<ut_mat[i][3*k+2]<<'\n'; + } + fout << "\nCode for rotating Structure 1 from (x,y,z) to (X,Y,Z):\n" + "for(i=0; i<L; i++)\n" + "{\n" + " X[i] = t[0] + u[0][0]*x[i] + u[0][1]*y[i] + u[0][2]*z[i];\n" + " Y[i] = t[1] + u[1][0]*x[i] + u[1][1]*y[i] + u[1][2]*z[i];\n" + " Z[i] = t[2] + u[2][0]*x[i] + u[2][1]*y[i] + u[2][2]*z[i];\n" + "}"<<endl; + fout.close(); + } + else + cout << "Open file to output rotation matrix fail.\n"; +} diff --git a/modules/bindings/src/tmalign/NW.h b/modules/bindings/src/USalign/NW.h similarity index 80% rename from modules/bindings/src/tmalign/NW.h rename to modules/bindings/src/USalign/NW.h index 4c9984853687c47fdf093f246d27ffe1c711416e..66e7e94f79280a7b45832005e89aeb05cb89d3d8 100644 --- a/modules/bindings/src/tmalign/NW.h +++ b/modules/bindings/src/USalign/NW.h @@ -259,6 +259,100 @@ void NWDP_SE(bool **path, double **val, double **x, double **y, } } +void NWDP_SE(bool **path, double **val, double **x, double **y, + int len1, int len2, double d02, double gap_open, int j2i[], + const int hinge) +{ + if (hinge==0) + { + NWDP_SE(path, val, x, y, len1, len2, d02, gap_open, j2i); + return; + } + int i, j; + double h, v, d; + + int L=(len2>len1)?len2:len1; + int int_min=L*(gap_open-1); + + for (i=0; i<=len1; i++) + { + for (j=0; j<=len2; j++) + { + val[i][j]=0; + path[i][j]=false; + } + } + + /* fill in old j2i */ + int k=0; + for (j=0; j<len2; j++) + { + i=j2i[j]; + if (i<0) continue; + path[i+1][j+1]=true; + val[i+1][j+1]=0; + } + + double dij; + + //decide matrix and path + for(i=1; i<=len1; i++) + { + for(j=1; j<=len2; j++) + { + dij=0; + if (path[i][j]==false) dij=dist(&x[i-1][0], &y[j-1][0]); + d=val[i-1][j-1] + 1.0/(1+dij/d02); + + //symbol insertion in horizontal (= a gap in vertical) + h=val[i-1][j]; + if(path[i-1][j]) h += gap_open; //aligned in last position + + //symbol insertion in vertical + v=val[i][j-1]; + if(path[i][j-1]) v += gap_open; //aligned in last position + + + if(d>=h && d>=v && val[i][j]==0) + { + path[i][j]=true; //from diagonal + val[i][j]=d; + } + else + { + path[i][j]=false; //from horizontal + if(v>=h) val[i][j]=v; + else val[i][j]=h; + } + } //for i + } //for j + + //trace back to extract the alignment + for (j=0;j<=len2;j++) j2i[j]=-1; + i=len1; + j=len2; + while(i>0 && j>0) + { + if(path[i][j]) //from diagonal + { + j2i[j-1]=i-1; + i--; + j--; + } + else + { + h=val[i-1][j]; + if(path[i-1][j]) h +=gap_open; + + v=val[i][j-1]; + if(path[i][j-1]) v +=gap_open; + + if(v>=h) j--; + else i--; + } + } +} + /* +ss * Input: secondary structure secx, secy, and gap_open * Output: j2i[1:len2] \in {1:len1} U {-1} diff --git a/modules/bindings/src/tmalign/NWalign.cpp b/modules/bindings/src/USalign/NWalign.cpp similarity index 100% rename from modules/bindings/src/tmalign/NWalign.cpp rename to modules/bindings/src/USalign/NWalign.cpp diff --git a/modules/bindings/src/tmalign/NWalign.h b/modules/bindings/src/USalign/NWalign.h similarity index 72% rename from modules/bindings/src/tmalign/NWalign.h rename to modules/bindings/src/USalign/NWalign.h index 2c7e36a1112f8c3a4a7cbb9d813e40e39698ae6e..7d6856b98db338b5aa24977baea7585df0006f8e 100644 --- a/modules/bindings/src/tmalign/NWalign.h +++ b/modules/bindings/src/USalign/NWalign.h @@ -502,7 +502,7 @@ void output_NWalign_results( printf(">%s%s\tL=%d\tseqID=%.3f\n", yname.c_str(), chainID2, ylen, Liden/ylen); printf("%s\n", seqyA); - printf("# Lali=%d\tseqID_ali=%.3f\n", L_ali, Liden/L_ali); + printf("#score=%d\tLali=%d\tseqID_ali=%.3f\n", aln_score, L_ali, Liden/L_ali); printf("$$$$\n"); } else if (outfmt_opt==2) @@ -515,4 +515,200 @@ void output_NWalign_results( cout << endl; } +/* extract pairwise sequence alignment from residue index vectors, + * assuming that "sequence" contains two empty strings. + * return length of alignment, including gap. */ +int extract_aln_from_resi(vector<string> &sequence, char *seqx, char *seqy, + const vector<string> resi_vec1, const vector<string> resi_vec2, + const int byresi_opt) +{ + sequence.clear(); + sequence.push_back(""); + sequence.push_back(""); + + int i1=0; // positions in resi_vec1 + int i2=0; // positions in resi_vec2 + int xlen=resi_vec1.size(); + int ylen=resi_vec2.size(); + if (byresi_opt==4 || byresi_opt==5) // global or glocal sequence alignment + { + int *invmap; + int glocal=0; + if (byresi_opt==5) glocal=2; + int mol_type=0; + for (i1=0;i1<xlen;i1++) + if ('a'<seqx[i1] && seqx[i1]<'z') mol_type++; + else mol_type--; + for (i2=0;i2<ylen;i2++) + if ('a'<seqx[i2] && seqx[i2]<'z') mol_type++; + else mol_type--; + NWalign_main(seqx, seqy, xlen, ylen, sequence[0],sequence[1], + mol_type, invmap, 0, glocal); + } + + + map<string,string> chainID_map1; + map<string,string> chainID_map2; + if (byresi_opt==3) + { + vector<string> chainID_vec; + string chainID; + stringstream ss; + int i; + for (i=0;i<xlen;i++) + { + chainID=resi_vec1[i].substr(5); + if (!chainID_vec.size()|| chainID_vec.back()!=chainID) + { + chainID_vec.push_back(chainID); + ss<<chainID_vec.size(); + chainID_map1[chainID]=ss.str(); + ss.str(""); + } + } + chainID_vec.clear(); + for (i=0;i<ylen;i++) + { + chainID=resi_vec2[i].substr(5); + if (!chainID_vec.size()|| chainID_vec.back()!=chainID) + { + chainID_vec.push_back(chainID); + ss<<chainID_vec.size(); + chainID_map2[chainID]=ss.str(); + ss.str(""); + } + } + vector<string>().swap(chainID_vec); + } + string chainID1=""; + string chainID2=""; + string chainID1_prev=""; + string chainID2_prev=""; + while(i1<xlen && i2<ylen) + { + if (byresi_opt==2) + { + chainID1=resi_vec1[i1].substr(5); + chainID2=resi_vec2[i2].substr(5); + } + else if (byresi_opt==3) + { + chainID1=chainID_map1[resi_vec1[i1].substr(5)]; + chainID2=chainID_map2[resi_vec2[i2].substr(5)]; + } + + if (chainID1==chainID2) + { + if (atoi(resi_vec1[i1].substr(0,4).c_str())< + atoi(resi_vec2[i2].substr(0,4).c_str())) + { + sequence[0]+=seqx[i1++]; + sequence[1]+='-'; + } + else if (atoi(resi_vec1[i1].substr(0,4).c_str())> + atoi(resi_vec2[i2].substr(0,4).c_str())) + { + sequence[0]+='-'; + sequence[1]+=seqy[i2++]; + } + else + { + sequence[0]+=seqx[i1++]; + sequence[1]+=seqy[i2++]; + } + chainID1_prev=chainID1; + chainID2_prev=chainID2; + } + else + { + if (chainID1_prev==chainID1 && chainID2_prev!=chainID2) + { + sequence[0]+=seqx[i1++]; + sequence[1]+='-'; + chainID1_prev=chainID1; + } + else if (chainID1_prev!=chainID1 && chainID2_prev==chainID2) + { + sequence[0]+='-'; + sequence[1]+=seqy[i2++]; + chainID2_prev=chainID2; + } + else + { + sequence[0]+=seqx[i1++]; + sequence[1]+=seqy[i2++]; + chainID1_prev=chainID1; + chainID2_prev=chainID2; + } + } + + } + map<string,string>().swap(chainID_map1); + map<string,string>().swap(chainID_map2); + chainID1.clear(); + chainID2.clear(); + chainID1_prev.clear(); + chainID2_prev.clear(); + return sequence[0].size(); +} + +/* extract pairwise sequence alignment from residue index vectors, + * return length of alignment, including gap. */ +int extract_aln_from_resi(vector<string> &sequence, char *seqx, char *seqy, + const vector<string> resi_vec1, const vector<string> resi_vec2, + const vector<int> xlen_vec, const vector<int> ylen_vec, + const int chain_i, const int chain_j) +{ + sequence.clear(); + sequence.push_back(""); + sequence.push_back(""); + + int i1=0; // positions in resi_vec1 + int i2=0; // positions in resi_vec2 + int xlen=xlen_vec[chain_i]; + int ylen=ylen_vec[chain_j]; + int i,j; + for (i=0;i<chain_i;i++) i1+=xlen_vec[i]; + for (j=0;j<chain_j;j++) i2+=ylen_vec[j]; + + i=j=0; + while(i<xlen && j<ylen) + { + if (atoi(resi_vec1[i+i1].substr(0,4).c_str())< + atoi(resi_vec2[j+i2].substr(0,4).c_str())) + { + sequence[0]+=seqx[i++]; + sequence[1]+='-'; + } + else if (atoi(resi_vec1[i+i1].substr(0,4).c_str())> + atoi(resi_vec2[j+i2].substr(0,4).c_str())) + { + sequence[0]+='-'; + sequence[1]+=seqy[j++]; + } + else + { + sequence[0]+=seqx[i++]; + sequence[1]+=seqy[j++]; + } + } + if (i<xlen && j==ylen) + { + for (i;i<xlen;i++) + { + sequence[0]+=seqx[i]; + sequence[1]+='-'; + } + } + else if (i==xlen && j<ylen) + { + for (j;j<ylen;j++) + { + sequence[0]+='-'; + sequence[1]+=seqy[j]; + } + } + return sequence[0].size(); +} + #endif diff --git a/modules/bindings/src/USalign/OST_INFO b/modules/bindings/src/USalign/OST_INFO new file mode 100644 index 0000000000000000000000000000000000000000..42124da831fd2d3412bc252f2f34eee5ad17027b --- /dev/null +++ b/modules/bindings/src/USalign/OST_INFO @@ -0,0 +1,6 @@ +Source code has been cloned May 4 2023 from: + +https://github.com/pylelab/USalign + +last commit: +8d968e0111ca275958f209d76b1cd10598864a34 diff --git a/modules/bindings/src/tmalign/PDB1.pdb b/modules/bindings/src/USalign/PDB1.pdb similarity index 100% rename from modules/bindings/src/tmalign/PDB1.pdb rename to modules/bindings/src/USalign/PDB1.pdb diff --git a/modules/bindings/src/tmalign/PDB2.pdb b/modules/bindings/src/USalign/PDB2.pdb similarity index 100% rename from modules/bindings/src/tmalign/PDB2.pdb rename to modules/bindings/src/USalign/PDB2.pdb diff --git a/modules/bindings/src/USalign/SOIalign.h b/modules/bindings/src/USalign/SOIalign.h new file mode 100644 index 0000000000000000000000000000000000000000..716afbaf8e734cc12cc9f2dfd8bb3e8bf954a748 --- /dev/null +++ b/modules/bindings/src/USalign/SOIalign.h @@ -0,0 +1,959 @@ +#ifndef SOIalign_h +#define SOIalign_h 1 + +#include "TMalign.h" + +void print_invmap(int *invmap, const int ylen) +{ + int i,j; + for (j=0;j<ylen;j++) + { + i=invmap[j]; + if (i>=0) cout<<" ("<<i<<","<<j<<")"; + } + cout<<endl; +} + +void assign_sec_bond(int **secx_bond, const char *secx, const int xlen) +{ + int i,j; + int starti=-1; + int endi=-1; + char ss; + char prev_ss=0; + for (i=0; i<xlen; i++) + { + ss=secx[i]; + secx_bond[i][0]=secx_bond[i][1]=-1; + if (ss!=prev_ss && !(ss=='C' && prev_ss=='T') + && !(ss=='T' && prev_ss=='C')) + { + if (starti>=0) // previous SSE end + { + endi=i; + for (j=starti;j<endi;j++) + { + secx_bond[j][0]=starti; + secx_bond[j][1]=endi; + } + } + if (ss=='H' || ss=='E' || ss=='<' || ss=='>') starti=i; + else starti=-1; + } + prev_ss=secx[i]; + } + if (starti>=0) // previous SSE end + { + endi=i; + for (j=starti;j<endi;j++) + { + secx_bond[j][0]=starti; + secx_bond[j][1]=endi; + } + } + for (i=0;i<xlen;i++) if (secx_bond[i][1]-secx_bond[i][0]==1) + secx_bond[i][0]=secx_bond[i][1]=-1; +} + +void getCloseK(double **xa, const int xlen, const int closeK_opt, double **xk) +{ + double **score; + NewArray(&score, xlen+1, xlen+1); + vector<pair<double,int> > close_idx_vec(xlen, make_pair(0,0)); + int i,j,k; + for (i=0;i<xlen;i++) + { + score[i+1][i+1]=0; + for (j=i+1;j<xlen;j++) score[j+1][i+1]=score[i+1][j+1]=dist(xa[i], xa[j]); + } + for (i=0;i<xlen;i++) + { + for (j=0;j<xlen;j++) + { + close_idx_vec[j].first=score[i+1][j+1]; + close_idx_vec[j].second=j; + } + sort(close_idx_vec.begin(), close_idx_vec.end()); + for (k=0;k<closeK_opt;k++) + { + j=close_idx_vec[k % xlen].second; + xk[i*closeK_opt+k][0]=xa[j][0]; + xk[i*closeK_opt+k][1]=xa[j][1]; + xk[i*closeK_opt+k][2]=xa[j][2]; + } + } + + /* clean up */ + vector<pair<double,int> >().swap(close_idx_vec); + DeleteArray(&score, xlen+1); +} + +/* check if pairing i to j conform to sequantiality within the SSE */ +inline bool sec2sq(const int i, const int j, + int **secx_bond, int **secy_bond, int *fwdmap, int *invmap) +{ + if (i<0 || j<0) return true; + int ii,jj; + if (secx_bond[i][0]>=0) + { + for (ii=secx_bond[i][0];ii<secx_bond[i][1];ii++) + { + jj=fwdmap[ii]; + if (jj>=0 && (i-ii)*(j-jj)<=0) return false; + } + } + if (secy_bond[j][0]>=0) + { + for (jj=secy_bond[j][0];jj<secy_bond[j][1];jj++) + { + ii=invmap[jj]; + if (ii>=0 && (i-ii)*(j-jj)<=0) return false; + } + } + return true; +} + +void soi_egs(double **score, const int xlen, const int ylen, int *invmap, + int **secx_bond, int **secy_bond, const int mm_opt) +{ + int i,j; + int *fwdmap=new int[xlen]; // j=fwdmap[i]; + for (i=0; i<xlen; i++) fwdmap[i]=-1; + for (j=0; j<ylen; j++) + { + i=invmap[j]; + if (i>=0) fwdmap[i]=j; + } + + /* stage 1 - make initial assignment, starting from the highest score pair */ + double max_score; + int maxi,maxj; + while(1) + { + max_score=0; + maxi=maxj=-1; + for (i=0;i<xlen;i++) + { + if (fwdmap[i]>=0) continue; + for (j=0;j<ylen;j++) + { + if (invmap[j]>=0 || score[i+1][j+1]<=max_score) continue; + if (mm_opt==6 && !sec2sq(i,j,secx_bond,secy_bond, + fwdmap,invmap)) continue; + maxi=i; + maxj=j; + max_score=score[i+1][j+1]; + } + } + if (maxi<0) break; // no assignment; + invmap[maxj]=maxi; + fwdmap[maxi]=maxj; + } + + double total_score=0; + for (j=0;j<ylen;j++) + { + i=invmap[j]; + if (i>=0) total_score+=score[i+1][j+1]; + } + + /* stage 2 - swap assignment until total score cannot be improved */ + int iter; + int oldi,oldj; + double delta_score; + for (iter=0; iter<getmin(xlen,ylen)*5; iter++) + { + //cout<<"total_score="<<total_score<<".iter="<<iter<<endl; + //print_invmap(invmap,ylen); + delta_score=-1; + for (i=0;i<xlen;i++) + { + oldj=fwdmap[i]; + for (j=0;j<ylen;j++) + { + oldi=invmap[j]; + if (score[i+1][j+1]<=0 || oldi==i) continue; + if (mm_opt==6 && (!sec2sq(i,j,secx_bond,secy_bond,fwdmap,invmap) || + !sec2sq(oldi,oldj,secx_bond,secy_bond,fwdmap,invmap))) + continue; + delta_score=score[i+1][j+1]; + if (oldi>=0 && oldj>=0) delta_score+=score[oldi+1][oldj+1]; + if (oldi>=0) delta_score-=score[oldi+1][j+1]; + if (oldj>=0) delta_score-=score[i+1][oldj+1]; + + if (delta_score>0) // successful swap + { + fwdmap[i]=j; + if (oldi>=0) fwdmap[oldi]=oldj; + invmap[j]=i; + if (oldj>=0) invmap[oldj]=oldi; + total_score+=delta_score; + break; + } + } + } + if (delta_score<=0) break; // cannot make further swap + } + + /* clean up */ + delete[]fwdmap; +} + +/* entry function for se + * u_opt corresponds to option -L + * if u_opt==2, use d0 from Lnorm_ass for alignment + * */ +int soi_se_main( + double **xa, double **ya, const char *seqx, const char *seqy, + double &TM1, double &TM2, double &TM3, double &TM4, double &TM5, + double &d0_0, double &TM_0, + double &d0A, double &d0B, double &d0u, double &d0a, double &d0_out, + string &seqM, string &seqxA, string &seqyA, + double &rmsd0, int &L_ali, double &Liden, + double &TM_ali, double &rmsd_ali, int &n_ali, int &n_ali8, + const int xlen, const int ylen, + const double Lnorm_ass, const double d0_scale, const bool i_opt, + const bool a_opt, const int u_opt, const bool d_opt, const int mol_type, + const int outfmt_opt, int *invmap, double *dist_list, + int **secx_bond, int **secy_bond, const int mm_opt) +{ + double D0_MIN; //for d0 + double Lnorm; //normalization length + double score_d8,d0,d0_search,dcu0;//for TMscore search + double **score; // score for aligning a residue pair + bool **path; // for dynamic programming + double **val; // for dynamic programming + + int *m1=NULL; + int *m2=NULL; + int i,j; + double d; + if (outfmt_opt<2) + { + m1=new int[xlen]; //alignd index in x + m2=new int[ylen]; //alignd index in y + } + + /***********************/ + /* allocate memory */ + /***********************/ + NewArray(&score, xlen+1, ylen+1); + NewArray(&path, xlen+1, ylen+1); + NewArray(&val, xlen+1, ylen+1); + //int *invmap = new int[ylen+1]; + + /* set d0 */ + parameter_set4search(xlen, ylen, D0_MIN, Lnorm, + score_d8, d0, d0_search, dcu0); // set score_d8 + parameter_set4final(xlen, D0_MIN, Lnorm, + d0B, d0_search, mol_type); // set d0B + parameter_set4final(ylen, D0_MIN, Lnorm, + d0A, d0_search, mol_type); // set d0A + if (a_opt) + parameter_set4final((xlen+ylen)*0.5, D0_MIN, Lnorm, + d0a, d0_search, mol_type); // set d0a + if (u_opt) + { + parameter_set4final(Lnorm_ass, D0_MIN, Lnorm, + d0u, d0_search, mol_type); // set d0u + if (u_opt==2) + { + parameter_set4search(Lnorm_ass, Lnorm_ass, D0_MIN, Lnorm, + score_d8, d0, d0_search, dcu0); // set score_d8 + } + } + + /* perform alignment */ + for(j=0; j<ylen; j++) invmap[j]=-1; + double d02=d0*d0; + double score_d82=score_d8*score_d8; + double d2; + for(i=0; i<xlen; i++) + { + for(j=0; j<ylen; j++) + { + d2=dist(xa[i], ya[j]); + if (d2>score_d82) score[i+1][j+1]=0; + else score[i+1][j+1]=1./(1+ d2/d02); + } + } + if (mm_opt==6) NWDP_TM(score, path, val, xlen, ylen, -0.6, invmap); + soi_egs(score, xlen, ylen, invmap, secx_bond, secy_bond, mm_opt); + + rmsd0=TM1=TM2=TM3=TM4=TM5=0; + int k=0; + n_ali=0; + n_ali8=0; + for(j=0; j<ylen; j++) + { + i=invmap[j]; + dist_list[j]=-1; + if(i>=0)//aligned + { + n_ali++; + d=sqrt(dist(&xa[i][0], &ya[j][0])); + dist_list[j]=d; + if (score[i+1][j+1]>0) + { + if (outfmt_opt<2) + { + m1[k]=i; + m2[k]=j; + } + k++; + TM2+=1/(1+(d/d0B)*(d/d0B)); // chain_1 + TM1+=1/(1+(d/d0A)*(d/d0A)); // chain_2 + if (a_opt) TM3+=1/(1+(d/d0a)*(d/d0a)); // -a + if (u_opt) TM4+=1/(1+(d/d0u)*(d/d0u)); // -u + if (d_opt) TM5+=1/(1+(d/d0_scale)*(d/d0_scale)); // -d + rmsd0+=d*d; + } + } + } + n_ali8=k; + TM2/=xlen; + TM1/=ylen; + TM3/=(xlen+ylen)*0.5; + TM4/=Lnorm_ass; + TM5/=ylen; + if (n_ali8) rmsd0=sqrt(rmsd0/n_ali8); + + if (outfmt_opt>=2) + { + DeleteArray(&score, xlen+1); + return 0; + } + + /* extract aligned sequence */ + int ali_len=xlen+ylen; + for (j=0;j<ylen;j++) ali_len-=(invmap[j]>=0); + seqxA.assign(ali_len,'-'); + seqM.assign( ali_len,' '); + seqyA.assign(ali_len,'-'); + + int *fwdmap = new int [xlen+1]; + for (i=0;i<xlen;i++) fwdmap[i]=-1; + + for (j=0;j<ylen;j++) + { + seqyA[j]=seqy[j]; + i=invmap[j]; + if (i<0) continue; + if (sqrt(dist(xa[i], ya[j]))<d0_out) seqM[j]=':'; + else seqM[j]='.'; + fwdmap[i]=j; + seqxA[j]=seqx[i]; + Liden+=(seqxA[k]==seqyA[k]); + } + k=0; + for (i=0;i<xlen;i++) + { + j=fwdmap[i]; + if (j>=0) continue; + seqxA[ylen+k]=seqx[i]; + k++; + } + + /* free memory */ + delete [] fwdmap; + delete [] m1; + delete [] m2; + DeleteArray(&score, xlen+1); + DeleteArray(&path, xlen+1); + DeleteArray(&val, xlen+1); + return 0; // zero for no exception +} + +inline void SOI_super2score(double **xt, double **ya, const int xlen, + const int ylen, double **score, double d0, double score_d8) +{ + int i,j; + double d02=d0*d0; + double score_d82=score_d8*score_d8; + double d2; + for (i=0; i<xlen; i++) + { + for(j=0; j<ylen; j++) + { + d2=dist(xt[i], ya[j]); + if (d2>score_d82) score[i+1][j+1]=0; + else score[i+1][j+1]=1./(1+ d2/d02); + } + } +} + +//heuristic run of dynamic programing iteratively to find the best alignment +//input: initial rotation matrix t, u +// vectors x and y, d0 +//output: best alignment that maximizes the TMscore, will be stored in invmap +double SOI_iter(double **r1, double **r2, double **xtm, double **ytm, + double **xt, double **score, bool **path, double **val, double **xa, double **ya, + int xlen, int ylen, double t[3], double u[3][3], int *invmap0, + int iteration_max, double local_d0_search, + double Lnorm, double d0, double score_d8, + int **secx_bond, int **secy_bond, const int mm_opt, const bool init_invmap=false) +{ + double rmsd; + int *invmap=new int[ylen+1]; + + int iteration, i, j, k; + double tmscore, tmscore_max, tmscore_old=0; + tmscore_max=-1; + + //double d01=d0+1.5; + double d02=d0*d0; + double score_d82=score_d8*score_d8; + double d2; + for (iteration=0; iteration<iteration_max; iteration++) + { + if (iteration==0 && init_invmap) + for (j=0;j<ylen;j++) invmap[j]=invmap0[j]; + else + { + for (j=0; j<ylen; j++) invmap[j]=-1; + if (mm_opt==6) NWDP_TM(score, path, val, xlen, ylen, -0.6, invmap); + } + soi_egs(score, xlen, ylen, invmap, secx_bond, secy_bond, mm_opt); + + k=0; + for (j=0; j<ylen; j++) + { + i=invmap[j]; + if (i<0) continue; + + xtm[k][0]=xa[i][0]; + xtm[k][1]=xa[i][1]; + xtm[k][2]=xa[i][2]; + + ytm[k][0]=ya[j][0]; + ytm[k][1]=ya[j][1]; + ytm[k][2]=ya[j][2]; + k++; + } + + tmscore = TMscore8_search(r1, r2, xtm, ytm, xt, k, t, u, + 40, 8, &rmsd, local_d0_search, Lnorm, score_d8, d0); + + if (tmscore>tmscore_max) + { + tmscore_max=tmscore; + for (j=0; j<ylen; j++) invmap0[j]=invmap[j]; + } + + if (iteration>0 && fabs(tmscore_old-tmscore)<0.000001) break; + tmscore_old=tmscore; + do_rotation(xa, xt, xlen, t, u); + SOI_super2score(xt, ya, xlen, ylen, score, d0, score_d8); + }// for iteration + + delete []invmap; + return tmscore_max; +} + +void get_SOI_initial_assign(double **xk, double **yk, const int closeK_opt, + double **score, bool **path, double **val, const int xlen, const int ylen, + double t[3], double u[3][3], int invmap[], + double local_d0_search, double d0, double score_d8, + int **secx_bond, int **secy_bond, const int mm_opt) +{ + int i,j,k; + double **xfrag; + double **xtran; + double **yfrag; + NewArray(&xfrag, closeK_opt, 3); + NewArray(&xtran, closeK_opt, 3); + NewArray(&yfrag, closeK_opt, 3); + double rmsd; + double d02=d0*d0; + double score_d82=score_d8*score_d8; + double d2; + + /* fill in score */ + for (i=0;i<xlen;i++) + { + for (k=0;k<closeK_opt;k++) + { + xfrag[k][0]=xk[i*closeK_opt+k][0]; + xfrag[k][1]=xk[i*closeK_opt+k][1]; + xfrag[k][2]=xk[i*closeK_opt+k][2]; + } + + for (j=0;j<ylen;j++) + { + for (k=0;k<closeK_opt;k++) + { + yfrag[k][0]=yk[j*closeK_opt+k][0]; + yfrag[k][1]=yk[j*closeK_opt+k][1]; + yfrag[k][2]=yk[j*closeK_opt+k][2]; + } + Kabsch(xfrag, yfrag, closeK_opt, 1, &rmsd, t, u); + do_rotation(xfrag, xtran, closeK_opt, t, u); + + //for (k=0; k<closeK_opt; k++) + //{ + //d2=dist(xtran[k], yfrag[k]); + //if (d2>score_d82) score[i+1][j+1]=0; + //else score[i+1][j+1]=1./(1+d2/d02); + //} + k=closeK_opt-1; + d2=dist(xtran[k], yfrag[k]); + if (d2>score_d82) score[i+1][j+1]=0; + else score[i+1][j+1]=1./(1+d2/d02); + } + } + + /* initial assignment */ + for (j=0;j<ylen;j++) invmap[j]=-1; + if (mm_opt==6) NWDP_TM(score, path, val, xlen, ylen, -0.6, invmap); + for (j=0; j<ylen;j++) i=invmap[j]; + soi_egs(score, xlen, ylen, invmap, secx_bond, secy_bond, mm_opt); + + /* clean up */ + DeleteArray(&xfrag, closeK_opt); + DeleteArray(&xtran, closeK_opt); + DeleteArray(&yfrag, closeK_opt); +} + +void SOI_assign2super(double **r1, double **r2, double **xtm, double **ytm, + double **xt, double **xa, double **ya, + const int xlen, const int ylen, double t[3], double u[3][3], int invmap[], + double local_d0_search, double Lnorm, double d0, double score_d8) +{ + int i,j,k; + double rmsd; + double d02=d0*d0; + double score_d82=score_d8*score_d8; + double d2; + + k=0; + for (j=0; j<ylen; j++) + { + i=invmap[j]; + if (i<0) continue; + xtm[k][0]=xa[i][0]; + xtm[k][1]=xa[i][1]; + xtm[k][2]=xa[i][2]; + + ytm[k][0]=ya[j][0]; + ytm[k][1]=ya[j][1]; + ytm[k][2]=ya[j][2]; + k++; + } + TMscore8_search(r1, r2, xtm, ytm, xt, k, t, u, + 40, 8, &rmsd, local_d0_search, Lnorm, score_d8, d0); + do_rotation(xa, xt, xlen, t, u); +} + +/* entry function for TM-align with circular permutation + * i_opt, a_opt, u_opt, d_opt, TMcut are not implemented yet */ +int SOIalign_main(double **xa, double **ya, + double **xk, double **yk, const int closeK_opt, + const char *seqx, const char *seqy, const char *secx, const char *secy, + double t0[3], double u0[3][3], + double &TM1, double &TM2, double &TM3, double &TM4, double &TM5, + double &d0_0, double &TM_0, + double &d0A, double &d0B, double &d0u, double &d0a, double &d0_out, + string &seqM, string &seqxA, string &seqyA, int *invmap, + double &rmsd0, int &L_ali, double &Liden, + double &TM_ali, double &rmsd_ali, int &n_ali, int &n_ali8, + const int xlen, const int ylen, + const vector<string> sequence, const double Lnorm_ass, + const double d0_scale, const int i_opt, const int a_opt, + const bool u_opt, const bool d_opt, const bool fast_opt, + const int mol_type, double *dist_list, + int **secx_bond, int **secy_bond, const int mm_opt) +{ + double D0_MIN; //for d0 + double Lnorm; //normalization length + double score_d8,d0,d0_search,dcu0;//for TMscore search + double t[3], u[3][3]; //Kabsch translation vector and rotation matrix + double **score; // Input score table for enhanced greedy search + double **scoret; // Transposed score table for enhanced greedy search + bool **path; // for dynamic programming + double **val; // for dynamic programming + double **xtm, **ytm; // for TMscore search engine + double **xt; //for saving the superposed version of r_1 or xtm + double **yt; //for saving the superposed version of r_2 or ytm + double **r1, **r2; // for Kabsch rotation + + /***********************/ + /* allocate memory */ + /***********************/ + int minlen = min(xlen, ylen); + int maxlen = (xlen>ylen)?xlen:ylen; + NewArray(&score, xlen+1, ylen+1); + NewArray(&scoret, ylen+1, xlen+1); + NewArray(&path, maxlen+1, maxlen+1); + NewArray(&val, maxlen+1, maxlen+1); + NewArray(&xtm, minlen, 3); + NewArray(&ytm, minlen, 3); + NewArray(&xt, xlen, 3); + NewArray(&yt, ylen, 3); + NewArray(&r1, minlen, 3); + NewArray(&r2, minlen, 3); + + /***********************/ + /* parameter set */ + /***********************/ + parameter_set4search(xlen, ylen, D0_MIN, Lnorm, + score_d8, d0, d0_search, dcu0); + int simplify_step = 40; //for simplified search engine + int score_sum_method = 8; //for scoring method, whether only sum over pairs with dis<score_d8 + + int i,j; + int *fwdmap0 = new int[xlen+1]; + int *invmap0 = new int[ylen+1]; + + double TMmax=-1, TM=-1; + for(i=0; i<xlen; i++) fwdmap0[i]=-1; + for(j=0; j<ylen; j++) invmap0[j]=-1; + double local_d0_search = d0_search; + int iteration_max=(fast_opt)?2:30; + //if (mm_opt==6) iteration_max=1; + + /*************************************************************/ + /* initial alignment with sequence order dependent alignment */ + /*************************************************************/ + CPalign_main( + xa, ya, seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + i_opt, a_opt, u_opt, d_opt, fast_opt, + mol_type,-1); + if (mm_opt==6) + { + i=0; + j=0; + for (int r=0;r<seqxA.size();r++) + { + if (seqxA[r]=='*') // circular permutation point + { + for (int jj=0;jj<j;jj++) if (invmap0[jj]>=0) + invmap0[jj]+=xlen - i; + i=0; + continue; + } + if (seqyA[r]!='-') + { + if (seqxA[r]!='-') invmap0[j]=i; + j++; + } + if (seqxA[r]!='-') i++; + } + for (j=0;j<ylen;j++) + { + i=invmap0[j]; + if (i>=0) fwdmap0[i]=j; + } + } + do_rotation(xa, xt, xlen, t0, u0); + SOI_super2score(xt, ya, xlen, ylen, score, d0, score_d8); + for (i=0;i<xlen;i++) for (j=0;j<ylen;j++) scoret[j+1][i+1]=score[i+1][j+1]; + TMmax=SOI_iter(r1, r2, xtm, ytm, xt, score, path, val, xa, ya, + xlen, ylen, t0, u0, invmap0, iteration_max, + local_d0_search, Lnorm, d0, score_d8, secx_bond, secy_bond, mm_opt, true); + TM =SOI_iter(r2, r1, ytm, xtm, yt,scoret, path, val, ya, xa, + ylen, xlen, t0, u0, fwdmap0, iteration_max, + local_d0_search, Lnorm, d0, score_d8, secy_bond, secx_bond, mm_opt, true); + //cout<<"TM2="<<TM2<<"\tTM1="<<TM1<<"\tTMmax="<<TMmax<<"\tTM="<<TM<<endl; + if (TM>TMmax) + { + TMmax = TM; + for (j=0; j<ylen; j++) invmap0[j]=-1; + for (i=0; i<xlen; i++) + { + j=fwdmap0[i]; + if (j>=0) invmap0[j]=i; + } + } + + /***************************************************************/ + /* initial alignment with sequence order independent alignment */ + /***************************************************************/ + if (closeK_opt>=3) + { + get_SOI_initial_assign(xk, yk, closeK_opt, score, path, val, + xlen, ylen, t, u, invmap, local_d0_search, d0, score_d8, + secx_bond, secy_bond, mm_opt); + for (i=0;i<xlen;i++) for (j=0;j<ylen;j++) scoret[j+1][i+1]=score[i+1][j+1]; + + SOI_assign2super(r1, r2, xtm, ytm, xt, xa, ya, + xlen, ylen, t, u, invmap, local_d0_search, Lnorm, d0, score_d8); + TM=SOI_iter(r1, r2, xtm, ytm, xt, score, path, val, xa, ya, + xlen, ylen, t, u, invmap, iteration_max, + local_d0_search, Lnorm, d0, score_d8, secx_bond, secy_bond, mm_opt); + if (TM>TMmax) + { + TMmax = TM; + for (j = 0; j<ylen; j++) invmap0[j] = invmap[j]; + } + + for (i=0;i<xlen;i++) fwdmap0[i]=-1; + if (mm_opt==6) NWDP_TM(scoret, path, val, ylen, xlen, -0.6, fwdmap0); + soi_egs(scoret, ylen, xlen, fwdmap0, secy_bond, secx_bond, mm_opt); + SOI_assign2super(r2, r1, ytm, xtm, yt, ya, xa, + ylen, xlen, t, u, fwdmap0, local_d0_search, Lnorm, d0, score_d8); + TM=SOI_iter(r2, r1, ytm, xtm, yt, scoret, path, val, ya, xa, ylen, xlen, t, u, + fwdmap0, iteration_max, local_d0_search, Lnorm, d0, score_d8,secy_bond, secx_bond, mm_opt); + if (TM>TMmax) + { + TMmax = TM; + for (j=0; j<ylen; j++) invmap0[j]=-1; + for (i=0; i<xlen; i++) + { + j=fwdmap0[i]; + if (j>=0) invmap0[j]=i; + } + } + } + + //*******************************************************************// + // The alignment will not be changed any more in the following // + //*******************************************************************// + //check if the initial alignment is generated appropriately + bool flag=false; + for (i=0; i<xlen; i++) fwdmap0[i]=-1; + for (j=0; j<ylen; j++) + { + i=invmap0[j]; + invmap[j]=i; + if (i>=0) + { + fwdmap0[i]=j; + flag=true; + } + } + if(!flag) + { + cout << "There is no alignment between the two structures! " + << "Program stop with no result!" << endl; + TM1=TM2=TM3=TM4=TM5=0; + return 1; + } + + + //********************************************************************// + // Detailed TMscore search engine --> prepare for final TMscore // + //********************************************************************// + //run detailed TMscore search engine for the best alignment, and + //extract the best rotation matrix (t, u) for the best alignment + simplify_step=1; + if (fast_opt) simplify_step=40; + score_sum_method=8; + TM = detailed_search_standard(r1, r2, xtm, ytm, xt, xa, ya, xlen, ylen, + invmap0, t, u, simplify_step, score_sum_method, local_d0_search, + false, Lnorm, score_d8, d0); + + double rmsd; + simplify_step=1; + score_sum_method=0; + double Lnorm_0=ylen; + + //select pairs with dis<d8 for final TMscore computation and output alignment + int k=0; + int *m1, *m2; + double d; + m1=new int[xlen]; //alignd index in x + m2=new int[ylen]; //alignd index in y + copy_t_u(t, u, t0, u0); + + //****************************************// + // Final TMscore 1 // + //****************************************// + + do_rotation(xa, xt, xlen, t, u); + k=0; + n_ali=0; + for (i=0; i<xlen; i++) + { + j=fwdmap0[i]; + if(j>=0)//aligned + { + n_ali++; + d=sqrt(dist(&xt[i][0], &ya[j][0])); + if (d <= score_d8) + { + m1[k]=i; + m2[k]=j; + + xtm[k][0]=xa[i][0]; + xtm[k][1]=xa[i][1]; + xtm[k][2]=xa[i][2]; + + ytm[k][0]=ya[j][0]; + ytm[k][1]=ya[j][1]; + ytm[k][2]=ya[j][2]; + + r1[k][0] = xt[i][0]; + r1[k][1] = xt[i][1]; + r1[k][2] = xt[i][2]; + r2[k][0] = ya[j][0]; + r2[k][1] = ya[j][1]; + r2[k][2] = ya[j][2]; + + k++; + } + else fwdmap0[i]=-1; + } + } + n_ali8=k; + + Kabsch(r1, r2, n_ali8, 0, &rmsd0, t, u);// rmsd0 is used for final output, only recalculate rmsd0, not t & u + rmsd0 = sqrt(rmsd0 / n_ali8); + + //normalized by length of structure A + parameter_set4final(xlen+0.0, D0_MIN, Lnorm, d0, d0_search, mol_type); + d0B=d0; + local_d0_search = d0_search; + TM2 = TMscore8_search(r1, r2, xtm, ytm, xt, n_ali8, t, u, simplify_step, + score_sum_method, &rmsd, local_d0_search, Lnorm, score_d8, d0); + + //****************************************// + // Final TMscore 2 // + //****************************************// + + do_rotation(xa, xt, xlen, t0, u0); + k=0; + for (j=0; j<ylen; j++) + { + i=invmap0[j]; + if(i>=0)//aligned + { + d=sqrt(dist(&xt[i][0], &ya[j][0])); + if (d <= score_d8) + { + m1[k]=i; + m2[k]=j; + + xtm[k][0]=xa[i][0]; + xtm[k][1]=xa[i][1]; + xtm[k][2]=xa[i][2]; + + ytm[k][0]=ya[j][0]; + ytm[k][1]=ya[j][1]; + ytm[k][2]=ya[j][2]; + + r1[k][0] = xt[i][0]; + r1[k][1] = xt[i][1]; + r1[k][2] = xt[i][2]; + r2[k][0] = ya[j][0]; + r2[k][1] = ya[j][1]; + r2[k][2] = ya[j][2]; + + k++; + } + else invmap[j]=invmap0[j]=-1; + } + } + + //normalized by length of structure B + parameter_set4final(Lnorm_0, D0_MIN, Lnorm, d0, d0_search, mol_type); + d0A=d0; + d0_0=d0A; + local_d0_search = d0_search; + TM1 = TMscore8_search(r1, r2, xtm, ytm, xt, n_ali8, t0, u0, simplify_step, + score_sum_method, &rmsd, local_d0_search, Lnorm, score_d8, d0); + TM_0 = TM1; + + if (a_opt>0) + { + //normalized by average length of structures A, B + Lnorm_0=(xlen+ylen)*0.5; + parameter_set4final(Lnorm_0, D0_MIN, Lnorm, d0, d0_search, mol_type); + d0a=d0; + d0_0=d0a; + local_d0_search = d0_search; + + TM3 = TMscore8_search(r1, r2, xtm, ytm, xt, n_ali8, t0, u0, + simplify_step, score_sum_method, &rmsd, local_d0_search, Lnorm, + score_d8, d0); + TM_0=TM3; + } + if (u_opt) + { + //normalized by user assigned length + parameter_set4final(Lnorm_ass, D0_MIN, Lnorm, + d0, d0_search, mol_type); + d0u=d0; + d0_0=d0u; + Lnorm_0=Lnorm_ass; + local_d0_search = d0_search; + TM4 = TMscore8_search(r1, r2, xtm, ytm, xt, n_ali8, t0, u0, + simplify_step, score_sum_method, &rmsd, local_d0_search, Lnorm, + score_d8, d0); + TM_0=TM4; + } + if (d_opt) + { + //scaled by user assigned d0 + parameter_set4scale(ylen, d0_scale, Lnorm, d0, d0_search); + d0_out=d0_scale; + d0_0=d0_scale; + //Lnorm_0=ylen; + local_d0_search = d0_search; + TM5 = TMscore8_search(r1, r2, xtm, ytm, xt, n_ali8, t0, u0, + simplify_step, score_sum_method, &rmsd, local_d0_search, Lnorm, + score_d8, d0); + TM_0=TM5; + } + + /* derive alignment from superposition */ + int ali_len=xlen+ylen; + for (j=0;j<ylen;j++) ali_len-=(invmap0[j]>=0); + seqxA.assign(ali_len,'-'); + seqM.assign( ali_len,' '); + seqyA.assign(ali_len,'-'); + + //do_rotation(xa, xt, xlen, t, u); + do_rotation(xa, xt, xlen, t0, u0); + + Liden=0; + //double SO=0; + for (j=0;j<ylen;j++) + { + seqyA[j]=seqy[j]; + i=invmap0[j]; + dist_list[j]=-1; + if (i<0) continue; + d=sqrt(dist(xt[i], ya[j])); + if (d<d0_out) seqM[j]=':'; + else seqM[j]='.'; + dist_list[j]=d; + //SO+=(d<3.5); + seqxA[j]=seqx[i]; + Liden+=(seqx[i]==seqy[j]); + } + //SO/=getmin(xlen,ylen); + k=0; + for (i=0;i<xlen;i++) + { + j=fwdmap0[i]; + if (j>=0) continue; + seqxA[ylen+k]=seqx[i]; + k++; + } + //cout<<n_ali8<<'\t' + //<<rmsd0<<'\t' + //<<100.*SO<<endl; + + + /* clean up */ + DeleteArray(&score, xlen+1); + DeleteArray(&scoret,ylen+1); + DeleteArray(&path,maxlen+1); + DeleteArray(&val, maxlen+1); + DeleteArray(&xtm, minlen); + DeleteArray(&ytm, minlen); + DeleteArray(&xt,xlen); + DeleteArray(&yt,ylen); + DeleteArray(&r1, minlen); + DeleteArray(&r2, minlen); + delete[]invmap0; + delete[]fwdmap0; + delete[]m1; + delete[]m2; + return 0; +} +#endif diff --git a/modules/bindings/src/tmalign/TMalign.cpp b/modules/bindings/src/USalign/TMalign.cpp similarity index 93% rename from modules/bindings/src/tmalign/TMalign.cpp rename to modules/bindings/src/USalign/TMalign.cpp index 7ea33e1a72155157b385a45a1710352813e52a1e..c822d4c3094c0e345c321dfce11711724eb06e51 100644 --- a/modules/bindings/src/tmalign/TMalign.cpp +++ b/modules/bindings/src/USalign/TMalign.cpp @@ -9,7 +9,7 @@ void print_version() cout << "\n" " **********************************************************************\n" -" * TM-align (Version 20210520): protein and RNA structure alignment *\n" +" * TM-align (Version 20220623): protein and RNA structure alignment *\n" " * References: Y Zhang, J Skolnick. Nucl Acids Res 33, 2302-9 (2005) *\n" " * S Gong, C Zhang, Y Zhang. Bioinformatics, bz282 (2019) *\n" " * Please email comments and suggestions to yangzhanglab@umich.edu *\n" @@ -67,7 +67,7 @@ void print_extra_help() " -1: full output, but without version or citation information\n" "\n" " -byresi Whether to assume residue index correspondence between the\n" -" two structures.\n" +" two structures. The same as -TMscore.\n" " 0: (default) sequence independent alignment\n" " 1: (same as TMscore program) sequence-dependent superposition,\n" " i.e. align by residue index\n" @@ -75,6 +75,11 @@ void print_extra_help() " align by residue index and chain ID\n" " 3: (similar to TMscore -c, should be used with -ter <=1)\n" " align by residue index and order of chain\n" +//" 4: sequence dependent alignment: perform Needleman-Wunsch\n" +//" global sequence alignment, followed by TM-score superposition\n" +" 5: sequence dependent alignment: perform glocal sequence\n" +" alignment followed by TM-score superposition.\n" +" -byresi 5 is thee same as -seq\n" "\n" " -TMcut -1: (default) do not consider TMcut\n" " Values in [0.5,1): Do not proceed with TM-align for this\n" @@ -308,10 +313,15 @@ int main(int argc, char *argv[]) { TMcut=atof(argv[i + 1]); i++; } - else if ( !strcmp(argv[i],"-byresi") && i < (argc-1) ) + else if ((!strcmp(argv[i],"-byresi") || !strcmp(argv[i],"-tmscore") || + !strcmp(argv[i],"-TMscore")) && i < (argc-1) ) { byresi_opt=atoi(argv[i + 1]); i++; } + else if ( !strcmp(argv[i],"-seq") ) + { + byresi_opt=5; + } else if ( !strcmp(argv[i],"-cp") ) { cp_opt=1; @@ -374,10 +384,10 @@ int main(int argc, char *argv[]) { if (i_opt) PrintErrorAndQuit("-byresi >=1 cannot be used with -i or -I"); - if (byresi_opt<0 || byresi_opt>3) - PrintErrorAndQuit("-byresi can only be 0, 1, 2 or 3"); - if (byresi_opt>=2 && ter_opt>=2) - PrintErrorAndQuit("-byresi >=2 should be used with -ter <=1"); + if (byresi_opt<0 || byresi_opt>5) + PrintErrorAndQuit("-byresi can only be 0, 1, 2, 3, 4, or 5"); + if (byresi_opt>=2 && byresi_opt<=3 && ter_opt>=2) + PrintErrorAndQuit("-byresi 2 and -byresi 3 should be used with -ter <=1"); } if (split_opt==1 && ter_opt!=0) PrintErrorAndQuit("-split 1 should be used with -ter 0"); @@ -566,9 +576,9 @@ int main(int argc, char *argv[]) n_ali8, L_ali, TM_ali, rmsd_ali, TM_0, d0_0, d0A, d0B, Lnorm_ass, d0_scale, d0a, d0u, - (m_opt?fname_matrix+chainID_list1[chain_i]:"").c_str(), + (m_opt?fname_matrix:"").c_str(), outfmt_opt, ter_opt, 0, split_opt, o_opt, - (o_opt?fname_super+chainID_list1[chain_i]:"").c_str(), + (o_opt?fname_super:"").c_str(), i_opt, a_opt, u_opt, d_opt,mirror_opt, resi_vec1, resi_vec2 ); @@ -618,6 +628,6 @@ int main(int argc, char *argv[]) t2 = clock(); float diff = ((float)t2 - (float)t1)/CLOCKS_PER_SEC; - printf("Total CPU time is %5.2f seconds\n", diff); + printf("#Total CPU time is %5.2f seconds\n", diff); return 0; } diff --git a/modules/bindings/src/tmalign/TMalign.h b/modules/bindings/src/USalign/TMalign.h similarity index 88% rename from modules/bindings/src/tmalign/TMalign.h rename to modules/bindings/src/USalign/TMalign.h index 9187ad3cbd170541a4c72baf44895c86185183d2..81196a8071fcec442bba46867e246bb9450facf8 100644 --- a/modules/bindings/src/tmalign/TMalign.h +++ b/modules/bindings/src/USalign/TMalign.h @@ -1,9 +1,12 @@ /* Functions for the core TMalign algorithm, including the entry function * TMalign_main */ +#ifndef TMalign_h +#define TMalign_h 1 #include "param_set.h" #include "NW.h" #include "Kabsch.h" +#include "NWalign.h" // 1, collect those residues with dis<d; // 2, calculate TMscore @@ -540,6 +543,10 @@ double get_score_fast( double **r1, double **r2, double **xtm, double **ytm, //second iteration double d002t=d002; + vector<double> dis_vec(dis, dis+n_ali); + sort(dis_vec.begin(), dis_vec.end()); + if (d002t<dis_vec[2]) d002t=dis_vec[2]; + dis_vec.clear(); while(1) { j=0; @@ -577,7 +584,10 @@ double get_score_fast( double **r1, double **r2, double **xtm, double **ytm, //third iteration d002t=d002+1; - + vector<double> dis_vec(dis, dis+n_ali); + sort(dis_vec.begin(), dis_vec.end()); + if (d002t<dis_vec[2]) d002t=dis_vec[2]; + dis_vec.clear(); while(1) { j=0; @@ -852,6 +862,11 @@ void make_sec(char *seq, double **x, int len, char *sec,const string atom_opt) if (i>0 && j+1<len && bp[i-1][j+1]) continue; if (!bp[i+1][j-1]) continue; sec_str(len,seq, bp, i,j,ii,jj); + if (jj<i || j<ii) + { + ii=i; + jj=j; + } A0.push_back(i); B0.push_back(j); C0.push_back(ii); @@ -1467,11 +1482,14 @@ void output_pymol(const string xname, const string yname, { int compress_type=0; // uncompressed file ifstream fin; +#ifndef REDI_PSTREAM_H_SEEN + ifstream fin_gz; +#else redi::ipstream fin_gz; // if file is compressed if (xname.size()>=3 && xname.substr(xname.size()-3,3)==".gz") { - fin_gz.open("zcat "+xname); + fin_gz.open("gunzip -c "+xname); compress_type=1; } else if (xname.size()>=4 && @@ -1480,7 +1498,9 @@ void output_pymol(const string xname, const string yname, fin_gz.open("bzcat "+xname); compress_type=2; } - else fin.open(xname.c_str()); + else +#endif + fin.open(xname.c_str()); stringstream buf; stringstream buf_pymol; @@ -1534,7 +1554,7 @@ void output_pymol(const string xname, const string yname, if (line.compare(0,11,"_atom_site.")) continue; _atom_site.clear(); atom_site_pos=0; - _atom_site[line.substr(11,line.size()-12)]=atom_site_pos; + _atom_site[Trim(line.substr(11))]=atom_site_pos; while(1) { while(1) @@ -1552,7 +1572,7 @@ void output_pymol(const string xname, const string yname, if (line.size()) break; } if (line.compare(0,11,"_atom_site.")) break; - _atom_site[line.substr(11,line.size()-12)]=++atom_site_pos; + _atom_site[Trim(line.substr(11))]=++atom_site_pos; buf<<line<<'\n'; } @@ -2438,30 +2458,37 @@ void output_rasmol(const string xname, const string yname, void output_rotation_matrix(const char* fname_matrix, const double t[3], const double u[3][3]) { - fstream fout; - fout.open(fname_matrix, ios::out | ios::trunc); - if (fout)// succeed - { - fout << "------ The rotation matrix to rotate Structure_1 to Structure_2 ------\n"; - char dest[1000]; - sprintf(dest, "m %18s %14s %14s %14s\n", "t[m]", "u[m][0]", "u[m][1]", "u[m][2]"); - fout << string(dest); - for (int k = 0; k < 3; k++) - { - sprintf(dest, "%d %18.10f %14.10f %14.10f %14.10f\n", k, t[k], u[k][0], u[k][1], u[k][2]); - fout << string(dest); - } - fout << "\nCode for rotating Structure 1 from (x,y,z) to (X,Y,Z):\n" - "for(i=0; i<L; i++)\n" - "{\n" - " X[i] = t[0] + u[0][0]*x[i] + u[0][1]*y[i] + u[0][2]*z[i];\n" - " Y[i] = t[1] + u[1][0]*x[i] + u[1][1]*y[i] + u[1][2]*z[i];\n" - " Z[i] = t[2] + u[2][0]*x[i] + u[2][1]*y[i] + u[2][2]*z[i];\n" - "}\n"; - fout.close(); - } + stringstream ss; + ss << "------ The rotation matrix to rotate Structure_1 to Structure_2 ------\n"; + char dest[1000]; + sprintf(dest, "m %18s %14s %14s %14s\n", "t[m]", "u[m][0]", "u[m][1]", "u[m][2]"); + ss << string(dest); + for (int k = 0; k < 3; k++) + { + sprintf(dest, "%d %18.10f %14.10f %14.10f %14.10f\n", k, t[k], u[k][0], u[k][1], u[k][2]); + ss << string(dest); + } + ss << "\nCode for rotating Structure 1 from (x,y,z) to (X,Y,Z):\n" + "for(i=0; i<L; i++)\n" + "{\n" + " X[i] = t[0] + u[0][0]*x[i] + u[0][1]*y[i] + u[0][2]*z[i];\n" + " Y[i] = t[1] + u[1][0]*x[i] + u[1][1]*y[i] + u[1][2]*z[i];\n" + " Z[i] = t[2] + u[2][0]*x[i] + u[2][1]*y[i] + u[2][2]*z[i];\n" + "}\n"; + if (strcmp(fname_matrix,(char *)("-"))==0) + cout<<ss.str(); else - cout << "Open file to output rotation matrix fail.\n"; + { + fstream fout; + fout.open(fname_matrix, ios::out | ios::trunc); + if (fout) + { + fout<<ss.str(); + fout.close(); + } + else cout << "Open file to output rotation matrix fail.\n"; + } + ss.str(string()); } //output the final results @@ -2560,6 +2587,82 @@ void output_results(const string xname, const string yname, xlen, ylen, d0A, n_ali8, rmsd, TM1, Liden); } +void output_mTMalign_results(const string xname, const string yname, + const string chainID1, const string chainID2, + const int xlen, const int ylen, double t[3], double u[3][3], + const double TM1, const double TM2, + const double TM3, const double TM4, const double TM5, + const double rmsd, const double d0_out, const char *seqM, + const char *seqxA, const char *seqyA, const double Liden, + const int n_ali8, const int L_ali, const double TM_ali, + const double rmsd_ali, const double TM_0, const double d0_0, + const double d0A, const double d0B, const double Lnorm_ass, + const double d0_scale, const double d0a, const double d0u, + const char* fname_matrix, const int outfmt_opt, const int ter_opt, + const int mm_opt, const int split_opt, const int o_opt, + const string fname_super, const int i_opt, const int a_opt, + const bool u_opt, const bool d_opt, const int mirror_opt, + const vector<string>&resi_vec1, const vector<string>&resi_vec2) +{ + if (outfmt_opt<=0) + { + printf("Average aligned length= %d, RMSD= %6.2f, Seq_ID=n_identical/n_aligned= %4.3f\n", n_ali8, rmsd, (n_ali8>0)?Liden/n_ali8:0); + printf("Average TM-score= %6.5f (normalized by length of shorter structure: L=%d, d0=%.2f)\n", TM2, xlen, d0B); + printf("Average TM-score= %6.5f (normalized by length of longer structure: L=%d, d0=%.2f)\n", TM1, ylen, d0A); + + if (a_opt==1) + printf("Average TM-score= %6.5f (if normalized by average length of two structures: L=%.1f, d0=%.2f)\n", TM3, (xlen+ylen)*0.5, d0a); + if (u_opt) + printf("Average TM-score= %6.5f (normalized by average L=%.2f and d0=%.2f)\n", TM4, Lnorm_ass, d0u); + if (d_opt) + printf("Average TM-score= %6.5f (scaled by user-specified d0=%.2f, and L=%d)\n", TM5, d0_scale, ylen); + + //output alignment + printf("In the following, seqID=n_identical/L.\n\n%s\n", seqM); + } + else if (outfmt_opt==1) + { + printf("%s\n", seqM); + + printf("# Lali=%d\tRMSD=%.2f\tseqID_ali=%.3f\n", + n_ali8, rmsd, (n_ali8>0)?Liden/n_ali8:0); + + if (i_opt) + printf("# User-specified initial alignment: TM=%.5lf\tLali=%4d\trmsd=%.3lf\n", TM_ali, L_ali, rmsd_ali); + + if(a_opt) + printf("# TM-score=%.5f (normalized by average length of two structures: L=%.1f\td0=%.2f)\n", TM3, (xlen+ylen)*0.5, d0a); + + if(u_opt) + printf("# TM-score=%.5f (normalized by average L=%.2f\td0=%.2f)\n", TM4, Lnorm_ass, d0u); + + if(d_opt) + printf("# TM-score=%.5f (scaled by user-specified d0=%.2f\tL=%d)\n", TM5, d0_scale, ylen); + + printf("$$$$\n"); + } + else if (outfmt_opt==2) + { + printf("%s%s\t%s%s\t%.4f\t%.4f\t%.2f\t%4.3f\t%4.3f\t%4.3f\t%d\t%d\t%d", + xname.c_str(), chainID1.c_str(), yname.c_str(), chainID2.c_str(), + TM2, TM1, rmsd, Liden/xlen, Liden/ylen, (n_ali8>0)?Liden/n_ali8:0, + xlen, ylen, n_ali8); + } + cout << endl; + + if (strlen(fname_matrix)) output_rotation_matrix(fname_matrix, t, u); + + if (o_opt==1) + output_pymol(xname, yname, fname_super, t, u, ter_opt, + mm_opt, split_opt, mirror_opt, seqM, seqxA, seqyA, + resi_vec1, resi_vec2, chainID1, chainID2); + else if (o_opt==2) + output_rasmol(xname, yname, fname_super, t, u, ter_opt, + mm_opt, split_opt, mirror_opt, seqM, seqxA, seqyA, + resi_vec1, resi_vec2, chainID1, chainID2, + xlen, ylen, d0A, n_ali8, rmsd, TM1, Liden); +} + double standard_TMscore(double **r1, double **r2, double **xtm, double **ytm, double **xt, double **x, double **y, int xlen, int ylen, int invmap[], int& L_ali, double& RMSD, double D0_MIN, double Lnorm, double d0, @@ -2757,7 +2860,6 @@ int TMalign_main(double **xa, double **ya, // get initial alignment from user's input: // // Stick to the initial alignment // //************************************************// - bool bAlignStick = false; if (i_opt==3)// if input has set parameter for "-I" { // In the original code, this loop starts from 1, which is @@ -2798,13 +2900,12 @@ int TMalign_main(double **xa, double **ya, TMmax = TM; for (i = 0; i<ylen; i++) invmap0[i] = invmap[i]; } - bAlignStick = true; } /******************************************************/ /* get initial alignment with gapless threading */ /******************************************************/ - if (!bAlignStick) + if (i_opt<=1) { get_initial(r1, r2, xtm, ytm, xa, ya, xlen, ylen, invmap0, d0, d0_search, fast_opt, t, u); @@ -3007,60 +3108,60 @@ int TMalign_main(double **xa, double **ya, return 6; } } + } - //************************************************// - // get initial alignment from user's input: // - //************************************************// - if (i_opt==1)// if input has set parameter for "-i" - { - for (int j = 0; j < ylen; j++)// Set aligned position to be "-1" - invmap[j] = -1; + //************************************************// + // get initial alignment from user's input: // + //************************************************// + if (i_opt>=1 && i_opt<=2)// if input has set parameter for "-i" + { + for (int j = 0; j < ylen; j++)// Set aligned position to be "-1" + invmap[j] = -1; - int i1 = -1;// in C version, index starts from zero, not from one - int i2 = -1; - int L1 = sequence[0].size(); - int L2 = sequence[1].size(); - int L = min(L1, L2);// Get positions for aligned residues - for (int kk1 = 0; kk1 < L; kk1++) + int i1 = -1;// in C version, index starts from zero, not from one + int i2 = -1; + int L1 = sequence[0].size(); + int L2 = sequence[1].size(); + int L = min(L1, L2);// Get positions for aligned residues + for (int kk1 = 0; kk1 < L; kk1++) + { + if (sequence[0][kk1] != '-') + i1++; + if (sequence[1][kk1] != '-') { - if (sequence[0][kk1] != '-') - i1++; - if (sequence[1][kk1] != '-') - { - i2++; - if (i2 >= ylen || i1 >= xlen) kk1 = L; - else if (sequence[0][kk1] != '-') invmap[i2] = i1; - } + i2++; + if (i2 >= ylen || i1 >= xlen) kk1 = L; + else if (sequence[0][kk1] != '-') invmap[i2] = i1; } + } - //--------------- 2. Align proteins from original alignment - double prevD0_MIN = D0_MIN;// stored for later use - int prevLnorm = Lnorm; - double prevd0 = d0; - TM_ali = standard_TMscore(r1, r2, xtm, ytm, xt, xa, ya, - xlen, ylen, invmap, L_ali, rmsd_ali, D0_MIN, Lnorm, d0, - d0_search, score_d8, t, u, mol_type); - D0_MIN = prevD0_MIN; - Lnorm = prevLnorm; - d0 = prevd0; + //--------------- 2. Align proteins from original alignment + double prevD0_MIN = D0_MIN;// stored for later use + int prevLnorm = Lnorm; + double prevd0 = d0; + TM_ali = standard_TMscore(r1, r2, xtm, ytm, xt, xa, ya, + xlen, ylen, invmap, L_ali, rmsd_ali, D0_MIN, Lnorm, d0, + d0_search, score_d8, t, u, mol_type); + D0_MIN = prevD0_MIN; + Lnorm = prevLnorm; + d0 = prevd0; - TM = detailed_search_standard(r1, r2, xtm, ytm, xt, xa, ya, - xlen, ylen, invmap, t, u, 40, 8, local_d0_search, true, Lnorm, - score_d8, d0); - if (TM > TMmax) - { - TMmax = TM; - for (i = 0; i<ylen; i++) invmap0[i] = invmap[i]; - } - // Different from get_initial, get_initial_ss and get_initial_ssplus - TM = DP_iter(r1, r2, xtm, ytm, xt, path, val, xa, ya, - xlen, ylen, t, u, invmap, 0, 2, (fast_opt)?2:30, - local_d0_search, D0_MIN, Lnorm, d0, score_d8); - if (TM>TMmax) - { - TMmax = TM; - for (i = 0; i<ylen; i++) invmap0[i] = invmap[i]; - } + TM = detailed_search_standard(r1, r2, xtm, ytm, xt, xa, ya, + xlen, ylen, invmap, t, u, 40, 8, local_d0_search, true, Lnorm, + score_d8, d0); + if (TM > TMmax) + { + TMmax = TM; + for (i = 0; i<ylen; i++) invmap0[i] = invmap[i]; + } + // Different from get_initial, get_initial_ss and get_initial_ssplus + TM = DP_iter(r1, r2, xtm, ytm, xt, path, val, xa, ya, + xlen, ylen, t, u, invmap, 0, 2, (fast_opt)?2:30, + local_d0_search, D0_MIN, Lnorm, d0, score_d8); + if (TM>TMmax) + { + TMmax = TM; + for (i = 0; i<ylen; i++) invmap0[i] = invmap[i]; } } @@ -3081,7 +3182,7 @@ int TMalign_main(double **xa, double **ya, } if(!flag) { - cout << "There is no alignment between the two proteins! " + cout << "There is no alignment between the two structures! " << "Program stop with no result!" << endl; TM1=TM2=TM3=TM4=TM5=0; return 1; @@ -3240,6 +3341,8 @@ int TMalign_main(double **xa, double **ya, int kk=0, i_old=0, j_old=0; d=0; + Liden=0; + //double SO=0; for(int k=0; k<n_ali8; k++) { for(int i=i_old; i<m1[k]; i++) @@ -3266,10 +3369,16 @@ int TMalign_main(double **xa, double **ya, d=sqrt(dist(&xt[m1[k]][0], &ya[m2[k]][0])); if(d<d0_out) seqM[kk]=':'; else seqM[kk]='.'; + //SO+=(d<3.5); kk++; i_old=m1[k]+1; j_old=m2[k]+1; } + //SO/=getmin(xlen,ylen); + //cout<<n_ali8<<'\t' + //<<rmsd0<<'\t' + //<<100.*SO<<endl; + //tail for(int i=i_old; i<xlen; i++) @@ -3342,13 +3451,14 @@ int CPalign_main(double **xa, double **ya, secx_cp[2*xlen]=0; /* fTM-align alignment */ - double TM1_cp,TM2_cp; + double TM1_cp,TM2_cp,TM4_cp; + const double Lnorm_tmp=getmin(xlen,ylen); TMalign_main(xa_cp, ya, seqx_cp, seqy, secx_cp, secy, - t0, u0, TM1_cp, TM2_cp, TM3, TM4, TM5, + t0, u0, TM1_cp, TM2_cp, TM3, TM4_cp, TM5, d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA_cp, seqyA_cp, rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, - xlen*2, ylen, sequence, Lnorm_ass, d0_scale, - 0, false, false, false, true, mol_type, -1); + xlen*2, ylen, sequence, Lnorm_tmp, d0_scale, + 0, false, true, false, true, mol_type, -1); /* delete gap in seqxA_cp */ r=0; @@ -3392,12 +3502,14 @@ int CPalign_main(double **xa, double **ya, t0, u0, TM1, TM2, TM3, TM4, TM5, d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, - xlen, ylen, sequence, Lnorm_ass, d0_scale, - 0, false, false, false, true, mol_type, -1); + xlen, ylen, sequence, Lnorm_tmp, d0_scale, + 0, false, true, false, true, mol_type, -1); - /* do not use cricular permutation of number of aligned residues is not + /* do not use circular permutation of number of aligned residues is not * larger than sequence-order dependent alignment */ - if (n_ali8>cp_aln_best) cp_point=0; + //cout<<"cp: aln="<<cp_aln_best<<"\tTM="<<TM4_cp<<endl; + //cout<<"TM: aln="<<n_ali8<<"\tTM="<<TM4<<endl; + if (n_ali8>=cp_aln_best || TM4>=TM4_cp) cp_point=0; /* prepare structure for final alignment */ seqM.clear(); @@ -3418,6 +3530,31 @@ int CPalign_main(double **xa, double **ya, seqx_cp[xlen]=0; secx_cp[xlen]=0; + /* test another round of alignment as concatenated alignment can + * inflate the number of aligned residues and TM-score. e.g. 1yadA 2duaA */ + if (cp_point!=0) + { + TMalign_main(xa_cp, ya, seqx_cp, seqy, secx_cp, secy, + t0, u0, TM1_cp, TM2_cp, TM3, TM4_cp, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA_cp, seqyA_cp, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, cp_aln_best, + xlen, ylen, sequence, Lnorm_tmp, d0_scale, + 0, false, true, false, true, mol_type, -1); + //cout<<"cp: aln="<<cp_aln_best<<"\tTM="<<TM4_cp<<endl; + if (n_ali8>=cp_aln_best || TM4>=TM4_cp) + { + cp_point=0; + for (r=0;r<xlen;r++) + { + xa_cp[r][0]=xa[r][0]; + xa_cp[r][1]=xa[r][1]; + xa_cp[r][2]=xa[r][2]; + seqx_cp[r]=seqx[r]; + secx_cp[r]=secx[r]; + } + } + } + /* full TM-align */ TMalign_main(xa_cp, ya, seqx_cp, seqy, secx_cp, secy, t0, u0, TM1, TM2, TM3, TM4, TM5, @@ -3459,3 +3596,46 @@ int CPalign_main(double **xa, double **ya, seqyA_cp.clear(); return cp_point; } + +bool output_cp(const string&xname, const string&yname, + const string &seqxA, const string &seqyA, const int outfmt_opt, + int &left_num, int &right_num, int &left_aln_num, int &right_aln_num) +{ + int r; + bool after_cp=false; + for (r=0;r<seqxA.size();r++) + { + if (seqxA[r]=='*') after_cp=true; + else + { + if (after_cp) + { + right_aln_num++; + right_num+=(seqxA[r]!='-'); + } + else + { + left_aln_num++; + left_num+=(seqxA[r]!='-'); + } + } + } + if (after_cp==false) + { + if (outfmt_opt<=0) cout<<"No CP"<<endl; + else if (outfmt_opt==1) cout<<"#No CP"<<endl; + else if (outfmt_opt==2) cout<<"@"<<xname<<'\t'<<yname<<'\t'<<"No CP"<<endl; + } + else + { + if (outfmt_opt<=0) cout<<"CP point in structure_1 alignment: "<<left_aln_num<<'/'<<right_aln_num<<'\n' + <<"CP point in structure_1: "<<left_num<<'/'<<right_num<<endl; + else if (outfmt_opt==1) + cout<<"#CP_in_aln="<<left_aln_num<<'/'<<right_aln_num + <<"\tCP_in_seq="<<left_num<<'/'<<right_num<<endl; + else if (outfmt_opt==2) cout<<"@"<<xname<<'\t'<<yname<<'\t'<<left_aln_num + <<'/'<<right_aln_num<<'\t'<<left_num<<'/'<<right_num<<endl; + } + return after_cp; +} +#endif diff --git a/modules/bindings/src/tmalign/TMscore.cpp b/modules/bindings/src/USalign/TMscore.cpp similarity index 91% rename from modules/bindings/src/tmalign/TMscore.cpp rename to modules/bindings/src/USalign/TMscore.cpp index c2ca9958a1ceabb2caf0b914aafa13bb87490635..c84d742c1f47c199bc179185e346dffcc2e987f5 100644 --- a/modules/bindings/src/tmalign/TMscore.cpp +++ b/modules/bindings/src/USalign/TMscore.cpp @@ -34,15 +34,15 @@ void print_extra_help() " -dir Perform all-against-all alignment among the list of PDB\n" " chains listed by 'chain_list' under 'chain_folder'. Note\n" " that the slash is necessary.\n" -" $ TMalign -dir chain_folder/ chain_list\n" +" $ TMscore -dir chain_folder/ chain_list\n" "\n" " -dir1 Use chain2 to search a list of PDB chains listed by 'chain1_list'\n" " under 'chain1_folder'. Note that the slash is necessary.\n" -" $ TMalign -dir1 chain1_folder/ chain1_list chain2\n" +" $ TMscore -dir1 chain1_folder/ chain1_list chain2\n" "\n" " -dir2 Use chain1 to search a list of PDB chains listed by 'chain2_list'\n" " under 'chain2_folder'\n" -" $ TMalign chain1 -dir2 chain2_folder/ chain2_list\n" +" $ TMscore chain1 -dir2 chain2_folder/ chain2_list\n" "\n" " -suffix (Only when -dir1 and/or -dir2 are set, default is empty)\n" " add file name suffix to files listed by chain1_list or chain2_list\n" @@ -106,13 +106,21 @@ void print_help(bool h_opt=false) " 2. TM-score normalized with an assigned scale d0 e.g. 5 A:\n" " $ TMscore model.pdb native.pdb -d 5\n" "\n" -" 3. TM-score normalized by a specific length, e.g. 120 AA:\n" -" $ TMscore model.pdb native.pdv -l 120\n" +" 3. TM-score normalized by a specific length, e.g. 120 residues:\n" +" $ TMscore model.pdb native.pdb -l 120\n" "\n" " 4. TM-score with superposition output, e.g. 'TM_sup.pdb':\n" " $ TMscore model.pdb native.pdb -o TM_sup.pdb\n" " To view superimposed atomic model by PyMOL:\n" " $ pymol TM_sup.pdb native.pdb\n" +"\n" +" 5. By default, this program assumes that residue pair with the same\n" +" residue index accross the two structure files are equivalent. This\n" +" often requires that the residue index in the input structures are\n" +" renumbered beforehand. Alternatively, residue equivalence can be\n" +" established by sequence alignment:\n" +" $ TMscore model.pdb native.pdb -seq\n" +"\n" <<endl; if (h_opt) print_extra_help(); @@ -253,6 +261,10 @@ int main(int argc, char *argv[]) { byresi_opt=2; } + else if ( !strcmp(argv[i],"-seq") ) + { + byresi_opt=5; + } else if ( !strcmp(argv[i],"-mirror") && i < (argc-1) ) { mirror_opt=atoi(argv[i + 1]); i++; @@ -307,8 +319,8 @@ int main(int argc, char *argv[]) PrintErrorAndQuit("Wrong value for option -d! It should be >0"); if (outfmt_opt>=2 && (a_opt || u_opt || d_opt)) PrintErrorAndQuit("-outfmt 2 cannot be used with -a, -u, -L, -d"); - if (byresi_opt>=2 && ter_opt>=2) - PrintErrorAndQuit("-byresi >=2 should be used with -ter <=1"); + if (byresi_opt>=2 && byresi_opt<=3 && ter_opt>=2) + PrintErrorAndQuit("-c should be used with -ter <=1"); if (split_opt==1 && ter_opt!=0) PrintErrorAndQuit("-split 1 should be used with -ter 0"); else if (split_opt==2 && ter_opt!=0 && ter_opt!=1) @@ -329,6 +341,11 @@ int main(int argc, char *argv[]) else if (dir2_opt.size()==0) chain2_list.push_back(yname); else file2chainlist(chain2_list, yname, dir2_opt, suffix_opt); + if (byresi_opt>=4) + cerr<<"WARNING! The residue correspondence between the two structures" + <<" are automatically established by sequence alignment. Results" + <<" may be unreliable."<<endl; + if (outfmt_opt==2) cout<<"#PDBchain1\tPDBchain2\tTM1\tTM2\t" <<"RMSD\tID1\tID2\tIDali\tL1\tL2\tLali"<<endl; @@ -447,6 +464,7 @@ int main(int argc, char *argv[]) int L_lt_d=0; double GDT_list[5]={0,0,0,0,0}; // 0.5, 1, 2, 4, 8 double maxsub=0; + TM1=TM2=TM3=TM4=TM5=0; /* entry function for structure alignment */ TMscore_main( @@ -473,9 +491,9 @@ int main(int argc, char *argv[]) n_ali8, L_ali, TM_ali, rmsd_ali, TM_0, d0_0, d0A, d0B, Lnorm_ass, d0_scale, d0a, d0u, - (m_opt?fname_matrix+chainID_list1[chain_i]:"").c_str(), + (m_opt?fname_matrix:"").c_str(), outfmt_opt, ter_opt, - (o_opt?fname_super+chainID_list1[chain_i]:"").c_str(), + (o_opt?fname_super:"").c_str(), a_opt, u_opt, d_opt, mirror_opt, L_lt_d, rmsd_d0_out, GDT_list, maxsub, split_opt, resi_vec1, resi_vec2); diff --git a/modules/bindings/src/tmalign/TMscore.h b/modules/bindings/src/USalign/TMscore.h similarity index 95% rename from modules/bindings/src/tmalign/TMscore.h rename to modules/bindings/src/USalign/TMscore.h index 445335c79e9f08561d0adef17addfeb2cff79830..90ded3c01c3d80cf1a321d74532cb4a010d2426f 100644 --- a/modules/bindings/src/tmalign/TMscore.h +++ b/modules/bindings/src/USalign/TMscore.h @@ -58,7 +58,7 @@ int score_fun8( double **xa, double **ya, int n_ali, double d, int i_ali[], } } } - //there are not enough feasible pairs, reliefe the threshold + //there are not enough feasible pairs, relieve the threshold if(n_cut<3 && n_ali>3) { inc++; @@ -130,7 +130,7 @@ int score_fun8_standard(double **xa, double **ya, int n_ali, double d, } } } - //there are not enough feasible pairs, reliefe the threshold + //there are not enough feasible pairs, relieve the threshold if (n_cut<3 && n_ali>3) { inc++; @@ -309,6 +309,7 @@ double TMscore8_search(double **r1, double **r2, double **xtm, double **ytm, return score_max; } + double TMscore8_search_standard( double **r1, double **r2, double **xtm, double **ytm, double **xt, int Lali, double t0[3], double u0[3][3], int simplify_step, int score_sum_method, @@ -353,7 +354,7 @@ double TMscore8_search_standard( double **r1, double **r2, //find the maximum score starting from local structures superposition int i_ali[kmax], n_cut; int L_frag; //fragment length - int iL_max; //maximum starting postion for the fragment + int iL_max; //maximum starting position for the fragment for (i_init = 0; i_init<n_init; i_init++) { @@ -560,7 +561,7 @@ int TMscore_main(double **xa, double **ya, /***********************/ parameter_set4search(xlen, ylen, D0_MIN, Lnorm, score_d8, d0, d0_search, dcu0); - int simplify_step = 40; //for similified search engine + int simplify_step = 40; //for simplified search engine int score_sum_method = 8; //for scoring method, whether only sum over pairs with dis<score_d8 int i; @@ -616,7 +617,7 @@ int TMscore_main(double **xa, double **ya, //*******************************************************************// // The alignment will not be changed any more in the following // //*******************************************************************// - //check if the initial alignment is generated approriately + //check if the initial alignment is generated appropriately bool flag=false; for(i=0; i<ylen; i++) { @@ -628,8 +629,8 @@ int TMscore_main(double **xa, double **ya, } if(!flag) { - cout << "There is no alignment between the two proteins!" << endl; - cout << "Program stop with no result!" << endl; + cout << "There is no alignment between the two structures! " + << "Program stop with no result!" << endl; return 1; } @@ -652,7 +653,7 @@ int TMscore_main(double **xa, double **ya, // Detailed TMscore search engine --> prepare for final TMscore // //********************************************************************// //run detailed TMscore search engine for the best alignment, and - //extract the best rotation matrix (t, u) for the best alginment + //extract the best rotation matrix (t, u) for the best alignment simplify_step=1; if (fast_opt) simplify_step=40; score_sum_method=8; diff --git a/modules/bindings/src/USalign/USalign.cpp b/modules/bindings/src/USalign/USalign.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fdd1d8b958446fbfcc5eeb331c2caba49b393212 --- /dev/null +++ b/modules/bindings/src/USalign/USalign.cpp @@ -0,0 +1,3137 @@ +/* command line argument parsing and document of US-align main program */ + +#include "MMalign.h" +#include "SOIalign.h" +#include "flexalign.h" + +using namespace std; + +void print_version() +{ + cout << +"\n" +" ********************************************************************\n" +" * US-align (Version 20220924) *\n" +" * Universal Structure Alignment of Proteins and Nucleic Acids *\n" +" * Reference: C Zhang, M Shine, AM Pyle, Y Zhang. (2022) Nat Methods*\n" +" * Please email comments and suggestions to zhang@zhanggroup.org *\n" +" ********************************************************************" + << endl; +} + +void print_extra_help() +{ + cout << +"Additional options:\n" +" -v Print the version of US-align\n" +"\n" +" -a TM-score normalized by the average length of two structures\n" +" T or F, (default F). -a does not change the final alignment.\n" +"\n" +" -fast Fast but slightly inaccurate alignment\n" +"\n" +" -dir Perform all-against-all alignment among the list of PDB\n" +" chains listed by 'chain_list' under 'chain_folder'. Note\n" +" that the slash is necessary.\n" +" $ USalign -dir chain_folder/ chain_list\n" +"\n" +" -dir1 Use chain2 to search a list of PDB chains listed by 'chain1_list'\n" +" under 'chain1_folder'. Note that the slash is necessary.\n" +" $ USalign -dir1 chain1_folder/ chain1_list chain2\n" +"\n" +" -dir2 Use chain1 to search a list of PDB chains listed by 'chain2_list'\n" +" under 'chain2_folder'\n" +" $ USalign chain1 -dir2 chain2_folder/ chain2_list\n" +"\n" +" -suffix (Only when -dir1 and/or -dir2 are set, default is empty)\n" +" add file name suffix to files listed by chain1_list or chain2_list\n" +"\n" +" -atom 4-character atom name used to represent a residue.\n" +" Default is \" C3'\" for RNA/DNA and \" CA \" for proteins\n" +" (note the spaces before and after CA).\n" +"\n" +" -split Whether to split PDB file into multiple chains\n" +" 0: treat the whole structure as one single chain\n" +" 1: treat each MODEL as a separate chain\n" +" 2: (default) treat each chain as a separate chain\n" +"\n" +" -outfmt Output format\n" +" 0: (default) full output\n" +" 1: fasta format compact output\n" +" 2: tabular format very compact output\n" +" -1: full output, but without version or citation information\n" +"\n" +" -TMcut -1: (default) do not consider TMcut\n" +" Values in [0.5,1): Do not proceed with TM-align for this\n" +" structure pair if TM-score is unlikely to reach TMcut.\n" +" TMcut is normalized as set by -a option:\n" +" -2: normalized by longer structure length\n" +" -1: normalized by shorter structure length\n" +" 0: (default, same as F) normalized by second structure\n" +" 1: same as T, normalized by average structure length\n" +"\n" +" -mirror Whether to align the mirror image of input structure\n" +" 0: (default) do not align mirrored structure\n" +" 1: align mirror of Structure_1 to origin Structure_2,\n" +" which usually requires the '-het 1' option:\n" +" $ USalign 4glu.pdb 3p9w.pdb -mirror 1 -het 1\n" +"\n" +" -het Whether to align residues marked as 'HETATM' in addition to 'ATOM '\n" +" 0: (default) only align 'ATOM ' residues\n" +" 1: align both 'ATOM ' and 'HETATM' residues\n" +" 2: align both 'ATOM ' and MSE residues\n" +"\n" +" -full Whether to show full pairwise alignment of individual chains for\n" +" -mm 2 or 4. T or F, (default F)\n" +//"\n" +//" -closeK Number of closest atoms used for sequence order independent\n" +//" initial alignment. default: 5\n" +//"\n" +//" -hinge Maximum number of hinge allowed in flexible alignment. default: 9\n" +"\n" +" -se Do not perform superposition. Useful for extracting alignment from\n" +" superposed structure pairs\n" +"\n" +" -infmt1 Input format for structure_11\n" +" -infmt2 Input format for structure_2\n" +" -1: (default) automatically detect PDB or PDBx/mmCIF format\n" +" 0: PDB format\n" +" 1: SPICKER format\n" +//" 2: xyz format\n" +" 3: PDBx/mmCIF format\n" +"\n" +"Advanced usage 1 (generate an image for a pair of superposed structures):\n" +" USalign 1cpc.pdb 1mba.pdb -o sup\n" +" pymol -c -d @sup_all_atm.pml -g sup_all_atm.png\n" +"\n" +"Advanced usage 2 (a quick search of query.pdb against I-TASSER PDB library):\n" +" wget https://zhanggroup.org/library/PDB.tar.bz2\n" +" tar -xjvf PDB.tar.bz2\n" +" USalign query.pdb -dir2 PDB/ PDB/list -suffix .pdb -outfmt 2 -fast\n" + <<endl; +} + +void print_help(bool h_opt=false) +{ + print_version(); + cout << +"\n" +"Usage: USalign PDB1.pdb PDB2.pdb [Options]\n" +"\n" +"Options:\n" +" -mol Type of molecule(s) to align.\n" +" auto: (default) align both protein and nucleic acids.\n" +" prot: only align proteins in a structure.\n" +" RNA : only align RNA and DNA in a structure.\n" +"\n" +" -mm Multimeric alignment option:\n" +" 0: (default) alignment of two monomeric structures\n" +" 1: alignment of two multi-chain oligomeric structures\n" +" 2: alignment of individual chains to an oligomeric structure\n" +" $ USalign -dir1 monomers/ list oligomer.pdb -ter 0 -mm 2\n" +" 3: alignment of circularly permuted structure\n" +" 4: alignment of multiple monomeric chains into a consensus alignment\n" +" $ USalign -dir chains/ list -suffix .pdb -mm 4\n" +" 5: fully non-sequential (fNS) alignment\n" +" 6: semi-non-sequential (sNS) alignment\n" +" To use -mm 1 or -mm 2, '-ter' option must be 0 or 1.\n" +"\n" +" -ter Number of chains to align.\n" +" 3: only align the first chain, or the first segment of the\n" +" first chain as marked by the 'TER' string in PDB file\n" +" 2: (default) only align the first chain\n" +" 1: align all chains of the first model (recommended for aligning\n" +" asymmetric units)\n" +" 0: align all chains from all models (recommended for aligning\n" +" biological assemblies, i.e. biounits)\n" +"\n" +" -TMscore Whether to perform TM-score superposition without structure-based\n" +" alignment. The same as -byresi.\n" +" 0: (default) sequence independent structure alignment\n" +" 1: superpose two structures by assuming that a pair of residues\n" +" with the same residue index are equivalent between the two\n" +" structures\n" +" 2: superpose two complex structures, assuming that a pair of\n" +" residues with the same residue index and the same chain ID\n" +" are equivalent between the two structures\n" +//" 3: (similar to TMscore '-c' option; used with -ter 0 or 1)\n" +//" align by residue index and order of chain\n" +//" 4: sequence dependent alignment: perform Needleman-Wunsch\n" +//" global sequence alignment, followed by TM-score superposition\n" +" 5: sequence dependent alignment: perform glocal sequence\n" +" alignment followed by TM-score superposition.\n" +" -byresi 5 is the same as -seq\n" +" 6: superpose two complex structures by first deriving optimal\n" +" chain mapping, followed by TM-score superposition for residues\n" +" with the same residue ID\n" +"\n" +" -I Use the final alignment specified by FASTA file 'align.txt'\n" +"\n" +" -i Use alignment specified by 'align.txt' as an initial alignment\n" +"\n" +" -m Output rotation matrix for superposition\n" +"\n" +" -d TM-score scaled by an assigned d0, e.g., '-d 3.5' reports MaxSub\n" +" score, where d0 is 3.5 Angstrom. -d does not change final alignment.\n" +"\n" +" -u TM-score normalized by an assigned length. It should be >= length\n" +" of protein to avoid TM-score >1. -u does not change final alignment.\n" +"\n" +" -o Output superposed structure1 to sup.* for PyMOL viewing.\n" +" $ USalign structure1.pdb structure2.pdb -o sup\n" +" $ pymol -d @sup.pml # C-alpha trace aligned region\n" +" $ pymol -d @sup_all.pml # C-alpha trace whole chain\n" +" $ pymol -d @sup_atm.pml # full-atom aligned region\n" +" $ pymol -d @sup_all_atm.pml # full-atom whole chain\n" +" $ pymol -d @sup_all_atm_lig.pml # full-atom with all molecules\n" +"\n" +" -rasmol Output superposed structure1 to sup.* for RasMol viewing.\n" +" $ USalign structure1.pdb structure2.pdb -rasmol sup\n" +" $ rasmol -script sup # C-alpha trace aligned region\n" +" $ rasmol -script sup_all # C-alpha trace whole chain\n" +" $ rasmol -script sup_atm # full-atom aligned region\n" +" $ rasmol -script sup_all_atm # full-atom whole chain\n" +" $ rasmol -script sup_all_atm_lig # full-atom with all molecules\n" +"\n" +//" -h Print the full help message, including additional options\n" +//"\n" +"Example usages ('gunzip' program is needed to read .gz compressed files):\n" +" USalign 101m.cif.gz 1mba.pdb # pairwise monomeric protein alignment\n" +" USalign 1qf6.cif 5yyn.pdb.gz -mol RNA # pairwise monomeric RNA alignment\n" +" USalign model.pdb native.pdb -TMscore 1 # calculate TM-score between two conformations of a monomer\n" +" USalign 4v4a.cif 4v49.cif -mm 1 -ter 1 # oligomeric alignment for asymmetic units\n" +" USalign 3ksc.pdb1 4lej.pdb1 -mm 1 -ter 0 # oligomeric alignment for biological units\n" +" USalign 1ajk.pdb.gz 2ayh.pdb.gz -mm 3 # circular permutation alignment\n" + <<endl; + + //if (h_opt) + print_extra_help(); + + exit(EXIT_SUCCESS); +} + +/* TMalign, RNAalign, CPalign, TMscore */ +int TMalign(string &xname, string &yname, const string &fname_super, + const string &fname_lign, const string &fname_matrix, + vector<string> &sequence, const double Lnorm_ass, const double d0_scale, + const bool m_opt, const int i_opt, const int o_opt, const int a_opt, + const bool u_opt, const bool d_opt, const double TMcut, + const int infmt1_opt, const int infmt2_opt, const int ter_opt, + const int split_opt, const int outfmt_opt, const bool fast_opt, + const int cp_opt, const int mirror_opt, const int het_opt, + const string &atom_opt, const string &mol_opt, const string &dir_opt, + const string &dir1_opt, const string &dir2_opt, const int byresi_opt, + const vector<string> &chain1_list, const vector<string> &chain2_list, + const bool se_opt) +{ + /* declare previously global variables */ + vector<vector<string> >PDB_lines1; // text of chain1 + vector<vector<string> >PDB_lines2; // text of chain2 + vector<int> mol_vec1; // molecule type of chain1, RNA if >0 + vector<int> mol_vec2; // molecule type of chain2, RNA if >0 + vector<string> chainID_list1; // list of chainID1 + vector<string> chainID_list2; // list of chainID2 + int i,j; // file index + int chain_i,chain_j; // chain index + int r; // residue index + int xlen, ylen; // chain length + int xchainnum,ychainnum;// number of chains in a PDB file + char *seqx, *seqy; // for the protein sequence + char *secx, *secy; // for the secondary structure + double **xa, **ya; // for input vectors xa[0...xlen-1][0..2] and + // ya[0...ylen-1][0..2], in general, + // ya is regarded as native structure + // --> superpose xa onto ya + vector<string> resi_vec1; // residue index for chain1 + vector<string> resi_vec2; // residue index for chain2 + int read_resi=byresi_opt; // whether to read residue index + if (byresi_opt==0 && o_opt) read_resi=2; + + /* loop over file names */ + for (i=0;i<chain1_list.size();i++) + { + /* parse chain 1 */ + xname=chain1_list[i]; + xchainnum=get_PDB_lines(xname, PDB_lines1, chainID_list1, + mol_vec1, ter_opt, infmt1_opt, atom_opt, split_opt, het_opt); + if (!xchainnum) + { + cerr<<"Warning! Cannot parse file: "<<xname + <<". Chain number 0."<<endl; + continue; + } + for (chain_i=0;chain_i<xchainnum;chain_i++) + { + xlen=PDB_lines1[chain_i].size(); + if (mol_opt=="RNA") mol_vec1[chain_i]=1; + else if (mol_opt=="protein") mol_vec1[chain_i]=-1; + if (!xlen) + { + cerr<<"Warning! Cannot parse file: "<<xname + <<". Chain length 0."<<endl; + continue; + } + else if (xlen<3) + { + cerr<<"Sequence is too short <3!: "<<xname<<endl; + continue; + } + NewArray(&xa, xlen, 3); + seqx = new char[xlen + 1]; + secx = new char[xlen + 1]; + xlen = read_PDB(PDB_lines1[chain_i], xa, seqx, + resi_vec1, read_resi); + if (mirror_opt) for (r=0;r<xlen;r++) xa[r][2]=-xa[r][2]; + if (mol_vec1[chain_i]>0) make_sec(seqx,xa, xlen, secx,atom_opt); + else make_sec(xa, xlen, secx); // secondary structure assignment + + for (j=(dir_opt.size()>0)*(i+1);j<chain2_list.size();j++) + { + /* parse chain 2 */ + if (PDB_lines2.size()==0) + { + yname=chain2_list[j]; + ychainnum=get_PDB_lines(yname, PDB_lines2, chainID_list2, + mol_vec2, ter_opt, infmt2_opt, atom_opt, split_opt, + het_opt); + if (!ychainnum) + { + cerr<<"Warning! Cannot parse file: "<<yname + <<". Chain number 0."<<endl; + continue; + } + } + for (chain_j=0;chain_j<ychainnum;chain_j++) + { + ylen=PDB_lines2[chain_j].size(); + if (mol_opt=="RNA") mol_vec2[chain_j]=1; + else if (mol_opt=="protein") mol_vec2[chain_j]=-1; + if (!ylen) + { + cerr<<"Warning! Cannot parse file: "<<yname + <<". Chain length 0."<<endl; + continue; + } + else if (ylen<3) + { + cerr<<"Sequence is too short <3!: "<<yname<<endl; + continue; + } + NewArray(&ya, ylen, 3); + seqy = new char[ylen + 1]; + secy = new char[ylen + 1]; + ylen = read_PDB(PDB_lines2[chain_j], ya, seqy, + resi_vec2, read_resi); + if (mol_vec2[chain_j]>0) + make_sec(seqy, ya, ylen, secy, atom_opt); + else make_sec(ya, ylen, secy); + + if (byresi_opt) extract_aln_from_resi(sequence, + seqx,seqy,resi_vec1,resi_vec2,byresi_opt); + + /* declare variable specific to this pair of TMalign */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + bool force_fast_opt=(getmin(xlen,ylen)>1500)?true:fast_opt; + + /* entry function for structure alignment */ + if (cp_opt) CPalign_main( + xa, ya, seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + i_opt, a_opt, u_opt, d_opt, force_fast_opt, + mol_vec1[chain_i]+mol_vec2[chain_j],TMcut); + else if (se_opt) + { + int *invmap = new int[ylen+1]; + u0[0][0]=u0[1][1]=u0[2][2]=1; + u0[0][1]= u0[0][2]= + u0[1][0]= u0[1][2]= + u0[2][0]= u0[2][1]= + t0[0] =t0[1] =t0[2] =0; + se_main( + xa, ya, seqx, seqy, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + i_opt, a_opt, u_opt, d_opt, + mol_vec1[chain_i]+mol_vec2[chain_j], + outfmt_opt, invmap); + if (outfmt_opt>=2) + { + Liden=L_ali=0; + int r1,r2; + for (r2=0;r2<ylen;r2++) + { + r1=invmap[r2]; + if (r1<0) continue; + L_ali+=1; + Liden+=(seqx[r1]==seqy[r2]); + } + } + delete [] invmap; + } + else TMalign_main( + xa, ya, seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + i_opt, a_opt, u_opt, d_opt, force_fast_opt, + mol_vec1[chain_i]+mol_vec2[chain_j],TMcut); + + /* print result */ + if (outfmt_opt==0) print_version(); + int left_num=0; + int right_num=0; + int left_aln_num=0; + int right_aln_num=0; + bool after_cp=false; + if (cp_opt) after_cp=output_cp( + xname.substr(dir1_opt.size()+dir_opt.size()), + yname.substr(dir2_opt.size()+dir_opt.size()), + seqxA,seqyA,outfmt_opt,left_num,right_num, + left_aln_num,right_aln_num); + output_results( + xname.substr(dir1_opt.size()+dir_opt.size()), + yname.substr(dir2_opt.size()+dir_opt.size()), + chainID_list1[chain_i], chainID_list2[chain_j], + xlen, ylen, t0, u0, TM1, TM2, TM3, TM4, TM5, + rmsd0, d0_out, seqM.c_str(), + seqxA.c_str(), seqyA.c_str(), Liden, + n_ali8, L_ali, TM_ali, rmsd_ali, TM_0, d0_0, + d0A, d0B, Lnorm_ass, d0_scale, d0a, d0u, + (m_opt?fname_matrix:"").c_str(), + outfmt_opt, ter_opt, false, split_opt, o_opt, + fname_super, i_opt, a_opt, u_opt, d_opt, mirror_opt, + resi_vec1, resi_vec2); + if (cp_opt && outfmt_opt<=0) + { + cout<<"###############\t###############\n" + <<"#Aligned atom 1\tAligned atom 2#\n"; + size_t r1=right_num; + size_t r2=0; + size_t r; + for (r=0;r<seqxA.size();r++) + { + r1+=seqxA[r]!='-'; + r2+=seqyA[r]!='-'; + if (seqxA[r]=='*') + { + cout<<"###### Circular\tPermutation ###\n"; + r1=0; + } + else if (seqxA[r]!='-' && seqyA[r]!='-') + { + cout<<PDB_lines1[chain_i][r1-1].substr(12,15)<<'\t' + <<PDB_lines2[chain_j][r2-1].substr(12,15)<<'\n'; + } + } + cout<<"###############\t###############"<<endl; + } + + /* Done! Free memory */ + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + DeleteArray(&ya, ylen); + delete [] seqy; + delete [] secy; + resi_vec2.clear(); + } // chain_j + if (chain2_list.size()>1) + { + yname.clear(); + for (chain_j=0;chain_j<ychainnum;chain_j++) + PDB_lines2[chain_j].clear(); + PDB_lines2.clear(); + chainID_list2.clear(); + mol_vec2.clear(); + } + } // j + PDB_lines1[chain_i].clear(); + DeleteArray(&xa, xlen); + delete [] seqx; + delete [] secx; + resi_vec1.clear(); + } // chain_i + xname.clear(); + PDB_lines1.clear(); + chainID_list1.clear(); + mol_vec1.clear(); + } // i + if (chain2_list.size()==1) + { + yname.clear(); + for (chain_j=0;chain_j<ychainnum;chain_j++) + PDB_lines2[chain_j].clear(); + PDB_lines2.clear(); + resi_vec2.clear(); + chainID_list2.clear(); + mol_vec2.clear(); + } + return 0; +} + +/* MMalign if more than two chains. TMalign if only one chain */ +int MMalign(const string &xname, const string &yname, + const string &fname_super, const string &fname_lign, + const string &fname_matrix, vector<string> &sequence, + const double d0_scale, const bool m_opt, const int o_opt, + const int a_opt, const bool d_opt, const bool full_opt, + const double TMcut, const int infmt1_opt, const int infmt2_opt, + const int ter_opt, const int split_opt, const int outfmt_opt, + bool fast_opt, const int mirror_opt, const int het_opt, + const string &atom_opt, const string &mol_opt, + const string &dir1_opt, const string &dir2_opt, + const vector<string> &chain1_list, const vector<string> &chain2_list, + const int byresi_opt) +{ + /* declare previously global variables */ + vector<vector<vector<double> > > xa_vec; // structure of complex1 + vector<vector<vector<double> > > ya_vec; // structure of complex2 + vector<vector<char> >seqx_vec; // sequence of complex1 + vector<vector<char> >seqy_vec; // sequence of complex2 + vector<vector<char> >secx_vec; // secondary structure of complex1 + vector<vector<char> >secy_vec; // secondary structure of complex2 + vector<int> mol_vec1; // molecule type of complex1, RNA if >0 + vector<int> mol_vec2; // molecule type of complex2, RNA if >0 + vector<string> chainID_list1; // list of chainID1 + vector<string> chainID_list2; // list of chainID2 + vector<int> xlen_vec; // length of complex1 + vector<int> ylen_vec; // length of complex2 + int i,j; // chain index + int xlen, ylen; // chain length + double **xa, **ya; // structure of single chain + char *seqx, *seqy; // for the protein sequence + char *secx, *secy; // for the secondary structure + int xlen_aa,ylen_aa; // total length of protein + int xlen_na,ylen_na; // total length of RNA/DNA + vector<string> resi_vec1; // residue index for chain1 + vector<string> resi_vec2; // residue index for chain2 + + /* parse complex */ + parse_chain_list(chain1_list, xa_vec, seqx_vec, secx_vec, mol_vec1, + xlen_vec, chainID_list1, ter_opt, split_opt, mol_opt, infmt1_opt, + atom_opt, mirror_opt, het_opt, xlen_aa, xlen_na, o_opt, resi_vec1); + if (xa_vec.size()==0) PrintErrorAndQuit("ERROR! 0 chain in complex 1"); + parse_chain_list(chain2_list, ya_vec, seqy_vec, secy_vec, mol_vec2, + ylen_vec, chainID_list2, ter_opt, split_opt, mol_opt, infmt2_opt, + atom_opt, 0, het_opt, ylen_aa, ylen_na, o_opt, resi_vec2); + if (ya_vec.size()==0) PrintErrorAndQuit("ERROR! 0 chain in complex 2"); + int len_aa=getmin(xlen_aa,ylen_aa); + int len_na=getmin(xlen_na,ylen_na); + if (a_opt) + { + len_aa=(xlen_aa+ylen_aa)/2; + len_na=(xlen_na+ylen_na)/2; + } + int i_opt=0; + if (byresi_opt) i_opt=3; + + /* perform monomer alignment if there is only one chain */ + if (xa_vec.size()==1 && ya_vec.size()==1) + { + xlen = xlen_vec[0]; + ylen = ylen_vec[0]; + seqx = new char[xlen+1]; + seqy = new char[ylen+1]; + secx = new char[xlen+1]; + secy = new char[ylen+1]; + NewArray(&xa, xlen, 3); + NewArray(&ya, ylen, 3); + copy_chain_data(xa_vec[0],seqx_vec[0],secx_vec[0], xlen,xa,seqx,secx); + copy_chain_data(ya_vec[0],seqy_vec[0],secy_vec[0], ylen,ya,seqy,secy); + + /* declare variable specific to this pair of TMalign */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + + if (byresi_opt) extract_aln_from_resi(sequence, + seqx,seqy,resi_vec1,resi_vec2,byresi_opt); + + /* entry function for structure alignment */ + TMalign_main(xa, ya, seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, 0, d0_scale, + i_opt, a_opt, false, d_opt, fast_opt, + mol_vec1[0]+mol_vec2[0],TMcut); + + /* print result */ + output_results( + xname.substr(dir1_opt.size()), + yname.substr(dir2_opt.size()), + chainID_list1[0], chainID_list2[0], + xlen, ylen, t0, u0, TM1, TM2, TM3, TM4, TM5, rmsd0, d0_out, + seqM.c_str(), seqxA.c_str(), seqyA.c_str(), Liden, + n_ali8, L_ali, TM_ali, rmsd_ali, TM_0, d0_0, d0A, d0B, + 0, d0_scale, d0a, d0u, (m_opt?fname_matrix:"").c_str(), + outfmt_opt, ter_opt, true, split_opt, o_opt, fname_super, + 0, a_opt, false, d_opt, mirror_opt, resi_vec1, resi_vec2); + + /* clean up */ + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + delete[]seqx; + delete[]seqy; + delete[]secx; + delete[]secy; + DeleteArray(&xa,xlen); + DeleteArray(&ya,ylen); + + vector<vector<vector<double> > >().swap(xa_vec); // structure of complex1 + vector<vector<vector<double> > >().swap(ya_vec); // structure of complex2 + vector<vector<char> >().swap(seqx_vec); // sequence of complex1 + vector<vector<char> >().swap(seqy_vec); // sequence of complex2 + vector<vector<char> >().swap(secx_vec); // secondary structure of complex1 + vector<vector<char> >().swap(secy_vec); // secondary structure of complex2 + mol_vec1.clear(); // molecule type of complex1, RNA if >0 + mol_vec2.clear(); // molecule type of complex2, RNA if >0 + chainID_list1.clear(); // list of chainID1 + chainID_list2.clear(); // list of chainID2 + xlen_vec.clear(); // length of complex1 + ylen_vec.clear(); // length of complex2 + return 0; + } + + /* declare TM-score tables */ + int chain1_num=xa_vec.size(); + int chain2_num=ya_vec.size(); + vector<string> tmp_str_vec(chain2_num,""); + double **TMave_mat; + double **ut_mat; // rotation matrices for all-against-all alignment + int ui,uj,ut_idx; + NewArray(&TMave_mat,chain1_num,chain2_num); + NewArray(&ut_mat,chain1_num*chain2_num,4*3); + vector<vector<string> >seqxA_mat(chain1_num,tmp_str_vec); + vector<vector<string> > seqM_mat(chain1_num,tmp_str_vec); + vector<vector<string> >seqyA_mat(chain1_num,tmp_str_vec); + + double maxTMmono=-1; + int maxTMmono_i,maxTMmono_j; + + /* get all-against-all alignment */ + if (len_aa+len_na>500) fast_opt=true; + for (i=0;i<chain1_num;i++) + { + xlen=xlen_vec[i]; + if (xlen<3) + { + for (j=0;j<chain2_num;j++) TMave_mat[i][j]=-1; + continue; + } + seqx = new char[xlen+1]; + secx = new char[xlen+1]; + NewArray(&xa, xlen, 3); + copy_chain_data(xa_vec[i],seqx_vec[i],secx_vec[i], + xlen,xa,seqx,secx); + + for (j=0;j<chain2_num;j++) + { + ut_idx=i*chain2_num+j; + for (ui=0;ui<4;ui++) + for (uj=0;uj<3;uj++) ut_mat[ut_idx][ui*3+uj]=0; + ut_mat[ut_idx][0]=1; + ut_mat[ut_idx][4]=1; + ut_mat[ut_idx][8]=1; + + if (mol_vec1[i]*mol_vec2[j]<0) //no protein-RNA alignment + { + TMave_mat[i][j]=-1; + continue; + } + + ylen=ylen_vec[j]; + if (ylen<3) + { + TMave_mat[i][j]=-1; + continue; + } + seqy = new char[ylen+1]; + secy = new char[ylen+1]; + NewArray(&ya, ylen, 3); + copy_chain_data(ya_vec[j],seqy_vec[j],secy_vec[j], + ylen,ya,seqy,secy); + + /* declare variable specific to this pair of TMalign */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + + int Lnorm_tmp=len_aa; + if (mol_vec1[i]+mol_vec2[j]>0) Lnorm_tmp=len_na; + + if (byresi_opt) + { + int total_aln=extract_aln_from_resi(sequence, + seqx,seqy,resi_vec1,resi_vec2,xlen_vec,ylen_vec, i, j); + seqxA_mat[i][j]=sequence[0]; + seqyA_mat[i][j]=sequence[1]; + if (total_aln>xlen+ylen-3) + { + for (ui=0;ui<3;ui++) for (uj=0;uj<3;uj++) + ut_mat[ut_idx][ui*3+uj]=(ui==uj)?1:0; + for (uj=0;uj<3;uj++) ut_mat[ut_idx][9+uj]=0; + TMave_mat[i][j]=0; + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + + delete[]seqy; + delete[]secy; + DeleteArray(&ya,ylen); + continue; + } + } + + /* entry function for structure alignment */ + TMalign_main(xa, ya, seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_tmp, d0_scale, + i_opt, false, true, false, fast_opt, + mol_vec1[i]+mol_vec2[j],TMcut); + + /* store result */ + for (ui=0;ui<3;ui++) + for (uj=0;uj<3;uj++) ut_mat[ut_idx][ui*3+uj]=u0[ui][uj]; + for (uj=0;uj<3;uj++) ut_mat[ut_idx][9+uj]=t0[uj]; + seqxA_mat[i][j]=seqxA; + seqyA_mat[i][j]=seqyA; + TMave_mat[i][j]=TM4*Lnorm_tmp; + if (TMave_mat[i][j]>maxTMmono) + { + maxTMmono=TMave_mat[i][j]; + maxTMmono_i=i; + maxTMmono_j=j; + } + + /* clean up */ + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + + delete[]seqy; + delete[]secy; + DeleteArray(&ya,ylen); + } + + delete[]seqx; + delete[]secx; + DeleteArray(&xa,xlen); + } + + /* calculate initial chain-chain assignment */ + int *assign1_list; // value is index of assigned chain2 + int *assign2_list; // value is index of assigned chain1 + assign1_list=new int[chain1_num]; + assign2_list=new int[chain2_num]; + double total_score=enhanced_greedy_search(TMave_mat, assign1_list, + assign2_list, chain1_num, chain2_num); + if (total_score<=0) PrintErrorAndQuit("ERROR! No assignable chain"); + + /* refine alignment for large oligomers */ + int aln_chain_num=count_assign_pair(assign1_list,chain1_num); + bool is_oligomer=(aln_chain_num>=3); + if (aln_chain_num==2) // dimer alignment + { + int na_chain_num1,na_chain_num2,aa_chain_num1,aa_chain_num2; + count_na_aa_chain_num(na_chain_num1,aa_chain_num1,mol_vec1); + count_na_aa_chain_num(na_chain_num2,aa_chain_num2,mol_vec2); + + /* align protein-RNA hybrid dimer to another hybrid dimer */ + if (na_chain_num1==1 && na_chain_num2==1 && + aa_chain_num1==1 && aa_chain_num2==1) is_oligomer=false; + /* align pure protein dimer or pure RNA dimer */ + else if ((getmin(na_chain_num1,na_chain_num2)==0 && + aa_chain_num1==2 && aa_chain_num2==2) || + (getmin(aa_chain_num1,aa_chain_num2)==0 && + na_chain_num1==2 && na_chain_num2==2)) + { + adjust_dimer_assignment(xa_vec,ya_vec,xlen_vec,ylen_vec,mol_vec1, + mol_vec2,assign1_list,assign2_list,seqxA_mat,seqyA_mat); + is_oligomer=false; // cannot refiner further + } + else is_oligomer=true; /* align oligomers to dimer */ + } + + if (aln_chain_num>=3 || is_oligomer) // oligomer alignment + { + /* extract centroid coordinates */ + double **xcentroids; + double **ycentroids; + NewArray(&xcentroids, chain1_num, 3); + NewArray(&ycentroids, chain2_num, 3); + double d0MM=getmin( + calculate_centroids(xa_vec, chain1_num, xcentroids), + calculate_centroids(ya_vec, chain2_num, ycentroids)); + + /* refine enhanced greedy search with centroid superposition */ + //double het_deg=check_heterooligomer(TMave_mat, chain1_num, chain2_num); + homo_refined_greedy_search(TMave_mat, assign1_list, + assign2_list, chain1_num, chain2_num, xcentroids, + ycentroids, d0MM, len_aa+len_na, ut_mat); + hetero_refined_greedy_search(TMave_mat, assign1_list, + assign2_list, chain1_num, chain2_num, xcentroids, + ycentroids, d0MM, len_aa+len_na); + + /* clean up */ + DeleteArray(&xcentroids, chain1_num); + DeleteArray(&ycentroids, chain2_num); + } + + /* store initial assignment */ + int init_pair_num=count_assign_pair(assign1_list,chain1_num); + int *assign1_init, *assign2_init; + assign1_init=new int[chain1_num]; + assign2_init=new int[chain2_num]; + double **TMave_init; + NewArray(&TMave_init,chain1_num,chain2_num); + vector<vector<string> >seqxA_init(chain1_num,tmp_str_vec); + vector<vector<string> >seqyA_init(chain1_num,tmp_str_vec); + vector<string> sequence_init; + copy_chain_assign_data(chain1_num, chain2_num, sequence_init, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, TMave_mat, + seqxA_init, seqyA_init, assign1_init, assign2_init, TMave_init); + + /* perform iterative alignment */ + double max_total_score=0; // ignore old total_score because previous + // score was from monomeric chain superpositions + int max_iter=5-(int)((len_aa+len_na)/200); + if (max_iter<2) max_iter=2; + if (byresi_opt==0) MMalign_iter(max_total_score, max_iter, xa_vec, ya_vec, + seqx_vec, seqy_vec, secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, + ylen_vec, xa, ya, seqx, seqy, secx, secy, len_aa, len_na, chain1_num, + chain2_num, TMave_mat, seqxA_mat, seqyA_mat, assign1_list, assign2_list, + sequence, d0_scale, fast_opt); + + /* sometime MMalign_iter is even worse than monomer alignment */ + if (byresi_opt==0 && max_total_score<maxTMmono) + { + copy_chain_assign_data(chain1_num, chain2_num, sequence, + seqxA_init, seqyA_init, assign1_init, assign2_init, TMave_init, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, TMave_mat); + for (i=0;i<chain1_num;i++) + { + if (i!=maxTMmono_i) assign1_list[i]=-1; + else assign1_list[i]=maxTMmono_j; + } + for (j=0;j<chain2_num;j++) + { + if (j!=maxTMmono_j) assign2_list[j]=-1; + else assign2_list[j]=maxTMmono_i; + } + sequence[0]=seqxA_mat[maxTMmono_i][maxTMmono_j]; + sequence[1]=seqyA_mat[maxTMmono_i][maxTMmono_j]; + max_total_score=maxTMmono; + MMalign_iter(max_total_score, max_iter, xa_vec, ya_vec, seqx_vec, seqy_vec, + secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, + xa, ya, seqx, seqy, secx, secy, len_aa, len_na, chain1_num, chain2_num, + TMave_mat, seqxA_mat, seqyA_mat, assign1_list, assign2_list, sequence, + d0_scale, fast_opt); + } + + /* perform cross chain alignment + * in some cases, this leads to dramatic improvement, esp for homodimer */ + int iter_pair_num=count_assign_pair(assign1_list,chain1_num); + if (iter_pair_num>=init_pair_num) copy_chain_assign_data( + chain1_num, chain2_num, sequence_init, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, TMave_mat, + seqxA_init, seqyA_init, assign1_init, assign2_init, TMave_init); + double max_total_score_cross=max_total_score; + if (byresi_opt==0 && len_aa+len_na<10000) + { + MMalign_dimer(max_total_score_cross, xa_vec, ya_vec, seqx_vec, seqy_vec, + secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, + xa, ya, seqx, seqy, secx, secy, len_aa, len_na, chain1_num, chain2_num, + TMave_init, seqxA_init, seqyA_init, assign1_init, assign2_init, + sequence_init, d0_scale, fast_opt); + if (max_total_score_cross>max_total_score) + { + max_total_score=max_total_score_cross; + copy_chain_assign_data(chain1_num, chain2_num, sequence, + seqxA_init, seqyA_init, assign1_init, assign2_init, TMave_init, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, TMave_mat); + } + } + + /* final alignment */ + if (outfmt_opt==0) print_version(); + MMalign_final(xname.substr(dir1_opt.size()), yname.substr(dir2_opt.size()), + chainID_list1, chainID_list2, + fname_super, fname_lign, fname_matrix, + xa_vec, ya_vec, seqx_vec, seqy_vec, + secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, + xa, ya, seqx, seqy, secx, secy, len_aa, len_na, + chain1_num, chain2_num, TMave_mat, + seqxA_mat, seqM_mat, seqyA_mat, assign1_list, assign2_list, sequence, + d0_scale, m_opt, o_opt, outfmt_opt, ter_opt, split_opt, + a_opt, d_opt, fast_opt, full_opt, mirror_opt, resi_vec1, resi_vec2); + + /* clean up everything */ + delete [] assign1_list; + delete [] assign2_list; + DeleteArray(&TMave_mat,chain1_num); + DeleteArray(&ut_mat, chain1_num*chain2_num); + vector<vector<string> >().swap(seqxA_mat); + vector<vector<string> >().swap(seqM_mat); + vector<vector<string> >().swap(seqyA_mat); + vector<string>().swap(tmp_str_vec); + + delete [] assign1_init; + delete [] assign2_init; + DeleteArray(&TMave_init,chain1_num); + vector<vector<string> >().swap(seqxA_init); + vector<vector<string> >().swap(seqyA_init); + + vector<vector<vector<double> > >().swap(xa_vec); // structure of complex1 + vector<vector<vector<double> > >().swap(ya_vec); // structure of complex2 + vector<vector<char> >().swap(seqx_vec); // sequence of complex1 + vector<vector<char> >().swap(seqy_vec); // sequence of complex2 + vector<vector<char> >().swap(secx_vec); // secondary structure of complex1 + vector<vector<char> >().swap(secy_vec); // secondary structure of complex2 + mol_vec1.clear(); // molecule type of complex1, RNA if >0 + mol_vec2.clear(); // molecule type of complex2, RNA if >0 + vector<string>().swap(chainID_list1); // list of chainID1 + vector<string>().swap(chainID_list2); // list of chainID2 + xlen_vec.clear(); // length of complex1 + ylen_vec.clear(); // length of complex2 + vector<string> ().swap(resi_vec1); // residue index for chain1 + vector<string> ().swap(resi_vec2); // residue index for chain2 + return 1; +} + + +/* alignment individual chains to a complex. */ +int MMdock(const string &xname, const string &yname, const string &fname_super, + const string &fname_matrix, vector<string> &sequence, const double Lnorm_ass, + const double d0_scale, const bool m_opt, const int o_opt, + const int a_opt, const bool u_opt, const bool d_opt, + const double TMcut, const int infmt1_opt, const int infmt2_opt, + const int ter_opt, const int split_opt, const int outfmt_opt, + bool fast_opt, const int mirror_opt, const int het_opt, + const string &atom_opt, const string &mol_opt, + const string &dir1_opt, const string &dir2_opt, + const vector<string> &chain1_list, const vector<string> &chain2_list) +{ + /* declare previously global variables */ + vector<vector<vector<double> > > xa_vec; // structure of complex1 + vector<vector<vector<double> > > ya_vec; // structure of complex2 + vector<vector<char> >seqx_vec; // sequence of complex1 + vector<vector<char> >seqy_vec; // sequence of complex2 + vector<vector<char> >secx_vec; // secondary structure of complex1 + vector<vector<char> >secy_vec; // secondary structure of complex2 + vector<int> mol_vec1; // molecule type of complex1, RNA if >0 + vector<int> mol_vec2; // molecule type of complex2, RNA if >0 + vector<string> chainID_list1; // list of chainID1 + vector<string> chainID_list2; // list of chainID2 + vector<int> xlen_vec; // length of complex1 + vector<int> ylen_vec; // length of complex2 + int i,j; // chain index + int xlen, ylen; // chain length + double **xa, **ya; // structure of single chain + char *seqx, *seqy; // for the protein sequence + char *secx, *secy; // for the secondary structure + int xlen_aa,ylen_aa; // total length of protein + int xlen_na,ylen_na; // total length of RNA/DNA + vector<string> resi_vec1; // residue index for chain1 + vector<string> resi_vec2; // residue index for chain2 + + /* parse complex */ + parse_chain_list(chain1_list, xa_vec, seqx_vec, secx_vec, mol_vec1, + xlen_vec, chainID_list1, ter_opt, split_opt, mol_opt, infmt1_opt, + atom_opt, mirror_opt, het_opt, xlen_aa, xlen_na, o_opt, resi_vec1); + if (xa_vec.size()==0) PrintErrorAndQuit("ERROR! 0 individual chain"); + parse_chain_list(chain2_list, ya_vec, seqy_vec, secy_vec, mol_vec2, + ylen_vec, chainID_list2, ter_opt, split_opt, mol_opt, infmt2_opt, + atom_opt, 0, het_opt, ylen_aa, ylen_na, o_opt, resi_vec2); + if (xa_vec.size()>ya_vec.size()) PrintErrorAndQuit( + "ERROR! more individual chains to align than number of chains in complex template"); + int len_aa=getmin(xlen_aa,ylen_aa); + int len_na=getmin(xlen_na,ylen_na); + if (a_opt) + { + len_aa=(xlen_aa+ylen_aa)/2; + len_na=(xlen_na+ylen_na)/2; + } + + /* perform monomer alignment if there is only one chain */ + if (xa_vec.size()==1 && ya_vec.size()==1) + { + xlen = xlen_vec[0]; + ylen = ylen_vec[0]; + seqx = new char[xlen+1]; + seqy = new char[ylen+1]; + secx = new char[xlen+1]; + secy = new char[ylen+1]; + NewArray(&xa, xlen, 3); + NewArray(&ya, ylen, 3); + copy_chain_data(xa_vec[0],seqx_vec[0],secx_vec[0], xlen,xa,seqx,secx); + copy_chain_data(ya_vec[0],seqy_vec[0],secy_vec[0], ylen,ya,seqy,secy); + + /* declare variable specific to this pair of TMalign */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + + /* entry function for structure alignment */ + TMalign_main(xa, ya, seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + 0, a_opt, u_opt, d_opt, fast_opt, + mol_vec1[0]+mol_vec2[0],TMcut); + + /* print result */ + output_results( + xname.substr(dir1_opt.size()), + yname.substr(dir2_opt.size()), + chainID_list1[0], chainID_list2[0], + xlen, ylen, t0, u0, TM1, TM2, TM3, TM4, TM5, rmsd0, d0_out, + seqM.c_str(), seqxA.c_str(), seqyA.c_str(), Liden, + n_ali8, L_ali, TM_ali, rmsd_ali, TM_0, d0_0, d0A, d0B, + Lnorm_ass, d0_scale, d0a, d0u, (m_opt?fname_matrix:"").c_str(), + (outfmt_opt==2?outfmt_opt:3), ter_opt, true, split_opt, o_opt, fname_super, + 0, a_opt, false, d_opt, mirror_opt, resi_vec1, resi_vec2); + if (outfmt_opt==2) printf("%s%s\t%s%s\t%.4f\n", + xname.substr(dir1_opt.size()).c_str(), chainID_list1[0].c_str(), + yname.substr(dir2_opt.size()).c_str(), chainID_list2[0].c_str(), + sqrt((TM1*TM1+TM2*TM2)/2)); + + /* clean up */ + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + delete[]seqx; + delete[]seqy; + delete[]secx; + delete[]secy; + DeleteArray(&xa,xlen); + DeleteArray(&ya,ylen); + + vector<vector<vector<double> > >().swap(xa_vec); // structure of complex1 + vector<vector<vector<double> > >().swap(ya_vec); // structure of complex2 + vector<vector<char> >().swap(seqx_vec); // sequence of complex1 + vector<vector<char> >().swap(seqy_vec); // sequence of complex2 + vector<vector<char> >().swap(secx_vec); // secondary structure of complex1 + vector<vector<char> >().swap(secy_vec); // secondary structure of complex2 + mol_vec1.clear(); // molecule type of complex1, RNA if >0 + mol_vec2.clear(); // molecule type of complex2, RNA if >0 + chainID_list1.clear(); // list of chainID1 + chainID_list2.clear(); // list of chainID2 + xlen_vec.clear(); // length of complex1 + ylen_vec.clear(); // length of complex2 + return 0; + } + + /* declare TM-score tables */ + int chain1_num=xa_vec.size(); + int chain2_num=ya_vec.size(); + vector<string> tmp_str_vec(chain2_num,""); + double **TMave_mat; + NewArray(&TMave_mat,chain1_num,chain2_num); + vector<vector<string> >seqxA_mat(chain1_num,tmp_str_vec); + vector<vector<string> > seqM_mat(chain1_num,tmp_str_vec); + vector<vector<string> >seqyA_mat(chain1_num,tmp_str_vec); + + /* trimComplex */ + vector<vector<vector<double> > > ya_trim_vec; // structure of complex2 + vector<vector<char> >seqy_trim_vec; // sequence of complex2 + vector<vector<char> >secy_trim_vec; // secondary structure of complex2 + vector<int> ylen_trim_vec; // length of complex2 + int Lchain_aa_max1=0; + int Lchain_na_max1=0; + for (i=0;i<chain1_num;i++) + { + xlen=xlen_vec[i]; + if (mol_vec1[i]>0 && xlen>Lchain_na_max1) Lchain_na_max1=xlen; + else if (mol_vec1[i]<=0 && xlen>Lchain_aa_max1) Lchain_aa_max1=xlen; + } + int trim_chain_count=trimComplex(ya_trim_vec,seqy_trim_vec, + secy_trim_vec,ylen_trim_vec,ya_vec,seqy_vec,secy_vec,ylen_vec, + mol_vec2,Lchain_aa_max1,Lchain_na_max1); + int ylen_trim; // chain length + double **ya_trim; // structure of single chain + char *seqy_trim; // for the protein sequence + char *secy_trim; // for the secondary structure + double **xt; + + /* get all-against-all alignment */ + if (len_aa+len_na>500) fast_opt=true; + for (i=0;i<chain1_num;i++) + { + xlen=xlen_vec[i]; + if (xlen<3) + { + for (j=0;j<chain2_num;j++) TMave_mat[i][j]=-1; + continue; + } + seqx = new char[xlen+1]; + secx = new char[xlen+1]; + NewArray(&xa, xlen, 3); + copy_chain_data(xa_vec[i],seqx_vec[i],secx_vec[i], + xlen,xa,seqx,secx); + + for (j=0;j<chain2_num;j++) + { + if (mol_vec1[i]*mol_vec2[j]<0) //no protein-RNA alignment + { + TMave_mat[i][j]=-1; + continue; + } + + ylen=ylen_vec[j]; + if (ylen<3) + { + TMave_mat[i][j]=-1; + continue; + } + seqy = new char[ylen+1]; + secy = new char[ylen+1]; + NewArray(&ya, ylen, 3); + copy_chain_data(ya_vec[j],seqy_vec[j],secy_vec[j], + ylen,ya,seqy,secy); + + /* declare variable specific to this pair of TMalign */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + + int Lnorm_tmp=len_aa; + if (mol_vec1[i]+mol_vec2[j]>0) Lnorm_tmp=len_na; + + /* entry function for structure alignment */ + if (trim_chain_count && ylen_trim_vec[j]<ylen) + { + ylen_trim = ylen_trim_vec[j]; + seqy_trim = new char[ylen_trim+1]; + secy_trim = new char[ylen_trim+1]; + NewArray(&ya_trim, ylen_trim, 3); + copy_chain_data(ya_trim_vec[j],seqy_trim_vec[j],secy_trim_vec[j], + ylen_trim,ya_trim,seqy_trim,secy_trim); + TMalign_main(xa, ya_trim, seqx, seqy_trim, secx, secy_trim, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen_trim, sequence, Lnorm_tmp, d0_scale, + 0, false, true, false, fast_opt, + mol_vec1[i]+mol_vec2[j],TMcut); + seqxA.clear(); + seqyA.clear(); + delete[]seqy_trim; + delete[]secy_trim; + DeleteArray(&ya_trim,ylen_trim); + + NewArray(&xt,xlen,3); + do_rotation(xa, xt, xlen, t0, u0); + int *invmap = new int[ylen+1]; + se_main(xt, ya, seqx, seqy, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_tmp, d0_scale, + 0, false, 2, false, mol_vec1[i]+mol_vec2[j], 1, invmap); + delete[]invmap; + + if (sequence.size()<2) sequence.push_back(""); + if (sequence.size()<2) sequence.push_back(""); + sequence[0]=seqxA; + sequence[1]=seqyA; + TMalign_main(xt, ya, seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_tmp, d0_scale, + 2, false, true, false, fast_opt, + mol_vec1[i]+mol_vec2[j],TMcut); + DeleteArray(&xt, xlen); + } + else + { + TMalign_main(xa, ya, seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_tmp, d0_scale, + 0, false, true, false, fast_opt, + mol_vec1[i]+mol_vec2[j],TMcut); + } + + /* store result */ + seqxA_mat[i][j]=seqxA; + seqyA_mat[i][j]=seqyA; + TMave_mat[i][j]=TM4*Lnorm_tmp; + + /* clean up */ + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + + delete[]seqy; + delete[]secy; + DeleteArray(&ya,ylen); + } + + delete[]seqx; + delete[]secx; + DeleteArray(&xa,xlen); + } + vector<vector<vector<double> > >().swap(ya_trim_vec); + vector<vector<char> >().swap(seqy_trim_vec); + vector<vector<char> >().swap(secy_trim_vec); + vector<int> ().swap(ylen_trim_vec); + + /* calculate initial chain-chain assignment */ + int *assign1_list; // value is index of assigned chain2 + int *assign2_list; // value is index of assigned chain1 + assign1_list=new int[chain1_num]; + assign2_list=new int[chain2_num]; + enhanced_greedy_search(TMave_mat, assign1_list, + assign2_list, chain1_num, chain2_num); + + /* final alignment */ + if (outfmt_opt==0) print_version(); + double **ut_mat; // rotation matrices for all-against-all alignment + NewArray(&ut_mat,chain1_num,4*3); + int ui,uj; + vector<string>xname_vec; + vector<string>yname_vec; + vector<double>TM_vec; + for (i=0;i<chain1_num;i++) + { + j=assign1_list[i]; + xname_vec.push_back(xname+chainID_list1[i]); + if (j<0) + { + cerr<<"Warning! "<<chainID_list1[i]<<" cannot be alighed"<<endl; + for (ui=0;ui<3;ui++) + { + for (uj=0;uj<4;uj++) ut_mat[i][ui*3+uj]=0; + ut_mat[i][ui*3+ui]=1; + } + yname_vec.push_back(yname); + continue; + } + yname_vec.push_back(yname+chainID_list2[j]); + + xlen =xlen_vec[i]; + seqx = new char[xlen+1]; + secx = new char[xlen+1]; + NewArray(&xa, xlen, 3); + copy_chain_data(xa_vec[i],seqx_vec[i],secx_vec[i], xlen,xa,seqx,secx); + + ylen =ylen_vec[j]; + seqy = new char[ylen+1]; + secy = new char[ylen+1]; + NewArray(&ya, ylen, 3); + copy_chain_data(ya_vec[j],seqy_vec[j],secy_vec[j], ylen,ya,seqy,secy); + + /* declare variable specific to this pair of TMalign */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + + int c; + for (c=0; c<sequence.size(); c++) sequence[c].clear(); + sequence.clear(); + sequence.push_back(seqxA_mat[i][j]); + sequence.push_back(seqyA_mat[i][j]); + + /* entry function for structure alignment */ + TMalign_main(xa, ya, seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + 3, a_opt, u_opt, d_opt, fast_opt, + mol_vec1[i]+mol_vec2[j]); + + for (ui=0;ui<3;ui++) for (uj=0;uj<3;uj++) ut_mat[i][ui*3+uj]=u0[ui][uj]; + for (uj=0;uj<3;uj++) ut_mat[i][9+uj]=t0[uj]; + + TM_vec.push_back(TM1); + TM_vec.push_back(TM2); + + if (outfmt_opt<2) output_results( + xname.c_str(), yname.c_str(), + chainID_list1[i], chainID_list2[j], + xlen, ylen, t0, u0, TM1, TM2, TM3, TM4, TM5, + rmsd0, d0_out, seqM.c_str(), + seqxA.c_str(), seqyA.c_str(), Liden, + n_ali8, L_ali, TM_ali, rmsd_ali, TM_0, d0_0, + d0A, d0B, Lnorm_ass, d0_scale, d0a, d0u, + "", outfmt_opt, ter_opt, false, split_opt, + false, "",//o_opt, fname_super+chainID_list1[i], + false, a_opt, u_opt, d_opt, mirror_opt, + resi_vec1, resi_vec2); + + /* clean up */ + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + + delete[]seqy; + delete[]secy; + DeleteArray(&ya,ylen); + + delete[]seqx; + delete[]secx; + DeleteArray(&xa,xlen); + } + if (outfmt_opt==2) + { + double TM=0; + for (i=0;i<TM_vec.size();i++) TM+=TM_vec[i]*TM_vec[i]; + TM=sqrt(TM/TM_vec.size()); + string query_name=xname; + string template_name=yname; + for (i=0;i<chain1_num;i++) + { + j=assign1_list[i]; + if (j<0) continue; + query_name +=chainID_list1[i]; + template_name+=chainID_list2[j]; + } + printf("%s\t%s\t%.4f\n",query_name.c_str(),template_name.c_str(),TM); + query_name.clear(); + template_name.clear(); + } + + if (m_opt) output_dock_rotation_matrix(fname_matrix.c_str(), + xname_vec,yname_vec, ut_mat, assign1_list); + + if (o_opt) output_dock(chain1_list, ter_opt, split_opt, infmt1_opt, + atom_opt, mirror_opt, ut_mat, fname_super); + + + /* clean up everything */ + vector<double>().swap(TM_vec); + vector<string>().swap(xname_vec); + vector<string>().swap(yname_vec); + delete [] assign1_list; + delete [] assign2_list; + DeleteArray(&TMave_mat,chain1_num); + DeleteArray(&ut_mat, chain1_num); + vector<vector<string> >().swap(seqxA_mat); + vector<vector<string> >().swap(seqM_mat); + vector<vector<string> >().swap(seqyA_mat); + vector<string>().swap(tmp_str_vec); + + vector<vector<vector<double> > >().swap(xa_vec); // structure of complex1 + vector<vector<vector<double> > >().swap(ya_vec); // structure of complex2 + vector<vector<char> >().swap(seqx_vec); // sequence of complex1 + vector<vector<char> >().swap(seqy_vec); // sequence of complex2 + vector<vector<char> >().swap(secx_vec); // secondary structure of complex1 + vector<vector<char> >().swap(secy_vec); // secondary structure of complex2 + mol_vec1.clear(); // molecule type of complex1, RNA if >0 + mol_vec2.clear(); // molecule type of complex2, RNA if >0 + vector<string>().swap(chainID_list1); // list of chainID1 + vector<string>().swap(chainID_list2); // list of chainID2 + xlen_vec.clear(); // length of complex1 + ylen_vec.clear(); // length of complex2 + return 1; +} + +int mTMalign(string &xname, string &yname, const string &fname_super, + const string &fname_matrix, + vector<string> &sequence, double Lnorm_ass, const double d0_scale, + const bool m_opt, const int i_opt, const int o_opt, const int a_opt, + bool u_opt, const bool d_opt, const bool full_opt, const double TMcut, + const int infmt_opt, const int ter_opt, + const int split_opt, const int outfmt_opt, bool fast_opt, + const int het_opt, + const string &atom_opt, const string &mol_opt, const string &dir_opt, + const int byresi_opt, + const vector<string> &chain_list) +{ + /* declare previously global variables */ + vector<vector<vector<double> > >a_vec; // atomic structure + vector<vector<vector<double> > >ua_vec; // unchanged atomic structure + vector<vector<char> >seq_vec; // sequence of complex + vector<vector<char> >sec_vec; // secondary structure of complex + vector<int> mol_vec; // molecule type of complex1, RNA if >0 + vector<string> chainID_list; // list of chainID + vector<int> len_vec; // length of complex + int i,j; // chain index + int xlen, ylen; // chain length + double **xa, **ya; // structure of single chain + char *seqx, *seqy; // for the protein sequence + char *secx, *secy; // for the secondary structure + int len_aa,len_na; // total length of protein and RNA/DNA + vector<string> resi_vec; // residue index for chain + + /* parse chain list */ + parse_chain_list(chain_list, a_vec, seq_vec, sec_vec, mol_vec, + len_vec, chainID_list, ter_opt, split_opt, mol_opt, infmt_opt, + atom_opt, false, het_opt, len_aa, len_na, o_opt, resi_vec); + int chain_num=a_vec.size(); + if (chain_num<=1) PrintErrorAndQuit("ERROR! <2 chains for multiple alignment"); + if (m_opt||o_opt) for (i=0;i<chain_num;i++) ua_vec.push_back(a_vec[i]); + int mol_type=0; + int total_len=0; + xlen=0; + for (i=0; i<chain_num; i++) + { + if (len_vec[i]>xlen) xlen=len_vec[i]; + total_len+=len_vec[i]; + mol_type+=mol_vec[i]; + } + if (!u_opt) Lnorm_ass=total_len/chain_num; + u_opt=true; + total_len-=xlen; + if (total_len>750) fast_opt=true; + + /* get all-against-all alignment */ + double **TMave_mat; + NewArray(&TMave_mat,chain_num,chain_num); + vector<string> tmp_str_vec(chain_num,""); + vector<vector<string> >seqxA_mat(chain_num,tmp_str_vec); + vector<vector<string> >seqyA_mat(chain_num,tmp_str_vec); + for (i=0;i<chain_num;i++) for (j=0;j<chain_num;j++) TMave_mat[i][j]=0; + for (i=0;i<chain_num;i++) + { + xlen=len_vec[i]; + if (xlen<3) continue; + seqx = new char[xlen+1]; + secx = new char[xlen+1]; + NewArray(&xa, xlen, 3); + copy_chain_data(a_vec[i],seq_vec[i],sec_vec[i],xlen,xa,seqx,secx); + seqxA_mat[i][i]=seqyA_mat[i][i]=(string)(seqx); + for (j=i+1;j<chain_num;j++) + { + ylen=len_vec[j]; + if (ylen<3) continue; + seqy = new char[ylen+1]; + secy = new char[ylen+1]; + NewArray(&ya, ylen, 3); + copy_chain_data(a_vec[j],seq_vec[j],sec_vec[j],ylen,ya,seqy,secy); + + /* declare variable specific to this pair of TMalign */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + + /* entry function for structure alignment */ + TMalign_main(xa, ya, seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + 0, false, u_opt, false, fast_opt, + mol_type,TMcut); + + /* store result */ + TMave_mat[i][j]=TMave_mat[j][i]=TM4; + seqxA_mat[i][j]=seqyA_mat[j][i]=seqxA; + seqyA_mat[i][j]=seqxA_mat[j][i]=seqyA; + //cout<<chain_list[i]<<':'<<chainID_list[i] + //<<chain_list[j]<<':'<<chainID_list[j]<<"\tTM4="<<TM4<<endl; + if (full_opt) output_results( + chain_list[i],chain_list[j], chainID_list[i], chainID_list[j], + xlen, ylen, t0, u0, TM1, TM2, TM3, TM4, TM5, rmsd0, d0_out, + seqM.c_str(), seqxA.c_str(), seqyA.c_str(), Liden, + n_ali8, L_ali, TM_ali, rmsd_ali, TM_0, d0_0, d0A, d0B, + Lnorm_ass, d0_scale, d0a, d0u, "", + outfmt_opt, ter_opt, true, split_opt, o_opt, "", + 0, a_opt, false, d_opt, false, resi_vec, resi_vec); + + /* clean up */ + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + + delete[]seqy; + delete[]secy; + DeleteArray(&ya,ylen); + } + + delete[]seqx; + delete[]secx; + DeleteArray(&xa,xlen); + } + + /* representative related variables */ + int r; + int repr_idx=0; + vector<string>xname_vec; + for (i=0;i<chain_num;i++) xname_vec.push_back( + chain_list[i].substr(dir_opt.size())+chainID_list[i]); + vector<string>yname_vec; + double *TMave_list; + TMave_list = new double[chain_num]; + int *assign_list; + assign_list=new int[chain_num]; + vector<string> msa(ylen,""); // row is position along msa; column is sequence + + int compare_num; + double TM1_total, TM2_total; + double TM3_total, TM4_total, TM5_total; // for a_opt, u_opt, d_opt + double d0_0_total, TM_0_total; + double d0A_total, d0B_total, d0u_total, d0a_total; + double d0_out_total; + double rmsd0_total; + int L_ali_total; // Aligned length in standard_TMscore + double Liden_total; + double TM_ali_total, rmsd_ali_total; // TMscore and rmsd in standard_TMscore + int n_ali_total; + int n_ali8_total; + int xlen_total, ylen_total; + double TM4_total_max=0; + + int max_iter=5-(int)(total_len/200); + if (max_iter<2) max_iter=2; + int iter=0; + vector<double> TM_vec(chain_num,0); + vector<double> d0_vec(chain_num,0); + vector<double> seqID_vec(chain_num,0); + vector<vector<double> > TM_mat(chain_num,TM_vec); + vector<vector<double> > d0_mat(chain_num,d0_vec); + vector<vector<double> > seqID_mat(chain_num,seqID_vec); + for (iter=0; iter<max_iter; iter++) + { + /* select representative */ + for (j=0; j<chain_num; j++) TMave_list[j]=0; + for (i=0; i<chain_num; i++ ) + { + for (j=0; j<chain_num; j++) + { + //cout<<'\t'<<setprecision(4)<<TMave_mat[i][j]; + TMave_list[j]+=TMave_mat[i][j]; + } + //cout<<'\t'<<chain_list[i]<<':'<<chainID_list[i]<<endl; + } + repr_idx=0; + double repr_TM=0; + for (j=0; j<chain_num; j++) + { + //cout<<chain_list[j]<<'\t'<<len_vec[j]<<'\t'<<TMave_list[j]<<endl; + if (TMave_list[j]<repr_TM) continue; + repr_TM=TMave_list[j]; + repr_idx=j; + } + //cout<<"repr="<<repr_idx<<"; "<<chain_list[repr_idx]<<"; TM="<<repr_TM<<endl; + + /* superpose superpose */ + yname=chain_list[repr_idx].substr(dir_opt.size())+chainID_list[repr_idx]; + double **xt; + vector<pair<double,int> >TM_pair_vec; // TM vs chain + + for (i=0; i<chain_num; i++) assign_list[i]=-1; + assign_list[repr_idx]=repr_idx; + //ylen = len_vec[repr_idx]; + //seqy = new char[ylen+1]; + //secy = new char[ylen+1]; + //NewArray(&ya, ylen, 3); + //copy_chain_data(a_vec[repr_idx],seq_vec[repr_idx],sec_vec[repr_idx], ylen,ya,seqy,secy); + for (r=0;r<sequence.size();r++) sequence[r].clear(); sequence.clear(); + sequence.push_back(""); + sequence.push_back(""); + for (i=0;i<chain_num;i++) + { + yname_vec.push_back(yname); + xlen = len_vec[i]; + if (i==repr_idx || xlen<3) continue; + TM_pair_vec.push_back(make_pair(-TMave_mat[i][repr_idx],i)); + } + sort(TM_pair_vec.begin(),TM_pair_vec.end()); + + int tm_idx; + if (outfmt_opt<0) cout<<"#PDBchain1\tPDBchain2\tTM1\tTM2\t" + <<"RMSD\tID1\tID2\tIDali\tL1\tL2\tLali"<<endl; + for (tm_idx=0; tm_idx<TM_pair_vec.size(); tm_idx++) + { + i=TM_pair_vec[tm_idx].second; + xlen = len_vec[i]; + seqx = new char[xlen+1]; + secx = new char[xlen+1]; + NewArray(&xa, xlen, 3); + copy_chain_data(a_vec[i],seq_vec[i],sec_vec[i], xlen,xa,seqx,secx); + + double maxTM=TMave_mat[i][repr_idx]; + int maxj=repr_idx; + for (j=0;j<chain_num;j++) + { + if (i==j || assign_list[j]<0 || TMave_mat[i][j]<=maxTM) continue; + maxj=j; + maxTM=TMave_mat[i][j]; + } + j=maxj; + assign_list[i]=j; + ylen = len_vec[j]; + seqy = new char[ylen+1]; + secy = new char[ylen+1]; + NewArray(&ya, ylen, 3); + copy_chain_data(a_vec[j],seq_vec[j],sec_vec[j], ylen,ya,seqy,secy); + + sequence[0]=seqxA_mat[i][j]; + sequence[1]=seqyA_mat[i][j]; + //cout<<"tm_idx="<<tm_idx<<"\ti="<<i<<"\tj="<<j<<endl; + //cout<<"superpose "<<xname_vec[i]<<" to "<<xname_vec[j]<<endl; + + /* declare variable specific to this pair of TMalign */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + + /* entry function for structure alignment */ + TMalign_main(xa, ya, seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + 2, a_opt, u_opt, d_opt, fast_opt, mol_type); + + if (outfmt_opt<0) output_results( + xname_vec[i].c_str(), xname_vec[j].c_str(), "", "", + xlen, ylen, t0, u0, TM1, TM2, TM3, TM4, TM5, + rmsd0, d0_out, seqM.c_str(), + seqxA.c_str(), seqyA.c_str(), Liden, + n_ali8, L_ali, TM_ali, rmsd_ali, TM_0, d0_0, + d0A, d0B, Lnorm_ass, d0_scale, d0a, d0u, + "", 2,//outfmt_opt, + ter_opt, false, split_opt, + false, "",//o_opt, fname_super+chainID_list1[i], + false, a_opt, u_opt, d_opt, false, + resi_vec, resi_vec); + + NewArray(&xt,xlen,3); + do_rotation(xa, xt, xlen, t0, u0); + for (r=0;r<xlen;r++) + { + a_vec[i][r][0]=xt[r][0]; + a_vec[i][r][1]=xt[r][1]; + a_vec[i][r][2]=xt[r][2]; + } + DeleteArray(&xt, xlen); + + /* clean up */ + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + sequence[0].clear(); + sequence[1].clear(); + + delete[]seqx; + delete[]secx; + DeleteArray(&xa,xlen); + + delete[]seqy; + delete[]secy; + DeleteArray(&ya,ylen); + } + ylen = len_vec[repr_idx]; + seqy = new char[ylen+1]; + secy = new char[ylen+1]; + NewArray(&ya, ylen, 3); + copy_chain_data(a_vec[repr_idx],seq_vec[repr_idx],sec_vec[repr_idx], ylen,ya,seqy,secy); + + /* recover alignment */ + int ylen_ext=ylen; // chain length + double **ya_ext; // structure of single chain + char *seqy_ext; // for the protein sequence + char *secy_ext; // for the secondary structure + for (r=0;r<msa.size();r++) msa[r].clear(); msa.clear(); + msa.assign(ylen,""); // row is position along msa; column is sequence + vector<string> msa_ext; // row is position along msa; column is sequence + for (r=0;r<ylen;r++) msa[r]=seqy[r]; + //for (r=0;r<msa.size();r++) cout<<"["<<r<<"]\t"<<msa[r]<<endl; + //cout<<"start recover"<<endl; + assign_list[repr_idx]=0; + for (tm_idx=0; tm_idx<TM_pair_vec.size(); tm_idx++) + { + i=TM_pair_vec[tm_idx].second; + assign_list[i]=tm_idx+1; + + xlen = len_vec[i]; + seqx = new char[xlen+1]; + secx = new char[xlen+1]; + NewArray(&xa, xlen, 3); + copy_chain_data(a_vec[i],seq_vec[i],sec_vec[i], xlen,xa,seqx,secx); + + /* declare variable specific to this pair of TMalign */ + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + int *invmap = new int[ylen+1]; + + se_main(xa, ya, seqx, seqy, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + 0, a_opt, u_opt, d_opt, mol_type, 1, invmap); + + int rx=0,ry=0; + ylen_ext=seqxA.size(); + NewArray(&ya_ext, ylen_ext, 3); // structure of single chain + seqy_ext= new char[ylen_ext+1]; // for the protein sequence + secy_ext= new char[ylen_ext+1]; // for the secondary structure + string tmp_gap=""; + for (r=0;r<msa[0].size();r++) tmp_gap+='-'; + for (r=msa_ext.size();r<ylen_ext;r++) msa_ext.push_back(""); + //cout<<"x:"<<xname_vec[i]<<'\n'<<seqxA<<endl; + //cout<<"y:"<<xname_vec[repr_idx]<<'\n'<<seqyA<<endl; + for (r=0;r<ylen_ext;r++) + { + if (seqyA[r]=='-') + { + msa_ext[r]=tmp_gap+seqxA[r]; + ya_ext[r][0]=xa[rx][0]; + ya_ext[r][1]=xa[rx][1]; + ya_ext[r][2]=xa[rx][2]; + seqy_ext[r]=seqx[rx]; + secy_ext[r]=secx[rx]; + } + else + { + msa_ext[r]=msa[ry]+seqxA[r]; + ya_ext[r][0]=ya[ry][0]; + ya_ext[r][1]=ya[ry][1]; + ya_ext[r][2]=ya[ry][2]; + seqy_ext[r]=seqy[ry]; + secy_ext[r]=secy[ry]; + } + rx+=(seqxA[r]!='-'); + ry+=(seqyA[r]!='-'); + } + + /* copy ya_ext to ya */ + delete[]seqy; + delete[]secy; + DeleteArray(&ya,ylen); + + ylen=ylen_ext; + NewArray(&ya,ylen,3); + seqy = new char[ylen+1]; + secy = new char[ylen+1]; + for (r=0;r<ylen;r++) + { + ya[r][0]=ya_ext[r][0]; + ya[r][1]=ya_ext[r][1]; + ya[r][2]=ya_ext[r][2]; + seqy[r]=seqy_ext[r]; + secy[r]=secy_ext[r]; + } + for (r=0;r<ylen;r++) + { + if (r<msa.size()) msa[r]=msa_ext[r]; + else msa.push_back(msa_ext[r]); + } + //for (r=0;r<ylen_ext;r++) cout<<"["<<r<<"]\t"<<msa_ext[r]<<'\t'<<seqy[r]<<'\t' + //<<ya[r][0]<<'\t'<<ya[r][1]<<'\t'<<ya[r][2]<<'\t'<<secy[r]<<endl; + + /* clean up */ + tmp_gap.clear(); + delete[]invmap; + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + + delete[]seqx; + delete[]secx; + DeleteArray(&xa,xlen); + + delete[]seqy_ext; + delete[]secy_ext; + DeleteArray(&ya_ext,ylen_ext); + } + vector<string>().swap(msa_ext); + vector<pair<double,int> >().swap(TM_pair_vec); + for (i=0; i<chain_num; i++) + { + tm_idx=assign_list[i]; + if (tm_idx<0) continue; + seqyA_mat[i][i]=""; + for (r=0 ;r<ylen ; r++) seqyA_mat[i][i]+=msa[r][tm_idx]; + seqxA_mat[i][i]=seqyA_mat[i][i]; + //cout<<xname_vec[i]<<'\t'<<seqxA_mat[i][i]<<endl; + } + for (i=0;i<chain_num; i++) + { + if (assign_list[i]<0) continue; + string seqxA=seqxA_mat[i][i]; + for (j=0; j<chain_num; j++) + { + if (i==j || assign_list[j]<0) continue; + string seqyA=seqyA_mat[j][j]; + seqxA_mat[i][j]=seqyA_mat[i][j]=""; + for (r=0;r<ylen;r++) + { + if (seqxA[r]=='-' && seqyA[r]=='-') continue; + seqxA_mat[i][j]+=seqxA[r]; + seqyA_mat[i][j]+=seqyA[r]; + } + seqyA.clear(); + } + seqxA.clear(); + } + + /* recover statistics such as TM-score */ + compare_num=0; + TM1_total=0, TM2_total=0; + TM3_total=0, TM4_total=0, TM5_total=0; + d0_0_total=0, TM_0_total=0; + d0A_total=0, d0B_total=0, d0u_total=0, d0a_total=0; + d0_out_total=0; + rmsd0_total = 0.0; + L_ali_total=0; + Liden_total=0; + TM_ali_total=0, rmsd_ali_total=0; + n_ali_total=0; + n_ali8_total=0; + xlen_total=0, ylen_total=0; + for (i=0; i< chain_num; i++) + { + xlen=len_vec[i]; + if (xlen<3) continue; + seqx = new char[xlen+1]; + secx = new char[xlen+1]; + NewArray(&xa, xlen, 3); + copy_chain_data(a_vec[i],seq_vec[i],sec_vec[i], xlen,xa,seqx,secx); + for (j=i+1;j<chain_num;j++) + { + ylen=len_vec[j]; + if (ylen<3) continue; + compare_num++; + seqy = new char[ylen+1]; + secy = new char[ylen+1]; + NewArray(&ya, ylen, 3); + copy_chain_data(a_vec[j],seq_vec[j],sec_vec[j],ylen,ya,seqy,secy); + sequence[0]=seqxA_mat[i][j]; + sequence[1]=seqyA_mat[i][j]; + + /* declare variable specific to this pair of TMalign */ + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali=0; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + int *invmap = new int[ylen+1]; + + se_main(xa, ya, seqx, seqy, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + true, a_opt, u_opt, d_opt, mol_type, 1, invmap); + + if (xlen<=ylen) + { + xlen_total+=xlen; + ylen_total+=ylen; + TM1_total+=TM1; + TM2_total+=TM2; + d0A_total+=d0A; + d0B_total+=d0B; + } + else + { + xlen_total+=ylen; + ylen_total+=xlen; + TM1_total+=TM2; + TM2_total+=TM1; + d0A_total+=d0B; + d0B_total+=d0A; + } + TM_mat[i][j]=TM2; + TM_mat[j][i]=TM1; + d0_mat[i][j]=d0B; + d0_mat[j][i]=d0A; + seqID_mat[i][j]=1.*Liden/xlen; + seqID_mat[j][i]=1.*Liden/ylen; + + TM3_total+=TM3; + TM4_total+=TM4; + TM5_total+=TM5; + d0_0_total+=d0_0; + TM_0_total+=TM_0; + d0u_total+=d0u; + d0_out_total+=d0_out; + rmsd0_total+=rmsd0; + L_ali_total+=L_ali; // Aligned length in standard_TMscore + Liden_total+=Liden; + TM_ali_total+=TM_ali; + rmsd_ali_total+=rmsd_ali; // TMscore and rmsd in standard_TMscore + n_ali_total+=n_ali; + n_ali8_total+=n_ali8; + + /* clean up */ + delete[]invmap; + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + + delete[]seqy; + delete[]secy; + DeleteArray(&ya,ylen); + } + delete[]seqx; + delete[]secx; + DeleteArray(&xa,xlen); + } + if (TM4_total<=TM4_total_max) break; + TM4_total_max=TM4_total; + } + for (i=0;i<chain_num;i++) + { + for (j=0;j<chain_num;j++) + { + if (i==j) continue; + TM_vec[i]+=TM_mat[i][j]; + d0_vec[i]+=d0_mat[i][j]; + seqID_vec[i]+=seqID_mat[i][j]; + } + TM_vec[i]/=(chain_num-1); + d0_vec[i]/=(chain_num-1); + seqID_vec[i]/=(chain_num-1); + } + xlen_total /=compare_num; + ylen_total /=compare_num; + TM1_total /=compare_num; + TM2_total /=compare_num; + d0A_total /=compare_num; + d0B_total /=compare_num; + TM3_total /=compare_num; + TM4_total /=compare_num; + TM5_total /=compare_num; + d0_0_total /=compare_num; + TM_0_total /=compare_num; + d0u_total /=compare_num; + d0_out_total /=compare_num; + rmsd0_total /=compare_num; + L_ali_total /=compare_num; + Liden_total /=compare_num; + TM_ali_total /=compare_num; + rmsd_ali_total/=compare_num; + n_ali_total /=compare_num; + n_ali8_total /=compare_num; + xname="shorter"; + yname="longer"; + string seqM=""; + string seqxA=""; + string seqyA=""; + double t0[3]; + double u0[3][3]; + stringstream buf; + for (i=0; i<chain_num; i++) + { + if (assign_list[i]<0) continue; + buf <<">"<<xname_vec[i]<<"\tL="<<len_vec[i] + <<"\td0="<<setiosflags(ios::fixed)<<setprecision(2)<<d0_vec[i] + <<"\tseqID="<<setiosflags(ios::fixed)<<setprecision(3)<<seqID_vec[i] + <<"\tTM-score="<<setiosflags(ios::fixed)<<setprecision(5)<<TM_vec[i]; + if (i==repr_idx) buf<<"\t*"; + buf<<'\n'<<seqxA_mat[i][i]<<endl; + } + seqM=buf.str(); + seqM=seqM.substr(0,seqM.size()-1); + buf.str(string()); + //MergeAlign(seqxA_mat,seqyA_mat,repr_idx,xname_vec,chain_num,seqM); + if (outfmt_opt==0) print_version(); + output_mTMalign_results( xname,yname, "","", + xlen_total, ylen_total, t0, u0, TM1_total, TM2_total, + TM3_total, TM4_total, TM5_total, rmsd0_total, d0_out_total, + seqM.c_str(), seqxA.c_str(), seqyA.c_str(), Liden_total, + n_ali8_total, L_ali_total, TM_ali_total, rmsd_ali_total, + TM_0_total, d0_0_total, d0A_total, d0B_total, + Lnorm_ass, d0_scale, d0a_total, d0u_total, + "", outfmt_opt, ter_opt, 0, split_opt, false, + "", false, a_opt, u_opt, d_opt, false, + resi_vec, resi_vec ); + + if (m_opt || o_opt) + { + double **ut_mat; // rotation matrices for all-against-all alignment + int ui,uj; + double t[3], u[3][3]; + double rmsd; + NewArray(&ut_mat,chain_num,4*3); + for (i=0;i<chain_num;i++) + { + xlen=ylen=a_vec[i].size(); + NewArray(&xa,xlen,3); + NewArray(&ya,xlen,3); + for (r=0;r<xlen;r++) + { + xa[r][0]=ua_vec[i][r][0]; + xa[r][1]=ua_vec[i][r][1]; + xa[r][2]=ua_vec[i][r][2]; + ya[r][0]= a_vec[i][r][0]; + ya[r][1]= a_vec[i][r][1]; + ya[r][2]= a_vec[i][r][2]; + } + Kabsch(xa,ya,xlen,1,&rmsd,t,u); + for (ui=0;ui<3;ui++) for (uj=0;uj<3;uj++) ut_mat[i][ui*3+uj]=u[ui][uj]; + for (uj=0;uj<3;uj++) ut_mat[i][9+uj]=t[uj]; + DeleteArray(&xa,xlen); + DeleteArray(&ya,xlen); + } + vector<vector<vector<double> > >().swap(ua_vec); + + if (m_opt) + { + assign_list[repr_idx]=-1; + output_dock_rotation_matrix(fname_matrix.c_str(), + xname_vec,yname_vec, ut_mat, assign_list); + } + + if (o_opt) output_dock(chain_list, ter_opt, split_opt, + infmt_opt, atom_opt, false, ut_mat, fname_super); + + DeleteArray(&ut_mat,chain_num); + } + + /* clean up */ + vector<string>().swap(msa); + vector<string>().swap(tmp_str_vec); + vector<vector<string> >().swap(seqxA_mat); + vector<vector<string> >().swap(seqyA_mat); + vector<string>().swap(xname_vec); + vector<string>().swap(yname_vec); + delete[]TMave_list; + DeleteArray(&TMave_mat,chain_num); + vector<vector<vector<double> > >().swap(a_vec); // structure of complex + vector<vector<char> >().swap(seq_vec); // sequence of complex + vector<vector<char> >().swap(sec_vec); // secondary structure of complex + vector<int>().swap(mol_vec); // molecule type of complex1, RNA if >0 + vector<string>().swap(chainID_list); // list of chainID + vector<int>().swap(len_vec); // length of complex + vector<double>().swap(TM_vec); + vector<double>().swap(d0_vec); + vector<double>().swap(seqID_vec); + vector<vector<double> >().swap(TM_mat); + vector<vector<double> >().swap(d0_mat); + vector<vector<double> >().swap(seqID_mat); + return 1; +} + +/* sequence order independent alignment */ +int SOIalign(string &xname, string &yname, const string &fname_super, + const string &fname_lign, const string &fname_matrix, + vector<string> &sequence, const double Lnorm_ass, const double d0_scale, + const bool m_opt, const int i_opt, const int o_opt, const int a_opt, + const bool u_opt, const bool d_opt, const double TMcut, + const int infmt1_opt, const int infmt2_opt, const int ter_opt, + const int split_opt, const int outfmt_opt, const bool fast_opt, + const int cp_opt, const int mirror_opt, const int het_opt, + const string &atom_opt, const string &mol_opt, const string &dir_opt, + const string &dir1_opt, const string &dir2_opt, + const vector<string> &chain1_list, const vector<string> &chain2_list, + const bool se_opt, const int closeK_opt, const int mm_opt) +{ + /* declare previously global variables */ + vector<vector<string> >PDB_lines1; // text of chain1 + vector<vector<string> >PDB_lines2; // text of chain2 + vector<int> mol_vec1; // molecule type of chain1, RNA if >0 + vector<int> mol_vec2; // molecule type of chain2, RNA if >0 + vector<string> chainID_list1; // list of chainID1 + vector<string> chainID_list2; // list of chainID2 + int i,j; // file index + int chain_i,chain_j; // chain index + int r; // residue index + int xlen, ylen; // chain length + int xchainnum,ychainnum;// number of chains in a PDB file + char *seqx, *seqy; // for the protein sequence + char *secx, *secy; // for the secondary structure + int **secx_bond; // boundary of secondary structure + int **secy_bond; // boundary of secondary structure + double **xa, **ya; // for input vectors xa[0...xlen-1][0..2] and + // ya[0...ylen-1][0..2], in general, + // ya is regarded as native structure + // --> superpose xa onto ya + double **xk, **yk; // k closest residues + vector<string> resi_vec1; // residue index for chain1 + vector<string> resi_vec2; // residue index for chain2 + int read_resi=0; // whether to read residue index + if (o_opt) read_resi=2; + + /* loop over file names */ + for (i=0;i<chain1_list.size();i++) + { + /* parse chain 1 */ + xname=chain1_list[i]; + xchainnum=get_PDB_lines(xname, PDB_lines1, chainID_list1, + mol_vec1, ter_opt, infmt1_opt, atom_opt, split_opt, het_opt); + if (!xchainnum) + { + cerr<<"Warning! Cannot parse file: "<<xname + <<". Chain number 0."<<endl; + continue; + } + for (chain_i=0;chain_i<xchainnum;chain_i++) + { + xlen=PDB_lines1[chain_i].size(); + if (mol_opt=="RNA") mol_vec1[chain_i]=1; + else if (mol_opt=="protein") mol_vec1[chain_i]=-1; + if (!xlen) + { + cerr<<"Warning! Cannot parse file: "<<xname + <<". Chain length 0."<<endl; + continue; + } + else if (xlen<3) + { + cerr<<"Sequence is too short <3!: "<<xname<<endl; + continue; + } + NewArray(&xa, xlen, 3); + if (closeK_opt>=3) NewArray(&xk, xlen*closeK_opt, 3); + seqx = new char[xlen + 1]; + secx = new char[xlen + 1]; + xlen = read_PDB(PDB_lines1[chain_i], xa, seqx, + resi_vec1, read_resi); + if (mirror_opt) for (r=0;r<xlen;r++) xa[r][2]=-xa[r][2]; + if (mol_vec1[chain_i]>0) make_sec(seqx,xa, xlen, secx,atom_opt); + else make_sec(xa, xlen, secx); // secondary structure assignment + if (closeK_opt>=3) getCloseK(xa, xlen, closeK_opt, xk); + if (mm_opt==6) + { + NewArray(&secx_bond, xlen, 2); + assign_sec_bond(secx_bond, secx, xlen); + } + + for (j=(dir_opt.size()>0)*(i+1);j<chain2_list.size();j++) + { + /* parse chain 2 */ + if (PDB_lines2.size()==0) + { + yname=chain2_list[j]; + ychainnum=get_PDB_lines(yname, PDB_lines2, chainID_list2, + mol_vec2, ter_opt, infmt2_opt, atom_opt, split_opt, + het_opt); + if (!ychainnum) + { + cerr<<"Warning! Cannot parse file: "<<yname + <<". Chain number 0."<<endl; + continue; + } + } + for (chain_j=0;chain_j<ychainnum;chain_j++) + { + ylen=PDB_lines2[chain_j].size(); + if (mol_opt=="RNA") mol_vec2[chain_j]=1; + else if (mol_opt=="protein") mol_vec2[chain_j]=-1; + if (!ylen) + { + cerr<<"Warning! Cannot parse file: "<<yname + <<". Chain length 0."<<endl; + continue; + } + else if (ylen<3) + { + cerr<<"Sequence is too short <3!: "<<yname<<endl; + continue; + } + NewArray(&ya, ylen, 3); + if (closeK_opt>=3) NewArray(&yk, ylen*closeK_opt, 3); + seqy = new char[ylen + 1]; + secy = new char[ylen + 1]; + ylen = read_PDB(PDB_lines2[chain_j], ya, seqy, + resi_vec2, read_resi); + if (mol_vec2[chain_j]>0) + make_sec(seqy, ya, ylen, secy, atom_opt); + else make_sec(ya, ylen, secy); + if (closeK_opt>=3) getCloseK(ya, ylen, closeK_opt, yk); + if (mm_opt==6) + { + NewArray(&secy_bond, ylen, 2); + assign_sec_bond(secy_bond, secy, ylen); + } + + /* declare variable specific to this pair of TMalign */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + bool force_fast_opt=(getmin(xlen,ylen)>1500)?true:fast_opt; + int *invmap = new int[ylen+1]; + double *dist_list = new double[ylen+1]; + + /* entry function for structure alignment */ + if (se_opt) + { + u0[0][0]=u0[1][1]=u0[2][2]=1; + u0[0][1]= u0[0][2]= + u0[1][0]= u0[1][2]= + u0[2][0]= u0[2][1]= + t0[0] =t0[1] =t0[2] =0; + soi_se_main( + xa, ya, seqx, seqy, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, Lnorm_ass, d0_scale, + i_opt, a_opt, u_opt, d_opt, + mol_vec1[chain_i]+mol_vec2[chain_j], + outfmt_opt, invmap, dist_list, + secx_bond, secy_bond, mm_opt); + if (outfmt_opt>=2) + { + Liden=L_ali=0; + int r1,r2; + for (r2=0;r2<ylen;r2++) + { + r1=invmap[r2]; + if (r1<0) continue; + L_ali+=1; + Liden+=(seqx[r1]==seqy[r2]); + } + } + } + else SOIalign_main(xa, ya, xk, yk, closeK_opt, + seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, invmap, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + i_opt, a_opt, u_opt, d_opt, force_fast_opt, + mol_vec1[chain_i]+mol_vec2[chain_j], dist_list, + secx_bond, secy_bond, mm_opt); + + /* print result */ + if (outfmt_opt==0) print_version(); + output_results( + xname.substr(dir1_opt.size()+dir_opt.size()), + yname.substr(dir2_opt.size()+dir_opt.size()), + chainID_list1[chain_i], chainID_list2[chain_j], + xlen, ylen, t0, u0, TM1, TM2, TM3, TM4, TM5, + rmsd0, d0_out, seqM.c_str(), + seqxA.c_str(), seqyA.c_str(), Liden, + n_ali8, L_ali, TM_ali, rmsd_ali, TM_0, d0_0, + d0A, d0B, Lnorm_ass, d0_scale, d0a, d0u, + (m_opt?fname_matrix:"").c_str(), + outfmt_opt, ter_opt, false, split_opt, o_opt, + fname_super, i_opt, a_opt, u_opt, d_opt, mirror_opt, + resi_vec1, resi_vec2); + if (outfmt_opt<=0) + { + cout<<"###############\t###############\t#########"<<endl; + cout<<"#Aligned atom 1\tAligned atom 2 \tDistance#"<<endl; + int r1,r2; + for (r2=0;r2<ylen;r2++) + { + r1=invmap[r2]; + if (r1<0) continue; + cout<<PDB_lines1[chain_i][r1].substr(12,15)<<'\t' + <<PDB_lines2[chain_j][r2].substr(12,15)<<'\t' + <<setw(9)<<setiosflags(ios::fixed)<<setprecision(3) + <<dist_list[r2]<<'\n'; + } + cout<<"###############\t###############\t#########"<<endl; + } + + /* Done! Free memory */ + delete [] invmap; + delete [] dist_list; + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + DeleteArray(&ya, ylen); + if (closeK_opt>=3) DeleteArray(&yk, ylen*closeK_opt); + delete [] seqy; + delete [] secy; + resi_vec2.clear(); + if (mm_opt==6) DeleteArray(&secy_bond, ylen); + } // chain_j + if (chain2_list.size()>1) + { + yname.clear(); + for (chain_j=0;chain_j<ychainnum;chain_j++) + PDB_lines2[chain_j].clear(); + PDB_lines2.clear(); + chainID_list2.clear(); + mol_vec2.clear(); + } + } // j + PDB_lines1[chain_i].clear(); + DeleteArray(&xa, xlen); + if (closeK_opt>=3) DeleteArray(&xk, xlen*closeK_opt); + delete [] seqx; + delete [] secx; + resi_vec1.clear(); + if (mm_opt==6) DeleteArray(&secx_bond, xlen); + } // chain_i + xname.clear(); + PDB_lines1.clear(); + chainID_list1.clear(); + mol_vec1.clear(); + } // i + if (chain2_list.size()==1) + { + yname.clear(); + for (chain_j=0;chain_j<ychainnum;chain_j++) + PDB_lines2[chain_j].clear(); + PDB_lines2.clear(); + resi_vec2.clear(); + chainID_list2.clear(); + mol_vec2.clear(); + } + return 0; +} + +int flexalign(string &xname, string &yname, const string &fname_super, + const string &fname_lign, const string &fname_matrix, + vector<string> &sequence, const double Lnorm_ass, const double d0_scale, + const bool m_opt, const int i_opt, const int o_opt, const int a_opt, + const bool u_opt, const bool d_opt, const double TMcut, + const int infmt1_opt, const int infmt2_opt, const int ter_opt, + const int split_opt, const int outfmt_opt, const bool fast_opt, + const int mirror_opt, const int het_opt, + const string &atom_opt, const string &mol_opt, const string &dir_opt, + const string &dir1_opt, const string &dir2_opt, const int byresi_opt, + const vector<string> &chain1_list, const vector<string> &chain2_list, + const int hinge_opt) +{ + /* declare previously global variables */ + vector<vector<string> >PDB_lines1; // text of chain1 + vector<vector<string> >PDB_lines2; // text of chain2 + vector<int> mol_vec1; // molecule type of chain1, RNA if >0 + vector<int> mol_vec2; // molecule type of chain2, RNA if >0 + vector<string> chainID_list1; // list of chainID1 + vector<string> chainID_list2; // list of chainID2 + int i,j; // file index + int chain_i,chain_j; // chain index + int r; // residue index + int xlen, ylen; // chain length + int xchainnum,ychainnum;// number of chains in a PDB file + char *seqx, *seqy; // for the protein sequence + char *secx, *secy; // for the secondary structure + double **xa, **ya; // for input vectors xa[0...xlen-1][0..2] and + // ya[0...ylen-1][0..2], in general, + // ya is regarded as native structure + // --> superpose xa onto ya + vector<string> resi_vec1; // residue index for chain1 + vector<string> resi_vec2; // residue index for chain2 + int read_resi=byresi_opt; // whether to read residue index + if (byresi_opt==0 && o_opt) read_resi=2; + + /* loop over file names */ + for (i=0;i<chain1_list.size();i++) + { + /* parse chain 1 */ + xname=chain1_list[i]; + xchainnum=get_PDB_lines(xname, PDB_lines1, chainID_list1, + mol_vec1, ter_opt, infmt1_opt, atom_opt, split_opt, het_opt); + if (!xchainnum) + { + cerr<<"Warning! Cannot parse file: "<<xname + <<". Chain number 0."<<endl; + continue; + } + for (chain_i=0;chain_i<xchainnum;chain_i++) + { + xlen=PDB_lines1[chain_i].size(); + if (mol_opt=="RNA") mol_vec1[chain_i]=1; + else if (mol_opt=="protein") mol_vec1[chain_i]=-1; + if (!xlen) + { + cerr<<"Warning! Cannot parse file: "<<xname + <<". Chain length 0."<<endl; + continue; + } + else if (xlen<3) + { + cerr<<"Sequence is too short <3!: "<<xname<<endl; + continue; + } + NewArray(&xa, xlen, 3); + seqx = new char[xlen + 1]; + secx = new char[xlen + 1]; + xlen = read_PDB(PDB_lines1[chain_i], xa, seqx, + resi_vec1, read_resi); + if (mirror_opt) for (r=0;r<xlen;r++) xa[r][2]=-xa[r][2]; + if (mol_vec1[chain_i]>0) make_sec(seqx,xa, xlen, secx,atom_opt); + else make_sec(xa, xlen, secx); // secondary structure assignment + + for (j=(dir_opt.size()>0)*(i+1);j<chain2_list.size();j++) + { + /* parse chain 2 */ + if (PDB_lines2.size()==0) + { + yname=chain2_list[j]; + ychainnum=get_PDB_lines(yname, PDB_lines2, chainID_list2, + mol_vec2, ter_opt, infmt2_opt, atom_opt, split_opt, + het_opt); + if (!ychainnum) + { + cerr<<"Warning! Cannot parse file: "<<yname + <<". Chain number 0."<<endl; + continue; + } + } + for (chain_j=0;chain_j<ychainnum;chain_j++) + { + ylen=PDB_lines2[chain_j].size(); + if (mol_opt=="RNA") mol_vec2[chain_j]=1; + else if (mol_opt=="protein") mol_vec2[chain_j]=-1; + if (!ylen) + { + cerr<<"Warning! Cannot parse file: "<<yname + <<". Chain length 0."<<endl; + continue; + } + else if (ylen<3) + { + cerr<<"Sequence is too short <3!: "<<yname<<endl; + continue; + } + NewArray(&ya, ylen, 3); + seqy = new char[ylen + 1]; + secy = new char[ylen + 1]; + ylen = read_PDB(PDB_lines2[chain_j], ya, seqy, + resi_vec2, read_resi); + if (mol_vec2[chain_j]>0) + make_sec(seqy, ya, ylen, secy, atom_opt); + else make_sec(ya, ylen, secy); + + if (byresi_opt) extract_aln_from_resi(sequence, + seqx,seqy,resi_vec1,resi_vec2,byresi_opt); + + /* declare variable specific to this pair of TMalign */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + bool force_fast_opt=(getmin(xlen,ylen)>1500)?true:fast_opt; + vector<vector<double> >tu_vec; + + /* entry function for structure alignment */ + int hingeNum=flexalign_main( + xa, ya, seqx, seqy, secx, secy, + t0, u0, tu_vec, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + i_opt, a_opt, u_opt, d_opt, force_fast_opt, + mol_vec1[chain_i]+mol_vec2[chain_j],hinge_opt); + + if (hinge_opt && hingeNum<=1 && + n_ali8<0.6*getmin(xlen,ylen)) + { + double t0_h[3], u0_h[3][3]; + double TM1_h, TM2_h; + double TM3_h, TM4_h, TM5_h; + double d0_0_h, TM_0_h; + double d0_out_h=5.0; + string seqM_h, seqxA_h, seqyA_h; + double rmsd0_h = 0.0; + int L_ali_h; + double Liden_h=0; + double TM_ali_h, rmsd_ali_h; + int n_ali_h=0; + int n_ali8_h=0; + vector<vector<double> >tu_vec_h(1,tu_vec[0]); + tu2t_u(tu_vec[0],t0_h,u0_h); + + int hingeNum_h=flexalign_main( + xa, ya, seqx, seqy, secx, secy, + t0_h, u0_h, tu_vec_h, + TM1_h, TM2_h, TM3_h, TM4_h, TM5_h, + d0_0_h, TM_0_h, d0A, d0B, d0u, d0a, d0_out_h, + seqM_h, seqxA_h, seqyA_h, rmsd0_h, L_ali_h, + Liden_h, TM_ali_h, rmsd_ali_h, n_ali_h, n_ali8_h, + xlen, ylen, sequence, Lnorm_ass, d0_scale, i_opt, + a_opt, u_opt, d_opt, force_fast_opt, + mol_vec1[chain_i]+mol_vec2[chain_j],hinge_opt); + + double TM =(TM1 >TM2 )?TM1 :TM2; + double TM_h=(TM1_h>TM2_h)?TM1_h:TM2_h; + if (TM_h>TM) + { + hingeNum=hingeNum_h; + tu2t_u(tu_vec_h[0],t0,u0); + TM1=TM1_h; + TM2=TM2_h; + TM3=TM3_h; + TM4=TM4_h; + TM5=TM5_h; + d0_0=d0_0_h; + TM_0=TM_0_h; + d0_out=d0_out_h; + seqM=seqM_h; + seqxA=seqxA_h; + seqyA=seqyA_h; + rmsd0=rmsd0_h; + L_ali=L_ali_h; + Liden=Liden_h; + TM_ali=TM_ali_h; + rmsd_ali=rmsd_ali_h; + n_ali=n_ali_h; + n_ali8=n_ali8_h; + tu_vec.clear(); + for (int hinge=0;hinge<tu_vec_h.size();hinge++) + tu_vec.push_back(tu_vec_h[hinge]); + } + else tu2t_u(tu_vec[0],t0,u0); + } + + /* print result */ + if (outfmt_opt==0) print_version(); + output_flexalign_results( + xname.substr(dir1_opt.size()+dir_opt.size()), + yname.substr(dir2_opt.size()+dir_opt.size()), + chainID_list1[chain_i], chainID_list2[chain_j], + xlen, ylen, t0, u0, tu_vec, TM1, TM2, TM3, TM4, TM5, + rmsd0, d0_out, seqM.c_str(), + seqxA.c_str(), seqyA.c_str(), Liden, + n_ali8, L_ali, TM_ali, rmsd_ali, TM_0, d0_0, + d0A, d0B, Lnorm_ass, d0_scale, d0a, d0u, + (m_opt?fname_matrix:"").c_str(), + outfmt_opt, ter_opt, false, split_opt, o_opt, + fname_super, i_opt, a_opt, u_opt, d_opt, mirror_opt, + resi_vec1, resi_vec2); + + /* Done! Free memory */ + tu_vec.clear(); + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + DeleteArray(&ya, ylen); + delete [] seqy; + delete [] secy; + resi_vec2.clear(); + } // chain_j + if (chain2_list.size()>1) + { + yname.clear(); + for (chain_j=0;chain_j<ychainnum;chain_j++) + PDB_lines2[chain_j].clear(); + PDB_lines2.clear(); + chainID_list2.clear(); + mol_vec2.clear(); + } + } // j + PDB_lines1[chain_i].clear(); + DeleteArray(&xa, xlen); + delete [] seqx; + delete [] secx; + resi_vec1.clear(); + } // chain_i + xname.clear(); + PDB_lines1.clear(); + chainID_list1.clear(); + mol_vec1.clear(); + } // i + if (chain2_list.size()==1) + { + yname.clear(); + for (chain_j=0;chain_j<ychainnum;chain_j++) + PDB_lines2[chain_j].clear(); + PDB_lines2.clear(); + resi_vec2.clear(); + chainID_list2.clear(); + mol_vec2.clear(); + } + return 0; +} + + +int main(int argc, char *argv[]) +{ + if (argc < 2) print_help(); + + + clock_t t1, t2; + t1 = clock(); + + /**********************/ + /* get argument */ + /**********************/ + string xname = ""; + string yname = ""; + string fname_super = ""; // file name for superposed structure + string fname_lign = ""; // file name for user alignment + string fname_matrix= ""; // file name for output matrix + vector<string> sequence; // get value from alignment file + double Lnorm_ass, d0_scale; + + bool h_opt = false; // print full help message + bool v_opt = false; // print version + bool m_opt = false; // flag for -m, output rotation matrix + int i_opt = 0; // 1 for -i, 3 for -I + int o_opt = 0; // 1 for -o, 2 for -rasmol + int a_opt = 0; // flag for -a, do not normalized by average length + bool u_opt = false; // flag for -u, normalized by user specified length + bool d_opt = false; // flag for -d, user specified d0 + + bool full_opt = false;// do not show chain level alignment + double TMcut =-1; + bool se_opt =false; + int infmt1_opt=-1; // PDB or PDBx/mmCIF format for chain_1 + int infmt2_opt=-1; // PDB or PDBx/mmCIF format for chain_2 + int ter_opt =2; // END, or different chainID + int split_opt =2; // split each chains + int outfmt_opt=0; // set -outfmt to full output + bool fast_opt =false; // flags for -fast, fTM-align algorithm + int cp_opt =0; // do not check circular permutation + int closeK_opt=-1; // number of atoms for SOI initial alignment. + // 5 and 0 for -mm 5 and 6 + int hinge_opt =9; // maximum number of hinge allowed for flexible + int mirror_opt=0; // do not align mirror + int het_opt=0; // do not read HETATM residues + int mm_opt=0; // do not perform MM-align + string atom_opt ="auto";// use C alpha atom for protein and C3' for RNA + string mol_opt ="auto";// auto-detect the molecule type as protein/RNA + string suffix_opt=""; // set -suffix to empty + string dir_opt =""; // set -dir to empty + string dir1_opt =""; // set -dir1 to empty + string dir2_opt =""; // set -dir2 to empty + int byresi_opt=0; // set -byresi to 0 + vector<string> chain1_list; // only when -dir1 is set + vector<string> chain2_list; // only when -dir2 is set + + for(int i = 1; i < argc; i++) + { + if ( !strcmp(argv[i],"-o") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -o"); + if (o_opt==2) + cerr<<"Warning! -rasmol is already set. Ignore -o"<<endl; + else + { + fname_super = argv[i + 1]; + o_opt = 1; + } + i++; + } + else if ( !strcmp(argv[i],"-rasmol") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -rasmol"); + if (o_opt==1) + cerr<<"Warning! -o is already set. Ignore -rasmol"<<endl; + else + { + fname_super = argv[i + 1]; + o_opt = 2; + } + i++; + } + else if ( !strcmp(argv[i],"-u") || !strcmp(argv[i],"-L") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -u or -L"); + Lnorm_ass = atof(argv[i + 1]); u_opt = true; i++; + if (Lnorm_ass<=0) PrintErrorAndQuit( + "ERROR! The value for -u or -L should be >0"); + } + else if ( !strcmp(argv[i],"-a") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -a"); + if (!strcmp(argv[i + 1], "T")) a_opt=true; + else if (!strcmp(argv[i + 1], "F")) a_opt=false; + else + { + a_opt=atoi(argv[i + 1]); + if (a_opt!=-2 && a_opt!=-1 && a_opt!=1) + PrintErrorAndQuit("-a must be -2, -1, 1, T or F"); + } + i++; + } + else if ( !strcmp(argv[i],"-full") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -full"); + if (!strcmp(argv[i + 1], "T")) full_opt=true; + else if (!strcmp(argv[i + 1], "F")) full_opt=false; + else PrintErrorAndQuit("-full must be T or F"); + i++; + } + else if ( !strcmp(argv[i],"-d") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -d"); + d0_scale = atof(argv[i + 1]); d_opt = true; i++; + } + else if ( !strcmp(argv[i],"-closeK") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -closeK"); + closeK_opt = atoi(argv[i + 1]); i++; + } + else if ( !strcmp(argv[i],"-hinge") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -hinge"); + hinge_opt = atoi(argv[i + 1]); i++; + } + else if ( !strcmp(argv[i],"-v") ) + { + v_opt = true; + } + else if ( !strcmp(argv[i],"-h") ) + { + h_opt = true; + } + else if ( !strcmp(argv[i],"-i") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -i"); + if (i_opt==3) + PrintErrorAndQuit("ERROR! -i and -I cannot be used together"); + fname_lign = argv[i + 1]; i_opt = 1; i++; + } + else if (!strcmp(argv[i], "-I") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -I"); + if (i_opt==1) + PrintErrorAndQuit("ERROR! -I and -i cannot be used together"); + fname_lign = argv[i + 1]; i_opt = 3; i++; + } + else if (!strcmp(argv[i], "-m") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -m"); + fname_matrix = argv[i + 1]; m_opt = true; i++; + }// get filename for rotation matrix + else if (!strcmp(argv[i], "-fast")) + { + fast_opt = true; + } + else if (!strcmp(argv[i], "-se")) + { + se_opt = true; + } + else if ( !strcmp(argv[i],"-infmt1") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -infmt1"); + infmt1_opt=atoi(argv[i + 1]); i++; + if (infmt1_opt<-1 || infmt1_opt>3) + PrintErrorAndQuit("ERROR! -infmt1 can only be -1, 0, 1, 2, or 3"); + } + else if ( !strcmp(argv[i],"-infmt2") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -infmt2"); + infmt2_opt=atoi(argv[i + 1]); i++; + if (infmt2_opt<-1 || infmt2_opt>3) + PrintErrorAndQuit("ERROR! -infmt2 can only be -1, 0, 1, 2, or 3"); + } + else if ( !strcmp(argv[i],"-ter") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -ter"); + ter_opt=atoi(argv[i + 1]); i++; + } + else if ( !strcmp(argv[i],"-split") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -split"); + split_opt=atoi(argv[i + 1]); i++; + } + else if ( !strcmp(argv[i],"-atom") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -atom"); + atom_opt=argv[i + 1]; i++; + if (atom_opt.size()!=4) PrintErrorAndQuit( + "ERROR! Atom name must have 4 characters, including space.\n" + "For example, C alpha, C3' and P atoms should be specified by\n" + "-atom \" CA \", -atom \" P \" and -atom \" C3'\", respectively."); + } + else if ( !strcmp(argv[i],"-mol") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -mol"); + mol_opt=argv[i + 1]; i++; + if (mol_opt=="prot") mol_opt="protein"; + else if (mol_opt=="DNA") mol_opt="RNA"; + if (mol_opt!="auto" && mol_opt!="protein" && mol_opt!="RNA") + PrintErrorAndQuit("ERROR! Molecule type must be one of the " + "following:\nauto, prot (the same as 'protein'), and " + "RNA (the same as 'DNA')."); + } + else if ( !strcmp(argv[i],"-dir") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -dir"); + dir_opt=argv[i + 1]; i++; + } + else if ( !strcmp(argv[i],"-dir1") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -dir1"); + dir1_opt=argv[i + 1]; i++; + } + else if ( !strcmp(argv[i],"-dir2") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -dir2"); + dir2_opt=argv[i + 1]; i++; + } + else if ( !strcmp(argv[i],"-suffix") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -suffix"); + suffix_opt=argv[i + 1]; i++; + } + else if ( !strcmp(argv[i],"-outfmt") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -outfmt"); + outfmt_opt=atoi(argv[i + 1]); i++; + } + else if ( !strcmp(argv[i],"-TMcut") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -TMcut"); + TMcut=atof(argv[i + 1]); i++; + } + else if ( !strcmp(argv[i],"-byresi") || + !strcmp(argv[i],"-tmscore") || + !strcmp(argv[i],"-TMscore")) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -byresi"); + byresi_opt=atoi(argv[i + 1]); i++; + } + else if ( !strcmp(argv[i],"-seq") ) + { + byresi_opt=5; + } + else if ( !strcmp(argv[i],"-cp") ) + { + mm_opt=3; + } + else if ( !strcmp(argv[i],"-mirror") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -mirror"); + mirror_opt=atoi(argv[i + 1]); i++; + } + else if ( !strcmp(argv[i],"-het") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -het"); + het_opt=atoi(argv[i + 1]); i++; + if (het_opt!=0 && het_opt!=1 && het_opt!=2) + PrintErrorAndQuit("-het must be 0, 1, or 2"); + } + else if ( !strcmp(argv[i],"-mm") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -mm"); + mm_opt=atoi(argv[i + 1]); i++; + } + else if (xname.size() == 0) xname=argv[i]; + else if (yname.size() == 0) yname=argv[i]; + else PrintErrorAndQuit(string("ERROR! Undefined option ")+argv[i]); + } + + if(xname.size()==0 || (yname.size()==0 && dir_opt.size()==0) || + (yname.size() && dir_opt.size())) + { + if (h_opt) print_help(h_opt); + if (v_opt) + { + print_version(); + exit(EXIT_FAILURE); + } + if (xname.size()==0) + PrintErrorAndQuit("Please provide input structures"); + else if (yname.size()==0 && dir_opt.size()==0 && mm_opt!=4) + PrintErrorAndQuit("Please provide structure B"); + else if (yname.size() && dir_opt.size()) + PrintErrorAndQuit("Please provide only one file name if -dir is set"); + } + + if (suffix_opt.size() && dir_opt.size()+dir1_opt.size()+dir2_opt.size()==0) + PrintErrorAndQuit("-suffix is only valid if -dir, -dir1 or -dir2 is set"); + if ((dir_opt.size() || dir1_opt.size() || dir2_opt.size())) + { + if (mm_opt!=2 && mm_opt!=4) + { + if (o_opt) + PrintErrorAndQuit("-o cannot be set with -dir, -dir1 or -dir2"); + if (m_opt && fname_matrix!="-") + PrintErrorAndQuit("-m can only be - or unset when using -dir, -dir1 or -dir2"); + } + else if (dir_opt.size() && (dir1_opt.size() || dir2_opt.size())) + PrintErrorAndQuit("-dir cannot be set with -dir1 or -dir2"); + } + if (o_opt && (infmt1_opt!=-1 && infmt1_opt!=0 && infmt1_opt!=3)) + PrintErrorAndQuit("-o can only be used with -infmt1 -1, 0 or 3"); + + if (mol_opt=="protein" && atom_opt=="auto") + atom_opt=" CA "; + else if (mol_opt=="RNA" && atom_opt=="auto") + atom_opt=" C3'"; + + if (d_opt && d0_scale<=0) + PrintErrorAndQuit("Wrong value for option -d! It should be >0"); + if (outfmt_opt>=2 && (a_opt || u_opt || d_opt)) + PrintErrorAndQuit("-outfmt 2 cannot be used with -a, -u, -L, -d"); + if (byresi_opt!=0) + { + if (i_opt) + PrintErrorAndQuit("-byresi >=1 cannot be used with -i or -I"); + if (byresi_opt<0 || byresi_opt>6) + PrintErrorAndQuit("-byresi can only be 0 to 6"); + if ((byresi_opt==2 || byresi_opt==3 || byresi_opt==6) && ter_opt>=2) + PrintErrorAndQuit("-byresi 2 and 6 must be used with -ter <=1"); + } + //if (split_opt==1 && ter_opt!=0) + //PrintErrorAndQuit("-split 1 should be used with -ter 0"); + //else if (split_opt==2 && ter_opt!=0 && ter_opt!=1) + //PrintErrorAndQuit("-split 2 should be used with -ter 0 or 1"); + if (split_opt<0 || split_opt>2) + PrintErrorAndQuit("-split can only be 0, 1 or 2"); + + if (mm_opt==3) + { + cp_opt=true; + mm_opt=0; + } + if (cp_opt && i_opt) + PrintErrorAndQuit("-mm 3 cannot be used with -i or -I"); + + if (mirror_opt && het_opt!=1) + cerr<<"WARNING! -mirror was not used with -het 1. " + <<"D amino acids may not be correctly aligned."<<endl; + + if (mm_opt) + { + if (i_opt) PrintErrorAndQuit("-mm cannot be used with -i or -I"); + if (u_opt) PrintErrorAndQuit("-mm cannot be used with -u or -L"); + //if (cp_opt) PrintErrorAndQuit("-mm cannot be used with -cp"); + if (dir_opt.size() && (mm_opt==1||mm_opt==2)) PrintErrorAndQuit("-mm 1 or 2 cannot be used with -dir"); + if (byresi_opt) PrintErrorAndQuit("-mm cannot be used with -byresi"); + if (ter_opt>=2 && (mm_opt==1 || mm_opt==2)) PrintErrorAndQuit("-mm 1 or 2 must be used with -ter 0 or -ter 1"); + if (mm_opt==4 && (yname.size() || dir2_opt.size())) + cerr<<"WARNING! structure_2 is ignored for -mm 4"<<endl; + } + else if (full_opt) PrintErrorAndQuit("-full can only be used with -mm"); + + if (o_opt && ter_opt<=1 && split_opt==2) + { + if (mm_opt && o_opt==2) cerr<<"WARNING! -mm may generate incorrect" + <<" RasMol output due to limitations in PDB file format. " + <<"When -mm is used, -o is recommended over -rasmol"<<endl; + else if (mm_opt==0) cerr<<"WARNING! Only the superposition of the" + <<"last aligned chain pair will be generated"<<endl; + } + + if (closeK_opt<0) + { + if (mm_opt==5) closeK_opt=5; + else closeK_opt=0; + } + + if (mm_opt==7 && hinge_opt>=10) + PrintErrorAndQuit("ERROR! -hinge must be <10"); + + + /* read initial alignment file from 'align.txt' */ + if (i_opt) read_user_alignment(sequence, fname_lign, i_opt); + + if (byresi_opt==6) mm_opt=1; + else if (byresi_opt) i_opt=3; + + if (m_opt && fname_matrix == "") // Output rotation matrix: matrix.txt + PrintErrorAndQuit("ERROR! Please provide a file name for option -m!"); + + /* parse file list */ + if (dir1_opt.size()+dir_opt.size()==0) chain1_list.push_back(xname); + else file2chainlist(chain1_list, xname, dir_opt+dir1_opt, suffix_opt); + + int i; + if (dir_opt.size()) + for (i=0;i<chain1_list.size();i++) + chain2_list.push_back(chain1_list[i]); + else if (dir2_opt.size()==0) chain2_list.push_back(yname); + else file2chainlist(chain2_list, yname, dir2_opt, suffix_opt); + + if (outfmt_opt==2) + { + if (mm_opt==2) cout<<"#Query\tTemplate\tTM"<<endl; + else cout<<"#PDBchain1\tPDBchain2\tTM1\tTM2\t" + <<"RMSD\tID1\tID2\tIDali\tL1\tL2\tLali"<<endl; + } + + /* real alignment. entry functions are MMalign_main and + * TMalign_main */ + if (mm_opt==0) TMalign(xname, yname, fname_super, fname_lign, fname_matrix, + sequence, Lnorm_ass, d0_scale, m_opt, i_opt, o_opt, a_opt, + u_opt, d_opt, TMcut, infmt1_opt, infmt2_opt, ter_opt, + split_opt, outfmt_opt, fast_opt, cp_opt, mirror_opt, het_opt, + atom_opt, mol_opt, dir_opt, dir1_opt, dir2_opt, byresi_opt, + chain1_list, chain2_list, se_opt); + else if (mm_opt==1) MMalign(xname, yname, fname_super, fname_lign, + fname_matrix, sequence, d0_scale, m_opt, o_opt, + a_opt, d_opt, full_opt, TMcut, infmt1_opt, infmt2_opt, + ter_opt, split_opt, outfmt_opt, fast_opt, mirror_opt, het_opt, + atom_opt, mol_opt, dir1_opt, dir2_opt, chain1_list, chain2_list, + byresi_opt); + else if (mm_opt==2) MMdock(xname, yname, fname_super, + fname_matrix, sequence, Lnorm_ass, d0_scale, m_opt, o_opt, a_opt, + u_opt, d_opt, TMcut, infmt1_opt, infmt2_opt, ter_opt, + split_opt, outfmt_opt, fast_opt, mirror_opt, het_opt, + atom_opt, mol_opt, dir1_opt, dir2_opt, chain1_list, chain2_list); + else if (mm_opt==3) ; // should be changed to mm_opt=0, cp_opt=true + else if (mm_opt==4) mTMalign(xname, yname, fname_super, fname_matrix, + sequence, Lnorm_ass, d0_scale, m_opt, i_opt, o_opt, a_opt, + u_opt, d_opt, full_opt, TMcut, infmt1_opt, ter_opt, + split_opt, outfmt_opt, fast_opt, het_opt, + atom_opt, mol_opt, dir_opt, byresi_opt, chain1_list); + else if (mm_opt==5 || mm_opt==6) SOIalign(xname, yname, fname_super, fname_lign, + fname_matrix, sequence, Lnorm_ass, d0_scale, m_opt, i_opt, o_opt, + a_opt, u_opt, d_opt, TMcut, infmt1_opt, infmt2_opt, ter_opt, + split_opt, outfmt_opt, fast_opt, cp_opt, mirror_opt, het_opt, + atom_opt, mol_opt, dir_opt, dir1_opt, dir2_opt, + chain1_list, chain2_list, se_opt, closeK_opt, mm_opt); + else if (mm_opt==7) flexalign(xname, yname, fname_super, fname_lign, + fname_matrix, sequence, Lnorm_ass, d0_scale, m_opt, i_opt, o_opt, + a_opt, u_opt, d_opt, TMcut, infmt1_opt, infmt2_opt, ter_opt, + split_opt, outfmt_opt, fast_opt, mirror_opt, het_opt, + atom_opt, mol_opt, dir_opt, dir1_opt, dir2_opt, byresi_opt, + chain1_list, chain2_list, hinge_opt); + else cerr<<"WARNING! -mm "<<mm_opt<<" not implemented"<<endl; + + /* clean up */ + vector<string>().swap(chain1_list); + vector<string>().swap(chain2_list); + vector<string>().swap(sequence); + + t2 = clock(); + float diff = ((float)t2 - (float)t1)/CLOCKS_PER_SEC; + if (outfmt_opt<2) printf("#Total CPU time is %5.2f seconds\n", diff); + return 0; +} diff --git a/modules/bindings/src/tmalign/align.txt b/modules/bindings/src/USalign/align.txt similarity index 100% rename from modules/bindings/src/tmalign/align.txt rename to modules/bindings/src/USalign/align.txt diff --git a/modules/bindings/src/tmalign/basic_fun.h b/modules/bindings/src/USalign/basic_fun.h similarity index 78% rename from modules/bindings/src/tmalign/basic_fun.h rename to modules/bindings/src/USalign/basic_fun.h index 0e8ae307d81a045f12998f90afdd4d0d00c628cb..0fe0701199465f79f0fc340dc7c85a95f90f1205 100644 --- a/modules/bindings/src/tmalign/basic_fun.h +++ b/modules/bindings/src/USalign/basic_fun.h @@ -137,6 +137,17 @@ void split(const string &line, vector<string> &line_vec, } } +/* strip white space at the begining or end of string */ +string Trim(const string &inputString) +{ + string result = inputString; + int idxBegin = inputString.find_first_not_of(" \n\r\t"); + int idxEnd = inputString.find_last_not_of(" \n\r\t"); + if (idxBegin >= 0 && idxEnd >= 0) + result = inputString.substr(idxBegin, idxEnd + 1 - idxBegin); + return result; +} + size_t get_PDB_lines(const string filename, vector<vector<string> >&PDB_lines, vector<string> &chainID_list, vector<int> &mol_vec, const int ter_opt, const int infmt_opt, @@ -152,11 +163,14 @@ size_t get_PDB_lines(const string filename, int compress_type=0; // uncompressed file ifstream fin; +#ifndef REDI_PSTREAM_H_SEEN + ifstream fin_gz; +#else redi::ipstream fin_gz; // if file is compressed if (filename.size()>=3 && filename.substr(filename.size()-3,3)==".gz") { - fin_gz.open("zcat '"+filename+"'"); + fin_gz.open("gunzip -c '"+filename+"'"); compress_type=1; } else if (filename.size()>=4 && @@ -165,14 +179,20 @@ size_t get_PDB_lines(const string filename, fin_gz.open("bzcat '"+filename+"'"); compress_type=2; } - else fin.open(filename.c_str()); + else +#endif + { + if (filename=="-") compress_type=-1; + else fin.open(filename.c_str()); + } if (infmt_opt==0||infmt_opt==-1) // PDB format { - while (compress_type?fin_gz.good():fin.good()) + while ((compress_type==-1)?cin.good():(compress_type?fin_gz.good():fin.good())) { - if (compress_type) getline(fin_gz, line); - else getline(fin, line); + if (compress_type==-1) getline(cin, line); + else if (compress_type) getline(fin_gz, line); + else getline(fin, line); if (infmt_opt==-1 && line.compare(0,5,"loop_")==0) // PDBx/mmCIF return get_PDB_lines(filename,PDB_lines,chainID_list, mol_vec, ter_opt, 3, atom_opt, split_opt,het_opt); @@ -194,6 +214,13 @@ size_t get_PDB_lines(const string filename, select_atom=(line.compare(12,4," C3'")==0); else select_atom=(line.compare(12,4," CA ")==0); } + else if (atom_opt=="PC4'") + { + if (line[17]==' ' && (line[18]=='D'||line[18]==' ')) + select_atom=(line.compare(12,4," P ")==0 + )||(line.compare(12,4," C4'")==0); + else select_atom=(line.compare(12,4," CA ")==0); + } else select_atom=(line.compare(12,4,atom_opt)==0); if (select_atom) { @@ -246,7 +273,7 @@ size_t get_PDB_lines(const string filename, mol_vec.push_back(0); } - if (resi==line.substr(22,5)) + if (resi==line.substr(22,5) && atom_opt!="PC4'") cerr<<"Warning! Duplicated residue "<<resi<<endl; resi=line.substr(22,5); // including insertion code @@ -263,13 +290,26 @@ size_t get_PDB_lines(const string filename, size_t L=0; float x,y,z; stringstream i8_stream; - while (compress_type?fin_gz.good():fin.good()) + while ((compress_type==-1)?cin.good():(compress_type?fin_gz.good():fin.good())) { - if (compress_type) fin_gz>>L>>x>>y>>z; - else fin >>L>>x>>y>>z; - if (compress_type) getline(fin_gz, line); - else getline(fin, line); - if (!(compress_type?fin_gz.good():fin.good())) break; + if (compress_type==-1) + { + cin>>L>>x>>y>>z; + getline(cin, line); + if (!cin.good()) break; + } + else if (compress_type) + { + fin_gz>>L>>x>>y>>z; + getline(fin_gz, line); + if (!fin_gz.good()) break; + } + else + { + fin >>L>>x>>y>>z; + getline(fin, line); + if (!fin.good()) break; + } model_idx++; stringstream i8_stream; i8_stream << ':' << model_idx; @@ -278,8 +318,9 @@ size_t get_PDB_lines(const string filename, mol_vec.push_back(0); for (i=0;i<L;i++) { - if (compress_type) fin_gz>>x>>y>>z; - else fin >>x>>y>>z; + if (compress_type==-1) cin>>x>>y>>z; + else if (compress_type) fin_gz>>x>>y>>z; + else fin >>x>>y>>z; i8_stream<<"ATOM "<<setw(4)<<i+1<<" CA UNK "<<setw(4) <<i+1<<" "<<setiosflags(ios::fixed)<<setprecision(3) <<setw(8)<<x<<setw(8)<<y<<setw(8)<<z; @@ -287,31 +328,35 @@ size_t get_PDB_lines(const string filename, i8_stream.str(string()); PDB_lines.back().push_back(line); } - if (compress_type) getline(fin_gz, line); - else getline(fin, line); + if (compress_type==-1) getline(cin, line); + else if (compress_type) getline(fin_gz, line); + else getline(fin, line); } } else if (infmt_opt==2) // xyz format { size_t L=0; stringstream i8_stream; - while (compress_type?fin_gz.good():fin.good()) + while ((compress_type==-1)?cin.good():(compress_type?fin_gz.good():fin.good())) { - if (compress_type) getline(fin_gz, line); - else getline(fin, line); + if (compress_type==-1) getline(cin, line); + else if (compress_type) getline(fin_gz, line); + else getline(fin, line); L=atoi(line.c_str()); - if (compress_type) getline(fin_gz, line); - else getline(fin, line); + if (compress_type==-1) getline(cin, line); + else if (compress_type) getline(fin_gz, line); + else getline(fin, line); for (i=0;i<line.size();i++) if (line[i]==' '||line[i]=='\t') break; - if (!(compress_type?fin_gz.good():fin.good())) break; + if (!((compress_type==-1)?cin.good():(compress_type?fin_gz.good():fin.good()))) break; chainID_list.push_back(':'+line.substr(0,i)); PDB_lines.push_back(tmp_str_vec); mol_vec.push_back(0); for (i=0;i<L;i++) { - if (compress_type) getline(fin_gz, line); - else getline(fin, line); + if (compress_type==-1) getline(cin, line); + else if (compress_type) getline(fin_gz, line); + else getline(fin, line); i8_stream<<"ATOM "<<setw(4)<<i+1<<" CA " <<AAmap(line[0])<<" "<<setw(4)<<i+1<<" " <<line.substr(2,8)<<line.substr(11,8)<<line.substr(20,8); @@ -339,18 +384,24 @@ size_t get_PDB_lines(const string filename, string prev_resi=""; string model_index=""; // the same as model_idx but type is string stringstream i8_stream; - while (compress_type?fin_gz.good():fin.good()) + while ((compress_type==-1)?cin.good():(compress_type?fin_gz.good():fin.good())) { - if (compress_type) getline(fin_gz, line); - else getline(fin, line); + if (compress_type==-1) getline(cin, line); + else if (compress_type) getline(fin_gz, line); + else getline(fin, line); if (line.size()==0) continue; - if (loop_) loop_ = line.compare(0,2,"# "); + if (loop_) loop_ = (line.size()>=2)?(line.compare(0,2,"# ")):(line.compare(0,1,"#")); if (!loop_) { if (line.compare(0,5,"loop_")) continue; while(1) { - if (compress_type) + if (compress_type==-1) + { + if (cin.good()) getline(cin, line); + else PrintErrorAndQuit("ERROR! Unexpected end of -"); + } + else if (compress_type) { if (fin_gz.good()) getline(fin_gz, line); else PrintErrorAndQuit("ERROR! Unexpected end of "+filename); @@ -367,15 +418,16 @@ size_t get_PDB_lines(const string filename, loop_=true; _atom_site.clear(); atom_site_pos=0; - _atom_site[line.substr(11,line.size()-12)]=atom_site_pos; + _atom_site[Trim(line.substr(11))]=atom_site_pos; while(1) { - if (compress_type) getline(fin_gz, line); - else getline(fin, line); + if (compress_type==-1) getline(cin, line); + else if (compress_type) getline(fin_gz, line); + else getline(fin, line); if (line.size()==0) continue; if (line.compare(0,11,"_atom_site.")) break; - _atom_site[line.substr(11,line.size()-12)]=++atom_site_pos; + _atom_site[Trim(line.substr(11))]=++atom_site_pos; } @@ -431,6 +483,13 @@ size_t get_PDB_lines(const string filename, select_atom=(atom==" C3'"); else select_atom=(atom==" CA "); } + else if (atom_opt=="PC4'") + { + if (line[17]==' ' && (line[18]=='D'||line[18]==' ')) + select_atom=(line.compare(12,4," P ")==0 + )||(line.compare(12,4," C4'")==0); + else select_atom=(line.compare(12,4," CA ")==0); + } else select_atom=(atom==atom_opt); if (!select_atom) continue; @@ -493,7 +552,7 @@ size_t get_PDB_lines(const string filename, resi+=line_vec[_atom_site["pdbx_PDB_ins_code"]][0]; else resi+=" "; - if (prev_resi==resi) + if (prev_resi==resi && atom_opt!="PC4'") cerr<<"Warning! Duplicated residue "<<resi<<endl; prev_resi=resi; @@ -514,8 +573,8 @@ size_t get_PDB_lines(const string filename, AA.clear(); } - if (compress_type) fin_gz.close(); - else fin.close(); + if (compress_type>=1) fin_gz.close(); + else if (compress_type==0) fin.close(); line.clear(); if (!split_opt) chainID_list.push_back(""); return PDB_lines.size(); @@ -537,11 +596,14 @@ size_t get_FASTA_lines(const string filename, int compress_type=0; // uncompressed file ifstream fin; +#ifndef REDI_PSTREAM_H_SEEN + ifstream fin_gz; +#else redi::ipstream fin_gz; // if file is compressed if (filename.size()>=3 && filename.substr(filename.size()-3,3)==".gz") { - fin_gz.open("zcat '"+filename+"'"); + fin_gz.open("gunzip -c '"+filename+"'"); compress_type=1; } else if (filename.size()>=4 && @@ -550,12 +612,19 @@ size_t get_FASTA_lines(const string filename, fin_gz.open("bzcat '"+filename+"'"); compress_type=2; } - else fin.open(filename.c_str()); + else +#endif + { + if (filename=="-") compress_type=-1; + else fin.open(filename.c_str()); + } - while (compress_type?fin_gz.good():fin.good()) + while ((compress_type==-1)?cin.good():(compress_type?fin_gz.good():fin.good())) { - if (compress_type) getline(fin_gz, line); - else getline(fin, line); + if (compress_type==-1) getline(cin, line); + else if (compress_type) getline(fin_gz, line); + else getline(fin, line); + if (line.size()==0 || line[0]=='#') continue; if (line[0]=='>') @@ -584,132 +653,11 @@ size_t get_FASTA_lines(const string filename, } line.clear(); - if (compress_type) fin_gz.close(); - else fin.close(); + if (compress_type>=1) fin_gz.close(); + else if (compress_type==0) fin.close(); return FASTA_lines.size(); } - -/* extract pairwise sequence alignment from residue index vectors, - * assuming that "sequence" contains two empty strings. - * return length of alignment, including gap. */ -int extract_aln_from_resi(vector<string> &sequence, char *seqx, char *seqy, - const vector<string> resi_vec1, const vector<string> resi_vec2, - const int byresi_opt) -{ - sequence.clear(); - sequence.push_back(""); - sequence.push_back(""); - - int i1=0; // positions in resi_vec1 - int i2=0; // positions in resi_vec2 - int xlen=resi_vec1.size(); - int ylen=resi_vec2.size(); - map<string,string> chainID_map1; - map<string,string> chainID_map2; - if (byresi_opt==3) - { - vector<string> chainID_vec; - string chainID; - stringstream ss; - int i; - for (i=0;i<xlen;i++) - { - chainID=resi_vec1[i].substr(5); - if (!chainID_vec.size()|| chainID_vec.back()!=chainID) - { - chainID_vec.push_back(chainID); - ss<<chainID_vec.size(); - chainID_map1[chainID]=ss.str(); - ss.str(""); - } - } - chainID_vec.clear(); - for (i=0;i<ylen;i++) - { - chainID=resi_vec2[i].substr(5); - if (!chainID_vec.size()|| chainID_vec.back()!=chainID) - { - chainID_vec.push_back(chainID); - ss<<chainID_vec.size(); - chainID_map2[chainID]=ss.str(); - ss.str(""); - } - } - vector<string>().swap(chainID_vec); - } - string chainID1=""; - string chainID2=""; - string chainID1_prev=""; - string chainID2_prev=""; - while(i1<xlen && i2<ylen) - { - if (byresi_opt==2) - { - chainID1=resi_vec1[i1].substr(5); - chainID2=resi_vec2[i2].substr(5); - } - else if (byresi_opt==3) - { - chainID1=chainID_map1[resi_vec1[i1].substr(5)]; - chainID2=chainID_map2[resi_vec2[i2].substr(5)]; - } - - if (chainID1==chainID2) - { - if (atoi(resi_vec1[i1].substr(0,4).c_str())< - atoi(resi_vec2[i2].substr(0,4).c_str())) - { - sequence[0]+=seqx[i1++]; - sequence[1]+='-'; - } - else if (atoi(resi_vec1[i1].substr(0,4).c_str())> - atoi(resi_vec2[i2].substr(0,4).c_str())) - { - sequence[0]+='-'; - sequence[1]+=seqy[i2++]; - } - else - { - sequence[0]+=seqx[i1++]; - sequence[1]+=seqy[i2++]; - } - chainID1_prev=chainID1; - chainID2_prev=chainID2; - } - else - { - if (chainID1_prev==chainID1 && chainID2_prev!=chainID2) - { - sequence[0]+=seqx[i1++]; - sequence[1]+='-'; - chainID1_prev=chainID1; - } - else if (chainID1_prev!=chainID1 && chainID2_prev==chainID2) - { - sequence[0]+='-'; - sequence[1]+=seqy[i2++]; - chainID2_prev=chainID2; - } - else - { - sequence[0]+=seqx[i1++]; - sequence[1]+=seqy[i2++]; - chainID1_prev=chainID1; - chainID2_prev=chainID2; - } - } - - } - map<string,string>().swap(chainID_map1); - map<string,string>().swap(chainID_map2); - chainID1.clear(); - chainID2.clear(); - chainID1_prev.clear(); - chainID2_prev.clear(); - return sequence[0].size(); -} - int read_PDB(const vector<string> &PDB_lines, double **a, char *seq, vector<string> &resi_vec, const int read_resi) { @@ -758,17 +706,6 @@ void do_rotation(double **x, double **x1, int len, double t[3], double u[3][3]) } } -/* strip white space at the begining or end of string */ -string Trim(const string &inputString) -{ - string result = inputString; - int idxBegin = inputString.find_first_not_of(" \n\r\t"); - int idxEnd = inputString.find_last_not_of(" \n\r\t"); - if (idxBegin >= 0 && idxEnd >= 0) - result = inputString.substr(idxBegin, idxEnd + 1 - idxBegin); - return result; -} - /* read user specified pairwise alignment from 'fname_lign' to 'sequence'. * This function should only be called by main function, as it will * terminate a program if wrong alignment is given */ diff --git a/modules/bindings/src/USalign/cif2pdb.cpp b/modules/bindings/src/USalign/cif2pdb.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cfd06c26921e85280adc84b04d669f1887211962 --- /dev/null +++ b/modules/bindings/src/USalign/cif2pdb.cpp @@ -0,0 +1,533 @@ +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <time.h> +#include <string.h> + +#include <sstream> +#include <iostream> +#include <iomanip> +#include <fstream> +#include <vector> +#include <iterator> +#include <algorithm> +#include <string> +#include <iomanip> +#include <map> + +#include "pstream.h" // For reading gzip and bz2 compressed files + +using namespace std; + +void print_help() +{ + cout << +"Converting mmCIF file to PDB file(s)\n" +"\n" +"Usage: cif2pdb input.cif output.pdb\n" +"\n" +" -chain Specify auth chain ID to convert:\n" +" $ cif2pdb input.cif output.pdb -chain A\n" +"\n" +" -mol macromolecule type. default is all macromolecules.\n" +" 1: protein only\n" +" 2: RNA only\n" +" 4: DNA only\n" +"\n" +" -split Whether to split PDB file into multiple chains\n" +" 0: (default) do not split; output a single PDB\n" +" 1: output one PDB file per chain\n" +"\n" +" -het Whether to read residues marked as 'HETATM' in addition to 'ATOM '\n" +" 0: only 'ATOM ' residues\n" +" 1: (default) 'ATOM ' and 'HETATM' for MSE\n" +" 2: 'ATOM ' and all 'HETATM', excluding HOH\n" +" 3: 'ATOM ' and all 'HETATM', including HOH\n" +" If -het >=1, MSE will be converted to MET\n" + <<endl; + exit(EXIT_SUCCESS); +} + +void PrintErrorAndQuit(const string sErrorString) +{ + cout << sErrorString << endl; + exit(1); +} + +/* strip white space at the begining or end of string */ +string Trim(const string &inputString) +{ + string result = inputString; + int idxBegin = inputString.find_first_not_of(" \n\r\t"); + int idxEnd = inputString.find_last_not_of(" \n\r\t"); + if (idxBegin >= 0 && idxEnd >= 0) + result = inputString.substr(idxBegin, idxEnd + 1 - idxBegin); + return result; +} + +/* split a long string into vectors by whitespace + * line - input string + * line_vec - output vector + * delimiter - delimiter */ +void split(const string &line, vector<string> &line_vec, + const char delimiter=' ') +{ + bool within_word = false; + for (size_t pos=0;pos<line.size();pos++) + { + if (line[pos]==delimiter) + { + within_word = false; + continue; + } + if (!within_word) + { + within_word = true; + line_vec.push_back(""); + } + line_vec.back()+=line[pos]; + } +} + +void write_mmcif_to_pdb(const string filename, + const vector<vector<string> >&PDB_lines, + const vector<string> &chainID_list, const int split_opt) +{ + size_t c,r; + + ofstream fout; + if (split_opt) + { + for (c=0;c<PDB_lines.size();c++) + { + if (PDB_lines[c].size()==0) continue; + if (filename=="-") + { + cout<<"REMARK cif2pdb "<<PDB_lines[c][0][21]<<" "<<chainID_list[c]<<endl; + for (r=0;r<PDB_lines[c].size();r++) cout<<PDB_lines[c][r]; + cout<<"TER"<<endl; + continue; + } + cout<< filename+Trim(chainID_list[c])+".pdb"<<endl; + fout.open((filename+Trim(chainID_list[c])+".pdb").c_str()); + fout<<"REMARK cif2pdb "<<PDB_lines[c][0][21]<<" "<<chainID_list[c]<<endl; + for (r=0;r<PDB_lines[c].size();r++) fout<<PDB_lines[c][r]; + fout<<"TER"<<endl; + fout.close(); + } + } + else if (filename=="-") + { + for (c=0;c<PDB_lines.size();c++) + { + if (PDB_lines[c].size()==0) continue; + cout<<"REMARK cif2pdb "<<PDB_lines[c][0][21]<<" "<<chainID_list[c]<<endl; + } + for (c=0;c<PDB_lines.size();c++) + { + if (PDB_lines[c].size()==0) continue; + for (r=0;r<PDB_lines[c].size();r++) cout<<PDB_lines[c][r]; + cout<<"TER"<<endl; + } + cout<<"END"<<endl; + } + else + { + fout.open(filename.c_str()); + for (c=0;c<PDB_lines.size();c++) + { + if (PDB_lines[c].size()==0) continue; + fout<<"REMARK cif2pdb "<<PDB_lines[c][0][21]<<" "<<chainID_list[c]<<endl; + } + for (c=0;c<PDB_lines.size();c++) + { + if (PDB_lines[c].size()==0) continue; + for (r=0;r<PDB_lines[c].size();r++) fout<<PDB_lines[c][r]; + fout<<"TER"<<endl; + } + fout<<"END"<<endl; + fout.close(); + } + return; +} + +size_t resolve_chainID_for_mmcif(vector<vector<string> >&PDB_lines, + const vector<string> &chainID_list) +{ + size_t changed_chains=0; + size_t c,r,i; + string chainID; + + string chainID_string="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + vector<bool> chainID_taken(chainID_string.size(),false); + vector<bool> chainID_accept(chainID_list.size(),false); + + /* accept all single character chain ID */ + for (c=0;c<PDB_lines.size();c++) + { + if (PDB_lines[c].size()==0) continue; + chainID=PDB_lines[c][0][21]; + if (chainID!=chainID_list[c]) continue; + chainID_accept[c]=true; + for (i=0;i<chainID_string.size();i++) + { + if (chainID_string[i]!=chainID[0]) continue; + if (chainID_taken[i]) chainID_accept[c]=false; + else chainID_taken[i]=true; + break; + } + } + + /* accept all remaining non-conflicting chain ID */ + for (c=0;c<PDB_lines.size();c++) + { + if (PDB_lines[c].size()==0 || chainID_accept[c]) continue; + chainID=PDB_lines[c][0][21]; + chainID_accept[c]=true; + for (i=0;i<chainID_string.size();i++) + { + if (chainID_string[i]!=chainID[0]) continue; + if (chainID_taken[i]) chainID_accept[c]=false; + else chainID_taken[i]=true; + break; + } + } + + /* resolve remaining chain ID */ + for (c=0;c<PDB_lines.size();c++) + { + if (PDB_lines[c].size()==0 || chainID_accept[c]) continue; + chainID=""; + for (i=0;i<chainID_taken.size();i++) + { + if (chainID_taken[i]) continue; + chainID=chainID_string[i]; + chainID_taken[i]=true; + break; + } + if (chainID=="") + { + cerr<<"WARNING! Cannot parse "<<chainID_list[c]<<" with " + <<PDB_lines[c].size()<<" atoms due to chain ID conflict. " + <<"Please consider -split 1"<<endl; + vector<string>().swap(PDB_lines[c]); + } + else + { + for (r=0;r<PDB_lines[c].size();r++) PDB_lines[c][r]= + PDB_lines[c][r].substr(0,21)+chainID+PDB_lines[c][r].substr(22); + changed_chains++; + } + } + if (changed_chains) + cerr<<"WARNING! Changed "<<changed_chains<<" chain ID(s)"<<endl; + + /* clean up*/ + chainID.clear(); + string().swap(chainID_string); + vector<bool>().swap(chainID_taken); + vector<bool>().swap(chainID_accept); + return changed_chains; +} + +size_t get_all_mmcif_lines(const string filename, const string chain_opt, + vector<vector<string> >&PDB_lines, vector<string> &chainID_list, + const bool dna_opt, const bool rna_opt, const bool protein_opt, + const bool hoh_opt, const bool lig_opt, const bool mse_opt) +{ + size_t a=0; // atom index + string line; + bool select_atom=false; + size_t model_idx=0; + vector<string> tmp_str_vec; + + int compress_type=0; // uncompressed file + ifstream fin; + redi::ipstream fin_gz; // if file is compressed + if (filename.size()>=3 && + filename.substr(filename.size()-3,3)==".gz") + { + fin_gz.open("gunzip -c '"+filename+"'"); + compress_type=1; + } + else if (filename.size()>=4 && + filename.substr(filename.size()-4,4)==".bz2") + { + fin_gz.open("bzcat '"+filename+"'"); + compress_type=2; + } + else + { + if (filename=="-") compress_type=-1; + else fin.open(filename.c_str()); + } + + bool loop_ = false; // not reading following content + map<string,int> _atom_site; + int atom_site_pos; + vector<string> line_vec; + string group_PDB="ATOM "; + string alt_id="."; // alternative location indicator + string asym_id="."; // this is similar to chainID, except that + // chainID is char while asym_id is a string + // with possibly multiple char + string prev_asym_id=""; + string resn=""; // residue name + string resi=""; + string atom=""; + string model_index=""; // the same as model_idx but type is string + stringstream i8_stream; + while ((compress_type==-1)?cin.good():(compress_type?fin_gz.good():fin.good())) + { + if (compress_type==-1) getline(cin, line); + else if (compress_type) getline(fin_gz, line); + else getline(fin, line); + if (line.size()==0) continue; + if (loop_) loop_ = (line.size()>=2)?(line.compare(0,2,"# ")):(line.compare(0,1,"#")); + if (!loop_) + { + if (line.compare(0,5,"loop_")) continue; + while(1) + { + if (compress_type==-1) + { + if (cin.good()) getline(cin, line); + else PrintErrorAndQuit("ERROR! Unexpected end of "+filename); + } + else if (compress_type) + { + if (fin_gz.good()) getline(fin_gz, line); + else PrintErrorAndQuit("ERROR! Unexpected end of "+filename); + } + else + { + if (fin.good()) getline(fin, line); + else PrintErrorAndQuit("ERROR! Unexpected end of "+filename); + } + if (line.size()) break; + } + if (line.compare(0,11,"_atom_site.")) continue; + + loop_=true; + _atom_site.clear(); + atom_site_pos=0; + _atom_site[Trim(line.substr(11))]=atom_site_pos; + + while(1) + { + if (compress_type==-1) getline(cin, line); + else if (compress_type) getline(fin_gz, line); + else getline(fin, line); + if (line.size()==0) continue; + if (line.compare(0,11,"_atom_site.")) break; + _atom_site[Trim(line.substr(11))]=++atom_site_pos; + } + + if (_atom_site.count("group_PDB")* + _atom_site.count("label_atom_id")* + _atom_site.count("label_comp_id")* + (_atom_site.count("auth_asym_id")+ + _atom_site.count("label_asym_id"))* + (_atom_site.count("auth_seq_id")+ + _atom_site.count("label_seq_id"))* + _atom_site.count("Cartn_x")* + _atom_site.count("Cartn_y")* + _atom_site.count("Cartn_z")==0) + { + loop_ = false; + cerr<<"Warning! Missing one of the following _atom_site data items: group_PDB, label_atom_id, label_comp_id, auth_asym_id/label_asym_id, auth_seq_id/label_seq_id, Cartn_x, Cartn_y, Cartn_z"<<endl; + continue; + } + } + + line_vec.clear(); + split(line,line_vec); + atom =line_vec[_atom_site["label_atom_id"]]; + resn =line_vec[_atom_site["label_comp_id"]]; + group_PDB=line_vec[_atom_site["group_PDB"]]; + if (group_PDB=="ATOM") group_PDB="ATOM "; + if (mse_opt && resn=="MSE") + { + group_PDB="ATOM "; + if (atom=="SE") atom="SD"; + } + if (group_PDB=="HETATM") + { + if (!lig_opt) continue; + if (!hoh_opt && resn=="HOH") continue; + if (asym_id!=prev_asym_id && prev_asym_id.size()) + asym_id=prev_asym_id; // no separate chain for ligand + } + else if (group_PDB!="ATOM ") continue; + + alt_id="."; + if (_atom_site.count("label_alt_id")) // in 39.4 % of entries + alt_id=line_vec[_atom_site["label_alt_id"]]; + if (alt_id!="." && alt_id!="A") continue; + + if (resn.size()==1) + { + if (!rna_opt && group_PDB=="ATOM ") continue; + resn=" "+resn; + } + else if (resn.size()==2) + { + if (!dna_opt && resn[0]=='D' && group_PDB=="ATOM ") continue; + resn=" " +resn; + } + else if (resn.size()==3 && !protein_opt && group_PDB=="ATOM ") continue; + else if (resn.size()>=4) resn=resn.substr(0,3); + + if (atom[0]=='"') atom=atom.substr(1); + if (atom.size() && atom[atom.size()-1]=='"') + atom=atom.substr(0,atom.size()-1); + if (atom.size()==0) continue; + else if (atom.size()==1) atom=" "+atom+" "; + else if (atom.size()==2) atom=" "+atom+" "; // wrong for sidechain H + else if (atom.size()==3) atom=" "+atom; + else if (atom.size()>=5) atom=atom.substr(0,4); + + if (_atom_site.count("auth_seq_id")) + resi=line_vec[_atom_site["auth_seq_id"]]; + else resi=line_vec[_atom_site["label_seq_id"]]; + if (_atom_site.count("pdbx_PDB_ins_code") && + line_vec[_atom_site["pdbx_PDB_ins_code"]]!="?") + resi+=line_vec[_atom_site["pdbx_PDB_ins_code"]][0]; + else resi+=" "; + if (resi.size()>5) + { + cerr<<"WARNING! Cannot parse line due to long residue index\n"<<line<<endl; + continue; + } + + if (_atom_site.count("auth_asym_id")) + asym_id=line_vec[_atom_site["auth_asym_id"]]; + else asym_id=line_vec[_atom_site["label_asym_id"]]; + if (asym_id==".") asym_id=" "; + if (chain_opt.size() && asym_id!=chain_opt && + !(asym_id==" " && (chain_opt=="_" || chain_opt=="."))) continue; + + if (_atom_site.count("pdbx_PDB_model_num") && + model_index!=line_vec[_atom_site["pdbx_PDB_model_num"]]) + { + if (PDB_lines.size()) break; + model_index=line_vec[_atom_site["pdbx_PDB_model_num"]]; + } + + if (prev_asym_id!=asym_id) + { + PDB_lines.push_back(tmp_str_vec); + chainID_list.push_back(asym_id); + prev_asym_id=asym_id; + } + + a++; + a%=100000; + i8_stream<<group_PDB + <<setw(5)<<a<<" "<<atom<<" "<<resn<<" "<<asym_id[asym_id.size()-1] + <<setw(5)<<resi<<" " + <<setw(8)<<line_vec[_atom_site["Cartn_x"]].substr(0,8) + <<setw(8)<<line_vec[_atom_site["Cartn_y"]].substr(0,8) + <<setw(8)<<line_vec[_atom_site["Cartn_z"]].substr(0,8); + if (_atom_site.count("B_iso_or_equiv")) + { + i8_stream<<" 1.00"<<setw(6)<<line_vec[_atom_site["B_iso_or_equiv"]].substr(0,6); + if (_atom_site.count("type_symbol")) + i8_stream<<setw(12)<<line_vec[_atom_site["type_symbol"]].substr(0,12); + } + i8_stream<<endl; + PDB_lines.back().push_back(i8_stream.str()); + i8_stream.str(string()); + } + group_PDB.clear(); + _atom_site.clear(); + line_vec.clear(); + alt_id.clear(); + asym_id.clear(); + resn.clear(); + + if (compress_type>=0) + { + if (compress_type) fin_gz.close(); + else fin.close(); + } + line.clear(); + chainID_list.push_back(""); + return PDB_lines.size(); +} + + +int main(int argc, char *argv[]) +{ + if (argc < 2) print_help(); + + /**********************/ + /* get argument */ + /**********************/ + string xname = ""; + string yname = ""; + + int split_opt =0; // do not split chain + int het_opt =0; // do not read HETATM residues + int mol_opt =7; // auto-detect the molecule type as protein/RNA + string chain_opt =""; // read all chains + + for(int i = 1; i < argc; i++) + { + if ( !strcmp(argv[i],"-split") && i < (argc-1) ) + { + split_opt=atoi(argv[i + 1]); i++; + } + else if ( !strcmp(argv[i],"-mol") && i < (argc-1) ) + { + mol_opt=atoi(argv[i + 1]); i++; + } + else if ( !strcmp(argv[i],"-chain") && i < (argc-1) ) + { + chain_opt=argv[i + 1]; i++; + } + else if ( !strcmp(argv[i],"-het") && i < (argc-1) ) + { + het_opt=atoi(argv[i + 1]); i++; + } + else if (xname.size() == 0) xname=argv[i]; + else if (yname.size() == 0) yname=argv[i]; + else PrintErrorAndQuit(string("ERROR! Undefined option ")+argv[i]); + } + + if(yname.size()==0) + { + if (xname.size()==0) + PrintErrorAndQuit("Please provide input structures"); + else if (yname.size()==0) yname="-"; + } + + bool dna_opt=(mol_opt>=4); + mol_opt %= 4; + bool rna_opt=(mol_opt>=2); + mol_opt %= 2; + bool protein_opt=(mol_opt>=1); + + if (split_opt<0 || split_opt>1) + PrintErrorAndQuit("-split can only be 0 or 1"); + if (het_opt<0 || het_opt>3) + PrintErrorAndQuit("-het can only be 0, 1, 2, or 3"); + + bool hoh_opt=(het_opt==3); + bool lig_opt=(het_opt>=2); + bool mse_opt=(het_opt>=1); + + /* parse structure */ + vector<vector<string> >PDB_lines; + vector<string> chainID_list; + get_all_mmcif_lines(xname, chain_opt, PDB_lines, chainID_list, + dna_opt, rna_opt, protein_opt, hoh_opt, lig_opt, mse_opt); + if (!split_opt) resolve_chainID_for_mmcif(PDB_lines,chainID_list); + write_mmcif_to_pdb(yname, PDB_lines, chainID_list, split_opt); + + /* clean up */ + vector<vector<string> >().swap(PDB_lines); + vector<string>().swap(chainID_list); + chain_opt.clear(); + return 0; +} diff --git a/modules/bindings/src/USalign/flexalign.h b/modules/bindings/src/USalign/flexalign.h new file mode 100644 index 0000000000000000000000000000000000000000..f982fa921a7d673c9c4e4befe55b30ade580eec3 --- /dev/null +++ b/modules/bindings/src/USalign/flexalign.h @@ -0,0 +1,1826 @@ +/* Functions for the core TMalign algorithm, including the entry function + * flexalign_main */ +#ifndef flexalign_h +#define flexalign_h 1 + +#include "TMalign.h" + +void t_u2tu(double t0[3],double u0[3][3], vector<double> &tu_tmp) +{ + int i,j,k; + for (i=0;i<3;i++) tu_tmp[i]=t0[i]; + k=3; + for (i=0;i<3;i++) for (j=0;j<3;j++) + { + tu_tmp[k]=u0[i][j]; + k++; + } +} + +void tu2t_u(vector<double> tu_tmp, double t0[3],double u0[3][3]) +{ + int i,j,k; + for (i=0;i<3;i++) t0[i]=tu_tmp[i]; + k=3; + for (i=0;i<3;i++) for (j=0;j<3;j++) + { + u0[i][j]=tu_tmp[k]; + k++; + } +} + +void aln2invmap(const string &seqxA, const string &seqyA, int *invmap) +{ + int i,j,r; + int ylen=0; + for (r=0;r<seqyA.size();r++) ylen+=seqyA[r]!='-'; + for(j=0; j<ylen; j++) invmap[j]=-1; + + i=j=-1; + for (r=0;r<seqxA.size();r++) + { + i+=seqxA[r]!='-'; + j+=seqyA[r]!='-'; + if (seqxA[r]!='-' && seqyA[r]!='-') invmap[j]=i; + } +} + +int flexalign_main(double **xa, double **ya, + const char *seqx, const char *seqy, const char *secx, const char *secy, + double t0[3], double u0[3][3], vector<vector<double> >&tu_vec, + double &TM1, double &TM2, double &TM3, double &TM4, double &TM5, + double &d0_0, double &TM_0, + double &d0A, double &d0B, double &d0u, double &d0a, double &d0_out, + string &seqM, string &seqxA, string &seqyA, + double &rmsd0, int &L_ali, double &Liden, + double &TM_ali, double &rmsd_ali, int &n_ali, int &n_ali8, + const int xlen, const int ylen, + const vector<string> sequence, const double Lnorm_ass, + const double d0_scale, const int i_opt, const int a_opt, + const bool u_opt, const bool d_opt, const bool fast_opt, + const int mol_type, const int hinge_opt) +{ + vector<double> tu_tmp(12,0); + int round2=tu_vec.size(); + if (round2==0) + { + TMalign_main(xa, ya, seqx, seqy, secx, secy, t0, u0, + TM1, TM2, TM3, TM4, TM5, d0_0, TM_0, + d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, + d0_scale, i_opt, a_opt, u_opt, d_opt, fast_opt, mol_type); + + t_u2tu(t0,u0,tu_tmp); + tu_vec.push_back(tu_tmp); + } + + int i,j,r; + int* invmap=new int[ylen+1]; + for (j=0;j<ylen+1;j++) invmap[j]=-1; + double **xt; + NewArray(&xt, xlen, 3); + do_rotation(xa, xt, xlen, t0, u0); + + TM1= TM2= TM3= TM4= TM5=rmsd0=0; + seqM=""; + seqxA=""; + seqyA=""; + n_ali=n_ali8=0; + se_main(xt, ya, seqx, seqy, TM1, TM2, TM3, TM4, TM5, d0_0, TM_0, + d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, i_opt, + a_opt, u_opt, d_opt, mol_type, 0, invmap, 1); + if (round2) + { + /* aligned structure A vs unaligned structure B */ + int xlen_h=n_ali8; + int ylen_h=ylen - n_ali8; + char *seqx_h = new char[xlen + 1]; + char *seqy_h = new char[ylen + 1]; + char *secx_h = new char[xlen + 1]; + char *secy_h = new char[ylen + 1]; + seqx_h[xlen]=seqy_h[ylen]=0; + secx_h[xlen]=secy_h[ylen]=0; + double **xa_h, **ya_h; + NewArray(&xa_h, xlen, 3); + NewArray(&ya_h, ylen, 3); + + int r1,r2; + i=j=-1; + r1=r2=0; + for (r=0;r<seqxA.size();r++) + { + i+=(seqxA[r]!='-'); + j+=(seqyA[r]!='-'); + if (seqxA[r]!='-' && seqyA[r]!='-') + { + seqx_h[r1]=seqx[i]; + secx_h[r1]=secx[i]; + xa_h[r1][0]=xa[i][0]; + xa_h[r1][1]=xa[i][1]; + xa_h[r1][2]=xa[i][2]; + r1++; + } + if (seqxA[r]=='-') + { + seqy_h[r2]=seqx[j]; + secy_h[r2]=secx[j]; + ya_h[r2][0]=ya[j][0]; + ya_h[r2][1]=ya[j][1]; + ya_h[r2][2]=ya[j][2]; + r2++; + } + } + + double TM1_h, TM2_h; + double TM3_h, TM4_h, TM5_h; // for a_opt, u_opt, d_opt + double d0_0_h, TM_0_h; + double d0A_h, d0B_h, d0u_h, d0a_h; + double d0_out_h=5.0; + string seqM_h, seqxA_h, seqyA_h;// for output alignment + double rmsd0_h = 0.0; + int L_ali_h=0; // Aligned length in standard_TMscore + double Liden_h=0; + double TM_ali_h, rmsd_ali_h; // TMscore and rmsd in standard_TMscore + int n_ali_h=0; + int n_ali8_h=0; + + TMalign_main(xa_h, ya_h, seqx_h, seqy_h, secx_h, secy_h, t0, u0, + TM1_h, TM2_h, TM3_h, TM4_h, TM5_h, d0_0_h, TM_0_h, + d0A_h, d0B_h, d0u_h, d0a_h, d0_out_h, seqM_h, seqxA_h, seqyA_h, + rmsd0_h, L_ali_h, Liden_h, TM_ali_h, rmsd_ali_h, n_ali_h, n_ali8_h, + xlen_h, ylen_h, sequence, Lnorm_ass, + d0_scale, i_opt, a_opt, u_opt, d_opt, fast_opt, mol_type); + + do_rotation(xa, xt, xlen, t0, u0); + t_u2tu(t0,u0,tu_vec[0]); + + int* invmap_h=new int[ylen+1]; + for (j=0;j<ylen+1;j++) invmap_h[j]=-1; + TM1_h= TM2_h= TM3_h= TM4_h= TM5_h=rmsd0_h=0; + seqM_h=""; + seqxA_h=""; + seqyA_h=""; + n_ali_h=n_ali8_h=0; + se_main(xt, ya, seqx, seqy, TM1_h, TM2_h, TM3_h, TM4_h, TM5_h, d0_0, + TM_0, d0A, d0B, d0u, d0a, d0_out, seqM_h, seqxA_h, seqyA_h, + rmsd0_h, L_ali, Liden, TM_ali, rmsd_ali, n_ali_h, n_ali8_h, + xlen, ylen, sequence, Lnorm_ass, d0_scale, i_opt, + a_opt, u_opt, d_opt, mol_type, 0, invmap_h, 1); + + /* unaligned structure A vs aligned structure B */ + xlen_h=xlen - n_ali8; + ylen_h=n_ali8; + + i=j=-1; + r1=r2=0; + for (r=0;r<seqxA.size();r++) + { + i+=(seqxA[r]!='-'); + j+=(seqyA[r]!='-'); + if (seqyA[r]=='-') + { + seqx_h[r1]=seqx[i]; + secx_h[r1]=secx[i]; + xa_h[r1][0]=xa[i][0]; + xa_h[r1][1]=xa[i][1]; + xa_h[r1][2]=xa[i][2]; + r1++; + } + if (seqxA[r]!='-' && seqyA[r]!='-') + { + seqy_h[r2]=seqx[j]; + secy_h[r2]=secx[j]; + ya_h[r2][0]=ya[j][0]; + ya_h[r2][1]=ya[j][1]; + ya_h[r2][2]=ya[j][2]; + r2++; + } + } + + d0_out_h=5.0; + L_ali_h=Liden_h=0; + TM1= TM2= TM3= TM4= TM5=rmsd0=0; + seqM=""; + seqxA=""; + seqyA=""; + n_ali=n_ali8=0; + + TMalign_main(xa_h, ya_h, seqx_h, seqy_h, secx_h, secy_h, t0, u0, + TM1, TM2, TM3, TM4, TM5, d0_0_h, TM_0_h, + d0A_h, d0B_h, d0u_h, d0a_h, d0_out_h, seqM, seqxA, seqyA, + rmsd0, L_ali_h, Liden_h, TM_ali_h, rmsd_ali_h, n_ali, n_ali8, + xlen_h, ylen_h, sequence, Lnorm_ass, + d0_scale, i_opt, a_opt, u_opt, d_opt, fast_opt, mol_type); + + do_rotation(xa, xt, xlen, t0, u0); + + for (j=0;j<ylen+1;j++) invmap[j]=-1; + TM1= TM2= TM3= TM4= TM5=rmsd0=0; + seqM=""; + seqxA=""; + seqyA=""; + n_ali=n_ali8=0; + se_main(xt, ya, seqx, seqy, TM1, TM2, TM3, TM4, TM5, d0_0, + TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, i_opt, + a_opt, u_opt, d_opt, mol_type, 0, invmap, 1); + + double TM_h=(TM1_h>TM2_h)?TM1_h:TM2_h; + double TM =(TM1 >TM2 )?TM1 :TM2 ; + if (TM_h>TM) + { + TM1=TM1_h; + TM2=TM2_h; + TM3=TM3_h; + TM4=TM4_h; + TM5=TM5_h; + seqM=seqM_h; + seqxA=seqxA_h; + seqyA=seqyA_h; + rmsd0=rmsd0_h; + n_ali=n_ali_h; + n_ali8=n_ali8_h; + for (j=0;j<ylen+1;j++) invmap[j]=invmap_h[j]; + } + else t_u2tu(t0,u0,tu_vec[0]); + + /* clean up */ + delete [] invmap_h; + DeleteArray(&xa_h, xlen); + DeleteArray(&ya_h, ylen); + seqM_h.clear(); + seqxA_h.clear(); + seqyA_h.clear(); + delete [] seqx_h; + delete [] secx_h; + delete [] seqy_h; + delete [] secy_h; + } + for (r=0;r<seqM.size();r++) if (seqM[r]=='1') seqM[r]='0'; + + int minlen = min(xlen, ylen); + int hinge; + for (hinge=0;hinge<hinge_opt;hinge++) + { + if (minlen-n_ali8<5) break; + int xlen_h=xlen - n_ali8; + int ylen_h=ylen - n_ali8; + char *seqx_h = new char[xlen_h + 1]; + char *seqy_h = new char[ylen_h + 1]; + char *secx_h = new char[xlen_h + 1]; + char *secy_h = new char[ylen_h + 1]; + seqx_h[xlen_h]=seqy_h[ylen_h]=0; + secx_h[xlen_h]=secy_h[ylen_h]=0; + double **xa_h, **ya_h; + NewArray(&xa_h, xlen_h, 3); + NewArray(&ya_h, ylen_h, 3); + vector<int> r1toi(xlen_h,0); + vector<int> r2toj(ylen_h,0); + + int r1,r2; + i=j=-1; + r1=r2=0; + for (r=0;r<seqxA.size();r++) + { + i+=(seqxA[r]!='-'); + j+=(seqyA[r]!='-'); + if (seqyA[r]=='-') + { + seqx_h[r1]=seqx[i]; + secx_h[r1]=secx[i]; + xa_h[r1][0]=xa[i][0]; + xa_h[r1][1]=xa[i][1]; + xa_h[r1][2]=xa[i][2]; + r1toi[r1]=i; + r1++; + } + if (seqxA[r]=='-') + { + seqy_h[r2]=seqx[j]; + secy_h[r2]=secx[j]; + ya_h[r2][0]=ya[j][0]; + ya_h[r2][1]=ya[j][1]; + ya_h[r2][2]=ya[j][2]; + r2toj[r2]=j; + r2++; + } + } + + double TM1_h, TM2_h; + double TM3_h, TM4_h, TM5_h; // for a_opt, u_opt, d_opt + double d0_0_h, TM_0_h; + double d0A_h, d0B_h, d0u_h, d0a_h; + double d0_out_h=5.0; + string seqM_h, seqxA_h, seqyA_h;// for output alignment + double rmsd0_h = 0.0; + int L_ali_h=0; // Aligned length in standard_TMscore + double Liden_h=0; + double TM_ali_h, rmsd_ali_h; // TMscore and rmsd in standard_TMscore + int n_ali_h=0; + int n_ali8_h=0; + + TMalign_main(xa_h, ya_h, seqx_h, seqy_h, secx_h, secy_h, t0, u0, + TM1_h, TM2_h, TM3_h, TM4_h, TM5_h, d0_0_h, TM_0_h, + d0A_h, d0B_h, d0u_h, d0a_h, d0_out_h, seqM_h, seqxA_h, seqyA_h, + rmsd0_h, L_ali_h, Liden_h, TM_ali_h, rmsd_ali_h, n_ali_h, n_ali8_h, + xlen_h, ylen_h, sequence, Lnorm_ass, + d0_scale, i_opt, a_opt, u_opt, d_opt, fast_opt, mol_type); + + do_rotation(xa, xt, xlen, t0, u0); + + TM1_h=TM1; + TM2_h=TM2; + TM3_h=TM3; + TM4_h=TM4; + TM5_h=TM5; + seqM_h=seqM; + seqxA_h=seqxA; + seqyA_h=seqyA; + rmsd0_h=rmsd0; + n_ali_h=n_ali; + n_ali8_h=n_ali8; + int* invmap_h=new int[ylen+1]; + for (j=0;j<ylen+1;j++) invmap_h[j]=invmap[j]; + se_main(xt, ya, seqx, seqy, TM1_h, TM2_h, TM3_h, TM4_h, TM5_h, d0_0, TM_0, + d0A, d0B, d0u, d0a, d0_out, seqM_h, seqxA_h, seqyA_h, + rmsd0_h, L_ali, Liden, TM_ali, rmsd_ali, n_ali_h, n_ali8_h, + xlen, ylen, sequence, Lnorm_ass, d0_scale, i_opt, + a_opt, u_opt, d_opt, mol_type, 0, invmap_h, hinge+1); + int new_ali=0; + for (r=0;r<seqM_h.size();r++) new_ali+=(seqM_h[r]==hinge+'1'); + if (n_ali8_h - n_ali8<5) new_ali=0; + if (new_ali>=5) + { + TM1=TM1_h; + TM2=TM2_h; + TM3=TM3_h; + TM4=TM4_h; + TM5=TM5_h; + seqM=seqM_h; + seqxA=seqxA_h; + seqyA=seqyA_h; + rmsd0=rmsd0_h; + n_ali=n_ali_h; + n_ali8=n_ali8_h; + t_u2tu(t0,u0,tu_tmp); + tu_vec.push_back(tu_tmp); + for (j=0;j<ylen+1;j++) invmap[j]=invmap_h[j]; + //cout<<">hinge="<<hinge<<'\n' + //<<seqxA<<'\n'<<seqM<<'\n'<<seqyA<<endl; + //for (j=0;j<ylen;j++) if ((i=invmap[j])>=0) cout<<"("<<i<<","<<j<<")"; + //cout<<endl; + } + + /* clean up */ + delete [] invmap_h; + DeleteArray(&xa_h, xlen_h); + DeleteArray(&ya_h, ylen_h); + r1toi.clear(); + r2toj.clear(); + seqM_h.clear(); + seqxA_h.clear(); + seqyA_h.clear(); + delete [] seqx_h; + delete [] secx_h; + delete [] seqy_h; + delete [] secy_h; + if (new_ali<5) break; + } + + if (tu_vec.size()<=1) + { + DeleteArray(&xt, xlen); + delete[] invmap; + return tu_vec.size(); + } + + /* re-derive alignment based on tu_vec */ + vector<char> seqM_char(ylen,' '); + vector<double> di_vec(ylen,-1); + double d; + for (hinge=tu_vec.size()-1;hinge>=0;hinge--) + { + tu2t_u(tu_vec[hinge],t0,u0); + do_rotation(xa, xt, xlen, t0, u0); + for (j=0;j<ylen;j++) + { + i=invmap[j]; + if (i<0) continue; + d=sqrt(dist(xt[i], ya[j])); + if (di_vec[j]<0 || d<=di_vec[j]) + { + di_vec[j]=d; + seqM_char[j]=hinge+'0'; + } + } + } + j=-1; + for (r=0;r<seqM.size();r++) + { + if (seqyA[r]=='-') continue; + j++; + seqM[r]=seqM_char[j]; + } + + /* smooth out AFP assignment: remove singleton insert */ + for (hinge=tu_vec.size()-1;hinge>=0;hinge--) + { + j=-1; + for (r=0;r<seqM.size();r++) + { + if (seqyA[r]=='-') continue; + j++; + if (seqM_char[j]!=hinge+'0') continue; + if (r<seqM.size()-1 && (seqM[r+1]==hinge+'0' || seqM[r+1]==' ')) + continue; + if (r>0 && (seqM[r-1]==hinge+'0' || seqM[r-1]==' ')) continue; + if (r<seqM.size()-1 && r>0 && seqM[r-1]!=seqM[r+1]) continue; + if (r>0) seqM[r]=seqM_char[j]=seqM[r-1]; + else seqM[r]=seqM_char[j]=seqM[r+1]; + } + } + /* smooth out AFP assignment: remove singleton at the end of fragment */ + char left_hinge=' '; + char right_hinge=' '; + for (hinge=tu_vec.size()-1;hinge>=0;hinge--) + { + j=-1; + for (r=0;r<seqM.size();r++) + { + if (seqyA[r]=='-') continue; + j++; + if (seqM[r]!=hinge+'0') continue; + if (r>0 && seqM[r-1]==' ' && r<seqM.size()-1 && seqM[r+1]==' ') + continue; + + left_hinge=' '; + for (i=r-1;i>=0;i--) + { + if (seqM[i]==' ') continue; + left_hinge=seqM[i]; + break; + } + if (left_hinge==hinge+'0') continue; + + right_hinge=' '; + for (i=r+1;i<seqM.size();i++) + { + if (seqM[i]==' ') continue; + right_hinge=seqM[i]; + break; + } + if (right_hinge==hinge+'0') continue; + if (left_hinge!=right_hinge && left_hinge!=' ' && right_hinge!=' ') + continue; + + if (right_hinge!=' ') seqM[r]=seqM_char[j]=right_hinge; + else if (left_hinge!=' ') seqM[r]=seqM_char[j]=left_hinge; + } + } + /* smooth out AFP assignment: remove dimer insert */ + for (hinge=tu_vec.size()-1;hinge>=0;hinge--) + { + j=-1; + for (r=0;r<seqM.size()-1;r++) + { + if (seqyA[r]=='-') continue; + j++; + if (seqM[r] !=hinge+'0'|| seqM[r+1]!=hinge+'0') continue; + + if (r<seqM.size()-2 && (seqM[r+2]==' ' || seqM[r+2]==hinge+'0')) + continue; + if (r>0 && (seqM[r-1]==' ' || seqM[r-1]==hinge+'0')) continue; + if (r<seqM.size()-2 && r>0 && seqM[r-1]!=seqM[r+2]) continue; + + if (r>0) seqM[r]=seqM_char[j]=seqM[r+1]=seqM_char[j+1]=seqM[r-1]; + else seqM[r]=seqM_char[j]=seqM[r+1]=seqM_char[j+1]=seqM[r+2]; + } + } + /* smooth out AFP assignment: remove disconnected singleton */ + int i1,i2; + for (hinge=tu_vec.size()-1;hinge>=0;hinge--) + { + j=-1; + for (r=0;r<seqM.size();r++) + { + if (seqyA[r]=='-') continue; + j++; + if (seqM[r]!=hinge+'0') continue; + + left_hinge=' '; + for (i=r-1;i>=0;i--) + { + if (seqM[i]==' ') continue; + left_hinge=seqM[i]; + i1=(r-i); + break; + } + if (left_hinge==hinge+'0') continue; + + right_hinge=' '; + for (i=r+1;i<seqM.size();i++) + { + if (seqM[i]==' ') continue; + right_hinge=seqM[i]; + i2=(i-r); + break; + } + if (right_hinge==hinge+'0') continue; + + if (right_hinge==' ') seqM[r]=seqM_char[j]=left_hinge; + else if (left_hinge==' ') seqM[r]=seqM_char[j]=right_hinge; + else + { + if (i1<i2) seqM[r]=seqM_char[j]=left_hinge; + else seqM[r]=seqM_char[j]=right_hinge; + } + } + } + + /* recalculate all scores */ + for (hinge=tu_vec.size()-1;hinge>=0;hinge--) + { + tu2t_u(tu_vec[hinge],t0,u0); + do_rotation(xa, xt, xlen, t0, u0); + for (j=0;j<ylen;j++) + { + i=invmap[j]; + if (i<0) continue; + if (seqM_char[j]!=hinge+'0') continue; + d=sqrt(dist(xt[i], ya[j])); + if (di_vec[j]<0 || d<=di_vec[j]) + { + di_vec[j]=d; + seqM_char[j]=hinge+'0'; + } + } + } + rmsd0=TM1=TM2=TM3=TM4=TM5=0; + Liden=0; + for (r=0;r<seqM.size();r++) if (seqM[r]!=' ') Liden+=seqxA[r]==seqyA[r]; + for(j=0; j<ylen; j++) + { + i=invmap[j]; + if(i<0) continue; + { + d=di_vec[j]; + TM2+=1/(1+(d/d0B)*(d/d0B)); // chain_1 + TM1+=1/(1+(d/d0A)*(d/d0A)); // chain_2 + if (a_opt) TM3+=1/(1+(d/d0a)*(d/d0a)); // -a + if (u_opt) TM4+=1/(1+(d/d0u)*(d/d0u)); // -u + if (d_opt) TM5+=1/(1+(d/d0_scale)*(d/d0_scale)); // -d + rmsd0+=d*d; + } + } + TM2/=xlen; + TM1/=ylen; + TM3/=(xlen+ylen)*0.5; + TM4/=Lnorm_ass; + TM5/=ylen; + if (n_ali8) rmsd0=sqrt(rmsd0/n_ali8); + for (hinge=tu_vec.size()-1;hinge>0;hinge--) + { + int afp_len=0; + for (r=0;r<seqM.size();r++) afp_len+=seqM[r]==hinge+'0'; + if (afp_len) break; + tu_vec.pop_back(); // remove unnecessary afp + } + + /* clean up */ + seqM_char.clear(); + di_vec.clear(); + DeleteArray(&xt, xlen); + delete[] invmap; + return tu_vec.size(); +} + +/* extract rotation matrix based on TMscore8 */ +void output_flexalign_rotation_matrix(const char* fname_matrix, + const vector<vector<double> >&tu_vec, double t[3], double u[3][3]) +{ + stringstream ss; + char dest[1000]; + for (int hinge=0;hinge<tu_vec.size();hinge++) + { + tu2t_u(tu_vec[hinge],t,u); + ss << "------ The rotation matrix to rotate Structure_1 to Structure_2 ------\n"; + sprintf(dest, "m %18s %14s %14s %14s\n", "t[m]", "u[m][0]", "u[m][1]", "u[m][2]"); + ss << string(dest); + for (int k = 0; k < 3; k++) + { + sprintf(dest, "%d %18.10f %14.10f %14.10f %14.10f\n", k, t[k], u[k][0], u[k][1], u[k][2]); + ss << string(dest); + } + } + ss << "\nCode for rotating Structure 1 from (x,y,z) to (X,Y,Z):\n" + "for(i=0; i<L; i++)\n" + "{\n" + " X[i] = t[0] + u[0][0]*x[i] + u[0][1]*y[i] + u[0][2]*z[i];\n" + " Y[i] = t[1] + u[1][0]*x[i] + u[1][1]*y[i] + u[1][2]*z[i];\n" + " Z[i] = t[2] + u[2][0]*x[i] + u[2][1]*y[i] + u[2][2]*z[i];\n" + "}\n"; + if (strcmp(fname_matrix,(char *)("-"))==0) + cout<<ss.str(); + else + { + fstream fout; + fout.open(fname_matrix, ios::out | ios::trunc); + if (fout) + { + fout<<ss.str(); + fout.close(); + } + else cout << "Open file to output rotation matrix fail.\n"; + } + ss.str(string()); +} + +void output_flexalign_rasmol(const string xname, const string yname, + const string fname_super,const vector<vector<double> >&tu_vec, + double t[3], double u[3][3], const int ter_opt, + const int mm_opt, const int split_opt, const int mirror_opt, + const char *seqM, const char *seqxA, const char *seqyA, + const vector<string>&resi_vec1, const vector<string>&resi_vec2, + const string chainID1, const string chainID2, + const int xlen, const int ylen, const double d0A, const int n_ali8, + const double rmsd, const double TM1, const double Liden) +{ + stringstream buf; + stringstream buf_all; + stringstream buf_atm; + stringstream buf_all_atm; + stringstream buf_all_atm_lig; + //stringstream buf_pdb; + stringstream buf_tm; + string line; + double x[3]; // before transform + double x1[3]; // after transform + bool after_ter; // true if passed the "TER" line in PDB + string asym_id; // chain ID + + map<string,int> resi2hinge_dict; + int r,i,j; + j=-1; + char hinge_char=0; + int ali_len=strlen(seqM); + for (r=0;r<strlen(seqxA);r++) + { + if (seqxA[r]=='-') continue; + j++; + hinge_char=seqM[r]; + if (hinge_char==' ') + { + for (i=1;i<ali_len;i++) + { + if (r-i>=0 && seqM[r-i]!=' ') + hinge_char=seqM[r-i]; + else if (r+i<xlen && seqM[r+i]!=' ') + hinge_char=seqM[r+i]; + if (hinge_char!=' ') break; + } + } + resi2hinge_dict[resi_vec1[j]]=hinge_char-'0'; + } + string resi=resi_vec1[0]; + int read_resi=resi.size()-4; + + buf_tm<<"REMARK US-align" + <<"\nREMARK Structure 1:"<<setw(11)<<left<<xname+chainID1<<" Size= "<<xlen + <<"\nREMARK Structure 2:"<<setw(11)<<yname+chainID2<<right<<" Size= "<<ylen + <<" (TM-score is normalized by "<<setw(4)<<ylen<<", d0=" + <<setiosflags(ios::fixed)<<setprecision(2)<<setw(6)<<d0A<<")" + <<"\nREMARK Aligned length="<<setw(4)<<n_ali8<<", RMSD=" + <<setw(6)<<setiosflags(ios::fixed)<<setprecision(2)<<rmsd + <<", TM-score="<<setw(7)<<setiosflags(ios::fixed)<<setprecision(5)<<TM1 + <<", ID="<<setw(5)<<setiosflags(ios::fixed)<<setprecision(3) + <<((n_ali8>0)?Liden/n_ali8:0)<<endl; + string rasmol_CA_header="load inline\nselect *A\nwireframe .45\nselect *B\nwireframe .20\nselect all\ncolor white\n"; + string rasmol_cartoon_header="load inline\nselect all\ncartoon\nselect *A\ncolor blue\nselect *B\ncolor red\nselect ligand\nwireframe 0.25\nselect solvent\nspacefill 0.25\nselect all\nexit\n"+buf_tm.str(); + if (!mm_opt) buf<<rasmol_CA_header; + buf_all<<rasmol_CA_header; + if (!mm_opt) buf_atm<<rasmol_cartoon_header; + buf_all_atm<<rasmol_cartoon_header; + buf_all_atm_lig<<rasmol_cartoon_header; + + /* selecting chains for -mol */ + string chain1_sele; + string chain2_sele; + if (!mm_opt) + { + if (split_opt==2 && ter_opt>=1) // align one chain from model 1 + { + chain1_sele=chainID1.substr(1); + chain2_sele=chainID2.substr(1); + } + else if (split_opt==2 && ter_opt==0) // align one chain from each model + { + for (i=1;i<chainID1.size();i++) if (chainID1[i]==',') break; + chain1_sele=chainID1.substr(i+1); + for (i=1;i<chainID2.size();i++) if (chainID2[i]==',') break; + chain2_sele=chainID2.substr(i+1); + } + } + + + /* for PDBx/mmCIF only */ + map<string,int> _atom_site; + int atom_site_pos; + vector<string> line_vec; + string atom; // 4-character atom name + string AA; // 3-character residue name + string inscode; // 1-character insertion code + string model_index; // model index + bool is_mmcif=false; + + /* used for CONECT record of chain1 */ + int ca_idx1=0; // all CA atoms + int lig_idx1=0; // all atoms + vector <int> idx_vec; + + /* used for CONECT record of chain2 */ + int ca_idx2=0; // all CA atoms + int lig_idx2=0; // all atoms + + /* extract aligned region */ + vector<string> resi_aln1; + vector<string> resi_aln2; + int i1=-1; + int i2=-1; + if (!mm_opt) + { + for (i=0;i<strlen(seqM);i++) + { + i1+=(seqxA[i]!='-'); + i2+=(seqyA[i]!='-'); + if (seqM[i]==' ') continue; + resi_aln1.push_back(resi_vec1[i1].substr(0,4)); + resi_aln2.push_back(resi_vec2[i2].substr(0,4)); + if (seqM[i]!=':') continue; + buf <<"select "<<resi_aln1.back()<<":A," + <<resi_aln2.back()<<":B\ncolor red\n"; + buf_all<<"select "<<resi_aln1.back()<<":A," + <<resi_aln2.back()<<":B\ncolor red\n"; + } + buf<<"select all\nexit\n"<<buf_tm.str(); + } + buf_all<<"select all\nexit\n"<<buf_tm.str(); + + ifstream fin; + /* read first file */ + after_ter=false; + asym_id=""; + fin.open(xname.c_str()); + int hinge=0; + while (fin.good()) + { + getline(fin, line); + if (ter_opt>=3 && line.compare(0,3,"TER")==0) after_ter=true; + if (is_mmcif==false && line.size()>=54 && + (line.compare(0, 6, "ATOM ")==0 || + line.compare(0, 6, "HETATM")==0)) // PDB format + { + if (line[16]!='A' && line[16]!=' ') continue; + x[0]=atof(line.substr(30,8).c_str()); + x[1]=atof(line.substr(38,8).c_str()); + x[2]=atof(line.substr(46,8).c_str()); + if (mirror_opt) x[2]=-x[2]; + if (read_resi==1) resi=line.substr(22,5); + else resi=line.substr(22,5)+line[21]; + hinge=0; + if (resi2hinge_dict.count(resi)) hinge=resi2hinge_dict[resi]; + tu2t_u(tu_vec[hinge],t,u); + transform(t, u, x, x1); + //buf_pdb<<line.substr(0,30)<<setiosflags(ios::fixed) + //<<setprecision(3) + //<<setw(8)<<x1[0] <<setw(8)<<x1[1] <<setw(8)<<x1[2] + //<<line.substr(54)<<'\n'; + + if (after_ter && line.compare(0,6,"ATOM ")==0) continue; + lig_idx1++; + buf_all_atm_lig<<line.substr(0,6)<<setw(5)<<lig_idx1 + <<line.substr(11,9)<<" A"<<line.substr(22,8) + <<setiosflags(ios::fixed)<<setprecision(3) + <<setw(8)<<x1[0]<<setw(8)<<x1[1] <<setw(8)<<x1[2]<<'\n'; + if (chain1_sele.size() && line[21]!=chain1_sele[0]) continue; + if (after_ter || line.compare(0,6,"ATOM ")) continue; + if (ter_opt>=2) + { + if (ca_idx1 && asym_id.size() && asym_id!=line.substr(21,1)) + { + after_ter=true; + continue; + } + asym_id=line[21]; + } + buf_all_atm<<"ATOM "<<setw(5)<<lig_idx1 + <<line.substr(11,9)<<" A"<<line.substr(22,8) + <<setiosflags(ios::fixed)<<setprecision(3) + <<setw(8)<<x1[0]<<setw(8)<<x1[1] <<setw(8)<<x1[2]<<'\n'; + if (!mm_opt && find(resi_aln1.begin(),resi_aln1.end(), + line.substr(22,4))!=resi_aln1.end()) + { + buf_atm<<"ATOM "<<setw(5)<<lig_idx1 + <<line.substr(11,9)<<" A"<<line.substr(22,8) + <<setiosflags(ios::fixed)<<setprecision(3) + <<setw(8)<<x1[0]<<setw(8)<<x1[1] <<setw(8)<<x1[2]<<'\n'; + } + if (line.substr(12,4)!=" CA " && line.substr(12,4)!=" C3'") continue; + ca_idx1++; + buf_all<<"ATOM "<<setw(5)<<ca_idx1<<' ' + <<line.substr(12,4)<<' '<<line.substr(17,3)<<" A"<<line.substr(22,8) + <<setiosflags(ios::fixed)<<setprecision(3) + <<setw(8)<<x1[0]<<setw(8)<<x1[1]<<setw(8)<<x1[2]<<'\n'; + if (find(resi_aln1.begin(),resi_aln1.end(), + line.substr(22,4))==resi_aln1.end()) continue; + if (!mm_opt) buf<<"ATOM "<<setw(5)<<ca_idx1<<' ' + <<line.substr(12,4)<<' '<<line.substr(17,3)<<" A"<<line.substr(22,8) + <<setiosflags(ios::fixed)<<setprecision(3) + <<setw(8)<<x1[0]<<setw(8)<<x1[1]<<setw(8)<<x1[2]<<'\n'; + idx_vec.push_back(ca_idx1); + } + else if (line.compare(0,5,"loop_")==0) // PDBx/mmCIF + { + while(1) + { + if (fin.good()) getline(fin, line); + else PrintErrorAndQuit("ERROR! Unexpected end of "+xname); + if (line.size()) break; + } + if (line.compare(0,11,"_atom_site.")) continue; + _atom_site.clear(); + atom_site_pos=0; + _atom_site[line.substr(11,line.size()-12)]=atom_site_pos; + while(1) + { + if (fin.good()) getline(fin, line); + else PrintErrorAndQuit("ERROR! Unexpected end of "+xname); + if (line.size()==0) continue; + if (line.compare(0,11,"_atom_site.")) break; + _atom_site[line.substr(11,line.size()-12)]=++atom_site_pos; + } + + if (is_mmcif==false) + { + //buf_pdb.str(string()); + is_mmcif=true; + } + + while(1) + { + line_vec.clear(); + split(line,line_vec); + if (line_vec[_atom_site["group_PDB"]]!="ATOM" && + line_vec[_atom_site["group_PDB"]]!="HETATM") break; + if (_atom_site.count("pdbx_PDB_model_num")) + { + if (model_index.size() && model_index!= + line_vec[_atom_site["pdbx_PDB_model_num"]]) + break; + model_index=line_vec[_atom_site["pdbx_PDB_model_num"]]; + } + + x[0]=atof(line_vec[_atom_site["Cartn_x"]].c_str()); + x[1]=atof(line_vec[_atom_site["Cartn_y"]].c_str()); + x[2]=atof(line_vec[_atom_site["Cartn_z"]].c_str()); + if (mirror_opt) x[2]=-x[2]; + + + if (_atom_site.count("auth_seq_id")) + resi=line_vec[_atom_site["auth_seq_id"]]; + else resi=line_vec[_atom_site["label_seq_id"]]; + if (_atom_site.count("pdbx_PDB_ins_code") && + line_vec[_atom_site["pdbx_PDB_ins_code"]]!="?") + resi+=line_vec[_atom_site["pdbx_PDB_ins_code"]][0]; + else resi+=" "; + if (read_resi>=2) + { + if (_atom_site.count("auth_asym_id")) + asym_id=line_vec[_atom_site["auth_asym_id"]]; + else asym_id=line_vec[_atom_site["label_asym_id"]]; + if (asym_id==".") asym_id=" "; + resi+=asym_id[0]; + } + hinge=0; + if (resi2hinge_dict.count(resi)) hinge=resi2hinge_dict[resi]; + tu2t_u(tu_vec[hinge],t,u); + transform(t, u, x, x1); + + if (_atom_site.count("label_alt_id")==0 || + line_vec[_atom_site["label_alt_id"]]=="." || + line_vec[_atom_site["label_alt_id"]]=="A") + { + atom=line_vec[_atom_site["label_atom_id"]]; + if (atom[0]=='"') atom=atom.substr(1); + if (atom.size() && atom[atom.size()-1]=='"') + atom=atom.substr(0,atom.size()-1); + if (atom.size()==0) atom=" "; + else if (atom.size()==1) atom=" "+atom+" "; + else if (atom.size()==2) atom=" "+atom+" "; + else if (atom.size()==3) atom=" "+atom; + else if (atom.size()>=5) atom=atom.substr(0,4); + + AA=line_vec[_atom_site["label_comp_id"]]; // residue name + if (AA.size()==1) AA=" "+AA; + else if (AA.size()==2) AA=" " +AA; + else if (AA.size()>=4) AA=AA.substr(0,3); + + if (_atom_site.count("auth_seq_id")) + resi=line_vec[_atom_site["auth_seq_id"]]; + else resi=line_vec[_atom_site["label_seq_id"]]; + while (resi.size()<4) resi=' '+resi; + if (resi.size()>4) resi=resi.substr(0,4); + + inscode=' '; + if (_atom_site.count("pdbx_PDB_ins_code") && + line_vec[_atom_site["pdbx_PDB_ins_code"]]!="?") + inscode=line_vec[_atom_site["pdbx_PDB_ins_code"]][0]; + + if (_atom_site.count("auth_asym_id")) + { + if (chain1_sele.size()) after_ter + =line_vec[_atom_site["auth_asym_id"]]!=chain1_sele; + else if (ter_opt>=2 && ca_idx1 && asym_id.size() && + asym_id!=line_vec[_atom_site["auth_asym_id"]]) + after_ter=true; + asym_id=line_vec[_atom_site["auth_asym_id"]]; + } + else if (_atom_site.count("label_asym_id")) + { + if (chain1_sele.size()) after_ter + =line_vec[_atom_site["label_asym_id"]]!=chain1_sele; + if (ter_opt>=2 && ca_idx1 && asym_id.size() && + asym_id!=line_vec[_atom_site["label_asym_id"]]) + after_ter=true; + asym_id=line_vec[_atom_site["label_asym_id"]]; + } + //buf_pdb<<left<<setw(6) + //<<line_vec[_atom_site["group_PDB"]]<<right + //<<setw(5)<<lig_idx1%100000<<' '<<atom<<' ' + //<<AA<<" "<<asym_id[asym_id.size()-1] + //<<resi<<inscode<<" " + //<<setiosflags(ios::fixed)<<setprecision(3) + //<<setw(8)<<x1[0] + //<<setw(8)<<x1[1] + //<<setw(8)<<x1[2]<<'\n'; + + if (after_ter==false || + line_vec[_atom_site["group_pdb"]]=="HETATM") + { + lig_idx1++; + buf_all_atm_lig<<left<<setw(6) + <<line_vec[_atom_site["group_PDB"]]<<right + <<setw(5)<<lig_idx1%100000<<' '<<atom<<' ' + <<AA<<" A"<<resi<<inscode<<" " + <<setiosflags(ios::fixed)<<setprecision(3) + <<setw(8)<<x1[0] + <<setw(8)<<x1[1] + <<setw(8)<<x1[2]<<'\n'; + if (after_ter==false && + line_vec[_atom_site["group_PDB"]]=="ATOM") + { + buf_all_atm<<"ATOM "<<setw(6) + <<setw(5)<<lig_idx1%100000<<' '<<atom<<' ' + <<AA<<" A"<<resi<<inscode<<" " + <<setiosflags(ios::fixed)<<setprecision(3) + <<setw(8)<<x1[0] + <<setw(8)<<x1[1] + <<setw(8)<<x1[2]<<'\n'; + if (!mm_opt && find(resi_aln1.begin(), + resi_aln1.end(),resi)!=resi_aln1.end()) + { + buf_atm<<"ATOM "<<setw(6) + <<setw(5)<<lig_idx1%100000<<' ' + <<atom<<' '<<AA<<" A"<<resi<<inscode<<" " + <<setiosflags(ios::fixed)<<setprecision(3) + <<setw(8)<<x1[0] + <<setw(8)<<x1[1] + <<setw(8)<<x1[2]<<'\n'; + } + if (atom==" CA " || atom==" C3'") + { + ca_idx1++; + //mm_opt, split_opt, mirror_opt, chainID1,chainID2); + buf_all<<"ATOM "<<setw(6) + <<setw(5)<<ca_idx1%100000<<' '<<atom<<' ' + <<AA<<" A"<<resi<<inscode<<" " + <<setiosflags(ios::fixed)<<setprecision(3) + <<setw(8)<<x1[0] + <<setw(8)<<x1[1] + <<setw(8)<<x1[2]<<'\n'; + if (!mm_opt && find(resi_aln1.begin(), + resi_aln1.end(),resi)!=resi_aln1.end()) + { + buf<<"ATOM "<<setw(6) + <<setw(5)<<ca_idx1%100000<<' '<<atom<<' ' + <<AA<<" A"<<resi<<inscode<<" " + <<setiosflags(ios::fixed)<<setprecision(3) + <<setw(8)<<x1[0] + <<setw(8)<<x1[1] + <<setw(8)<<x1[2]<<'\n'; + idx_vec.push_back(ca_idx1); + } + } + } + } + } + + while(1) + { + if (fin.good()) getline(fin, line); + else break; + if (line.size()) break; + } + } + } + else if (line.size() && is_mmcif==false) + { + //buf_pdb<<line<<'\n'; + if (ter_opt>=1 && line.compare(0,3,"END")==0) break; + } + } + fin.close(); + if (!mm_opt) buf<<"TER\n"; + buf_all<<"TER\n"; + if (!mm_opt) buf_atm<<"TER\n"; + buf_all_atm<<"TER\n"; + buf_all_atm_lig<<"TER\n"; + for (i=1;i<ca_idx1;i++) buf_all<<"CONECT" + <<setw(5)<<i%100000<<setw(5)<<(i+1)%100000<<'\n'; + if (!mm_opt) for (i=1;i<idx_vec.size();i++) buf<<"CONECT" + <<setw(5)<<idx_vec[i-1]%100000<<setw(5)<<idx_vec[i]%100000<<'\n'; + idx_vec.clear(); + + /* read second file */ + after_ter=false; + asym_id=""; + fin.open(yname.c_str()); + while (fin.good()) + { + getline(fin, line); + if (ter_opt>=3 && line.compare(0,3,"TER")==0) after_ter=true; + if (line.size()>=54 && (line.compare(0, 6, "ATOM ")==0 || + line.compare(0, 6, "HETATM")==0)) // PDB format + { + if (line[16]!='A' && line[16]!=' ') continue; + if (after_ter && line.compare(0,6,"ATOM ")==0) continue; + lig_idx2++; + buf_all_atm_lig<<line.substr(0,6)<<setw(5)<<lig_idx1+lig_idx2 + <<line.substr(11,9)<<" B"<<line.substr(22,32)<<'\n'; + if (chain1_sele.size() && line[21]!=chain1_sele[0]) continue; + if (after_ter || line.compare(0,6,"ATOM ")) continue; + if (ter_opt>=2) + { + if (ca_idx2 && asym_id.size() && asym_id!=line.substr(21,1)) + { + after_ter=true; + continue; + } + asym_id=line[21]; + } + buf_all_atm<<"ATOM "<<setw(5)<<lig_idx1+lig_idx2 + <<line.substr(11,9)<<" B"<<line.substr(22,32)<<'\n'; + if (!mm_opt && find(resi_aln2.begin(),resi_aln2.end(), + line.substr(22,4))!=resi_aln2.end()) + { + buf_atm<<"ATOM "<<setw(5)<<lig_idx1+lig_idx2 + <<line.substr(11,9)<<" B"<<line.substr(22,32)<<'\n'; + } + if (line.substr(12,4)!=" CA " && line.substr(12,4)!=" C3'") continue; + ca_idx2++; + buf_all<<"ATOM "<<setw(5)<<ca_idx1+ca_idx2<<' '<<line.substr(12,4) + <<' '<<line.substr(17,3)<<" B"<<line.substr(22,32)<<'\n'; + if (find(resi_aln2.begin(),resi_aln2.end(),line.substr(22,4) + )==resi_aln2.end()) continue; + if (!mm_opt) buf<<"ATOM "<<setw(5)<<ca_idx1+ca_idx2<<' ' + <<line.substr(12,4)<<' '<<line.substr(17,3)<<" B" + <<line.substr(22,32)<<'\n'; + idx_vec.push_back(ca_idx1+ca_idx2); + } + else if (line.compare(0,5,"loop_")==0) // PDBx/mmCIF + { + while(1) + { + if (fin.good()) getline(fin, line); + else PrintErrorAndQuit("ERROR! Unexpected end of "+yname); + if (line.size()) break; + } + if (line.compare(0,11,"_atom_site.")) continue; + _atom_site.clear(); + atom_site_pos=0; + _atom_site[line.substr(11,line.size()-12)]=atom_site_pos; + while(1) + { + if (fin.good()) getline(fin, line); + else PrintErrorAndQuit("ERROR! Unexpected end of "+yname); + if (line.size()==0) continue; + if (line.compare(0,11,"_atom_site.")) break; + _atom_site[line.substr(11,line.size()-12)]=++atom_site_pos; + } + + while(1) + { + line_vec.clear(); + split(line,line_vec); + if (line_vec[_atom_site["group_PDB"]]!="ATOM" && + line_vec[_atom_site["group_PDB"]]!="HETATM") break; + if (_atom_site.count("pdbx_PDB_model_num")) + { + if (model_index.size() && model_index!= + line_vec[_atom_site["pdbx_PDB_model_num"]]) + break; + model_index=line_vec[_atom_site["pdbx_PDB_model_num"]]; + } + + if (_atom_site.count("label_alt_id")==0 || + line_vec[_atom_site["label_alt_id"]]=="." || + line_vec[_atom_site["label_alt_id"]]=="A") + { + atom=line_vec[_atom_site["label_atom_id"]]; + if (atom[0]=='"') atom=atom.substr(1); + if (atom.size() && atom[atom.size()-1]=='"') + atom=atom.substr(0,atom.size()-1); + if (atom.size()==0) atom=" "; + else if (atom.size()==1) atom=" "+atom+" "; + else if (atom.size()==2) atom=" "+atom+" "; + else if (atom.size()==3) atom=" "+atom; + else if (atom.size()>=5) atom=atom.substr(0,4); + + AA=line_vec[_atom_site["label_comp_id"]]; // residue name + if (AA.size()==1) AA=" "+AA; + else if (AA.size()==2) AA=" " +AA; + else if (AA.size()>=4) AA=AA.substr(0,3); + + if (_atom_site.count("auth_seq_id")) + resi=line_vec[_atom_site["auth_seq_id"]]; + else resi=line_vec[_atom_site["label_seq_id"]]; + while (resi.size()<4) resi=' '+resi; + if (resi.size()>4) resi=resi.substr(0,4); + + inscode=' '; + if (_atom_site.count("pdbx_PDB_ins_code") && + line_vec[_atom_site["pdbx_PDB_ins_code"]]!="?") + inscode=line_vec[_atom_site["pdbx_PDB_ins_code"]][0]; + + if (_atom_site.count("auth_asym_id")) + { + if (chain2_sele.size()) after_ter + =line_vec[_atom_site["auth_asym_id"]]!=chain2_sele; + if (ter_opt>=2 && ca_idx2 && asym_id.size() && + asym_id!=line_vec[_atom_site["auth_asym_id"]]) + after_ter=true; + asym_id=line_vec[_atom_site["auth_asym_id"]]; + } + else if (_atom_site.count("label_asym_id")) + { + if (chain2_sele.size()) after_ter + =line_vec[_atom_site["label_asym_id"]]!=chain2_sele; + if (ter_opt>=2 && ca_idx2 && asym_id.size() && + asym_id!=line_vec[_atom_site["label_asym_id"]]) + after_ter=true; + asym_id=line_vec[_atom_site["label_asym_id"]]; + } + if (after_ter==false || + line_vec[_atom_site["group_PDB"]]=="HETATM") + { + lig_idx2++; + buf_all_atm_lig<<left<<setw(6) + <<line_vec[_atom_site["group_PDB"]]<<right + <<setw(5)<<(lig_idx1+lig_idx2)%100000<<' ' + <<atom<<' '<<AA<<" B"<<resi<<inscode<<" " + <<setw(8)<<line_vec[_atom_site["Cartn_x"]] + <<setw(8)<<line_vec[_atom_site["Cartn_y"]] + <<setw(8)<<line_vec[_atom_site["Cartn_z"]] + <<'\n'; + if (after_ter==false && + line_vec[_atom_site["group_PDB"]]=="ATOM") + { + buf_all_atm<<"ATOM "<<setw(6) + <<setw(5)<<(lig_idx1+lig_idx2)%100000<<' ' + <<atom<<' '<<AA<<" B"<<resi<<inscode<<" " + <<setw(8)<<line_vec[_atom_site["Cartn_x"]] + <<setw(8)<<line_vec[_atom_site["Cartn_y"]] + <<setw(8)<<line_vec[_atom_site["Cartn_z"]] + <<'\n'; + if (!mm_opt && find(resi_aln2.begin(), + resi_aln2.end(),resi)!=resi_aln2.end()) + { + buf_atm<<"ATOM "<<setw(6) + <<setw(5)<<(lig_idx1+lig_idx2)%100000<<' ' + <<atom<<' '<<AA<<" B"<<resi<<inscode<<" " + <<setw(8)<<line_vec[_atom_site["Cartn_x"]] + <<setw(8)<<line_vec[_atom_site["Cartn_y"]] + <<setw(8)<<line_vec[_atom_site["Cartn_z"]] + <<'\n'; + } + if (atom==" CA " || atom==" C3'") + { + ca_idx2++; + buf_all<<"ATOM "<<setw(6) + <<setw(5)<<(ca_idx1+ca_idx2)%100000 + <<' '<<atom<<' '<<AA<<" B"<<resi<<inscode<<" " + <<setw(8)<<line_vec[_atom_site["Cartn_x"]] + <<setw(8)<<line_vec[_atom_site["Cartn_y"]] + <<setw(8)<<line_vec[_atom_site["Cartn_z"]] + <<'\n'; + if (!mm_opt && find(resi_aln2.begin(), + resi_aln2.end(),resi)!=resi_aln2.end()) + { + buf<<"ATOM "<<setw(6) + <<setw(5)<<(ca_idx1+ca_idx2)%100000 + <<' '<<atom<<' '<<AA<<" B"<<resi<<inscode<<" " + <<setw(8)<<line_vec[_atom_site["Cartn_x"]] + <<setw(8)<<line_vec[_atom_site["Cartn_y"]] + <<setw(8)<<line_vec[_atom_site["Cartn_z"]] + <<'\n'; + idx_vec.push_back(ca_idx1+ca_idx2); + } + } + } + } + } + + if (fin.good()) getline(fin, line); + else break; + } + } + else if (line.size()) + { + if (ter_opt>=1 && line.compare(0,3,"END")==0) break; + } + } + fin.close(); + if (!mm_opt) buf<<"TER\n"; + buf_all<<"TER\n"; + if (!mm_opt) buf_atm<<"TER\n"; + buf_all_atm<<"TER\n"; + buf_all_atm_lig<<"TER\n"; + for (i=ca_idx1+1;i<ca_idx1+ca_idx2;i++) buf_all<<"CONECT" + <<setw(5)<<i%100000<<setw(5)<<(i+1)%100000<<'\n'; + for (i=1;i<idx_vec.size();i++) buf<<"CONECT" + <<setw(5)<<idx_vec[i-1]%100000<<setw(5)<<idx_vec[i]%100000<<'\n'; + idx_vec.clear(); + + /* write pymol script */ + ofstream fp; + /* + stringstream buf_pymol; + vector<string> pml_list; + pml_list.push_back(fname_super+""); + pml_list.push_back(fname_super+"_atm"); + pml_list.push_back(fname_super+"_all"); + pml_list.push_back(fname_super+"_all_atm"); + pml_list.push_back(fname_super+"_all_atm_lig"); + for (i=0;i<pml_list.size();i++) + { + buf_pymol<<"#!/usr/bin/env pymol\n" + <<"load "<<pml_list[i]<<"\n" + <<"hide all\n" + <<((i==0 || i==2)?("show stick\n"):("show cartoon\n")) + <<"color blue, chain A\n" + <<"color red, chain B\n" + <<"set ray_shadow, 0\n" + <<"set stick_radius, 0.3\n" + <<"set sphere_scale, 0.25\n" + <<"show stick, not polymer\n" + <<"show sphere, not polymer\n" + <<"bg_color white\n" + <<"set transparency=0.2\n" + <<"zoom polymer\n" + <<endl; + fp.open((pml_list[i]+".pml").c_str()); + fp<<buf_pymol.str(); + fp.close(); + buf_pymol.str(string()); + pml_list[i].clear(); + } + pml_list.clear(); + */ + + /* write rasmol script */ + if (!mm_opt) + { + fp.open((fname_super).c_str()); + fp<<buf.str(); + fp.close(); + } + fp.open((fname_super+"_all").c_str()); + fp<<buf_all.str(); + fp.close(); + if (!mm_opt) + { + fp.open((fname_super+"_atm").c_str()); + fp<<buf_atm.str(); + fp.close(); + } + fp.open((fname_super+"_all_atm").c_str()); + fp<<buf_all_atm.str(); + fp.close(); + fp.open((fname_super+"_all_atm_lig").c_str()); + fp<<buf_all_atm_lig.str(); + fp.close(); + //fp.open((fname_super+".pdb").c_str()); + //fp<<buf_pdb.str(); + //fp.close(); + + /* clear stream */ + buf.str(string()); + buf_all.str(string()); + buf_atm.str(string()); + buf_all_atm.str(string()); + buf_all_atm_lig.str(string()); + //buf_pdb.str(string()); + buf_tm.str(string()); + resi_aln1.clear(); + resi_aln2.clear(); + asym_id.clear(); + line_vec.clear(); + atom.clear(); + AA.clear(); + resi.clear(); + inscode.clear(); + model_index.clear(); +} + +void output_flexalign_pymol(const string xname, const string yname, + const string fname_super, const vector<vector<double> >&tu_vec, + double t[3], double u[3][3], const int ter_opt, + const int mm_opt, const int split_opt, const int mirror_opt, + const char *seqM, const char *seqxA, const char *seqyA, + const vector<string>&resi_vec1, const vector<string>&resi_vec2, + const string chainID1, const string chainID2) +{ + int compress_type=0; // uncompressed file + ifstream fin; +#ifndef REDI_PSTREAM_H_SEEN + ifstream fin_gz; +#else + redi::ipstream fin_gz; // if file is compressed + if (xname.size()>=3 && + xname.substr(xname.size()-3,3)==".gz") + { + fin_gz.open("gunzip -c "+xname); + compress_type=1; + } + else if (xname.size()>=4 && + xname.substr(xname.size()-4,4)==".bz2") + { + fin_gz.open("bzcat "+xname); + compress_type=2; + } + else +#endif + fin.open(xname.c_str()); + + map<string,int> resi2hinge_dict; + int r,i,j; + j=-1; + char hinge_char=0; + int xlen=resi_vec1.size(); + int ali_len=strlen(seqM); + for (r=0;r<strlen(seqxA);r++) + { + if (seqxA[r]=='-') continue; + j++; + hinge_char=seqM[r]; + if (hinge_char==' ') + { + for (i=1;i<ali_len;i++) + { + if (r-i>=0 && seqM[r-i]!=' ') + hinge_char=seqM[r-i]; + else if (r+i<xlen && seqM[r+i]!=' ') + hinge_char=seqM[r+i]; + if (hinge_char!=' ') break; + } + } + resi2hinge_dict[resi_vec1[j]]=hinge_char-'0'; + } + string resi=resi_vec1[0]; + int read_resi=resi.size()-4; + + stringstream buf; + stringstream buf_pymol; + string line; + double x[3]; // before transform + double x1[3]; // after transform + + /* for PDBx/mmCIF only */ + map<string,int> _atom_site; + size_t atom_site_pos; + vector<string> line_vec; + int infmt=-1; // 0 - PDB, 3 - PDBx/mmCIF + int hinge=0; + string asym_id="."; // this is similar to chainID, except that + // chainID is char while asym_id is a string + // with possibly multiple char + while (compress_type?fin_gz.good():fin.good()) + { + if (compress_type) getline(fin_gz, line); + else getline(fin, line); + if (line.compare(0, 6, "ATOM ")==0 || + line.compare(0, 6, "HETATM")==0) // PDB format + { + infmt=0; + x[0]=atof(line.substr(30,8).c_str()); + x[1]=atof(line.substr(38,8).c_str()); + x[2]=atof(line.substr(46,8).c_str()); + if (mirror_opt) x[2]=-x[2]; + if (read_resi==1) resi=line.substr(22,5); + else resi=line.substr(22,5)+line[21]; + hinge=0; + if (resi2hinge_dict.count(resi)) hinge=resi2hinge_dict[resi]; + tu2t_u(tu_vec[hinge],t,u); + transform(t, u, x, x1); + buf<<line.substr(0,30)<<setiosflags(ios::fixed) + <<setprecision(3) + <<setw(8)<<x1[0] <<setw(8)<<x1[1] <<setw(8)<<x1[2] + <<line.substr(54)<<'\n'; + } + else if (line.compare(0,5,"loop_")==0) // PDBx/mmCIF + { + infmt=3; + buf<<line<<'\n'; + while(1) + { + if (compress_type) + { + if (fin_gz.good()) getline(fin_gz, line); + else PrintErrorAndQuit("ERROR! Unexpected end of "+xname); + } + else + { + if (fin.good()) getline(fin, line); + else PrintErrorAndQuit("ERROR! Unexpected end of "+xname); + } + if (line.size()) break; + } + buf<<line<<'\n'; + if (line.compare(0,11,"_atom_site.")) continue; + _atom_site.clear(); + atom_site_pos=0; + _atom_site[Trim(line.substr(11))]=atom_site_pos; + while(1) + { + while(1) + { + if (compress_type) + { + if (fin_gz.good()) getline(fin_gz, line); + else PrintErrorAndQuit("ERROR! Unexpected end of "+xname); + } + else + { + if (fin.good()) getline(fin, line); + else PrintErrorAndQuit("ERROR! Unexpected end of "+xname); + } + if (line.size()) break; + } + if (line.compare(0,11,"_atom_site.")) break; + _atom_site[Trim(line.substr(11))]=++atom_site_pos; + buf<<line<<'\n'; + } + + if (_atom_site.count("group_PDB")* + _atom_site.count("Cartn_x")* + _atom_site.count("Cartn_y")* + _atom_site.count("Cartn_z")==0) + { + buf<<line<<'\n'; + cerr<<"Warning! Missing one of the following _atom_site data items: group_PDB, Cartn_x, Cartn_y, Cartn_z"<<endl; + continue; + } + + while(1) + { + line_vec.clear(); + split(line,line_vec); + if (line_vec[_atom_site["group_PDB"]]!="ATOM" && + line_vec[_atom_site["group_PDB"]]!="HETATM") break; + + x[0]=atof(line_vec[_atom_site["Cartn_x"]].c_str()); + x[1]=atof(line_vec[_atom_site["Cartn_y"]].c_str()); + x[2]=atof(line_vec[_atom_site["Cartn_z"]].c_str()); + if (mirror_opt) x[2]=-x[2]; + + + + if (_atom_site.count("auth_seq_id")) + resi=line_vec[_atom_site["auth_seq_id"]]; + else resi=line_vec[_atom_site["label_seq_id"]]; + if (_atom_site.count("pdbx_PDB_ins_code") && + line_vec[_atom_site["pdbx_PDB_ins_code"]]!="?") + resi+=line_vec[_atom_site["pdbx_PDB_ins_code"]][0]; + else resi+=" "; + if (read_resi>=2) + { + if (_atom_site.count("auth_asym_id")) + asym_id=line_vec[_atom_site["auth_asym_id"]]; + else asym_id=line_vec[_atom_site["label_asym_id"]]; + if (asym_id==".") asym_id=" "; + resi+=asym_id[0]; + } + hinge=0; + if (resi2hinge_dict.count(resi)) hinge=resi2hinge_dict[resi]; + tu2t_u(tu_vec[hinge],t,u); + transform(t, u, x, x1); + + for (atom_site_pos=0; atom_site_pos<_atom_site.size(); atom_site_pos++) + { + if (atom_site_pos==_atom_site["Cartn_x"]) + buf<<setiosflags(ios::fixed)<<setprecision(3) + <<setw(8)<<x1[0]<<' '; + else if (atom_site_pos==_atom_site["Cartn_y"]) + buf<<setiosflags(ios::fixed)<<setprecision(3) + <<setw(8)<<x1[1]<<' '; + else if (atom_site_pos==_atom_site["Cartn_z"]) + buf<<setiosflags(ios::fixed)<<setprecision(3) + <<setw(8)<<x1[2]<<' '; + else buf<<line_vec[atom_site_pos]<<' '; + } + buf<<'\n'; + + if (compress_type && fin_gz.good()) getline(fin_gz, line); + else if (!compress_type && fin.good()) getline(fin, line); + else break; + } + if (compress_type?fin_gz.good():fin.good()) buf<<line<<'\n'; + } + else if (line.size()) + { + buf<<line<<'\n'; + if (ter_opt>=1 && line.compare(0,3,"END")==0) break; + } + } + if (compress_type) fin_gz.close(); + else fin.close(); + + string fname_super_full=fname_super; + if (infmt==0) fname_super_full+=".pdb"; + else if (infmt==3) fname_super_full+=".cif"; + ofstream fp; + fp.open(fname_super_full.c_str()); + fp<<buf.str(); + fp.close(); + buf.str(string()); // clear stream + + string chain1_sele; + string chain2_sele; + if (!mm_opt) + { + if (split_opt==2 && ter_opt>=1) // align one chain from model 1 + { + chain1_sele=" and c. "+chainID1.substr(1); + chain2_sele=" and c. "+chainID2.substr(1); + } + else if (split_opt==2 && ter_opt==0) // align one chain from each model + { + for (i=1;i<chainID1.size();i++) if (chainID1[i]==',') break; + chain1_sele=" and c. "+chainID1.substr(i+1); + for (i=1;i<chainID2.size();i++) if (chainID2[i]==',') break; + chain2_sele=" and c. "+chainID2.substr(i+1); + } + } + + /* extract aligned region */ + int i1=-1; + int i2=-1; + string resi1_sele; + string resi2_sele; + string resi1_bond; + string resi2_bond; + string prev_resi1; + string prev_resi2; + string curr_resi1; + string curr_resi2; + if (mm_opt) + { + ; + } + else + { + for (i=0;i<strlen(seqM);i++) + { + i1+=(seqxA[i]!='-' && seqxA[i]!='*'); + i2+=(seqyA[i]!='-'); + if (seqM[i]==' ' || seqxA[i]=='*') continue; + curr_resi1=resi_vec1[i1].substr(0,4); + curr_resi2=resi_vec2[i2].substr(0,4); + if (resi1_sele.size()==0) + resi1_sele = "i. "+curr_resi1; + else + { + resi1_sele+=" or i. "+curr_resi1; + resi1_bond+="bond structure1 and i. "+prev_resi1+ + ", i. "+curr_resi1+"\n"; + } + if (resi2_sele.size()==0) + resi2_sele = "i. "+curr_resi2; + else + { + resi2_sele+=" or i. "+curr_resi2; + resi2_bond+="bond structure2 and i. "+prev_resi2+ + ", i. "+curr_resi2+"\n"; + } + prev_resi1=curr_resi1; + prev_resi2=curr_resi2; + //if (seqM[i]!=':') continue; + } + if (resi1_sele.size()) resi1_sele=" and ( "+resi1_sele+")"; + if (resi2_sele.size()) resi2_sele=" and ( "+resi2_sele+")"; + } + + /* write pymol script */ + vector<string> pml_list; + pml_list.push_back(fname_super+""); + pml_list.push_back(fname_super+"_atm"); + pml_list.push_back(fname_super+"_all"); + pml_list.push_back(fname_super+"_all_atm"); + pml_list.push_back(fname_super+"_all_atm_lig"); + + for (int p=0;p<pml_list.size();p++) + { + if (mm_opt && p<=1) continue; + buf_pymol + <<"#!/usr/bin/env pymol\n" + <<"cmd.load(\""<<fname_super_full<<"\", \"structure1\")\n" + <<"cmd.load(\""<<yname<<"\", \"structure2\")\n" + <<"hide all\n" + <<"set all_states, "<<((ter_opt==0)?"on":"off")<<'\n'; + if (p==0) // .pml + { + if (chain1_sele.size()) buf_pymol + <<"remove structure1 and not "<<chain1_sele.substr(4)<<"\n"; + if (chain2_sele.size()) buf_pymol + <<"remove structure2 and not "<<chain2_sele.substr(4)<<"\n"; + buf_pymol + <<"remove not n. CA and not n. C3'\n" + <<resi1_bond + <<resi2_bond + <<"show stick, structure1"<<chain1_sele<<resi1_sele<<"\n" + <<"show stick, structure2"<<chain2_sele<<resi2_sele<<"\n"; + } + else if (p==1) // _atm.pml + { + buf_pymol + <<"show cartoon, structure1"<<chain1_sele<<resi1_sele<<"\n" + <<"show cartoon, structure2"<<chain2_sele<<resi2_sele<<"\n"; + } + else if (p==2) // _all.pml + { + buf_pymol + <<"show ribbon, structure1"<<chain1_sele<<"\n" + <<"show ribbon, structure2"<<chain2_sele<<"\n"; + } + else if (p==3) // _all_atm.pml + { + buf_pymol + <<"show cartoon, structure1"<<chain1_sele<<"\n" + <<"show cartoon, structure2"<<chain2_sele<<"\n"; + } + else if (p==4) // _all_atm_lig.pml + { + buf_pymol + <<"show cartoon, structure1\n" + <<"show cartoon, structure2\n" + <<"show stick, not polymer\n" + <<"show sphere, not polymer\n"; + } + buf_pymol + <<"color blue, structure1\n" + <<"color red, structure2\n" + <<"set ribbon_width, 6\n" + <<"set stick_radius, 0.3\n" + <<"set sphere_scale, 0.25\n" + <<"set ray_shadow, 0\n" + <<"bg_color white\n" + <<"set transparency=0.2\n" + <<"zoom polymer and ((structure1"<<chain1_sele + <<") or (structure2"<<chain2_sele<<"))\n" + <<endl; + + fp.open((pml_list[p]+".pml").c_str()); + fp<<buf_pymol.str(); + fp.close(); + buf_pymol.str(string()); + } + + /* clean up */ + pml_list.clear(); + + resi1_sele.clear(); + resi2_sele.clear(); + + resi1_bond.clear(); + resi2_bond.clear(); + + prev_resi1.clear(); + prev_resi2.clear(); + + curr_resi1.clear(); + curr_resi2.clear(); + + chain1_sele.clear(); + chain2_sele.clear(); + resi2hinge_dict.clear(); +} + +//output the final results +void output_flexalign_results(const string xname, const string yname, + const string chainID1, const string chainID2, + const int xlen, const int ylen, double t[3], double u[3][3], + const vector<vector<double> >&tu_vec, const double TM1, const double TM2, + const double TM3, const double TM4, const double TM5, + const double rmsd, const double d0_out, const char *seqM, + const char *seqxA, const char *seqyA, const double Liden, + const int n_ali8, const int L_ali, const double TM_ali, + const double rmsd_ali, const double TM_0, const double d0_0, + const double d0A, const double d0B, const double Lnorm_ass, + const double d0_scale, const double d0a, const double d0u, + const char* fname_matrix, const int outfmt_opt, const int ter_opt, + const int mm_opt, const int split_opt, const int o_opt, + const string fname_super, const int i_opt, const int a_opt, + const bool u_opt, const bool d_opt, const int mirror_opt, + const vector<string>&resi_vec1, const vector<string>&resi_vec2) +{ + if (outfmt_opt<=0) + { + printf("\nName of Structure_1: %s%s (to be superimposed onto Structure_2)\n", + xname.c_str(), chainID1.c_str()); + printf("Name of Structure_2: %s%s\n", yname.c_str(), chainID2.c_str()); + printf("Length of Structure_1: %d residues\n", xlen); + printf("Length of Structure_2: %d residues\n\n", ylen); + + if (i_opt) + printf("User-specified initial alignment: TM/Lali/rmsd = %7.5lf, %4d, %6.3lf\n", TM_ali, L_ali, rmsd_ali); + + printf("Aligned length= %d, RMSD= %6.2f, Seq_ID=n_identical/n_aligned= %4.3f\n", n_ali8, rmsd, (n_ali8>0)?Liden/n_ali8:0); + printf("TM-score= %6.5f (normalized by length of Structure_1: L=%d, d0=%.2f)\n", TM2, xlen, d0B); + printf("TM-score= %6.5f (normalized by length of Structure_2: L=%d, d0=%.2f)\n", TM1, ylen, d0A); + + if (a_opt==1) + printf("TM-score= %6.5f (if normalized by average length of two structures: L=%.1f, d0=%.2f)\n", TM3, (xlen+ylen)*0.5, d0a); + if (u_opt) + printf("TM-score= %6.5f (normalized by user-specified L=%.2f and d0=%.2f)\n", TM4, Lnorm_ass, d0u); + if (d_opt) + printf("TM-score= %6.5f (scaled by user-specified d0=%.2f, and L=%d)\n", TM5, d0_scale, ylen); + printf("(You should use TM-score normalized by length of the reference structure)\n"); + + //output alignment + printf("\n([0-9] denote different aligned fragment pairs separated by different hinges)\n"); + printf("%s\n", seqxA); + printf("%s\n", seqM); + printf("%s\n", seqyA); + } + else if (outfmt_opt==1) + { + printf(">%s%s\tL=%d\td0=%.2f\tseqID=%.3f\tTM-score=%.5f\n", + xname.c_str(), chainID1.c_str(), xlen, d0B, Liden/xlen, TM2); + printf("%s\n", seqxA); + printf(">%s%s\tL=%d\td0=%.2f\tseqID=%.3f\tTM-score=%.5f\n", + yname.c_str(), chainID2.c_str(), ylen, d0A, Liden/ylen, TM1); + printf("%s\n", seqyA); + + printf("# Lali=%d\tRMSD=%.2f\tseqID_ali=%.3f\n", + n_ali8, rmsd, (n_ali8>0)?Liden/n_ali8:0); + + if (i_opt) + printf("# User-specified initial alignment: TM=%.5lf\tLali=%4d\trmsd=%.3lf\n", TM_ali, L_ali, rmsd_ali); + + if(a_opt) + printf("# TM-score=%.5f (normalized by average length of two structures: L=%.1f\td0=%.2f)\n", TM3, (xlen+ylen)*0.5, d0a); + + if(u_opt) + printf("# TM-score=%.5f (normalized by user-specified L=%.2f\td0=%.2f)\n", TM4, Lnorm_ass, d0u); + + if(d_opt) + printf("# TM-score=%.5f (scaled by user-specified d0=%.2f\tL=%d)\n", TM5, d0_scale, ylen); + + printf("$$$$\n"); + } + else if (outfmt_opt==2) + { + printf("%s%s\t%s%s\t%.4f\t%.4f\t%.2f\t%4.3f\t%4.3f\t%4.3f\t%d\t%d\t%d", + xname.c_str(), chainID1.c_str(), yname.c_str(), chainID2.c_str(), + TM2, TM1, rmsd, Liden/xlen, Liden/ylen, (n_ali8>0)?Liden/n_ali8:0, + xlen, ylen, n_ali8); + } + cout << endl; + + if (strlen(fname_matrix)) output_flexalign_rotation_matrix( + fname_matrix, tu_vec, t, u); + + if (o_opt==1) output_flexalign_pymol(xname, yname, fname_super, tu_vec, + t, u, ter_opt, mm_opt, split_opt, mirror_opt, seqM, seqxA, seqyA, + resi_vec1, resi_vec2, chainID1, chainID2); + else if (o_opt==2) + output_flexalign_rasmol(xname, yname, fname_super, tu_vec, + t, u, ter_opt, mm_opt, split_opt, mirror_opt, seqM, seqxA, seqyA, + resi_vec1, resi_vec2, chainID1, chainID2, + xlen, ylen, d0A, n_ali8, rmsd, TM1, Liden); +} + +#endif diff --git a/modules/bindings/src/tmalign/param_set.h b/modules/bindings/src/USalign/param_set.h similarity index 100% rename from modules/bindings/src/tmalign/param_set.h rename to modules/bindings/src/USalign/param_set.h diff --git a/modules/bindings/src/tmalign/pdb2fasta.cpp b/modules/bindings/src/USalign/pdb2fasta.cpp similarity index 79% rename from modules/bindings/src/tmalign/pdb2fasta.cpp rename to modules/bindings/src/USalign/pdb2fasta.cpp index 7c94206ffebee9e6f6847318e001496e53ce64c7..e0fc71206d788719907bb275d776baf2f81e2a83 100644 --- a/modules/bindings/src/tmalign/pdb2fasta.cpp +++ b/modules/bindings/src/USalign/pdb2fasta.cpp @@ -20,16 +20,21 @@ void print_help() " Default is \" C3'\" for RNA/DNA and \" CA \" for proteins\n" " (note the spaces before and after CA).\n" "\n" +" -mol Type of molecule(s) to align.\n" +" auto: (default) align both protein and nucleic acids.\n" +" prot: only align proteins in a structure.\n" +" RNA : only align RNA and DNA in a structure.\n" +"\n" " -ter Strings to mark the end of a chain\n" -" 3: (default) TER, ENDMDL, END or different chain ID\n" +" 3: TER, ENDMDL, END or different chain ID\n" " 2: ENDMDL, END, or different chain ID\n" -" 1: ENDMDL or END\n" +" 1: (default) ENDMDL or END\n" " 0: end of file\n" "\n" " -split Whether to split PDB file into multiple chains\n" -" 0: (default) treat the whole structure as one single chain\n" +" 0: treat the whole structure as one single chain\n" " 1: treat each MODEL as a separate chain (-ter should be 0)\n" -" 2: treat each chain as a seperate chain (-ter should be <=1)\n" +" 2: (default) treat each chain as a seperate chain (-ter should be <=1)\n" "\n" " -het Whether to read residues marked as 'HETATM' in addition to 'ATOM '\n" " 0: (default) only align 'ATOM ' residues\n" @@ -53,11 +58,12 @@ int main(int argc, char *argv[]) /* get argument */ /**********************/ string xname = ""; - int ter_opt =3; // TER, END, or different chainID + int ter_opt =1; // TER, END, or different chainID int infmt_opt =-1; // PDB or PDBx/mmCIF format - int split_opt =0; // do not split chain + int split_opt =2; // do not split chain int het_opt=0; // do not read HETATM residues string atom_opt ="auto";// use C alpha atom for protein and C3' for RNA + string mol_opt ="auto";// auto-detect the molecule type as protein/RNA string suffix_opt=""; // set -suffix to empty string dir_opt =""; // set -dir to empty vector<string> chain_list; // only when -dir1 is set @@ -77,6 +83,12 @@ int main(int argc, char *argv[]) { atom_opt=argv[i + 1]; i++; } + else if ( !strcmp(argv[i],"-mol") ) + { + if (i>=(argc-1)) + PrintErrorAndQuit("ERROR! Missing value for -mol"); + mol_opt=argv[i + 1]; i++; + } else if ( !strcmp(argv[i],"-dir") && i < (argc-1) ) { dir_opt=argv[i + 1]; i++; @@ -108,6 +120,16 @@ int main(int argc, char *argv[]) PrintErrorAndQuit("-split 2 should be used with -ter 0 or 1"); if (split_opt<0 || split_opt>2) PrintErrorAndQuit("-split can only be 0, 1 or 2"); + if (mol_opt=="prot") mol_opt="protein"; + else if (mol_opt=="DNA") mol_opt="RNA"; + if (mol_opt!="auto" && mol_opt!="protein" && mol_opt!="RNA") + PrintErrorAndQuit("ERROR! Molecule type must be one of the" + "following:\nauto, prot (the same as 'protein'), and " + "RNA (the same as 'DNA')."); + if (mol_opt=="protein" && atom_opt=="auto") + atom_opt=" CA "; + else if (mol_opt=="RNA" && atom_opt=="auto") + atom_opt=" C3'"; /* parse file list */ if (dir_opt.size()==0) diff --git a/modules/bindings/src/tmalign/pdb2ss.cpp b/modules/bindings/src/USalign/pdb2ss.cpp similarity index 100% rename from modules/bindings/src/tmalign/pdb2ss.cpp rename to modules/bindings/src/USalign/pdb2ss.cpp diff --git a/modules/bindings/src/tmalign/pdb2xyz.cpp b/modules/bindings/src/USalign/pdb2xyz.cpp similarity index 100% rename from modules/bindings/src/tmalign/pdb2xyz.cpp rename to modules/bindings/src/USalign/pdb2xyz.cpp diff --git a/modules/bindings/src/USalign/pdbAtomName.cpp b/modules/bindings/src/USalign/pdbAtomName.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d65c576d2f375343b7d1e95f2e75c926182022a8 --- /dev/null +++ b/modules/bindings/src/USalign/pdbAtomName.cpp @@ -0,0 +1,232 @@ +#include <fstream> +#include <map> +#include <sstream> +#include <iostream> +#include <string> +#include <vector> +#include "pstream.h" + +using namespace std; + +void print_help() +{ + cout << +"Fix atom name justification in PDB format file.\n" +"\n" +"Usage: pdbAtomName input.pdb output.pdb\n" + <<endl; + exit(EXIT_SUCCESS); +} + +void splitlines(const string &line, vector<string> &lines, + const char delimiter='\n') +{ + bool within_word = false; + for (size_t pos=0;pos<line.size();pos++) + { + if (line[pos]==delimiter) + { + within_word = false; + continue; + } + if (!within_word) + { + within_word = true; + lines.push_back(""); + } + lines.back()+=line[pos]; + } +} + +size_t pdbAtomName(const string &infile,const string &outfile) +{ + stringstream buf; + if (infile=="-") buf<<cin.rdbuf(); +#if defined(REDI_PSTREAM_H_SEEN) + else if (infile.size()>3 && infile.substr(infile.size()-3)==".gz") + { + redi::ipstream fp_gz; // if file is compressed + fp_gz.open("gunzip -c "+infile); + buf<<fp_gz.rdbuf(); + fp_gz.close(); + } +#endif + else + { + ifstream fp; + fp.open(infile.c_str(),ios::in); //ifstream fp(filename,ios::in); + buf<<fp.rdbuf(); + fp.close(); + } + vector<string> lines; + splitlines(buf.str(),lines); + buf.str(string()); + + map<string,string> aa3to1; + aa3to1[" A"]=aa3to1[" DA"]='a'; + aa3to1[" C"]=aa3to1[" DC"]='c'; + aa3to1[" G"]=aa3to1[" DG"]='g'; + aa3to1[" U"]=aa3to1["PSU"]='u'; + aa3to1[" I"]=aa3to1[" DI"]='i'; + aa3to1[" T"]='t'; + aa3to1["ALA"]='A'; + aa3to1["CYS"]='C'; + aa3to1["ASP"]='D'; + aa3to1["GLU"]='E'; + aa3to1["PHE"]='F'; + aa3to1["GLY"]='G'; + aa3to1["HIS"]='H'; + aa3to1["ILE"]='I'; + aa3to1["LYS"]='K'; + aa3to1["LEU"]='L'; + aa3to1["MET"]=aa3to1["MSE"]='M'; + aa3to1["ASN"]='N'; + aa3to1["PRO"]='P'; + aa3to1["GLN"]='Q'; + aa3to1["ARG"]='R'; + aa3to1["SER"]='S'; + aa3to1["THR"]='T'; + aa3to1["VAL"]='V'; + aa3to1["TRP"]='W'; + aa3to1["TYR"]='Y'; + aa3to1["ASX"]='B'; + aa3to1["GLX"]='Z'; + aa3to1["SEC"]='U'; + aa3to1["PYL"]='O'; + + size_t l=0; + string atom=" "; + string resn=" "; + int idxBegin = -1; + int idxEnd = -1; + int i; + string msg; + map<string,int> msg_dict; + size_t changeNum=0; + for (l=0;l<lines.size();l++) + { + if (lines[l].substr(0,6)=="ATOM " || + lines[l].substr(0,6)=="HETATM") + { + if (lines[l].size()<54) + { + cerr<<"incomplete:"<<lines[l]<<endl; + continue; + } + resn=lines[l].substr(17,3); + if (resn[2]==' ') + { + if (resn[1]==' ') resn=" "+resn.substr(0,1); + else resn=" "+resn.substr(0,2); + msg=lines[l].substr(17,3)+"=>"+resn; + if (msg_dict.count(msg)==0) + { + cerr<<msg<<'.'<<endl; + msg_dict[msg]=0; + } + msg_dict[msg]++; + changeNum++; + } + if (lines[l].size()<78 && aa3to1.count(resn)==0) + { + cerr<<"heteroatom:"<<lines[l]<<endl; + buf<<lines[l].substr(0,17)<<resn<<lines[l].substr(20)<<endl; + continue; + } + + atom=lines[l].substr(12,4); + idxBegin = idxEnd = -1; + for (i=0;i<4;i++) + { + if (atom[i]==' ') continue; + if (idxBegin==-1) idxBegin=i; + idxEnd=i; + } + if (idxBegin>=0 && (idxBegin>0 || idxEnd<3)) + atom = atom.substr(idxBegin, idxEnd + 1 - idxBegin); + if (atom[atom.size()-1]=='*') // C3* (old) => C3' (new) + atom=atom.substr(0,atom.size()-1)+"'"; + if (atom.size()==4) + { + buf<<lines[l].substr(0,17)<<resn<<lines[l].substr(20)<<endl; + continue; + } + if ((lines[l].size()>=78 && lines[l][76]!=' ' && lines[l][77]!=' ')|| + ('0'<=atom[0] && atom[0]<='9')) + { + if (atom.size()==1) atom+=" "; + else if (atom.size()==2) atom+=" "; + else if (atom.size()==3) atom+=" "; + } + else if (resn=="MSE" && atom=="SE") atom="SE "; + else + { + if (atom.size()==1) atom=" "+atom+" "; + else if (atom.size()==2) atom=" "+atom+" "; + else if (atom.size()==3) atom=" "+atom; + } + if (atom!=lines[l].substr(12,4)) + { + msg=resn+":"+lines[l].substr(12,4)+"=>"+atom; + if (msg_dict.count(msg)==0) + { + cerr<<msg<<'.'<<endl; + msg_dict[msg]=0; + } + msg_dict[msg]++; + changeNum++; + } + buf<<lines[l].substr(0,12)<<atom<<lines[l].substr(16,1) + <<resn<<lines[l].substr(20)<<endl; + } + else if (lines[l].size()) + { + buf<<lines[l]<<endl; + } + lines[l].clear(); + } + + if (outfile=="-") + cout<<buf.str(); + else + { + ofstream fout; + fout.open(outfile.c_str(),ios::out); + fout<<buf.str(); + fout.close(); + } + buf.str(string()); + vector<string>().swap(lines); + map<string,int>().swap(msg_dict); + map<string,string>().swap(aa3to1); + if (changeNum) + cerr<<"Update "<<changeNum<<" atom name in "<<infile<<endl; + return changeNum; +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) print_help(); + + string infile =""; + string outfile=""; + + for (int i=1; i<argc; i++) + { + if (infile.size()==0) infile=argv[i]; + else if (outfile.size()==0) outfile=argv[i]; + else + { + cerr<<"ERROR! no such option "<<argv[i]<<endl; + exit(1); + } + } + + if (outfile.size()==0) outfile="-"; + + pdbAtomName(infile,outfile); + + infile.clear(); + outfile.clear(); + return 0; +} diff --git a/modules/bindings/src/tmalign/pstream.h b/modules/bindings/src/USalign/pstream.h similarity index 99% rename from modules/bindings/src/tmalign/pstream.h rename to modules/bindings/src/USalign/pstream.h index 28cbeadb1e9752f894b66fee9801446bc4eb8ccb..12c759874a82c9e2ab710490f6106582918c7ef8 100644 --- a/modules/bindings/src/tmalign/pstream.h +++ b/modules/bindings/src/USalign/pstream.h @@ -15,6 +15,11 @@ * and redi::rpstream. */ +/* do not compile on windows, which does not have cygwin */ +#if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__CYGWIN__) +#define NO_PSTREAM +#else + #ifndef REDI_PSTREAM_H_SEEN #define REDI_PSTREAM_H_SEEN @@ -2250,6 +2255,6 @@ namespace redi */ #endif // REDI_PSTREAM_H_SEEN - +#endif // WIN32 // vim: ts=2 sw=2 expandtab diff --git a/modules/bindings/src/USalign/qTMclust.cpp b/modules/bindings/src/USalign/qTMclust.cpp new file mode 100644 index 0000000000000000000000000000000000000000..08fc64b68d12fc701cf3eff12f3c83885420fb1c --- /dev/null +++ b/modules/bindings/src/USalign/qTMclust.cpp @@ -0,0 +1,723 @@ +/* Different filters are used when different header files are included. + * At least one of HwRMSD.h and TMalign.h should be included. + * HwRMSD.h implement HwRMSD filter. + * No filter will be used if only TMalign.h is included. */ + +#include "HwRMSD.h" +#include "TMalign.h" + +using namespace std; + +void print_extra_help() +{ + cout << +"Additional options:\n" +" -fast Fast but slightly inaccurate final alignment\n" +"\n" +" -atom 4-character atom name used to represent a residue.\n" +" Default is \" C3'\" for RNA/DNA and \" CA \" for proteins\n" +" (note the spaces before and after CA).\n" +"\n" +" -mol Molecule type: RNA or protein\n" +" Default is detect molecule type automatically\n" +"\n" +" -het Whether to align residues marked as 'HETATM' in addition to 'ATOM '\n" +" 0: (default) only align 'ATOM ' residues\n" +" 1: align both 'ATOM ' and 'HETATM' residues\n" +"\n" +" -infmt Input format\n" +" -1: (default) automatically detect PDB or PDBx/mmCIF format\n" +" 0: PDB format\n" +" 1: SPICKER format\n" +" 2: xyz format\n" +" 3: PDBx/mmCIF format\n" + <<endl; +} + +void print_help(bool h_opt=false) +{ + cout << "\n" +"qTMclust: Structure Clustering by Sequence-Indepedent Structure Alignment\n" +"\n" +"Usage 1: (alignment within a folder of PDB files)\n" +" qTMclust -dir chain_folder/ chain_list -TMcut 0.5 -o cluster.txt\n" +"\n" +"Usage 2: (alignment within chains or within models of a single PDB file)\n" +" qTMclust -split 2 -ter 1 multichain.pdb -TMcut 0.5 -o cluster.txt\n" +" qTMclust -split 1 -ter 0 multimodel.pdb -TMcut 0.5 -o cluster.txt\n" +"\n" +"Options:\n" +" -TMcut TM-score cutoff in the range of [0.45,1) for considering two\n" +" structures being similar. Default is 0.5.\n" +"\n" +" -s Which TM-score to use when aligning structures with different lengths?\n" +" 1: the larger TM-score, i.e. normalized by shorter length\n" +" 2: (default) the smaller TM-score, i.e. normalized by longer length\n" +" 3: average of the two TM-scores\n" +" 4: harmonic average of the two TM-scores\n" +" 5: geometric average of the two TM-scores\n" +" 6: root mean square of the two TM-scores\n" +"\n" +" -o Output the cluster result to file.\n" +" Default is print result to screen.\n" +"\n" +" -dir Perform all-against-all alignment among the list of PDB\n" +" chains listed by 'chain_list' under 'chain_folder'. Note\n" +" that the slash is necessary.\n" +" $ qTMclust -dir chain_folder/ chain_list\n" +"\n" +" -suffix (Only when -dir is set, default is empty)\n" +" add file name suffix to files listed by chain_list\n" +"\n" +" -ter Strings to mark the end of a chain\n" +" 3: (default) TER, ENDMDL, END or different chain ID\n" +" 2: ENDMDL, END, or different chain ID\n" +" 1: ENDMDL or END\n" +" 0: end of file\n" +"\n" +" -split Whether to split PDB file into multiple chains\n" +" 0: (default) treat the whole structure as one single chain\n" +" 1: treat each MODEL as a separate chain (-ter should be 0)\n" +" 2: treat each chain as a seperate chain (-ter should be <=1)\n" +"\n" +" -h Print the full help message, including additional options.\n" +"\n" + <<endl; + + if (h_opt) print_extra_help(); + + exit(EXIT_SUCCESS); +} + +void filter_lower_bound(double &lb_HwRMSD, double &lb_TMfast, + const double TMcut, const int s_opt,const int mol_type) +{ + lb_HwRMSD=0.5*TMcut; + lb_TMfast=0.9*TMcut; + if (s_opt<=1) + { + if (mol_type>0) // RNA + { + lb_HwRMSD=0.02*TMcut; + lb_TMfast=0.60*TMcut; + } + else // protein + { + lb_HwRMSD=0.25*TMcut; + lb_TMfast=0.80*TMcut; + } + } + return; +} + +int main(int argc, char *argv[]) +{ + if (argc < 2) print_help(); + + + clock_t t1, t2; + t1 = clock(); + + /**********************/ + /* get argument */ + /**********************/ + string xname = ""; + double TMcut = 0.5; + string fname_clust = ""; // file name for output cluster result + string fname_lign = ""; // file name for user alignment + vector<string> sequence; // get value from alignment file + double Lnorm_ass, d0_scale; + + bool h_opt = false; // print full help message + int i_opt = 0; // 3 for -I, stick to user given alignment + int a_opt = 0; // flag for -a, do not normalized by average length + int s_opt = 2; // flag for -s, normalized by longer length + bool u_opt = false; // flag for -u, normalized by user specified length + bool d_opt = false; // flag for -d, user specified d0 + + int infmt_opt =-1; // PDB or PDBx/mmCIF format + int ter_opt =3; // TER, END, or different chainID + int split_opt =0; // do not split chain + bool fast_opt =false; // flags for -fast, fTM-align algorithm + int het_opt =0; // do not read HETATM residues + string atom_opt ="auto";// use C alpha atom for protein and C3' for RNA + string mol_opt ="auto";// auto-detect the molecule type as protein/RNA + string suffix_opt=""; // set -suffix to empty + string dir_opt =""; // set -dir to empty + int byresi_opt=0; // set -byresi to 0 + vector<string> chain_list; + + for(int i = 1; i < argc; i++) + { + if ( (!strcmp(argv[i],"-u")||!strcmp(argv[i],"-L")) && i < (argc-1) ) + { + PrintErrorAndQuit("Sorry! -u has not been implemented yet"); + Lnorm_ass = atof(argv[i + 1]); u_opt = true; i++; + } + else if ( !strcmp(argv[i],"-d") && i < (argc-1) ) + { + PrintErrorAndQuit("Sorry! -d has not been implemented yet"); + d0_scale = atof(argv[i + 1]); d_opt = true; i++; + } + else if (!strcmp(argv[i], "-I") && i < (argc-1) ) + { + fname_lign = argv[i + 1]; i_opt = 3; i++; + } + else if ( !strcmp(argv[i],"-o") && i < (argc-1) ) + { + fname_clust = argv[i + 1]; i++; + } + else if ( !strcmp(argv[i],"-a") && i < (argc-1)) + { + PrintErrorAndQuit("Sorry! -a is not used for clustering"); + } + else if ( !strcmp(argv[i],"-s") && i < (argc-1) ) + { + s_opt=atoi(argv[i + 1]); i++; + if (s_opt<1 || s_opt>6) + PrintErrorAndQuit("-s must be within 1 to 6"); + } + else if ( !strcmp(argv[i],"-h") ) + { + h_opt = true; + } + else if (!strcmp(argv[i], "-fast")) + { + fast_opt = true; + } + else if ( !strcmp(argv[i],"-infmt") && i < (argc-1) ) + { + infmt_opt=atoi(argv[i + 1]); i++; + } + else if ( !strcmp(argv[i],"-ter") && i < (argc-1) ) + { + ter_opt=atoi(argv[i + 1]); i++; + } + else if ( !strcmp(argv[i],"-split") && i < (argc-1) ) + { + split_opt=atoi(argv[i + 1]); i++; + } + else if ( !strcmp(argv[i],"-atom") && i < (argc-1) ) + { + atom_opt=argv[i + 1]; i++; + } + else if ( !strcmp(argv[i],"-mol") && i < (argc-1) ) + { + mol_opt=argv[i + 1]; i++; + } + else if ( !strcmp(argv[i],"-dir") && i < (argc-1) ) + { + dir_opt=argv[i + 1]; i++; + } + else if ( !strcmp(argv[i],"-suffix") && i < (argc-1) ) + { + suffix_opt=argv[i + 1]; i++; + } + else if ( !strcmp(argv[i],"-TMcut") && i < (argc-1) ) + { + TMcut=atof(argv[i + 1]); i++; + if (TMcut>1 or TMcut<0.45) + PrintErrorAndQuit("TMcut must be in the range of [0.45,1)"); + } + else if ( !strcmp(argv[i],"-byresi") && i < (argc-1) ) + { + PrintErrorAndQuit("Sorry! -byresi has not been implemented yet"); + byresi_opt=atoi(argv[i + 1]); i++; + } + else if ( !strcmp(argv[i],"-het") && i < (argc-1) ) + { + het_opt=atoi(argv[i + 1]); i++; + } + else if (xname.size() == 0) xname=argv[i]; + else PrintErrorAndQuit(string("ERROR! Undefined option ")+argv[i]); + } + + if(xname.size()==0) print_help(h_opt); + + if (suffix_opt.size() && dir_opt.size()==0) + PrintErrorAndQuit("-suffix is only valid if -dir, -dir1 or -dir2 is set"); + if (atom_opt.size()!=4) + PrintErrorAndQuit("ERROR! Atom name must have 4 characters, including space."); + if (mol_opt!="auto" && mol_opt!="protein" && mol_opt!="RNA") + PrintErrorAndQuit("ERROR! Molecule type must be either RNA or protein."); + else if (mol_opt=="protein" && atom_opt=="auto") + atom_opt=" CA "; + else if (mol_opt=="RNA" && atom_opt=="auto") + atom_opt=" C3'"; + + if (u_opt && Lnorm_ass<=0) + PrintErrorAndQuit("Wrong value for option -u! It should be >0"); + if (d_opt && d0_scale<=0) + PrintErrorAndQuit("Wrong value for option -d! It should be >0"); + if (split_opt==1 && ter_opt!=0) + PrintErrorAndQuit("-split 1 should be used with -ter 0"); + else if (split_opt==2 && ter_opt!=0 && ter_opt!=1) + PrintErrorAndQuit("-split 2 should be used with -ter 0 or 1"); + if (split_opt<0 || split_opt>2) + PrintErrorAndQuit("-split can only be 0, 1 or 2"); + + /* read initial alignment file from 'align.txt' */ + if (i_opt) read_user_alignment(sequence, fname_lign, i_opt); + + if (byresi_opt) i_opt=3; + + /* parse file list */ + if (dir_opt.size()==0) chain_list.push_back(xname); + else file2chainlist(chain_list, xname, dir_opt, suffix_opt); + + /* declare previously global variables */ + vector<vector<string> >PDB_lines; // text of chain + vector<int> mol_vec; // molecule type of chain1, RNA if >0 + vector<string> chainID_list; // list of chainID + size_t xchainnum=0; // number of chains in a PDB file + size_t i,j; // number of residues/chains in a PDB is + // usually quite limited. Yet, the number of + // files can be very large. size_t is safer + // than int for very long list of files + int xlen,ylen; // chain length + double **xa,**ya; // xyz coordinate + vector<string> resi_vec; // residue index for chain, dummy variable + vector<pair<int,size_t> >chainLen_list; // vector of (length,index) pair + vector<vector<char> > seq_vec; + vector<vector<char> > sec_vec; + vector<vector<vector<float> > >xyz_vec; + + /* parse files */ + string chain_name; + vector<char> seq_tmp; + vector<char> sec_tmp; + vector<float> flt_tmp(3,0); + vector<vector<float> >xyz_tmp; + int r; // residue index + size_t newchainnum; + double ub_HwRMSD=0.90*TMcut+0.10; + double lb_HwRMSD=0.5*TMcut; + double ub_TMfast=0.90*TMcut+0.10; + double lb_TMfast=0.9*TMcut; + if (s_opt==2 || s_opt==4 || s_opt==5) a_opt=-2; // normalized by longer length, i.e. smaller TM + else if (s_opt==1 || s_opt==5) a_opt=-1; // normalized by shorter length, i.e. larger TM + else if (s_opt==3) a_opt= 1; // normalized by average length + +#ifdef TMalign_HwRMSD_h + /* These parameters controls HwRMSD filter. iter_opt typically should be + * >=3. Many alignments converge within iter_opt=5. Occassionally + * some alignments require iter_opt=10. Higher iter_opt takes more time, + * even though HwRMSD iter_opt 10 still takes far less time than TMalign + * -fast -TMcut 0.5. + * After HwRMSD filter, at least min_repr_num and at most max_repr_num + * are used for subsequent TMalign. The actual number of representatives + * are decided by xlen */ + const int glocal =0; // global alignment + const int iter_opt =10; + const int min_repr_num=10; + const int max_repr_num=50; +#endif + + for (i=0;i<chain_list.size();i++) + { + xname=chain_list[i]; + newchainnum=get_PDB_lines(xname, PDB_lines, chainID_list, + mol_vec, ter_opt, infmt_opt, atom_opt, split_opt, het_opt); + if (!newchainnum) + { + cerr<<"Warning! Cannot parse file: "<<xname + <<". Chain number 0."<<endl; + continue; + } + chain_name=xname.substr(dir_opt.size(), + xname.size()-dir_opt.size()-suffix_opt.size()); + for (j=0;j<newchainnum;j++) + { + chainID_list[j+xchainnum]=chain_name+chainID_list[j+xchainnum]; + xlen=PDB_lines[j].size(); + cout<<"Parsing "<<xname<<'\t'<<chainID_list[j+xchainnum] + <<" ("<<xlen<<" residues)."<<endl; + if (mol_opt=="RNA") mol_vec[j+xchainnum]=1; + else if (mol_opt=="protein") mol_vec[j+xchainnum]=-1; + + NewArray(&xa, xlen, 3); + seq_tmp.assign(xlen+1,'A'); + sec_tmp.assign(xlen+1,0); + + read_PDB(PDB_lines[j], xa, &seq_tmp[0], resi_vec, byresi_opt); + + if (mol_vec[j]<=0) make_sec(xa, xlen, &sec_tmp[0]); + else make_sec(&seq_tmp[0],xa,xlen,&sec_tmp[0],atom_opt); + + xyz_tmp.assign(xlen,flt_tmp); + for (r=0;r<xlen;r++) + { + xyz_tmp[r][0]=xa[r][0]; + xyz_tmp[r][1]=xa[r][1]; + xyz_tmp[r][2]=xa[r][2]; + } + + seq_vec.push_back(seq_tmp); + sec_vec.push_back(sec_tmp); + xyz_vec.push_back(xyz_tmp); + + chainLen_list.push_back( + make_pair(PDB_lines[j].size(),j+xchainnum)); + + seq_tmp.clear(); + sec_tmp.clear(); + xyz_tmp.clear(); + DeleteArray(&xa, xlen); + PDB_lines[j].clear(); + } + PDB_lines.clear(); + xchainnum+=newchainnum; + } + flt_tmp.clear(); + chain_list.clear(); + + // swap completely destroy the vector and free up the memory capacity + vector<vector<string> >().swap(PDB_lines); + size_t Nstruct=chainLen_list.size(); + + /* sort by chain length */ + stable_sort(chainLen_list.begin(),chainLen_list.end(), + greater<pair<int,int> >()); + cout<<"Clustering "<<chainLen_list.size() + <<" chains with TM-score cutoff >="<<TMcut<<'\n' + <<"Longest chain "<<chainID_list[chainLen_list[0].second]<<'\t' + <<chainLen_list[0].first<<" residues.\n" + <<"Shortest chain "<<chainID_list[chainLen_list.back().second]<<'\t' + <<chainLen_list.back().first<<" residues."<<endl; + + /* set the first cluster */ + vector<size_t> clust_mem_vec(Nstruct,-1); // cluster membership + vector<size_t> clust_repr_vec; // the same as number of clusters + size_t chain_i=chainLen_list[0].second; + clust_repr_vec.push_back(chain_i); + clust_mem_vec[chain_i]=0; + map<size_t,size_t> clust_repr_map; + + /* perform alignment */ + size_t chain_j; + const double fast_lb=50.; // proteins shorter than fast_lb never use -fast + const double fast_ub=1000.;// proteins longer than fast_ub always use -fast + double Lave; // average protein length for chain_i and chain_j + size_t sizePROT; // number of representatives for current chain + vector<size_t> index_vec; // index of cluster representatives for the chain + bool found_clust; // whether current chain hit previous cluster + + for (i=1;i<Nstruct;i++) + { + chain_i=chainLen_list[i].second; + xlen=xyz_vec[chain_i].size(); + if (xlen<=5) // TMalign cannot handle L<=5 + { + clust_mem_vec[chain_i]=clust_repr_vec.size(); + clust_repr_vec.push_back(clust_repr_vec.size()); + continue; + } + + NewArray(&xa, xlen, 3); + for (r=0;r<xlen;r++) + { + xa[r][0]=xyz_vec[chain_i][r][0]; + xa[r][1]=xyz_vec[chain_i][r][1]; + xa[r][2]=xyz_vec[chain_i][r][2]; + } + + // j-1 is index of old cluster. here, we starts from the latest + // cluster because proteins with similar length are more likely + // to be similar. we cannot use j as index because size_t j cannot + // be negative at the end of this loop + for (j=clust_repr_vec.size();j>0;j--) + { + chain_j=clust_repr_vec[j-1]; + ylen=xyz_vec[chain_j].size(); + if (mol_vec[chain_i]*mol_vec[chain_j]<0) continue; + else if (s_opt==2 && xlen<TMcut*ylen) continue; + else if (s_opt==3 && xlen<(2*TMcut-1)*ylen) continue; + else if (s_opt==4 && xlen*(2/TMcut-1)<ylen) continue; + else if (s_opt==5 && xlen<TMcut*TMcut*ylen) continue; + else if (s_opt==6 && xlen*xlen<(2*TMcut*TMcut-1)*ylen*ylen) continue; + index_vec.push_back(chain_j); + } + sizePROT=index_vec.size(); + + cout<<'>'<<chainID_list[chain_i]<<'\t'<<xlen<<'\t' + <<setiosflags(ios::fixed)<<setprecision(2) + <<100.*i/Nstruct<<"%(#"<<i<<")\t" + <<"#repr="<<sizePROT<<"/"<<clust_repr_vec.size()<<endl; + +#ifdef TMalign_HwRMSD_h + vector<pair<double,size_t> > HwRMSDscore_list; + double TM; + for (j=0;j<sizePROT;j++) + { + chain_j=index_vec[j]; + ylen=xyz_vec[chain_j].size(); + if (mol_vec[chain_i]*mol_vec[chain_j]<0) continue; + else if (s_opt==2 && xlen<TMcut*ylen) continue; + else if (s_opt==3 && xlen<(2*TMcut-1)*ylen) continue; + else if (s_opt==4 && xlen*(2/TMcut-1)<ylen) continue; + else if (s_opt==5 && xlen<TMcut*TMcut*ylen) continue; + else if (s_opt==6 && xlen*xlen<(2*TMcut*TMcut-1)*ylen*ylen) continue; + + if (s_opt<=1) filter_lower_bound(lb_HwRMSD, lb_TMfast, + TMcut, s_opt, mol_vec[chain_i]+mol_vec[chain_j]); + + NewArray(&ya, ylen, 3); + for (r=0;r<ylen;r++) + { + ya[r][0]=xyz_vec[chain_j][r][0]; + ya[r][1]=xyz_vec[chain_j][r][1]; + ya[r][2]=xyz_vec[chain_j][r][2]; + } + + /* declare variable specific to this pair of HwRMSD */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for s_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + int *invmap = new int[ylen+1]; + + /* entry function for structure alignment */ + HwRMSD_main( + xa, ya, &seq_vec[chain_i][0], &seq_vec[chain_j][0], + &sec_vec[chain_i][0], &sec_vec[chain_j][0], t0, u0, + TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, + d0a, d0_out, seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, + rmsd_ali, n_ali, n_ali8, xlen, ylen, + sequence, Lnorm_ass, + d0_scale, i_opt, + a_opt, u_opt, d_opt, mol_vec[chain_i]+mol_vec[chain_j], + invmap, glocal, iter_opt); + + TM=TM3; // average length + if (s_opt==1) TM=TM2; // shorter length + else if (s_opt==2) TM=TM1; // longer length + else if (s_opt==3) TM=(TM1+TM2)/2; // average TM + else if (s_opt==4) TM=2/(1/TM1+1/TM2); // harmonic average + else if (s_opt==5) TM=sqrt(TM1*TM2); // geometric average + else if (s_opt==6) TM=sqrt((TM1*TM1+TM2*TM2)/2); // root mean square + + Lave=sqrt(xlen*ylen); // geometry average because O(L1*L2) + if (TM>=lb_HwRMSD || Lave<=fast_lb) + HwRMSDscore_list.push_back(make_pair(TM,index_vec[j])); + + /* clean up after each HwRMSD */ + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + DeleteArray(&ya, ylen); + delete [] invmap; + + /* if a good hit is guaranteed to be found, stop the loop */ + if (TM>=ub_HwRMSD) break; + } + + stable_sort(HwRMSDscore_list.begin(),HwRMSDscore_list.end(), + greater<pair<double,size_t> >()); + + int cur_repr_num_cutoff=min_repr_num; + if (xlen<=fast_lb) cur_repr_num_cutoff=max_repr_num; + else if (xlen>fast_lb && xlen<fast_ub) cur_repr_num_cutoff+= + (fast_ub-xlen)/(fast_ub-fast_lb)*(max_repr_num-min_repr_num); + + index_vec.clear(); + for (j=0;j<HwRMSDscore_list.size();j++) + { + TM=HwRMSDscore_list[j].first; + chain_j=HwRMSDscore_list[j].second; + ylen=xyz_vec[chain_j].size(); + Lave=sqrt(xlen*ylen); // geometry average because O(L1*L2) + if (Lave>fast_lb && TM<TMcut*0.5 && + index_vec.size()>=cur_repr_num_cutoff) break; + index_vec.push_back(chain_j); + cout<<"#"<<chain_j<<"\t"<<chainID_list[chain_j]<<"\t" + <<setiosflags(ios::fixed)<<setprecision(4)<<TM<<endl; + } + cout<<index_vec.size()<<" out of " + <<HwRMSDscore_list.size()<<" entries"<<endl; + HwRMSDscore_list.clear(); +#endif + + found_clust=false; + for (j=0;j<index_vec.size();j++) + { + chain_j=index_vec[j]; + ylen=xyz_vec[chain_j].size(); + if (mol_vec[chain_i]*mol_vec[chain_j]<0) continue; + else if (s_opt==2 && xlen<TMcut*ylen) continue; + else if (s_opt==3 && xlen<(2*TMcut-1)*ylen) continue; + else if (s_opt==4 && xlen*(2/TMcut-1)<ylen) continue; + else if (s_opt==5 && xlen<TMcut*TMcut*ylen) continue; + else if (s_opt==6 && xlen*xlen<(2*TMcut*TMcut-1)*ylen*ylen) continue; + if (s_opt<=1) filter_lower_bound(lb_HwRMSD, lb_TMfast, + TMcut, s_opt, mol_vec[chain_i]+mol_vec[chain_j]); + + NewArray(&ya, ylen, 3); + for (r=0;r<ylen;r++) + { + ya[r][0]=xyz_vec[chain_j][r][0]; + ya[r][1]=xyz_vec[chain_j][r][1]; + ya[r][2]=xyz_vec[chain_j][r][2]; + } + + Lave=sqrt(xlen*ylen); // geometry average because O(L1*L2) + bool overwrite_fast_opt=(fast_opt==true || Lave>=fast_ub); + + /* declare variable specific to this pair of TMalign */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for s_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + + /* entry function for structure alignment */ + int status=TMalign_main( + xa, ya, &seq_vec[chain_i][0], &seq_vec[chain_j][0], + &sec_vec[chain_i][0], &sec_vec[chain_j][0], + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + i_opt, a_opt, u_opt, d_opt, overwrite_fast_opt, + mol_vec[chain_i]+mol_vec[chain_j],TMcut); + + cout<<status<<'\t'<<chainID_list[chain_j]<<'\t' + <<setiosflags(ios::fixed)<<setprecision(4) + <<TM2<<'\t'<<TM1<<'\t'<<overwrite_fast_opt<<endl; + + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + + double TM=TM3; // average length + if (s_opt==1) TM=TM2; // shorter length + else if (s_opt==2) TM=TM1; // longer length + else if (s_opt==3) TM=(TM1+TM2)/2; // average TM + else if (s_opt==4) TM=2/(1/TM1+1/TM2); // harmonic average + else if (s_opt==5) TM=sqrt(TM1*TM2); // geometric average + else if (s_opt==6) TM=sqrt((TM1*TM1+TM2*TM2)/2); // root mean square + + if (TM<lb_TMfast || + (TM<TMcut && (fast_opt || overwrite_fast_opt==false))) + { + DeleteArray(&ya, ylen); + continue; + } + + if (TM>=ub_TMfast || + (TM>=TMcut && (fast_opt || overwrite_fast_opt==false))) + { + clust_mem_vec[chain_i]=clust_repr_map[chain_j]; + DeleteArray(&ya, ylen); + found_clust=true; + break; + } + + if (TM<lb_TMfast && overwrite_fast_opt==false) + { + TMalign_main( + xa, ya, &seq_vec[chain_i][0], &seq_vec[chain_j][0], + &sec_vec[chain_i][0], &sec_vec[chain_j][0], + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + i_opt, a_opt, u_opt, d_opt, false, + mol_vec[chain_i]+mol_vec[chain_j],TMcut); + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + DeleteArray(&ya, ylen); + + TM=TM3; // average length + if (s_opt==1) TM=TM2; // shorter length + else if (s_opt==2) TM=TM1; // longer length + else if (s_opt==3) TM=(TM1+TM2)/2; // average TM + else if (s_opt==4) TM=2/(1/TM1+1/TM2); // harmonic average + else if (s_opt==5) TM=sqrt(TM1*TM2); // geometric average + else if (s_opt==6) TM=sqrt((TM1*TM1+TM2*TM2)/2); // root mean square + cout<<"*\t"<<chainID_list[chain_j]<<'\t'<<TM2<<'\t'<<TM1<<endl; + if (TM>=TMcut) + { + clust_mem_vec[chain_i]=clust_repr_map[chain_j]; + found_clust=true; + break; + } + } + } + DeleteArray(&xa, xlen); + index_vec.clear(); + + if (!found_clust) // new cluster + { + clust_mem_vec[chain_i]=clust_repr_vec.size(); + clust_repr_map[chain_i]=clust_repr_vec.size(); + clust_repr_vec.push_back(chain_i); + } + else // member structures are not used further + { + vector<char> ().swap(seq_vec[chain_i]); + vector<char> ().swap(sec_vec[chain_i]); + vector<vector<float> > ().swap(xyz_vec[chain_i]); + } + } + + /* clean up */ + mol_vec.clear(); + xyz_vec.clear(); + seq_vec.clear(); + sec_vec.clear(); + + /* print out cluster */ + stringstream txt; + for (j=0;j<clust_repr_vec.size();j++) + { + chain_j=clust_repr_vec[j]; // cluster representative + txt<<chainID_list[chain_j]; + for (chain_i=0;chain_i<clust_mem_vec.size();chain_i++) + { + if (chain_i!=chain_j && clust_mem_vec[chain_i]==j) + txt<<'\t'<<chainID_list[chain_i]; + } + txt<<'\n'; + } + if (fname_clust.size() && fname_clust!="-") + { + ofstream fp(fname_clust.c_str()); + fp<<txt.str(); + fp.close(); + } + else cout<<txt.str()<<endl; + + /* clean up */ + txt.str(string()); + clust_repr_vec.clear(); + clust_mem_vec.clear(); + chainID_list.clear(); + clust_repr_map.clear(); + + t2 = clock(); + float diff = ((float)t2 - (float)t1)/CLOCKS_PER_SEC; + printf("#Total CPU time is %5.2f seconds\n", diff); + return 0; +} diff --git a/modules/bindings/src/tmalign/readme.txt b/modules/bindings/src/USalign/readme.txt similarity index 66% rename from modules/bindings/src/tmalign/readme.txt rename to modules/bindings/src/USalign/readme.txt index 3249215e8e3a65fa56cb3661df7ed3a24638c18b..2a03302527c578b4e646739b2a2e5331b53091c7 100644 --- a/modules/bindings/src/tmalign/readme.txt +++ b/modules/bindings/src/USalign/readme.txt @@ -1,15 +1,11 @@ ============================================================================== - TM-align: protein and RNA structure alignment by TM-score superposition. - - This program was written by (in reverse chronological order) - Chengxin Zhang, Sha Gong, Jianjie Wu, and Jianyi Yang - at Yang Zhang lab, Department of Computational Medicine and Bioinformatics, - University of Michigan, 100 Washtenaw Ave, Ann Arbor, MI 48109-2218. - Please report issues to yangzhanglab@umich.edu + US-align: universal structure alignment of monomeric and complex proteins + and nucleic acids References to cite: - S Gong, C Zhang, Y Zhang. Bioinformatics, btz282 (2019) - Y Zhang, J Skolnick. Nucl Acids Res 33, 2302-9 (2005) + (1) Chengxin Zhang, Morgan Shine, Anna Marie Pyle, Yang Zhang + (2022) Nat Methods + (2) Chengxin Zhang, Anna Marie Pyle (2022) iScience DISCLAIMER: Permission to use, copy, modify, and distribute this program for @@ -61,38 +57,53 @@ 2021/01/07: Fixed bug in TMscore -c 2021/05/29: Remove unnecessary depedency on malloc.h, which prevent compilation on Mac OS + 2021/08/17: Complete implementation of MMalign + 2021/10/03: Support Windows + 2022/02/27: Add -seq (-byresi 4 & 5) for TM-score superimposition guided by + sequence alignment. + 2022/04/12: Support AlphaFold CIF + 2022/05/11: Update -mm 4 output format + 2022/05/24: Limited support for sequence order independent alignment + 2022/05/30: Correct atom pair output for -mm 5 + 2022/06/07: Sequence order semi-independent alignment + 2022/06/20: Sequentiality within SSE in sequence order semi-independent + alignment + 2022/06/22: Fix infinite loop for mal-formatted PDB + 2022/06/23: Fix -m for Windows. Add pymol plugin. + 2022/06/26: Add -full option for -mm 2 and 4 + 2022/09/24: Support -TMscore for complex when the chain order is different =============================================================================== ========================= - How to install TM-align + How to install US-align ========================= To compile the program in your Linux computer, simply enter - make + make or - g++ -static -O3 -ffast-math -lm -o TMalign TMalign.cpp + g++ -static -O3 -ffast-math -lm -o USalign USalign.cpp The '-static' flag should be removed on Mac OS, which does not support building static executables. +USalign compiled on Linux, Mac OS and Linux Subsystem for Windows (WSL2) on +Windows 10 onwards can read both uncompressed files and gz compressed +files, provided that the "gunzip" command is available. On the other hand, due +to the lack of POSIX support on Windows, US-align natively compiled on Windows +without WSL2 cannot parse gz compressed files. + +US-align is known to be compilable by g++ version 4.8.5 or later, clang++ +version 12.0.5 or later and mingw-w64 version 9.3 or later. + ===================== - How to use TM-align + How to use US-align ===================== You can run the program without arguments to obtain a brief instruction - ./TMalign structure1.pdb structure2.pdb - -=================== - Fortran version -=================== -You can download the fortran version of TM-align from -https://zhanglab.ccmb.med.umich.edu/TM-align/ + ./USalign structure1.pdb structure2.pdb -This C++ version of TM-align implemented several features not available in the -fortran version, including RNA alignment and batch alignment of multiple -structures. A full list of available options can be explored by: - ./TMalign -h +A full list of available options can be explored by: -2021/05/20 + ./USalign -h diff --git a/modules/bindings/src/tmalign/se.cpp b/modules/bindings/src/USalign/se.cpp similarity index 94% rename from modules/bindings/src/tmalign/se.cpp rename to modules/bindings/src/USalign/se.cpp index c4d7606816f84beceb4b569f0b14d1d082ddc119..af24ae78cafc6b5a930522e938cac871409db55d 100644 --- a/modules/bindings/src/tmalign/se.cpp +++ b/modules/bindings/src/USalign/se.cpp @@ -48,12 +48,17 @@ void print_extra_help() " 2: tabular format very compact output\n" "\n" " -byresi Whether to align two structures by residue index.\n" +" The same as -TMscore.\n" " 0: (default) do not align by residue index\n" " 1: (same as TMscore program) align by residue index\n" " 2: (same as TMscore -c, should be used with -ter <=1)\n" " align by residue index and chain ID\n" " 3: (similar to TMscore -c, should be used with -ter <=1)\n" " align by residue index and order of chain\n" +" 4: sequence dependent alignment: perform Needleman-Wunsch\n" +" global sequence alignment\n" +" 5: sequence dependent alignment: perform glocal sequence\n" +" alignment\n" "\n" " -het Whether to align residues marked as 'HETATM' in addition to 'ATOM '\n" " 0: (default) only align 'ATOM ' residues\n" @@ -208,7 +213,8 @@ int main(int argc, char *argv[]) { outfmt_opt=atoi(argv[i + 1]); i++; } - else if ( !strcmp(argv[i],"-byresi") && i < (argc-1) ) + else if ( (!strcmp(argv[i],"-byresi") || !strcmp(argv[i],"-TMscore") || + !strcmp(argv[i],"-tmscore") ) && i < (argc-1) ) { byresi_opt=atoi(argv[i + 1]); i++; } @@ -255,10 +261,10 @@ int main(int argc, char *argv[]) { if (i_opt) PrintErrorAndQuit("-byresi >=1 cannot be used with -i or -I"); - if (byresi_opt<0 || byresi_opt>3) - PrintErrorAndQuit("-byresi can only be 0, 1, 2 or 3"); - if (byresi_opt>=2 && ter_opt>=2) - PrintErrorAndQuit("-byresi >=2 should be used with -ter <=1"); + if (byresi_opt<0 || byresi_opt>5) + PrintErrorAndQuit("-byresi can only be 0, 1, 2, 3, 4, or 5"); + if (byresi_opt>=2 && byresi_opt<=3 && ter_opt>=2) + PrintErrorAndQuit("-byresi 2 and -byresi 3 should be used with -ter <=1"); } if (split_opt==1 && ter_opt!=0) PrintErrorAndQuit("-split 1 should be used with -ter 0"); @@ -398,7 +404,7 @@ int main(int argc, char *argv[]) outfmt_opt, invmap); if (outfmt_opt>=2) - get_seqID(invmap, seqx, seqy, ylen, Liden, n_ali8); + get_seqID(invmap, seqx, seqy, ylen, Liden, n_ali); /* print result */ output_results( diff --git a/modules/bindings/src/tmalign/se.h b/modules/bindings/src/USalign/se.h similarity index 73% rename from modules/bindings/src/tmalign/se.h rename to modules/bindings/src/USalign/se.h index 6ccc84132d02b9e54179d9b6378937af38ce35a0..27eb3b48c0c3d112deba1b6d458c786c45f1b6d5 100644 --- a/modules/bindings/src/tmalign/se.h +++ b/modules/bindings/src/USalign/se.h @@ -1,7 +1,10 @@ #include "TMalign.h" /* entry function for se - * outfmt_opt>=2 should not parse sequence alignment */ + * outfmt_opt>=2 should not parse sequence alignment + * u_opt corresponds to option -L + * if u_opt==2, use d0 from Lnorm_ass for alignment + * if hinge>0, append to original invmap */ int se_main( double **xa, double **ya, const char *seqx, const char *seqy, double &TM1, double &TM2, double &TM3, double &TM4, double &TM5, @@ -12,8 +15,8 @@ int se_main( double &TM_ali, double &rmsd_ali, int &n_ali, int &n_ali8, const int xlen, const int ylen, const vector<string> &sequence, const double Lnorm_ass, const double d0_scale, const bool i_opt, - const bool a_opt, const bool u_opt, const bool d_opt, const int mol_type, - const int outfmt_opt, int *invmap) + const bool a_opt, const int u_opt, const bool d_opt, const int mol_type, + const int outfmt_opt, int *invmap, const int hinge=0) { double D0_MIN; //for d0 double Lnorm; //normalization length @@ -37,7 +40,21 @@ int se_main( NewArray(&score, xlen+1, ylen+1); NewArray(&path, xlen+1, ylen+1); NewArray(&val, xlen+1, ylen+1); - //int *invmap = new int[ylen+1]; + int *invmap0 = new int[ylen+1]; + int i,j; + if (hinge==0) for (j=0;j<=ylen;j++) invmap0[j]=-1; + else for (j=0;j<ylen;j++) invmap0[j]=invmap[j]; + vector<char> seqM_char; + if (hinge) + { + seqM_char.assign(ylen,hinge+'0'); + j=-1; + for (int r=0;r<seqM.size();r++) + { + j+=seqyA[r]!='-'; + if (seqM[r]!=' ') seqM_char[j]=seqM[r]; + } + } /* set d0 */ parameter_set4search(xlen, ylen, D0_MIN, Lnorm, @@ -50,12 +67,19 @@ int se_main( parameter_set4final((xlen+ylen)*0.5, D0_MIN, Lnorm, d0a, d0_search, mol_type); // set d0a if (u_opt) + { parameter_set4final(Lnorm_ass, D0_MIN, Lnorm, d0u, d0_search, mol_type); // set d0u + if (u_opt==2) + { + parameter_set4search(Lnorm_ass, Lnorm_ass, D0_MIN, Lnorm, + score_d8, d0, d0_search, dcu0); // set score_d8 + } + } /* perform alignment */ - for(int j=0; j<ylen; j++) invmap[j]=-1; - if (!i_opt) NWDP_SE(path, val, xa, ya, xlen, ylen, d0*d0, 0, invmap); + if (hinge==0) for(j=0; j<ylen; j++) invmap[j]=-1; + if (!i_opt) NWDP_SE(path, val, xa, ya, xlen, ylen, d0*d0, 0, invmap, hinge); else { int i1 = -1;// in C version, index starts from zero, not from one @@ -74,8 +98,17 @@ int se_main( } } } - - rmsd0=TM1=TM2=TM3=TM4=TM5=0; + + if (hinge==0) rmsd0=TM1=TM2=TM3=TM4=TM5=0; + else + { + TM2*=xlen; + TM1*=ylen; + TM3*=(xlen+ylen)*0.5; + TM4*=Lnorm_ass; + TM5*=ylen; + rmsd0=rmsd0*rmsd0*n_ali8; + } int k=0; n_ali=0; n_ali8=0; @@ -86,7 +119,7 @@ int se_main( { n_ali++; d=sqrt(dist(&xa[i][0], &ya[j][0])); - if (d <= score_d8 || i_opt) + if (d <= score_d8 || i_opt || invmap0[j]==i) { if (outfmt_opt<2) { @@ -94,6 +127,7 @@ int se_main( m2[k]=j; } k++; + if (invmap0[j]==i) continue; TM2+=1/(1+(d/d0B)*(d/d0B)); // chain_1 TM1+=1/(1+(d/d0A)*(d/d0A)); // chain_2 if (a_opt) TM3+=1/(1+(d/d0a)*(d/d0a)); // -a @@ -101,6 +135,7 @@ int se_main( if (d_opt) TM5+=1/(1+(d/d0_scale)*(d/d0_scale)); // -d rmsd0+=d*d; } + else if (hinge) invmap[j]=-1; } } n_ali8=k; @@ -113,6 +148,8 @@ int se_main( if (outfmt_opt>=2) { + if (hinge) seqM_char.clear(); + delete []invmap0; DeleteArray(&score, xlen+1); DeleteArray(&path, xlen+1); DeleteArray(&val, xlen+1); @@ -179,9 +216,18 @@ int se_main( seqxA=seqxA.substr(0,kk); seqyA=seqyA.substr(0,kk); seqM =seqM.substr(0,kk); + if (hinge) + { + j=-1; + for (int r=0;r<seqM.size();r++) + { + j+=seqyA[r]!='-'; + if (seqM[r]!=' ') seqM[r]=seqM_char[j]; + } + } /* free memory */ - //delete [] invmap; + delete [] invmap0; delete [] m1; delete [] m2; DeleteArray(&score, xlen+1); diff --git a/modules/bindings/src/USalign/usalign.py b/modules/bindings/src/USalign/usalign.py new file mode 100644 index 0000000000000000000000000000000000000000..fc9ddd3df697a2c4f4ea228a256489bd3a3e0254 --- /dev/null +++ b/modules/bindings/src/USalign/usalign.py @@ -0,0 +1,132 @@ +#!/usr/bin/env pymol +''' +PyMOL plugin for US-align + +USAGE: + + usalign mobile, fix [,args [,exe]] + +INSTALLATION + + Install this script as a PyMOL plugin by + "Plugin" - "Plugin Manager" - "Install New Plugin" + + This plugin depends on the binary executable of US-align, which must be + available within a directory specified by PATH. You can get the PATH + value within PyMOL by the following command: + + print(os.getenv('PATH')) +''' +#This script is partly based on tmalign plugin by Thomas Holder available at +#https://github.com/Pymol-Scripts/Pymol-script-repo/blob/master/tmalign.py + +from __future__ import print_function + +__author__ = 'Chengxin Zhang' +__version__ = '20220924' +__license__ = 'BSD-2-Clause' + +from pymol import cmd, CmdException +import subprocess +import tempfile +import os +import platform + +def get_usalign_path(exe="USalign"): + if platform.system().lower().startswith("win"): + exe+=".exe" + filename = os.path.join(os.path.dirname(os.path.abspath(__file__)),exe) + if os.path.isfile(filename): + return filename + else: + for p in os.getenv("PATH").split(os.pathsep): + filename=os.path.join(p,exe) + if os.path.isfile(filename): + return filename + print("ERROR! Cannot locate %s at %s or at %s"%(exe, + os.path.dirname(os.path.abspath(__file__)),os.getenv("PATH"))) + print("Please put the USalign executable at one of the aforementioned paths") + return exe + +def usalign(mobile, target, args='', exe='', transform=1): + ''' +USAGE + + usalign mobile, target [, args [, exe ]] + +ARGUMENTS + + mobile, target = string: atom selections + + args = string: Extra arguments such as -mm and -byresi + + exe = string: Path to USalign executable {default: USalign} + +CITATION + + Zhang C, Shine M, Pyle AM, Zhang Y. bioRxiv 2022.04.18.488565. + https://github.com/pylelab/USalign + ''' + + mobile_filename = tempfile.mktemp('.pdb', 'mobile') + target_filename = tempfile.mktemp('.pdb', 'target') + mobile_ca_sele = '(%s) and (not hetatm) and alt +A' % (mobile) + target_ca_sele = '(%s) and (not hetatm) and alt +A' % (target) + if not "-atom" in args: + mobile_ca_sele+=" and (name CA or name C3')" + target_ca_sele+=" and (name CA or name C3')" + + cmd.save(mobile_filename, mobile_ca_sele) + cmd.save(target_filename, target_ca_sele) + + if len(exe)==0: + exe=get_usalign_path("USalign") + if args=='""': + args='' + if len(args)>2 and args[0]=='"' and args[-1]=='"': + args=args[1:-1] + if not "-outfmt" in args: + args+=" -outfmt -1" + args = ' '.join([exe, mobile_filename, target_filename, args, '-m -']) + print(args) + + try: + process = subprocess.Popen(args, stdout=subprocess.PIPE, shell=True, + universal_newlines=True) + lines = process.stdout.readlines() + except OSError: + print('Cannot execute "%s", please provide full path to USalign executable' % (args)) + raise CmdException + finally: + os.remove(mobile_filename) + os.remove(target_filename) + + rowcount = 0 + matrix = [] + for line in iter(lines): + print(line.rstrip()) + if line.strip().startswith('------ The rotation matrix to rotate '): + rowcount = 1 + elif 4 >= rowcount and rowcount> 0: + if rowcount >= 2: + a = list(map(float, line.split())) + matrix.extend(a[2:5]) + matrix.append(a[1]) + rowcount += 1 + + assert len(matrix) == 3 * 4 + matrix.extend([0, 0, 0, 1]) + + if int(transform): + cmd.transform_selection('byobject (%s)' % (mobile), matrix, homogenous=1) + return + +# pymol commands +cmd.extend('usalign', usalign) +cmd.extend('USalign', usalign) + +# autocompletion +cmd.auto_arg[0].update({ 'usalign': cmd.auto_arg[0]['align'], }) +cmd.auto_arg[1].update({ 'usalign': cmd.auto_arg[1]['align'], }) +cmd.auto_arg[0].update({ 'USalign': cmd.auto_arg[0]['align'], }) +cmd.auto_arg[1].update({ 'USalign': cmd.auto_arg[1]['align'], }) diff --git a/modules/bindings/src/tmalign/xyz_sfetch.cpp b/modules/bindings/src/USalign/xyz_sfetch.cpp similarity index 83% rename from modules/bindings/src/tmalign/xyz_sfetch.cpp rename to modules/bindings/src/USalign/xyz_sfetch.cpp index 5d413d5c5a59b7fd803e61458af9c726e55592b3..4cf0576053b9b7c7a6dcc3171e42439e17f26ca6 100644 --- a/modules/bindings/src/tmalign/xyz_sfetch.cpp +++ b/modules/bindings/src/USalign/xyz_sfetch.cpp @@ -84,15 +84,27 @@ int main(int argc, char *argv[]) /* read entry list */ vector<string> chain_list; - ifstream fp(list_opt.c_str()); - while (fp.good()) + ifstream fp; + if (list_opt=="-") { - getline(fp, line); - for (i=0;i<line.size();i++) - if (line[i]==' '||line[i]=='\t') break; - if (line.size() && i) chain_list.push_back(line.substr(0,i)); + while (cin.good()) + { + getline(cin, line); + for (i=0;i<line.size();i++) if (line[i]==' '||line[i]=='\t') break; + if (line.size() && i) chain_list.push_back(line.substr(0,i)); + } + } + else + { + fp.open(list_opt.c_str(),ios::in); + while (fp.good()) + { + getline(fp, line); + for (i=0;i<line.size();i++) if (line[i]==' '||line[i]=='\t') break; + if (line.size() && i) chain_list.push_back(line.substr(0,i)); + } + fp.close(); } - fp.close(); /* read xyz index */ /* In xyz file, each line has 28 chacters plus an additional '\n'. In PDB @@ -128,6 +140,6 @@ int main(int argc, char *argv[]) delete[]buf; filename.clear(); list_opt.clear(); - chain_list.clear(); + vector<string>().swap(chain_list); return 0; } diff --git a/modules/bindings/src/tmalign/.gitignore b/modules/bindings/src/tmalign/.gitignore deleted file mode 100644 index 4dbbc7f9963a945219f65c16cc78d9304d44d358..0000000000000000000000000000000000000000 --- a/modules/bindings/src/tmalign/.gitignore +++ /dev/null @@ -1,17 +0,0 @@ -# compiled python code -*.pyc - -# vim temporary backup -.*.sw* - -# binary executables -TMalign -TMalignc -pdb2xyz -pdb2fasta -pdb2ss -xyz_sfetch -se -qTMclust -NWalign -HwRMSD diff --git a/modules/bindings/src/tmalign/MMalign.h b/modules/bindings/src/tmalign/MMalign.h deleted file mode 100644 index af9920a8cdc8087982310c94dfba08378f46b2d7..0000000000000000000000000000000000000000 --- a/modules/bindings/src/tmalign/MMalign.h +++ /dev/null @@ -1,1194 +0,0 @@ -#include "se.h" - -/* count the number of nucleic acid chains (na_chain_num) and - * protein chains (aa_chain_num) in a complex */ -int count_na_aa_chain_num(int &na_chain_num,int &aa_chain_num, - const vector<int>&mol_vec) -{ - na_chain_num=0; - aa_chain_num=0; - for (size_t i=0;i<mol_vec.size();i++) - { - if (mol_vec[i]>0) na_chain_num++; - else aa_chain_num++; - } - return na_chain_num+aa_chain_num; -} - -/* adjust chain assignment for dimer-dimer alignment - * return true if assignment is adjusted */ -bool adjust_dimer_assignment( - const vector<vector<vector<double> > >&xa_vec, - const vector<vector<vector<double> > >&ya_vec, - const vector<int>&xlen_vec, const vector<int>&ylen_vec, - const vector<int>&mol_vec1, const vector<int>&mol_vec2, - int *assign1_list, int *assign2_list, - const vector<vector<string> >&seqxA_mat, - const vector<vector<string> >&seqyA_mat) -{ - /* check currently assigned chains */ - int i1,i2,j1,j2; - i1=i2=j1=j2=-1; - int chain1_num=xa_vec.size(); - int i,j; - for (i=0;i<chain1_num;i++) - { - if (assign1_list[i]>=0) - { - if (i1<0) - { - i1=i; - j1=assign1_list[i1]; - } - else - { - i2=i; - j2=assign1_list[i2]; - } - } - } - - /* normalize d0 by L */ - int xlen=xlen_vec[i1]+xlen_vec[i2]; - int ylen=ylen_vec[j1]+ylen_vec[j2]; - int mol_type=mol_vec1[i1]+mol_vec1[i2]+ - mol_vec2[j1]+mol_vec2[j2]; - double D0_MIN, d0, d0_search; - double Lnorm=getmin(xlen,ylen); - parameter_set4final(getmin(xlen,ylen), D0_MIN, Lnorm, d0, - d0_search, mol_type); - - double **xa,**ya, **xt; - NewArray(&xa, xlen, 3); - NewArray(&ya, ylen, 3); - NewArray(&xt, xlen, 3); - - double RMSD = 0; - double dd = 0; - double t[3]; - double u[3][3]; - size_t L_ali=0; // index of residue in aligned region - size_t r=0; // index of residue in full alignment - - /* total score using current assignment */ - L_ali=0; - i=j=-1; - for (r=0;r<seqxA_mat[i1][j1].size();r++) - { - i+=(seqxA_mat[i1][j1][r]!='-'); - j+=(seqyA_mat[i1][j1][r]!='-'); - if (seqxA_mat[i1][j1][r]=='-' || seqyA_mat[i1][j1][r]=='-') continue; - xa[L_ali][0]=xa_vec[i1][i][0]; - xa[L_ali][1]=xa_vec[i1][i][1]; - xa[L_ali][2]=xa_vec[i1][i][2]; - ya[L_ali][0]=ya_vec[j1][j][0]; - ya[L_ali][1]=ya_vec[j1][j][1]; - ya[L_ali][2]=ya_vec[j1][j][2]; - L_ali++; - } - i=j=-1; - for (r=0;r<seqxA_mat[i2][j2].size();r++) - { - i+=(seqxA_mat[i2][j2][r]!='-'); - j+=(seqyA_mat[i2][j2][r]!='-'); - if (seqxA_mat[i2][j2][r]=='-' || seqyA_mat[i2][j2][r]=='-') continue; - xa[L_ali][0]=xa_vec[i2][i][0]; - xa[L_ali][1]=xa_vec[i2][i][1]; - xa[L_ali][2]=xa_vec[i2][i][2]; - ya[L_ali][0]=ya_vec[j2][j][0]; - ya[L_ali][1]=ya_vec[j2][j][1]; - ya[L_ali][2]=ya_vec[j2][j][2]; - L_ali++; - } - - Kabsch(xa, ya, L_ali, 1, &RMSD, t, u); - do_rotation(xa, xt, L_ali, t, u); - - double total_score1=0; - for (r=0;r<L_ali;r++) - { - dd=dist(xt[r],ya[r]); - total_score1+=1/(1+dd/d0*d0); - } - total_score1/=Lnorm; - - /* total score using reversed assignment */ - L_ali=0; - i=j=-1; - for (r=0;r<seqxA_mat[i1][j2].size();r++) - { - i+=(seqxA_mat[i1][j2][r]!='-'); - j+=(seqyA_mat[i1][j2][r]!='-'); - if (seqxA_mat[i1][j2][r]=='-' || seqyA_mat[i1][j2][r]=='-') continue; - xa[L_ali][0]=xa_vec[i1][i][0]; - xa[L_ali][1]=xa_vec[i1][i][1]; - xa[L_ali][2]=xa_vec[i1][i][2]; - ya[L_ali][0]=ya_vec[j2][j][0]; - ya[L_ali][1]=ya_vec[j2][j][1]; - ya[L_ali][2]=ya_vec[j2][j][2]; - L_ali++; - } - i=j=-1; - for (r=0;r<seqxA_mat[i2][j1].size();r++) - { - i+=(seqxA_mat[i2][j1][r]!='-'); - j+=(seqyA_mat[i2][j1][r]!='-'); - if (seqxA_mat[i2][j1][r]=='-' || seqyA_mat[i2][j1][r]=='-') continue; - xa[L_ali][0]=xa_vec[i2][i][0]; - xa[L_ali][1]=xa_vec[i2][i][1]; - xa[L_ali][2]=xa_vec[i2][i][2]; - ya[L_ali][0]=ya_vec[j1][j][0]; - ya[L_ali][1]=ya_vec[j1][j][1]; - ya[L_ali][2]=ya_vec[j1][j][2]; - L_ali++; - } - - Kabsch(xa, ya, L_ali, 1, &RMSD, t, u); - do_rotation(xa, xt, L_ali, t, u); - - double total_score2=0; - for (r=0;r<L_ali;r++) - { - dd=dist(xt[r],ya[r]); - total_score2+=1/(1+dd/d0*d0); - } - total_score2/=Lnorm; - - /* swap chain assignment */ - if (total_score1<total_score2) - { - assign1_list[i1]=j2; - assign1_list[i2]=j1; - assign2_list[j1]=i2; - assign2_list[j2]=i1; - } - - /* clean up */ - DeleteArray(&xa, xlen); - DeleteArray(&ya, ylen); - DeleteArray(&xt, xlen); - return total_score1<total_score2; -} - -/* assign chain-chain correspondence */ -double enhanced_greedy_search(double **TMave_mat,int *assign1_list, - int *assign2_list, const int chain1_num, const int chain2_num) -{ - double total_score=0; - double tmp_score=0; - int i,j; - int maxi=0; - int maxj=0; - - /* initialize parameters */ - for (i=0;i<chain1_num;i++) assign1_list[i]=-1; - for (j=0;j<chain2_num;j++) assign2_list[j]=-1; - - /* greedy assignment: in each iteration, the highest chain pair is - * assigned, until no assignable chain is left */ - while(1) - { - tmp_score=-1; - for (i=0;i<chain1_num;i++) - { - if (assign1_list[i]>=0) continue; - for (j=0;j<chain2_num;j++) - { - if (assign2_list[j]>=0 || TMave_mat[i][j]<=0) continue; - if (TMave_mat[i][j]>tmp_score) - { - maxi=i; - maxj=j; - tmp_score=TMave_mat[i][j]; - } - } - } - if (tmp_score<=0) break; // error: no assignable chain - assign1_list[maxi]=maxj; - assign2_list[maxj]=maxi; - total_score+=tmp_score; - } - if (total_score<=0) return total_score; // error: no assignable chain - //cout<<"assign1_list={"; - //for (i=0;i<chain1_num;i++) cout<<assign1_list[i]<<","; cout<<"}"<<endl; - //cout<<"assign2_list={"; - //for (j=0;j<chain2_num;j++) cout<<assign2_list[j]<<","; cout<<"}"<<endl; - - /* iterative refinemnt */ - double delta_score; - int *assign1_tmp=new int [chain1_num]; - int *assign2_tmp=new int [chain2_num]; - for (i=0;i<chain1_num;i++) assign1_tmp[i]=assign1_list[i]; - for (j=0;j<chain2_num;j++) assign2_tmp[j]=assign2_list[j]; - int old_i=-1; - int old_j=-1; - - for (int iter=0;iter<getmin(chain1_num,chain2_num)*5;iter++) - { - delta_score=-1; - for (i=0;i<chain1_num;i++) - { - old_j=assign1_list[i]; - for (j=0;j<chain2_num;j++) - { - // attempt to swap (i,old_j=assign1_list[i]) with (i,j) - if (j==assign1_list[i] || TMave_mat[i][j]<=0) continue; - old_i=assign2_list[j]; - - assign1_tmp[i]=j; - if (old_i>=0) assign1_tmp[old_i]=old_j; - assign2_tmp[j]=i; - if (old_j>=0) assign2_tmp[old_j]=old_i; - - delta_score=TMave_mat[i][j]; - if (old_j>=0) delta_score-=TMave_mat[i][old_j]; - if (old_i>=0) delta_score-=TMave_mat[old_i][j]; - if (old_i>=0 && old_j>=0) delta_score+=TMave_mat[old_i][old_j]; - - if (delta_score>0) // successful swap - { - assign1_list[i]=j; - if (old_i>=0) assign1_list[old_i]=old_j; - assign2_list[j]=i; - if (old_j>=0) assign2_list[old_j]=old_i; - total_score+=delta_score; - break; - } - else - { - assign1_tmp[i]=assign1_list[i]; - if (old_i>=0) assign1_tmp[old_i]=assign1_list[old_i]; - assign2_tmp[j]=assign2_list[j]; - if (old_j>=0) assign2_tmp[old_j]=assign2_list[old_j]; - } - } - if (delta_score>0) break; - } - if (delta_score<=0) break; // cannot swap any chain pair - } - - /* clean up */ - delete[]assign1_tmp; - delete[]assign2_tmp; - return total_score; -} - -double calculate_centroids(const vector<vector<vector<double> > >&a_vec, - const int chain_num, double ** centroids) -{ - int L=0; - int c,r; // index of chain and residue - for (c=0; c<chain_num; c++) - { - centroids[c][0]=0; - centroids[c][1]=0; - centroids[c][2]=0; - L=a_vec[c].size(); - for (r=0; r<L; r++) - { - centroids[c][0]+=a_vec[c][r][0]; - centroids[c][1]+=a_vec[c][r][1]; - centroids[c][2]+=a_vec[c][r][2]; - } - centroids[c][0]/=L; - centroids[c][1]/=L; - centroids[c][2]/=L; - //cout<<centroids[c][0]<<'\t' - //<<centroids[c][1]<<'\t' - //<<centroids[c][2]<<endl; - } - - vector<double> d0_vec(chain_num,-1); - int c2=0; - double d0MM=0; - for (c=0; c<chain_num; c++) - { - for (c2=0; c2<chain_num; c2++) - { - if (c2==c) continue; - d0MM=sqrt(dist(centroids[c],centroids[c2])); - if (d0_vec[c]<=0) d0_vec[c]=d0MM; - else d0_vec[c]=getmin(d0_vec[c], d0MM); - } - } - d0MM=0; - for (c=0; c<chain_num; c++) d0MM+=d0_vec[c]; - d0MM/=chain_num; - d0_vec.clear(); - //cout<<d0MM<<endl; - return d0MM; -} - -/* calculate MMscore of aligned chains - * MMscore = sum(TMave_mat[i][j]) * sum(1/(1+dij^2/d0MM^2)) - * / (L* getmin(chain1_num,chain2_num)) - * dij is the centroid distance between chain pair i and j - * d0MM is scaling factor. TMave_mat[i][j] is the TM-score between - * chain pair i and j multiple by getmin(Li*Lj) */ -double calMMscore(double **TMave_mat,int *assign1_list, - const int chain1_num, const int chain2_num, double **xcentroids, - double **ycentroids, const double d0MM, double **r1, double **r2, - double **xt, double t[3], double u[3][3], const int L) -{ - int Nali=0; // number of aligned chain - int i,j; - double MMscore=0; - for (i=0;i<chain1_num;i++) - { - j=assign1_list[i]; - if (j<0) continue; - - r1[Nali][0]=xcentroids[i][0]; - r1[Nali][1]=xcentroids[i][1]; - r1[Nali][2]=xcentroids[i][2]; - - r2[Nali][0]=ycentroids[j][0]; - r2[Nali][1]=ycentroids[j][1]; - r2[Nali][2]=ycentroids[j][2]; - - Nali++; - MMscore+=TMave_mat[i][j]; - } - MMscore/=L; - - double RMSD = 0; - double TMscore=0; - if (Nali>=3) - { - /* Kabsch superposition */ - Kabsch(r1, r2, Nali, 1, &RMSD, t, u); - do_rotation(r1, xt, Nali, t, u); - - /* calculate pseudo-TMscore */ - double dd=0; - for (i=0;i<Nali;i++) - { - dd=dist(xt[i], r2[i]); - TMscore+=1/(1+dd/(d0MM*d0MM)); - } - } - else if (Nali==2) - { - double dd=dist(r1[0],r2[0]); - TMscore=1/(1+dd/(d0MM*d0MM)); - } - else TMscore=1; // only one aligned chain. - TMscore/=getmin(chain1_num,chain2_num); - MMscore*=TMscore; - return MMscore; -} - -/* check if this is alignment of heterooligomer or homooligomer - * return het_deg, which ranges from 0 to 1. - * The larger the value, the more "hetero"; - * Tthe smaller the value, the more "homo" */ -double check_heterooligomer(double **TMave_mat, const int chain1_num, - const int chain2_num) -{ - double het_deg=0; - double min_TM=-1; - double max_TM=-1; - int i,j; - for (i=0;i<chain1_num;i++) - { - for (j=0;j<chain2_num;j++) - { - if (min_TM<0 || TMave_mat[i][j] <min_TM) min_TM=TMave_mat[i][j]; - if (max_TM<0 || TMave_mat[i][j]>=max_TM) max_TM=TMave_mat[i][j]; - } - } - het_deg=(max_TM-min_TM)/max_TM; - //cout<<"min_TM="<<min_TM<<endl; - //cout<<"max_TM="<<max_TM<<endl; - return het_deg; -} - -/* reassign chain-chain correspondence, specific for homooligomer */ -double homo_refined_greedy_search(double **TMave_mat,int *assign1_list, - int *assign2_list, const int chain1_num, const int chain2_num, - double **xcentroids, double **ycentroids, const double d0MM, - const int L, double **ut_mat) -{ - double MMscore_max=0; - double MMscore=0; - int i,j; - int c1,c2; - int max_i=-1; // the chain pair whose monomer u t yields highest MMscore - int max_j=-1; - - int chain_num=getmin(chain1_num,chain2_num); - int *assign1_tmp=new int [chain1_num]; - int *assign2_tmp=new int [chain2_num]; - double **xt; - NewArray(&xt, chain1_num, 3); - double t[3]; - double u[3][3]; - int ui,uj,ut_idx; - double TMscore=0; // pseudo TM-score - double TMsum =0; - double TMnow =0; - double TMmax =0; - double dd=0; - - size_t total_pair=chain1_num*chain2_num; // total pair - double *ut_tmc_mat=new double [total_pair]; // chain level TM-score - vector<pair<double,int> > ut_tm_vec(total_pair,make_pair(0.0,0)); // product of both - - for (c1=0;c1<chain1_num;c1++) - { - for (c2=0;c2<chain2_num;c2++) - { - if (TMave_mat[c1][c2]<=0) continue; - ut_idx=c1*chain2_num+c2; - for (ui=0;ui<3;ui++) - for (uj=0;uj<3;uj++) u[ui][uj]=ut_mat[ut_idx][ui*3+uj]; - for (uj=0;uj<3;uj++) t[uj]=ut_mat[ut_idx][9+uj]; - - do_rotation(xcentroids, xt, chain1_num, t, u); - - for (i=0;i<chain1_num;i++) assign1_tmp[i]=-1; - for (j=0;j<chain2_num;j++) assign2_tmp[j]=-1; - - - for (i=0;i<chain1_num;i++) - { - for (j=0;j<chain2_num;j++) - { - ut_idx=i*chain2_num+j; - ut_tmc_mat[ut_idx]=0; - ut_tm_vec[ut_idx].first=-1; - ut_tm_vec[ut_idx].second=ut_idx; - if (TMave_mat[i][j]<=0) continue; - dd=dist(xt[i],ycentroids[j]); - ut_tmc_mat[ut_idx]=1/(1+dd/(d0MM*d0MM)); - ut_tm_vec[ut_idx].first= - ut_tmc_mat[ut_idx]*TMave_mat[i][j]; - //cout<<"TM["<<ut_idx<<"]="<<ut_tm_vec[ut_idx].first<<endl; - } - } - //cout<<"sorting "<<total_pair<<" chain pairs"<<endl; - - /* initial assignment */ - assign1_tmp[c1]=c2; - assign2_tmp[c2]=c1; - TMsum=TMave_mat[c1][c2]; - TMscore=ut_tmc_mat[c1*chain2_num+c2]; - - /* further assignment */ - sort(ut_tm_vec.begin(), ut_tm_vec.end()); // sort in ascending order - for (ut_idx=total_pair-1;ut_idx>=0;ut_idx--) - { - j=ut_tm_vec[ut_idx].second % chain2_num; - i=int(ut_tm_vec[ut_idx].second / chain2_num); - if (TMave_mat[i][j]<=0) break; - if (assign1_tmp[i]>=0 || assign2_tmp[j]>=0) continue; - assign1_tmp[i]=j; - assign2_tmp[j]=i; - TMsum+=TMave_mat[i][j]; - TMscore+=ut_tmc_mat[i*chain2_num+j]; - //cout<<"ut_idx="<<ut_tm_vec[ut_idx].second - //<<"\ti="<<i<<"\tj="<<j<<"\ttm="<<ut_tm_vec[ut_idx].first<<endl; - } - - /* final MMscore */ - MMscore=(TMsum/L)*(TMscore/chain_num); - if (max_i<0 || max_j<0 || MMscore>MMscore_max) - { - max_i=c1; - max_j=c2; - MMscore_max=MMscore; - for (i=0;i<chain1_num;i++) assign1_list[i]=assign1_tmp[i]; - for (j=0;j<chain2_num;j++) assign2_list[j]=assign2_tmp[j]; - //cout<<"TMsum/L="<<TMsum/L<<endl; - //cout<<"TMscore/chain_num="<<TMscore/chain_num<<endl; - //cout<<"MMscore="<<MMscore<<endl; - //cout<<"assign1_list={"; - //for (i=0;i<chain1_num;i++) - //cout<<assign1_list[i]<<","; cout<<"}"<<endl; - //cout<<"assign2_list={"; - //for (j=0;j<chain2_num;j++) - //cout<<assign2_list[j]<<","; cout<<"}"<<endl; - } - } - } - - /* clean up */ - delete[]assign1_tmp; - delete[]assign2_tmp; - delete[]ut_tmc_mat; - ut_tm_vec.clear(); - DeleteArray(&xt, chain1_num); - return MMscore; -} - -/* reassign chain-chain correspondence, specific for heterooligomer */ -double hetero_refined_greedy_search(double **TMave_mat,int *assign1_list, - int *assign2_list, const int chain1_num, const int chain2_num, - double **xcentroids, double **ycentroids, const double d0MM, const int L) -{ - double MMscore_old=0; - double MMscore=0; - int i,j; - - double **r1; - double **r2; - double **xt; - int chain_num=getmin(chain1_num,chain2_num); - NewArray(&r1, chain_num, 3); - NewArray(&r2, chain_num, 3); - NewArray(&xt, chain_num, 3); - double t[3]; - double u[3][3]; - - /* calculate MMscore */ - MMscore=MMscore_old=calMMscore(TMave_mat, assign1_list, chain1_num, - chain2_num, xcentroids, ycentroids, d0MM, r1, r2, xt, t, u, L); - //cout<<"MMscore="<<MMscore<<endl; - //cout<<"TMave_mat="<<endl; - //for (i=0;i<chain1_num;i++) - //{ - //for (j=0; j<chain2_num; j++) - //{ - //if (j<chain2_num-1) cout<<TMave_mat[i][j]<<'\t'; - //else cout<<TMave_mat[i][j]<<endl; - //} - //} - - /* iteratively refine chain assignment. in each iteration, attempt - * to swap (i,old_j=assign1_list[i]) with (i,j) */ - double delta_score=-1; - int *assign1_tmp=new int [chain1_num]; - int *assign2_tmp=new int [chain2_num]; - for (i=0;i<chain1_num;i++) assign1_tmp[i]=assign1_list[i]; - for (j=0;j<chain2_num;j++) assign2_tmp[j]=assign2_list[j]; - int old_i=-1; - int old_j=-1; - - //cout<<"assign1_list={"; - //for (i=0;i<chain1_num;i++) cout<<assign1_list[i]<<","; cout<<"}"<<endl; - //cout<<"assign2_list={"; - //for (j=0;j<chain2_num;j++) cout<<assign2_list[j]<<","; cout<<"}"<<endl; - - for (int iter=0;iter<chain1_num*chain2_num;iter++) - { - delta_score=-1; - for (i=0;i<chain1_num;i++) - { - old_j=assign1_list[i]; - for (j=0;j<chain2_num;j++) - { - if (j==assign1_list[i] || TMave_mat[i][j]<=0) continue; - old_i=assign2_list[j]; - - assign1_tmp[i]=j; - if (old_i>=0) assign1_tmp[old_i]=old_j; - assign2_tmp[j]=i; - if (old_j>=0) assign2_tmp[old_j]=old_i; - - MMscore=calMMscore(TMave_mat, assign1_tmp, chain1_num, - chain2_num, xcentroids, ycentroids, d0MM, - r1, r2, xt, t, u, L); - - //cout<<"(i,j,old_i,old_j,MMscore)=("<<i<<","<<j<<"," - //<<old_i<<","<<old_j<<","<<MMscore<<")"<<endl; - - if (MMscore>MMscore_old) // successful swap - { - assign1_list[i]=j; - if (old_i>=0) assign1_list[old_i]=old_j; - assign2_list[j]=i; - if (old_j>=0) assign2_list[old_j]=old_i; - delta_score=(MMscore-MMscore_old); - MMscore_old=MMscore; - //cout<<"MMscore="<<MMscore<<endl; - break; - } - else - { - assign1_tmp[i]=assign1_list[i]; - if (old_i>=0) assign1_tmp[old_i]=assign1_list[old_i]; - assign2_tmp[j]=assign2_list[j]; - if (old_j>=0) assign2_tmp[old_j]=assign2_list[old_j]; - } - } - } - //cout<<"iter="<<iter<<endl; - //cout<<"assign1_list={"; - //for (i=0;i<chain1_num;i++) cout<<assign1_list[i]<<","; cout<<"}"<<endl; - //cout<<"assign2_list={"; - //for (j=0;j<chain2_num;j++) cout<<assign2_list[j]<<","; cout<<"}"<<endl; - if (delta_score<=0) break; // cannot swap any chain pair - } - MMscore=MMscore_old; - //cout<<"MMscore="<<MMscore<<endl; - - /* clean up */ - delete[]assign1_tmp; - delete[]assign2_tmp; - DeleteArray(&r1, chain_num); - DeleteArray(&r2, chain_num); - DeleteArray(&xt, chain_num); - return MMscore; -} - -void copy_chain_data(const vector<vector<double> >&a_vec_i, - const vector<char>&seq_vec_i,const vector<char>&sec_vec_i, - const int len,double **a,char *seq,char *sec) -{ - int r; - for (r=0;r<len;r++) - { - a[r][0]=a_vec_i[r][0]; - a[r][1]=a_vec_i[r][1]; - a[r][2]=a_vec_i[r][2]; - seq[r]=seq_vec_i[r]; - sec[r]=sec_vec_i[r]; - } - seq[len]=0; - sec[len]=0; -} - -void parse_chain_list(const vector<string>&chain_list, - vector<vector<vector<double> > >&a_vec, vector<vector<char> >&seq_vec, - vector<vector<char> >&sec_vec, vector<int>&mol_vec, vector<int>&len_vec, - vector<string>&chainID_list, const int ter_opt, const int split_opt, - const string mol_opt, const int infmt_opt, const string atom_opt, - const int mirror_opt, const int het_opt, int &len_aa, int &len_na, - const int o_opt, vector<string>&resi_vec) -{ - size_t i; - int chain_i,r; - string name; - int chainnum; - double **xa; - int len; - char *seq,*sec; - - vector<vector<string> >PDB_lines; - vector<double> tmp_atom_array(3,0); - vector<vector<double> > tmp_chain_array; - vector<char>tmp_seq_array; - vector<char>tmp_sec_array; - //vector<string> resi_vec; - int read_resi=0; - if (o_opt) read_resi=2; - - for (i=0;i<chain_list.size();i++) - { - name=chain_list[i]; - chainnum=get_PDB_lines(name, PDB_lines, chainID_list, - mol_vec, ter_opt, infmt_opt, atom_opt, split_opt, het_opt); - if (!chainnum) - { - cerr<<"Warning! Cannot parse file: "<<name - <<". Chain number 0."<<endl; - continue; - } - for (chain_i=0;chain_i<chainnum;chain_i++) - { - len=PDB_lines[chain_i].size(); - if (!len) - { - cerr<<"Warning! Cannot parse file: "<<name - <<". Chain length 0."<<endl; - continue; - } - else if (len<3) - { - cerr<<"Sequence is too short <3!: "<<name<<endl; - continue; - } - NewArray(&xa, len, 3); - seq = new char[len + 1]; - sec = new char[len + 1]; - len = read_PDB(PDB_lines[chain_i], xa, seq, resi_vec, read_resi); - if (mirror_opt) for (r=0;r<len;r++) xa[r][2]=-xa[r][2]; - if (mol_vec[chain_i]>0 || mol_opt=="RNA") - make_sec(seq, xa, len, sec,atom_opt); - else make_sec(xa, len, sec); // secondary structure assignment - - /* store in vector */ - tmp_chain_array.assign(len,tmp_atom_array); - vector<char>tmp_seq_array(len+1,0); - vector<char>tmp_sec_array(len+1,0); - for (r=0;r<len;r++) - { - tmp_chain_array[r][0]=xa[r][0]; - tmp_chain_array[r][1]=xa[r][1]; - tmp_chain_array[r][2]=xa[r][2]; - tmp_seq_array[r]=seq[r]; - tmp_sec_array[r]=sec[r]; - } - a_vec.push_back(tmp_chain_array); - seq_vec.push_back(tmp_seq_array); - sec_vec.push_back(tmp_sec_array); - len_vec.push_back(len); - - /* clean up */ - tmp_chain_array.clear(); - tmp_seq_array.clear(); - tmp_sec_array.clear(); - PDB_lines[chain_i].clear(); - DeleteArray(&xa, len); - delete [] seq; - delete [] sec; - } // chain_i - name.clear(); - PDB_lines.clear(); - mol_vec.clear(); - } // i - tmp_atom_array.clear(); - - if (mol_opt=="RNA") mol_vec.assign(a_vec.size(),1); - else if (mol_opt=="protein") mol_vec.assign(a_vec.size(),-1); - else - { - mol_vec.assign(a_vec.size(),0); - for (i=0;i<a_vec.size();i++) - { - for (r=0;r<len_vec[i];r++) - { - if (seq_vec[i][r]>='a' && seq_vec[i][r]<='z') mol_vec[i]++; - else mol_vec[i]--; - } - } - } - - len_aa=0; - len_na=0; - for (i=0;i<a_vec.size();i++) - { - if (mol_vec[i]>0) len_na+=len_vec[i]; - else len_aa+=len_vec[i]; - } -} - -int copy_chain_pair_data( - const vector<vector<vector<double> > >&xa_vec, - const vector<vector<vector<double> > >&ya_vec, - const vector<vector<char> >&seqx_vec, const vector<vector<char> >&seqy_vec, - const vector<vector<char> >&secx_vec, const vector<vector<char> >&secy_vec, - const vector<int> &mol_vec1, const vector<int> &mol_vec2, - const vector<int> &xlen_vec, const vector<int> &ylen_vec, - double **xa, double **ya, char *seqx, char *seqy, char *secx, char *secy, - int chain1_num, int chain2_num, - vector<vector<string> >&seqxA_mat, vector<vector<string> >&seqyA_mat, - int *assign1_list, int *assign2_list, vector<string>&sequence) -{ - int i,j,r; - sequence.clear(); - sequence.push_back(""); - sequence.push_back(""); - int mol_type=0; - int xlen=0; - int ylen=0; - for (i=0;i<chain1_num;i++) - { - j=assign1_list[i]; - if (j<0) continue; - for (r=0;r<xlen_vec[i];r++) - { - seqx[xlen]=seqx_vec[i][r]; - secx[xlen]=secx_vec[i][r]; - xa[xlen][0]= xa_vec[i][r][0]; - xa[xlen][1]= xa_vec[i][r][1]; - xa[xlen][2]= xa_vec[i][r][2]; - xlen++; - } - sequence[0]+=seqxA_mat[i][j]; - for (r=0;r<ylen_vec[j];r++) - { - seqy[ylen]=seqy_vec[j][r]; - secy[ylen]=secy_vec[j][r]; - ya[ylen][0]= ya_vec[j][r][0]; - ya[ylen][1]= ya_vec[j][r][1]; - ya[ylen][2]= ya_vec[j][r][2]; - ylen++; - } - sequence[1]+=seqyA_mat[i][j]; - mol_type+=mol_vec1[i]+mol_vec2[j]; - } - seqx[xlen]=0; - secx[xlen]=0; - seqy[ylen]=0; - secy[ylen]=0; - return mol_type; -} - -double MMalign_search( - const vector<vector<vector<double> > >&xa_vec, - const vector<vector<vector<double> > >&ya_vec, - const vector<vector<char> >&seqx_vec, const vector<vector<char> >&seqy_vec, - const vector<vector<char> >&secx_vec, const vector<vector<char> >&secy_vec, - const vector<int> &mol_vec1, const vector<int> &mol_vec2, - const vector<int> &xlen_vec, const vector<int> &ylen_vec, - double **xa, double **ya, char *seqx, char *seqy, char *secx, char *secy, - int len_aa, int len_na, int chain1_num, int chain2_num, - double **TM1_mat, double **TM2_mat, double **TMave_mat, - vector<vector<string> >&seqxA_mat, vector<vector<string> >&seqyA_mat, - int *assign1_list, int *assign2_list, vector<string>&sequence, - double d0_scale, bool fast_opt) -{ - double total_score=0; - int i,j; - int xlen=0; - int ylen=0; - for (i=0;i<chain1_num;i++) - { - if (assign1_list[i]<0) continue; - xlen+=xlen_vec[i]; - ylen+=ylen_vec[assign1_list[i]]; - } - if (xlen<=3 || ylen<=3) return total_score; - - seqx = new char[xlen+1]; - secx = new char[xlen+1]; - NewArray(&xa, xlen, 3); - seqy = new char[ylen+1]; - secy = new char[ylen+1]; - NewArray(&ya, ylen, 3); - - int mol_type=copy_chain_pair_data(xa_vec, ya_vec, seqx_vec, seqy_vec, - secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, - xa, ya, seqx, seqy, secx, secy, chain1_num, chain2_num, - seqxA_mat, seqyA_mat, assign1_list, assign2_list, sequence); - - /* declare variable specific to this pair of TMalign */ - double t0[3], u0[3][3]; - double TM1, TM2; - double TM3, TM4, TM5; // for a_opt, u_opt, d_opt - double d0_0, TM_0; - double d0A, d0B, d0u, d0a; - double d0_out=5.0; - string seqM, seqxA, seqyA;// for output alignment - double rmsd0 = 0.0; - int L_ali; // Aligned length in standard_TMscore - double Liden=0; - double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore - int n_ali=0; - int n_ali8=0; - - double Lnorm_ass=len_aa+len_na; - - /* entry function for structure alignment */ - TMalign_main(xa, ya, seqx, seqy, secx, secy, - t0, u0, TM1, TM2, TM3, TM4, TM5, - d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, - rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, - xlen, ylen, sequence, Lnorm_ass, d0_scale, - 3, false, true, false, fast_opt, mol_type, -1); - - /* clean up */ - delete [] seqx; - delete [] seqy; - delete [] secx; - delete [] secy; - DeleteArray(&xa,xlen); - DeleteArray(&ya,ylen); - - /* re-compute chain level alignment */ - for (i=0;i<chain1_num;i++) - { - xlen=xlen_vec[i]; - if (xlen<3) - { - for (j=0;j<chain2_num;j++) - TM1_mat[i][j]=TM2_mat[i][j]=TMave_mat[i][j]=-1; - continue; - } - seqx = new char[xlen+1]; - secx = new char[xlen+1]; - NewArray(&xa, xlen, 3); - copy_chain_data(xa_vec[i],seqx_vec[i],secx_vec[i], - xlen,xa,seqx,secx); - - double **xt; - NewArray(&xt, xlen, 3); - do_rotation(xa, xt, xlen, t0, u0); - - for (j=0;j<chain2_num;j++) - { - if (mol_vec1[i]*mol_vec2[j]<0) //no protein-RNA alignment - { - TM1_mat[i][j]=TM2_mat[i][j]=TMave_mat[i][j]=-1; - continue; - } - - ylen=ylen_vec[j]; - if (ylen<3) - { - TM1_mat[i][j]=TM2_mat[i][j]=TMave_mat[i][j]=-1; - continue; - } - seqy = new char[ylen+1]; - secy = new char[ylen+1]; - NewArray(&ya, ylen, 3); - copy_chain_data(ya_vec[j],seqy_vec[j],secy_vec[j], - ylen,ya,seqy,secy); - - /* declare variable specific to this pair of TMalign */ - d0_out=5.0; - seqM.clear(); - seqxA.clear(); - seqyA.clear(); - rmsd0 = 0.0; - Liden=0; - int *invmap = new int[ylen+1]; - - double Lnorm_ass=len_aa; - if (mol_vec1[i]+mol_vec2[j]>0) Lnorm_ass=len_na; - - /* entry function for structure alignment */ - se_main(xt, ya, seqx, seqy, TM1, TM2, TM3, TM4, TM5, - d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, - rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, - xlen, ylen, sequence, Lnorm_ass, d0_scale, - 0, false, true, false, - mol_vec1[i]+mol_vec2[j], 1, invmap); - - /* print result */ - TM1_mat[i][j]=TM2; // normalized by chain1 - TM2_mat[i][j]=TM1; // normalized by chain2 - seqxA_mat[i][j]=seqxA; - seqyA_mat[i][j]=seqyA; - - TMave_mat[i][j]=TM4*Lnorm_ass; - - /* clean up */ - seqM.clear(); - seqxA.clear(); - seqyA.clear(); - - delete[]seqy; - delete[]secy; - DeleteArray(&ya,ylen); - } - delete[]seqx; - delete[]secx; - DeleteArray(&xa,xlen); - DeleteArray(&xt,xlen); - } - return total_score; -} - -void MMalign_final( - const string xname, const string yname, - const vector<string> chainID_list1, const vector<string> chainID_list2, - string fname_super, string fname_lign, string fname_matrix, - const vector<vector<vector<double> > >&xa_vec, - const vector<vector<vector<double> > >&ya_vec, - const vector<vector<char> >&seqx_vec, const vector<vector<char> >&seqy_vec, - const vector<vector<char> >&secx_vec, const vector<vector<char> >&secy_vec, - const vector<int> &mol_vec1, const vector<int> &mol_vec2, - const vector<int> &xlen_vec, const vector<int> &ylen_vec, - double **xa, double **ya, char *seqx, char *seqy, char *secx, char *secy, - int len_aa, int len_na, int chain1_num, int chain2_num, - double **TM1_mat, double **TM2_mat, double **TMave_mat, - vector<vector<string> >&seqxA_mat, vector<vector<string> >&seqM_mat, - vector<vector<string> >&seqyA_mat, int *assign1_list, int *assign2_list, - vector<string>&sequence, const double d0_scale, const bool m_opt, - const int o_opt, const int outfmt_opt, const int ter_opt, - const int split_opt, const bool a_opt, const bool d_opt, - const bool fast_opt, const bool full_opt, const int mirror_opt, - const vector<string>&resi_vec1, const vector<string>&resi_vec2) -{ - int i,j; - int xlen=0; - int ylen=0; - for (i=0;i<chain1_num;i++) xlen+=xlen_vec[i]; - for (j=0;j<chain2_num;j++) ylen+=ylen_vec[j]; - if (xlen<=3 || ylen<=3) return; - - seqx = new char[xlen+1]; - secx = new char[xlen+1]; - NewArray(&xa, xlen, 3); - seqy = new char[ylen+1]; - secy = new char[ylen+1]; - NewArray(&ya, ylen, 3); - - int mol_type=copy_chain_pair_data(xa_vec, ya_vec, seqx_vec, seqy_vec, - secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, - xa, ya, seqx, seqy, secx, secy, chain1_num, chain2_num, - seqxA_mat, seqyA_mat, assign1_list, assign2_list, sequence); - - /* declare variable specific to this pair of TMalign */ - double t0[3], u0[3][3]; - double TM1, TM2; - double TM3, TM4, TM5; // for a_opt, u_opt, d_opt - double d0_0, TM_0; - double d0A, d0B, d0u, d0a; - double d0_out=5.0; - string seqM, seqxA, seqyA;// for output alignment - double rmsd0 = 0.0; - int L_ali; // Aligned length in standard_TMscore - double Liden=0; - double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore - int n_ali=0; - int n_ali8=0; - - double Lnorm_ass=len_aa+len_na; - - /* entry function for structure alignment */ - TMalign_main(xa, ya, seqx, seqy, secx, secy, - t0, u0, TM1, TM2, TM3, TM4, TM5, - d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, - rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, - xlen, ylen, sequence, Lnorm_ass, d0_scale, - 3, a_opt, false, d_opt, fast_opt, mol_type, -1); - - /* prepare full complex alignment */ - string chainID1=""; - string chainID2=""; - sequence.clear(); - sequence.push_back(""); // seqxA - sequence.push_back(""); // seqyA - sequence.push_back(""); // seqM - int aln_start=0; - int aln_end=0; - for (i=0;i<chain1_num;i++) - { - j=assign1_list[i]; - if (j<0) continue; - chainID1+=chainID_list1[i]; - chainID2+=chainID_list2[j]; - sequence[0]+=seqxA_mat[i][j]+'*'; - sequence[1]+=seqyA_mat[i][j]+'*'; - - aln_end+=seqxA_mat[i][j].size(); - seqM_mat[i][j]=seqM.substr(aln_start,aln_end-aln_start); - sequence[2]+=seqM_mat[i][j]+'*'; - aln_start=aln_end; - } - - /* prepare unaligned region */ - for (i=0;i<chain1_num;i++) - { - if (assign1_list[i]>=0) continue; - chainID1+=chainID_list1[i]; - chainID2+=':'; - string s(seqx_vec[i].begin(),seqx_vec[i].end()); - sequence[0]+=s.substr(0,xlen_vec[i])+'*'; - sequence[1]+=string(xlen_vec[i],'-')+'*'; - s.clear(); - sequence[2]+=string(xlen_vec[i],' ')+'*'; - } - for (j=0;j<chain2_num;j++) - { - if (assign2_list[j]>=0) continue; - chainID1+=':'; - chainID2+=chainID_list2[j]; - string s(seqy_vec[j].begin(),seqy_vec[j].end()); - sequence[0]+=string(ylen_vec[j],'-')+'*'; - sequence[1]+=s.substr(0,ylen_vec[j])+'*'; - s.clear(); - sequence[2]+=string(ylen_vec[j],' ')+'*'; - } - - /* print alignment */ - output_results(xname, yname, chainID1.c_str(), chainID2.c_str(), - xlen, ylen, t0, u0, TM1, TM2, TM3, TM4, TM5, rmsd0, d0_out, - sequence[2].c_str(), sequence[0].c_str(), sequence[1].c_str(), - Liden, n_ali8, L_ali, TM_ali, rmsd_ali, - TM_0, d0_0, d0A, d0B, 0, d0_scale, d0a, d0u, - (m_opt?fname_matrix:"").c_str(), outfmt_opt, ter_opt, true, - split_opt, o_opt, fname_super, - false, a_opt, false, d_opt, mirror_opt, resi_vec1, resi_vec2); - - /* clean up */ - seqM.clear(); - seqxA.clear(); - seqyA.clear(); - delete [] seqx; - delete [] seqy; - delete [] secx; - delete [] secy; - DeleteArray(&xa,xlen); - DeleteArray(&ya,ylen); - sequence[0].clear(); - sequence[1].clear(); - sequence[2].clear(); - - if (!full_opt) return; - - cout<<"# End of alignment for full complex. The following blocks list alignments for individual chains."<<endl; - - /* re-compute chain level alignment */ - for (i=0;i<chain1_num;i++) - { - j=assign1_list[i]; - if (j<0) continue; - xlen=xlen_vec[i]; - seqx = new char[xlen+1]; - secx = new char[xlen+1]; - NewArray(&xa, xlen, 3); - copy_chain_data(xa_vec[i],seqx_vec[i],secx_vec[i], - xlen,xa,seqx,secx); - - double **xt; - NewArray(&xt, xlen, 3); - do_rotation(xa, xt, xlen, t0, u0); - - ylen=ylen_vec[j]; - if (ylen<3) - { - TM1_mat[i][j]=TM2_mat[i][j]=TMave_mat[i][j]=-1; - continue; - } - seqy = new char[ylen+1]; - secy = new char[ylen+1]; - NewArray(&ya, ylen, 3); - copy_chain_data(ya_vec[j],seqy_vec[j],secy_vec[j], - ylen,ya,seqy,secy); - - /* declare variable specific to this pair of TMalign */ - d0_out=5.0; - rmsd0 = 0.0; - Liden=0; - int *invmap = new int[ylen+1]; - seqM=""; - seqxA=""; - seqyA=""; - double Lnorm_ass=len_aa; - if (mol_vec1[i]+mol_vec2[j]>0) Lnorm_ass=len_na; - sequence[0]=seqxA_mat[i][j]; - sequence[1]=seqyA_mat[i][j]; - - /* entry function for structure alignment */ - se_main(xt, ya, seqx, seqy, TM1, TM2, TM3, TM4, TM5, - d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, - rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, - xlen, ylen, sequence, Lnorm_ass, d0_scale, - 1, a_opt, true, d_opt, mol_vec1[i]+mol_vec2[j], 1, invmap); - - //TM2=TM4*Lnorm_ass/xlen; - //TM1=TM4*Lnorm_ass/ylen; - //d0A=d0u; - //d0B=d0u; - - /* print result */ - output_results(xname, yname, - chainID_list1[i].c_str(), chainID_list2[j].c_str(), - xlen, ylen, t0, u0, TM1, TM2, TM3, TM4, TM5, rmsd0, d0_out, - seqM_mat[i][j].c_str(), seqxA_mat[i][j].c_str(), - seqyA_mat[i][j].c_str(), Liden, n_ali8, L_ali, TM_ali, rmsd_ali, - TM_0, d0_0, d0A, d0B, Lnorm_ass, d0_scale, d0a, d0u, - "", outfmt_opt, ter_opt, false, split_opt, 0, - "", false, a_opt, false, d_opt, 0, resi_vec1, resi_vec2); - - /* clean up */ - seqxA.clear(); - seqM.clear(); - seqyA.clear(); - sequence[0].clear(); - sequence[1].clear(); - delete[]seqy; - delete[]secy; - DeleteArray(&ya,ylen); - delete[]seqx; - delete[]secx; - DeleteArray(&xa,xlen); - DeleteArray(&xt,xlen); - } - sequence.clear(); - return; -} diff --git a/modules/bindings/src/tmalign/OST_INFO b/modules/bindings/src/tmalign/OST_INFO deleted file mode 100644 index 16ce115699e3266a2a8d6dd7eecfafc01758aa6e..0000000000000000000000000000000000000000 --- a/modules/bindings/src/tmalign/OST_INFO +++ /dev/null @@ -1,7 +0,0 @@ -Source code has been cloned August 2 2022 from: - -https://github.com/kad-ecoli/TMalign - -last commit: -f0824499d8ab4fa84b2e75d253de80ab2c894c56 - diff --git a/modules/bindings/src/wrap_tmalign.cc b/modules/bindings/src/wrap_tmalign.cc index cefbe1a4497372983902f5f5f143a5c21ed1e1eb..49da68d00061044d2cb4832f09b5907ce1121be4 100644 --- a/modules/bindings/src/wrap_tmalign.cc +++ b/modules/bindings/src/wrap_tmalign.cc @@ -17,7 +17,8 @@ // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA //------------------------------------------------------------------------------ -#include "tmalign/TMalign.h" // include for the external TMalign +#include "USalign/TMalign.h" // include for the external TMalign +#include "USalign/MMalign.h" #include <ost/mol/atom_view.hh> #include <ost/message.hh> @@ -25,11 +26,294 @@ namespace ost{ namespace bindings{ + +// hacked version of MMalign_final which does not print anything to stdout +// but returns an MMAlignResult object + +MMAlignResult MMalign_final_hacked( + const string xname, const string yname, + const vector<string> chainID_list1, const vector<string> chainID_list2, + string fname_super, string fname_lign, string fname_matrix, + const vector<vector<vector<double> > >&xa_vec, + const vector<vector<vector<double> > >&ya_vec, + const vector<vector<char> >&seqx_vec, const vector<vector<char> >&seqy_vec, + const vector<vector<char> >&secx_vec, const vector<vector<char> >&secy_vec, + const vector<int> &mol_vec1, const vector<int> &mol_vec2, + const vector<int> &xlen_vec, const vector<int> &ylen_vec, + double **xa, double **ya, char *seqx, char *seqy, char *secx, char *secy, + int len_aa, int len_na, int chain1_num, int chain2_num, + double **TMave_mat, + vector<vector<string> >&seqxA_mat, vector<vector<string> >&seqM_mat, + vector<vector<string> >&seqyA_mat, int *assign1_list, int *assign2_list, + vector<string>&sequence, const double d0_scale, const bool m_opt, + const int o_opt, const int outfmt_opt, const int ter_opt, + const int split_opt, const bool a_opt, const bool d_opt, + const bool fast_opt, const bool full_opt, const int mirror_opt, + const vector<string>&resi_vec1, const vector<string>&resi_vec2) +{ + int i,j; + int xlen=0; + int ylen=0; + for (i=0;i<chain1_num;i++) xlen+=xlen_vec[i]; + for (j=0;j<chain2_num;j++) ylen+=ylen_vec[j]; + //if (xlen<=3 || ylen<=3) return; + if (xlen<=3 || ylen<=3) return MMAlignResult(); + + seqx = new char[xlen+1]; + secx = new char[xlen+1]; + NewArray(&xa, xlen, 3); + seqy = new char[ylen+1]; + secy = new char[ylen+1]; + NewArray(&ya, ylen, 3); + + int mol_type=copy_chain_pair_data(xa_vec, ya_vec, seqx_vec, seqy_vec, + secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, + xa, ya, seqx, seqy, secx, secy, chain1_num, chain2_num, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, sequence); + + /* declare variable specific to this pair of TMalign */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + + double Lnorm_ass=len_aa+len_na; + + /* entry function for structure alignment */ + TMalign_main(xa, ya, seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_ass, d0_scale, + 3, a_opt, false, d_opt, fast_opt, mol_type, -1); + + // this whole stuff is not needed in the hacked version + + /* prepare full complex alignment */ + //string chainID1=""; + //string chainID2=""; + //sequence.clear(); + //sequence.push_back(""); // seqxA + //sequence.push_back(""); // seqyA + //sequence.push_back(""); // seqM + //int aln_start=0; + //int aln_end=0; + //for (i=0;i<chain1_num;i++) + //{ + // j=assign1_list[i]; + // if (j<0) continue; + // chainID1+=chainID_list1[i]; + // chainID2+=chainID_list2[j]; + // sequence[0]+=seqxA_mat[i][j]+'*'; + // sequence[1]+=seqyA_mat[i][j]+'*'; + + // aln_end+=seqxA_mat[i][j].size(); + // seqM_mat[i][j]=seqM.substr(aln_start,aln_end-aln_start); + // sequence[2]+=seqM_mat[i][j]+'*'; + // aln_start=aln_end; + //} + + /* prepare unaligned region */ + //for (i=0;i<chain1_num;i++) + //{ + // if (assign1_list[i]>=0) continue; + // chainID1+=chainID_list1[i]; + // chainID2+=':'; + // string s(seqx_vec[i].begin(),seqx_vec[i].end()); + // sequence[0]+=s.substr(0,xlen_vec[i])+'*'; + // sequence[1]+=string(xlen_vec[i],'-')+'*'; + // s.clear(); + // sequence[2]+=string(xlen_vec[i],' ')+'*'; + //} + //for (j=0;j<chain2_num;j++) + //{ + // if (assign2_list[j]>=0) continue; + // chainID1+=':'; + // chainID2+=chainID_list2[j]; + // string s(seqy_vec[j].begin(),seqy_vec[j].end()); + // sequence[0]+=string(ylen_vec[j],'-')+'*'; + // sequence[1]+=s.substr(0,ylen_vec[j])+'*'; + // s.clear(); + // sequence[2]+=string(ylen_vec[j],' ')+'*'; + //} + + /* print alignment */ + //output_results(xname, yname, chainID1.c_str(), chainID2.c_str(), + // xlen, ylen, t0, u0, TM1, TM2, TM3, TM4, TM5, rmsd0, d0_out, + // sequence[2].c_str(), sequence[0].c_str(), sequence[1].c_str(), + // Liden, n_ali8, L_ali, TM_ali, rmsd_ali, + // TM_0, d0_0, d0A, d0B, 0, d0_scale, d0a, d0u, + // (m_opt?fname_matrix:"").c_str(), outfmt_opt, ter_opt, true, + // split_opt, o_opt, fname_super, + // false, a_opt, false, d_opt, mirror_opt, resi_vec1, resi_vec2); + + + MMAlignResult res; + res.rmsd = rmsd0; + res.tm_score = TM1; + res.tm_score_swapped = TM2; + res.aligned_length = n_ali8; + res.transform = geom::Mat4(u0[0][0], u0[0][1], u0[0][2], t0[0], + u0[1][0], u0[1][1], u0[1][2], t0[1], + u0[2][0], u0[2][1], u0[2][2], t0[2], + 0.0, 0.0, 0.0, 1.0); + + for (i=0;i<chain1_num;i++) + { + j=assign1_list[i]; + if (j<0) continue; + res.ent1_mapped_chains.push_back(chainID_list1[i]); + res.ent2_mapped_chains.push_back(chainID_list2[j]); + ost::seq::AlignmentHandle aln = ost::seq::CreateAlignment(); + aln.AddSequence(ost::seq::CreateSequence(chainID_list1[i], + seqxA_mat[i][j])); + aln.AddSequence(ost::seq::CreateSequence(chainID_list2[j], + seqyA_mat[i][j])); + res.alignments.push_back(aln); + } + + /* clean up */ + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + delete [] seqx; + delete [] seqy; + delete [] secx; + delete [] secy; + DeleteArray(&xa,xlen); + DeleteArray(&ya,ylen); + // sequence gets never filled in hacked version + //sequence[0].clear(); + //sequence[1].clear(); + //sequence[2].clear(); + + return res; + + // deleted remainder that is only active if full_opt is enabled in original + // code + +} + +void parse_chain_list_hacked(const std::vector<geom::Vec3List>& pos, + const ost::seq::SequenceList& seq, + const std::vector<bool>& rna, + const std::vector<string>&chain_list, + std::vector<std::vector<std::vector<double> > >&a_vec, + std::vector<std::vector<char> >&seq_vec, + std::vector<std::vector<char> >&sec_vec, + std::vector<int>&mol_vec, vector<int>&len_vec, + std::vector<std::string>&chainID_list, + const int ter_opt, const int split_opt, + const string mol_opt, const int infmt_opt, + const string atom_opt, + const int mirror_opt, const int het_opt, + int &len_aa, int &len_na, + const int o_opt, std::vector<std::string>&resi_vec) { + + // variables used for injection + // - pos => position data for each chain + // - seq => sequence data for each chain, sequence name is chain name + // - rna => whether were dealing with amini acid or nucleotide chains + + // variables that need assignment: + // - a_vec => position data with 3 dimensions. 1: chain, 2: residue, 3:xyz + // - seq_vec => sequence data + // - sec_vec => secondary structure data + // - mol_vec => one entry per chain, 0 if peptide chain, > 0 if nucleotides + // - len_vec => length of chains + // - chainID_list => chain names + // - len_aa => total number of peptide residues + // - len_na => total number of nucleotide residues + // - resi_vec => leave empty... this is only used if byresi_opt is True or + // for generating output which is disabled anyways. + + // all other variables are simply ignored but stay here to keep the interface + // as similar as possible + + int n_chains = pos.size(); + a_vec.resize(n_chains); + seq_vec.resize(n_chains); + sec_vec.resize(n_chains); + mol_vec.resize(n_chains); + len_vec.resize(n_chains); + chainID_list.resize(n_chains); + len_aa = 0; + len_na = 0; + for(int i = 0; i < n_chains; ++i) { + int n_residues = pos[i].size(); + + // assign a_vec + a_vec[i].resize(n_residues, std::vector<double>(3)); + for(int j = 0; j < n_residues; ++j) { + a_vec[i][j][0] = pos[i][j][0]; + a_vec[i][j][1] = pos[i][j][1]; + a_vec[i][j][2] = pos[i][j][2]; + } + + // assign seq_vec + const std::string& s = seq[i].GetString(); + seq_vec[i].assign(s.begin(), s.end()); + + // assign sec_vec + double **xa; + NewArray(&xa, n_residues, 3); + // yet another conversion needed... + for(int j = 0; j < n_residues; ++j) { + xa[j][0] = a_vec[i][j][0]; + xa[j][1] = a_vec[i][j][1]; + xa[j][2] = a_vec[i][j][2]; + } + char* sec = new char[n_residues + 1]; + if(rna[i]) { + // make a const cast here... USalign doesn't do anything to that value + // if it does in the future, thats a recipe for disaster + make_sec(const_cast<char*>(s.c_str()), xa, n_residues, sec, " C3'"); + } else { + make_sec(xa, n_residues, sec); + } + sec_vec[i].assign(sec, sec + n_residues); + DeleteArray(&xa, n_residues); + delete [] sec; + + // assign mol_vec + if(rna[i]) { + mol_vec[i] = n_residues; + } else { + mol_vec[i] = (-1)*n_residues; + } + + // assign len_vec + len_vec[i] = n_residues; + + // assign chainID_list + // chainID_list[i] = ":1," + seq[i].GetName(); + chainID_list[i] = seq[i].GetName(); + + // update length variables + if(rna[i]) { + len_na += n_residues; + } else { + len_aa += n_residues; + } + } +} + + TMAlignResult WrappedTMAlign(const geom::Vec3List& pos_one, const geom::Vec3List& pos_two, const ost::seq::SequenceHandle& seq1, const ost::seq::SequenceHandle& seq2, - bool fast) { + bool fast, + bool rna) { int xlen = pos_one.size(); int ylen = pos_two.size(); @@ -46,10 +330,8 @@ TMAlignResult WrappedTMAlign(const geom::Vec3List& pos_one, // squeeze input into right format char* seqx = new char[xlen+1]; char* seqy = new char[ylen+1]; - seqx[xlen] = '\0'; - seqy[ylen] = '\0'; - char* secx = new char[xlen]; - char* secy = new char[ylen]; + char* secx = new char[xlen+1]; + char* secy = new char[ylen+1]; // use TMalign functionality to generate position arrays double** xa; @@ -71,8 +353,13 @@ TMAlignResult WrappedTMAlign(const geom::Vec3List& pos_one, seqy[i] = seq2[i]; } - make_sec(xa, xlen, secx); - make_sec(ya, ylen, secy); + if(rna) { + make_sec(seqx, xa, xlen, secx, " C3'"); + make_sec(seqy, ya, ylen, secy, " C3'"); + } else { + make_sec(xa, xlen, secx); + make_sec(ya, ylen, secy); + } // these variables are chosen such that running TMalign_main is the same as // you would call the executable without any additional parameters @@ -99,12 +386,13 @@ TMAlignResult WrappedTMAlign(const geom::Vec3List& pos_one, double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore int n_ali=0; int n_ali8=0; + int mol_type=static_cast<int>(rna); // Treated as RNA if mol_type > 0 TMalign_main(xa, ya, seqx, seqy, secx, secy, t0, u0, TM1, TM2, TM3, TM4, TM5, d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, seqM, seqxA, seqyA, rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, xlen, ylen, sequence, Lnorm_ass, d0_scale, i_opt, a_opt, u_opt, d_opt, - fast, 0, TMcut); + fast, mol_type, TMcut); // cleanup DeleteArray(&xa, xlen); @@ -117,6 +405,7 @@ TMAlignResult WrappedTMAlign(const geom::Vec3List& pos_one, // collect results and return TMAlignResult res; res.tm_score = TM1; + res.tm_score_swapped = TM2; res.rmsd = rmsd0; res.aligned_length = n_ali8; res.transform = geom::Mat4(u0[0][0], u0[0][1], u0[0][2], t0[0], @@ -124,67 +413,696 @@ TMAlignResult WrappedTMAlign(const geom::Vec3List& pos_one, u0[2][0], u0[2][1], u0[2][2], t0[2], 0.0, 0.0, 0.0, 1.0); res.alignment = ost::seq::CreateAlignment(); - ost::seq::SequenceHandle aligned_seq1 = ost::seq::CreateSequence("seq1", seqxA); - ost::seq::SequenceHandle aligned_seq2 = ost::seq::CreateSequence("seq2", seqyA); + ost::seq::SequenceHandle aligned_seq1 = + ost::seq::CreateSequence(seq1.GetName(), seqxA); + ost::seq::SequenceHandle aligned_seq2 = + ost::seq::CreateSequence(seq2.GetName(), seqyA); res.alignment.AddSequence(aligned_seq1); res.alignment.AddSequence(aligned_seq2); return res; } - -TMAlignResult WrappedTMAlign(const ost::mol::ChainView& chain1, - const ost::mol::ChainView& chain2, +MMAlignResult WrappedMMAlign(const std::vector<geom::Vec3List>& pos_one, + const std::vector<geom::Vec3List>& pos_two, + const ost::seq::SequenceList& seq1, + const ost::seq::SequenceList& seq2, + const std::vector<bool>& rna1, + const std::vector<bool>& rna2, bool fast) { - geom::Vec3List pos1; - geom::Vec3List pos2; - std::vector<char> s1; - std::vector<char> s2; + // input checks + if(pos_one.empty() || pos_two.empty()) { + throw ost::Error("Cannot compute MMAlign on empty chains!"); + } - ost::mol::ResidueViewList res_list_1 = chain1.GetResidueList(); - ost::mol::ResidueViewList res_list_2 = chain2.GetResidueList(); + if(static_cast<int>(pos_one.size()) != seq1.GetCount() || + pos_one.size() != rna1.size()) { + throw ost::Error("Inconsistent input sizes in WrappedMMAlign"); + } - for(ost::mol::ResidueViewList::iterator it = res_list_1.begin(); - it != res_list_1.end(); ++it) { - if(!it->IsPeptideLinking()) { - continue; + if(static_cast<int>(pos_two.size()) != seq2.GetCount() || + pos_two.size() != rna2.size()) { + throw ost::Error("Inconsistent input sizes in WrappedMMAlign"); + } + + if(pos_one.size() == 1 && pos_two.size() == 1) { + // just run TMAlign... + if(rna1[0] != rna2[0]) { + throw ost::Error("Error in WrappedMMAlign: If both complexes only have " + "one chain, they must either be both peptide or both " + "RNA."); } - ost::mol::AtomView ca = it->FindAtom("CA"); - if(!ca.IsValid()) { - continue; + TMAlignResult tm_result = WrappedTMAlign(pos_one[0], pos_two[0], seq1[0], + seq2[0], fast, rna1[0]); + ost::seq::AlignmentList alns; + alns.push_back(tm_result.alignment); + std::vector<String> ent1_mapped_chains; + std::vector<String> ent2_mapped_chains; + ent1_mapped_chains.push_back(seq1[0].GetName()); + ent2_mapped_chains.push_back(seq2[0].GetName()); + return MMAlignResult(tm_result.rmsd, tm_result.tm_score, + tm_result.tm_score_swapped, + tm_result.aligned_length, + tm_result.transform, + alns, ent1_mapped_chains, + ent2_mapped_chains); + } + + // the following is a copy of the variable definition section in USalign.cpp + // main function to get default param + // changes to the defaults there are explicitely marked + // most with: silence compiler warning due to unused variables + std::string xname = ""; + std::string yname = ""; + std::string fname_super = ""; // file name for superposed structure + std::string fname_lign = ""; // file name for user alignment + std::string fname_matrix= ""; // file name for output matrix + vector<std::string> sequence; // get value from alignment file + //double Lnorm_ass, d0_scale; // silence compiler warning + double d0_scale = 0.0; // only d0 scale required, directly initialize with + // default value to silence compiler warning + + // silence compiler warning + //bool h_opt = false; // print full help message + // silence compiler warning + //bool v_opt = false; // print version + bool m_opt = false; // flag for -m, output rotation matrix + int i_opt = 0; // 1 for -i, 3 for -I + int o_opt = 0; // 1 for -o, 2 for -rasmol + int a_opt = 0; // flag for -a, do not normalized by average length + // silence compiler warning + //bool u_opt = false; // flag for -u, normalized by user specified length + bool d_opt = false; // flag for -d, user specified d0 + + bool full_opt = false;// do not show chain level alignment + double TMcut =-1; + // silence compiler warning + //bool se_opt =false; + int infmt1_opt=-1; // PDB or PDBx/mmCIF format for chain_1 + int infmt2_opt=-1; // PDB or PDBx/mmCIF format for chain_2 + int ter_opt =2; // END, or different chainID + int split_opt =2; // split each chains + int outfmt_opt=0; // set -outfmt to full output + bool fast_opt =false; // flags for -fast, fTM-align algorithm + // silence compiler warning + //int cp_opt =0; // do not check circular permutation + // silence compiler warning + //int closeK_opt=-1; // number of atoms for SOI initial alignment. + // 5 and 0 for -mm 5 and 6 + // silence compiler warning + // int hinge_opt =9; // maximum number of hinge allowed for flexible + int mirror_opt=0; // do not align mirror + int het_opt=0; // do not read HETATM residues + // silence compiler warning + // int mm_opt=0; // do not perform MM-align + std::string atom_opt ="auto";// use C alpha atom for protein and C3' for RNA + std::string mol_opt ="auto";// auto-detect the molecule type as protein/RNA + std::string suffix_opt=""; // set -suffix to empty + std::string dir_opt =""; // set -dir to empty + std::string dir1_opt =""; // set -dir1 to empty + std::string dir2_opt =""; // set -dir2 to empty + int byresi_opt=0; // set -byresi to 0 + vector<std::string> chain1_list; // only when -dir1 is set + vector<std::string> chain2_list; // only when -dir2 is set + + + // The following is pretty much a copy of the MMalign function in USalign.cpp + // with adaptions to inject our own data structures + + /* declare previously global variables */ + vector<vector<vector<double> > > xa_vec; // structure of complex1 + vector<vector<vector<double> > > ya_vec; // structure of complex2 + vector<vector<char> >seqx_vec; // sequence of complex1 + vector<vector<char> >seqy_vec; // sequence of complex2 + vector<vector<char> >secx_vec; // secondary structure of complex1 + vector<vector<char> >secy_vec; // secondary structure of complex2 + vector<int> mol_vec1; // molecule type of complex1, RNA if >0 + vector<int> mol_vec2; // molecule type of complex2, RNA if >0 + vector<string> chainID_list1; // list of chainID1 + vector<string> chainID_list2; // list of chainID2 + vector<int> xlen_vec; // length of complex1 + vector<int> ylen_vec; // length of complex2 + int i,j; // chain index + int xlen, ylen; // chain length + double **xa, **ya; // structure of single chain + char *seqx, *seqy; // for the protein sequence + char *secx, *secy; // for the secondary structure + int xlen_aa,ylen_aa; // total length of protein + int xlen_na,ylen_na; // total length of RNA/DNA + vector<string> resi_vec1; // residue index for chain1 + vector<string> resi_vec2; // residue index for chain2 + + // hardcode some variable values + ter_opt = 0; + + + // COMMENT OUT MMALIGN STRUCTURE PARSING WHICH IS FILE BASED + //////////////////////////////////////////////////////////// + + ///* parse complex */ + //parse_chain_list(chain1_list, xa_vec, seqx_vec, secx_vec, mol_vec1, + // xlen_vec, chainID_list1, ter_opt, split_opt, mol_opt, infmt1_opt, + // atom_opt, mirror_opt, het_opt, xlen_aa, xlen_na, o_opt, resi_vec1); + //if (xa_vec.size()==0) PrintErrorAndQuit("ERROR! 0 chain in complex 1"); + //parse_chain_list(chain2_list, ya_vec, seqy_vec, secy_vec, mol_vec2, + // ylen_vec, chainID_list2, ter_opt, split_opt, mol_opt, infmt2_opt, + // atom_opt, 0, het_opt, ylen_aa, ylen_na, o_opt, resi_vec2); + //if (ya_vec.size()==0) PrintErrorAndQuit("ERROR! 0 chain in complex 2"); + + // INJECT OWN DATA + ////////////////// + parse_chain_list_hacked(pos_one, seq1, rna1, + chain1_list, xa_vec, seqx_vec, secx_vec, mol_vec1, + xlen_vec, chainID_list1, ter_opt, split_opt, mol_opt, infmt1_opt, + atom_opt, mirror_opt, het_opt, xlen_aa, xlen_na, o_opt, resi_vec1); + parse_chain_list_hacked(pos_two, seq2, rna2, + chain2_list, ya_vec, seqy_vec, secy_vec, mol_vec2, + ylen_vec, chainID_list2, ter_opt, split_opt, mol_opt, infmt2_opt, + atom_opt, 0, het_opt, ylen_aa, ylen_na, o_opt, resi_vec2); + + int len_aa=getmin(xlen_aa,ylen_aa); + int len_na=getmin(xlen_na,ylen_na); + if (a_opt) + { + len_aa=(xlen_aa+ylen_aa)/2; + len_na=(xlen_na+ylen_na)/2; + } + if (byresi_opt) i_opt=3; + + // this is already handled above + //////////////////////////////// + + ///* perform monomer alignment if there is only one chain */ + //if (xa_vec.size()==1 && ya_vec.size()==1) + //{ + // xlen = xlen_vec[0]; + // ylen = ylen_vec[0]; + // seqx = new char[xlen+1]; + // seqy = new char[ylen+1]; + // secx = new char[xlen+1]; + // secy = new char[ylen+1]; + // NewArray(&xa, xlen, 3); + // NewArray(&ya, ylen, 3); + // copy_chain_data(xa_vec[0],seqx_vec[0],secx_vec[0], xlen,xa,seqx,secx); + // copy_chain_data(ya_vec[0],seqy_vec[0],secy_vec[0], ylen,ya,seqy,secy); + // + // /* declare variable specific to this pair of TMalign */ + // double t0[3], u0[3][3]; + // double TM1, TM2; + // double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + // double d0_0, TM_0; + // double d0A, d0B, d0u, d0a; + // double d0_out=5.0; + // string seqM, seqxA, seqyA;// for output alignment + // double rmsd0 = 0.0; + // int L_ali; // Aligned length in standard_TMscore + // double Liden=0; + // double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + // int n_ali=0; + // int n_ali8=0; + // + // if (byresi_opt) extract_aln_from_resi(sequence, + // seqx,seqy,resi_vec1,resi_vec2,byresi_opt); + + // /* entry function for structure alignment */ + // TMalign_main(xa, ya, seqx, seqy, secx, secy, + // t0, u0, TM1, TM2, TM3, TM4, TM5, + // d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + // seqM, seqxA, seqyA, + // rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + // xlen, ylen, sequence, 0, d0_scale, + // i_opt, a_opt, false, d_opt, fast_opt, + // mol_vec1[0]+mol_vec2[0],TMcut); + + // /* print result */ + // output_results( + // xname.substr(dir1_opt.size()), + // yname.substr(dir2_opt.size()), + // chainID_list1[0], chainID_list2[0], + // xlen, ylen, t0, u0, TM1, TM2, TM3, TM4, TM5, rmsd0, d0_out, + // seqM.c_str(), seqxA.c_str(), seqyA.c_str(), Liden, + // n_ali8, L_ali, TM_ali, rmsd_ali, TM_0, d0_0, d0A, d0B, + // 0, d0_scale, d0a, d0u, (m_opt?fname_matrix:"").c_str(), + // outfmt_opt, ter_opt, true, split_opt, o_opt, fname_super, + // 0, a_opt, false, d_opt, mirror_opt, resi_vec1, resi_vec2); + + // /* clean up */ + // seqM.clear(); + // seqxA.clear(); + // seqyA.clear(); + // delete[]seqx; + // delete[]seqy; + // delete[]secx; + // delete[]secy; + // DeleteArray(&xa,xlen); + // DeleteArray(&ya,ylen); + + // vector<vector<vector<double> > >().swap(xa_vec); // structure of complex1 + // vector<vector<vector<double> > >().swap(ya_vec); // structure of complex2 + // vector<vector<char> >().swap(seqx_vec); // sequence of complex1 + // vector<vector<char> >().swap(seqy_vec); // sequence of complex2 + // vector<vector<char> >().swap(secx_vec); // secondary structure of complex1 + // vector<vector<char> >().swap(secy_vec); // secondary structure of complex2 + // mol_vec1.clear(); // molecule type of complex1, RNA if >0 + // mol_vec2.clear(); // molecule type of complex2, RNA if >0 + // chainID_list1.clear(); // list of chainID1 + // chainID_list2.clear(); // list of chainID2 + // xlen_vec.clear(); // length of complex1 + // ylen_vec.clear(); // length of complex2 + // return 0; + //} + + /* declare TM-score tables */ + int chain1_num=xa_vec.size(); + int chain2_num=ya_vec.size(); + vector<string> tmp_str_vec(chain2_num,""); + double **TMave_mat; + double **ut_mat; // rotation matrices for all-against-all alignment + int ui,uj,ut_idx; + NewArray(&TMave_mat,chain1_num,chain2_num); + NewArray(&ut_mat,chain1_num*chain2_num,4*3); + vector<vector<string> >seqxA_mat(chain1_num,tmp_str_vec); + vector<vector<string> > seqM_mat(chain1_num,tmp_str_vec); + vector<vector<string> >seqyA_mat(chain1_num,tmp_str_vec); + + double maxTMmono=-1; + int maxTMmono_i,maxTMmono_j; + + // assign default values to silence compiler warnings + maxTMmono_i = -1; + maxTMmono_j = -1; + + /* get all-against-all alignment */ + if (len_aa+len_na>500) fast_opt=true; + for (i=0;i<chain1_num;i++) + { + xlen=xlen_vec[i]; + if (xlen<3) + { + for (j=0;j<chain2_num;j++) TMave_mat[i][j]=-1; + continue; + } + seqx = new char[xlen+1]; + secx = new char[xlen+1]; + NewArray(&xa, xlen, 3); + copy_chain_data(xa_vec[i],seqx_vec[i],secx_vec[i], + xlen,xa,seqx,secx); + + for (j=0;j<chain2_num;j++) + { + ut_idx=i*chain2_num+j; + for (ui=0;ui<4;ui++) + for (uj=0;uj<3;uj++) ut_mat[ut_idx][ui*3+uj]=0; + ut_mat[ut_idx][0]=1; + ut_mat[ut_idx][4]=1; + ut_mat[ut_idx][8]=1; + + if (mol_vec1[i]*mol_vec2[j]<0) //no protein-RNA alignment + { + TMave_mat[i][j]=-1; + continue; + } + + ylen=ylen_vec[j]; + if (ylen<3) + { + TMave_mat[i][j]=-1; + continue; + } + seqy = new char[ylen+1]; + secy = new char[ylen+1]; + NewArray(&ya, ylen, 3); + copy_chain_data(ya_vec[j],seqy_vec[j],secy_vec[j], + ylen,ya,seqy,secy); + + /* declare variable specific to this pair of TMalign */ + double t0[3], u0[3][3]; + double TM1, TM2; + double TM3, TM4, TM5; // for a_opt, u_opt, d_opt + double d0_0, TM_0; + double d0A, d0B, d0u, d0a; + double d0_out=5.0; + string seqM, seqxA, seqyA;// for output alignment + double rmsd0 = 0.0; + int L_ali; // Aligned length in standard_TMscore + double Liden=0; + double TM_ali, rmsd_ali; // TMscore and rmsd in standard_TMscore + int n_ali=0; + int n_ali8=0; + + int Lnorm_tmp=len_aa; + if (mol_vec1[i]+mol_vec2[j]>0) Lnorm_tmp=len_na; + + if (byresi_opt) + { + int total_aln=extract_aln_from_resi(sequence, + seqx,seqy,resi_vec1,resi_vec2,xlen_vec,ylen_vec, i, j); + seqxA_mat[i][j]=sequence[0]; + seqyA_mat[i][j]=sequence[1]; + if (total_aln>xlen+ylen-3) + { + for (ui=0;ui<3;ui++) for (uj=0;uj<3;uj++) + ut_mat[ut_idx][ui*3+uj]=(ui==uj)?1:0; + for (uj=0;uj<3;uj++) ut_mat[ut_idx][9+uj]=0; + TMave_mat[i][j]=0; + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + + delete[]seqy; + delete[]secy; + DeleteArray(&ya,ylen); + continue; + } + } + + /* entry function for structure alignment */ + TMalign_main(xa, ya, seqx, seqy, secx, secy, + t0, u0, TM1, TM2, TM3, TM4, TM5, + d0_0, TM_0, d0A, d0B, d0u, d0a, d0_out, + seqM, seqxA, seqyA, + rmsd0, L_ali, Liden, TM_ali, rmsd_ali, n_ali, n_ali8, + xlen, ylen, sequence, Lnorm_tmp, d0_scale, + i_opt, false, true, false, fast_opt, + mol_vec1[i]+mol_vec2[j],TMcut); + + /* store result */ + for (ui=0;ui<3;ui++) + for (uj=0;uj<3;uj++) ut_mat[ut_idx][ui*3+uj]=u0[ui][uj]; + for (uj=0;uj<3;uj++) ut_mat[ut_idx][9+uj]=t0[uj]; + seqxA_mat[i][j]=seqxA; + seqyA_mat[i][j]=seqyA; + TMave_mat[i][j]=TM4*Lnorm_tmp; + if (TMave_mat[i][j]>maxTMmono) + { + maxTMmono=TMave_mat[i][j]; + maxTMmono_i=i; + maxTMmono_j=j; + } + + /* clean up */ + seqM.clear(); + seqxA.clear(); + seqyA.clear(); + + delete[]seqy; + delete[]secy; + DeleteArray(&ya,ylen); + } + + delete[]seqx; + delete[]secx; + DeleteArray(&xa,xlen); } - char olc = it->GetOneLetterCode(); - if(olc == '?') { - continue; + + /* calculate initial chain-chain assignment */ + int *assign1_list; // value is index of assigned chain2 + int *assign2_list; // value is index of assigned chain1 + assign1_list=new int[chain1_num]; + assign2_list=new int[chain2_num]; + double total_score=enhanced_greedy_search(TMave_mat, assign1_list, + assign2_list, chain1_num, chain2_num); + if (total_score<=0) PrintErrorAndQuit("ERROR! No assignable chain"); + + /* refine alignment for large oligomers */ + int aln_chain_num=count_assign_pair(assign1_list,chain1_num); + bool is_oligomer=(aln_chain_num>=3); + if (aln_chain_num==2) // dimer alignment + { + int na_chain_num1,na_chain_num2,aa_chain_num1,aa_chain_num2; + count_na_aa_chain_num(na_chain_num1,aa_chain_num1,mol_vec1); + count_na_aa_chain_num(na_chain_num2,aa_chain_num2,mol_vec2); + + /* align protein-RNA hybrid dimer to another hybrid dimer */ + if (na_chain_num1==1 && na_chain_num2==1 && + aa_chain_num1==1 && aa_chain_num2==1) is_oligomer=false; + /* align pure protein dimer or pure RNA dimer */ + else if ((getmin(na_chain_num1,na_chain_num2)==0 && + aa_chain_num1==2 && aa_chain_num2==2) || + (getmin(aa_chain_num1,aa_chain_num2)==0 && + na_chain_num1==2 && na_chain_num2==2)) + { + adjust_dimer_assignment(xa_vec,ya_vec,xlen_vec,ylen_vec,mol_vec1, + mol_vec2,assign1_list,assign2_list,seqxA_mat,seqyA_mat); + is_oligomer=false; // cannot refiner further + } + else is_oligomer=true; /* align oligomers to dimer */ } - pos1.push_back(ca.GetPos()); - s1.push_back(olc); - } - for(ost::mol::ResidueViewList::iterator it = res_list_2.begin(); - it != res_list_2.end(); ++it) { - if(!it->IsPeptideLinking()) { - continue; + if (aln_chain_num>=3 || is_oligomer) // oligomer alignment + { + /* extract centroid coordinates */ + double **xcentroids; + double **ycentroids; + NewArray(&xcentroids, chain1_num, 3); + NewArray(&ycentroids, chain2_num, 3); + double d0MM=getmin( + calculate_centroids(xa_vec, chain1_num, xcentroids), + calculate_centroids(ya_vec, chain2_num, ycentroids)); + + /* refine enhanced greedy search with centroid superposition */ + //double het_deg=check_heterooligomer(TMave_mat, chain1_num, chain2_num); + homo_refined_greedy_search(TMave_mat, assign1_list, + assign2_list, chain1_num, chain2_num, xcentroids, + ycentroids, d0MM, len_aa+len_na, ut_mat); + hetero_refined_greedy_search(TMave_mat, assign1_list, + assign2_list, chain1_num, chain2_num, xcentroids, + ycentroids, d0MM, len_aa+len_na); + + /* clean up */ + DeleteArray(&xcentroids, chain1_num); + DeleteArray(&ycentroids, chain2_num); } - ost::mol::AtomView ca = it->FindAtom("CA"); - if(!ca.IsValid()) { - continue; + + /* store initial assignment */ + int init_pair_num=count_assign_pair(assign1_list,chain1_num); + int *assign1_init, *assign2_init; + assign1_init=new int[chain1_num]; + assign2_init=new int[chain2_num]; + double **TMave_init; + NewArray(&TMave_init,chain1_num,chain2_num); + vector<vector<string> >seqxA_init(chain1_num,tmp_str_vec); + vector<vector<string> >seqyA_init(chain1_num,tmp_str_vec); + vector<string> sequence_init; + copy_chain_assign_data(chain1_num, chain2_num, sequence_init, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, TMave_mat, + seqxA_init, seqyA_init, assign1_init, assign2_init, TMave_init); + + /* perform iterative alignment */ + double max_total_score=0; // ignore old total_score because previous + // score was from monomeric chain superpositions + int max_iter=5-(int)((len_aa+len_na)/200); + if (max_iter<2) max_iter=2; + if (byresi_opt==0) MMalign_iter(max_total_score, max_iter, xa_vec, ya_vec, + seqx_vec, seqy_vec, secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, + ylen_vec, xa, ya, seqx, seqy, secx, secy, len_aa, len_na, chain1_num, + chain2_num, TMave_mat, seqxA_mat, seqyA_mat, assign1_list, assign2_list, + sequence, d0_scale, fast_opt); + + /* sometime MMalign_iter is even worse than monomer alignment */ + if (byresi_opt==0 && max_total_score<maxTMmono) + { + copy_chain_assign_data(chain1_num, chain2_num, sequence, + seqxA_init, seqyA_init, assign1_init, assign2_init, TMave_init, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, TMave_mat); + for (i=0;i<chain1_num;i++) + { + if (i!=maxTMmono_i) assign1_list[i]=-1; + else assign1_list[i]=maxTMmono_j; + } + for (j=0;j<chain2_num;j++) + { + if (j!=maxTMmono_j) assign2_list[j]=-1; + else assign2_list[j]=maxTMmono_i; + } + sequence[0]=seqxA_mat[maxTMmono_i][maxTMmono_j]; + sequence[1]=seqyA_mat[maxTMmono_i][maxTMmono_j]; + max_total_score=maxTMmono; + MMalign_iter(max_total_score, max_iter, xa_vec, ya_vec, seqx_vec, seqy_vec, + secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, + xa, ya, seqx, seqy, secx, secy, len_aa, len_na, chain1_num, chain2_num, + TMave_mat, seqxA_mat, seqyA_mat, assign1_list, assign2_list, sequence, + d0_scale, fast_opt); } + + /* perform cross chain alignment + * in some cases, this leads to dramatic improvement, esp for homodimer */ + int iter_pair_num=count_assign_pair(assign1_list,chain1_num); + if (iter_pair_num>=init_pair_num) copy_chain_assign_data( + chain1_num, chain2_num, sequence_init, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, TMave_mat, + seqxA_init, seqyA_init, assign1_init, assign2_init, TMave_init); + double max_total_score_cross=max_total_score; + if (byresi_opt==0 && len_aa+len_na<10000) + { + MMalign_dimer(max_total_score_cross, xa_vec, ya_vec, seqx_vec, seqy_vec, + secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, + xa, ya, seqx, seqy, secx, secy, len_aa, len_na, chain1_num, chain2_num, + TMave_init, seqxA_init, seqyA_init, assign1_init, assign2_init, + sequence_init, d0_scale, fast_opt); + if (max_total_score_cross>max_total_score) + { + max_total_score=max_total_score_cross; + copy_chain_assign_data(chain1_num, chain2_num, sequence, + seqxA_init, seqyA_init, assign1_init, assign2_init, TMave_init, + seqxA_mat, seqyA_mat, assign1_list, assign2_list, TMave_mat); + } + } + + /* final alignment */ + + // commented out by Gabriel => avoid include that defines print_version + //if (outfmt_opt==0) print_version(); + + // Call hacked version of MMalign_final that returns MMAlignResult object + + MMAlignResult res = MMalign_final_hacked(xname.substr(dir1_opt.size()), yname.substr(dir2_opt.size()), + chainID_list1, chainID_list2, + fname_super, fname_lign, fname_matrix, + xa_vec, ya_vec, seqx_vec, seqy_vec, + secx_vec, secy_vec, mol_vec1, mol_vec2, xlen_vec, ylen_vec, + xa, ya, seqx, seqy, secx, secy, len_aa, len_na, + chain1_num, chain2_num, TMave_mat, + seqxA_mat, seqM_mat, seqyA_mat, assign1_list, assign2_list, sequence, + d0_scale, m_opt, o_opt, outfmt_opt, ter_opt, split_opt, + a_opt, d_opt, fast_opt, full_opt, mirror_opt, resi_vec1, resi_vec2); + + /* clean up everything */ + delete [] assign1_list; + delete [] assign2_list; + DeleteArray(&TMave_mat,chain1_num); + DeleteArray(&ut_mat, chain1_num*chain2_num); + vector<vector<string> >().swap(seqxA_mat); + vector<vector<string> >().swap(seqM_mat); + vector<vector<string> >().swap(seqyA_mat); + vector<string>().swap(tmp_str_vec); + + delete [] assign1_init; + delete [] assign2_init; + DeleteArray(&TMave_init,chain1_num); + vector<vector<string> >().swap(seqxA_init); + vector<vector<string> >().swap(seqyA_init); + + vector<vector<vector<double> > >().swap(xa_vec); // structure of complex1 + vector<vector<vector<double> > >().swap(ya_vec); // structure of complex2 + vector<vector<char> >().swap(seqx_vec); // sequence of complex1 + vector<vector<char> >().swap(seqy_vec); // sequence of complex2 + vector<vector<char> >().swap(secx_vec); // secondary structure of complex1 + vector<vector<char> >().swap(secy_vec); // secondary structure of complex2 + mol_vec1.clear(); // molecule type of complex1, RNA if >0 + mol_vec2.clear(); // molecule type of complex2, RNA if >0 + vector<string>().swap(chainID_list1); // list of chainID1 + vector<string>().swap(chainID_list2); // list of chainID2 + xlen_vec.clear(); // length of complex1 + ylen_vec.clear(); // length of complex2 + vector<string> ().swap(resi_vec1); // residue index for chain1 + vector<string> ().swap(resi_vec2); // residue index for chain2 + + + return res; +} + +void ExtractChainInfo(const ost::mol::ChainView& chain, geom::Vec3List& pos, + ost::seq::SequenceHandle& s, bool& rna_mode) { + + pos.clear(); + std::vector<char> olcs; + rna_mode = false; + ost::mol::ResidueViewList res_list = chain.GetResidueList(); + + for(auto it = res_list.begin(); it != res_list.end(); ++it) { char olc = it->GetOneLetterCode(); if(olc == '?') { continue; } - pos2.push_back(ca.GetPos()); - s2.push_back(olc); + if(it->IsPeptideLinking()) { + ost::mol::AtomView ca = it->FindAtom("CA"); + if(!ca.IsValid()) { + continue; + } + if(rna_mode) { + std::stringstream ss; + ss << "Error in WrappedTMAlign: Chains cannot have peptide and RNA "; + ss << "residues in same chain. Problematic chain: "<<chain.GetName(); + throw ost::Error(ss.str()); + } + olcs.push_back(olc); + pos.push_back(ca.GetPos()); + } + else if(it->IsNucleotideLinking()) { + ost::mol::AtomView c3 = it->FindAtom("C3'"); + if(!c3.IsValid()) { + continue; + } + if(rna_mode==false && !pos.empty()) { + std::stringstream ss; + ss << "Error in WrappedTMAlign: Chains cannot have peptide and RNA "; + ss << "residues in same chain. Problematic chain: "<<chain.GetName(); + throw ost::Error(ss.str()); + } + rna_mode = true; + // for some reason, USalign wants nucleotides to be lower case + olcs.push_back(tolower(olc)); + pos.push_back(c3.GetPos()); + } } + String str_s = String(olcs.begin(), olcs.end()); + s = ost::seq::CreateSequence(chain.GetName(), str_s); +} - String str_s1(s1.begin(), s1.end()); - String str_s2(s2.begin(), s2.end()); - ost::seq::SequenceHandle seq_s1 = ost::seq::CreateSequence("one", str_s1); - ost::seq::SequenceHandle seq_s2 = ost::seq::CreateSequence("two", str_s2); +TMAlignResult WrappedTMAlign(const ost::mol::ChainView& chain1, + const ost::mol::ChainView& chain2, + bool fast) { + + geom::Vec3List pos1; + ost::seq::SequenceHandle s1; + bool rna_mode1; + ExtractChainInfo(chain1, pos1, s1, rna_mode1); + + geom::Vec3List pos2; + ost::seq::SequenceHandle s2; + bool rna_mode2; + ExtractChainInfo(chain2, pos2, s2, rna_mode2); + + if(rna_mode1 != rna_mode2) { + throw ost::Error("Error in WrappedTMAlign: Cannot compare peptide with " + "RNA chains"); + } + + return WrappedTMAlign(pos1, pos2, s1, s2, fast, rna_mode1); +} + +MMAlignResult WrappedMMAlign(const ost::mol::EntityView& ent1, + const ost::mol::EntityView& ent2, + bool fast) { + ost::mol::ChainViewList chains1 = ent1.GetChainList(); + int n1 = chains1.size(); + std::vector<geom::Vec3List> pos1(n1); + ost::seq::SequenceList s1 = ost::seq::CreateSequenceList(); + std::vector<bool> rna1(n1); + for(int i = 0; i < n1; ++i) { + bool rna; + ost::seq::SequenceHandle s; + ExtractChainInfo(chains1[i], pos1[i], s, rna); + rna1[i] = rna; + s1.AddSequence(s); + } + + ost::mol::ChainViewList chains2 = ent2.GetChainList(); + int n2 = chains2.size(); + std::vector<geom::Vec3List> pos2(n2); + ost::seq::SequenceList s2 = ost::seq::CreateSequenceList(); + std::vector<bool> rna2(n2); + for(int i = 0; i < n2; ++i) { + bool rna; + ost::seq::SequenceHandle s; + ExtractChainInfo(chains2[i], pos2[i], s, rna); + rna2[i] = rna; + s2.AddSequence(s); + } - return WrappedTMAlign(pos1, pos2, seq_s1, seq_s2, fast); + return WrappedMMAlign(pos1, pos2, s1, s2, rna1, rna2, fast); } }} //ns diff --git a/modules/bindings/src/wrap_tmalign.hh b/modules/bindings/src/wrap_tmalign.hh index 4163d4644f8f4e703f15bff9ea6e6099f79a2f1f..282209ca57597bef4e5d5a3f15fbbaafb3e2dd0a 100644 --- a/modules/bindings/src/wrap_tmalign.hh +++ b/modules/bindings/src/wrap_tmalign.hh @@ -31,9 +31,10 @@ struct TMAlignResult { TMAlignResult() { } - TMAlignResult(Real rm, Real tm, int aln_l, const geom::Mat4& t, + TMAlignResult(Real rm, Real tm, Real tm_swp, int aln_l, const geom::Mat4& t, const ost::seq::AlignmentHandle& aln): rmsd(rm), tm_score(tm), + tm_score_swapped(tm_swp), aligned_length(aln_l), transform(t), alignment(aln) { } @@ -41,26 +42,77 @@ struct TMAlignResult { Real rmsd; Real tm_score; + Real tm_score_swapped; int aligned_length; geom::Mat4 transform; ost::seq::AlignmentHandle alignment; Real GetTMScore() { return tm_score; } + Real GetTMScoreSwapped() { return tm_score_swapped; } Real GetRMSD() { return rmsd; } int GetAlignedLength() { return aligned_length; } const geom::Mat4& GetTransform() { return transform; } const ost::seq::AlignmentHandle& GetAlignment() { return alignment; } }; +struct MMAlignResult { + + MMAlignResult() { } + + MMAlignResult(Real rm, Real tm, Real tm_swp, int al, const geom::Mat4& t, + const ost::seq::AlignmentList& alns, + const std::vector<String>& e1c, + const std::vector<String>& e2c): rmsd(rm), + tm_score(tm), + tm_score_swapped(tm_swp), + aligned_length(al), + transform(t), + alignments(alns), + ent1_mapped_chains(e1c), + ent2_mapped_chains(e2c) { } + + + Real rmsd; + Real tm_score; + Real tm_score_swapped; + int aligned_length; + geom::Mat4 transform; + ost::seq::AlignmentList alignments; + std::vector<String> ent1_mapped_chains; + std::vector<String> ent2_mapped_chains; + + Real GetTMScore() { return tm_score; } + Real GetTMScoreSwapped() { return tm_score_swapped; } + Real GetRMSD() { return rmsd; } + int GetAlignedLength() { return aligned_length; } + const geom::Mat4& GetTransform() { return transform; } + const ost::seq::AlignmentList& GetAlignments() { return alignments; } + const std::vector<String>& GetEnt1MappedChains() {return ent1_mapped_chains; } + const std::vector<String>& GetEnt2MappedChains() {return ent2_mapped_chains; } +}; + TMAlignResult WrappedTMAlign(const geom::Vec3List& pos_one, const geom::Vec3List& pos_two, const ost::seq::SequenceHandle& seq1, const ost::seq::SequenceHandle& seq2, + bool fast = false, + bool rna = false); + +MMAlignResult WrappedMMAlign(const std::vector<geom::Vec3List>& pos_one, + const std::vector<geom::Vec3List>& pos_two, + const ost::seq::SequenceList& seq1, + const ost::seq::SequenceList& seq2, + const std::vector<bool>& rna1, + const std::vector<bool>& rna2, bool fast = false); TMAlignResult WrappedTMAlign(const ost::mol::ChainView& ent1, const ost::mol::ChainView& ent2, bool fast = false); + +MMAlignResult WrappedMMAlign(const ost::mol::EntityView& ent1, + const ost::mol::EntityView& ent2, + bool fast = false); }} //ns #endif diff --git a/modules/conop/doc/compoundlib.rst b/modules/conop/doc/compoundlib.rst index b454ff98d8e20725ba0186c22a78689d342d934f..fed70b6379713ceee56ca9273dc3a1541455947c 100644 --- a/modules/conop/doc/compoundlib.rst +++ b/modules/conop/doc/compoundlib.rst @@ -21,10 +21,17 @@ build the compound library manually. .. function:: GetDefaultLib() - :return: Default compound library set by :func:`SetDefaultLib`. If you got - OpenStructure as a bundle or you :ref:`compiled <cmake-flags>` it - with a specified ``COMPOUND_LIB`` flag, this will return a compound - library when executing scripts with ``ost``. + Get the default compound library. This is set by :func:`SetDefaultLib`. + + If you obtained OpenStructure as a container or you + :ref:`compiled <cmake-flags>` it with a specified ``COMPOUND_LIB`` flag, + this function will return a compound library. + + You can override the default compound library by pointing the + ``OST_COMPOUNDS_CHEMLIB`` environment variable to a valid compound library + file. + + :return: Default compound library. :rtype: :class:`CompoundLib` or None if no library set .. function:: SetDefaultLib(lib) diff --git a/modules/conop/tests/test_cleanup.py b/modules/conop/tests/test_cleanup.py index 54d33a8bc67bf1b95d2df425ea76b8ac5927a09c..3abc1f5ffad1908934d833be586ac3e6794571be 100644 --- a/modules/conop/tests/test_cleanup.py +++ b/modules/conop/tests/test_cleanup.py @@ -160,7 +160,7 @@ class TestCleanUp(unittest.TestCase): if __name__ == "__main__": from ost import testutils - if testutils.SetDefaultCompoundLib(): + if testutils.DefaultCompoundLibIsSet(): testutils.RunTests() else: print('No compound library available. Ignoring test_cleanup.py tests.') diff --git a/modules/conop/tests/test_complib.py b/modules/conop/tests/test_complib.py index dca8dc3e920b6c9c830419553016e31ead5db032..2c3e3f8121c2ea6d678a1edf67b9e0fbf1469dbf 100644 --- a/modules/conop/tests/test_complib.py +++ b/modules/conop/tests/test_complib.py @@ -31,7 +31,7 @@ class TestCompLib(unittest.TestCase): if __name__ == "__main__": from ost import testutils - if testutils.SetDefaultCompoundLib(): + if testutils.DefaultCompoundLibIsSet(): testutils.RunTests() else: print('No compound lib available. Ignoring test_complib tests.') \ No newline at end of file diff --git a/modules/conop/tests/test_compound.py b/modules/conop/tests/test_compound.py index 01c59a233e263e1c9d538c6ae303a7cf5442a8d3..a6c704d408d1c3926a786e3e4f0988b6ddfbfcf2 100644 --- a/modules/conop/tests/test_compound.py +++ b/modules/conop/tests/test_compound.py @@ -25,7 +25,7 @@ class TestCompound(unittest.TestCase): if __name__=='__main__': from ost import testutils - if testutils.SetDefaultCompoundLib(): + if testutils.DefaultCompoundLibIsSet(): testutils.RunTests() else: print('No compound library available. Ignoring test_compound.py tests.') \ No newline at end of file diff --git a/modules/doc/actions.rst b/modules/doc/actions.rst index 08276326718da6b622169ca1d02dcd28699e5edd..8fd2dbab5d4be5f12cc6ae9c0443818fa20e8e44 100644 --- a/modules/doc/actions.rst +++ b/modules/doc/actions.rst @@ -23,7 +23,6 @@ You can compare two structures from the command line with the interface to :class:`ost.mol.alg.scoring.Scorer` .. warning:: - ``compare-structures`` underwent a complete rewrite in OpenStructure release 2.4.0. The old version is still available as ``compare-structures-legacy`` with documentation available @@ -38,17 +37,20 @@ Details on the usage (output of ``ost compare-structures --help``): [-mb MODEL_BIOUNIT] [-rb REFERENCE_BIOUNIT] [-rna] [-ec] [-d] [-ds DUMP_SUFFIX] [-ft] [-c CHAIN_MAPPING [CHAIN_MAPPING ...]] [--lddt] - [--local-lddt] [--cad-score] [--local-cad-score] - [--cad-exec CAD_EXEC] [--qs-score] + [--local-lddt] [--bb-lddt] [--bb-local-lddt] + [--cad-score] [--local-cad-score] + [--cad-exec CAD_EXEC] + [--usalign-exec USALIGN_EXEC] [--qs-score] [--rigid-scores] [--interface-scores] - [--patch-scores] + [--patch-scores] [--tm-score] + [--lddt-no-stereochecks] Evaluate model against reference Example: ost compare-structures -m model.pdb -r reference.cif Loads the structures and performs basic cleanup: - + * Assign elements according to the PDB Chemical Component Dictionary * Map nonstandard residues to their parent residues as defined by the PDB Chemical Component Dictionary, e.g. phospho-serine => serine @@ -57,11 +59,11 @@ Details on the usage (output of ``ost compare-structures --help``): * Remove unknown atoms, i.e. atoms that are not expected according to the PDB Chemical Component Dictionary * Select for peptide/nucleotide residues - + The cleaned structures are optionally dumped using -d/--dump-structures Output is written in JSON format (default: out.json). In case of no additional - options, this is a dictionary with 8 keys: + options, this is a dictionary with 8 keys describing model/reference comparison: * "reference_chains": Chain names of reference * "model_chains": Chain names of model @@ -80,14 +82,28 @@ Details on the usage (output of ``ost compare-structures --help``): format. * "inconsistent_residues": List of strings that represent name mismatches of aligned residues in form - <trg_cname>.<trg_rname><trg_rnum>-<mdl_cname>.<mdl_rname><mdl_rnum>. + <trg_cname>.<trg_rnum>.<trg_ins_code>-<mdl_cname>.<mdl_rnum>.<mdl_ins_code>. Inconsistencies may lead to corrupt results but do not abort the program. Program abortion in these cases can be enforced with -ec/--enforce-consistency. * "status": SUCCESS if everything ran through. In case of failure, the only content of the JSON output will be "status" set to FAILURE and an additional key: "traceback". - + + The following additional keys store relevant input parameters to reproduce + results: + + * "model" + * "reference" + * "fault_tolerant" + * "model_biounit" + * "reference_biounit" + * "residue_number_alignment" + * "enforce_consistency" + * "cad_exec" + * "usalign_exec" + * "lddt_no_stereochecks" + The pairwise sequence alignments are computed with Needleman-Wunsch using BLOSUM62 (NUC44 for nucleotides). Many benchmarking scenarios preprocess the structures to ensure matching residue numbers (CASP/CAMEO). In these cases, @@ -122,13 +138,13 @@ Details on the usage (output of ``ost compare-structures --help``): filepath if not given. -mb MODEL_BIOUNIT, --model-biounit MODEL_BIOUNIT Only has an effect if model is in mmcif format. By - default, the assymetric unit (AU) is used for scoring. + default, the asymmetric unit (AU) is used for scoring. If there are biounits defined in the mmcif file, you can specify the (0-based) index of the one which should be used. -rb REFERENCE_BIOUNIT, --reference-biounit REFERENCE_BIOUNIT Only has an effect if reference is in mmcif format. By - default, the assymetric unit (AU) is used for scoring. + default, the asymmetric unit (AU) is used for scoring. If there are biounits defined in the mmcif file, you can specify the (0-based) index of the one which should be used. @@ -163,28 +179,44 @@ Details on the usage (output of ``ost compare-structures --help``): counterparts. --local-lddt Compute per-residue lDDT scores with default parameterization and store as key "local_lddt". Score - for model residue with number 42 in chain X can be - extracted with: data["local_lddt"]["X"]["42"]. If - there is an insertion code, lets say A, the last key - becomes "42A" Stereochemical irregularities affecting - lDDT are reported as keys "model_clashes", - "model_bad_bonds", "model_bad_angles" and the - respective reference counterparts. + for each residue is accessible by key + <chain_name>.<resnum>.<resnum_inscode>. Residue with + number 42 in chain X can be extracted with: + data["local_lddt"]["X.42."]. If there is an insertion + code, lets say A, the residue key becomes "X.42.A". + Stereochemical irregularities affecting lDDT are + reported as keys "model_clashes", "model_bad_bonds", + "model_bad_angles" and the respective reference + counterparts. Atoms specified in there follow the + following format: + <chain_name>.<resnum>.<resnum_inscode>.<atom_name> + --bb-lddt Compute global lDDT score with default + parameterization and store as key "bb_lddt". lDDT in + this case is only computed on backbone atoms: CA for + peptides and C3' for nucleotides + --bb-local-lddt Compute per-residue lDDT scores with default + parameterization and store as key "bb_local_lddt". + lDDT in this case is only computed on backbone atoms: + CA for peptides and C3' for nucleotides. Per-residue + scores are accessible as described for local_lddt. --cad-score Compute global CAD's atom-atom (AA) score and store as key "cad_score". --residue-number-alignment must be enabled to compute this score. Requires voronota_cadscore executable in PATH. Alternatively you can set cad-exec. --local-cad-score Compute local CAD's atom-atom (AA) scores and store as - key "local_cad_score". Score for model residue with - number 42 in chain X can be extracted with: - data["local_cad_score"]["X"]["42"]. --residue-number- - alignments must be enabled to compute this score. - Requires voronota_cadscore executable in PATH. + key "local_cad_score". Per-residue scores are + accessible as described for local_lddt. --residue- + number-alignments must be enabled to compute this + score. Requires voronota_cadscore executable in PATH. Alternatively you can set cad-exec. --cad-exec CAD_EXEC Path to voronota-cadscore executable (installed from https://github.com/kliment-olechnovic/voronota). Searches PATH if not set. + --usalign-exec USALIGN_EXEC + Path to USalign executable to compute TM-score. If not + given, an OpenStructure internal copy of USalign code + is used. --qs-score Compute QS-score, stored as key "qs_global", and the QS-best variant, stored as key "qs_best". --rigid-scores Computes rigid superposition based scores. They're @@ -235,8 +267,17 @@ Details on the usage (output of ``ost compare-structures --help``): "model_interface_residues", reference interface residues as key "reference_interface_residues". Residues are represented as string in form - <num><inscode>. The respective scores are available as - keys "patch_qs" and "patch_dockq" + <chain_name>.<resnum>.<resnum_inscode>. The respective + scores are available as keys "patch_qs" and + "patch_dockq" + --tm-score Computes TM-score with the USalign tool. Also computes + a chain mapping in case of complexes that is stored in + the same format as the default mapping. TM-score and + the mapping are available as keys "tm_score" and + "usalign_mapping" + --lddt-no-stereochecks + Disable stereochecks for lDDT computation + .. _ost compare ligand structures: @@ -370,4 +411,4 @@ Details on the usage (output of ``ost compare-ligand-structures --help``): Additional information about the scores and output values is available in :meth:`rmsd_details <ost.mol.alg.ligand_scoring.LigandScorer.rmsd_details>` and -:meth:`lddt_pli_details <ost.mol.alg.ligand_scoring.LigandScorer.lddt_pli_details>`. \ No newline at end of file +:meth:`lddt_pli_details <ost.mol.alg.ligand_scoring.LigandScorer.lddt_pli_details>`. diff --git a/modules/doc/install.rst b/modules/doc/install.rst index d840eeab2f2084f5eba8ba5e57e2afe3faa41baf..dd4d4d5e16b5d27ba0c6836070c8eedf2bb2ffbf 100644 --- a/modules/doc/install.rst +++ b/modules/doc/install.rst @@ -420,6 +420,22 @@ or, to start the command-line interpreter: If you repeatedly use OpenStructure, it is recommended to add /path/to/ost/stage/bin to your path. +You can also import OpenStructure directly into your existing python scripts, +jupyter notebooks etc. Simply make sure to point the following environment +variables to the right folders: + +.. code-block:: bash + + export OST_ROOT=/path/to/ost/stage + export PYTHONPATH=$OST_ROOT/lib64/python3.10/site-packages/:$PYTHONPATH + python + +And then you can simply import ost as a module: + +.. code-block:: python + + import ost + Getting the newest changes -------------------------------------------------------------------------------- diff --git a/modules/io/doc/mmcif.rst b/modules/io/doc/mmcif.rst index c4c361bda053584f6e89f9dc0c9e0342c0b68c94..20a816d2dd4e1fb19c8f28901ef75e00c44784e0 100644 --- a/modules/io/doc/mmcif.rst +++ b/modules/io/doc/mmcif.rst @@ -64,11 +64,32 @@ Notes: It is added as string property named "pdb_auth_chain_name" to the :class:`~ost.mol.ChainHandle`. The mapping is also stored in :class:`MMCifInfo` as :meth:`~MMCifInfo.GetMMCifPDBChainTr` and - :meth:`~MMCifInfo.GetPDBMMCifChainTr` if SEQRES records are read in - :func:`~ost.io.LoadMMCIF` and a non-empty SEQRES record exists for that chain - (this should exclude ligands and water). -* Molecular entities in mmCIF are identified by an ``entity.id``. Each chain is - mapped to an ID in :class:`MMCifInfo` as :meth:`~MMCifInfo.GetMMCifEntityIdTr`. + :meth:`~MMCifInfo.GetPDBMMCifChainTr` if a non-empty SEQRES record exists for + that chain (this should exclude ligands and water). +* Molecular entities in mmCIF are identified by an ``entity.id``, which is + extracted from ``atom_site.label_entity_id`` for the first atom of the chain. + It is added as string property named "entity_id" to the + :class:`~ost.mol.ChainHandle`. Each chain is mapped to an ID in + :class:`MMCifInfo` as :meth:`~MMCifInfo.GetMMCifEntityIdTr`. +* For more complex mappings, such as ligands which may be in a same "old" chain + as the protein chain but are represented in a separate "new" chain in mmCIF, + we also store string properties on a per-residue level. + For mmCIF files from the PDB, there is a unique mapping between + ("label_asym_id", "label_seq_id") and ("auth_asym_id", "auth_seq_id", + "pdbx_PDB_ins_code"). + The following data items are available: + + * ``atom_site.label_asym_id``: ``residue.chain.name`` + * ``atom_site.label_seq_id``: ``residue.GetStringProp("resnum")`` + (this is the same as ``residue.number`` for residues in polymer chains. + However, for ligands ``residue.number`` is unset in mmCIF, but it + is set to 1 by openstructure.) + * ``atom_site.label_entity_id``: ``residue.GetStringProp("entity_id")`` + * ``atom_site.auth_asym_id``: ``residue.GetStringProp("pdb_auth_chain_name")`` + * ``atom_site.auth_seq_id``: ``residue.GetStringProp("pdb_auth_resnum")`` + * ``atom_site.pdbx_PDB_ins_code``: ``residue.GetStringProp("pdb_auth_ins_code")`` +* Missing values in the aforementioned data items will be denoted as ``.`` or + ``?``. Info Classes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -250,9 +271,7 @@ of the annotation available. .. method:: GetMMCifPDBChainTr(cif_chain_id) Get the translation of a certain mmCIF chain name to the traditional PDB - chain name. Only works if SEQRES records are read in - :func:`~ost.io.LoadMMCIF` and a compound library is available (see - :func:`~ost.conop.GetDefaultLib`). + chain name. :param cif_chain_id: atom_site.label_asym_id :type cif_chain_id: :class:`str` diff --git a/modules/io/pymod/__init__.py b/modules/io/pymod/__init__.py index 965a39ce4b95c15fcf4c4703a910b1b7145233f8..ca619a1561732da5bc65912e1216d339351271f9 100644 --- a/modules/io/pymod/__init__.py +++ b/modules/io/pymod/__init__.py @@ -20,6 +20,7 @@ import os, tempfile, ftplib, http.client from ._ost_io import * from ost import mol, geom, conop, seq +from ost import LogWarning class IOProfiles: def __init__(self): @@ -356,7 +357,10 @@ def LoadMMCIF(filename, fault_tolerant=None, calpha_only=None, customize the exact behaviour of the mmCIF import. For more information on these options, see :doc:`profile`. - Residues are flagged as ligand if they are mentioned in a HET record. + Residues are flagged as ligand if they are not waters nor covered by an + ``entity_poly`` record (ie. they are non-polymer entities in + ``pdbx_entity_nonpoly``). Note that all residues except waters will be + flagged as ligands if ``seqres=False`` (the default). :param filename: File to be loaded :type filename: :class:`str` @@ -389,14 +393,15 @@ def LoadMMCIF(filename, fault_tolerant=None, calpha_only=None, interpreted as the pdb id. :type remote: :class:`bool` - :param seqres: Whether to read SEQRES records. If True, a + :param seqres: Whether to return SEQRES records. If True, a :class:`~ost.seq.SequenceList` object is returned as the second item. The sequences in the list are named according to the mmCIF chain name. This feature requires a default :class:`compound library <ost.conop.CompoundLib>` to be defined and accessible via - :func:`~ost.conop.GetDefaultLib` or an empty list is returned. + :func:`~ost.conop.GetDefaultLib`. One letter codes of non + standard compounds are set to X otherwise. :type seqres: :class:`bool` :param info: Whether to return an info container with the other output. @@ -430,7 +435,6 @@ def LoadMMCIF(filename, fault_tolerant=None, calpha_only=None, try: ent = mol.CreateEntity() reader = MMCifReader(filename, ent, prof) - reader.read_seqres = seqres # NOTE: to speed up things, we could introduce a restrict_chains parameter # similar to the one in LoadPDB. Here, it would have to be a list/set @@ -443,6 +447,11 @@ def LoadMMCIF(filename, fault_tolerant=None, calpha_only=None, reader.info.ConnectBranchLinks() #else: # raise IOError("File doesn't contain any entities") + + # Warn about info dependency on seqres + if info and not reader.seqres: + LogWarning("MMCifInfo is incomplete when seqres=False") + if seqres and info: return ent, reader.seqres, reader.info if seqres: diff --git a/modules/io/src/mol/mmcif_info.cc b/modules/io/src/mol/mmcif_info.cc index 576e05912bb0073c9d93215d12ced3747353a2c8..ef44d4813fef36f2c709888b2acaef4d007342ee 100644 --- a/modules/io/src/mol/mmcif_info.cc +++ b/modules/io/src/mol/mmcif_info.cc @@ -207,6 +207,12 @@ void MMCifInfo::AddEntityBranchLink(String chain_name, mol::AtomHandle atom2, unsigned char bond_order) { + if (!atom1.IsValid() || !atom2.IsValid()) { + /* Would love to give details about the atoms... but atom names are not + available at this point. */ + LOG_WARNING("Invalid branch link found in chain '"+chain_name+"'."); + return; + } // check if element already exists MMCifInfoEntityBranchLinkMap::iterator blm_it = entity_branches_.find(chain_name); diff --git a/modules/io/src/mol/mmcif_reader.cc b/modules/io/src/mol/mmcif_reader.cc index 527f68fbef1e4c42ca0e32a5d918cce94c5b61d8..e550d96f83dfc160819f860055561534606b48fd 100644 --- a/modules/io/src/mol/mmcif_reader.cc +++ b/modules/io/src/mol/mmcif_reader.cc @@ -24,6 +24,7 @@ #include <ost/dyn_cast.hh> #include <ost/mol/xcs_editor.hh> #include <ost/conop/conop.hh> +#include <ost/conop/minimal_compound_lib.hh> #include <ost/io/mol/mmcif_reader.hh> @@ -65,7 +66,7 @@ void MMCifReader::Init() curr_chain_ = mol::ChainHandle(); curr_residue_ = mol::ResidueHandle(); seqres_ = seq::CreateSequenceList(); - read_seqres_ = false; + read_seqres_ = true; warned_rule_based_ = false; info_ = MMCifInfo(); } @@ -140,6 +141,7 @@ bool MMCifReader::OnBeginLoop(const StarLoopDesc& header) indices_[AUTH_SEQ_ID] = header.GetIndex("auth_seq_id"); indices_[PDBX_PDB_INS_CODE] = header.GetIndex("pdbx_PDB_ins_code"); indices_[PDBX_PDB_MODEL_NUM] = header.GetIndex("pdbx_PDB_model_num"); + indices_[FORMAL_CHARGE] = header.GetIndex("pdbx_formal_charge"); // post processing if (category_counts_[category_] > 0) { @@ -481,6 +483,7 @@ void MMCifReader::ParseAndAddAtom(const std::vector<StringRef>& columns) return; } Real occ = 1.00f, temp = 0; + int charge = 0; geom::Vec3 apos; for (int i = CARTN_X; i <= CARTN_Z; ++i) { @@ -504,6 +507,13 @@ void MMCifReader::ParseAndAddAtom(const std::vector<StringRef>& columns) "atom_site.B_iso_or_equiv"); } } + if (indices_[FORMAL_CHARGE] != -1) { // unit test + String charge_s = columns[indices_[FORMAL_CHARGE]].str(); + if (charge_s != "?" && charge_s != ".") { + charge = this->TryGetInt(columns[indices_[FORMAL_CHARGE]], + "atom_site.pdbx_formal_charge"); + } + } // determine element String s_ele(columns[indices_[TYPE_SYMBOL]].str()); @@ -559,6 +569,7 @@ void MMCifReader::ParseAndAddAtom(const std::vector<StringRef>& columns) ++chain_count_; // store entity id String ent_id = columns[indices_[LABEL_ENTITY_ID]].str(); + curr_chain_.SetStringProp("entity_id", ent_id); chain_id_pairs_.push_back(std::pair<mol::ChainHandle,String>(curr_chain_, ent_id)); info_.AddMMCifEntityIdTr(cif_chain_name, ent_id); @@ -585,9 +596,15 @@ void MMCifReader::ParseAndAddAtom(const std::vector<StringRef>& columns) curr_residue_ = editor.AppendResidue(curr_chain_, res_name.str(), res_num); + } else { curr_residue_ = editor.AppendResidue(curr_chain_, res_name.str()); } + curr_residue_.SetStringProp("pdb_auth_chain_name", auth_chain_name); + curr_residue_.SetStringProp("pdb_auth_resnum", columns[indices_[AUTH_SEQ_ID]].str()); + curr_residue_.SetStringProp("pdb_auth_ins_code", columns[indices_[PDBX_PDB_INS_CODE]].str()); + curr_residue_.SetStringProp("entity_id", columns[indices_[LABEL_ENTITY_ID]].str()); + curr_residue_.SetStringProp("resnum", columns[indices_[LABEL_SEQ_ID]].str()); warned_name_mismatch_=false; ++residue_count_; } @@ -657,6 +674,8 @@ void MMCifReader::ParseAndAddAtom(const std::vector<StringRef>& columns) ah.SetOccupancy(occ); + ah.SetCharge(charge); + // record type ah.SetHetAtom(indices_[GROUP_PDB] == -1 ? false : columns[indices_[GROUP_PDB]][0]=='H'); @@ -735,15 +754,16 @@ void MMCifReader::ParseEntityPoly(const std::vector<StringRef>& columns) } else if (indices_[PDBX_SEQ_ONE_LETTER_CODE] != -1) { seqres=columns[indices_[PDBX_SEQ_ONE_LETTER_CODE]]; - conop::CompoundLibPtr comp_lib=conop::Conopology::Instance() - .GetDefaultLib(); + conop::CompoundLibBasePtr comp_lib=conop::Conopology::Instance() + .GetDefaultLib(); if (!comp_lib) { if (!warned_rule_based_) { - LOG_WARNING("SEQRES import requires a compound library. " - "Ignoring SEQRES records"); + LOG_WARNING("SEQRES import requires a valid compound library to " + "handle non standard compounds. Their One letter " + "codes will be set to X."); } warned_rule_based_=true; - return; + comp_lib = conop::CompoundLibBasePtr(new ost::conop::MinimalCompoundLib); } edm_it->second.seqres = this->ConvertSEQRES(seqres.str_no_whitespace(), comp_lib); @@ -756,7 +776,7 @@ void MMCifReader::ParseEntityPoly(const std::vector<StringRef>& columns) } String MMCifReader::ConvertSEQRES(const String& seqres, - conop::CompoundLibPtr comp_lib) + conop::CompoundLibBasePtr comp_lib) { String can_seqres; for (String::const_iterator i=seqres.begin(), e=seqres.end(); i!=e; ++i) { diff --git a/modules/io/src/mol/mmcif_reader.hh b/modules/io/src/mol/mmcif_reader.hh index 3d10f03d47aa7b25311e1a5975bc628511034870..f2f2d2dbd78069a778c6cd3ccab9d49abb7b0cb1 100644 --- a/modules/io/src/mol/mmcif_reader.hh +++ b/modules/io/src/mol/mmcif_reader.hh @@ -240,7 +240,7 @@ protected: /// not possible, however, since the PDB assigns multiple one letter codes /// to some of the residues. To be consistent, we have to do the conversion /// on our own. - String ConvertSEQRES(const String& seqres, conop::CompoundLibPtr compound_lib); + String ConvertSEQRES(const String& seqres, conop::CompoundLibBasePtr compound_lib); /// \brief Fetch mmCIF citation_author information /// /// \param columns data row @@ -359,7 +359,7 @@ protected: private: /// \enum magic numbers of this class typedef enum { - MAX_ITEMS_IN_ROW=18 ///< count for possible items in a loop row + MAX_ITEMS_IN_ROW=19 ///< count for possible items in a loop row } MMCifMagicNos; /// \enum items of the atom_site category @@ -381,7 +381,8 @@ private: B_ISO_OR_EQUIV, PDBX_PDB_INS_CODE, GROUP_PDB, ///< record name - PDBX_PDB_MODEL_NUM ///< model no. (especially NMR structures) + PDBX_PDB_MODEL_NUM,///< model no. (especially NMR structures) + FORMAL_CHARGE } AtomSiteItems; /// \enum items of the entity category diff --git a/modules/io/src/mol/pdb_reader.cc b/modules/io/src/mol/pdb_reader.cc index 3b69915de24795389f6566e5ce43e0cc487d9c8d..930ffbcf453f0563768d8aae0351f65fe6ac07bf 100644 --- a/modules/io/src/mol/pdb_reader.cc +++ b/modules/io/src/mol/pdb_reader.cc @@ -697,8 +697,11 @@ void PDBReader::ParseAndAddAtom(const StringRef& line, int line_num, atom_name, alt_loc, record_type, serial)) { return; } - std::pair<bool, Real> charge, radius; - std::pair<bool, Real> occ, temp; + + std::pair<bool, Real> charge = std::make_pair(false, 0.0); + std::pair<bool, Real> radius = std::make_pair(false, 0.0); + std::pair<bool, Real> occ = std::make_pair(false, 0.0); + std::pair<bool, Real> temp = std::make_pair(false, 0.0); geom::Vec3 apos; for (int i=0;i<3; ++i) { @@ -728,6 +731,20 @@ void PDBReader::ParseAndAddAtom(const StringRef& line, int line_num, if (line.length()>=66) { temp=line.substr(60, 6).ltrim().to_float(); } + if (line.length()>=80) { + // example charge formatting: 1+, 2- etc. + charge = line.substr(78,1).trim().to_float(); + if(charge.first) { + if(line[79] != '-' && line[79] != '+') { + std::stringstream ss; + ss << "error on line " << line_num << ": " + << "expect charge in format 1+, 2-, etc. got: " + << line.substr(78, 2); + throw IOException(ss.str()); + } + if(line[79] == '-') charge.second *= (-1); + } + } } LOG_TRACE( "line: [" << line << "]" ); String s_ele; @@ -838,6 +855,7 @@ void PDBReader::ParseAndAddAtom(const StringRef& line, int line_num, } Real b=temp.first ? temp.second : 0.0; Real o=occ.first ? occ.second : 1.0; + if (!profile_.quack_mode && alt_loc!=' ') { // Check if there is already a atom with the same name. mol::AtomHandle me=curr_residue_.FindAtom(aname); @@ -869,16 +887,16 @@ void PDBReader::ParseAndAddAtom(const StringRef& line, int line_num, ah=editor.InsertAtom(curr_residue_, aname, apos, s_ele); ++atom_count_; } - if(is_pqr_) { - if (radius.first) { - ah.SetRadius(radius.second); - } + + if (radius.first) { + ah.SetRadius(radius.second); } - ah.SetBFactor(b); - ah.SetOccupancy(o); if (charge.first) { ah.SetCharge(charge.second); } + ah.SetBFactor(b); + ah.SetOccupancy(o); + ah.SetHetAtom(record_type[0]=='H'); if(profile_.read_conect && serial != -1) { this->amap_[serial] = ah; diff --git a/modules/io/src/mol/sdf_reader.cc b/modules/io/src/mol/sdf_reader.cc index 7f3a7f56030b7c3584bfd8b9f52c16bc0a90f4e0..3c28e279cb71a3f89575d5fe3bca70ce621c6c6f 100644 --- a/modules/io/src/mol/sdf_reader.cc +++ b/modules/io/src/mol/sdf_reader.cc @@ -125,6 +125,8 @@ void SDFReader::NextMolecule() atom_count_=0; bond_count_=0; line_num=0; + curr_residue_ = ost::mol::ResidueHandle(); + curr_chain_ = ost::mol::ChainHandle(); } void SDFReader::ParseAndAddHeader(const String& line, int line_num, @@ -145,13 +147,10 @@ void SDFReader::ParseAndAddHeader(const String& line, int line_num, String msg="Bad molecule name line %d: Line is empty"; throw IOException(str(format(msg) % line_num)); } - curr_chain_=editor.InsertChain(s_chain); - LOG_DEBUG("new chain " << s_chain); - - mol::ResidueKey rkey=boost::trim_copy(s_title); - mol::ResNum rnum(++residue_count_); - curr_residue_=editor.AppendResidue(curr_chain_, rkey, rnum); - LOG_DEBUG("new residue " << rkey << "(" << rnum << ")"); + // prepeare required variables to add new chain and residue + // once we parse the first atom + curr_chain_name_ = s_chain; + curr_res_key_ = boost::trim_copy(s_title); break; } case 2: // user information line @@ -160,6 +159,11 @@ void SDFReader::ParseAndAddHeader(const String& line, int line_num, break; case 4: // counts line { + String version_str=line.substr(34, 5); + if (version_str != "V2000") { + String msg="Unsupported SDF version: %s."; + throw IOException(str(format(msg) % version_str)); + } String s_anum=line.substr(0,3); try { atom_count_=boost::lexical_cast<int>(boost::trim_copy(s_anum)); @@ -189,9 +193,15 @@ void SDFReader::ParseAndAddAtom(const String& line, int line_num, LOG_TRACE( "line: [" << line << "]" ); if(line.length()<48 || line.length()>69) { - String msg="Bad atom line %d: Not correct number of characters on the" - " line: %i (should be between 48 and 69)"; - throw IOException(str(format(msg) % line_num % line.length())); + // Handle the case where we have trailing space characters + if (line.length()>69 && boost::trim_copy(line.substr(69)) == "") { + LOG_DEBUG( "Ignoring trailing space" ); + } + else { + String msg="Bad atom line %d: Not correct number of characters on the" + " line: %i (should be between 48 and 69)"; + throw IOException(str(format(msg) % line_num % line.length())); + } } int anum = line_num-4; // start at 1 on fifth line since first four lines are header String s_posx=line.substr(0,10); @@ -228,6 +238,17 @@ void SDFReader::ParseAndAddAtom(const String& line, int line_num, throw IOException(str(format(msg) % line_num % s_charge)); } + if(!curr_chain_.IsValid()) { + curr_chain_=editor.InsertChain(curr_chain_name_); + LOG_DEBUG("new chain " << curr_chain_name_); + } + + if(!curr_residue_.IsValid()) { + mol::ResNum rnum(++residue_count_); + curr_residue_=editor.AppendResidue(curr_chain_, curr_res_key_, rnum); + LOG_DEBUG("new residue " << curr_res_key_ << "(" << rnum << ")"); + } + LOG_DEBUG("adding atom " << aname << " (" << s_ele << ") @" << apos); mol::AtomHandle atom=editor.InsertAtom(curr_residue_, aname, apos, upper_ele); @@ -243,9 +264,15 @@ void SDFReader::ParseAndAddBond(const String& line, int line_num, LOG_TRACE( "line: [" << line << "]" ); if(line.length()<9 || line.length()>21) { - String msg="Bad bond line %d: Not correct number of characters on the" - " line: %i (should be between 9 and 21)"; - throw IOException(str(format(msg) % line_num % line.length())); + // Handle the case where we have trailing space characters + if (line.length()>21 && boost::trim_copy(line.substr(21)) == "") { + LOG_DEBUG( "Ignoring trailing space" ); + } + else { + String msg="Bad bond line %d: Not correct number of characters on the" + " line: %i (should be between 9 and 21)"; + throw IOException(str(format(msg) % line_num % line.length())); + } } String s_first_name=line.substr(0,3); diff --git a/modules/io/src/mol/sdf_reader.hh b/modules/io/src/mol/sdf_reader.hh index 04d05a2d6d2f2f9fd54c76b775f3224f22f71a8c..f786f4e5426bf5068022ef0f8ed3657c8481be2c 100644 --- a/modules/io/src/mol/sdf_reader.hh +++ b/modules/io/src/mol/sdf_reader.hh @@ -53,6 +53,8 @@ private: void ParseAndAddBond(const String& line, int line_num, mol::EntityHandle& ent, mol::XCSEditor& editor); + String curr_chain_name_; + mol::ResidueKey curr_res_key_; mol::ChainHandle curr_chain_; mol::ResidueHandle curr_residue_; int chain_count_; diff --git a/modules/io/src/mol/sdf_writer.cc b/modules/io/src/mol/sdf_writer.cc index 8deaf9a7819f0fff6c069b721af7b55e446a0245..e39444d0cdd394b568f82568622c6b4fbc006545 100644 --- a/modules/io/src/mol/sdf_writer.cc +++ b/modules/io/src/mol/sdf_writer.cc @@ -50,7 +50,12 @@ namespace { << format("%10.4f") % atom.GetPos()[1] << format("%10.4f ") % atom.GetPos()[2] << format("%-3s") % SDFAtomWriter::FormatEle(atom.GetElement()) - << " 0 0 0 0 0 0" + << " 0" // Mass difference + << format("%-3s") % SDFAtomWriter::FormatCharge(atom.GetCharge()) // Charge + << " 0" // Atom stereo parity + << " 0" // Hydrogen count + 1 + << " 0" // Stereo care box + << " 0" // Valence << std::endl; return true; } @@ -66,6 +71,24 @@ namespace { } return return_ele; } + + static String FormatCharge(const Real& chg) { + // Format charge according to https://doi.org/10.1021/ci00007a012 + // 0 = uncharged or value other than these, 1 = +3, 2 = +2, 3 = +1, + // 4 doublet (A), 5 = -1, 6 = -2, 7 = -3 + // Doublet means radical. This function would never return 4. + if (chg == 0) { + return " 0"; + } + else if (abs(chg) > 3) { + String msg = "SDF format only supports charges from -3 to +3, not %g"; + throw IOException(str(format(msg) % chg)); + } + else { + Real chg_sdf = 4 - chg; + return str(format("%3.0f") % chg_sdf); + } + } private: std::ostream& ostr_; std::map<long, int>& atom_indices_; diff --git a/modules/io/tests/test_io_omf.py b/modules/io/tests/test_io_omf.py index 1ed14044a23919d8f9624254a7d270db2d815a8d..8d519c5084969316d2aeab844aeac45cdd89d8bd 100644 --- a/modules/io/tests/test_io_omf.py +++ b/modules/io/tests/test_io_omf.py @@ -248,7 +248,7 @@ class TestOMF(unittest.TestCase): if __name__== '__main__': from ost import testutils - if testutils.SetDefaultCompoundLib(): + if testutils.DefaultCompoundLibIsSet(): testutils.RunTests() else: print('No compound library available. Ignoring test_stereochemistry.py tests.') diff --git a/modules/io/tests/test_io_sdf.py b/modules/io/tests/test_io_sdf.py index 718ac0691192f24ddbc04c71d2ef71d15fa631cf..735158fc3c38f8ed2345415ae913143f8b7d1bf1 100644 --- a/modules/io/tests/test_io_sdf.py +++ b/modules/io/tests/test_io_sdf.py @@ -16,6 +16,28 @@ class TestSDF(unittest.TestCase): ent = io.LoadSDF('testfiles/sdf/6d5w_rank1_crlf.sdf.gz') self.assertEqual(len(ent.atoms), 21) self.assertEqual(len(ent.bonds), 24) + + def test_Charge(self): + ent = io.LoadSDF('testfiles/sdf/simple.sdf') + self.assertEqual(ent.FindAtom("00001_Simple Ligand", 1, "6").charge, 0) + + # Write and read charges properly + for chg in range(-3, 4): + ent.FindAtom("00001_Simple Ligand", 1, "6").charge = chg + sdf_str = io.EntityToSDFStr(ent) + ent = io.SDFStrToEntity(sdf_str) + self.assertEqual(ent.FindAtom("00001_Simple Ligand", 1, "6").charge, chg) + + # Only -3 to +3 is supported + # If M CHG is implemented the following tests can be removed + with self.assertRaises(Exception): + ent.FindAtom("00001_Simple Ligand", 1, "6").charge = 4 + io.EntityToSDFStr(ent) + + with self.assertRaises(Exception): + ent.FindAtom("00001_Simple Ligand", 1, "6").charge = -4 + io.EntityToSDFStr(ent) + if __name__== '__main__': from ost import testutils diff --git a/modules/io/tests/test_mmcif_info.cc b/modules/io/tests/test_mmcif_info.cc index 5370d2ce1eeb733bbba23b2ff52fadaf2cd22274..faf5cc10379e88b73db25e58be05bbf64a34ce6b 100644 --- a/modules/io/tests/test_mmcif_info.cc +++ b/modules/io/tests/test_mmcif_info.cc @@ -341,16 +341,23 @@ BOOST_AUTO_TEST_CASE(mmcif_info) mol::ResidueHandle res11 = editor.AppendResidue(ch1, "NAG"); mol::ResidueHandle res12 = editor.AppendResidue(ch1, "NAG"); // create AtomHandles for testing - mol::AtomHandle atom11 = editor.InsertAtom(res12, "C1",geom::Vec3()); - mol::AtomHandle atom12 = editor.InsertAtom(res11, "O4",geom::Vec3()); + mol::AtomHandle atom11 = editor.InsertAtom(res12, "C1", geom::Vec3()); + mol::AtomHandle atom12 = editor.InsertAtom(res11, "O4", geom::Vec3()); mol::ChainHandle ch2 = editor.InsertChain("B"); mol::ResidueHandle res21 = editor.AppendResidue(ch2, "BMA"); mol::ResidueHandle res22 = editor.AppendResidue(ch2, "MAN"); // create AtomHandles for testing - mol::AtomHandle atom21 = editor.InsertAtom(res22, "C1",geom::Vec3()); - mol::AtomHandle atom22 = editor.InsertAtom(res21, "O3",geom::Vec3()); + mol::AtomHandle atom21 = editor.InsertAtom(res22, "C1", geom::Vec3()); + mol::AtomHandle atom22 = editor.InsertAtom(res21, "O3", geom::Vec3()); + // create invalid AtomHandle pairs for testing + mol::AtomHandle atom_invalid; info.AddEntityBranchLink(ch1.GetName(), atom11, atom12, 1); info.AddEntityBranchLink(ch2.GetName(), atom21, atom22, 1); + /* Sometimes branched PDB entries link two atoms which are available in the + compound's definition but not resolved (missing) in the coordinates, e.g. + RCSB entry 7zim. Check that in case of invalid atom, no link is created. */ + info.AddEntityBranchLink(ch2.GetName(), atom11, atom_invalid, 1); + info.AddEntityBranchLink(ch2.GetName(), atom_invalid, atom12, 1); std::vector<MMCifInfoEntityBranchLink> blinks = info.GetEntityBranchLinks(); BOOST_CHECK(blinks.size() == 2); diff --git a/modules/io/tests/test_mmcif_reader.cc b/modules/io/tests/test_mmcif_reader.cc index 57fb3d3e0c629a0830f0450f3845b1702d7aef99..6d81b46033ab1143be596f7464020706a4645abd 100644 --- a/modules/io/tests/test_mmcif_reader.cc +++ b/modules/io/tests/test_mmcif_reader.cc @@ -414,6 +414,7 @@ BOOST_AUTO_TEST_CASE(mmcif_entity_poly_tests) BOOST_TEST_MESSAGE(" testing type recognition..."); { TestMMCifReaderProtected tmmcif_p("testfiles/mmcif/atom_site.mmcif", eh); + tmmcif_p.SetReadSeqRes(false); std::vector<StringRef> columns; // create corresponding entity entry @@ -1225,6 +1226,17 @@ BOOST_AUTO_TEST_CASE(mmcif_testreader) BOOST_CHECK(ch.IsValid()); BOOST_TEST_MESSAGE(" done."); + BOOST_TEST_MESSAGE(" testing chain/residue mapping properties..."); + BOOST_CHECK_EQUAL(ch.GetStringProp("pdb_auth_chain_name"), "A"); + BOOST_CHECK_EQUAL(ch.GetStringProp("entity_id"), "1"); + mol::ResidueHandle res = ch.FindResidue(12); + BOOST_CHECK(res.IsValid()); + BOOST_CHECK_EQUAL(res.GetStringProp("pdb_auth_chain_name"), "A"); + BOOST_CHECK_EQUAL(res.GetStringProp("pdb_auth_resnum"), "12"); + BOOST_CHECK_EQUAL(res.GetStringProp("pdb_auth_ins_code"), "?"); + BOOST_CHECK_EQUAL(res.GetStringProp("entity_id"), "1"); + BOOST_TEST_MESSAGE(" done."); + BOOST_TEST_MESSAGE(" testing numbering water..."); ch = eh.FindChain("O"); BOOST_CHECK(ch.IsValid()); @@ -1604,5 +1616,22 @@ BOOST_AUTO_TEST_CASE(mmcif_atom_site_B_iso_or_equiv_tests) BOOST_TEST_MESSAGE(" done."); } +BOOST_AUTO_TEST_CASE(mmcif_formal_charge) +{ + mol::EntityHandle eh = mol::CreateEntity(); + std::ifstream s("testfiles/mmcif/4C79_charged.cif"); + IOProfile profile; + MMCifReader mmcif_p(s, eh, profile); + mmcif_p.Parse(); + + BOOST_CHECK_EQUAL(eh.FindAtom("A", 49, "OE2").GetCharge(), -1); + BOOST_CHECK_EQUAL(eh.FindAtom("A", 49, "OE1").GetCharge(), 0); // '?' + BOOST_CHECK_EQUAL(eh.FindAtom("A", 49, "CA").GetCharge(), 0); // Explicit 0 + BOOST_CHECK_EQUAL(eh.FindAtom("A", 49, "CB").GetCharge(), 0); // '.' + BOOST_CHECK_EQUAL(eh.FindAtom("C", 1, "ZN").GetCharge(), 2); + BOOST_CHECK_EQUAL(eh.FindAtom("D", 1, "NA").GetCharge(), 1); + +} + BOOST_AUTO_TEST_SUITE_END(); diff --git a/modules/io/tests/testfiles/mmcif/4C79_charged.cif b/modules/io/tests/testfiles/mmcif/4C79_charged.cif new file mode 100644 index 0000000000000000000000000000000000000000..c5089608ff706068345942fb2240a2f7394c5a52 --- /dev/null +++ b/modules/io/tests/testfiles/mmcif/4C79_charged.cif @@ -0,0 +1,289 @@ +data_4C79 +# taken and modified from 4C79.cif +_entry.id 4C79 +# +loop_ +_entity.id +_entity.type +_entity.src_method +_entity.pdbx_description +_entity.formula_weight +_entity.pdbx_number_of_molecules +_entity.pdbx_ec +_entity.pdbx_mutation +_entity.pdbx_fragment +_entity.details +1 polymer man SMOOTHENED 22144.174 2 ? ? 'CYSTEINE-RICH DOMAIN (CRD), RESIDUES 28-210' ? +2 non-polymer syn 'ZINC ION' 65.409 1 ? ? ? ? +3 non-polymer syn 'SODIUM ION' 22.990 2 ? ? ? ? +4 water nat water 18.015 48 ? ? ? ? +# +_entity_poly.entity_id 1 +_entity_poly.type 'polypeptide(L)' +_entity_poly.nstd_linkage no +_entity_poly.nstd_monomer no +_entity_poly.pdbx_seq_one_letter_code +;MAVILHPNETIFNDFCKKSTTCEVLKYNTCLGSPLPYTHTSLILAEDSETQEEAFEKLAMWSGLRNAPRCWAVIQPLLCA +VYMPKCENGKVELPSQHLCQATRNPCSIVERERGWPNFLKCENKEQFPKGCQNEVQKLKFNTSGQCEAPLVKTDIQASWY +KDVEGCGIQCDNPLFTEDEHSDMHKLEHHHHHH +; +_entity_poly.pdbx_seq_one_letter_code_can +;MAVILHPNETIFNDFCKKSTTCEVLKYNTCLGSPLPYTHTSLILAEDSETQEEAFEKLAMWSGLRNAPRCWAVIQPLLCA +VYMPKCENGKVELPSQHLCQATRNPCSIVERERGWPNFLKCENKEQFPKGCQNEVQKLKFNTSGQCEAPLVKTDIQASWY +KDVEGCGIQCDNPLFTEDEHSDMHKLEHHHHHH +; +_entity_poly.pdbx_strand_id A +_entity_poly.pdbx_target_identifier ? +# +loop_ +_entity_poly_seq.entity_id +_entity_poly_seq.num +_entity_poly_seq.mon_id +_entity_poly_seq.hetero +1 1 MET n +1 2 ALA n +1 3 VAL n +1 4 ILE n +1 5 LEU n +1 6 HIS n +1 7 PRO n +1 8 ASN n +1 9 GLU n +1 10 THR n +1 11 ILE n +1 12 PHE n +1 13 ASN n +1 14 ASP n +1 15 PHE n +1 16 CYS n +1 17 LYS n +1 18 LYS n +1 19 SER n +1 20 THR n +1 21 THR n +1 22 CYS n +1 23 GLU n +1 24 VAL n +1 25 LEU n +1 26 LYS n +1 27 TYR n +1 28 ASN n +1 29 THR n +1 30 CYS n +1 31 LEU n +1 32 GLY n +1 33 SER n +1 34 PRO n +1 35 LEU n +1 36 PRO n +1 37 TYR n +1 38 THR n +1 39 HIS n +1 40 THR n +1 41 SER n +1 42 LEU n +1 43 ILE n +1 44 LEU n +1 45 ALA n +1 46 GLU n +1 47 ASP n +1 48 SER n +1 49 GLU n +1 50 THR n +1 51 GLN n +1 52 GLU n +1 53 GLU n +1 54 ALA n +1 55 PHE n +1 56 GLU n +1 57 LYS n +1 58 LEU n +1 59 ALA n +1 60 MET n +1 61 TRP n +1 62 SER n +1 63 GLY n +1 64 LEU n +1 65 ARG n +1 66 ASN n +1 67 ALA n +1 68 PRO n +1 69 ARG n +1 70 CYS n +1 71 TRP n +1 72 ALA n +1 73 VAL n +1 74 ILE n +1 75 GLN n +1 76 PRO n +1 77 LEU n +1 78 LEU n +1 79 CYS n +1 80 ALA n +1 81 VAL n +1 82 TYR n +1 83 MET n +1 84 PRO n +1 85 LYS n +1 86 CYS n +1 87 GLU n +1 88 ASN n +1 89 GLY n +1 90 LYS n +1 91 VAL n +1 92 GLU n +1 93 LEU n +1 94 PRO n +1 95 SER n +1 96 GLN n +1 97 HIS n +1 98 LEU n +1 99 CYS n +1 100 GLN n +1 101 ALA n +1 102 THR n +1 103 ARG n +1 104 ASN n +1 105 PRO n +1 106 CYS n +1 107 SER n +1 108 ILE n +1 109 VAL n +1 110 GLU n +1 111 ARG n +1 112 GLU n +1 113 ARG n +1 114 GLY n +1 115 TRP n +1 116 PRO n +1 117 ASN n +1 118 PHE n +1 119 LEU n +1 120 LYS n +1 121 CYS n +1 122 GLU n +1 123 ASN n +1 124 LYS n +1 125 GLU n +1 126 GLN n +1 127 PHE n +1 128 PRO n +1 129 LYS n +1 130 GLY n +1 131 CYS n +1 132 GLN n +1 133 ASN n +1 134 GLU n +1 135 VAL n +1 136 GLN n +1 137 LYS n +1 138 LEU n +1 139 LYS n +1 140 PHE n +1 141 ASN n +1 142 THR n +1 143 SER n +1 144 GLY n +1 145 GLN n +1 146 CYS n +1 147 GLU n +1 148 ALA n +1 149 PRO n +1 150 LEU n +1 151 VAL n +1 152 LYS n +1 153 THR n +1 154 ASP n +1 155 ILE n +1 156 GLN n +1 157 ALA n +1 158 SER n +1 159 TRP n +1 160 TYR n +1 161 LYS n +1 162 ASP n +1 163 VAL n +1 164 GLU n +1 165 GLY n +1 166 CYS n +1 167 GLY n +1 168 ILE n +1 169 GLN n +1 170 CYS n +1 171 ASP n +1 172 ASN n +1 173 PRO n +1 174 LEU n +1 175 PHE n +1 176 THR n +1 177 GLU n +1 178 ASP n +1 179 GLU n +1 180 HIS n +1 181 SER n +1 182 ASP n +1 183 MET n +1 184 HIS n +1 185 LYS n +1 186 LEU n +1 187 GLU n +1 188 HIS n +1 189 HIS n +1 190 HIS n +1 191 HIS n +1 192 HIS n +1 193 HIS n +# +_struct.entry_id 4C79 +_struct.title 'Crystal structure of the Smoothened CRD, native' +_struct.pdbx_descriptor SMOOTHENED +_struct.pdbx_model_details ? +_struct.pdbx_CASP_flag ? +_struct.pdbx_model_type_details ? +# +_struct_keywords.entry_id 4C79 +_struct_keywords.pdbx_keywords 'SIGNALING PROTEIN' +_struct_keywords.text 'SIGNALING PROTEIN' +# +loop_ +_atom_site.group_PDB +_atom_site.id +_atom_site.type_symbol +_atom_site.label_atom_id +_atom_site.label_alt_id +_atom_site.label_comp_id +_atom_site.label_asym_id +_atom_site.label_entity_id +_atom_site.label_seq_id +_atom_site.pdbx_PDB_ins_code +_atom_site.Cartn_x +_atom_site.Cartn_y +_atom_site.Cartn_z +_atom_site.occupancy +_atom_site.B_iso_or_equiv +_atom_site.pdbx_formal_charge +_atom_site.auth_seq_id +_atom_site.auth_comp_id +_atom_site.auth_asym_id +_atom_site.auth_atom_id +_atom_site.pdbx_PDB_model_num +ATOM 262 N N . GLU A 1 49 ? -25.812 5.207 -4.954 1.00 35.58 ? 75 GLU A N 1 +ATOM 263 C CA . GLU A 1 49 ? -26.815 4.149 -4.979 1.00 36.58 0 75 GLU A CA 1 +ATOM 264 C C . GLU A 1 49 ? -27.600 4.155 -6.288 1.00 35.91 ? 75 GLU A C 1 +ATOM 265 O O . GLU A 1 49 ? -28.206 3.150 -6.663 1.00 35.94 ? 75 GLU A O 1 +ATOM 266 C CB . GLU A 1 49 ? -27.774 4.284 -3.794 1.00 38.91 . 75 GLU A CB 1 +ATOM 267 C CG . GLU A 1 49 ? -27.114 4.163 -2.426 1.00 40.64 ? 75 GLU A CG 1 +ATOM 268 C CD . GLU A 1 49 ? -26.931 2.725 -1.965 1.00 42.04 ? 75 GLU A CD 1 +ATOM 269 O OE1 . GLU A 1 49 ? -26.905 1.807 -2.812 1.00 42.07 ? 75 GLU A OE1 1 +ATOM 270 O OE2 . GLU A 1 49 ? -26.811 2.516 -0.740 1.00 42.97 -1 75 GLU A OE2 1 +HETATM 1881 ZN ZN . ZN C 2 . ? -29.714 -4.622 -22.478 1.00 31.76 2 1159 ZN A ZN 1 +HETATM 1882 NA NA . NA D 3 . ? -23.050 1.721 -4.584 1.00 26.51 1 1160 NA A NA 1 +# +loop_ +_pdbx_entity_nonpoly.entity_id +_pdbx_entity_nonpoly.name +_pdbx_entity_nonpoly.comp_id +2 'ZINC ION' ZN +3 'SODIUM ION' NA +4 water HOH +# diff --git a/modules/mol/alg/doc/lddt_deprecated.rst b/modules/mol/alg/doc/lddt_deprecated.rst index 4293046fd7e9359808d394a815366b1696277453..173bcb5ed5b3d8adbdc5abec02df9f2bc580b765 100644 --- a/modules/mol/alg/doc/lddt_deprecated.rst +++ b/modules/mol/alg/doc/lddt_deprecated.rst @@ -3,6 +3,11 @@ lDDT (deprecated) ================================================================================ +.. warning:: + + These functions in `ost.mol.alg` are deprecated. Consider using the newer + implementation in :class:`ost.mol.alg.lDDTScorer` instead. + .. function:: LocalDistDiffTest(model, distance_list, tolerance_list, \ sequence_separation=0, \ local_lddt_property_string="") diff --git a/modules/mol/alg/doc/molalg.rst b/modules/mol/alg/doc/molalg.rst index bf451df52b20b045f3a0a1e5e82d793cd537847b..3a43ea5de90b8b5bf14393c15754b09b6e2d37d4 100644 --- a/modules/mol/alg/doc/molalg.rst +++ b/modules/mol/alg/doc/molalg.rst @@ -7,14 +7,14 @@ Local Distance Test scores (lDDT, DRMSD) -------------------------------------------------------------------------------- -.. warning:: +.. note:: - The code that comes with + This is a new implementation of lDDT, introduced in OpenStructure 2.4 with + focus on supporting quaternary structure and compounds beyond the 20 + standard proteinogenic amino acids. + The :doc:`previous lDDT code <lddt_deprecated>` that comes with `Mariani et al. <https://dx.doi.org/10.1093/bioinformatics/btt473>`_ is - considered deprecated. lDDT has been re-implemented with a focus on - supporting quaternary structure and compounds beyond the 20 standard - proteinogenic amino acids. The old code is still available and documented - :doc:`here <lddt_deprecated>`. + considered deprecated. .. note:: @@ -135,16 +135,16 @@ Local Distance Test scores (lDDT, DRMSD) .. currentmodule:: ost.mol.alg -:mod:`qsscoring <ost.mol.alg.qsscore>` -- QS score implementation +:mod:`qsscore <ost.mol.alg.qsscore>` -- New QS score implementation -------------------------------------------------------------------------------- -.. warning:: +.. note:: - The code that comes with + This is a new implementation of QS Score, introduced in OpenStructure 2.4 and + tightly integrated with the chain mapping algorithms. + The :doc:`previous qsscoring code <qsscoring_deprecated>` that comes with `Bertoni et al. <https://www.nature.com/articles/s41598-017-09654-8>`_ is - considered deprecated. QS score has been re-implemented to be tightly - integrated with the chain mapping algorithms. The old code is still available - and documented :doc:`here <qsscoring_deprecated>`. + considered deprecated. .. automodule:: ost.mol.alg.qsscore :members: diff --git a/modules/mol/alg/pymod/chain_mapping.py b/modules/mol/alg/pymod/chain_mapping.py index 85983ed88d08ffe4ceab26136da0c90928562103..25a19259a2de600490e74a89f292246ed15e76e3 100644 --- a/modules/mol/alg/pymod/chain_mapping.py +++ b/modules/mol/alg/pymod/chain_mapping.py @@ -1228,10 +1228,30 @@ class ChainMapper: return MappingResult(self.target, mdl, self.chem_groups, chem_mapping, final_mapping, alns) + def GetMapping(self, model, n_max_naive = 12): + """ Convenience function to get mapping with currently preferred method + + If number of chains in model and target are <= *n_max_naive*, a naive + QS-score mapping is performed. For anything else, a QS-score mapping + with the greedy_block strategy is performed (steep_opt_rate = 3, + block_seed_size = 5, block_blocks_per_chem_group = 6). + """ + n_trg_chains = len(self.target.chains) + res = self.GetChemMapping(model) + n_mdl_chains = len(res[2].chains) + if n_trg_chains <= n_max_naive and n_mdl_chains <= n_max_naive: + return self.GetQSScoreMapping(model, strategy="naive", + chem_mapping_result=res) + else: + return self.GetQSScoreMapping(model, strategy="greedy_block", + steep_opt_rate=3, block_seed_size=5, + block_blocks_per_chem_group=6, + chem_mapping_result=res) def GetRepr(self, substructure, model, topn=1, inclusion_radius=15.0, thresholds=[0.5, 1.0, 2.0, 4.0], bb_only=False, - only_interchain=False, chem_mapping_result = None): + only_interchain=False, chem_mapping_result = None, + global_mapping = None): """ Identify *topn* representations of *substructure* in *model* *substructure* defines a subset of :attr:`~target` for which one @@ -1267,12 +1287,32 @@ class ChainMapper: *model*. If set, *model* parameter is not used. :type chem_mapping_result: :class:`tuple` + :param global_mapping: Pro param. Specify a global mapping result. This + fully defines the desired representation in the + model but extracts it and enriches it with all + the nice attributes of :class:`ReprResult`. + The target attribute in *global_mapping* must be + of the same entity as self.target and the model + attribute of *global_mapping* must be of the same + entity as *model*. + :type global_mapping: :class:`MappingResult` :returns: :class:`list` of :class:`ReprResult` """ if topn < 1: raise RuntimeError("topn must be >= 1") + if global_mapping is not None: + # ensure that this mapping is derived from the same structures + if global_mapping.target.handle.GetHashCode() != \ + self.target.handle.GetHashCode(): + raise RuntimeError("global_mapping.target must be the same " + "entity as self.target") + if global_mapping.model.handle.GetHashCode() != \ + model.handle.GetHashCode(): + raise RuntimeError("global_mapping.model must be the same " + "entity as model param") + # check whether substructure really is a subset of self.target for r in substructure.residues: ch_name = r.GetChain().GetName() @@ -1377,9 +1417,31 @@ class ChainMapper: inclusion_radius = inclusion_radius, bb_only = bb_only) scored_mappings = list() - for mapping in _ChainMappings(substructure_chem_groups, - substructure_chem_mapping, - self.n_max_naive): + + if global_mapping: + # construct mapping of substructure from global mapping + flat_mapping = global_mapping.GetFlatMapping() + mapping = list() + for chem_group, chem_mapping in zip(substructure_chem_groups, + substructure_chem_mapping): + chem_group_mapping = list() + for ch in chem_group: + if ch in flat_mapping: + mdl_ch = flat_mapping[ch] + if mdl_ch in chem_mapping: + chem_group_mapping.append(mdl_ch) + else: + chem_group_mapping.append(None) + else: + chem_group_mapping.append(None) + mapping.append(chem_group_mapping) + mappings = [mapping] + else: + mappings = list(_ChainMappings(substructure_chem_groups, + substructure_chem_mapping, + self.n_max_naive)) + + for mapping in mappings: # chain_mapping and alns as input for lDDT computation lddt_chain_mapping = dict() lddt_alns = dict() diff --git a/modules/mol/alg/pymod/ligand_scoring.py b/modules/mol/alg/pymod/ligand_scoring.py index 15ca94afda9dbdb2e7f5327b3d4c37c23d374433..4b1c57d4096c1d67a620b90f5b638c0b0fe2410e 100644 --- a/modules/mol/alg/pymod/ligand_scoring.py +++ b/modules/mol/alg/pymod/ligand_scoring.py @@ -23,7 +23,7 @@ class LigandScorer: * lDDT-PLI, that looks at the conservation of protein-ligand contacts with :class:`lDDT <ost.mol.alg.lddt.lDDTScorer>`. * Binding-site superposed, symmetry-corrected RMSD that assesses the - accuracy of the ligand pose. + accuracy of the ligand pose (BiSyRMSD, hereinafter referred to as RMSD). Both scores involve local chain mapping of the reference binding site onto the model, symmetry-correction, and finally assignment (mapping) @@ -31,11 +31,33 @@ class LigandScorer: Results are available as matrices (`(lddt_pli|rmsd)_matrix`), where every target-model score is reported in a matrix; as `(lddt_pli|rmsd)` where - a model-target assignment has been determined, starting from the "best" - possible mapping and using each target and model ligand in a single - assignment, and the results are reported in a dictionary; and as - (`(lddt_pli|rmsd)_details`) methods, which report additional details - about different aspects of the scoring such as chain mapping. + a model-target assignment has been determined (see below) and reported in + a dictionary; and as (`(lddt_pli|rmsd)_details`) methods, which report + additional details about different aspects of the scoring such as chain + mapping. + + The behavior of chain mapping and ligand assignment can be controlled + with the `global_chain_mapping` and `rmsd_assignment` arguments. + + By default, chain mapping is performed locally, ie. only within the + binding site. As a result, different ligand scores can correspond to + different chain mappings. This tends to produce more favorable scores, + especially in large, partially regular oligomeric complexes. + Setting `global_chain_mapping=True` enforces a single global chain mapping, + as per :meth:`ost.mol.alg.chain_mapping.ChainMapper.GetMapping`. + Note that this global chain mapping currently ignores non polymer entities + such as small ligands, and may result in overly pessimistic scores. + + By defaults, target-model ligand assignments are computed independently + for the RMSD and lDDT-PLI scores. For RMSD, each model ligand is uniquely + assigned to a target ligand, starting from the "best" possible mapping + (lowest RMSD) and using each target and model ligand in a single + assignment. Ties are resolved by best (highest) lDDT-PLI. Similarly, + for lDDT-PLI, the assignment is based on the highest lDDT-PLI, and ties + broken by lowest RMSD. Setting `rmsd_assignment=True` forces a single + ligand assignment, based on RMSD only. Ties are broken arbitrarily. + + Assumptions: The class generally assumes that the :attr:`~ost.mol.ResidueHandle.is_ligand` property is properly set on all @@ -89,7 +111,7 @@ class LigandScorer: # Ligand model as SDF file model_ligand = io.LoadEntity("path_to_ligand.sdf", format="sdf") # Target loaded from mmCIF, containing the ligand - target, _ = io.LoadMMCIF("path_to_target.cif", seqres=True) + target = io.LoadMMCIF("path_to_target.cif") # Cleanup a copy of the structures cleaned_model = model.Copy() @@ -170,18 +192,44 @@ class LigandScorer: :type radius: :class:`float` :param lddt_pli_radius: lDDT inclusion radius for lDDT-PLI. :type lddt_pli_radius: :class:`float` - :param lddt_bs_radius: lDDT inclusion radius for lDDT-BS. - :type lddt_bs_radius: :class:`float` + :param lddt_lp_radius: lDDT inclusion radius for lDDT-LP. + :type lddt_lp_radius: :class:`float` :param binding_sites_topn: maximum number of target binding site representations to assess, per target ligand. + Ignored if `global_chain_mapping` is True. :type binding_sites_topn: :class:`int` + :param global_chain_mapping: set to True to use a global chain mapping for + the polymer (protein, nucleotide) chains. + Defaults to False, in which case only local + chain mappings are allowed (where different + ligand may be scored against different chain + mappings). + :type global_chain_mapping: :class:`bool` + :param custom_mapping: Provide custom chain mapping between *model* and + *target* that is used as global chain mapping. + Dictionary with target chain names as key and model + chain names as value. Only has an effect if + *global_chain_mapping* is True. + :type custom_mapping: :class:`dict` + :param rmsd_assignment: assign ligands based on RMSD only. The default + (False) is to use a combination of lDDT-PLI and + RMSD for the assignment. + :type rmsd_assignment: :class:`bool` + :param n_max_naive: Parameter for global chain mapping. If *model* and + *target* have less or equal that number of chains, + the full + mapping solution space is enumerated to find the + the optimum. A heuristic is used otherwise. + :type n_max_naive: :class:`int` """ def __init__(self, model, target, model_ligands=None, target_ligands=None, resnum_alignments=False, check_resnames=True, rename_ligand_chain=False, chain_mapper=None, substructure_match=False, - radius=4.0, lddt_pli_radius=6.0, lddt_bs_radius=10.0, - binding_sites_topn=100000): + radius=4.0, lddt_pli_radius=6.0, lddt_lp_radius=10.0, + binding_sites_topn=100000, global_chain_mapping=False, + rmsd_assignment=False, n_max_naive=12, + custom_mapping=None): if isinstance(model, mol.EntityView): self.model = mol.CreateEntityFromView(model, False) @@ -224,8 +272,11 @@ class LigandScorer: self.substructure_match = substructure_match self.radius = radius self.lddt_pli_radius = lddt_pli_radius - self.lddt_bs_radius = lddt_bs_radius + self.lddt_lp_radius = lddt_lp_radius self.binding_sites_topn = binding_sites_topn + self.global_chain_mapping = global_chain_mapping + self.rmsd_assignment = rmsd_assignment + self.n_max_naive = n_max_naive # scoring matrices self._rmsd_matrix = None @@ -241,6 +292,10 @@ class LigandScorer: # lazily precomputed variables self._binding_sites = {} + self.__model_mapping = None + + if custom_mapping is not None: + self._set_custom_mapping(custom_mapping) @property def chain_mapper(self): @@ -254,6 +309,14 @@ class LigandScorer: resnum_alignments=self.resnum_alignments) return self._chain_mapper + @property + def _model_mapping(self): + """Get the global chain mapping for the model.""" + if self.__model_mapping is None: + self.__model_mapping = self.chain_mapper.GetMapping(self.model, + n_max_naive=self.n_max_naive) + return self.__model_mapping + @staticmethod def _extract_ligands(entity): """Extract ligands from entity. Return a list of residues. @@ -415,9 +478,14 @@ class LigandScorer: ref_bs.AddResidue(r, mol.ViewAddFlag.INCLUDE_ALL) # Find the representations - self._binding_sites[ligand.hash_code] = self.chain_mapper.GetRepr( - ref_bs, self.model, inclusion_radius=self.lddt_bs_radius, - topn=self.binding_sites_topn) + if self.global_chain_mapping: + self._binding_sites[ligand.hash_code] = self.chain_mapper.GetRepr( + ref_bs, self.model, inclusion_radius=self.lddt_lp_radius, + global_mapping = self._model_mapping) + else: + self._binding_sites[ligand.hash_code] = self.chain_mapper.GetRepr( + ref_bs, self.model, inclusion_radius=self.lddt_lp_radius, + topn=self.binding_sites_topn) return self._binding_sites[ligand.hash_code] @staticmethod @@ -530,7 +598,7 @@ class LigandScorer: rmsd_full_matrix[target_i, model_i]["rmsd"] > rmsd: rmsd_full_matrix[target_i, model_i] = { "rmsd": rmsd, - "lddt_bs": binding_site.lDDT, + "lddt_lp": binding_site.lDDT, "bs_ref_res": binding_site.substructure.residues, "bs_ref_res_mapped": binding_site.ref_residues, "bs_mdl_res_mapped": binding_site.mdl_residues, @@ -590,7 +658,7 @@ class LigandScorer: lddt_pli_full_matrix[target_i, model_i] = { "lddt_pli": global_lddt, "rmsd": rmsd, - "lddt_bs": binding_site.lDDT, + "lddt_lp": binding_site.lDDT, "lddt_pli_n_contacts": lddt_tot, "bs_ref_res": binding_site.substructure.residues, "bs_ref_res_mapped": binding_site.ref_residues, @@ -609,15 +677,21 @@ class LigandScorer: self._lddt_pli_full_matrix = lddt_pli_full_matrix @staticmethod - def _find_ligand_assignment(mat1, mat2): - """ Find the ligand assignment based on mat1 + def _find_ligand_assignment(mat1, mat2=None): + """ Find the ligand assignment based on mat1. If mat2 is provided, it + will be used to break ties in mat1. If mat2 is not provided, ties will + be resolved by taking the first match arbitrarily. Both mat1 and mat2 should "look" like RMSD - ie be between inf (bad) and 0 (good). """ # We will modify mat1 and mat2, so make copies of it first mat1 = np.copy(mat1) - mat2 = np.copy(mat2) + if mat2 is None: + mat2 = np.copy(mat1) + mat2[~np.isnan(mat2)] = np.inf + else: + mat2 = np.copy(mat1) assignments = [] min_mat1 = LigandScorer._nanmin_nowarn(mat1) while not np.isnan(min_mat1): @@ -627,6 +701,7 @@ class LigandScorer: # Get the values of mat2 at these positions best_mat2_match = [mat2[tuple(x)] for x in best_mat1] # Find the index of the best mat2 + # Note: argmin returns the first value which is min. best_mat2_idx = np.array(best_mat2_match).argmin() # Now get the original indices max_i_trg, max_i_mdl = best_mat1[best_mat2_idx] @@ -667,7 +742,7 @@ class LigandScorer: def _assign_ligands_rmsd(self): """Assign (map) ligands between model and target. - Sets self._rmsd, self._rmsd_assignment and self._rmsd_details. + Sets self._rmsd and self._rmsd_details. """ mat2 = self._reverse_lddt(self.lddt_pli_matrix) @@ -722,8 +797,73 @@ class LigandScorer: trg_idx, mdl_idx] return out_main, out_details + def _assign_matrix(self, mat, data1, main_key1, data2, main_key2): + """ + Perform the ligand assignment, ie find the mapping between model and + target ligands, based on a single matrix + + The algorithm starts by assigning the "best" mapping, and then discards + the target and model ligands (row, column) so that every model ligand + can be assigned to a single target ligand, and every target ligand + is only assigned to a single model ligand. Repeat until there is + nothing left to assign. + + This algorithm doesn't guarantee a globally optimal assignment. + + `mat` should contain values between 0 and infinity, + with lower values representing better scores. Use the + :meth:`_reverse_lddt` method to convert lDDT values to such a score. + + :param mat: the ligand assignment criteria (RMSD or lDDT-PLI) + :param data1: the first data (either self._rmsd_full_matrix or self._lddt_pli_matrix) + :param main_key1: the first key of data (dictionnaries within `data`) to + assign into out_main. + :param data2: the second data (either self._rmsd_full_matrix or self._lddt_pli_matrix) + :param main_key2: the second key of data (dictionnaries within `data`) to + assign into out_main. + :return: a tuple with 4 dictionaries of matrices containing the main + data1, details1, main data2 and details2, respectively. + """ + assignments = self._find_ligand_assignment(mat) + out_main1 = {} + out_details1 = {} + out_main2 = {} + out_details2 = {} + for assignment in assignments: + trg_idx, mdl_idx = assignment + mdl_lig = self.model_ligands[mdl_idx] + mdl_cname = mdl_lig.chain.name + mdl_resnum = mdl_lig.number + # Data 1 + if mdl_cname not in out_main1: + out_main1[mdl_cname] = {} + out_details1[mdl_cname] = {} + out_main1[mdl_cname][mdl_resnum] = data1[ + trg_idx, mdl_idx][main_key1] + out_details1[mdl_cname][mdl_resnum] = data1[ + trg_idx, mdl_idx] + out_main1[mdl_cname][mdl_resnum] = data2[ + trg_idx, mdl_idx][main_key2] + out_details1[mdl_cname][mdl_resnum] = data2[ + trg_idx, mdl_idx] + # Data2 + if mdl_cname not in out_main2: + out_main2[mdl_cname] = {} + out_details2[mdl_cname] = {} + out_main2[mdl_cname][mdl_resnum] = data1[ + trg_idx, mdl_idx][main_key1] + out_details2[mdl_cname][mdl_resnum] = data1[ + trg_idx, mdl_idx] + out_main2[mdl_cname][mdl_resnum] = data2[ + trg_idx, mdl_idx][main_key2] + out_details2[mdl_cname][mdl_resnum] = data2[ + trg_idx, mdl_idx] + return out_main1, out_details1, out_main2, out_details2 + def _assign_ligands_lddt_pli(self): """ Assign ligands based on lDDT-PLI. + + Sets self._lddt_pli and self._lddt_pli_details. """ mat1 = self._reverse_lddt(self.lddt_pli_matrix) @@ -734,6 +874,22 @@ class LigandScorer: self._lddt_pli = mat_tuple[0] self._lddt_pli_details = mat_tuple[1] + def _assign_ligands_rmsd_only(self): + """Assign (map) ligands between model and target based on RMSD only. + + Sets self._rmsd, self._rmsd_details, self._lddt_pli and + self._lddt_pli_details. + """ + mat_tuple = self._assign_matrix(self.rmsd_matrix, + self._rmsd_full_matrix, + "rmsd", + self._lddt_pli_full_matrix, + "lddt_pli") + self._rmsd = mat_tuple[0] + self._rmsd_details = mat_tuple[1] + self._lddt_pli = mat_tuple[2] + self._lddt_pli_details = mat_tuple[3] + @property def rmsd_matrix(self): """ Get the matrix of RMSD values. @@ -788,7 +944,10 @@ class LigandScorer: :rtype: :class:`dict` """ if self._rmsd is None: - self._assign_ligands_rmsd() + if self.rmsd_assignment: + self._assign_ligands_rmsd_only() + else: + self._assign_ligands_rmsd() return self._rmsd @property @@ -799,7 +958,7 @@ class LigandScorer: Each sub-dictionary contains the following information: * `rmsd`: the RMSD score value. - * `lddt_bs`: the lDDT-BS score of the binding site. + * `lddt_lp`: the lDDT score of the ligand pocket (lDDT-LP). * `bs_ref_res`: a list of residues (:class:`~ost.mol.ResidueHandle`) that define the binding site in the reference. * `bs_ref_res_mapped`: a list of residues @@ -830,7 +989,10 @@ class LigandScorer: :rtype: :class:`dict` """ if self._rmsd_details is None: - self._assign_ligands_rmsd() + if self.rmsd_assignment: + self._assign_ligands_rmsd_only() + else: + self._assign_ligands_rmsd() return self._rmsd_details @property @@ -841,7 +1003,10 @@ class LigandScorer: :rtype: :class:`dict` """ if self._lddt_pli is None: - self._assign_ligands_lddt_pli() + if self.rmsd_assignment: + self._assign_ligands_rmsd_only() + else: + self._assign_ligands_lddt_pli() return self._lddt_pli @property @@ -856,7 +1021,7 @@ class LigandScorer: chain mapping and assignment. This may differ from the RMSD-based assignment. Note that a different isomorphism than `lddt_pli` may be used. - * `lddt_bs`: the lDDT-BS score of the binding site. + * `lddt_lp`: the lDDT score of the ligand pocket (lDDT-LP). * `lddt_pli_n_contacts`: number of total contacts used in lDDT-PLI, summed over all thresholds. Can be divided by 8 to obtain the number of atomic contacts. @@ -892,10 +1057,106 @@ class LigandScorer: :rtype: :class:`dict` """ if self._lddt_pli_details is None: - self._assign_ligands_lddt_pli() + if self.rmsd_assignment: + self._assign_ligands_rmsd_only() + else: + self._assign_ligands_lddt_pli() return self._lddt_pli_details + def _set_custom_mapping(self, mapping): + """ sets self.__model_mapping with a full blown MappingResult object + + :param mapping: mapping with trg chains as key and mdl ch as values + :type mapping: :class:`dict` + """ + chain_mapper = self.chain_mapper + chem_mapping, chem_group_alns, mdl = \ + chain_mapper.GetChemMapping(self.model) + + # now that we have a chem mapping, lets do consistency checks + # - check whether chain names are unique and available in structures + # - check whether the mapped chains actually map to the same chem groups + if len(mapping) != len(set(mapping.keys())): + raise RuntimeError(f"Expect unique trg chain names in mapping. Got " + f"{mapping.keys()}") + if len(mapping) != len(set(mapping.values())): + raise RuntimeError(f"Expect unique mdl chain names in mapping. Got " + f"{mapping.values()}") + + trg_chains = set([ch.GetName() for ch in chain_mapper.target.chains]) + mdl_chains = set([ch.GetName() for ch in mdl.chains]) + for k,v in mapping.items(): + if k not in trg_chains: + raise RuntimeError(f"Target chain \"{k}\" is not available " + f"in target processed for chain mapping " + f"({trg_chains})") + if v not in mdl_chains: + raise RuntimeError(f"Model chain \"{v}\" is not available " + f"in model processed for chain mapping " + f"({mdl_chains})") + + for trg_ch, mdl_ch in mapping.items(): + trg_group_idx = None + mdl_group_idx = None + for idx, group in enumerate(chain_mapper.chem_groups): + if trg_ch in group: + trg_group_idx = idx + break + for idx, group in enumerate(chem_mapping): + if mdl_ch in group: + mdl_group_idx = idx + break + if trg_group_idx is None or mdl_group_idx is None: + raise RuntimeError("Could not establish a valid chem grouping " + "of chain names provided in custom mapping.") + + if trg_group_idx != mdl_group_idx: + raise RuntimeError(f"Chem group mismatch in custom mapping: " + f"target chain \"{trg_ch}\" groups with the " + f"following chemically equivalent target " + f"chains: " + f"{chain_mapper.chem_groups[trg_group_idx]} " + f"but model chain \"{mdl_ch}\" maps to the " + f"following target chains: " + f"{chain_mapper.chem_groups[mdl_group_idx]}") + + pairs = set([(trg_ch, mdl_ch) for trg_ch, mdl_ch in mapping.items()]) + ref_mdl_alns = \ + chain_mapping._GetRefMdlAlns(chain_mapper.chem_groups, + chain_mapper.chem_group_alignments, + chem_mapping, + chem_group_alns, + pairs = pairs) + + # translate mapping format + final_mapping = list() + for ref_chains in chain_mapper.chem_groups: + mapped_mdl_chains = list() + for ref_ch in ref_chains: + if ref_ch in mapping: + mapped_mdl_chains.append(mapping[ref_ch]) + else: + mapped_mdl_chains.append(None) + final_mapping.append(mapped_mdl_chains) + + alns = dict() + for ref_group, mdl_group in zip(chain_mapper.chem_groups, + final_mapping): + for ref_ch, mdl_ch in zip(ref_group, mdl_group): + if ref_ch is not None and mdl_ch is not None: + aln = ref_mdl_alns[(ref_ch, mdl_ch)] + trg_view = chain_mapper.target.Select(f"cname={ref_ch}") + mdl_view = mdl.Select(f"cname={mdl_ch}") + aln.AttachView(0, trg_view) + aln.AttachView(1, mdl_view) + alns[(ref_ch, mdl_ch)] = aln + + self.__model_mapping = chain_mapping.MappingResult(chain_mapper.target, mdl, + chain_mapper.chem_groups, + chem_mapping, + final_mapping, alns) + def _ResidueToGraph(residue, by_atom_index=False): """Return a NetworkX graph representation of the residue. diff --git a/modules/mol/alg/pymod/qsscoring.py b/modules/mol/alg/pymod/qsscoring.py index b47aa4e76e1544ba28caa18482253833b57106c9..8928646b98283584f2f5e5b82a0b4b8e0b49715d 100644 --- a/modules/mol/alg/pymod/qsscoring.py +++ b/modules/mol/alg/pymod/qsscoring.py @@ -2,6 +2,11 @@ Scoring of quaternary structures (QS). The QS scoring is according to the paper by `Bertoni et al. <https://dx.doi.org/10.1038/s41598-017-09654-8>`_. +.. warning:: + + The `qsscoring` module is deprecated. Consider using the newer implementation + in :mod:`~ost.mol.alg.qsscore` instead. + .. note :: Requirements for use: diff --git a/modules/mol/alg/pymod/scoring.py b/modules/mol/alg/pymod/scoring.py index 97423299d0702509af5fc292a46f651b34ebb089..3ccecfd23860e862eb9818d327baca2d683ced26 100644 --- a/modules/mol/alg/pymod/scoring.py +++ b/modules/mol/alg/pymod/scoring.py @@ -13,7 +13,9 @@ from ost.mol.alg import dockq from ost.mol.alg.lddt import lDDTScorer from ost.mol.alg.qsscore import QSScorer from ost.mol.alg import Molck, MolckSettings +from ost import bindings from ost.bindings import cadscore +from ost.bindings import tmtools import numpy as np class lDDTBSScorer: @@ -109,12 +111,6 @@ class Scorer: colored to True in :class:`ost.mol.alg.MolckSettings` constructor. :type molck_settings: :class:`ost.mol.alg.MolckSettings` - :param naive_chain_mapping_thresh: Chain mappings for targets/models up to - that number of chains will be fully - enumerated to optimize for QS-score. - Everything above is treated with a - heuristic. - :type naive_chain_mapping_thresh: :class:`int` :param cad_score_exec: Explicit path to voronota-cadscore executable from voronota installation from https://github.com/kliment-olechnovic/voronota. If @@ -125,10 +121,23 @@ class Scorer: *target*. Dictionary with target chain names as key and model chain names as value. :type custom_mapping: :class:`dict` + :param usalign_exec: Explicit path to USalign executable used to compute + TM-score. If not given, TM-score will be computed + with OpenStructure internal copy of USalign code. + :type usalign_exec: :class:`str` + :param lddt_no_stereochecks: Whether to compute lDDT without stereochemistry + checks + :type lddt_no_stereochecks: :class:`bool` + :param n_max_naive: Parameter for chain mapping. If *model* and *target* + have less or equal that number of chains, the full + mapping solution space is enumerated to find the + the optimum. A heuristic is used otherwise. + :type n_max_naive: :class:`int` """ def __init__(self, model, target, resnum_alignments=False, - molck_settings = None, naive_chain_mapping_thresh=12, - cad_score_exec = None, custom_mapping=None): + molck_settings = None, cad_score_exec = None, + custom_mapping=None, usalign_exec = None, + lddt_no_stereochecks=False, n_max_naive=12): if isinstance(model, mol.EntityView): model = mol.CreateEntityFromView(model, False) @@ -193,8 +202,10 @@ class Scorer: "resnum_alignments are enabled") self.resnum_alignments = resnum_alignments - self.naive_chain_mapping_thresh = naive_chain_mapping_thresh self.cad_score_exec = cad_score_exec + self.usalign_exec = usalign_exec + self.lddt_no_stereochecks = lddt_no_stereochecks + self.n_max_naive = n_max_naive # lazily evaluated attributes self._stereochecked_model = None @@ -214,11 +225,14 @@ class Scorer: # lazily constructed scorer objects self._lddt_scorer = None + self._bb_lddt_scorer = None self._qs_scorer = None # lazily computed scores self._lddt = None self._local_lddt = None + self._bb_lddt = None + self._bb_local_lddt = None self._qs_global = None self._qs_best = None @@ -255,6 +269,9 @@ class Scorer: self._patch_qs = None self._patch_dockq = None + self._tm_score = None + self._usalign_mapping = None + if custom_mapping is not None: self._set_custom_mapping(custom_mapping) @@ -408,22 +425,9 @@ class Scorer: :type: :class:`ost.mol.alg.chain_mapping.MappingResult` """ if self._mapping is None: - n_trg_chains = len(self.chain_mapper.target.chains) - res = self.chain_mapper.GetChemMapping(self.model) - n_mdl_chains = len(res[2].chains) - thresh = self.naive_chain_mapping_thresh - if n_trg_chains <= thresh and n_mdl_chains <= thresh: - m = self.chain_mapper.GetQSScoreMapping(self.model, - strategy="naive", - chem_mapping_result=res) - else: - m = self.chain_mapper.GetQSScoreMapping(self.model, - strategy="greedy_block", - steep_opt_rate=3, - block_seed_size=5, - block_blocks_per_chem_group=6, - chem_mapping_result=res) - self._mapping = m + self._mapping = \ + self.chain_mapper.GetMapping(self.model, + n_max_naive = self.n_max_naive) return self._mapping @property @@ -460,9 +464,25 @@ class Scorer: :type: :class:`ost.mol.alg.lddt.lDDTScorer` """ if self._lddt_scorer is None: - self._lddt_scorer = lDDTScorer(self.stereochecked_target) + if self.lddt_no_stereochecks: + self._lddt_scorer = lDDTScorer(self.target) + else: + self._lddt_scorer = lDDTScorer(self.stereochecked_target) return self._lddt_scorer + @property + def bb_lddt_scorer(self): + """ Backbone only lDDT scorer for :attr:`~target` + + No stereochecks applied for bb only lDDT which considers CA atoms + for peptides and C3' atoms for nucleotides. + + :type: :class:`ost.mol.alg.lddt.lDDTScorer` + """ + if self._bb_lddt_scorer is None: + self._bb_lddt_scorer = lDDTScorer(self.target, bb_only=True) + return self._bb_lddt_scorer + @property def qs_scorer(self): """ QS scorer constructed from :attr:`~mapping` @@ -506,6 +526,36 @@ class Scorer: self._compute_lddt() return self._local_lddt + @property + def bb_lddt(self): + """ Backbone only global lDDT score in range [0.0, 1.0] + + Computed based on :attr:`~model` on backbone atoms only. This is CA for + peptides and C3' for nucleotides. No stereochecks are performed. In case + of oligomers, :attr:`~mapping` is used. + + :type: :class:`float` + """ + if self._bb_lddt is None: + self._compute_bb_lddt() + return self._bb_lddt + + @property + def bb_local_lddt(self): + """ Backbone only per residue lDDT scores in range [0.0, 1.0] + + Computed based on :attr:`~model` on backbone atoms only. This is CA for + peptides and C3' for nucleotides. No stereochecks are performed. If a + residue is not covered by the target or is in a chain skipped by the + chain mapping procedure (happens for super short chains), the respective + score is set to None. In case of oligomers, :attr:`~mapping` is used. + + :type: :class:`dict` + """ + if self._bb_local_lddt is None: + self._compute_bb_lddt() + return self._bb_local_lddt + @property def qs_global(self): """ Global QS-score @@ -915,6 +965,35 @@ class Scorer: self._compute_patchdockq_scores() return self._patch_dockq + @property + def tm_score(self): + """ TM-score computed with USalign + + USalign executable can be specified with usalign_exec kwarg at Scorer + construction, an OpenStructure internal copy of the USalign code is + used otherwise. + + :type: :class:`float` + """ + if self._tm_score is None: + self._compute_tmscore() + return self._tm_score + + @property + def usalign_mapping(self): + """ Mapping computed with USalign + + Dictionary with target chain names as key and model chain names as + values. No guarantee that all chains are mapped. USalign executable + can be specified with usalign_exec kwarg at Scorer construction, an + OpenStructure internal copy of the USalign code is used otherwise. + + :type: :class:`dict` + """ + if self._usalign_mapping is None: + self._compute_tmscore() + return self._usalign_mapping + def _aln_helper(self, target, model): # perform required alignments - cannot take the alignments from the # mapping results as we potentially remove stuff there as compared @@ -976,51 +1055,113 @@ class Scorer: mdl_seq = aln.GetSequence(1) alns[mdl_seq.name] = aln + # score variables to be set + lddt_score = None + local_lddt = None + + if self.lddt_no_stereochecks: + lddt_chain_mapping = dict() + for mdl_ch, trg_ch in flat_mapping.items(): + if mdl_ch in alns: + lddt_chain_mapping[mdl_ch] = trg_ch + lddt_score = self.lddt_scorer.lDDT(self.model, + chain_mapping = lddt_chain_mapping, + residue_mapping = alns, + check_resnames=False, + local_lddt_prop="lddt")[0] + local_lddt = dict() + for r in self.model.residues: + cname = r.GetChain().GetName() + if cname not in local_lddt: + local_lddt[cname] = dict() + if r.HasProp("lddt"): + score = round(r.GetFloatProp("lddt"), 3) + local_lddt[cname][r.GetNumber()] = score + else: + # not covered by trg or skipped in chain mapping procedure + # the latter happens if its part of a super short chain + local_lddt[cname][r.GetNumber()] = None + + else: + lddt_chain_mapping = dict() + for mdl_ch, trg_ch in flat_mapping.items(): + if mdl_ch in stereochecked_alns: + lddt_chain_mapping[mdl_ch] = trg_ch + lddt_score = self.lddt_scorer.lDDT(self.stereochecked_model, + chain_mapping = lddt_chain_mapping, + residue_mapping = stereochecked_alns, + check_resnames=False, + local_lddt_prop="lddt")[0] + local_lddt = dict() + for r in self.model.residues: + cname = r.GetChain().GetName() + if cname not in local_lddt: + local_lddt[cname] = dict() + if r.HasProp("lddt"): + score = round(r.GetFloatProp("lddt"), 3) + local_lddt[cname][r.GetNumber()] = score + else: + # rsc => residue stereo checked... + mdl_res = self.stereochecked_model.FindResidue(cname, r.GetNumber()) + if mdl_res.IsValid(): + # not covered by trg or skipped in chain mapping procedure + # the latter happens if its part of a super short chain + local_lddt[cname][r.GetNumber()] = None + else: + # opt 1: removed by stereochecks => assign 0.0 + # opt 2: removed by stereochecks AND not covered by ref + # => assign None + # fetch trg residue from non-stereochecked aln + trg_r = None + if cname in flat_mapping: + for col in alns[cname]: + if col[0] != '-' and col[1] != '-': + if col.GetResidue(1).number == r.number: + trg_r = col.GetResidue(0) + break + if trg_r is None: + local_lddt[cname][r.GetNumber()] = None + else: + local_lddt[cname][r.GetNumber()] = 0.0 + + self._lddt = lddt_score + self._local_lddt = local_lddt + + def _compute_bb_lddt(self): + + # make alignments accessible by mdl seq name + alns = dict() + for aln in self.aln: + mdl_seq = aln.GetSequence(1) + alns[mdl_seq.name] = aln + + # lDDT requires a flat mapping with mdl_ch as key and trg_ch as value + flat_mapping = self.mapping.GetFlatMapping(mdl_as_key=True) lddt_chain_mapping = dict() for mdl_ch, trg_ch in flat_mapping.items(): - if mdl_ch in stereochecked_alns: + if mdl_ch in alns: lddt_chain_mapping[mdl_ch] = trg_ch - lddt_score = self.lddt_scorer.lDDT(self.stereochecked_model, - chain_mapping = lddt_chain_mapping, - residue_mapping = stereochecked_alns, - check_resnames=False, - local_lddt_prop="lddt")[0] + lddt_score = self.bb_lddt_scorer.lDDT(self.model, + chain_mapping = lddt_chain_mapping, + residue_mapping = alns, + check_resnames=False, + local_lddt_prop="bb_lddt")[0] local_lddt = dict() for r in self.model.residues: cname = r.GetChain().GetName() if cname not in local_lddt: local_lddt[cname] = dict() - if r.HasProp("lddt"): - score = round(r.GetFloatProp("lddt"), 3) + if r.HasProp("bb_lddt"): + score = round(r.GetFloatProp("bb_lddt"), 3) local_lddt[cname][r.GetNumber()] = score else: - # rsc => residue stereo checked... - mdl_res = self.stereochecked_model.FindResidue(cname, r.GetNumber()) - if mdl_res.IsValid(): - # not covered by trg or skipped in chain mapping procedure - # the latter happens if its part of a super short chain - local_lddt[cname][r.GetNumber()] = None - else: - # opt 1: removed by stereochecks => assign 0.0 - # opt 2: removed by stereochecks AND not covered by ref - # => assign None - - # fetch trg residue from non-stereochecked aln - trg_r = None - if cname in flat_mapping: - for col in alns[cname]: - if col[0] != '-' and col[1] != '-': - if col.GetResidue(1).number == r.number: - trg_r = col.GetResidue(0) - break - if trg_r is None: - local_lddt[cname][r.GetNumber()] = None - else: - local_lddt[cname][r.GetNumber()] = 0.0 + # not covered by trg or skipped in chain mapping procedure + # the latter happens if its part of a super short chain + local_lddt[cname][r.GetNumber()] = None - self._lddt = lddt_score - self._local_lddt = local_lddt + self._bb_lddt = lddt_score + self._bb_local_lddt = local_lddt def _compute_qs(self): qs_score_result = self.qs_scorer.Score(self.mapping.mapping) @@ -1561,4 +1702,24 @@ class Scorer: self._mapping = chain_mapping.MappingResult(chain_mapper.target, mdl, chain_mapper.chem_groups, + chem_mapping, final_mapping, alns) + + def _compute_tmscore(self): + res = None + if self.usalign_exec is not None: + if not os.path.exists(self.usalign_exec): + raise RuntimeError(f"USalign exec ({self.usalign_exec}) " + f"not found") + if not os.access(self.usalign_exec, os.X_OK): + raise RuntimeError(f"USalign exec ({self.usalign_exec}) " + f"is not executable") + res = tmtools.USAlign(self.model, self.target, + usalign = self.usalign_exec) + else: + res = bindings.WrappedMMAlign(self.model, self.target) + + self._tm_score = res.tm_score + self._usalign_mapping = dict() + for a,b in zip(res.ent1_mapped_chains, res.ent2_mapped_chains): + self._usalign_mapping[b] = a diff --git a/modules/mol/alg/pymod/stereochemistry.py b/modules/mol/alg/pymod/stereochemistry.py index 625ee4ea15be6e0654a1048efb42d0b64cf86726..b3708648bf26a4e331f683e9fe7309196450f72c 100644 --- a/modules/mol/alg/pymod/stereochemistry.py +++ b/modules/mol/alg/pymod/stereochemistry.py @@ -9,6 +9,18 @@ from ost import geom from ost import mol +def _AtomToQualifiedName(a): + """ Returns string to uniquely identify atom + + format: <chain_name>.<resnum>.<resnum_inscode>.<atom_name> + """ + r = a.GetResidue() + ch = r.GetChain() + num = r.number.num + ins_code = r.number.ins_code.strip("\u0000") + return f"{ch.name}.{r.number.num}.{ins_code}.{a.name}" + + def _PotentialDisulfid(a_one, a_two): """ Returns whether two atoms can potentially build a disulfid bond @@ -355,9 +367,12 @@ class ClashInfo: def ToJSON(self): """ Return JSON serializable dict + + Atoms are represented by a string in format: + <chain_name>.<resnum>.<resnum_inscode>.<atom_name> """ - return {"a1": self.a1.GetQualifiedName(), - "a2": self.a2.GetQualifiedName(), + return {"a1": _AtomToQualifiedName(self.a1), + "a2": _AtomToQualifiedName(self.a2), "dist": self.dist, "tolerated_dist": self.tolerated_dist} @@ -382,9 +397,12 @@ class BondViolationInfo: def ToJSON(self): """ Return JSON serializable dict + + Atoms are represented by a string in format: + <chain_name>.<resnum>.<resnum_inscode>.<atom_name> """ - return {"a1": self.a1.GetQualifiedName(), - "a2": self.a2.GetQualifiedName(), + return {"a1": _AtomToQualifiedName(self.a1), + "a2": _AtomToQualifiedName(self.a2), "length": self.length, "exp_length": self.exp_length, "std": self.std} @@ -412,10 +430,13 @@ class AngleViolationInfo: def ToJSON(self): """ Return JSON serializable dict + + Atoms are represented by a string in format: + <chain_name>.<resnum>.<resnum_inscode>.<atom_name> """ - return {"a1": self.a1.GetQualifiedName(), - "a2": self.a2.GetQualifiedName(), - "a3": self.a3.GetQualifiedName(), + return {"a1": _AtomToQualifiedName(self.a1), + "a2": _AtomToQualifiedName(self.a2), + "a3": _AtomToQualifiedName(self.a3), "angle": self.angle, "exp_angle": self.exp_angle, "std": self.std} diff --git a/modules/mol/alg/tests/test_chain_mapping.py b/modules/mol/alg/tests/test_chain_mapping.py index 8c8846aba8a3de61c6a52991c33041186b00219e..3d4ccbde250a3f9e2f4b39e7b4b9a5010a18645f 100644 --- a/modules/mol/alg/tests/test_chain_mapping.py +++ b/modules/mol/alg/tests/test_chain_mapping.py @@ -352,17 +352,66 @@ class TestChainMapper(unittest.TestCase): self.assertEqual(ref_aln.GetSequence(1).GetString(), aln.GetSequence(1).GetString()) + def test_get_repr(self): + + ref, ref_seqres = io.LoadMMCIF(os.path.join("testfiles", "1r8q.cif.gz"), + seqres=True) + mdl, mdl_seqres = io.LoadMMCIF(os.path.join("testfiles", "4c0a.cif.gz"), + seqres=True) + + pep_ref = ref.Select("peptide=true") + lig_ref = ref.Select("cname=K") + + # create view of reference binding site + ref_residues_hashes = set() # helper to keep track of added residues + for ligand_at in lig_ref.atoms: + close_atoms = pep_ref.FindWithin(ligand_at.GetPos(), 10.0) + for close_at in close_atoms: + ref_res = close_at.GetResidue() + h = ref_res.handle.GetHashCode() + if h not in ref_residues_hashes: + ref_residues_hashes.add(h) + + ref_bs = ref.CreateEmptyView() + for ch in ref.chains: + for r in ch.residues: + if r.handle.GetHashCode() in ref_residues_hashes: + ref_bs.AddResidue(r, mol.ViewAddFlag.INCLUDE_ALL) + + chain_mapper = ChainMapper(ref) + global_mapping = chain_mapper.GetQSScoreMapping(mdl) + flat_mapping = global_mapping.GetFlatMapping() + + # find optimal representation of binding site + optimal_repr_result = chain_mapper.GetRepr(ref_bs, mdl)[0] + self.assertTrue(optimal_repr_result.lDDT > 0.6) # exp result: 0.6047 + # mapping should be different than overall mapping + repr_mapping = optimal_repr_result.GetFlatChainMapping() + for ref_ch, mdl_ch in repr_mapping.items(): + self.assertNotEqual(mdl_ch, flat_mapping[ref_ch]) + + # enforce usage of global mapping, which gives a different pocket + # with slightly lower lDDT + global_repr_result = \ + chain_mapper.GetRepr(ref_bs, mdl, global_mapping=global_mapping)[0] + self.assertTrue(global_repr_result.lDDT < 0.6) # exp result: 0.5914 + + # ensure that mapping from global_repr_result corresponds to global + # mapping + repr_mapping = global_repr_result.GetFlatChainMapping() + for ref_ch, mdl_ch in repr_mapping.items(): + self.assertEqual(mdl_ch, flat_mapping[ref_ch]) def test_misc(self): - # check for triggered error when no chain fulfills length threshold - ref = _LoadFile("3l1p.1.pdb").Select("cname=A and rnum<8") - self.assertRaises(Exception, ChainMapper, ref) + # check for triggered error when no chain fulfills length threshold + ref = _LoadFile("3l1p.1.pdb").Select("cname=A and rnum<8") + self.assertRaises(Exception, ChainMapper, ref) if __name__ == "__main__": from ost import testutils - if testutils.SetDefaultCompoundLib(): + if testutils.DefaultCompoundLibIsSet(): testutils.RunTests() else: print('No compound lib available. Ignoring test_chain_mapping.py tests.') diff --git a/modules/mol/alg/tests/test_lddt.py b/modules/mol/alg/tests/test_lddt.py index cb8f1d28ad8c910d44954e05758d11c769503607..6cfe9d5b6ba1e5f6b71d37898e15f1f5147eac0c 100644 --- a/modules/mol/alg/tests/test_lddt.py +++ b/modules/mol/alg/tests/test_lddt.py @@ -276,7 +276,7 @@ class TestlDDTBS(unittest.TestCase): if __name__ == "__main__": from ost import testutils - if testutils.SetDefaultCompoundLib(): + if testutils.DefaultCompoundLibIsSet(): testutils.RunTests() else: print('No compound library available. Ignoring test_lddt.py tests.') diff --git a/modules/mol/alg/tests/test_ligand_scoring.py b/modules/mol/alg/tests/test_ligand_scoring.py index b23de7b9c49bac4af17fe06781d23fd601ba59ca..2cd83eca03817052e318cec9de8d7419f046e748 100644 --- a/modules/mol/alg/tests/test_ligand_scoring.py +++ b/modules/mol/alg/tests/test_ligand_scoring.py @@ -1,8 +1,8 @@ import unittest, os, sys +from functools import lru_cache import numpy as np -import ost from ost import io, mol, geom # check if we can import: fails if numpy or scipy not available try: @@ -13,22 +13,42 @@ except ImportError: "networkx is missing. Ignoring test_ligand_scoring.py tests.") sys.exit(0) -#ost.PushVerbosityLevel(ost.LogLevel.Debug) + +def _GetTestfilePath(filename): + """Get the path to the test file given filename""" + return os.path.join('testfiles', filename) + + +@lru_cache(maxsize=None) +def _LoadMMCIF(filename): + path = _GetTestfilePath(filename) + ent = io.LoadMMCIF(path) + return ent + + +@lru_cache(maxsize=None) +def _LoadPDB(filename): + path = _GetTestfilePath(filename) + ent = io.LoadPDB(path) + return ent + + +@lru_cache(maxsize=None) +def _LoadEntity(filename): + path = _GetTestfilePath(filename) + ent = io.LoadEntity(path) + return ent + class TestLigandScoring(unittest.TestCase): def test_extract_ligands_mmCIF(self): """Test that we can extract ligands from mmCIF files. """ - trg, trg_seqres = io.LoadMMCIF(os.path.join('testfiles', "1r8q.cif.gz"), seqres=True) - mdl, mdl_seqres = io.LoadMMCIF(os.path.join('testfiles', "P84080_model_02.cif.gz"), seqres=True) + trg = _LoadMMCIF("1r8q.cif.gz") + mdl = _LoadMMCIF("P84080_model_02.cif.gz") sc = LigandScorer(mdl, trg, None, None) - # import ipdb; ipdb.set_trace() - # import ost.mol.alg.scoring - # scr = ost.mol.alg.scoring.Scorer(sc.model, sc.target) - # scr.lddt - # scr.local_lddt assert len(sc.target_ligands) == 7 assert len(sc.model_ligands) == 1 @@ -39,8 +59,8 @@ class TestLigandScoring(unittest.TestCase): """Test that we can instantiate the scorer with ligands contained in the target and model entity and given in a list. """ - trg, trg_seqres = io.LoadMMCIF(os.path.join('testfiles', "1r8q.cif.gz"), seqres=True) - mdl, mdl_seqres = io.LoadMMCIF(os.path.join('testfiles', "P84080_model_02.cif.gz"), seqres=True) + trg = _LoadMMCIF("1r8q.cif.gz") + mdl = _LoadMMCIF("P84080_model_02.cif.gz") # Pass entity views trg_lig = [trg.Select("rname=MG"), trg.Select("rname=G3D")] @@ -87,10 +107,10 @@ class TestLigandScoring(unittest.TestCase): io.SaveEntity(lig_ent, "%s_ligand_%d.sdf" % (prefix, lig_num)) lig_num += 1 """ - mdl = io.LoadPDB(os.path.join('testfiles', "P84080_model_02_nolig.pdb")) - mdl_ligs = [io.LoadEntity(os.path.join('testfiles', "P84080_model_02_ligand_0.sdf"))] - trg = io.LoadPDB(os.path.join('testfiles', "1r8q_protein.pdb.gz")) - trg_ligs = [io.LoadEntity(os.path.join('testfiles', "1r8q_ligand_%d.sdf" % i)) for i in range(7)] + mdl = _LoadPDB("P84080_model_02_nolig.pdb") + mdl_ligs = [_LoadEntity("P84080_model_02_ligand_0.sdf")] + trg = _LoadPDB("1r8q_protein.pdb.gz") + trg_ligs = [_LoadEntity("1r8q_ligand_%d.sdf" % i) for i in range(7)] # Pass entities sc = LigandScorer(mdl, trg, mdl_ligs, trg_ligs) @@ -114,18 +134,18 @@ class TestLigandScoring(unittest.TestCase): """Test that we reject input if multiple ligands with the same chain name/residue number are given. """ - mdl = io.LoadPDB(os.path.join('testfiles', "P84080_model_02_nolig.pdb")) - mdl_ligs = [io.LoadEntity(os.path.join('testfiles', "P84080_model_02_ligand_0.sdf"))] - trg = io.LoadPDB(os.path.join('testfiles', "1r8q_protein.pdb.gz")) - trg_ligs = [io.LoadEntity(os.path.join('testfiles', "1r8q_ligand_%d.sdf" % i)) for i in range(7)] + mdl = _LoadPDB("P84080_model_02_nolig.pdb") + mdl_ligs = [_LoadEntity("P84080_model_02_ligand_0.sdf")] + trg = _LoadPDB("1r8q_protein.pdb.gz") + trg_ligs = [_LoadEntity("1r8q_ligand_%d.sdf" % i) for i in range(7)] # Reject identical model ligands with self.assertRaises(RuntimeError): sc = LigandScorer(mdl, trg, [mdl_ligs[0], mdl_ligs[0]], trg_ligs) # Reject identical target ligands - lig0 = trg_ligs[0] - lig1 = trg_ligs[1] + lig0 = trg_ligs[0].Copy() + lig1 = trg_ligs[1].Copy() ed1 = lig1.EditXCS() ed1.RenameChain(lig1.chains[0], lig0.chains[0].name) ed1.SetResidueNumber(lig1.residues[0], lig0.residues[0].number) @@ -135,7 +155,7 @@ class TestLigandScoring(unittest.TestCase): def test__ResidueToGraph(self): """Test that _ResidueToGraph works as expected """ - mdl_lig = io.LoadEntity(os.path.join('testfiles', "P84080_model_02_ligand_0.sdf")) + mdl_lig = _LoadEntity("P84080_model_02_ligand_0.sdf") graph = ligand_scoring._ResidueToGraph(mdl_lig.residues[0]) assert len(graph.edges) == 34 @@ -152,8 +172,8 @@ class TestLigandScoring(unittest.TestCase): def test__ComputeSymmetries(self): """Test that _ComputeSymmetries works. """ - trg, trg_seqres = io.LoadMMCIF(os.path.join('testfiles', "1r8q.cif.gz"), seqres=True) - mdl, mdl_seqres = io.LoadMMCIF(os.path.join('testfiles', "P84080_model_02.cif.gz"), seqres=True) + trg = _LoadMMCIF("1r8q.cif.gz") + mdl = _LoadMMCIF("P84080_model_02.cif.gz") trg_mg1 = trg.FindResidue("E", 1) trg_g3d1 = trg.FindResidue("F", 1) @@ -168,7 +188,7 @@ class TestLigandScoring(unittest.TestCase): assert len(sym) == 72 # Test that we can match ions read from SDF - sdf_lig = io.LoadEntity(os.path.join('testfiles', "1r8q_ligand_0.sdf")) + sdf_lig = _LoadEntity("1r8q_ligand_0.sdf") sym = ligand_scoring._ComputeSymmetries(trg_mg1, sdf_lig.residues[0], by_atom_index=True) assert len(sym) == 1 @@ -195,8 +215,8 @@ class TestLigandScoring(unittest.TestCase): def test_SCRMSD(self): """Test that SCRMSD works. """ - trg, trg_seqres = io.LoadMMCIF(os.path.join('testfiles', "1r8q.cif.gz"), seqres=True) - mdl, mdl_seqres = io.LoadMMCIF(os.path.join('testfiles', "P84080_model_02.cif.gz"), seqres=True) + trg = _LoadMMCIF("1r8q.cif.gz") + mdl = _LoadMMCIF("P84080_model_02.cif.gz") trg_mg1 = trg.FindResidue("E", 1) trg_g3d1 = trg.FindResidue("F", 1) @@ -240,8 +260,8 @@ class TestLigandScoring(unittest.TestCase): def test__compute_scores(self): """Test that _compute_scores works. """ - trg, trg_seqres = io.LoadMMCIF(os.path.join('testfiles', "1r8q.cif.gz"), seqres=True) - mdl, mdl_seqres = io.LoadMMCIF(os.path.join('testfiles', "P84080_model_02.cif.gz"), seqres=True) + trg = _LoadMMCIF("1r8q.cif.gz") + mdl = _LoadMMCIF("P84080_model_02.cif.gz") mdl_lig = io.LoadEntity(os.path.join('testfiles', "P84080_model_02_ligand_0.sdf")) sc = LigandScorer(mdl, trg, [mdl_lig], None) @@ -273,8 +293,8 @@ class TestLigandScoring(unittest.TestCase): """Test check_resname argument works """ # 4C0A has mismatching sequence and fails with check_resnames=True - mdl_1r8q, _ = io.LoadMMCIF(os.path.join('testfiles', "1r8q.cif.gz"), seqres=True) - trg_4c0a, _ = io.LoadMMCIF(os.path.join('testfiles', "4c0a.cif.gz"), seqres=True) + mdl_1r8q = _LoadMMCIF("1r8q.cif.gz") + trg_4c0a = _LoadMMCIF("4c0a.cif.gz") mdl = mdl_1r8q.Select("cname=D or cname=F") trg = trg_4c0a.Select("cname=C or cname=I") @@ -290,8 +310,8 @@ class TestLigandScoring(unittest.TestCase): """Test that the scores are computed correctly """ # 4C0A has more ligands - trg, trg_seqres = io.LoadMMCIF(os.path.join('testfiles', "1r8q.cif.gz"), seqres=True) - trg_4c0a, _ = io.LoadMMCIF(os.path.join('testfiles', "4c0a.cif.gz"), seqres=True) + trg = _LoadMMCIF("1r8q.cif.gz") + trg_4c0a = _LoadMMCIF("4c0a.cif.gz") sc = LigandScorer(trg, trg_4c0a, None, None, check_resnames=False) expected_keys = {"J", "F"} @@ -338,10 +358,59 @@ class TestLigandScoring(unittest.TestCase): self.assertEqual(sc.lddt_pli_details["F"][mol.ResNum(1)]["target_ligand"].qualified_name, 'K.G3D1') self.assertEqual(sc.lddt_pli_details["F"][mol.ResNum(1)]["model_ligand"].qualified_name, 'F.G3D1') + def test_global_chain_mapping(self): + """Test that the global and local chain mappings works. + + For RMSD, A: A results in a better chain mapping. However, C: A is a + better global chain mapping from an lDDT perspective (and lDDT-PLI). + """ + trg = _LoadMMCIF("1r8q.cif.gz") + mdl = _LoadMMCIF("P84080_model_02.cif.gz") + + # Local by default + sc = LigandScorer(mdl, trg, None, None) + assert sc.rmsd_details["L_2"][1]["chain_mapping"] == {'A': 'A'} + assert sc.lddt_pli_details["L_2"][1]["chain_mapping"] == {'C': 'A'} + + # Global + sc = LigandScorer(mdl, trg, None, None, global_chain_mapping=True) + assert sc.rmsd_details["L_2"][1]["chain_mapping"] == {'C': 'A'} + assert sc.lddt_pli_details["L_2"][1]["chain_mapping"] == {'C': 'A'} + + # Custom + sc = LigandScorer(mdl, trg, None, None, global_chain_mapping=True, custom_mapping={'A': 'A'}) + assert sc.rmsd_details["L_2"][1]["chain_mapping"] == {'A': 'A'} + assert sc.lddt_pli_details["L_2"][1]["chain_mapping"] == {'A': 'A'} + + # Custom only active with global chain mapping + sc = LigandScorer(mdl, trg, None, None, global_chain_mapping=False, custom_mapping={'A': 'A'}) + assert sc.rmsd_details["L_2"][1]["chain_mapping"] == {'A': 'A'} + assert sc.lddt_pli_details["L_2"][1]["chain_mapping"] == {'C': 'A'} + + + def test_rmsd_assignment(self): + """Test that the RMSD-based assignment works. + + For RMSD, A: A results in a better chain mapping. However, C: A is a + better global chain mapping from an lDDT perspective (and lDDT-PLI). + """ + trg = _LoadMMCIF("1r8q.cif.gz") + mdl = _LoadMMCIF("P84080_model_02.cif.gz") + + # By default, assignment differs between RMSD and lDDT-PLI in this + # specific test case, so we can first ensure it does. + # For now we skip as this is slow + # sc = LigandScorer(mdl, trg, None, None) + # assert sc.rmsd_details["L_2"][1]["target_ligand"] != sc.lddt_pli_details["L_2"][1]["target_ligand"] + + # RMSD assignment forces the same assignment + sc = LigandScorer(mdl, trg, None, None, rmsd_assignment=True) + assert sc.rmsd_details["L_2"][1]["target_ligand"] == sc.lddt_pli_details["L_2"][1]["target_ligand"] + if __name__ == "__main__": from ost import testutils - if testutils.SetDefaultCompoundLib(): + if testutils.DefaultCompoundLibIsSet(): testutils.RunTests() else: print('No compound lib available. Ignoring test_ligand_scoring.py tests.') diff --git a/modules/mol/alg/tests/test_nonstandard.py b/modules/mol/alg/tests/test_nonstandard.py index c7f49215eb79b559c1c43cd71659569de5e447fb..99bf7fdd0a8284986e4d4bb546de42a2d58193a2 100644 --- a/modules/mol/alg/tests/test_nonstandard.py +++ b/modules/mol/alg/tests/test_nonstandard.py @@ -163,7 +163,7 @@ class TestNonStandard(unittest.TestCase): if __name__ == "__main__": from ost import testutils - if testutils.SetDefaultCompoundLib(): + if testutils.DefaultCompoundLibIsSet(): testutils.RunTests() else: print('No compound library available. Ignoring test_nonstandard.py tests.') diff --git a/modules/mol/alg/tests/test_qsscore.py b/modules/mol/alg/tests/test_qsscore.py index 34ba726ad41926866a292c28174bebc05c3ac82e..5aed15ee8eb8f2591876b8862431e1680a41e88b 100644 --- a/modules/mol/alg/tests/test_qsscore.py +++ b/modules/mol/alg/tests/test_qsscore.py @@ -264,7 +264,7 @@ class TestQSScore(unittest.TestCase): if __name__ == "__main__": from ost import testutils - if testutils.SetDefaultCompoundLib(): + if testutils.DefaultCompoundLibIsSet(): testutils.RunTests() else: print('No compound lib available. Ignoring test_qsscore.py tests.') diff --git a/modules/mol/alg/tests/test_qsscoring.py b/modules/mol/alg/tests/test_qsscoring.py index 31daffe565ed5ac3d3cb8cf59ecb76dee7e62534..fc73c72d6b72a34dacb0365997460eba9c9e2a1d 100644 --- a/modules/mol/alg/tests/test_qsscoring.py +++ b/modules/mol/alg/tests/test_qsscoring.py @@ -706,7 +706,7 @@ if __name__ == "__main__": print("Could not find ClustalW. Ignoring test_qsscoring.py tests.") sys.exit(0) from ost import testutils - if testutils.SetDefaultCompoundLib(): + if testutils.DefaultCompoundLibIsSet(): testutils.RunTests() else: print('No compound library available. Ignoring test_qsscoring.py tests.') diff --git a/modules/mol/alg/tests/test_stereochemistry.py b/modules/mol/alg/tests/test_stereochemistry.py index ae458d4b5e696f71e57f1fe2cc9b500cf19225bb..b449c669bd641771681ff4f4edc8bec2f683cc93 100644 --- a/modules/mol/alg/tests/test_stereochemistry.py +++ b/modules/mol/alg/tests/test_stereochemistry.py @@ -209,7 +209,7 @@ class TestStereochemistry(unittest.TestCase): if __name__ == "__main__": from ost import testutils - if testutils.SetDefaultCompoundLib(): + if testutils.DefaultCompoundLibIsSet(): testutils.RunTests() else: print('No compound library available. Ignoring test_stereochemistry.py tests.') diff --git a/modules/mol/base/doc/editors.rst b/modules/mol/base/doc/editors.rst index e2218db714d771cd1c8ed66c6659b0148ed0f719..2be8e65f6aeadc4fb03c7e043ef82f11dde10ab1 100644 --- a/modules/mol/base/doc/editors.rst +++ b/modules/mol/base/doc/editors.rst @@ -69,19 +69,24 @@ The basic functionality of editors is implemented in the EditorBase class. :returns: :class:`ChainHandle` .. method:: AppendResidue(chain, residue_name, [res_num]) - AppendResidue(chain, residue_name, deep=False) + AppendResidue(chain, residue, deep=False) - Append residue to the end of the chain. If res_num is not given, the - residue number will be set to the residue number of the last added residue - plus one. The insertion code is the same. + Append residue to the end of the chain. - By default, atoms and bonds are not added. If deep is `True`, atoms (but - not bonds) are added to the new residue, including alternative atoms. + If res_num is not given, the residue number will be set to the residue + number of the last added residue plus one. The insertion code is the same. + + It is possible to give a residue handle as second argument, instead of a + residue name. By default, atoms and bonds are not added. If deep is + `True`, atoms (but not bonds) are added to the new residue, including + alternative atoms. :param chain: Must be a valid chain :type chain: :class:`ChainHandle` :param residue_name: 3-letter-code of the residue, e.g. ``GLY``. :type residue_name: :class:`string` + :param residue: A residue handle to copy + :type residue: :class:`ResidueHandle` :param deep: If set to true, insert atoms as well. :type deep: :class:`bool` :returns: :class:`ResidueHandle` diff --git a/modules/mol/base/doc/entity.rst b/modules/mol/base/doc/entity.rst index 657125a2fa11c289dcfb3339d22cfdae4ccd561a..795c9b7c2674fd0bb1835a08186fe407bca947ce 100644 --- a/modules/mol/base/doc/entity.rst +++ b/modules/mol/base/doc/entity.rst @@ -741,11 +741,6 @@ The Handle Classes .. attribute:: is_ligand - .. warning:: - This property is meaningless on mmCIF files loaded with - :func:`ost.io.LoadMMCIF` with `seqres=False` (the default), or if no - default compound library is set. - Whether the residue is a ligand. When loading PDB structures, this property is set based on the HET records. This also means, that this property will most likely not be set properly for all except PDB files coming from diff --git a/modules/mol/base/pymod/export_entity.cc b/modules/mol/base/pymod/export_entity.cc index c0b54781a2d85a1bcefa2a7695a21c66734c970d..bd2e97248f2ebd6ff6d0e253b1286d7581350637 100644 --- a/modules/mol/base/pymod/export_entity.cc +++ b/modules/mol/base/pymod/export_entity.cc @@ -225,7 +225,8 @@ void export_Entity() .def("EditXCS", &EntityHandle::EditXCS, arg("mode")=UNBUFFERED_EDIT) .def("EditICS", &EntityHandle::EditICS, arg("mode")=UNBUFFERED_EDIT) .def("RequestXCSEditor", &depr_request_xcs_editor, arg("mode")=UNBUFFERED_EDIT) - .def("RequestICSEditor", &depr_request_ics_editor, arg("mode")=UNBUFFERED_EDIT) + .def("RequestICSEditor", &depr_request_ics_editor, arg("mode")=UNBUFFERED_EDIT) + .def("GetHashCode", &EntityHandle::GetHashCode) .def(self==self) .def(self!=self) #if OST_NUMPY_SUPPORT_ENABLED diff --git a/modules/mol/base/pymod/export_entity_view.cc b/modules/mol/base/pymod/export_entity_view.cc index 0db065a075f16a29fe887c5a0d728379477d8ff2..f2738e23f69c52156ab6f06f21a74b4fcec204a6 100644 --- a/modules/mol/base/pymod/export_entity_view.cc +++ b/modules/mol/base/pymod/export_entity_view.cc @@ -215,8 +215,8 @@ void export_EntityView() def("CreateViewFromAtoms", create_view); def("CreateViewFromAtomList", create_view); - def("CreateEntityFromView", &CreateEntityFromView, - arg("handle")=EntityHandle()); + def("CreateEntityFromView", &CreateEntityFromView, + (arg("view"), arg("include_exlusive_atoms"), arg("handle")=EntityHandle())); class_<EntityViewList>("EntityViewList", init<>()) .def(vector_indexing_suite<EntityViewList>()) ; diff --git a/modules/mol/base/src/entity_handle.cc b/modules/mol/base/src/entity_handle.cc index b17d23f0b12edf44e6d3e3581f50bd7c0a171b36..ade0c4524d17f4e0bd2360fb39fdbecbe711f549 100644 --- a/modules/mol/base/src/entity_handle.cc +++ b/modules/mol/base/src/entity_handle.cc @@ -388,4 +388,11 @@ EntityHandle EntityHandle::GetHandle() const { return *this; } + +unsigned long EntityHandle::GetHashCode() const +{ + this->CheckValidity(); + return reinterpret_cast<unsigned long>(Impl().get()); +} + }} // ns diff --git a/modules/mol/base/src/entity_handle.hh b/modules/mol/base/src/entity_handle.hh index 18383529adc27e7472876d44d104d0381d0d8af3..a85db21be815dbe2e4cd32b8075416931efc2ad6 100644 --- a/modules/mol/base/src/entity_handle.hh +++ b/modules/mol/base/src/entity_handle.hh @@ -272,6 +272,9 @@ public: /// /// Useful for templated code and duck-typing in Python EntityHandle GetHandle() const; + + unsigned long GetHashCode() const; + bool operator==(const EntityHandle& ref) const; bool operator!=(const EntityHandle& ref) const; }; diff --git a/modules/mol/base/src/impl/chain_impl.cc b/modules/mol/base/src/impl/chain_impl.cc index ca2aeaad7c5df77bf7f2d2860311df43f9507ae4..adf5aeb8b88ea887c6af3252ca0fdea423eab516 100644 --- a/modules/mol/base/src/impl/chain_impl.cc +++ b/modules/mol/base/src/impl/chain_impl.cc @@ -409,7 +409,9 @@ int ChainImpl::GetIndexForResNumInSequence(const ResNum& number) const if (pos<0 || pos>=static_cast<int>(residue_list_.size())) { return -1; } - assert(residue_list_[pos]->GetNumber()==number); + + if(residue_list_[pos]->GetNumber()!=number) return -1; + return pos; } diff --git a/modules/mol/base/src/view_op.cc b/modules/mol/base/src/view_op.cc index 4b37c4c32e950d82583b8cd0bb5cc601ae64060f..dd62c13717c75ecebad6663303063ff06497025f 100644 --- a/modules/mol/base/src/view_op.cc +++ b/modules/mol/base/src/view_op.cc @@ -295,7 +295,7 @@ public: } virtual bool VisitChain(const ChainHandle& chain) { - chain_=ent_->InsertChain(chain.GetName()); + chain_=ent_->InsertChain(chain.GetName(), chain.Impl(), false); return true; } diff --git a/modules/seq/alg/tests/test_aaindex.py b/modules/seq/alg/tests/test_aaindex.py index a1915e28e3e7564525159a98dd34216b0e4c7a0b..2fcd8002c6163f60eff1a4d6bc43c974d27a0d26 100644 --- a/modules/seq/alg/tests/test_aaindex.py +++ b/modules/seq/alg/tests/test_aaindex.py @@ -45,5 +45,5 @@ if __name__ == "__main__": from ost import testutils # the function below indirectly enables GetSharedDataPath when # calling stuff from python which is the case in unit tests - testutils.SetDefaultCompoundLib() + testutils.DefaultCompoundLibIsSet() testutils.RunTests() diff --git a/modules/seq/alg/tests/test_aligntoseqres.py b/modules/seq/alg/tests/test_aligntoseqres.py index 5664ff8378a30667bf543d2463b2a6c29a51b385..642edb8056cba4341a1a0ce6cd1f25323add6c23 100644 --- a/modules/seq/alg/tests/test_aligntoseqres.py +++ b/modules/seq/alg/tests/test_aligntoseqres.py @@ -68,7 +68,7 @@ class TestAlignToSeqRes(unittest.TestCase): if __name__ == "__main__": from ost import testutils - if testutils.SetDefaultCompoundLib(): + if testutils.DefaultCompoundLibIsSet(): testutils.RunTests() else: print('No compound library available. Ignoring test_aligntoseqres.py tests.') diff --git a/scripts/ost_config.in b/scripts/ost_config.in index d0ba65da0123a8581c7cfd59ca81e12bca673534..69e792960240232c671b3d2f9f2be54c49c4be03 100644 --- a/scripts/ost_config.in +++ b/scripts/ost_config.in @@ -25,6 +25,7 @@ export DNG_ROOT=`cd "$BIN_DIR/..";pwd` export DNG_BINDIR="$DNG_ROOT/bin" export DNG_LIBDIR="$DNG_ROOT/@LIBDIR@" export DNG_INITDIR="$DNG_LIBDIR/@PYTHON_MODULE_PATH@/ost/" +export OST_ROOT="$DNG_ROOT" export PATH="$DNG_BINDIR:${PATH}" export DYLD_FRAMEWORK_PATH="$DNG_LIBDIR:${DYLD_FRAMEWORK_PATH}" diff --git a/scripts/ost_startup.py.in b/scripts/ost_startup.py.in index 3d22c7e7ea0bf7def822d4d0e37a92644d0c6787..c451bec380f5d81da50c8b3be92c225a6e5290e0 100644 --- a/scripts/ost_startup.py.in +++ b/scripts/ost_startup.py.in @@ -44,23 +44,10 @@ parser.disable_interspersed_args() _site_packs='python%d.%d/site-packages' % sys.version_info[0:2] _base_dir=os.getenv('DNG_ROOT') sys.path.insert(0, os.path.join(_base_dir, '@LIBDIR@', _site_packs)) - -from ost import SetPrefixPath, GetSharedDataPath, conop -SetPrefixPath(_base_dir) - -def _InitRuleBasedProcessor(): - compound_lib_path=os.path.join(GetSharedDataPath(), 'compounds.chemlib') - if os.path.exists(compound_lib_path): - compound_lib=conop.CompoundLib.Load(compound_lib_path) - conop.SetDefaultLib(compound_lib) - -# switch to rule-based processor, if compound library is available -_InitRuleBasedProcessor() from ost import * import ost -import os.path HistoryFile=os.path.expanduser('~/.ost_history') # we are not in GUI mode. diff --git a/singularity/README.rst b/singularity/README.rst index 3f0391c0fbae1a48df9035f3b5fae777b1f5aada..1407509dca0972155b2bf1990d3608786539558f 100644 --- a/singularity/README.rst +++ b/singularity/README.rst @@ -64,3 +64,77 @@ Then (in the same terminal window) to invoke IPython app one can just type: To make the alias permanent put it into your ``.bashrc`` file or whatever file you use to store the aliases. + +Actions +------- + +To see the list of available actions do: + + .. code-block:: + + singularity run --app OST ost.img -h + +To run chosen action do: + + .. code-block:: + + singularity run --app OST ost.img <ACTION NAME> + + +Here is an example run of the compare-structures action: + +.. code-block:: + + singularity run --app OST ost.img compare-structures \ + --model model.pdb \ + --reference reference.cif \ + --output scores.json \ + --lddt \ + --local-lddt + +In order to see all available options for this action run: + +.. code-block:: + + singularity run --app OST ost.img compare-structures -h + +CASP15 used lDDT for RNA scoring. lDDT runs stereochemistry checks by default, +removing sidechains if they have problematic stereochemistry. This gives lower +lDDT scores. The full residue is removed if the backbone has problematic +stereochemistry resulting in an lDDT score of 0.0 for that particular residue. +Stereochemistry checks for RNA were not yet available in CASP15. To reproduce +these results, use the ``--lddt-no-stereochecks`` flag. This disables +stereochemistry checks for lDDT computation but stereochemical irregularities +are still reported in the output. + +The Compound Library +-------------------- + +You'll have the exact same problem with outdated compound libraries as in the +raw Docker image. You can find more information on that matter in the Docker +section of the documentation: :ref:`docker_compound_lib`. + +The same trick of mounting an up to date compound library from the local host into +the container applies. The two relevant commands for Singularity are building +a new library and mount it. + +Build a new library: + +.. code-block:: bash + + singularity run --app ChemdictTool <IMAGE> create components.cif.gz \ + compounds.chemlib + +Run some script with an updated compound library from localhost: + +.. code-block:: bash + + singularity run \ + -B <COMPLIB_DIR_LOCALHOST>/compounds.chemlib:/compounds.chemlib \ + --env OST_COMPOUNDS_CHEMLIB=/compounds.chemlib \ + --app PM <IMAGE> my_script.py + +<COMPLIB_DIR_LOCALHOST> is the directory that contains the compound lib with the +name compounds.chemlib that you created before. Make sure that everything works +as expected by executing the exact same lines of Python code as described +in the Docker documentation: :ref:`docker_compound_lib`. diff --git a/singularity/Singularity b/singularity/Singularity index 899e8e54ea6749bcd86f0329259e81464c78bf0a..71f2c00fcad8ca8a2be1939865a2e7254ef0b486 100644 --- a/singularity/Singularity +++ b/singularity/Singularity @@ -1,22 +1,19 @@ BootStrap: docker -From: registry.scicore.unibas.ch/schwede/openstructure:2.4.0-jammy +From: registry.scicore.unibas.ch/schwede/openstructure:2.5.0-jammy %post ############################################################################## # POST ############################################################################## - # CHANGE DASH TO BASH rm /bin/sh ln -sf /bin/bash /bin/sh - # INSTALL SYSTEM DEPS ##################### -apt-get update -y && apt-get install -y python3-pip +apt-get update -y && apt-get install -y ipython3 jupyter python3-pip +pip3 install ipywidgets==7.6.0 pip3 install nglview \ - ipython \ - jupyter \ six # SET LOCALE @@ -28,23 +25,9 @@ locale-gen en_US.UTF-8 # SETUP IPYTHON / JUPYTER ######################### -# As the Singularity mounts $HOME by default Jupyter and Ipython config files -# are moved to separate directories. Proper environmental variables are also -# set -export IPYTHONDIR="/usr/local/share/ipython" -export JUPYTER_CONFIG_DIR="/usr/local/etc/jupyter" export JUPYTER_PATH="/usr/local/share/jupyter" -export JUPYTER_RUNTIME_DIR="/tmp/jupyter_runtime" - -mkdir -p /usr/local/share/ipython mkdir -p $JUPYTER_PATH -mkdir -p $JUPYTER_RUNTIME_DIR -mkdir -p $JUPYTER_CONFIG_DIR mkdir -p $JUPYTER_PATH/kernels/ost-kernel -chmod a+rw -R /usr/local/share/ipython -chmod a+rw -R $JUPYTER_PATH -chmod a+rw -R $JUPYTER_CONFIG_DIR -chmod a+rw -R $JUPYTER_RUNTIME_DIR cat > $JUPYTER_PATH/kernels/ost-kernel/kernel.json <<EOF { "display_name": "OST", @@ -54,7 +37,7 @@ cat > $JUPYTER_PATH/kernels/ost-kernel/kernel.json <<EOF "-m", "ipykernel", "-f", "{connection_file}", "--InteractiveShellApp.exec_PYTHONSTARTUP=False", - "--InteractiveShellApp.exec_files=['/usr/local/lib64/python3.8/site-packages/ost/ost_startup.py']" + "--InteractiveShellApp.exec_files=/usr/local/lib64/python3.10/site-packages/ost/ost_startup.py" ], "env": { } @@ -71,10 +54,7 @@ export TEMP="/tmp" export TEMPDIR="/tmp" export TMPDIR="/tmp" export TMP="/tmp" -export IPYTHONDIR="/usr/local/share/ipython" -export JUPYTER_CONFIG_DIR="/usr/local/etc/jupyter" export JUPYTER_PATH="/usr/local/share/jupyter" -export JUPYTER_RUNTIME_DIR="$TMPDIR/jupyter_runtime" %apprun ChemdictTool ############################################################################## @@ -173,15 +153,15 @@ goes to CWD. Thus this sould work as expected out of the box. %appenv IPython ############################################################################## -# NOTEBOOK ENV +# IPYTHON ENV ############################################################################## export DNG_ROOT=$OST_ROOT -export DNG_INITDIR=${DNG_ROOT}/lib64/python3.8/site-packages/ost +export DNG_INITDIR=${DNG_ROOT}/lib64/python3.10/site-packages/ost %apprun IPython ############################################################################## -# OST IPYTON APP +# OST IPYTHON APP ############################################################################## ipython3 -i $DNG_INITDIR/ost_startup.py "$@" @@ -235,9 +215,6 @@ load all necessary OST components just like in the OST shell. We also enabled the nglview widget to interactively view molecular structures and trajectories. For more details on how to use nglview see http://nglviewer.org/nglview/latest/. -As the Singularity mounts $HOME by default Jupyter and Ipython config files -are moved to separate directories. Proper environmental variables are also set. - To list of all available options: singularity run --app Notebook <IMAGE> --help