diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index 3e0f907ae284424631f868c9d145bae6586f67b9..135c5a6936cfef202bc3a9ffbded2d6d26e91bd5 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -5,9 +5,23 @@ Changes in Release 2.4.0
    based, natively supports complexes and is able to score DNA/RNA. The
    deprecated lDDT implementation remains for consistency.
  * Add algorithms to establish one to one mappings between chains in a reference
-   structure and a model.
+   structure and a model. Chain mappings can be derived by optimizing oligomeric
+   lDDT, RMSD or QS-score. 
  * Substitution matrixes for RNA/DNA to compute alignments: seq.alg.IDENTITY and
-   seq.alg.NUC44
+   seq.alg.NUC44.
+ * Add binding to DockQ (https://github.com/bjornwallner/DockQ) as well as an
+   OpenStructure specific implementation of it. Also allows to extract CAPRI
+   specific oligo scores (fnat, fnonnat, irmsd, lrmsd etc.)
+ * Reimplentation of QS-score in mol.alg.qsscore. Implements speedups and
+   heavy caching which benefits heavy enumeration approaches in chain mapping.
+ * Stereochemistry related algorithms in mol.alg.stereochemistry. Identifies 
+   clashes and non-sensible bond lengths/angles based on parameterizations from
+   CCP4 MON_LIB.
+ * Better compression in OMF structure format.
+ * One central scoring object: mol.alg.scoring.Scorer. Access to all
+   OpenStructure specific scoring capabilities including required
+   pre-processing of model/reference (cleanup, stereochemistry checks,
+   chain mapping etc.).
  * Several minor bug fixes and improvements.
 
 Changes in Release 2.3.1
diff --git a/modules/conop/tests/test_heuristic_builder.cc b/modules/conop/tests/test_heuristic_builder.cc
deleted file mode 100644
index 8ec8b697c5db071f340b3b0e8a28d62a8ba19468..0000000000000000000000000000000000000000
--- a/modules/conop/tests/test_heuristic_builder.cc
+++ /dev/null
@@ -1,224 +0,0 @@
-// ------------------------------------------------------------------------------
-// This file is part of the OpenStructure project <www.openstructure.org>
-
-// Copyright (C) 2008-2020 by the OpenStructure authors
-
-// This library is free software; you can redistribute it and/or modify it under
-// the terms of the GNU Lesser General Public License as published by the Free
-// Software Foundation; either version 3.0 of the License, or (at your option)
-// any later version.
-// This library is distributed in the hope that it will be useful, but WITHOUT
-// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
-// FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
-// details.
-
-// You should have received a copy of the GNU Lesser General Public License
-// along with this library; if not, write to the Free Software Foundation, Inc.,
-// 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
-// ------------------------------------------------------------------------------
-
-#include <ost/mol/mol.hh>
-#include <ost/conop/heuristic_builder.hh>
-
-#define BOOST_TEST_DYN_LINK
-#include <boost/test/unit_test.hpp>
-
-#include <ost/log.hh>
-using boost::unit_test_framework::test_suite;
-using namespace ost;
-using namespace ost::conop;
-using namespace ost::mol;
-namespace {
-} // anon ns
-
-ResidueHandle make_arg(ChainHandle chain) 
-{
-  XCSEditor e=chain.GetEntity().EditXCS();
-  ResidueHandle res = e.AppendResidue(chain, "ARG");
-  e.InsertAtom(res, "N",geom::Vec3(20.202,33.112,58.011));
-  e.InsertAtom(res, "CA",geom::Vec3(19.396,31.903,58.033));
-  e.InsertAtom(res, "C",geom::Vec3(18.608,31.739,59.328));
-  e.InsertAtom(res, "O",geom::Vec3(17.651,30.965,59.381));
-  e.InsertAtom(res, "CB",geom::Vec3(20.284,30.681,57.801));
-  e.InsertAtom(res, "CG",geom::Vec3(20.665,30.488,56.342));
-  e.InsertAtom(res, "CD",geom::Vec3(21.557,29.281,56.154));
-  e.InsertAtom(res, "NE",geom::Vec3(22.931,29.557,56.551));
-  e.InsertAtom(res, "CZ",geom::Vec3(23.901,28.653,56.528));
-  e.InsertAtom(res, "NH1",geom::Vec3(23.640,27.417,56.130));
-  e.InsertAtom(res, "NH2",geom::Vec3(25.132,28.980,56.893));
-  return res;
-}
-
-ResidueHandle make_leu(ChainHandle chain) 
-{
-  XCSEditor e=chain.GetEntity().EditXCS();  
-  ResidueHandle res=e.AppendResidue(chain, "LEU");
-
-  e.InsertAtom(res, "N", geom::Vec3(19.003,32.473,60.366));
-  e.InsertAtom(res, "CA", geom::Vec3(18.330,32.402,61.664));
-  e.InsertAtom(res, "C", geom::Vec3(17.884,33.787,62.117));
-  e.InsertAtom(res, "O", geom::Vec3(17.853,34.091,63.308));
-  e.InsertAtom(res, "CB", geom::Vec3(19.269,31.793,62.710));
-  e.InsertAtom(res, "CG", geom::Vec3(19.695,30.340,62.501));
-  e.InsertAtom(res, "CD1", geom::Vec3(20.585,29.897,63.648));
-  e.InsertAtom(res, "CD2", geom::Vec3(18.461,29.459,62.420));
-  return res;
-}
-
-ResidueHandle make_defective_leu(ChainHandle chain) 
-{
-  XCSEditor e=chain.GetEntity().EditXCS();  
-  ResidueHandle res=e.AppendResidue(chain, "LEU");
-
-  e.InsertAtom(res, "N", geom::Vec3(19.003,32.473,60.366));
-  e.InsertAtom(res, "CA", geom::Vec3(18.330,32.402,61.664));
-  e.InsertAtom(res, "C", geom::Vec3(17.884,33.787,62.117));
-  e.InsertAtom(res, "O", geom::Vec3(17.853,34.091,63.308));
-  e.InsertAtom(res, "CB", geom::Vec3(19.269,31.793,102.710));
-  e.InsertAtom(res, "CG", geom::Vec3(19.695,30.340,62.501));
-  e.InsertAtom(res, "CD1", geom::Vec3(20.585,29.897,63.648));
-  e.InsertAtom(res, "CD2", geom::Vec3(18.461,29.459,62.420));
-  return res;
-}
-
-void verify_connectivity_x(const ResidueHandle& res) 
-{
-  BOOST_CHECK(BondExists(res.FindAtom("XN"),
-                         res.FindAtom("XCA")));
-  BOOST_CHECK(BondExists(res.FindAtom("XCA"),
-                         res.FindAtom("XC")));
-  BOOST_CHECK(BondExists(res.FindAtom("XC"),
-                         res.FindAtom("XO")));
-  BOOST_CHECK(BondExists(res.FindAtom("XCA"),
-                         res.FindAtom("XCB")));
-  BOOST_CHECK(BondExists(res.FindAtom("XCB"),
-                         res.FindAtom("XCG")));
-  if (res.GetKey()=="ARG") {
-
-    BOOST_CHECK(BondExists(res.FindAtom("XCB"),
-                           res.FindAtom("XCG")));
-    BOOST_CHECK(BondExists(res.FindAtom("XCG"),
-                           res.FindAtom("XCD")));
-    BOOST_CHECK(BondExists(res.FindAtom("XCD"),
-                           res.FindAtom("XNE")));
-    BOOST_CHECK(BondExists(res.FindAtom("XNE"),
-                           res.FindAtom("XCZ")));
-    BOOST_CHECK(BondExists(res.FindAtom("XCZ"),
-                           res.FindAtom("XNH1")));
-    BOOST_CHECK(BondExists(res.FindAtom("XCZ"),
-                           res.FindAtom("XNH2")));
-    // TODO: Check that no other atoms are connected!
-  }
-  if (res.GetKey()=="ILE") {
-    BOOST_CHECK(BondExists(res.FindAtom("XCG"),
-                           res.FindAtom("XCD1")));
-    BOOST_CHECK(BondExists(res.FindAtom("XCG"),
-                           res.FindAtom("XCD2")));
-    // TODO: Check that no other atoms are connected!
-  }
-}
-
-void verify_connectivity(const ResidueHandle& res) 
-{
-  BOOST_CHECK(BondExists(res.FindAtom("N"),
-                        res.FindAtom("CA")));
-  BOOST_CHECK(BondExists(res.FindAtom("CA"),
-                         res.FindAtom("C")));
-  BOOST_CHECK(BondExists(res.FindAtom("C"),
-                         res.FindAtom("O")));
-  BOOST_CHECK(BondExists(res.FindAtom("CA"),
-                         res.FindAtom("CB")));
-  BOOST_CHECK(BondExists(res.FindAtom("CB"),
-                         res.FindAtom("CG")));
-  if (res.GetKey()=="ARG") {
-
-    BOOST_CHECK(BondExists(res.FindAtom("CB"),
-                           res.FindAtom("CG")));
-    BOOST_CHECK(BondExists(res.FindAtom("CG"),
-                           res.FindAtom("CD")));
-    BOOST_CHECK(BondExists(res.FindAtom("CD"),
-                           res.FindAtom("NE")));
-    BOOST_CHECK(BondExists(res.FindAtom("NE"),
-                           res.FindAtom("CZ")));
-    BOOST_CHECK(BondExists(res.FindAtom("CZ"),
-                           res.FindAtom("NH1")));
-    BOOST_CHECK(BondExists(res.FindAtom("CZ"),
-                           res.FindAtom("NH2")));
-    // TODO: Check that no other atoms are connected!
-  }
-  if (res.GetKey()=="ILE") {
-    BOOST_CHECK(BondExists(res.FindAtom("CG"),
-                           res.FindAtom("CD1")));
-    BOOST_CHECK(BondExists(res.FindAtom("CG"),
-                           res.FindAtom("CD2")));
-    // TODO: Check that no other atoms are connected!
-  }
-}
-
-BOOST_AUTO_TEST_SUITE( conop );
-
-
-BOOST_AUTO_TEST_CASE(name_based_connect) 
-{
-  EntityHandle e=CreateEntity();
-  ChainHandle c=e.EditXCS().InsertChain("A");
-  ResidueHandle ile=make_leu(c);
-  ResidueHandle arg=make_arg(c);
-  HeuristicBuilder heuristic_builder;  
-  AtomHandleList atoms =e .GetAtomList();
-  for (AtomHandleList::const_iterator i = atoms.begin(), e = atoms.end(); i!=e; ++i ){
-    heuristic_builder.FillAtomProps(*i);
-  }
-
-  EntityHandle de=CreateEntity();
-  ChainHandle dc=de.EditXCS().InsertChain("A");
-  ResidueHandle dile=make_defective_leu(dc);
-  HeuristicBuilder dheuristic_builder;
-  dheuristic_builder.SetBondFeasibilityCheck(false);
-  atoms = de.GetAtomList();
-  for (AtomHandleList::const_iterator i = atoms.begin(), e = atoms.end(); i!=e; ++i ){
-    dheuristic_builder.FillAtomProps(*i);
-  }
-   
-  BOOST_TEST_MESSAGE("running distance based checks on arginine");
-  heuristic_builder.ConnectAtomsOfResidue(arg);
-  verify_connectivity(arg);
-  BOOST_TEST_MESSAGE("running distance based checks on leu");
-  heuristic_builder.ConnectAtomsOfResidue(ile);
-  verify_connectivity(ile);
-  
-  BOOST_TEST_MESSAGE("running distance based checks on defective leu");
-  dheuristic_builder.ConnectAtomsOfResidue(dile);
-  verify_connectivity(dile);  
-}
-
-BOOST_AUTO_TEST_CASE(test_assign_torsions){
-  EntityHandle e=CreateEntity();
-  ChainHandle c=e.EditXCS().InsertChain("A");
-  ResidueHandle l1=make_leu(c);
-  ResidueHandle a2=make_arg(c);
-  ResidueHandle l3=make_leu(c);
-  l1.SetChemClass(ChemClass(ChemClass::L_PEPTIDE_LINKING));
-  a2.SetChemClass(ChemClass(ChemClass::L_PEPTIDE_LINKING));  
-  l3.SetChemClass(ChemClass(ChemClass::L_PEPTIDE_LINKING));
-  HeuristicBuilder heuristic_builder;
-  AtomHandleList atoms = e.GetAtomList();
-  for (AtomHandleList::const_iterator i = atoms.begin(), e = atoms.end(); i!=e; ++i ){
-    heuristic_builder.FillAtomProps(*i);
-  }
-  heuristic_builder.ConnectAtomsOfResidue(l1);
-  heuristic_builder.ConnectAtomsOfResidue(a2);
-  heuristic_builder.ConnectAtomsOfResidue(l3);
-  XCSEditor edi=e.EditXCS();
-  edi.Connect(l1.FindAtom("C"), a2.FindAtom("N"));
-  edi.Connect(a2.FindAtom("C"), l3.FindAtom("N"));  
-  heuristic_builder.AssignTorsions(c);
-
-  BOOST_CHECK(a2.GetPhiTorsion().IsValid());
-  BOOST_CHECK(l3.GetPhiTorsion().IsValid());
-
-  BOOST_CHECK(l1.GetPsiTorsion().IsValid());
-  BOOST_CHECK(a2.GetPsiTorsion().IsValid());
-}
-
-BOOST_AUTO_TEST_SUITE_END( );
diff --git a/modules/io/doc/mmcif.rst b/modules/io/doc/mmcif.rst
index 41a6ed5f2e59c0c13e915d1bc527d2de5469b498..63bfd882298c4ef22dae31c1ef2d29919296863f 100644
--- a/modules/io/doc/mmcif.rst
+++ b/modules/io/doc/mmcif.rst
@@ -96,7 +96,8 @@ of the annotation available.
 
   .. attribute:: method
 
-    Stores the experimental method used to create the structure.
+    Stores the experimental method used to create the structure
+    (|exptl.method|_).
 
     Also available as :meth:`GetMethod`. May also be modified by
     :meth:`SetMethod`.
@@ -1308,6 +1309,9 @@ of the annotation available.
 
     See :attr:`bond_order`
 
+.. |exptl.method| replace:: ``exptl.method``
+.. _exptl.method: https://mmcif.wwpdb.org/dictionaries/mmcif_pdbx_v50.dic/Items/_exptl.method.html
+
 ..  LocalWords:  cas isbn pubmed asu seqres conop ConnectAll casp COMPND OBSLTE
 ..  LocalWords:  SPRSDE pdb func autofunction exptl attr pdbx oper conf spr dif
 ..  LocalWords:  biounits biounit uniprot UNP seqs AddMMCifPDBChainTr cif asym
diff --git a/modules/mol/alg/pymod/chain_mapping.py b/modules/mol/alg/pymod/chain_mapping.py
index c9e9aec85a40f46929024d8510058a194b1197fd..c8bfa7c7789ca7440cdb84c55ce5ea6bf03709cb 100644
--- a/modules/mol/alg/pymod/chain_mapping.py
+++ b/modules/mol/alg/pymod/chain_mapping.py
@@ -723,7 +723,8 @@ class ChainMapper:
     def GetlDDTMapping(self, model, inclusion_radius=15.0,
                        thresholds=[0.5, 1.0, 2.0, 4.0], strategy="naive",
                        steep_opt_rate = None, full_n_mdl_chains = None,
-                       block_seed_size = 5, block_blocks_per_chem_group = 5):
+                       block_seed_size = 5, block_blocks_per_chem_group = 5,
+                       chem_mapping_result = None):
         """ Identify chain mapping by optimizing lDDT score
 
         Maps *model* chain sequences to :attr:`~chem_groups` and find mapping
@@ -789,6 +790,11 @@ class ChainMapper:
                                             are extended in an initial search
                                             for high scoring local solutions.
         :type block_blocks_per_chem_group: :class:`int`
+        :param chem_mapping_result: Pro param. The result of
+                                    :func:`~GetChemMapping` where you provided
+                                    *model*. If set, *model* parameter is not
+                                    used.
+        :type chem_mapping_result: :class:`tuple`
         :returns: A :class:`MappingResult`
         """
 
