diff --git a/CHANGELOG.txt b/CHANGELOG.txt
index d468d0dfb1d0b75d18530c1488c50cf70b725f16..dc4b0a416dbd06bc58067bc353584b68b697204d 100644
--- a/CHANGELOG.txt
+++ b/CHANGELOG.txt
@@ -1,3 +1,10 @@
+Changes in Release 1.9.0
+--------------------------------------------------------------------------------
+
+ * Improved compare-structures action (chain mapping handling and documentation).
+ * Improved handling of citations read from mmCIF files (mainly for books).
+ * Several minor bug fixes and improvements.
+
 Changes in Release 1.8.0
 --------------------------------------------------------------------------------
 
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 74a3348880146b72ffb3f3e045ef4502110eea98..065174c38250fcf10e35959228716be55a9a2f0b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 2.6.4 FATAL_ERROR)
 project(OpenStructure CXX C)
 set (CMAKE_EXPORT_COMPILE_COMMANDS 1)
 set (OST_VERSION_MAJOR 1)
-set (OST_VERSION_MINOR 8)
+set (OST_VERSION_MINOR 9)
 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-structures b/actions/ost-compare-structures
index ff579be64c219ea4029389fb6be4c23eda099d13..82e572e186f39d643339c69c8ebd5ae0da3417e3 100644
--- a/actions/ost-compare-structures
+++ b/actions/ost-compare-structures
@@ -33,38 +33,29 @@ Only model structures are "Molck-ed" in CAMEO. The call to molck is as follows:
       --complib=<COMPOUND LIB> \\
       --rm=hyd,oxt,unk \\
       --fix-ele \\
-      --map-nonstd <FILEPATH> \\
-      --out=<OUTPUT>
+      --map-nonstd \\
+      --out=<OUTPUT> \\
+      <FILEPATH>
 
 To be as much compatible with with CAMEO as possible one should call
 compare-structures as follows:
 
   ost compare-structures \\
-      # General parameters
-      ####################
       --model <MODEL> \\
       --reference <REF> \\
       --output output.json \\
-      # QS-score parameters
-      #####################
-      --qs-score \\
-      --residue-number-alignment \\
-      # lDDT parameters
-      #################
-      --lddt \\
-      --inclusion-radius 15.0 \\
-      # Molecular check parameters
-      ############################
       --molck \\
       --remove oxt hyd unk \\
       --clean-element-column \\
       --map-nonstandard-residues \\
-      # Additional checks
-      ###################
       --structural-checks \\
       --bond-tolerance 15.0 \\
       --angle-tolerance 15.0 \\
-      --consistency-checks
+      --residue-number-alignment \\
+      --consistency-checks \\
+      --qs-score \\
+      --lddt \\
+      --inclusion-radius 15.0
 """
 
 import os
@@ -131,38 +122,49 @@ def _GetDefaultCompoundLibraryPath():
 
 def _ParseArgs():
     """Parse command-line arguments."""
-    #
-    # General options
-    #
+
     parser = argparse.ArgumentParser(
         formatter_class=argparse.RawTextHelpFormatter,
         description=__doc__,
         prog="ost compare-structures")
 
-    parser.add_argument(
-        '-v',
-        '--verbosity',
-        type=int,
-        default=3,
-        help="Set verbosity level.")
-    parser.add_argument(
+    #
+    # Required arguments
+    #
+
+    group_required = parser.add_argument_group('required arguments')
+
+    group_required.add_argument(
         "-m",
         "--model",
         dest="model",
         required=True,
         help=("Path to the model file."))
-    parser.add_argument(
+    group_required.add_argument(
         "-r",
         "--reference",
         dest="reference",
         required=True,
         help=("Path to the reference file."))
-    parser.add_argument(
+
+    #
+    # General arguments
+    #
+
+    group_general = parser.add_argument_group('general arguments')
+
+    group_general.add_argument(
+        '-v',
+        '--verbosity',
+        type=int,
+        default=3,
+        help="Set verbosity level. Defaults to 3.")
+    group_general.add_argument(
         "-o",
         "--output",
         dest="output",
         help=("Output file name. The output will be saved as a JSON file."))
-    parser.add_argument(
+    group_general.add_argument(
         "-d",
         "--dump-structures",
         dest="dump_structures",
@@ -171,26 +173,26 @@ def _ParseArgs():
         help=("Dump cleaned structures used to calculate all the scores as\n"
               "PDB files using specified suffix. Files will be dumped to the\n"
               "same location as original files."))
-    parser.add_argument(
+    group_general.add_argument(
         "-ds",
         "--dump-suffix",
         dest="dump_suffix",
         default=".compare.structures.pdb",
         help=("Use this suffix to dump structures.\n"
               "Defaults to .compare.structures.pdb."))
-    parser.add_argument(
+    group_general.add_argument(
         "-rs",
         "--reference-selection",
         dest="reference_selection",
         default="",
         help=("Selection performed on reference structures."))
-    parser.add_argument(
+    group_general.add_argument(
         "-ms",
         "--model-selection",
         dest="model_selection",
         default="",
         help=("Selection performed on model structures."))
-    parser.add_argument(
+    group_general.add_argument(
         "-ca",
         "--c-alpha-only",
         dest="c_alpha_only",
@@ -199,14 +201,14 @@ def _ParseArgs():
         help=("Use C-alpha atoms only. Equivalent of calling the action with\n"
               "'--model-selection=\"aname=CA\" "
               "--reference-selection=\"aname=CA\"'\noptions."))
-    parser.add_argument(
+    group_general.add_argument(
         "-ft",
         "--fault-tolerant",
         dest="fault_tolerant",
         default=False,
         action="store_true",
         help=("Fault tolerant parsing."))
-    parser.add_argument(
+    group_general.add_argument(
         "-cl",
         "--compound-library",
         dest="compound_library",
@@ -215,86 +217,21 @@ def _ParseArgs():
               "If not provided, the following locations are searched in this\n"
               "order: 1. Working directory, 2. OpenStructure standard library"
               "\nlocation."))
+
     #
-    # QS-scorer options
-    #
-    parser.add_argument(
-        "-qs",
-        "--qs-score",
-        dest="qs_score",
-        default=False,
-        action="store_true",
-        help=("Calculate QS-score."))
-    parser.add_argument(
-        "-c",
-        "--chain-mapping",
-        nargs="+",
-        type=lambda x: x.split(":"),
-        dest="chain_mapping",
-        help=("Mapping of chains between the reference and the model.\n"
-              "Each separate mapping consist of key:value pairs where key\n"
-              "is the chain name in reference and value is the chain name in\n"
-              "model."))
-    parser.add_argument(
-        "--qs-rmsd",
-        dest="qs_rmsd",
-        default=False,
-        action="store_true",
-        help=("Calculate CA RMSD between shared CA atoms of mapped chains.\n"
-              "This uses a superposition using all mapped chains which\n"
-              "minimizes the CA RMSD."))
-    parser.add_argument(
-        "-rna",
-        "--residue-number-alignment",
-        dest="residue_number_alignment",
-        default=False,
-        action="store_true",
-        help=("Make alignment based on residue number instead of using\n"
-              "a global BLOSUM62-based alignment."))
-    #
-    # lDDT options
-    #
-    parser.add_argument(
-        "-l",
-        "--lddt",
-        dest="lddt",
-        default=False,
-        action="store_true",
-        help=("Calculate lDDT."))
-    parser.add_argument(
-        "-ir",
-        "--inclusion-radius",
-        dest="inclusion_radius",
-        type=float,
-        default=15.0,
-        help=("Distance inclusion radius."))
-    parser.add_argument(
-        "-ss",
-        "--sequence-separation",
-        dest="sequence_separation",
-        type=int,
-        default=0,
-        help=("Sequence separation. Only distances between residues whose\n"
-              "separation is higher than the provided parameter are\n"
-              "considered when computing the score"))
-    parser.add_argument(
-        "-spr",
-        "--save-per-residue-scores",
-        dest="save_per_residue_scores",
-        default=False,
-        action="store_true",
-        help=(""))
-    #
-    # Molecular check parameters
+    # Molecular check arguments
     #
-    parser.add_argument(
+
+    group_molck = parser.add_argument_group('molecular check arguments')
+
+    group_molck.add_argument(
         "-ml",
         "--molck",
         dest="molck",
         default=False,
         action="store_true",
         help=("Run molecular checker to clean up input."))
-    parser.add_argument(
+    group_molck.add_argument(
         "-rm",
         "--remove",
         dest="remove",
@@ -307,16 +244,17 @@ def _ParseArgs():
               " * oxt - remove terminal oxygens\n"
               " * nonstd - remove all residues not one of the 20\n"
               " * standard amino acids\n"
-              " * unk - Remove unknown and atoms not following the"
-              "nomenclature"))
-    parser.add_argument(
+              " * unk - Remove unknown and atoms not following the\n"
+              "         nomenclature\n"
+              "Defaults to hyd."))
+    group_molck.add_argument(
         "-ce",
         "--clean-element-column",
         dest="clean_element_column",
         default=False,
         action="store_true",
         help=("Clean up element column"))
-    parser.add_argument(
+    group_molck.add_argument(
         "-mn",
         "--map-nonstandard-residues",
         dest="map_nonstandard_residues",
@@ -324,17 +262,21 @@ def _ParseArgs():
         action="store_true",
         help=("Map modified residues back to the parent amino acid, for\n"
               "example MSE -> MET, SEP -> SER."))
+    
     #
-    # Options for various checks
+    # Structural check arguments
     #
-    parser.add_argument(
+
+    group_sc = parser.add_argument_group('structural check arguments')
+
+    group_sc.add_argument(
         "-sc",
         "--structural-checks",
         dest="structural_checks",
         default=False,
         action="store_true",
         help=("Perform structural checks and filter input data."))
-    parser.add_argument(
+    group_sc.add_argument(
         "-p",
         "--parameter-file",
         dest="parameter_file",
@@ -344,21 +286,58 @@ def _ParseArgs():
               "If not provided, the following locations are searched in this\n"
               "order: 1. Working directory, 2. OpenStructure standard library"
               "\nlocation."))
-    parser.add_argument(
+    group_sc.add_argument(
         "-bt",
         "--bond-tolerance",
         dest="bond_tolerance",
         type=float,
         default=12.0,
-        help=("Tolerance in STD for bonds."))
-    parser.add_argument(
+        help=("Tolerance in STD for bonds. Defaults to 12."))
+    group_sc.add_argument(
         "-at",
         "--angle-tolerance",
         dest="angle_tolerance",
         type=float,
         default=12.0,
-        help=("Tolerance in STD for angles."))
-    parser.add_argument(
+        help=("Tolerance in STD for angles. Defaults to 12."))
+
+    #
+    # Chain mapping arguments
+    #
+
+    group_cm = parser.add_argument_group('chain mapping arguments')
+
+    group_cm.add_argument(
+        "-c",
+        "--chain-mapping",
+        nargs="+",
+        type=lambda x: x.split(":"),
+        dest="chain_mapping",
+        help=("Mapping of chains between the reference and the model.\n"
+              "Each separate mapping consist of key:value pairs where key\n"
+              "is the chain name in reference and value is the chain name in\n"
+              "model."))
+    group_cm.add_argument(
+        "--qs-max-mappings-extensive",
+        dest="qs_max_mappings_extensive",
+        type=int,
+        default=1000000,
+        help=("Maximal number of chain mappings to test for 'extensive'\n"
+              "chain mapping scheme which is used as a last resort if\n"
+              "other schemes failed. The extensive chain mapping search\n"
+              "must in the worst case check O(N!) possible mappings for\n"
+              "complexes with N chains. Two octamers without symmetry\n"
+              "would require 322560 mappings to be checked. To limit\n"
+              "computations, no scores are computed if we try more than\n"
+              "the maximal number of chain mappings. Defaults to 1000000."))
+
+    #
+    # Sequence alignment arguments
+    #
+
+    group_aln = parser.add_argument_group('sequence alignment arguments')
+
+    group_aln.add_argument(
         "-cc",
         "--consistency-checks",
         dest="consistency_checks",
@@ -370,6 +349,73 @@ def _ParseArgs():
               "will continue to calculate scores. If this flag is ON, checks\n"
               "will not be ignored and if the pair does not pass the test\n"
               "all the scores for that pair will be marked as a FAILURE."))
+    group_aln.add_argument(
+        "-rna",
+        "--residue-number-alignment",
+        dest="residue_number_alignment",
+        default=False,
+        action="store_true",
+        help=("Make alignment based on residue number instead of using\n"
+              "a global BLOSUM62-based alignment."))
+
+    #
+    # QS score arguments
+    #
+
+    group_qs = parser.add_argument_group('QS score arguments')
+
+    group_qs.add_argument(
+        "-qs",
+        "--qs-score",
+        dest="qs_score",
+        default=False,
+        action="store_true",
+        help=("Calculate QS-score."))
+    group_qs.add_argument(
+        "--qs-rmsd",
+        dest="qs_rmsd",
+        default=False,
+        action="store_true",
+        help=("Calculate CA RMSD between shared CA atoms of mapped chains.\n"
+              "This uses a superposition using all mapped chains which\n"
+              "minimizes the CA RMSD."))
+
+    #
+    # lDDT score arguments
+    #
+
+    group_lddt = parser.add_argument_group('lDDT score arguments')
+
+    group_lddt.add_argument(
+        "-l",
+        "--lddt",
+        dest="lddt",
+        default=False,
+        action="store_true",
+        help=("Calculate lDDT."))
+    group_lddt.add_argument(
+        "-ir",
+        "--inclusion-radius",
+        dest="inclusion_radius",
+        type=float,
+        default=15.0,
+        help=("Distance inclusion radius for lDDT. Defaults to 15 A."))
+    group_lddt.add_argument(
+        "-ss",
+        "--sequence-separation",
+        dest="sequence_separation",
+        type=int,
+        default=0,
+        help=("Sequence separation. Only distances between residues whose\n"
+              "separation is higher than the provided parameter are\n"
+              "considered when computing the score. Defaults to 0."))
+    group_lddt.add_argument(
+        "-spr",
+        "--save-per-residue-scores",
+        dest="save_per_residue_scores",
+        default=False,
+        action="store_true",
+        help=(""))
 
     # Print full help is no arguments provided
     if len(sys.argv) == 1:
@@ -502,21 +548,21 @@ def _GetAlignmentsAsFasta(alignments):
 
 def _ReadStructureFile(path, c_alpha_only=False, fault_tolerant=False,
                        selection=""):
-    """Safely read structure file into OST entity.
-
-    The functin can read both PDB and mmCIF files.
+    """Safely read structure file into OST entities (split by biounit).
 
+    The function can read both PDB and mmCIF files.
+    
     :param path: Path to the file.
     :type path: :class:`str`
-    :returns: Entity
-    :rtype: :class:`~ost.mol.EntityHandle`
+    :returns: list of entities
+    :rtype: :class:`list` of :class:`~ost.mol.EntityHandle`
     """
 
     def _Select(entity):
