diff --git a/doc/tests/scripts/loop_mm_sys_creation.py b/doc/tests/scripts/loop_mm_sys_creation.py
index a3f29d39973a814c4b52b708f6dd15d8701da9b9..642c4dac3b7403b5eb7a389608c6b28eeaa5336c 100644
--- a/doc/tests/scripts/loop_mm_sys_creation.py
+++ b/doc/tests/scripts/loop_mm_sys_creation.py
@@ -12,24 +12,20 @@ num_residues = len(res_list)
 
 # get all atom positions for full protein
 all_atoms = loop.AllAtomPositions(res_list)
-# get res. indices for loop followed by rest
-# -> here we add everything to rest but in practice this could be
-#    just a subset of residues relevant to the loop of interest
-loop_start = 10
-loop_length = 6
-res_indices = list(range(loop_start, loop_start + loop_length)) \
-            + list(range(0, loop_start)) \
-            + list(range(loop_start + loop_length, num_residues))
+# here full structure in res_indices but in practice this could
+# be just a subset of residues relevant to the loop of interest
+res_indices = range(num_residues)
+# define two loops (indices into res_indices list)
+loop_start_indices = [10, 20]
+loop_lengths = [6, 4]
 # define which of the res_indices is terminal
-is_n_ter = [False] * num_residues
-is_c_ter = [False] * num_residues
-is_n_ter[res_indices.index(0)] = True
-is_c_ter[res_indices.index(num_residues-1)] = True
+is_n_ter = [True] + [False] * (num_residues - 1)
+is_c_ter = [False] * (num_residues - 1) + [True]
 # get disulfid bridges
 disulfid_bridges = mm_sys.GetDisulfidBridges(all_atoms, res_indices)
 # setup MM system
-mm_sys.SetupSystem(all_atoms, res_indices, loop_length,
-                   is_n_ter, is_c_ter, disulfid_bridges)
+mm_sys.SetupSystem(all_atoms, res_indices, loop_start_indices,
+                   loop_lengths, is_n_ter, is_c_ter, disulfid_bridges)
 
 # run simulation
 sim = mm_sys.GetSimulation()
diff --git a/loop/doc/mm_system_creation.rst b/loop/doc/mm_system_creation.rst
index fbfd17382a7e7aa116915ec33ae878d0059eeb55..4cea3afc29cd59e1239391d46f13a14161cdc944 100644
--- a/loop/doc/mm_system_creation.rst
+++ b/loop/doc/mm_system_creation.rst
@@ -11,6 +11,15 @@ The example below showcases the creation and use of an MM system:
 
 .. literalinclude:: ../../../tests/doc/scripts/loop_mm_sys_creation.py
 
+.. note::
+
+  To simplify use when sidechains may be missing and the region of interest for
+  the MM system has to be determined, it might be better to use the simpler
+  :class:`promod3.modelling.SidechainReconstructor` and
+  :class:`promod3.modelling.AllAtomRelaxer` classes. Even if all the sidechains
+  are available, those classes will be helpful since the overhead to check
+  sidechains without reconstructing them is minimal.
+
 
 Create MM systems for loops
 --------------------------------------------------------------------------------
@@ -61,6 +70,8 @@ Create MM systems for loops
 
   .. method:: SetupSystem(all_pos, res_indices, loop_length, is_n_ter, \
                           is_c_ter, disulfid_bridges)
+              SetupSystem(all_pos, res_indices, loop_start_indices, \
+                          loop_lengths, is_n_ter, is_c_ter, disulfid_bridges)
 
     Setup a new system for the loop / surrounding defined by *all_pos* and
     *res_indices*. Positions are taken from *all_pos[res_indices[i]]* and each
@@ -72,11 +83,16 @@ Create MM systems for loops
     approximation. Similarly, we do not generate OXT atoms for residues followed
     by a gap unless they are specifically labelled as C-terminal.
 