@@ -796,7 +802,10 @@ class ChainMapper:
         if strategy not in strategies:
             raise RuntimeError(f"Strategy must be in {strategies}")
 
-        chem_mapping, chem_group_alns, mdl = self.GetChemMapping(model)
+        if chem_mapping_result is None:
+            chem_mapping, chem_group_alns, mdl = self.GetChemMapping(model)
+        else:
+            chem_mapping, chem_group_alns, mdl = chem_mapping_result
 
         ref_mdl_alns =  _GetRefMdlAlns(self.chem_groups,
                                        self.chem_group_alignments,
@@ -854,7 +863,7 @@ class ChainMapper:
     def GetQSScoreMapping(self, model, contact_d = 12.0, strategy = "naive",
                           full_n_mdl_chains = None, block_seed_size = 5,
                           block_blocks_per_chem_group = 5,
-                          steep_opt_rate = None):
+                          steep_opt_rate = None, chem_mapping_result = None):
         """ Identify chain mapping based on QSScore
 
         Scoring is based on CA/C3' positions which are present in all chains of
@@ -892,6 +901,11 @@ class ChainMapper:
         :type contact_d: :class:`float` 
         :param strategy: Strategy for sampling, must be in ["naive"]
         :type strategy: :class:`str`
+        :param chem_mapping_result: Pro param. The result of
+                                    :func:`~GetChemMapping` where you provided
+                                    *model*. If set, *model* parameter is not
+                                    used.
+        :type chem_mapping_result: :class:`tuple`
         :returns: A :class:`MappingResult`
         """
 