-        selection_message = "Selecting %s" % selection
         if selection:
-            ost.LogInfo(selection_message)
-            entity = entity.Select(selection)
+            ost.LogInfo("Selecting %s" % selection)
+            ent_view = entity.Select(selection)
+            entity = mol.CreateEntityFromView(ent_view, False)
         return entity
 
     entities = list()
@@ -541,7 +587,7 @@ def _ReadStructureFile(path, c_alpha_only=False, fault_tolerant=False,
                 calpha_only=c_alpha_only)
             if len(cif_info.biounits) == 0:
                 tbu = MMCifInfoBioUnit()
-                tbu.id = 'ASU of ' + entity.pdb_id
+                tbu.id = 'ASU'
                 tbu.details = 'asymmetric unit'
                 for chain in tmp_entity.chains:
                     tbu.AddChain(str(chain))
@@ -683,13 +729,19 @@ def _Main():
             qs_scorer = qsscoring.QSscorer(reference,
                                            model,
                                            opts.residue_number_alignment)
+            qs_scorer.max_mappings_extensive = opts.qs_max_mappings_extensive
             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
             else:
-                qs_scorer.chain_mapping  # just to initialize it
+                try:
+                  qs_scorer.chain_mapping  # just to initialize it
+                except qsscoring.QSscoreError as ex:
+                  ost.LogError('Chain mapping failed:', str(ex))
+                  ost.LogError('Skipping comparison')
+                  continue
             ost.LogInfo("-" * 80)
             ost.LogInfo("Checking consistency between %s and %s" % (
                         model_name, reference_name))
@@ -935,7 +987,7 @@ def _Main():
         ost.LogInfo("#" * 80)
         ost.LogInfo("Saving output into %s" % opts.output)
         with open(opts.output, "w") as outfile:
-            outfile.write(json.dumps(result, indent=4))
+            json.dump(result, outfile, indent=4, sort_keys=True)
 
 
 if __name__ == '__main__':
diff --git a/docker/Dockerfile b/docker/Dockerfile
index 996b7fc8b87adb21b37c209ef48d267706d469c0..e7db46f9a9629035a989be788aa96607b331f977 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -129,7 +129,7 @@ RUN cmake .. -DPYTHON_LIBRARIES=/usr/lib/x86_64-linux-gnu/libpython2.7.so \
 
 # get the compound library
 RUN wget ftp://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif.gz
-RUN stage/bin/chemdict_tool create components.cif.gz compounds.chemlib pdb && stage/bin/chemdict_tool update modules/conop/data/charmm.cif compounds.chemlib charmm
+RUN stage/bin/chemdict_tool create components.cif.gz compounds.chemlib pdb && stage/bin/chemdict_tool update ../modules/conop/data/charmm.cif compounds.chemlib charmm
 RUN mkdir -p $OPENSTRUCTURE_SHARE && chmod a+rw -R $OPENSTRUCTURE_SHARE && mv compounds.chemlib $OPENSTRUCTURE_SHARE
 
 # Build and install OST
diff --git a/modules/doc/actions.rst b/modules/doc/actions.rst
index 4ff4afc6aabf48fc88e8fb75ec9a717e51a19f5a..ccda8476eee514ece15815bab7bff2fe0b27de02 100644
--- a/modules/doc/actions.rst
+++ b/modules/doc/actions.rst
@@ -1,3 +1,7 @@
+..  Note on large code blocks: keep max. width to 120 or it will look bad
+                               on webpage!
+..  TODO: look at argparse directive to autogenerate --help output!
+
 .. ost-actions:
 
 OST Actions
@@ -15,20 +19,226 @@ Comparing two structures
 --------------------------------------------------------------------------------
 
 You can compare two structures in terms of quaternary structure score and
-lDDT scores between two complexes from the command line with:
+lDDT scores between two complexes from the command line with the
+``ost compare-structures`` action.
+
+In summary it performs the following steps:
+
+- Read structures (PDB or mmCIF format, can be gzipped) and split into
+  biological assemblies (all possible pairs are scored).
+- Optional cleanup of structures with :func:`~ost.mol.alg.Molck`.
+- Optional structural checks with :func:`~ost.mol.alg.CheckStructure`.
+- Unless user-provided, find chain mapping between complexes (see
+  :attr:`here <ost.mol.alg.qsscoring.QSscorer.chain_mapping>` for details)
+- Perform sequence alignment of chain pairs (unless user asks for alignment
+  based on residue numbers). Alignment can optionally checked for consistency
+  if 100% sequence identity is expected.
+- Compute scores requested by user (CA-RMSD of oligomer,
+  :mod:`QS scores of oligomer <ost.mol.alg.qsscoring>`,
+  :class:`single chain lDDT scores <ost.mol.alg.lDDTScorer>`,
+  :attr:`weighted average of single chain lDDT scores <ost.mol.alg.qsscoring.OligoLDDTScorer.weighted_lddt>`,
+  :attr:`lDDT score of oligomer <ost.mol.alg.qsscoring.OligoLDDTScorer.oligo_lddt>`).
+  Note that while the QS score is symmetric (same result when swapping reference
+  and model), the lDDT scores are not. Extra atoms in the model for mapped
+  chains have no effect on the score, while extra atoms in the reference reduce
+  the score. For the oligomeric variants (weighted-lDDT & oligo-lDDT), we do
+  :attr:`penalize for extra chains <ost.mol.alg.qsscoring.OligoLDDTScorer.penalize_extra_chains>`
+  in both reference and model.
+
+.. note ::
+
+  The action relies on OST's :mod:`~ost.mol.alg.qsscoring` module and has the
+  same requirements on your OST installation (needs compound library, ClustalW,
+  numpy and scipy).
+
+Details on the usage (output of ``ost compare-structures --help``):
 
 .. code-block:: console
 
