diff --git a/modules/bindings/pymod/export_tmalign.cc b/modules/bindings/pymod/export_tmalign.cc index 060421d01230bb5a35e27f3a6b4ec3c09bd889b6..352e03e35dd66d482c282ea11a34469af7fa33be 100644 --- a/modules/bindings/pymod/export_tmalign.cc +++ b/modules/bindings/pymod/export_tmalign.cc @@ -40,9 +40,17 @@ ost::bindings::TMAlignResult WrapTMAlignView(const ost::mol::ChainView& chain1, ost::bindings::MMAlignResult WrapMMAlignView(const ost::mol::EntityView& ent1, const ost::mol::EntityView& ent2, - bool fast) { + bool fast, + boost::python::dict& mapping) { + boost::python::list keys(mapping.keys()); + boost::python::list values(mapping.values()); + std::map<String, String> m_mapping; + for(uint i = 0; i < boost::python::len(keys); ++i) { + m_mapping[boost::python::extract<String>(keys[i])] = + boost::python::extract<String>(values[i]); + } - return ost::bindings::WrappedMMAlign(ent1, ent2, fast); + return ost::bindings::WrappedMMAlign(ent1, ent2, fast, m_mapping); } void export_TMAlign() { @@ -84,5 +92,6 @@ void export_TMAlign() { arg("fast")=false)); def("WrappedMMAlign", &WrapMMAlignView, (arg("ent1"), arg("ent2"), - arg("fast")=false)); + arg("fast")=false, + arg("mapping")=boost::python::dict())); } diff --git a/modules/bindings/src/wrap_tmalign.cc b/modules/bindings/src/wrap_tmalign.cc index 9be1639a4f601ffddcc4d41d8fa85c9cb9a04ba2..91544c06ce2973d026ea15a3eba932a1d3e556f9 100644 --- a/modules/bindings/src/wrap_tmalign.cc +++ b/modules/bindings/src/wrap_tmalign.cc @@ -428,9 +428,10 @@ MMAlignResult WrappedMMAlign(const std::vector<geom::Vec3List>& pos_one, const ost::seq::SequenceList& seq2, const std::vector<bool>& rna1, const std::vector<bool>& rna2, - bool fast) { + bool fast, + const std::map<int, int>& mapping) { - std::map<int,int> chainmap; + std::map<int, int> chainmap(mapping); // input checks if(pos_one.empty() || pos_two.empty()) { @@ -1081,7 +1082,8 @@ TMAlignResult WrappedTMAlign(const ost::mol::ChainView& chain1, MMAlignResult WrappedMMAlign(const ost::mol::EntityView& ent1, const ost::mol::EntityView& ent2, - bool fast) { + bool fast, + const std::map<String, String>& mapping) { ost::mol::ChainViewList chains1 = ent1.GetChainList(); int n1 = chains1.size(); std::vector<geom::Vec3List> pos1(n1); @@ -1108,7 +1110,70 @@ MMAlignResult WrappedMMAlign(const ost::mol::EntityView& ent1, s2.AddSequence(s); } - return WrappedMMAlign(pos1, pos2, s1, s2, rna1, rna2, fast); + std::map<int,int> int_mapping; + if(!mapping.empty()) { + // OpenStructure mapping is a dict with target chains as key and + // model chains as values. Target is ent2 and model is ent1. + // USalign needs the mapping the other way around, + // i.e. the chain idx of ent1 as key and chain idx of ent2 as value + std::set<int> mapped_trg_chains; + std::set<int> mapped_mdl_chains; + for(auto it = mapping.begin(); it != mapping.end(); ++it) { + String trg_cname = it->first; + String mdl_cname = it->second; + int trg_idx = -1; + int mdl_idx = -1; + + for(int i = 0; i < n1; ++i) { + if(s1[i].GetName() == mdl_cname) { + mdl_idx = i; + break; + } + } + + for(int i = 0; i < n2; ++i) { + if(s2[i].GetName() == trg_cname) { + trg_idx = i; + break; + } + } + + if(mdl_idx == -1) { + std::stringstream ss; + ss << "Specified \""<<mdl_cname<<"\" as mdl chain name in custom "; + ss << "mapping. Mdl has no mappable chain with this name."; + throw ost::Error(ss.str()); + } + + if(mapped_mdl_chains.find(mdl_idx) != mapped_mdl_chains.end()) { + std::stringstream ss; + ss << "\""<<mdl_cname<<"\" appears more than once as mdl chain in "; + ss << "custom mapping"; + throw ost::Error(ss.str()); + } + + if(trg_idx == -1) { + std::stringstream ss; + ss << "Specified \""<<trg_cname<<"\" as trg chain name in custom "; + ss << "mapping. Trg has no mappable chain with this name."; + throw ost::Error(ss.str()); + } + + if(mapped_trg_chains.find(trg_idx) != mapped_trg_chains.end()) { + std::stringstream ss; + ss << "\""<<trg_cname<<"\" appears more than once as trg chain in "; + ss << "custom mapping"; + throw ost::Error(ss.str()); + } + + int_mapping[mdl_idx] = trg_idx; + mapped_trg_chains.insert(trg_idx); + mapped_mdl_chains.insert(mdl_idx); + } + } + + return WrappedMMAlign(pos1, pos2, s1, s2, rna1, rna2, fast, + int_mapping); } }} //ns diff --git a/modules/bindings/src/wrap_tmalign.hh b/modules/bindings/src/wrap_tmalign.hh index 282209ca57597bef4e5d5a3f15fbbaafb3e2dd0a..bb96ff28c9a8498a84daf07408ce7ecc352ef19b 100644 --- a/modules/bindings/src/wrap_tmalign.hh +++ b/modules/bindings/src/wrap_tmalign.hh @@ -104,7 +104,9 @@ MMAlignResult WrappedMMAlign(const std::vector<geom::Vec3List>& pos_one, const ost::seq::SequenceList& seq2, const std::vector<bool>& rna1, const std::vector<bool>& rna2, - bool fast = false); + bool fast = false, + const std::map<int, int>& mapping = + std::map<int,int>()); TMAlignResult WrappedTMAlign(const ost::mol::ChainView& ent1, const ost::mol::ChainView& ent2, @@ -112,7 +114,9 @@ TMAlignResult WrappedTMAlign(const ost::mol::ChainView& ent1, MMAlignResult WrappedMMAlign(const ost::mol::EntityView& ent1, const ost::mol::EntityView& ent2, - bool fast = false); + bool fast = false, + const std::map<String, String>& mapping = + std::map<String, String>()); }} //ns #endif diff --git a/modules/mol/alg/pymod/scoring.py b/modules/mol/alg/pymod/scoring.py index 530a881bea6121795047225984484367a784d858..4cfcc614da4fa81dba62c2ba10343fe7beaa06c5 100644 --- a/modules/mol/alg/pymod/scoring.py +++ b/modules/mol/alg/pymod/scoring.py @@ -139,9 +139,7 @@ class Scorer: :type n_max_naive: :class:`int` :param oum: Override USalign Mapping. Inject mapping of :class:`Scorer` object into USalign to compute TM-score. Experimental feature - with limitations. Only works if external *usalign_exec* is - provided that is reasonably new and contains the respective - feature. + with limitations. :type oum: :class:`bool` """ def __init__(self, model, target, resnum_alignments=False, @@ -223,12 +221,6 @@ class Scorer: raise RuntimeError(f"USalign exec ({usalign_exec}) " f"is not executable") - # this limitation can be removed as soon as custom chain mappings can - # be injected in the OpenStructure internal USalign code - if oum and (usalign_exec is None): - raise RuntimeError("Must provide external USalign exec if oum " - "enabled") - self.resnum_alignments = resnum_alignments self.cad_score_exec = cad_score_exec self.usalign_exec = usalign_exec @@ -2103,7 +2095,14 @@ class Scorer: def _compute_tmscore(self): res = None - if self.usalign_exec is not None: + if self.usalign_exec is None: + if self.oum: + flat_mapping = self.mapping.GetFlatMapping() + res = res = bindings.WrappedMMAlign(self.model, self.target, + mapping=flat_mapping) + else: + res = bindings.WrappedMMAlign(self.model, self.target) + else: if self.oum: flat_mapping = self.mapping.GetFlatMapping() res = tmtools.USAlign(self.model, self.target, @@ -2112,8 +2111,6 @@ class Scorer: else: 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()