@@ -899,7 +913,10 @@ class ChainMapper:
         if strategy not in strategies:
             raise RuntimeError(f"strategy must be {strategies}")
 
-        chem_mapping, chem_group_alns, mdl = self.GetChemMapping(model)
+        if chem_mapping_result is None:
+            chem_mapping, chem_group_alns, mdl = self.GetChemMapping(model)
+        else:
+            chem_mapping, chem_group_alns, mdl = chem_mapping_result
         ref_mdl_alns =  _GetRefMdlAlns(self.chem_groups,
                                        self.chem_group_alignments,
                                        chem_mapping,
@@ -952,7 +969,8 @@ class ChainMapper:
 
     def GetRigidMapping(self, model, strategy = "greedy_single_gdtts",
                         single_chain_gdtts_thresh=0.4, subsampling=None,
-                        first_complete=False, iterative_superposition=False):
+                        first_complete=False, iterative_superposition=False,
+                        chem_mapping_result = None):
         """Identify chain mapping based on rigid superposition
 
         Superposition and scoring is based on CA/C3' positions which are present
@@ -1007,6 +1025,11 @@ class ChainMapper:
                                         as oposed to
                                         :func:`ost.mol.alg.SuperposeSVD`
         :type iterative_superposition: :class:`bool`
+        :param chem_mapping_result: Pro param. The result of
+                                    :func:`~GetChemMapping` where you provided
+                                    *model*. If set, *model* parameter is not
+                                    used.
+        :type chem_mapping_result: :class:`tuple`
         :returns: A :class:`MappingResult`
         """
 
@@ -1015,7 +1038,10 @@ class ChainMapper:
         if strategy not in strategies:
             raise RuntimeError(f"strategy must be {strategies}")
 
-        chem_mapping, chem_group_alns, mdl = self.GetChemMapping(model)
+        if chem_mapping_result is None:
+            chem_mapping, chem_group_alns, mdl = self.GetChemMapping(model)
+        else:
+            chem_mapping, chem_group_alns, mdl = chem_mapping_result
         ref_mdl_alns =  _GetRefMdlAlns(self.chem_groups,
                                        self.chem_group_alignments,
                                        chem_mapping,
@@ -1111,7 +1137,7 @@ class ChainMapper:
 
     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):
+                only_interchain=False, chem_mapping_result = None):
         """ Identify *topn* representations of *substructure* in *model*
 
         *substructure* defines a subset of :attr:`~target` for which one
@@ -1142,7 +1168,11 @@ class ChainMapper:
         :param only_interchain: Only score interchain contacts in lDDT. Useful
                                 if you want to identify interface patches.
         :type only_interchain: :class:`bool`
-
+        :param chem_mapping_result: Pro param. The result of
+                                    :func:`~GetChemMapping` where you provided
+                                    *model*. If set, *model* parameter is not
+                                    used.
+        :type chem_mapping_result: :class:`tuple`
         :returns: :class:`list` of :class:`ReprResult`
         """
 
@@ -1186,7 +1216,10 @@ class ChainMapper:
                                    "a backbone atom named CA or C3\'")
 
         # perform mapping and alignments on full structures