-  $ ost compare-structures [-h] [-v VERBOSITY] -m MODEL -r REFERENCE
-                           [-o OUTPUT] [-d] [-ds DUMP_SUFFIX]
-                           [-rs REFERENCE_SELECTION] [-ms MODEL_SELECTION]
-                           [-ca] [-ft] [-cl COMPOUND_LIBRARY] [-qs]
-                           [-c CHAIN_MAPPING [CHAIN_MAPPING ...]] [-rna]
-                           [-l] [-ir INCLUSION_RADIUS]
-                           [-ss SEQUENCE_SEPARATION] [-spr] [-ml]
-                           [-rm REMOVE [REMOVE ...]] [-ce] [-mn] [-sc]
-                           [-p PARAMETER_FILE] [-bt BOND_TOLERANCE]
-                           [-at ANGLE_TOLERANCE] [-cc]
+  usage: ost compare-structures [-h] -m MODEL -r REFERENCE [-v VERBOSITY]
+                                [-o OUTPUT] [-d] [-ds DUMP_SUFFIX]
+                                [-rs REFERENCE_SELECTION] [-ms MODEL_SELECTION]
+                                [-ca] [-ft] [-cl COMPOUND_LIBRARY] [-ml]
+                                [-rm REMOVE [REMOVE ...]] [-ce] [-mn] [-sc]
+                                [-p PARAMETER_FILE] [-bt BOND_TOLERANCE]
+                                [-at ANGLE_TOLERANCE]
+                                [-c CHAIN_MAPPING [CHAIN_MAPPING ...]]
+                                [--qs-max-mappings-extensive QS_MAX_MAPPINGS_EXTENSIVE]
+                                [-cc] [-rna] [-qs] [--qs-rmsd] [-l]
+                                [-ir INCLUSION_RADIUS] [-ss SEQUENCE_SEPARATION]
+                                [-spr]
+
+  Evaluate model structure against reference.
+
+  eg.
+
+    ost compare-structures \
+        --model <MODEL> \
+        --reference <REF> \
+        --output output.json \
+        --lddt \
+        --structural-checks \
+        --consistency-checks \
+        --molck \
+        --remove oxt hyd \
+        --map-nonstandard-residues
+
+  Here we describe how the parameters can be set to mimick a CAMEO evaluation
+  (as of August 2018).
+
+  CAMEO calls the lddt binary as follows:
+
+    lddt \
+        -p <PARAMETER FILE> \
+        -f \
+        -a 15 \
+        -b 15 \
+        -r 15 \
+        <MODEL> \
+        <REF>
+
+  Only model structures are "Molck-ed" in CAMEO. The call to molck is as follows:
+
+    molck \
+        --complib=<COMPOUND LIB> \
+        --rm=hyd,oxt,unk \
+        --fix-ele \
+        --map-nonstd \
+        --out=<OUTPUT> \
+        <FILEPATH>
+
+  To be as much compatible with with CAMEO as possible one should call
+  compare-structures as follows:
+
+    ost compare-structures \
+        --model <MODEL> \
+        --reference <REF> \
+        --output output.json \
+        --molck \
+        --remove oxt hyd unk \
+        --clean-element-column \
+        --map-nonstandard-residues \
+        --structural-checks \
+        --bond-tolerance 15.0 \
+        --angle-tolerance 15.0 \
+        --residue-number-alignment \
+        --consistency-checks \
+        --qs-score \
+        --lddt \
+        --inclusion-radius 15.0
+
+  optional arguments:
+    -h, --help            show this help message and exit
+
+  required arguments:
+    -m MODEL, --model MODEL
+                          Path to the model file.
+    -r REFERENCE, --reference REFERENCE
+                          Path to the reference file.
+
+  general arguments:
+    -v VERBOSITY, --verbosity VERBOSITY
+                          Set verbosity level. Defaults to 3.
+    -o OUTPUT, --output OUTPUT
+                          Output file name. The output will be saved as a JSON file.
+    -d, --dump-structures
+                          Dump cleaned structures used to calculate all the scores as
+                          PDB files using specified suffix. Files will be dumped to the
+                          same location as original files.
+    -ds DUMP_SUFFIX, --dump-suffix DUMP_SUFFIX
+                          Use this suffix to dump structures.
+                          Defaults to .compare.structures.pdb.
+    -rs REFERENCE_SELECTION, --reference-selection REFERENCE_SELECTION
+                          Selection performed on reference structures.
+    -ms MODEL_SELECTION, --model-selection MODEL_SELECTION
+                          Selection performed on model structures.
+    -ca, --c-alpha-only   Use C-alpha atoms only. Equivalent of calling the action with
+                          '--model-selection="aname=CA" --reference-selection="aname=CA"'
+                          options.
+    -ft, --fault-tolerant
+                          Fault tolerant parsing.
+    -cl COMPOUND_LIBRARY, --compound-library COMPOUND_LIBRARY
+                          Location of the compound library file (compounds.chemlib).
+                          If not provided, the following locations are searched in this
+                          order: 1. Working directory, 2. OpenStructure standard library
+                          location.
+
+  molecular check arguments:
+    -ml, --molck          Run molecular checker to clean up input.
+    -rm REMOVE [REMOVE ...], --remove REMOVE [REMOVE ...]
+                          Remove atoms and residues matching some criteria:
+                           * zeroocc - Remove atoms with zero occupancy
+                           * hyd - remove hydrogen atoms
+                           * oxt - remove terminal oxygens
+                           * nonstd - remove all residues not one of the 20
+                           * standard amino acids
+                           * unk - Remove unknown and atoms not following the
+                                   nomenclature
+                          Defaults to hyd.
+    -ce, --clean-element-column
+                          Clean up element column
+    -mn, --map-nonstandard-residues
+                          Map modified residues back to the parent amino acid, for
+                          example MSE -> MET, SEP -> SER.
+
+  structural check arguments:
+    -sc, --structural-checks
+                          Perform structural checks and filter input data.
+    -p PARAMETER_FILE, --parameter-file PARAMETER_FILE
+                          Location of the stereochemical parameter file
+                          (stereo_chemical_props.txt).
+                          If not provided, the following locations are searched in this
+                          order: 1. Working directory, 2. OpenStructure standard library
+                          location.
+    -bt BOND_TOLERANCE, --bond-tolerance BOND_TOLERANCE
+                          Tolerance in STD for bonds. Defaults to 12.
+    -at ANGLE_TOLERANCE, --angle-tolerance ANGLE_TOLERANCE
+                          Tolerance in STD for angles. Defaults to 12.
+
+  chain mapping arguments:
+    -c CHAIN_MAPPING [CHAIN_MAPPING ...], --chain-mapping CHAIN_MAPPING [CHAIN_MAPPING ...]
+                          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.
+    --qs-max-mappings-extensive QS_MAX_MAPPINGS_EXTENSIVE
+                          Maximal number of chain mappings to test for 'extensive'
+                          chain mapping scheme which is used as a last resort if
+                          other schemes failed. The extensive chain mapping search
+                          must in the worst case check O(N!) possible mappings for
+                          complexes with N chains. Two octamers without symmetry
+                          would require 322560 mappings to be checked. To limit
+                          computations, no scores are computed if we try more than
+                          the maximal number of chain mappings. Defaults to 1000000.
+
+  sequence alignment arguments:
+    -cc, --consistency-checks
+                          Take consistency checks into account. By default residue name
+                          consistency between a model-reference pair would be checked
+                          but only a warning message will be displayed and the script
+                          will continue to calculate scores. If this flag is ON, checks
+                          will not be ignored and if the pair does not pass the test
+                          all the scores for that pair will be marked as a FAILURE.
+    -rna, --residue-number-alignment
+                          Make alignment based on residue number instead of using
+                          a global BLOSUM62-based alignment.
+
+  QS score arguments:
+    -qs, --qs-score       Calculate QS-score.
+    --qs-rmsd             Calculate CA RMSD between shared CA atoms of mapped chains.
+                          This uses a superposition using all mapped chains which
+                          minimizes the CA RMSD.
+
+  lDDT score arguments:
+    -l, --lddt            Calculate lDDT.
+    -ir INCLUSION_RADIUS, --inclusion-radius INCLUSION_RADIUS
+                          Distance inclusion radius for lDDT. Defaults to 15 A.
+    -ss SEQUENCE_SEPARATION, --sequence-separation SEQUENCE_SEPARATION
+                          Sequence separation. Only distances between residues whose
+                          separation is higher than the provided parameter are
+                          considered when computing the score. Defaults to 0.
+    -spr, --save-per-residue-scores
+
 
 By default the verbosity is set to 3 which will result in the informations
 being shown in the console. The result can be (optionally) saved as JSON file
@@ -47,37 +257,42 @@ The output file has following format:
                       "residue_names_consistent": <Are the residue numbers consistent? true or false>,
                       "mapping": {
                           "chain_mapping": <Mapping of chains eg. {"A": "B", "B": "A"}>,
-                          "chain_mapping_scheme": <Scheme used to get mapping, check mapping manually if "permissive" or "extensive">,
+                          "chain_mapping_scheme": <Scheme used to get mapping, check mapping manually
+                                                   if "permissive" or "extensive">,
                           "alignments": <list of chain-chain alignments in FASTA format>
                       }
                   }, 
