diff --git a/src/doc/gallery/Makefile b/src/doc/gallery/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..658f9a6a93e23957b20aee5f38e5565bde35af80
--- /dev/null
+++ b/src/doc/gallery/Makefile
@@ -0,0 +1,23 @@
+# This file is a part of the CaosDB Project.
+#
+# Copyright (C) 2022 IndiScale GmbH <info@indiscale.com>
+# Copyright (C) 2022 Daniel Hornung <d.hornung@indiscale.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program 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 Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+# Run tests on the examples.
+test:
+	python3 -m doctest simulation.py
+.PHONY: test
diff --git a/src/doc/gallery/index.rst b/src/doc/gallery/index.rst
new file mode 100644
index 0000000000000000000000000000000000000000..a6ef53e4c7d1272c5dbc8c62b4d90a89591cac0f
--- /dev/null
+++ b/src/doc/gallery/index.rst
@@ -0,0 +1,16 @@
+
+PyCaosDB Code Gallery
+=====================
+
+This chapter collects code examples which can be immediately run against an empty CaosDB instance.
+
+.. note::
+
+   These examples require a configuration file with valid server and user/password settings.  Refer
+   to the :ref:`Configuration <Configuration of PyCaosDB>` section for details.
+
+.. toctree::
+   :maxdepth: 2
+   :caption: The code examples:
+
+   simulation
diff --git a/src/doc/gallery/simulation.py b/src/doc/gallery/simulation.py
new file mode 100644
index 0000000000000000000000000000000000000000..342d5d980fc2b1a981f4a76d99e1954f8b2f5c2a
--- /dev/null
+++ b/src/doc/gallery/simulation.py
@@ -0,0 +1,134 @@
+"""
+Run a simulation and store the values into CaosDB.
+
+>>> main()              # doctest: +ELLIPSIS
+These distances resulted in small x,y, values:
+[...]
+"""
+
+import numpy as np
+import scipy.integrate
+import caosdb as db
+from caosadvancedtools.table_converter import to_table
+
+
+def setup_caosdb():
+    """Create the data model and insert it into CaosDB
+
+    The data model consists of the following RecordTypes:
+
+    Software
+      with author and revision.
+
+    SoftwareRun
+      A specific run of the sofware, with input parameters, time of completion and a result.
+
+    State
+      An aggregate of x,y,z values.
+
+    Parameters
+      In this case the x,y,z initial values before the integration, so this is just a state.
+
+    Result
+      The x,y,z values at the end of the software run, the final state.
+
+    The data model of course also contains the corresponding properties for these RecordTypes.
+    """
+
+    cont = db.Container()  # Container to insert all Entities at once into CaosDB
+    # create Properties
+    cont.append(db.Property("x", datatype=db.DOUBLE))
+    cont.append(db.Property("y", datatype=db.DOUBLE))
+    cont.append(db.Property("z", datatype=db.DOUBLE))
+    cont.append(db.Property("completed", datatype=db.DATETIME))
+    cont.append(db.Property("author", datatype=db.TEXT))
+    cont.append(db.Property("revision", datatype=db.TEXT))
+    # create RecordTypes
+    cont.append(db.RecordType("Software").add_property("author").add_property("revision"))
+    cont.append(db.RecordType("State").add_property("x", importance=db.OBLIGATORY)
+                .add_property("y").add_property("z"))
+    cont.append(db.RecordType("Parameters").add_parent("State", inheritance=db.ALL))
+    cont.append(db.RecordType("Result").add_parent("State", inheritance=db.RECOMMENDED))
+    cont.append(db.RecordType("SoftwareRun").add_property("Software").add_property("Parameters")
+                .add_property("completed").add_property("Result"))
+    cont.insert()  # actually insert the Entities
+
+
+def simulations(n, t_max):
+    """Run the simulations.
+
+    Parameters
+    ----------
+    n : int
+      The number of runs.
+
+    t_max : float
+      The maximum time of integration.
+    """
+
+    software = (db.Record("simulator").add_parent("Software")
+                .add_property("author", value="IndiScale GmbH")
+                .add_property("revision", value="1234CDEF89AB"))
+    software.insert()
+    for i in range(n):
+        # Get the parameters and result
+        initial, result = run_simulation(run=i, t_max=t_max)
+
+        # Prepare CaosDB insertion
+        run = db.Record().add_parent("SoftwareRun").add_property("Software", value=software.id)
+        parameters = (db.Record().add_parent("Parameters").add_property("x", initial[0])
+                      .add_property("y", initial[1]).add_property("z", initial[2]))
+        result_record = (db.Record().add_parent("Result").add_property("x", result[0])
+                         .add_property("y", result[1]).add_property("z", result[2]))
+        run.add_property("Parameters", value=parameters).add_property("Result", value=result_record)
+        cont = db.Container()
+        cont.extend([run, parameters, result_record])
+        cont.insert()           # Insert everything of this run into CaosDB.
+
+
+def run_simulation(run, t_max):
+    """Integrate the Rössler attractor from random initial values."""
+    a, b, c = (0.1, 0.1, 14)
+
+    def diff(t, x):
+        diff = np.array([-x[1] - x[2],
+                         x[0] + a * x[1],
+                         b + x[2] * (x[0] - c)])
+        return diff
+
+    x0 = np.random.uniform(-100, 100, 3)
+
+    result = scipy.integrate.solve_ivp(diff, [0, t_max], x0)
+    x = result.y[:, -1]
+    return (x0, x)
+
+
+def analyze():
+    """Find the initial conditions which produce the smalles x,y values after the given time."""
+    distance = 5
+    data = db.execute_query("""SELECT Parameters, Result FROM RECORD SoftwareRun WITH
+        (((Result.x < {dist}) AND (Result.x > -{dist}))
+        AND (Result.y < {dist})) AND Result.y > -{dist}""".format(dist=distance))
+    dataframe = to_table(data)  # Convert into a Pandas DataFrame
+
+    parameters = db.Container().extend([db.Record(id=id) for id in dataframe.Parameters]).retrieve()
+
+    initial_distances = [np.linalg.norm([p.get_property(dim).value for dim in ["x", "y", "z"]])
+                         for p in parameters]
+
+    print("These distances resulted in small x,y, values:\n{}".format(initial_distances))
+
+
+def main():
+    # 1. Set up the data model
+    setup_caosdb()
+
+    # 2. Run simulations
+    simulations(n=200, t_max=5)
+
+    # 3. Find initial conditions with interesting results
+    analyze()
+
+
+if __name__ == '__main__':
+    main()
diff --git a/src/doc/gallery/simulation.rst b/src/doc/gallery/simulation.rst
new file mode 100644
index 0000000000000000000000000000000000000000..ce1a7f457a142e36ef9f2b0cfe6a4df0b9fcedf6
--- /dev/null
+++ b/src/doc/gallery/simulation.rst
@@ -0,0 +1,14 @@
+========================================
+Managing data from numerical simulations
+========================================
+
+This code example
+
+1. sets up the data model
+2. runs simulations
+3. stores the simulation parameters and results into CaosDB
+4. retrieves the parameters for interesting results.
+
+:download:`Download code<simulation.py>`
+
+.. literalinclude:: simulation.py
diff --git a/src/doc/index.rst b/src/doc/index.rst
index bd29c6c56acf5c173e94ae6471a6aeba56ea4b93..004ae3a9926ed7a9a27720db1f3c28e72f1f3f28 100644
--- a/src/doc/index.rst
+++ b/src/doc/index.rst
@@ -12,6 +12,7 @@ Welcome to PyCaosDB's documentation!
    Concepts <concepts>
    Configuration <configuration>
    Administration <administration>
+   Code gallery <gallery/index>
    API documentation<_apidoc/caosdb>
 
 This is the documentation for the Python client library for CaosDB, ``PyCaosDB``.