-        chem_mapping, chem_group_alns, mdl = self.GetChemMapping(model)
+        if chem_mapping_result is None:
+            chem_mapping, chem_group_alns, mdl = self.GetChemMapping(model)
+        else:
+            chem_mapping, chem_group_alns, mdl = chem_mapping_result
         ref_mdl_alns =  _GetRefMdlAlns(self.chem_groups,
                                        self.chem_group_alignments,
                                        chem_mapping,
diff --git a/modules/mol/alg/pymod/qsscore.py b/modules/mol/alg/pymod/qsscore.py
index 3d28456532e7eb94ee80a9ba5a2d88cf6d7bf485..db5079ef9fd8a3b4ddeb70bf3b0fa77d60db9182 100644
--- a/modules/mol/alg/pymod/qsscore.py
+++ b/modules/mol/alg/pymod/qsscore.py
@@ -396,6 +396,41 @@ class QSScorer:
 
         return self.FromFlatMapping(flat_mapping)
 
+    def ScoreInterface(self, trg_ch1, trg_ch2, mdl_ch1, mdl_ch2):
+        """ Computes QS-score only considering one interface
+
+        This only works for interfaces that are computed in :func:`Score`, i.e.
+        interfaces for which the alignments are set up correctly.
+
+        :param trg_ch1: Name of first interface chain in target
+        :type trg_ch1: :class:`str`
+        :param trg_ch2: Name of second interface chain in target
+        :type trg_ch2: :class:`str`
+        :param mdl_ch1: Name of first interface chain in model
+        :type mdl_ch1: :class:`str`
+        :param mdl_ch2: Name of second interface chain in model
+        :type mdl_ch2: :class:`str`
+        :returns: Result object of type :class:`QSScorerResult`
+        :raises: :class:`RuntimeError` if no aln for trg_ch1/mdl_ch1 or
+                 trg_ch2/mdl_ch2 is available.
+        """
+        if (trg_ch1, mdl_ch1) not in self.alns:
+            raise RuntimeError(f"No aln between trg_ch1 ({trg_ch1}) and "
+                               f"mdl_ch1 ({mdl_ch1}) available. Did you "
+                               f"construct the QSScorer object from a "
+                               f"MappingResult and are trg_ch1 and mdl_ch1 "
+                               f"mapped to each other?")
+        if (trg_ch2, mdl_ch2) not in self.alns:
+            raise RuntimeError(f"No aln between trg_ch1 ({trg_ch1}) and "
+                               f"mdl_ch1 ({mdl_ch1}) available. Did you "
+                               f"construct the QSScorer object from a "
+                               f"MappingResult and are trg_ch1 and mdl_ch1 "
+                               f"mapped to each other?")
+        trg_int = (trg_ch1, trg_ch2)
+        mdl_int = (mdl_ch1, mdl_ch2)
+        a, b, c, d = self._MappedInterfaceScores(trg_int, mdl_int)
+        return QSScorerResult(a, b, c, d)
+
     def FromFlatMapping(self, flat_mapping):
         """ Same as :func:`Score` but with flat mapping
 
diff --git a/modules/mol/alg/pymod/scoring.py b/modules/mol/alg/pymod/scoring.py
index 6d5b345ec9bb012c6b262c43bb0b587ff096641d..70f2dd5b2560df259ae3ba5d0fd02b678bc47dc0 100644
--- a/modules/mol/alg/pymod/scoring.py
+++ b/modules/mol/alg/pymod/scoring.py
@@ -109,10 +109,11 @@ 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 up to that
-                                       number of chains will be fully enumerated
-                                       to optimize for QS-score. Everything
-                                       above is treated with a heuristic.
+    :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 
@@ -204,12 +205,19 @@ class Scorer:
 
         self._qs_global = None
         self._qs_best = None
-
-        self._dockq_interfaces = None
-        self._dockq_native_contacts = None
+        self._interface_qs_global = None
+        self._interface_qs_best = None
+
+        self._interfaces = None
+        self._native_contacts = None
+        self._model_contacts = None
+        self._fnat = None
+        self._fnonnat = None
+        self._irmsd = None
+        self._lrmsd = None
+        self._nonmapped_interfaces = None
+        self._nonmapped_interfaces_contacts = None
         self._dockq_scores = None
-        self._dockq_nonmapped_interfaces = None
-        self._dockq_nonmapped_interfaces_counts = None
         self._dockq_ave = None
         self._dockq_wave = None
         self._dockq_ave_full = None
@@ -356,15 +364,20 @@ class Scorer:
         """
         if self._mapping is None:
             n_trg_chains = len(self.chain_mapper.target.chains)
-            if n_trg_chains <= self.naive_chain_mapping_thresh:
+            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")
+                                                        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)
+                                                        block_blocks_per_chem_group=6,
+                                                        chem_mapping_result=res)
             self._mapping = m
         return self._mapping
 
@@ -476,40 +489,130 @@ class Scorer:
         return self._qs_best
 
     @property
-    def dockq_interfaces(self):
-        """ Interfaces considered in DockQ (i.e. nonzero native contacts)
-
-        DockQ is computed on interfaces as defined by :attr:`~mapping`.
+    def interfaces(self):
+        """ Interfaces with nonzero :attr:`native_contacts`
 
         :type: :class:`list` of :class:`tuple` with 4 elements each:
                (trg_ch1, trg_ch2, mdl_ch1, mdl_ch2)
         """
-        if self._dockq_interfaces is None:
-            self._compute_dockq()
-        return self._dockq_interfaces
+        if self._interfaces is None:
+            self._compute_per_interface_scores()
+        return self._interfaces
+
+    @property
+    def interface_qs_global(self):
+        """ QS-score for each interface in :attr:`interfaces`
+
+        :type: :class:`list` of :class:`float`
+        """
+        if self._interface_qs_global is None:
+            self._compute_per_interface_scores()
+        return self._interface_qs_global
     
     @property
-    def dockq_native_contacts(self):
-        """ N native contacts for interfaces in :attr:`~dockq_interfaces`
+    def interface_qs_best(self):
+        """ QS-score for each interface in :attr:`interfaces`
+
+        Only computed on aligned residues
+
+        :type: :class:`list` of :class:`float`
+        """
+        if self._interface_qs_best is None:
+            self._compute_per_interface_scores()
+        return self._interface_qs_best
+    
+    @property
+    def native_contacts(self):
+        """ N native contacts for interfaces in :attr:`~interfaces`
+
+        A contact is a pair or residues from distinct chains that have
+        a minimal heavy atom distance < 5A
+
+        :type: :class:`list` of :class:`int`
+        """
+        if self._native_contacts is None:
+            self._compute_per_interface_scores()
+        return self._native_contacts
+
+    @property
+    def model_contacts(self):
+        """ N model contacts for interfaces in :attr:`~interfaces`
+
+        A contact is a pair or residues from distinct chains that have
+        a minimal heavy atom distance < 5A
 
         :type: :class:`list` of :class:`int`
         """
-        if self._dockq_native_contacts is None:
-            self._compute_dockq()
-        return self._dockq_native_contacts
+        if self._model_contacts is None:
+            self._compute_per_interface_scores()
+        return self._model_contacts
 
     @property
     def dockq_scores(self):
-        """ DockQ scores for interfaces in :attr:`~dockq_interfaces` 
+        """ DockQ scores for interfaces in :attr:`~interfaces` 
 
         :class:`list` of :class:`float`
         """
         if self._dockq_scores is None:
-            self._compute_dockq()
+            self._compute_per_interface_scores()
         return self._dockq_scores
 
     @property
-    def dockq_nonmapped_interfaces(self):
+    def fnat(self):
+        """ fnat scores for interfaces in :attr:`~interfaces` 
+
+        fnat: Fraction of native contacts that are also present in model
+
+        :class:`list` of :class:`float`
+        """
+        if self._fnat is None:
+            self._compute_per_interface_scores()
+        return self._fnat
+
+    @property
+    def fnonnat(self):
+        """ fnonnat scores for interfaces in :attr:`~interfaces` 
+
+        fnat: Fraction of model contacts that are not present in target
+
+        :class:`list` of :class:`float`
+        """
+        if self._fnonnat is None:
+            self._compute_per_interface_scores()
+        return self._fnonnat
+
+    @property
+    def irmsd(self):
+        """ irmsd scores for interfaces in :attr:`~interfaces` 
+
+        irmsd: RMSD of interface (RMSD computed on N, CA, C, O atoms) which
+        consists of each residue that has at least one heavy atom within 10A of
+        other chain.
+
+        :class:`list` of :class:`float`
+        """
+        if self._irmsd is None:
+            self._compute_per_interface_scores()
+        return self._irmsd
+
+    @property
+    def lrmsd(self):
+        """ lrmsd scores for interfaces in :attr:`~interfaces` 
+
+        lrmsd: The interfaces are superposed based on the receptor (rigid
+        min RMSD superposition) and RMSD for the ligand is reported.
+        Superposition and RMSD are based on N, CA, C and O positions,
+        receptor is the chain contributing to the interface with more
+        residues in total.
+
+        :class:`list` of :class:`float`
+        """
+        if self._lrmsd is None:
+            self._compute_per_interface_scores()
+        return self._lrmsd
+
+    @property
+    def nonmapped_interfaces(self):
         """ Interfaces present in target that are not mapped
 
         At least one of the chains is not present in target