-                  "lddt": {  # calculated when --lddt (-l) option is selected
+                  "lddt": {
+                      # calculated when --lddt (-l) option is selected
                       "oligo_lddt": {
                           "status": <SUCCESS or FAILURE>,
                           "error": <ERROR message if any>, 
-                          "global_score": <calculated oligomeric lddt score>, 
+                          "global_score": <calculated oligomeric lDDT score>
                       }, 
                       "weighted_lddt": {
                           "status": <SUCCESS or FAILURE>,
                           "error": <ERROR message if any>, 
-                          "global_score": <calculated weighted lddt score>, 
+                          "global_score": <calculated weighted lDDT score>
                       }, 
-                      "single_chain_lddt": [ # a list of chain-chain lDDts
+                      "single_chain_lddt": [
+                          # a list of chain-chain lDDTs
                           {
                               "status": <SUCCESS or FAILURE>,
                               "error": <ERROR message if any>, 
                               "reference_chain": <name of the chain in reference>, 
                               "model_chain": <name of the chain in model>
-                              "global_score": <calculated single-chain lddt score>, 
-                              "conserved_contacts": <number of conserved contacts between model and reference>, 
-                              "total_contacts": <total number of contacts between model and reference>,
-                              "per_residue_scores": [  # per-residue lDDT scores - calculated when --save-per-residue-scores (-spr) option is selected
+                              "global_score": <calculated single-chain lDDT score>, 
+                              "conserved_contacts": <number of conserved contacts between model and reference>,
+                              "total_contacts": <total number of contacts in reference>,
+                              "per_residue_scores": [
+                                  # per-residue lDDT scores
+                                  # only calculated when --save-per-residue-scores (-spr) option is selected
                                   {
-                                      "total_contacts": <total number of contacts between model and reference>, 
-                                      "residue_name": <three letter code of the residue in reference chain>, 
-                                      "lddt": <residue lDDT score>, 
-                                      "conserved_contacts": <number of conserved contacts between model and reference for given residue>, 
-                                      "residue_number": <residue number in reference chain>
+                                      "residue_name": <three letter code of the residue in reference chain>,
+                                      "residue_number": <residue number in reference chain>,
+                                      "lddt": <residue lDDT score>,
+                                      "conserved_contacts": <conserved_contacts for given residue>,
+                                      "total_contacts": <total_contacts for given residue>
                                   },
                                   .
                                   .
@@ -86,13 +301,13 @@ The output file has following format:
                           }
                       ]
                   },
-                  "qs_score": {   # calculated when --qs-score (-q) option is selected
+                  "qs_score": {
+                    # calculated when --qs-score (-q) option is selected
                     "status": <SUCCESS or FAILURE>,
-                    "error": <ERROR message if any>, 
-                    "global_score": <Global QS-score>, 
-                    "best_score": <Best QS-score>, 
-                }
-
+                    "error": <ERROR message if any>,
+                    "global_score": <Global QS-score>,
+                    "best_score": <Best QS-score>
+                  }
               }
           }
       }, 
@@ -107,8 +322,9 @@ Example usage:
 
 .. code-block:: console
 
-  $ curl https://www.cameo3d.org/static/data/modeling/2018.03.03/5X7J_B/bu_target_01.pdb > reference.pdb
-  $ curl https://www.cameo3d.org/static/data/modeling/2018.03.03/5X7J_B/servers/server11/oligo_model-1/superposed_oligo_model-1.pdb > model.pdb
+  $ CAMEO_TARGET_URL=https://www.cameo3d.org/static/data/modeling/2018.03.03/5X7J_B
+  $ curl $CAMEO_TARGET_URL/bu_target_01.pdb > reference.pdb
+  $ curl $CAMEO_TARGET_URL/servers/server11/oligo_model-1/superposed_oligo_model-1.pdb > model.pdb
   $ $OST_ROOT/bin/ost compare-structures \
         --model model.pdb --reference reference.pdb --output output.json \
         --qs-score --residue-number-alignment --lddt --structural-checks \
@@ -248,114 +464,133 @@ Example usage:
   ################################################################################
   Saving output into output.json
 
+This reads the model and reference file and calculates QS- and lDDT-scores
+between them. In the example above the output file looks as follows (FASTA
+alignments were cut in display here for readability):
 
-This reads the model and reference file and calculates QS-score between them.
-In the example above the output file looks as follows:
+..  code snippet to fix output.json generated above
+  import json
+  json_data = json.load(open("output.json"))
+  mapping = json_data["result"]["model.pdb"]["reference.pdb"]["info"]["mapping"]
+  new_alns = list()
+  for aln in mapping["alignments"]:
+    aln_lines = aln.splitlines()
+    aln_lines[1] = aln_lines[1][:20] + "..."
+    aln_lines[3] = aln_lines[3][:20] + "..."
+    new_alns.append("\n".join(aln_lines))
+  mapping["alignments"] = new_alns
+  json_data["options"]["parameter_file"] = "Path to stage/share/openstructure/stereo_chemical_props.txt"
+  json_data["options"]["compound_library"] = "Path to stage/share/openstructure/compounds.chemlib"
+  with open("output_fixed.json", "w") as outfile:
+    json.dump(json_data, outfile, indent=4, sort_keys=True)
 
-.. code-block:: python
+.. code-block:: json
 
   {
+      "options": {
+          "angle_tolerance": 15.0, 
+          "bond_tolerance": 15.0, 
+          "c_alpha_only": false, 
+          "chain_mapping": null, 
+          "clean_element_column": true, 
+          "compound_library": "Path to stage/share/openstructure/compounds.chemlib", 
+          "consistency_checks": true, 
+          "cwd": "/home/taurielg/GT/Code/ost/build", 
+          "dump_structures": false, 
+          "dump_suffix": ".compare.structures.pdb", 
+          "fault_tolerant": false, 
+          "inclusion_radius": 15.0, 
+          "lddt": true, 
+          "map_nonstandard_residues": true, 
+          "model": "model.pdb", 
+          "model_selection": "", 
+          "molck": true, 
+          "output": "output.json", 
+          "parameter_file": "Path to stage/share/openstructure/stereo_chemical_props.txt", 
+          "qs_max_mappings_extensive": 1000000, 
+          "qs_rmsd": false, 
+          "qs_score": true, 
+          "reference": "reference.pdb", 
+          "reference_selection": "", 
+          "remove": [
+              "oxt", 
+              "hyd", 
+              "unk"
+          ], 
+          "residue_number_alignment": true, 
+          "save_per_residue_scores": false, 
+          "sequence_separation": 0, 
+          "structural_checks": true, 
+          "verbosity": 3
+      }, 
       "result": {
           "model.pdb": {
               "reference.pdb": {
                   "info": {
-                      "residue_names_consistent": true, 
                       "mapping": {
+                          "alignments": [
+                              ">reference:A\n-PGLFLTLEGLDGSGKTTQA...\n>model:B\nMPGLFLTLEGLDGSGKTTQA...", 
+                              ">reference:B\n-PGLFLTLEGLDGSGKTTQA...\n>model:A\nMPGLFLTLEGLDGSGKTTQA..."
+                          ], 
                           "chain_mapping": {
                               "A": "B", 
                               "B": "A"
                           }, 
-                          "chain_mapping_scheme": "strict", 
-                          "alignments": [
-                              ">reference:A\n-PGLFLTLEGLDGSGKTTQARRLAAFLEAQGRPVLLTREPGGGLPEVRSL---QELSPEAEYLLFSADRAEHVRKVILPGLAAGKVVISDRYLDSSLAYQGYGRGLPLPWLREVAREATRGLKPRLTFLLDLPPEAALRRVR-------LGLEFFRRVREGYLALARAEPGRFVVLDATLPEEEIARAIQAHLRPLLP\n>model:B\nMPGLFLTLEGLDGSGKTTQARRLAAFLEAQGRPVLLTREPGGGLPEVRSLLLTQELSPEAEYLLFSADRAEHVRKVILPGLAAGKVVISDRYLDSSLAYQGYGRGLPLPWLREVAREATRGLKPRLTFLLDLPPEAALRRVRRPDRLEGLGLEFFRRVREGYLALARAEPGRFVVLDATLPEEEIARAIQAHLRPLLP", 
-                              ">reference:B\n-PGLFLTLEGLDGSGKTTQARRLAAFLEAQGRPVLLTREPGGGLPEVRSLLLTQELSPEAEYLLFSADRAEHVRKVILPGLAAGKVVISDRYLDSSLAYQGYGRGLPLPWLREVAREATRGLKPRLTFLLDLPPEAALRRVRRPDRLEGLGLEFFRRVREGYLALARAEPGRFVVLDATLPEEEIARAIQAHLRPLLP\n>model:A\nMPGLFLTLEGLDGSGKTTQARRLAAFLEAQGRPVLLTREPGGGLPEVRSLLLTQELSPEAEYLLFSADRAEHVRKVILPGLAAGKVVISDRYLDSSLAYQGYGRGLPLPWLREVAREATRGLKPRLTFLLDLPPEAALRRVRRPDRLEGLGLEFFRRVREGYLALARAEPGRFVVLDATLPEEEIARAIQAHLRPLLP"
-                          ]
-                      }
+                          "chain_mapping_scheme": "strict"
+                      }, 
+                      "residue_names_consistent": true
                   }, 
                   "lddt": {
                       "oligo_lddt": {
-                          "status": "SUCCESS", 
+                          "error": "", 
                           "global_score": 0.8025223275721413, 
-                          "error": ""
-                      }, 
-                      "weighted_lddt": {
-                          "status": "SUCCESS", 
-                          "global_score": 0.804789180710712, 
-                          "error": ""
+                          "status": "SUCCESS"
                       }, 
                       "single_chain_lddt": [
                           {
-                              "status": "SUCCESS", 
-                              "global_score": 0.8257459402084351, 
                               "conserved_contacts": 877834, 
-                              "reference_chain": "A", 
-                              "total_contacts": 1063080, 
                               "error": "", 
-                              "model_chain": "B"
+                              "global_score": 0.8257459402084351, 
+                              "model_chain": "B", 
+                              "reference_chain": "A", 
+                              "status": "SUCCESS", 
+                              "total_contacts": 1063080
                           }, 
                           {
-                              "status": "SUCCESS", 
-                              "global_score": 0.7854443788528442, 
                               "conserved_contacts": 904568, 
-                              "reference_chain": "B", 
-                              "total_contacts": 1151664, 
                               "error": "", 
-                              "model_chain": "A"
+                              "global_score": 0.7854443788528442, 
+                              "model_chain": "A", 
+                              "reference_chain": "B", 
+                              "status": "SUCCESS", 
+                              "total_contacts": 1151664
                           }
-                      ]
+                      ], 
+                      "weighted_lddt": {
+                          "error": "", 
+                          "global_score": 0.804789180710712, 
+                          "status": "SUCCESS"
+                      }
                   }, 
                   "qs_score": {
-                      "status": "SUCCESS", 
-                      "global_score": 0.8974384796108209, 
                       "best_score": 0.9022811630070536, 
-                      "error": ""
+                      "error": "", 
+                      "global_score": 0.8974384796108209, 
+                      "status": "SUCCESS"
                   }
               }
           }
-      }, 
-      "options": {
-          "reference": "reference.pdb", 
-          "structural_checks": true, 
-          "chain_mapping": null, 
-          "bond_tolerance": 15.0, 
-          "parameter_file": "Path to stage/share/openstructure/stereo_chemical_props.txt", 
-          "consistency_checks": true, 
-          "qs_score": true, 
-          "map_nonstandard_residues": true, 
-          "save_per_residue_scores": false, 
-          "fault_tolerant": false, 
-          "reference_selection": "", 
-          "qs_rmsd": false, 
-          "cwd": "CWD", 
-          "inclusion_radius": 15.0, 
-          "angle_tolerance": 15.0, 
-          "c_alpha_only": false, 
-          "clean_element_column": true, 
-          "dump_suffix": ".compare.structures.pdb", 
-          "compound_library": "Path to stage/share/openstructure/compounds.chemlib", 
-          "dump_structures": false, 
-          "residue_number_alignment": true, 
-          "verbosity": 3, 
-          "remove": [
-              "oxt", 
-              "hyd", 
-              "unk"
-          ], 
-          "molck": true, 
-          "sequence_separation": 0, 
-          "output": "output.json", 
-          "model": "model.pdb", 
-          "lddt": true, 
-          "model_selection": ""
       }
   }
 
