diff --git a/actions/ost-qs-score b/actions/ost-qs-score index dc78ba4fa251a07510377639ccf09caef3a28662..ffdc76b03d6b7a27e3e55ec9f468fae18bf674a3 100644 --- a/actions/ost-qs-score +++ b/actions/ost-qs-score @@ -1,13 +1,13 @@ -"""Calculate Quaternary Structure score (QS-score) between two complexes. - -""" +"""Calculate Quaternary Structure score (QS-score) between two complexes.""" import os import sys +import json import argparse import ost -from ost.io import LoadPDB, LoadMMCIF +from ost.io import (LoadPDB, LoadMMCIF, MMCifInfoBioUnit, MMCifInfo, + MMCifInfoTransOp) from ost import PushVerbosityLevel from ost.mol.alg import qsscoring @@ -47,6 +47,11 @@ def _ParseArgs(): "Each separate mapping consist of key:value pairs where key " "is the chain name in model and value is the chain name in " "reference.")) + parser.add_argument( + "-o", + "--output", + dest="output", + help=("Output file name. The output will be saved as a JSON file.")) opts = parser.parse_args() if opts.chain_mapping is not None: @@ -69,17 +74,51 @@ def _ReadStructureFile(path): :returns: Entity :rtype: :class:`~ost.mol.EntityHandle` """ + entities = list() if not os.path.isfile(path): raise IOError("%s is not a file" % path) try: - ent = LoadPDB(path, profile="SLOPPY") - except IOError: + entity = LoadPDB(path) + if not entity.IsValid(): + raise IOError("Provided file does not contain valid entity.") + entity.SetName(os.path.basename(path)) + entities.append(entity) + except Exception: try: - ent = LoadMMCIF(path, profile="SLOPPY") - except IOError as err: - raise err - - return ent + tmp_entity, cif_info = LoadMMCIF(path, info=True) + if len(cif_info.biounits) == 0: + tbu = MMCifInfoBioUnit() + tbu.id = 'ASU of ' + entity.pdb_id + tbu.details = 'asymmetric unit' + for chain in tmp_entity.chains: + tbu.AddChain(str(chain)) + tinfo = MMCifInfo() + tops = MMCifInfoTransOp() + tinfo.AddOperation(tops) + tbu.AddOperations(tinfo.GetOperations()) + entity = tbu.PDBize(tmp_entity, min_polymer_size=0) + entity.SetName(os.path.basename(path) + ".au") + entities.append(entity) + elif len(cif_info.biounits) > 1: + for i, biounit in enumerate(cif_info.biounits): + entity = biounit.PDBize(tmp_entity, min_polymer_size=0) + if not entity.IsValid(): + raise IOError( + "Provided file does not contain valid entity.") + entity.SetName(os.path.basename(path) + "." + str(i)) + entities.append(entity) + else: + biounit = cif_info.biounits[0] + entity = biounit.PDBize(tmp_entity, min_polymer_size=0) + if not entity.IsValid(): + raise IOError( + "Provided file does not contain valid entity.") + entity.SetName(os.path.basename(path)) + entities.append(entity) + + except Exception as exc: + raise exc + return entities def _Main(): @@ -90,24 +129,56 @@ def _Main(): # # Read the input files ost.LogInfo("Reading model from %s" % opts.model) - model = _ReadStructureFile(opts.model) + models = _ReadStructureFile(opts.model) ost.LogInfo("Reading reference from %s" % opts.reference) - reference = _ReadStructureFile(opts.reference) + references = _ReadStructureFile(opts.reference) + if len(models) > 1 or len(references) > 1: + ost.LogInfo( + "Multiple complexes detected. All combinations will be tried.") + + result = {"result": []} # # Perform scoring - try: - qs_scorer = qsscoring.QSscorer(reference, - model) - if opts.chain_mapping is not None: - ost.LogInfo( - "Using custom chain mapping: %s" % str(opts.chain_mapping)) - qs_scorer.chain_mapping = opts.chain_mapping - ost.LogScript('QSscore:', str(qs_scorer.global_score)) - ost.LogScript('Chain mapping used:', str(qs_scorer.chain_mapping)) - except qsscoring.QSscoreError as ex: - # default handling: report failure and set score to 0 - ost.LogError('QSscore failed:', str(ex)) + for model in models: + for reference in references: + ost.LogInfo("#\nComparing %s to %s" % ( + model.GetName(), + reference.GetName())) + try: + qs_scorer = qsscoring.QSscorer(reference, + model) + if opts.chain_mapping is not None: + ost.LogInfo( + "Using custom chain mapping: %s" % str( + opts.chain_mapping)) + qs_scorer.chain_mapping = opts.chain_mapping + result["result"].append({ + "status": "SUCCESS", + "error": "", + "model_name": model.GetName(), + "reference_name": reference.GetName(), + "global_score": qs_scorer.global_score, + "lddt_score": qs_scorer.lddt_score, + "best_score": qs_scorer.best_score, + "chain_mapping": qs_scorer.chain_mapping + }) + except qsscoring.QSscoreError as ex: + # default handling: report failure and set score to 0 + ost.LogError('QSscore failed:', str(ex)) + result["result"].append({ + "status": "FAILURE", + "error": str(ex), + "model_name": model.GetName(), + "reference_name": reference.GetName(), + "global_score": 0.0, + "lddt_score": 0.0, + "best_score": 0.0, + "chain_mapping": None + }) + if opts.output is not None: + with open(opts.output, "w") as outfile: + outfile.write(json.dumps(result, indent=4)) if __name__ == '__main__':