@@ -517,19 +620,19 @@ class Scorer:
         :type: :class:`list` of :class:`tuple` with two elements each:
                (trg_ch1, trg_ch2)
         """
-        if self._dockq_nonmapped_interfaces is None:
-            self._compute_dockq()
-        return self._dockq_nonmapped_interfaces
+        if self._nonmapped_interfaces is None:
+            self._compute_per_interface_scores()
+        return self._nonmapped_interfaces
 
     @property
-    def dockq_nonmapped_interfaces_counts(self):
-        """ Number of native contacts in :attr:`~dockq_nonmapped_interfaces`
+    def nonmapped_interfaces_contacts(self):
+        """ Number of native contacts in :attr:`~nonmapped_interfaces`
 
         :type: :class:`list` of :class:`int`
         """
-        if self._dockq_nonmapped_interfaces_counts is None:
-            self._compute_dockq()
-        return self._dockq_nonmapped_interfaces_counts
+        if self._nonmapped_interfaces_contacts is None:
+            self._compute_per_interface_scores()
+        return self._nonmapped_interfaces_contacts
         
     @property
     def dockq_ave(self):
@@ -542,42 +645,42 @@ class Scorer:
         :type: :class:`float`
         """
         if self._dockq_ave is None:
-            self._compute_dockq()
+            self._compute_per_interface_scores()
         return self._dockq_ave
     
     @property
     def dockq_wave(self):
-        """ Same as :attr:`dockq_ave`, weighted by :attr:`dockq_native_contacts`
+        """ Same as :attr:`dockq_ave`, weighted by :attr:`native_contacts`
 
         :type: :class:`float`
         """
         if self._dockq_wave is None:
-            self._compute_dockq()
+            self._compute_per_interface_scores()
         return self._dockq_wave
         
     @property
     def dockq_ave_full(self):
         """ Same as :attr:`~dockq_ave` but penalizing for missing interfaces
 
-        Interfaces in :attr:`dockq_nonmapped_interfaces` are added as 0.0
+        Interfaces in :attr:`nonmapped_interfaces` are added as 0.0
         in average computation.
 
         :type: :class:`float`
         """
         if self._dockq_ave_full is None:
-            self._compute_dockq()
+            self._compute_per_interface_scores()
         return self._dockq_ave_full
     
     @property
     def dockq_wave_full(self):
         """ Same as :attr:`~dockq_ave_full`, but weighted
 
-        Interfaces in :attr:`dockq_nonmapped_interfaces` are added as 0.0 in
+        Interfaces in :attr:`nonmapped_interfaces` are added as 0.0 in
         average computations and the respective weights are derived from
-        :attr:`~dockq_nonmapped_interfaces_counts` 
+        :attr:`~nonmapped_interfaces_contacts` 
         """
         if self._dockq_wave_full is None:
-            self._compute_dockq()
+            self._compute_per_interface_scores()
         return self._dockq_wave_full
 
     @property
@@ -856,24 +959,25 @@ class Scorer:
         self._qs_global = qs_score_result.QS_global
         self._qs_best = qs_score_result.QS_best
 
-    def _compute_dockq(self):
-        if not self.resnum_alignments:
-            raise RuntimeError("DockQ computations rely on residue numbers "
-                               "that are consistent between target and model "
-                               "chains, i.e. only work if resnum_alignments "
-                               "is True at Scorer construction.")
-
+    def _compute_per_interface_scores(self):
         flat_mapping = self.mapping.GetFlatMapping()
         # list of [trg_ch1, trg_ch2, mdl_ch1, mdl_ch2]
-        self._dockq_interfaces = list()
+        self._interfaces = list()
         # lists with respective values for these interfaces
-        self._dockq_native_contacts = list()
+        self._native_contacts = list()
+        self._model_contacts = list()
+        self._interface_qs_global = list()
+        self._interface_qs_best = list()
         self._dockq_scores = list()
+        self._fnat = list()
+        self._fnonnat = list()
+        self._irmsd = list()
+        self._lrmsd = list()
 
         # list of interfaces which are present in target but not mapped, i.e.
         # not present in mdl
-        self._dockq_nonmapped_interfaces = list()
-        self._dockq_nonmapped_interfaces_counts = list()
+        self._nonmapped_interfaces = list()
+        self._nonmapped_interfaces_contacts = list()
 
         nonmapped_interface_counts = list()
 
@@ -898,10 +1002,6 @@ class Scorer:
             s.AttachView(self.model.Select(f"cname={cname}"))
             mdl_seqs[ch.GetName()] = s
 
-        trg_pep_chains = [s.GetName() for s in self.chain_mapper.polypep_seqs]
-        trg_nuc_chains = [s.GetName() for s in self.chain_mapper.polynuc_seqs]
-        trg_pep_chains = set(trg_pep_chains)
-        trg_nuc_chains = set(trg_nuc_chains)
         dockq_alns = dict()
         for trg_ch, mdl_ch in flat_mapping.items():
             if trg_ch in pep_seqs:
@@ -922,10 +1022,19 @@ class Scorer:
                                       trg_ch1, trg_ch2, ch1_aln=aln1,
                                       ch2_aln=aln2)
                     if res["nnat"] > 0:
-                        self._dockq_interfaces.append((trg_ch1, trg_ch2,
-                                                       mdl_ch1, mdl_ch2))
-                        self._dockq_native_contacts.append(res["nnat"])
+                        self._interfaces.append((trg_ch1, trg_ch2,
+                                                 mdl_ch1, mdl_ch2))
+                        self._native_contacts.append(res["nnat"])
+                        self._model_contacts.append(res["nmdl"])
+                        self._fnat.append(res["fnat"])
+                        self._fnonnat.append(res["fnonnat"])
+                        self._irmsd.append(res["irmsd"])
+                        self._lrmsd.append(res["lrmsd"])
                         self._dockq_scores.append(res["DockQ"])
+                        qs_res = self.qs_scorer.ScoreInterface(trg_ch1, trg_ch2,
+                                                               mdl_ch1, mdl_ch2)
+                        self._interface_qs_best.append(qs_res.QS_best)
+                        self._interface_qs_global.append(qs_res.QS_global)
                 else:
                     # interface which is not covered by mdl... let's run DockQ
                     # with trg as trg/mdl in order to get the native contacts
@@ -935,20 +1044,19 @@ class Scorer:
                                       trg_ch1, trg_ch2, trg_ch1, trg_ch2)
                     nnat = res["nnat"]
                     if nnat > 0:
-                        self._dockq_nonmapped_interfaces.append((trg_ch1,
-                                                                 trg_ch2))
-                        self._dockq_nonmapped_interfaces_counts.append(nnat)
+                        self._nonmapped_interfaces.append((trg_ch1, trg_ch2))
+                        self._nonmapped_interfaces_contacts.append(nnat)
 
         # there are 4 types of combined scores
         # - simple average
         # - average weighted by native_contacts
         # - the two above including nonmapped_interfaces => set DockQ to 0.0
         scores = np.array(self._dockq_scores)