-If all the structures are clean one can omit all the checking steps and
-calculate eg. QS-score directly:
+If all the structures are clean and have matching residue numbers, one can omit
+all the checking steps and calculate scores directly as here:
 
 .. code:: console
 
-  $ $OST_ROOT/bin/ost compare-structures --model model.pdb --reference reference.pdb --output output_qs.json --qs-score --residue-number-alignment
+  $ $OST_ROOT/bin/ost compare-structures \
+        --model model.pdb --reference reference.pdb --output output_qs.json \
+        --qs-score --residue-number-alignment
 
   ################################################################################
   Reading input files (fault_tolerant=False)
@@ -388,4 +623,3 @@ calculate eg. QS-score directly:
   QSscore reference.pdb, model.pdb: best: 0.90, global: 0.90
   ################################################################################
   Saving output into output_qs.json
-
diff --git a/modules/io/doc/mmcif.rst b/modules/io/doc/mmcif.rst
index 23dc32f461a08cf1633362e60bb3b9c54ecb09af..479085543c91e249c98731dcbf613c21548b5565 100644
--- a/modules/io/doc/mmcif.rst
+++ b/modules/io/doc/mmcif.rst
@@ -373,6 +373,33 @@ of the annotation available.
     Also available as :meth:`GetTitle`. May also be modified by
     :meth:`SetTitle`.
 
+  .. attribute:: book_publisher
+
+    Name of publisher of the citation, relevant for books and book chapters.
+
+    Also available as :meth:`GetBookPublisher` and :meth:`SetBookPublisher`.
+
+  .. attribute:: book_publisher_city
+
+    City of the publisher of the citation, relevant for books and book
+    chapters.
+
+    Also available as :meth:`GetBookPublisherCity` and
+    :meth:`SetBookPublisherCity`.
+
+ 
+  .. attribute:: citation_type
+
+     Defines where a citation was published. Either journal, book or unknown.
+
+     Also available as :meth:`GetCitationType`. May also be modified by
+     :meth:`SetCitationType` with values from :class:`MMCifInfoCType`. For
+     conveinience setters :meth:`SetCitationTypeJournal`,
+     :meth:`SetCitationTypeBook` and :meth:`SetCitationTypeUnknown` exist.
+
+     For checking the type of a citation, :meth:`IsCitationTypeJournal`,
+     :meth:`IsCitationTypeBook` and :meth:`IsCitationTypeUnknown` can be used.
+
   .. attribute:: authors
 
     Stores a :class:`~ost.StringList` of authors.
@@ -460,6 +487,54 @@ of the annotation available.
 
     See :attr:`title`
 
+  .. method:: GetBookPublisher
+
+    See :attr:`book_publisher`
+
+  .. method:: SetBookPublisher
+
+    See :attr:`book_publisher`
+
+  .. method:: GetBookPublisherCity
+
+    See :attr:`book_publisher_city`
+
+  .. method:: SetBookPublisherCity
+
+    See :attr:`book_publisher_city`
+
+  .. method:: GetCitationType()
+
+    See :attr:`citation_type`
+
+  .. method:: SetCitationType(publication_type)
+
+    See :attr:`citation_type`
+
+  .. method:: SetCitationTypeJournal()
+
+    See :attr:`citation_type`
+
+  .. method:: SetCitationTypeBook()
+
+    See :attr:`citation_type`
+
+  .. method:: SetCitationTypeUnknown()
+
+    See :attr:`citation_type`
+
+  .. method:: IsCitationTypeJournal()
+
+    See :attr:`citation_type`
+
+  .. method:: IsCitationTypeBook()
+
+    See :attr:`citation_type`
+
+  .. method:: IsCitationTypeUnknown()
+
+    See :attr:`citation_type`
+ 
   .. method:: GetAuthorList()
 
     See :attr:`authors`
diff --git a/modules/io/pymod/export_mmcif_io.cc b/modules/io/pymod/export_mmcif_io.cc
index dd90843e39fa7a32ed661975b208ea6325e7f7db..806031da6e317f6496775e0f0b0c1ef6d613b5ed 100644
--- a/modules/io/pymod/export_mmcif_io.cc
+++ b/modules/io/pymod/export_mmcif_io.cc
@@ -49,6 +49,12 @@ void export_mmcif_io()
                                    return_value_policy<copy_const_reference>()))
     ;
 
+  enum_<MMCifInfoCitation::MMCifInfoCType>("MMCifInfoCType")
+    .value("Journal", MMCifInfoCitation::JOURNAL)
+    .value("Book", MMCifInfoCitation::BOOK)
+    .value("Unknown", MMCifInfoCitation::UNKNOWN)
+  ;
+ 
   class_<MMCifInfoCitation>("MMCifInfoCitation", init<>())
     .def("SetID", &MMCifInfoCitation::SetID)
     .def("GetID", &MMCifInfoCitation::GetID)
@@ -72,6 +78,18 @@ void export_mmcif_io()
     .def("GetYear", &MMCifInfoCitation::GetYear)
     .def("SetTitle", &MMCifInfoCitation::SetTitle)
     .def("GetTitle", &MMCifInfoCitation::GetTitle)
+    .def("SetBookPublisher", &MMCifInfoCitation::SetBookPublisher)
+    .def("GetBookPublisher", &MMCifInfoCitation::GetBookPublisher)
+    .def("SetBookPublisherCity", &MMCifInfoCitation::SetBookPublisherCity)
+    .def("GetBookPublisherCity", &MMCifInfoCitation::GetBookPublisherCity)
+    .def("SetCitationType", &MMCifInfoCitation::SetCitationType)
+    .def("SetCitationTypeJournal", &MMCifInfoCitation::SetCitationTypeJournal)
+    .def("SetCitationTypeBook", &MMCifInfoCitation::SetCitationTypeBook)
+    .def("SetCitationTypeUnknown", &MMCifInfoCitation::SetCitationTypeUnknown)
+    .def("GetCitationType", &MMCifInfoCitation::GetCitationType)
+    .def("IsCitationTypeJournal", &MMCifInfoCitation::IsCitationTypeJournal)
+    .def("IsCitationTypeBook", &MMCifInfoCitation::IsCitationTypeBook)
+    .def("IsCitationTypeUnknown", &MMCifInfoCitation::IsCitationTypeUnknown)
     .def("SetAuthorList", &MMCifInfoCitation::SetAuthorList)
     .def("GetAuthorList", make_function(&MMCifInfoCitation::GetAuthorList,
                                    return_value_policy<copy_const_reference>()))
@@ -94,9 +112,18 @@ void export_mmcif_io()
                   &MMCifInfoCitation::SetYear)
     .add_property("title", &MMCifInfoCitation::GetTitle,
                   &MMCifInfoCitation::SetTitle)
+    .add_property("book_publisher", &MMCifInfoCitation::GetBookPublisher,
+                  &MMCifInfoCitation::SetBookPublisher)
+    .add_property("book_publisher_city",
+                  &MMCifInfoCitation::GetBookPublisherCity,
+                  &MMCifInfoCitation::SetBookPublisherCity)
+    .add_property("citation_type", &MMCifInfoCitation::GetCitationType,
+                  &MMCifInfoCitation::SetCitationType)
     .add_property("authors", make_function(&MMCifInfoCitation::GetAuthorList,
                                    return_value_policy<copy_const_reference>()),
                   &MMCifInfoCitation::SetAuthorList)
+    .def("__eq__", &MMCifInfoCitation::operator==) 
+    .def("__ne__", &MMCifInfoCitation::operator!=)
   ;
 
   class_<std::vector<MMCifInfoCitation> >("MMCifInfoCitationList", init<>())
