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()