-        weights = np.array(self._dockq_native_contacts)
+        weights = np.array(self._native_contacts)
         self._dockq_ave = np.mean(scores)
         self._dockq_wave = np.sum(np.multiply(weights/np.sum(weights), scores))
-        scores = np.append(scores, [0.0]*len(self._dockq_nonmapped_interfaces))
-        weights = np.append(weights, self._dockq_nonmapped_interfaces_counts)
+        scores = np.append(scores, [0.0]*len(self._nonmapped_interfaces))
+        weights = np.append(weights, self._nonmapped_interfaces_contacts)
         self._dockq_ave_full = np.mean(scores)
         self._dockq_wave_full = np.sum(np.multiply(weights/np.sum(weights),
                                                    scores))
diff --git a/modules/mol/base/doc/editors.rst b/modules/mol/base/doc/editors.rst
index 71577df3659a187de0de1d57683cf155a16ceb86..e2218db714d771cd1c8ed66c6659b0148ed0f719 100644
--- a/modules/mol/base/doc/editors.rst
+++ b/modules/mol/base/doc/editors.rst
@@ -69,15 +69,21 @@ 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)
   
      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.
 
+     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: string
+     :type  residue_name: :class:`string`
+     :param deep: If set to true, insert atoms as well.
+     :type  deep: :class:`bool`
      :returns:     :class:`ResidueHandle`
 
   .. method:: RenameResidue(residue, new_name)
diff --git a/modules/mol/base/doc/entity.rst b/modules/mol/base/doc/entity.rst
index 5e778d670030a26e19533f4a679c5fbcde9cd2d3..173ea4a9296c87a2f96345d1857aa9edf3b3189a 100644
--- a/modules/mol/base/doc/entity.rst
+++ b/modules/mol/base/doc/entity.rst
@@ -98,19 +98,28 @@ The Handle Classes
     
     :type: float
 
+  .. attribute:: center_of_atoms
+
+    Center of atoms, that is the average atom position of the entity.
+    Use :attr:`center_of_mass` for the the mass-weighted center of the entity.
+    Also available as :meth:`GetCenterOfAtoms`.
+
+    :type: :class:`~ost.geom.Vec3`
+
   .. attribute:: center_of_mass
   
-    Center of mass. Also available as :meth:`GetCenterOfMass`
-    
-    :type: :class:`~ost.geom.Vec3`
-    
-  .. attribute:: center_of_atoms
-  
-    Center of atoms (not mass-weighted). Also available as 
-    :meth:`GetCenterOfAtoms`.
+    Center of mass of the entity.
+    Also available as :meth:`GetCenterOfMass`
     
     :type: :class:`~ost.geom.Vec3`
 
+  .. attribute:: geometric_center
+
+    Mid-point of the axis aligned bounding box of the entity.
+    Also available as :meth:`GetGeometricCenter`
+
+    :type: Vec3
+
   .. attribute:: positions
 
     Equivalent to calling :meth:`GetPositions` with *sort_by_index = True*. This
@@ -286,29 +295,21 @@ The Handle Classes
       
       alternative atom positions are not handled yet.
 
+  .. method:: GetMass()
+
+    See :attr:`mass`
+
   .. method:: GetCenterOfAtoms()
-    
-    Get center of atoms, that is the average atom position of the entity. Use
-    :meth:`GetCenterOfMass` to calculate the mass-weighted center of the entity.
-    
-    :returns: :class:`~ost.geom.Vec3`
-    
+
+    See :attr:`center_of_atoms`
+
   .. method:: GetCenterOfMass()
-    
-    Calculates the center of mass of the entity. Use :meth:`GetCenterOfAtoms`
-    to calculate the non-mass-weighted center of the entity.
-    
-    :returns: :class:`~ost.geom.Vec3`
-    
+
+    See :attr:`center_of_mass`
+
   .. method:: GetGeometricCenter()
-  
-    Calculates the mid-point of the axis aligned bounding box of the entity.
-    
-    :returns: :class:`~ost.geom.Vec3`
-    
-  .. method:: GetMass()
-  
-    See :attr:`mass`
+
+    See :attr:`geometric_center`
 
   .. method:: GetPositions(sort_by_index=True)
 
@@ -367,10 +368,17 @@ The Handle Classes
 
   .. attribute:: center_of_mass
 
-    Center of mass. Also available as :meth:`GetCenterOfMass`
+    Center of mass. Also available as :meth:`GetCenterOfMass`.
 
     :type: :class:`~ost.geom.Vec3`
 
+  .. attribute:: geometric_center
+
+    Mid-point of the axis aligned bounding box of the chain.
+    Also available as :meth:`GetGeometricCenter`
+
+    :type: Vec3
+
   .. attribute:: description
 
      Details about the chain. Not categorised, just text.
@@ -567,9 +575,20 @@ The Handle Classes
     The residue name is usually a str of 3 characters, e.g. `GLY` for 
     glycine or `ALA` for alanine, but may be shorter, e.g. `G` for guanosine, 
     or longer for structures loaded from formats other than PDB.
+    Also available as :meth:`GetName`.
     
     This property is read-only. To change the name of the residue, use
     :meth:`~EditorBase.RenameResidue`.
+
+    :type: str
+
+  .. attribute:: qualified_name
+
+    The qualified name consists of a residue identifier and chain name.
+    For a glycine with residue number 2 of chain A, the qualified name is
+    "A.GLY2". Also available as :meth:`GetQualifiedName`.
+
+    :type: str
   
   .. attribute:: number
   
@@ -605,6 +624,18 @@ The Handle Classes
 
      :type: :class:`AtomHandleList` (list of :class:`AtomHandle`)
 
+  .. attribute:: atom_count
+
+    Number of atoms. Read-only. See :meth:`GetAtomCount`.
+
+    :type: :class:`int`
+
+  .. attribute:: bond_count
+
+    Number of bonds. Read-only. See :meth:`GetBondCount`.
+
+    :type: :class:`int`
+
   .. attribute:: bounds
   
     Axis-aligned bounding box of the residue. Read-only.
@@ -616,12 +647,6 @@ The Handle Classes
     The total mass of this residue in Dalton. Also available as :meth:`GetMass`.
   
     :type: float
-
-  .. attribute:: center_of_mass
-
-    Center of mass. Also available as :meth:`GetCenterOfMass`
-  
-    :type: :class:`~ost.geom.Vec3`
   
   .. attribute:: center_of_atoms
     
@@ -630,6 +655,18 @@ The Handle Classes
     
     :type: :class:`~ost.geom.Vec3`
 
+  .. attribute:: center_of_mass
+
+    Center of mass. Also available as :meth:`GetCenterOfMass`
+
+    :type: :class:`~ost.geom.Vec3`
+
+  .. attribute:: geometric_center
+
+    Mid-point of the axis aligned bounding box of the residue.
+
+    :type: Vec3
+
   .. attribute:: chain
   
     The chain this residue belongs to. Read-only. Also available as 
@@ -656,6 +693,15 @@ The Handle Classes
     torsion, the PSI torsion is an invalid handle.
     
     Read-only. Also available as :meth:`GetPsiTorsion`
+
+  .. attribute:: omega_torsion
+
+    The OMEGA dihedral angle between this residue and the previous. For residues
+    that are not amino acids, residues that do not have all atoms required or
+    residues that do not have bonds between the four atoms involved in the
+    torsion, the OMEGA torsion is an invalid handle.
+
+    Read-only. Also available as :meth:`GetOmegaTorsion`
     
     :type: :class:`TorsionHandle`
   