diff --git a/modules/io/src/mol/mmcif_info.hh b/modules/io/src/mol/mmcif_info.hh
index 0a00a838258de949f47c1796a9c70c93b284ef1e..68d60318f9fe1328eca94b7fc33daca14236749e 100644
--- a/modules/io/src/mol/mmcif_info.hh
+++ b/modules/io/src/mol/mmcif_info.hh
@@ -390,11 +390,17 @@ private:
 
 class DLLEXPORT_OST_IO MMCifInfoCitation {
 public:
+  /// \enum types of citations
+  typedef enum {
+    JOURNAL,
+    BOOK,
+    UNKNOWN
+  } MMCifInfoCType;
+
   /// \brief Create a citation.
   MMCifInfoCitation(): id_(""), where_(UNKNOWN), cas_(""), published_in_(""),
     volume_(""), page_first_(""), page_last_(""), doi_(""), pubmed_(0),
-    year_(0), title_("") {};
-
+    year_(0), title_(""), book_publisher_(""), book_publisher_city_("") {};
   /// \brief Set ID
   ///
   /// \param id ID
@@ -463,12 +469,35 @@ public:
   /// \return last page
   String GetPageLast() const { return page_last_; }
 
+  /// \brief Set the publisher for a book
+  ///
+  /// \param publisher
+  void SetBookPublisher(String publisher) { book_publisher_ = publisher; }
+
+  /// \brief Get the publisher of a book
+  ///
+  /// \return publisher
+  String GetBookPublisher() const { return book_publisher_; }
+
+  /// \brief Set the publisher city for a book
+  ///
+  /// \param publisher_city
+  void SetBookPublisherCity(String publisher_city) {
+    book_publisher_city_ = publisher_city;
+  }
+
+  /// \brief Get the publisher city of a book
+  ///
+  /// \return publisher_city
+  String GetBookPublisherCity() const { return book_publisher_city_; }
+
+//book_publisher_city_
+
   /// \brief Set the DOI of a document
   ///
   /// \param doi
   void SetDOI(String doi) { doi_ = doi; }
 
-
   /// \brief Get the DOI of a document
   ///
   /// \return DOI
@@ -506,6 +535,54 @@ public:
   /// \return title
   String GetTitle() const { return title_; }
 
+  /// \brief Set the type of a publication
+  ///
+  /// \param publication_type
+  void SetCitationType(MMCifInfoCType publication_type) {
+    where_ = publication_type;
+  }
+
+  /// \brief Set the type of a publication to journal
+  void SetCitationTypeJournal() {
+    where_ = MMCifInfoCitation::JOURNAL;
+  }
+
+  /// \brief Set the type of a publication to book
+  void SetCitationTypeBook() {
+    where_ = MMCifInfoCitation::BOOK;
+  }
+
+  /// \brief Set the type of a publication to unknown
+  void SetCitationTypeUnknown() {
+    where_ = MMCifInfoCitation::UNKNOWN;
+  }
+
+  /// \brief Get the type of a publication
+  ///
+  /// \return citation type
+  MMCifInfoCType GetCitationType() const { return where_; }
+
+  /// \brief Check a citation to be published in a journal
+  ///
+  /// \return true or false
+  bool IsCitationTypeJournal() const {
+    return where_ == MMCifInfoCitation::JOURNAL;
+  }
+
+  /// \brief Check a citation to be published in a book
+  ///
+  /// \return true or false
+  bool IsCitationTypeBook() const {
+    return where_ == MMCifInfoCitation::BOOK;
+  }
+
+  /// \brief Check if the citation type is unknow
+  ///
+  /// \return true or false
+  bool IsCitationTypeUnknown() const {
+    return where_ == MMCifInfoCitation::UNKNOWN;
+  }
+
   /// \brief Set the list of authors
   ///
   /// \param list
@@ -562,6 +639,18 @@ public:
         StringRef(cit.title_.c_str(), cit.title_.length())) {
       return false;
     }
+    if (StringRef(this->book_publisher_.c_str(),
+                  this->book_publisher_.length()) !=
+        StringRef(cit.book_publisher_.c_str(),
+                  cit.book_publisher_.length())) {
+      return false;
+    }
+    if (StringRef(this->book_publisher_city_.c_str(),
+                  this->book_publisher_city_.length()) !=
+        StringRef(cit.book_publisher_city_.c_str(),
+                  cit.book_publisher_city_.length())) {
+      return false;
+    }
     if (this->authors_ != cit.authors_) {
       return false;
     }
@@ -574,26 +663,21 @@ public:
   }
 
 private:
-  /// \enum types of citations
-  typedef enum {
-    JOURNAL,
-    BOOK,
-    UNKNOWN
-  } MMCifInfoCType;
-
-  String              id_;           ///< internal identifier
-  MMCifInfoCType      where_;        ///< journal or book?
-  String              cas_;          ///< CAS identifier
-  String              isbn_;         ///< ISBN no. of medium
-  String              published_in_; ///< book title or full journal name
-  String              volume_;       ///< journal volume
-  String              page_first_;   ///< first page
-  String              page_last_;    ///< last page
-  String              doi_;          ///< DOI identifier
-  int                 pubmed_;       ///< accession no.
-  int                 year_;         ///< year of publication
-  String              title_;        ///< title of the publication
-  std::vector<String> authors_;       ///< author information
+  String              id_;                  ///< internal identifier
+  MMCifInfoCType      where_;               ///< journal or book?
+  String              cas_;                 ///< CAS identifier
+  String              isbn_;                ///< ISBN no. of medium
+  String              published_in_;        ///< book title or journal name
+  String              volume_;              ///< journal volume
+  String              page_first_;          ///< first page
+  String              page_last_;           ///< last page
+  String              doi_;                 ///< DOI identifier
+  int                 pubmed_;              ///< accession no.
+  int                 year_;                ///< year of publication
+  String              title_;               ///< title of the publication
+  String              book_publisher_;      ///< name of publisher
+  String              book_publisher_city_; ///< location of publisher
+  std::vector<String> authors_;             ///< author information
 };
 
 /// \brief container class for information on obsolete entries
diff --git a/modules/io/src/mol/mmcif_reader.cc b/modules/io/src/mol/mmcif_reader.cc
index b473c96ca4ba3e0416211a74498386b39f82142b..5ed09e1edbec1cb892781eeffc5cd54feccb5d80 100644
--- a/modules/io/src/mol/mmcif_reader.cc
+++ b/modules/io/src/mol/mmcif_reader.cc
@@ -177,6 +177,8 @@ bool MMCifReader::OnBeginLoop(const StarLoopDesc& header)
     indices_[ABSTRACT_ID_CAS]         = header.GetIndex("abstract_id_CAS");
     indices_[BOOK_ID_ISBN]            = header.GetIndex("book_id_ISBN");
     indices_[BOOK_TITLE]              = header.GetIndex("book_title");
+    indices_[BOOK_PUBLISHER]          = header.GetIndex("book_publisher");
+    indices_[BOOK_PUBLISHER_CITY]     = header.GetIndex("book_publisher_city");
     indices_[JOURNAL_ABBREV]          = header.GetIndex("journal_abbrev");
     indices_[YEAR]                    = header.GetIndex("year");
     indices_[TITLE]                   = header.GetIndex("title");
@@ -495,6 +497,9 @@ void MMCifReader::ParseAndAddAtom(const std::vector<StringRef>& columns)
 
   if(!curr_residue_) { // unit test
     update_residue=true;
+    subst_res_id_ = cif_chain_name +
+                    columns[indices_[AUTH_SEQ_ID]].str() +
+                    columns[indices_[PDBX_PDB_INS_CODE]].str();
   } else if (!valid_res_num) {
     if (indices_[AUTH_SEQ_ID] != -1 &&
         indices_[PDBX_PDB_INS_CODE] != -1) {
@@ -780,31 +785,35 @@ void MMCifReader::ParseCitation(const std::vector<StringRef>& columns)
       cit.SetISBN(columns[indices_[BOOK_ID_ISBN]].str());
     }
   }