-    Residues *res_indices[i]* for *i* < *loop_length* are loop, rest is
-    surrounding. Residues *res_indices[0]* / *res_indices[loop_length-1]* are
-    assumed to be N- / C-stems of loop. The stems of the loop are kept fixed (N,
+    Each loop is defined by an entry in *loop_start_indices* and *loop_lengths*.
+    For loop *i_loop*, *res_indices[loop_start_indices[i_loop]]* is the N-stem
+    and *res_indices[loop_start_indices[i_loop] + loop_lengths[i_loop] - 1]* is
+    the C-stem of the loop. The loop indices are expected to be contiguous in
+    *res_indices* between the stems. The stems of the loop are kept fixed (N,
     CA, CB for N-stem / CA, CB, C, O for C-stem) unless they are terminal
-    residues.
+    residues. Overlapping loops are merged and 0-length loops are removed.
+
+    If *loop_length* is given, a single loop with start index 0 and the given
+    loop length is defined.
 
     If a system was setup previously, it is overwritten completely.
 
@@ -88,8 +104,12 @@ Create MM systems for loops
     :type all_pos:  :class:`AllAtomPositions`
     :param res_indices: Residue indices into *all_pos*.
     :type res_indices:  :class:`list` of :class:`int`
-    :param loop_length: Length of loop.
+    :param loop_length: Length of single loop (incl. stems).
     :type loop_length:  :class:`int`
+    :param loop_start_indices: Start indices of loops.
+    :type loop_start_indices:  :class:`list` of :class:`int`
+    :param loop_lengths: Lengths of loops (incl. stems).
+    :type loop_lengths:  :class:`list` of :class:`int`
     :param is_n_ter: For each *res_indices[i]*, *is_n_ter[i]* defines whether
                      that residue is N-terminal.
     :type is_n_ter:  :class:`list` of :class:`bool`
@@ -102,10 +122,11 @@ Create MM systems for loops
     :type disulfid_bridges:  :class:`list` of :class:`tuple` with two
                              :class:`int`
 
-    :raises: :exc:`~exceptions.RuntimeError` if *loop_length* is longer than
-             *res_indices*, *res_indices* / *is_n_ter* / *is_c_ter* have
-             inconsistent lengths or if any *all_pos[res_indices[i]]* is invalid
-             or has unset heavy atoms.
+    :raises: :exc:`~exceptions.RuntimeError` if loops out of bounds with respect
+             to *res_indices*, all loops have length 0, *loop_start_indices* /
+             *loop_lengths* or *res_indices* / *is_n_ter* / *is_c_ter* have
+             inconsistent lengths, or if any *all_pos[res_indices[i]]* is
+             invalid or has unset heavy atoms.
 
   .. method:: UpdatePositions(all_pos, res_indices)
 
@@ -130,14 +151,21 @@ Create MM systems for loops
     :meth:`SetupSystem`. This function then extracts these positions back into a
     compatible output storage.
 
-    :param loop_pos: Storage for loop positions. Residues *loop_pos[i]* and
-                     *all_pos[res_indices[i]]* (of :meth:`SetupSystem`) must
-                     have matching amino acid types for *i* < *loop_length*.
+    If *loop_pos* is passed, only the loops are stored. The loop residues are
+    stored contiguously and the loops are stored in the order given by
+    :meth:`GetLoopStartIndices` / :meth:`GetLoopLengths`. The *loop_pos* storage
+    must have at least :meth:`GetNumLoopResidues` residues and must have
+    matching amino acid types with respect to the loop residues passed as
+    *all_pos* of :meth:`SetupSystem`.
+
+    If *out_pos* and *res_indices* are passed, residues must have matching amino
+    acid types for *out_pos[res_indices[i]]* and *all_pos[res_indices[i]]* of
+    :meth:`SetupSystem`. Only loop residues with *i* in the ranges defined by
+    :meth:`GetLoopStartIndices` / :meth:`GetLoopLengths` are touched.
+
+    :param loop_pos: Reduced storage only covering loop positions.
     :type loop_pos:  :class:`AllAtomPositions`
-    :param out_pos: Storage for loop positions. Residues
-                    *out_pos[res_indices[i]]* and *all_pos[res_indices[i]]* (of
-                    :meth:`SetupSystem`) must have matching amino acid types for
-                    *i* < *loop_length*.
+    :param out_pos: Storage for loop positions linked to *res_indices*.
     :type out_pos:  :class:`AllAtomPositions`
     :param res_indices: Residue indices into *out_pos*.
     :type res_indices:  :class:`list` of :class:`int`
@@ -151,6 +179,26 @@ Create MM systems for loops
              MM simulations.
     :rtype:  :class:`ost.mol.mm.Simulation`
 
+  .. method:: GetNumResidues
+
+    :return: Number of residues of current simulation setup.
+    :rtype:  :class:`int`
+
+  .. method:: GetNumLoopResidues
+
+    :return: Number of loop residues (incl. stems) of current simulation setup.
+    :rtype:  :class:`int`
+
+  .. method:: GetLoopStartIndices
+
+    :return: Start indices of loops (see :meth:`SetupSystem`).
+    :rtype:  :class:`list` of :class:`int`
+
+  .. method:: GetLoopLengths
+
+    :return: Lengths of loops (see :meth:`SetupSystem`).
+    :rtype:  :class:`list` of :class:`int`
+
   .. method:: GetForcefieldAminoAcids
 
     :return: Forcefield-specific amino acid type for each residue of last
diff --git a/loop/src/mm_system_creator.cc b/loop/src/mm_system_creator.cc
index c071411790bb073461f89b5ea3a92f260ab145b2..5ffe7a97b1b6bcfbfc2f190eb31d0bbe9170822f 100644
--- a/loop/src/mm_system_creator.cc
+++ b/loop/src/mm_system_creator.cc
@@ -197,7 +197,8 @@ MmSystemCreator::MmSystemCreator(ForcefieldLookupPtr ff_lookup,
                                  bool kill_electrostatics,
                                  Real nonbonded_cutoff,
                                  bool inaccurate_pot_energy)
-                                 : aa_lookup_(AminoAcidLookup::GetInstance()) {
+                                 : num_loop_residues_(0)
+                                 , aa_lookup_(AminoAcidLookup::GetInstance()) {
   // store settings and check for CPU support
   ff_lookup_ = ff_lookup;
   fix_surrounding_hydrogens_ = fix_surrounding_hydrogens;