@@ -808,42 +854,98 @@ The Handle Classes
     :returns:           Whether the switch was successful (e.g. False if no such
                         group exists)
 
+  .. method:: GetName()
+
+    See :attr:`name`
+
+  .. method:: GetQualifiedName()
+
+    See :attr:`qualified_name`
+
+  .. method:: GetNumber()
+
+    See :attr:`number`
+
+  .. method:: GetOneLetterCode()
+
+    See :attr:`one_letter_code`
+
   .. method:: GetAtomList()
 
     See :attr:`atoms`
 
-  .. method:: IsPeptideLinking()
+  .. method:: GetAtomCount()
 
-    See :attr:`peptide_linking`
+    See :attr:`atom_count`
 
-  .. method:: IsNucleotideLinking()
+  .. method:: GetBondCount()
+
+    See :attr:`bond_count`
+
+  .. method:: GetBounds()
+
+    See :attr:`bounds`
+
+  .. method:: GetMass()
+
+    See :attr:`mass`
 
-    See :attr:`nucleotide_linking`
-    
-  .. method:: GetChain()
-  
-    See :attr:`chain`
-  
   .. method:: GetCenterOfAtoms()
-    
+
     See :attr:`center_of_atoms`
-    
+
   .. method:: GetCenterOfMass()
-  
+
     See :attr:`center_of_mass`
-  
+
+  .. method:: GetGeometricCenter()
+
+    See :attr:`geometric_center`
+
+  .. method:: GetChain()
+
+    See :attr:`chain`
+
   .. method:: GetPhiTorsion()
-    
+
     See :attr:`phi_torsion`
-  
+
   .. method:: GetPsiTorsion()
-  
+
     See :attr:`psi_torsion`
-  
+
+  .. method:: GetOmegaTorsion()
+
+    See :attr:`omega_torsion`
+
+  .. method:: GetChemClass()
+
+    See :attr:`chem_class`
+
   .. method:: GetChemType()
-    
+
     See :attr:`chem_type`
 
+  .. method:: GetSecStructure()
+
+    See :attr:`sec_structure`
+
+  .. method:: IsLigand()
+
+    See :attr:`is_ligand`
+
+  .. method:: IsProtein()
+
+    See :attr:`is_protein`
+
+  .. method:: IsPeptideLinking()
+
+    See :attr:`peptide_linking`
+
+  .. method:: IsNucleotideLinking()
+
+    See :attr:`nucleotide_linking`
+
   .. method:: GetIndex()
     
     See :attr:`index`
@@ -861,10 +963,6 @@ The Handle Classes
   
     See :attr:`valid`
 
-  .. method:: IsLigand()
-
-    See :attr:`is_ligand`
-
   .. method:: SetIsLigand()
 
     Set the :meth:`IsLigand` flag explicitly.
@@ -872,10 +970,6 @@ The Handle Classes
     :param ligand: Whether this residue is a ligand or not
     :type  ligand: bool
 
-  .. method:: IsProtein()
-
-    See :attr:`is_protein`
-
   .. method:: SetIsProtein()
 
     Set the :meth:`IsProtein` flag explicitly.
@@ -883,6 +977,14 @@ The Handle Classes
     :param protein: Whether this residue is a protein or not
     :type  protein: bool
 
+  .. method:: GetNext()
+
+    See :attr:`next`
+
+  .. method:: GetPrev()
+
+    See :attr:`next`
+
 
 .. class:: AtomHandle
 
@@ -900,7 +1002,7 @@ The Handle Classes
   
   .. attribute:: qualified_name
   
-     The qualified name consists of the atom name as well as a unique residue
+     The qualified name consists of the atom name as well as a residue
      identifier and chain name. For CA of a glycine with residue number 2 of
      chain A, the qualified name is "A.GLY2.CA".
      
@@ -1230,6 +1332,13 @@ The View Classes
 
   An entity view represents a structural subset of an :class:`EntityHandle`. For 
   an introduction ,see :doc:`../../intro-01`.
+
+  .. attribute:: handle
+
+     The underlying :class:`handle <EntityHandle>` of the entity view. Also
+     available as :meth:`GetHandle`.
+
+     :type: :class:`EntityHandle`
   
   .. attribute:: chains
    
@@ -1291,16 +1400,23 @@ The View Classes
 
   .. attribute:: bounds
   
-    Axis-aligned bounding box of the entity view. Read-only.
-    
-    :type: :class:`ost.geom.AlignedCuboid`
+    See :attr:`EntityHandle.bounds`
 
-  .. attribute:: handle
-  
-     The underlying :class:`handle <EntityHandle>` of the entity view. Also 
-     available as :meth:`GetHandle`.
-     
-     :type: :class:`EntityHandle`
+  .. attribute:: mass
+
+    See :attr:`EntityHandle.mass`
+
+  .. attribute:: center_of_atoms
+
+    See :attr:`EntityHandle.center_of_atoms`
+
+  .. attribute:: center_of_mass
+
+    See :attr:`EntityHandle.center_of_mass`
+
+  .. attribute:: geometric_center
+
+    See :attr:`EntityHandle.geometric_center`
 
   .. attribute:: valid
 
@@ -1581,6 +1697,12 @@ The View Classes
   A view representation of a :class:`ChainHandle`. Mostly, the same
   functionality is provided as for the handle.
 
+  .. attribute:: handle
+
+    The chain handle this view points to. Also available as :meth:`GetHandle`.
+
+    :type: :class:`ChainHandle`
+
   .. attribute:: name
   
      The chain name. The name uniquely identifies the chain in the entity. In
@@ -1629,6 +1751,11 @@ The View Classes
        print(chain.residues) # [B.GLY1, B.GLY4, B.GLY3]
        print(chain.in_sequence) # prints false
 
+    Note that the value of  `in_sequence` is independent from the value of
+    :attr:`ChainHandle.in_sequence`.
+
+    :type: bool
+
   .. attribute:: atoms
 
      Get list of all atoms of this chain. To access a single atom, use
@@ -1650,38 +1777,17 @@ The View Classes
   
     :type: float
 
-  .. attribute:: center_of_mass
-
-    Center of mass. Also available as :meth:`GetCenterOfMass`
-  
-    :type: :class:`~ost.geom.Vec3`
-  
   .. attribute:: center_of_atoms
-    
-    Center of atoms (not mass weighted). Also available as 
-    :meth:`GetCenterOfAtoms`.
-    
-    :type: :class:`~ost.geom.Vec3`
 
-  .. attribute:: handle
+    See :attr:`ChainHandle.center_of_atoms`
 
-    The chain handle this view points to. Also available as :meth:`GetHandle`.
- 
-    :type: :class:`ChainHandle`
-   
-  .. attribute:: in_sequence
-  
-    Whether the residue numbers are in ascending order. Note that the value of 
-    `in_sequence` is independent from the value of 
-    :attr:`ChainHandle.in_sequence`.
-    
-    :type: bool
+  .. attribute:: center_of_mass
 
-  .. attribute:: geometric_center
+    See :attr:`ChainHandle.center_of_mass`
 
-    Mid-point of the axis aligned bounding box of the entity.
+  .. attribute:: geometric_center
 
-    :type: Vec3
+    See :attr:`ChainHandle.geometric_center`
 
   .. attribute:: valid
 
@@ -1822,63 +1928,47 @@ The View Classes
     :type: :class:`ResidueHandle`
 
   .. attribute:: name