+  if (indices_[JOURNAL_ABBREV] != -1) {
+    if ((columns[indices_[JOURNAL_ABBREV]] != StringRef(".", 1)) &&
+        (columns[indices_[JOURNAL_ABBREV]][0] != '?')) {
+          cit.SetPublishedIn(columns[indices_[JOURNAL_ABBREV]].str());
+          cit.SetCitationTypeJournal();
+        }
+  }
   if (indices_[BOOK_TITLE] != -1) {
     // this is only set in few PDB entries and RCSB overrides it with
     // the journal_abbrev for their citations
     // -> as of August 1, 2017, 5 entries known: 5b1j, 5b1k, 5fax, 5fbz, 5ffn
     //    -> all those have journal_abbrev set
     if ((columns[indices_[BOOK_TITLE]] != StringRef(".", 1)) &&
-        (columns[indices_[BOOK_TITLE]][0]!='?')) {
+        (columns[indices_[BOOK_TITLE]][0] != '?')) {
+      // This will override published_in if already set by journal_abbrev. We
+      // consider this OK for now since usually the book title is copied to
+      // the journal_abbrev attribute.
       cit.SetPublishedIn(columns[indices_[BOOK_TITLE]].str());
-    }
-  }
-  if (indices_[JOURNAL_ABBREV] != -1) {
-    if (columns[indices_[JOURNAL_ABBREV]] != StringRef(".", 1)) {
-      const String journal_abbrev = columns[indices_[JOURNAL_ABBREV]].str();
-      const String published_in = cit.GetPublishedIn();
-      if (published_in.length() > 0 && published_in != journal_abbrev) {
-        LOG_WARNING(this->FormatDiagnostic(STAR_DIAG_WARNING,
-                                           "The 'published_in' field was "
-                                           "already set by citation.book_title "
-                                           "'" + published_in + "'! "
-                                           "This will be overwritten by "
-                                           "citation.journal_abbrev '" +
-                                           journal_abbrev + "'.",
-                                           this->GetCurrentLinenum()));
+      cit.SetCitationTypeBook();
+      
+      // In theory, book_publisher and book_publisher_city are only set for
+      // books and book chapters, so we only try to fetch them if the citation
+      // type points to book.
+      if (indices_[BOOK_PUBLISHER] != -1) {
+        cit.SetBookPublisher(columns[indices_[BOOK_PUBLISHER]].str());
+      }
+      if (indices_[BOOK_PUBLISHER_CITY] != -1) {
+        cit.SetBookPublisherCity(columns[indices_[BOOK_PUBLISHER_CITY]].str());
       }
-      cit.SetPublishedIn(journal_abbrev);
     }
   }
   if (indices_[JOURNAL_VOLUME] != -1) {
diff --git a/modules/io/src/mol/mmcif_reader.hh b/modules/io/src/mol/mmcif_reader.hh
index 163972b60160d565f188259b9b6056aed9606a03..4a58d567b866815f7f27ff3ff720f718dd36ab2f 100644
--- a/modules/io/src/mol/mmcif_reader.hh
+++ b/modules/io/src/mol/mmcif_reader.hh
@@ -392,6 +392,8 @@ private:
     ABSTRACT_ID_CAS,              ///< CAS identifier
     BOOK_ID_ISBN,                 ///< ISBN code assigned, if book cited
     BOOK_TITLE,                   ///< title of book storing the citation
+    BOOK_PUBLISHER,               ///< name of publisher f a book
+    BOOK_PUBLISHER_CITY,          ///< location of a publisher of a book
     JOURNAL_ABBREV,               ///< abbreviated journal title for articles
     JOURNAL_VOLUME,               ///< volume of cited journal
     PAGE_FIRST,                   ///< first page of citation
diff --git a/modules/io/tests/test_io_mmcif.py b/modules/io/tests/test_io_mmcif.py
index c7cb807bcca47a281aaa0b9e46b3a41724a6dcf9..dfd12ee30a384b35ca5bcf0e545c9a3528624396 100644
--- a/modules/io/tests/test_io_mmcif.py
+++ b/modules/io/tests/test_io_mmcif.py
@@ -40,6 +40,15 @@ class TestMMCifInfo(unittest.TestCase):
     # test title setting/ getting
     c.SetTitle('Foo')
     self.assertEquals(c.GetTitle(), 'Foo')
+    # test book_publisher set & get
+    c.SetBookPublisher("Hugo")
+    self.assertEquals(c.GetBookPublisher(), "Hugo")
+    # test book_publisher_city set & get
+    c.SetBookPublisherCity("Basel")
+    self.assertEquals(c.book_publisher_city, "Basel")
+    # test citation type
+    self.assertTrue(c.IsCitationTypeUnknown())
+    self.assertEquals(c.citation_type, io.MMCifInfoCType.Unknown)
     # test auhtors setting/ getting
     s = ost.StringList()
     s.append('Foo')
diff --git a/modules/io/tests/test_mmcif_info.cc b/modules/io/tests/test_mmcif_info.cc
index 2eb111336ddca9de0379430b91ba5b9906dfad9d..6707ff930cf29eded775b6f85d3ad57c0dc19cb4 100644
--- a/modules/io/tests/test_mmcif_info.cc
+++ b/modules/io/tests/test_mmcif_info.cc
@@ -72,7 +72,10 @@ BOOST_AUTO_TEST_CASE(mmcif_info_citation)
   cit.SetPubMed(815);
   cit.SetYear(815);
   cit.SetTitle("Foo");
+  cit.SetBookPublisher("Brackelmann and Sons");
+  cit.SetBookPublisherCity("Stenkelfeld");
   cit.SetAuthorList(author_list);
+  cit.SetCitationType(MMCifInfoCitation::JOURNAL);
   author_list.clear();
 
   BOOST_CHECK(cit.GetID() == "ID");
@@ -86,9 +89,23 @@ BOOST_AUTO_TEST_CASE(mmcif_info_citation)
   BOOST_CHECK(cit.GetPubMed() == 815);
   BOOST_CHECK(cit.GetYear() == 815);
   BOOST_CHECK(cit.GetTitle() == "Foo");
+  BOOST_CHECK(cit.GetBookPublisher() == "Brackelmann and Sons");
+  BOOST_CHECK(cit.GetBookPublisherCity() == "Stenkelfeld");
+  BOOST_CHECK(cit.GetCitationType() == MMCifInfoCitation::JOURNAL);
+  BOOST_CHECK(cit.IsCitationTypeJournal() == true);
+  BOOST_CHECK(cit.IsCitationTypeBook() == false);
+  BOOST_CHECK(cit.IsCitationTypeUnknown() == false);
   author_list = cit.GetAuthorList();
   BOOST_CHECK(author_list.back() == "Kabel, H.");
 
+  // checking all possible variants of citation type
+  cit.SetCitationTypeJournal();
+  BOOST_CHECK(cit.IsCitationTypeJournal() == true);
+  cit.SetCitationTypeBook();
+  BOOST_CHECK(cit.IsCitationTypeBook() == true);
+  cit.SetCitationTypeUnknown();
+  BOOST_CHECK(cit.IsCitationTypeUnknown() == true);
+
   BOOST_TEST_MESSAGE("  done.");
   BOOST_TEST_MESSAGE("  trying to add everything to an info object");
   MMCifInfo info = MMCifInfo();
diff --git a/modules/io/tests/test_mmcif_reader.cc b/modules/io/tests/test_mmcif_reader.cc
index e4129c2ab3ccc803a5c2828fb939bbf8f726deb5..df82b4ec85df1c455da8a5aa09652ac58a4b77b3 100644
--- a/modules/io/tests/test_mmcif_reader.cc
+++ b/modules/io/tests/test_mmcif_reader.cc
@@ -559,14 +559,18 @@ BOOST_AUTO_TEST_CASE(mmcif_citation_tests)
   tmmcif_h.SetCategory(StringRef("citation", 8));
   tmmcif_h.Add(StringRef("id", 2));
   tmmcif_h.Add(StringRef("year", 4));
+  tmmcif_h.Add(StringRef("book_publisher_city", 19));
   tmmcif_h.Add(StringRef("book_title", 10));
+  tmmcif_h.Add(StringRef("book_publisher", 14));
   tmmcif_h.Add(StringRef("journal_abbrev", 14));
   tmmcif_p.OnBeginLoop(tmmcif_h);
 
   // ensure that we use book_title if no journal given (no RCSB use of this)
   columns.push_back(StringRef("Foo", 3));
   columns.push_back(StringRef("1979", 4));
+  columns.push_back(StringRef("The restaurant", 14));
   columns.push_back(StringRef("The Guide", 9));
+  columns.push_back(StringRef("Doug", 4));
   columns.push_back(StringRef(".", 1));
 
   BOOST_CHECK_NO_THROW(tmmcif_p.ParseCitation(columns));
@@ -574,27 +578,33 @@ BOOST_AUTO_TEST_CASE(mmcif_citation_tests)
   BOOST_CHECK_EQUAL(cit.GetID(), String("Foo"));
   BOOST_CHECK_EQUAL(cit.GetYear(), 1979);
   BOOST_CHECK_EQUAL(cit.GetPublishedIn(), String("The Guide"));
+  BOOST_CHECK_EQUAL(cit.GetBookPublisher(), String("Doug"));
+  BOOST_CHECK_EQUAL(cit.GetBookPublisherCity(), String("The restaurant"));
+  BOOST_CHECK_EQUAL(cit.IsCitationTypeBook(), true);
 
   // ensure that we override book_title if not properly given
   columns.pop_back();
   columns.pop_back();
+  columns.pop_back();
   columns.push_back(StringRef(".", 1));
+  columns.push_back(StringRef("Doug", 4));
   columns.push_back(StringRef("Hitch", 5));
 
   BOOST_CHECK_NO_THROW(tmmcif_p.ParseCitation(columns));
   BOOST_CHECK_EQUAL(tmmcif_p.GetInfo().GetCitations().back().GetPublishedIn(),
                     String("Hitch"));
 
-  // ensure that we override book_title if journal given
-  // (def. behavior on RCSB webpage)
+  // ensure that we override journal if book_title given
+  columns.pop_back();
   columns.pop_back();
   columns.pop_back();
   columns.push_back(StringRef("The Guide", 9));
+  columns.push_back(StringRef("Doug", 4));
   columns.push_back(StringRef("Hitch", 5));
 
   BOOST_CHECK_NO_THROW(tmmcif_p.ParseCitation(columns));
   BOOST_CHECK_EQUAL(tmmcif_p.GetInfo().GetCitations().back().GetPublishedIn(),
-                    String("Hitch"));
+                    String("The Guide"));
 
   BOOST_TEST_MESSAGE("  done.");
 }
diff --git a/modules/mol/alg/doc/molalg.rst b/modules/mol/alg/doc/molalg.rst
index 4636a4ce45ee911c944366545be4bc10947d82ba..795d4f96e2613fcb50c66abcde51ffe91075f724 100644
--- a/modules/mol/alg/doc/molalg.rst
+++ b/modules/mol/alg/doc/molalg.rst
@@ -135,7 +135,7 @@ Local Distance Test scores (lDDT, DRMSD)
 
   This function calculates the Local Distance Difference Test, using the same
   threshold values as the GDT-HA test (the default set of thresholds used for
-  the lDTT score) (See previous functions). The thresholds are 0.5, 1, 2, and 4
+  the lDDT score) (See previous functions). The thresholds are 0.5, 1, 2, and 4
   Angstroms.
 
   The function only compares the input distance list to the first chain of the
@@ -549,7 +549,8 @@ Local Distance Test scores (lDDT, DRMSD)
 
 .. class:: lDDTScorer(reference, model, settings)
 
-  Object to compute lDDT scores.
+  Object to compute lDDT scores using :func:`LocalDistDiffTest` as in
+  `Mariani et al. <https://dx.doi.org/10.1093/bioinformatics/btt473>`_.
   
   Example usage.
   
diff --git a/modules/mol/alg/pymod/qsscoring.py b/modules/mol/alg/pymod/qsscoring.py
index 228fbb43208b9fee2b1b763275636e6a64849945..7c7cb3cfe1d8073152c5850ad807c9fa0376a28c 100644
--- a/modules/mol/alg/pymod/qsscoring.py
+++ b/modules/mol/alg/pymod/qsscoring.py
@@ -13,9 +13,8 @@ by `Bertoni et al. <https://dx.doi.org/10.1038/s41598-017-09654-8>`_.
   - ClustalW must be installed (unless you provide chain mappings)
   - Python modules `numpy` and `scipy` must be installed and available
     (e.g. use ``pip install scipy numpy``)
-
-Authors: Gerardo Tauriello, Martino Bertoni
 """
+# Original authors: Gerardo Tauriello, Martino Bertoni
 
 from ost import mol, geom, conop, seq, settings, PushVerbosityLevel
 from ost import LogError, LogWarning, LogScript, LogInfo, LogVerbose, LogDebug
@@ -143,6 +142,19 @@ class QSscorer:
 
     :type: :class:`int`
 
+  .. attribute:: max_mappings_extensive
+
+    Maximal number of chain mappings to test for 'extensive'
+    :attr:`chain_mapping_scheme`. The extensive chain mapping search must in the
+    worst case check O(N^2) * O(N!) possible mappings for complexes with N
+    chains. Two octamers without symmetry would require 322560 mappings to be
+    checked. To limit computations, a :class:`QSscoreError` is thrown if we try
+    more than the maximal number of chain mappings.
+    The value must be set before the first use of :attr:`chain_mapping`.
+    By default it is set to 100000.
+
+    :type: :class:`int`
+
   .. attribute:: res_num_alignment
 
     Forces each alignment in :attr:`alignments` to be based on residue numbers
@@ -174,6 +186,7 @@ class QSscorer:
     self.res_num_alignment = res_num_alignment
     self.calpha_only = self.qs_ent_1.calpha_only or self.qs_ent_2.calpha_only
     self.max_ca_per_chain_for_cm = 100
+    self.max_mappings_extensive = 100000
     # init cached stuff
     self._chem_mapping = None
     self._ent_to_cm_1 = None
@@ -361,12 +374,13 @@ class QSscorer:
     :type: :class:`dict` with key / value = :class:`str` (chain names, key
            for :attr:`ent_to_cm_1`, value for :attr:`ent_to_cm_2`)
     :raises: :class:`QSscoreError` if there are too many combinations to check
-             to find a chain mapping.
+             to find a chain mapping (see :attr:`max_mappings_extensive`).
     """
     if self._chain_mapping is None:
       self._chain_mapping, self._chain_mapping_scheme = \
         _GetChainMapping(self.ent_to_cm_1, self.ent_to_cm_2, self.symm_1,
-                         self.symm_2, self.chem_mapping)
+                         self.symm_2, self.chem_mapping,
+                         self.max_mappings_extensive)
       LogInfo('Mapping found: %s' % str(self._chain_mapping))
     return self._chain_mapping
 
@@ -1811,7 +1825,8 @@ def _FindSymmetry(qs_ent_1, qs_ent_2, ent_to_cm_1, ent_to_cm_2, chem_mapping):
   return [], []
 
 
-def _GetChainMapping(ent_1, ent_2, symm_1, symm_2, chem_mapping):
+def _GetChainMapping(ent_1, ent_2, symm_1, symm_2, chem_mapping,
+                     max_mappings_extensive):
   """
   :return: Tuple with mapping from *ent_1* to *ent_2* (see
            :attr:`QSscorer.chain_mapping`) and scheme used (see
@@ -1822,6 +1837,7 @@ def _GetChainMapping(ent_1, ent_2, symm_1, symm_2, chem_mapping):
   :param symm_1: See :attr:`QSscorer.symm_1`
   :param symm_2: See :attr:`QSscorer.symm_2`
   :param chem_mapping: See :attr:`QSscorer.chem_mapping`
+  :param max_mappings_extensive: See :attr:`QSscorer.max_mappings_extensive`
   """
   LogInfo('Symmetry-groups used in %s: %s' % (ent_1.GetName(), str(symm_1)))
   LogInfo('Symmetry-groups used in %s: %s' % (ent_2.GetName(), str(symm_2)))
@@ -1861,7 +1877,7 @@ def _GetChainMapping(ent_1, ent_2, symm_1, symm_2, chem_mapping):
   LogInfo('Inter Symmetry-group mappings to check: %s' \
           % count['inter']['mappings'])
   nr_mapp = count['intra']['mappings'] + count['inter']['mappings']
-  if nr_mapp > 100000: # 322560 is octamer vs octamer
+  if nr_mapp > max_mappings_extensive:
     raise QSscoreError('Too many possible mappings: %s' % nr_mapp)
 
   # to speed up the computations we cache chain views and RMSDs
diff --git a/modules/mol/alg/src/find_membrane.hh b/modules/mol/alg/src/find_membrane.hh
index 962ea68fc09680304c2da095f78900eff5483225..c2e00d87a9446bcf3907d35cdabae7bef248d483 100644
--- a/modules/mol/alg/src/find_membrane.hh
+++ b/modules/mol/alg/src/find_membrane.hh
@@ -1,10 +1,28 @@
-#include <ost/mol/mol.hh>
+//------------------------------------------------------------------------------
+// This file is part of the OpenStructure project <www.openstructure.org>
+//
+// Copyright (C) 2008-2011 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
+//------------------------------------------------------------------------------
+#ifndef OST_MOL_ALG_FIND_MEMBRANE_H
+#define OST_MOL_ALG_FIND_MEMBRANE_H
 
+#include <ost/mol/mol.hh>
 #include <ost/geom/geom.hh>
-#include <ost/io/binary_data_source.hh>
-#include <ost/io/binary_data_sink.hh>
 
-namespace ost { namespace mol{ namespace alg{
+namespace ost { namespace mol { namespace alg {
 
 struct FindMemParam{
   FindMemParam() { }
@@ -29,3 +47,5 @@ FindMemParam FindMembrane(ost::mol::EntityView& ent,
                           bool fast);
 
 }}} // ns
+
+#endif
\ No newline at end of file
diff --git a/modules/mol/alg/src/local_dist_diff_test.cc b/modules/mol/alg/src/local_dist_diff_test.cc
index de6ea31e6febb60862594f372891710e604539cd..c52bf115666962310aef3de44b126eb5dd300e9b 100644
--- a/modules/mol/alg/src/local_dist_diff_test.cc
+++ b/modules/mol/alg/src/local_dist_diff_test.cc
@@ -7,7 +7,6 @@
 #include <boost/concept_check.hpp>
 #include <boost/filesystem/convenience.hpp>
 #include <ost/mol/alg/consistency_checks.hh>
-#include <ost/io/stereochemical_params_reader.hh>
 
 namespace ost { namespace mol { namespace alg {
 
diff --git a/modules/mol/base/doc/entity.rst b/modules/mol/base/doc/entity.rst
index 6e956923bf33a9044cd3b38289c3df525b502bf2..4d9f7733d7244a87f0492e567eb16d1a752ab8dc 100644
--- a/modules/mol/base/doc/entity.rst
+++ b/modules/mol/base/doc/entity.rst
@@ -2032,7 +2032,9 @@ ChemClass
     * ``WATER``             = 'W'
     * ``UNKNOWN``           = 'U'
 
-  Python can implicitly convert characters to objects of this type.
+  The constants are defined directly within the :mod:`mol` module.
+  Python can implicitly convert characters to objects of this type. Note however
+  that only the first character of a :class:`str` object is considered!
 
   :param chem_class: Chemical class to set.
   :type chem_class:  :class:`str`
@@ -2071,7 +2073,8 @@ ChemType
     * ``WATERS``           = 'W'
     * ``UNKNOWN``          = 'U'
 
-  Python can implicitly convert characters to objects of this type.
+  Python can implicitly convert characters to objects of this type. Note however
+  that only the first character of a :class:`str` object is considered!
 
   :param chem_type: Chemical type to set.
   :type chem_type:  :class:`str`
diff --git a/modules/mol/base/pymod/export_residue.cc b/modules/mol/base/pymod/export_residue.cc
index 60566cfa51ee08035ba25e13549038470bef5eb2..d0915dcd719b02502ba2158cac951fb91acea277 100644
--- a/modules/mol/base/pymod/export_residue.cc
+++ b/modules/mol/base/pymod/export_residue.cc
@@ -68,6 +68,7 @@ void export_Residue()
   class_<ChemClass>("ChemClass", init<char>(args("chem_class")))
     .def(self!=self)
     .def(self==self)
+    .def(self_ns::str(self))
     .def("IsPeptideLinking", &ChemClass::IsPeptideLinking)
     .def("IsNucleotideLinking", &ChemClass::IsNucleotideLinking)
   ;
diff --git a/singularity/README.rst b/singularity/README.rst
index f90cf47553a96a3bf9eaab68970fcf91f2b109ff..7dbc72271e821a69dcafaa066a5e8940c4ae45b3 100644
--- a/singularity/README.rst
+++ b/singularity/README.rst
@@ -9,7 +9,7 @@ In order to build OST Singularity image:
 .. code-block:: bash
 
   cd <OST ROOT>/singularity
-  sudo singularity build ost.img Singularity.1.8.0
+  sudo singularity build ost.img Singularity.1.9.0
 
 .. note::
 
diff --git a/singularity/Singularity.1.8.0 b/singularity/Singularity.1.9.0
similarity index 98%
rename from singularity/Singularity.1.8.0
rename to singularity/Singularity.1.9.0
index 5a1e2600f40eecf259152800311d4686f124bed0..f4d5a7e339380e1a663c944670315642f2dcf876 100644
--- a/singularity/Singularity.1.8.0
+++ b/singularity/Singularity.1.9.0
@@ -17,7 +17,7 @@ export CPUS_FOR_MAKE=8
 export PYTHONPATH="/usr/local/lib64/python2.7/site-packages:${PYTHONPATH}"
 # When changing OPENSTRUCTURE_VERSION make sure to change it also in the
 # environment section of singularity recipe (this file).
-export OPENSTRUCTURE_VERSION="1.8.0"
+export OPENSTRUCTURE_VERSION="1.9.0"
 export OPENSTRUCTURE_SHARE="/usr/local/share/ost"
 export MSMS_VERSION="2.6.1"
 export OPENMM_VERSION="7.1.1"
@@ -148,7 +148,7 @@ if [ ! -f openstructure-${OPENSTRUCTURE_VERSION}.tar.gz ]; then
     # get the compound library
     wget ftp://ftp.wwpdb.org/pub/pdb/data/monomers/components.cif.gz
     stage/bin/chemdict_tool create components.cif.gz compounds.chemlib pdb
-    stage/bin/chemdict_tool update modules/conop/data/charmm.cif compounds.chemlib charmm
+    stage/bin/chemdict_tool update ../modules/conop/data/charmm.cif compounds.chemlib charmm
     mkdir -p $OPENSTRUCTURE_SHARE
     chmod a+rw -R $OPENSTRUCTURE_SHARE
     mv compounds.chemlib $OPENSTRUCTURE_SHARE
@@ -223,9 +223,10 @@ cd /home
 # ENVIRONMENT
 ##############################################################################
 export OST_ROOT="/usr/local"
-export OPENSTRUCTURE_VERSION="1.8.0"
+export OPENSTRUCTURE_VERSION="1.9.0"
+export OPENMM_LIB_PATH=/usr/local/openmm/lib/
 export PYTHONPATH="/usr/local/lib64/python2.7/site-packages:${PYTHONPATH}"
-export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib64"
+export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:/usr/local/lib64:${OPENMM_LIB_PATH}"
 export QT_X11_NO_MITSHM=1
 export IPYTHONDIR="/usr/local/share/ipython"
 export JUPYTER_CONFIG_DIR="/usr/local/etc/jupyter"