+                 qualified_name
+                 number
+                 one_letter_code
+                 bounds
+                 mass
+                 center_of_atoms
+                 center_of_mass
+                 geometric_center
+                 phi_torsion
+                 psi_torsion
+                 omega_torsion
+                 chem_class
+                 chem_type
+                 sec_structure
+                 is_ligand
+                 is_protein
+                 peptide_linking
+                 nucleotide_linking
+                 central_atom
+                 central_normal
+                 valid
+                 next
+                 prev
+
+    See the respective attributes in :class:`ResidueHandle`.
 
-    The residue name is usually a str of 3 characters, e.g. `GLY` for 
-    glycine or `ALA` for alanine, but may be shorter, e.g. `G` for guanosine, 
-    or longer for structures loaded from formats other than PDB.
-  
-    This property is read-only. To change the name of the residue, use
-    :meth:`~EditorBase.RenameResidue`.
-
-  .. attribute:: number
-
-    The number of this residue. The residue number has a numeric part and an
-    insertion-code. This property is read-only. Also available as 
-    :meth:`GetNumber`.
-  
-    :type: :class:`ResNum`
-
-  .. attribute:: one_letter_code
-
-    For amino acids, and nucleotides the `one_letter_code` is an alpha-numeric 
-    character. For unknown or more *exotic* residues, the one letter code is set 
-    to '?'.
-  
-    **Example**
-  
-    This code-snippet shows how to get the sequence string from a list of 
-    residues.
-  
-    .. code-block:: python
-  
-      print(''.join([r.one_letter_code for r in chain.residues]))
-  
-    :type: str
-  
-  .. attribute:: bounds
-
-    Axis-aligned bounding box of the residue view. Read-only
-
-    :type: :class:`ost.geom.AlignedCuboid`
-
-  .. attribute:: mass
+  .. attribute:: atoms
 
-    The total mass of this residue in Dalton. Also available as :meth:`GetMass`.
+     Get list of all atoms of this residue included in the view.
+     To access a single atom, use
+     :meth:`FindAtom`.
 
-    :type: float
+     This property is read-only. Also available as :meth:`GetAtomList`
 
-  .. attribute:: center_of_mass
+     :type: :class:`AtomViewList` (list of :class:`AtomView`)
 
-    Center of mass. Also available as :meth:`GetCenterOfMass`
+  .. attribute:: atom_count
 
-    :type: :class:`~ost.geom.Vec3`
+    Number of atoms included in the view. Read-only. See :meth:`GetAtomCount`.
 
-  .. attribute:: center_of_atoms
-  
-    Center of atoms (not mass weighted). Also available as 
-    :meth:`GetCenterOfAtoms`.
-  
-    :type: :class:`~ost.geom.Vec3`
+    :type: :class:`int`
 
   .. attribute:: chain
 
@@ -1887,21 +1977,6 @@ The View Classes
   
     :type: :class:`ChainView`
 
-  .. attribute:: handle
-  
-    The residue handle this view points to
-    
-    :type: :class:`ResidueHandle`
-
-  .. attribute:: atoms
-
-     Get list of all atoms of this residue. To access a single atom, use
-     :meth:`FindAtom`.
-   
-     This property is read-only. Also available as :meth:`GetAtomList`
-
-     :type: :class:`AtomHandleList` (list of :class:`AtomHandle`)
-
   .. attribute:: index
 
     Residue index (starting at 0) within chain view.
@@ -1919,9 +1994,34 @@ The View Classes
 
     See :attr:`handle`
 
-  .. method:: GetMass()
-
-    See :attr:`mass`
+  .. method:: GetName
+              GetQualifiedName
+              GetNumber
+              GetOneLetterCode
+              GetBounds
+              GetMass
+              GetCenterOfAtoms
+              GetCenterOfMass
+              GetGeometricCenter
+              GetPhiTorsion
+              GetPsiTorsion
+              GetOmegaTorsion
+              GetChemClass
+              GetChemType
+              GetSecStructure
+              IsLigand
+              SetIsLigand
+              IsProtein
+              IsPeptideLinking
+              IsNucleotideLinking
+              GetCentralAtom
+              SetCentralAtom
+              GetCentralNormal
+              IsValid
+              GetNext
+              GetPrev
+
+    See the respective methods in :class:`ResidueHandle`.
 
   .. method:: GetChain()
 
@@ -1936,14 +2036,6 @@ The View Classes
     :type  atom_name: str
     :rtype: :class:`AtomView`
 
-  .. method:: GetIndex()
-    
-    See :attr:`index`
-
-  .. method:: GetCenterOfMass()
-
-    See :attr:`center_of_mass`
-
   .. method:: IsAtomIncluded(atom_handle)
 
     Returns true if the given atom is part of the view, false if not.
@@ -1952,10 +2044,6 @@ The View Classes
     :type  atom_handle: :class:`AtomHandle`
     :rtype: bool
 
-  .. method:: GetGeometricCenter()
-
-    See :attr:`geometric_center`
-
   .. method:: AddAtom(atom_handle[, flags])
 
     Add atom to the view.
@@ -1966,13 +2054,17 @@ The View Classes
     :type  flags: :class:`int` / :class:`ViewAddFlag`
     :rtype: :class:`AtomView`
 
-  .. method:: GetCenterOfAtoms()
-
-    See :attr:`center_of_atoms`
-
   .. method:: GetAtomList()
 
     See :attr:`atoms`
+
+  .. method:: GetAtomCount()
+
+    See :attr:`atom_count`
+
+  .. method:: GetIndex()
+
+    See :attr:`index`
     
   .. method:: Select(query, flags=0)
    
diff --git a/modules/mol/base/pymod/export_residue.cc b/modules/mol/base/pymod/export_residue.cc
index 8a6fbe99ece49ddfc6e5d2a5fb723239d69619e0..9016f9d02f209c2e3ac339be7cec631099af4b96 100644
--- a/modules/mol/base/pymod/export_residue.cc
+++ b/modules/mol/base/pymod/export_residue.cc
@@ -261,6 +261,7 @@ void export_Residue()
     .def("GetAtomCount", &ResidueHandle::GetAtomCount)
     .def("GetBondCount", &ResidueHandle::GetBondCount)
     .add_property("atom_count", &ResidueHandle::GetAtomCount)
+    .add_property("bond_count", &ResidueHandle::GetBondCount)
     .add_property("index", &ResidueHandle::GetIndex)
     .def("Select", select_string, arg("flags")=0)
     .def("Select", select_query, arg("flags")=0)    
diff --git a/modules/mol/base/pymod/export_residue_view.cc b/modules/mol/base/pymod/export_residue_view.cc
index a3a7f16eaa232216a97b1091fd9048ff3a7e87c0..9d14320de594bb3220792085258551f9e1d82cbe 100644
--- a/modules/mol/base/pymod/export_residue_view.cc
+++ b/modules/mol/base/pymod/export_residue_view.cc
@@ -64,6 +64,7 @@ void export_ResidueView()
     .add_property("atoms",
                   make_function(&ResidueView::GetAtomList,
                                 return_value_policy<copy_const_reference>()))
+    .add_property("atom_count", &ResidueView::GetAtomCount)
     .def("FindAtom", string_find_atom, args("atom_name"))
     .def("AddAtom", add_atom_handle, X_add_atom_overloads(args("atom_handle", "flags")))
     .def("AddAtom", add_atom_view, X_add_atom_overloads(args("atom_view", "flags")))