Repository: zincware/MDSuite (language: python)

Issue:
Issue while plotting RDF directly after PMF
**Describe the bug**
Because we use `fig, ax = plt.subplots()` and `plt.plot()` if you run e.g.,

```py
project.experiments["NaCl"].sample_rate = 1
# project.experiments["ml_md"].run_computation.RadialDistributionFunction(number_of_configurations=5000, minibatch=1, stop=5000)
project.experiments["NaCl"].run_computation.RadialDistributionFunction(
    number_of_configurations=10000, benchmark=True, minibatch=1, plot=True, batches=1, stop=10000
)
project.experiments["NaCl"].run_computation.PotentialOfMeanForce()
```

in a Jupyter notebook in a single cell, the output will look like:
![image](https://user-images.githubusercontent.com/46721498/127813823-0a94b61a-9552-40c9-9ba5-417c7b29b76e.png)

**Potential fix**
Remove all `plt.plot()` references and purely use `fig, ax = plt.subplots()` to avoid issues like this.



Discussion / hints:

Fix patch:
diff --git a/README.rst b/README.rst
index 748fc58c..7bab4b14 100644
--- a/README.rst
+++ b/README.rst
@@ -84,6 +84,5 @@ of the autocomplete features that are available for the calculators.
     :alt: Build tests passing
     :target: https://mdsuite.readthedocs.io/en/latest/
 
-.. |license| image:: https://img.shields.io/badge/License-EPL-purple.svg?style=flat
-    :alt: Project license
-    :target: https://www.gnu.org/licenses/quick-guide-gplv3.en.html
\ No newline at end of file
+.. |license| image:: https://img.shields.io/badge/License-ZINCv0-purple.svg?style=flat
+    :alt: Project license
\ No newline at end of file
diff --git a/docs/source/_calculators/green_kubo_diffusion_coefficients.rst b/docs/source/_calculators/green_kubo_diffusion_coefficients.rst
index 1dfeb082..33740d2c 100644
--- a/docs/source/_calculators/green_kubo_diffusion_coefficients.rst
+++ b/docs/source/_calculators/green_kubo_diffusion_coefficients.rst
@@ -1,6 +1,6 @@
 Green-Kubo Diffusion Coefficients Class
 =====================================================
 
-.. autoclass:: mdsuite.calculators.green_kubo_self_diffusion_coefficients.GreenKuboSelfDiffusionCoefficients
+.. autoclass:: mdsuite.calculators.green_kubo_self_diffusion_coefficients.GreenKuboDiffusionCoefficients
     :members:
     
\ No newline at end of file
diff --git a/examples/notebooks/molten_nacl_1400K.ipynb b/examples/notebooks/molten_nacl_1400K.ipynb
new file mode 100644
index 00000000..209224cd
--- /dev/null
+++ b/examples/notebooks/molten_nacl_1400K.ipynb
@@ -0,0 +1,553 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Jupyter environment detected. Enabling Open3D WebVisualizer.\n",
+      "[Open3D INFO] WebRTC GUI backend enabled.\n",
+      "[Open3D INFO] WebRTCWindowSystem: HTTP handshake server disabled.\n"
+     ]
+    },
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "INFO - 2021-09-21 11:34:33,458 - utils - Note: NumExpr detected 12 cores but \"NUMEXPR_MAX_THREADS\" not set, so enforcing safe limit of 8.\n",
+      "INFO - 2021-09-21 11:34:33,459 - utils - NumExpr defaulting to 8 threads.\n"
+     ]
+    }
+   ],
+   "source": [
+    "import mdsuite as mds\n",
+    "import os\n",
+    "import sys\n",
+    "import tempfile\n",
+    "sys.path.insert(0, '../scripts')\n",
+    "from green_kubo_diffusion import load_data"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "temp_dir = tempfile.TemporaryDirectory()\n",
+    "os.chdir(temp_dir.name)\n",
+    "load_data()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "2021-09-21 11:34:34,201 - WARNING: Using Jupyter\n"
+     ]
+    },
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "WARNING - 2021-09-21 11:34:34,201 - project - Using Jupyter\n"
+     ]
+    },
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "2021-09-21 11:34:34,456 - INFO: Creating a new experiment!\n"
+     ]
+    },
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "INFO - 2021-09-21 11:34:34,456 - experiment - Creating a new experiment!\n"
+     ]
+    },
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "2021-09-21 11:34:34,458 - INFO: ** An experiment has been added titled NaCl_GK **\n"
+     ]
+    },
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "INFO - 2021-09-21 11:34:34,458 - experiment - ** An experiment has been added titled NaCl_GK **\n",
+      "100%|███████████████████████████████████| 1/1 [00:06<00:00,  6.72s/it]\n"
+     ]
+    }
+   ],
+   "source": [
+    "project = mds.Project(\"GK_Diffusion_Example\")\n",
+    "project.add_experiment(\n",
+    "    experiment='NaCl_GK',\n",
+    "    timestep=0.002,\n",
+    "    temperature=1400.0,\n",
+    "    units='metal',\n",
+    "    data='NaCl_gk_i_q.lammpstraj'\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "501"
+      ]
+     },
+     "execution_count": 4,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "project.experiments.NaCl_GK.number_of_configurations"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "\n",
+       "    <div class=\"bk-root\">\n",
+       "        <a href=\"https://bokeh.org\" target=\"_blank\" class=\"bk-logo bk-logo-small bk-logo-notebook\"></a>\n",
+       "        <span id=\"1271\">Loading BokehJS ...</span>\n",
+       "    </div>"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "application/javascript": [
+       "\n",
+       "(function(root) {\n",
+       "  function now() {\n",
+       "    return new Date();\n",
+       "  }\n",
+       "\n",
+       "  const force = true;\n",
+       "\n",
+       "  if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n",
+       "    root._bokeh_onload_callbacks = [];\n",
+       "    root._bokeh_is_loading = undefined;\n",
+       "  }\n",
+       "\n",
+       "  const JS_MIME_TYPE = 'application/javascript';\n",
+       "  const HTML_MIME_TYPE = 'text/html';\n",
+       "  const EXEC_MIME_TYPE = 'application/vnd.bokehjs_exec.v0+json';\n",
+       "  const CLASS_NAME = 'output_bokeh rendered_html';\n",
+       "\n",
+       "  /**\n",
+       "   * Render data to the DOM node\n",
+       "   */\n",
+       "  function render(props, node) {\n",
+       "    const script = document.createElement(\"script\");\n",
+       "    node.appendChild(script);\n",
+       "  }\n",
+       "\n",
+       "  /**\n",
+       "   * Handle when an output is cleared or removed\n",
+       "   */\n",
+       "  function handleClearOutput(event, handle) {\n",
+       "    const cell = handle.cell;\n",
+       "\n",
+       "    const id = cell.output_area._bokeh_element_id;\n",
+       "    const server_id = cell.output_area._bokeh_server_id;\n",
+       "    // Clean up Bokeh references\n",
+       "    if (id != null && id in Bokeh.index) {\n",
+       "      Bokeh.index[id].model.document.clear();\n",
+       "      delete Bokeh.index[id];\n",
+       "    }\n",
+       "\n",
+       "    if (server_id !== undefined) {\n",
+       "      // Clean up Bokeh references\n",
+       "      const cmd_clean = \"from bokeh.io.state import curstate; print(curstate().uuid_to_server['\" + server_id + \"'].get_sessions()[0].document.roots[0]._id)\";\n",
+       "      cell.notebook.kernel.execute(cmd_clean, {\n",
+       "        iopub: {\n",
+       "          output: function(msg) {\n",
+       "            const id = msg.content.text.trim();\n",
+       "            if (id in Bokeh.index) {\n",
+       "              Bokeh.index[id].model.document.clear();\n",
+       "              delete Bokeh.index[id];\n",
+       "            }\n",
+       "          }\n",
+       "        }\n",
+       "      });\n",
+       "      // Destroy server and session\n",
+       "      const cmd_destroy = \"import bokeh.io.notebook as ion; ion.destroy_server('\" + server_id + \"')\";\n",
+       "      cell.notebook.kernel.execute(cmd_destroy);\n",
+       "    }\n",
+       "  }\n",
+       "\n",
+       "  /**\n",
+       "   * Handle when a new output is added\n",
+       "   */\n",
+       "  function handleAddOutput(event, handle) {\n",
+       "    const output_area = handle.output_area;\n",
+       "    const output = handle.output;\n",
+       "\n",
+       "    // limit handleAddOutput to display_data with EXEC_MIME_TYPE content only\n",
+       "    if ((output.output_type != \"display_data\") || (!Object.prototype.hasOwnProperty.call(output.data, EXEC_MIME_TYPE))) {\n",
+       "      return\n",
+       "    }\n",
+       "\n",
+       "    const toinsert = output_area.element.find(\".\" + CLASS_NAME.split(' ')[0]);\n",
+       "\n",
+       "    if (output.metadata[EXEC_MIME_TYPE][\"id\"] !== undefined) {\n",
+       "      toinsert[toinsert.length - 1].firstChild.textContent = output.data[JS_MIME_TYPE];\n",
+       "      // store reference to embed id on output_area\n",
+       "      output_area._bokeh_element_id = output.metadata[EXEC_MIME_TYPE][\"id\"];\n",
+       "    }\n",
+       "    if (output.metadata[EXEC_MIME_TYPE][\"server_id\"] !== undefined) {\n",
+       "      const bk_div = document.createElement(\"div\");\n",
+       "      bk_div.innerHTML = output.data[HTML_MIME_TYPE];\n",
+       "      const script_attrs = bk_div.children[0].attributes;\n",
+       "      for (let i = 0; i < script_attrs.length; i++) {\n",
+       "        toinsert[toinsert.length - 1].firstChild.setAttribute(script_attrs[i].name, script_attrs[i].value);\n",
+       "        toinsert[toinsert.length - 1].firstChild.textContent = bk_div.children[0].textContent\n",
+       "      }\n",
+       "      // store reference to server id on output_area\n",
+       "      output_area._bokeh_server_id = output.metadata[EXEC_MIME_TYPE][\"server_id\"];\n",
+       "    }\n",
+       "  }\n",
+       "\n",
+       "  function register_renderer(events, OutputArea) {\n",
+       "\n",
+       "    function append_mime(data, metadata, element) {\n",
+       "      // create a DOM node to render to\n",
+       "      const toinsert = this.create_output_subarea(\n",
+       "        metadata,\n",
+       "        CLASS_NAME,\n",
+       "        EXEC_MIME_TYPE\n",
+       "      );\n",
+       "      this.keyboard_manager.register_events(toinsert);\n",
+       "      // Render to node\n",
+       "      const props = {data: data, metadata: metadata[EXEC_MIME_TYPE]};\n",
+       "      render(props, toinsert[toinsert.length - 1]);\n",
+       "      element.append(toinsert);\n",
+       "      return toinsert\n",
+       "    }\n",
+       "\n",
+       "    /* Handle when an output is cleared or removed */\n",
+       "    events.on('clear_output.CodeCell', handleClearOutput);\n",
+       "    events.on('delete.Cell', handleClearOutput);\n",
+       "\n",
+       "    /* Handle when a new output is added */\n",
+       "    events.on('output_added.OutputArea', handleAddOutput);\n",
+       "\n",
+       "    /**\n",
+       "     * Register the mime type and append_mime function with output_area\n",
+       "     */\n",
+       "    OutputArea.prototype.register_mime_type(EXEC_MIME_TYPE, append_mime, {\n",
+       "      /* Is output safe? */\n",
+       "      safe: true,\n",
+       "      /* Index of renderer in `output_area.display_order` */\n",
+       "      index: 0\n",
+       "    });\n",
+       "  }\n",
+       "\n",
+       "  // register the mime type if in Jupyter Notebook environment and previously unregistered\n",
+       "  if (root.Jupyter !== undefined) {\n",
+       "    const events = require('base/js/events');\n",
+       "    const OutputArea = require('notebook/js/outputarea').OutputArea;\n",
+       "\n",
+       "    if (OutputArea.prototype.mime_types().indexOf(EXEC_MIME_TYPE) == -1) {\n",
+       "      register_renderer(events, OutputArea);\n",
+       "    }\n",
+       "  }\n",
+       "\n",
+       "  \n",
+       "  if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n",
+       "    root._bokeh_timeout = Date.now() + 5000;\n",
+       "    root._bokeh_failed_load = false;\n",
+       "  }\n",
+       "\n",
+       "  const NB_LOAD_WARNING = {'data': {'text/html':\n",
+       "     \"<div style='background-color: #fdd'>\\n\"+\n",
+       "     \"<p>\\n\"+\n",
+       "     \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n",
+       "     \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n",
+       "     \"</p>\\n\"+\n",
+       "     \"<ul>\\n\"+\n",
+       "     \"<li>re-rerun `output_notebook()` to attempt to load from CDN again, or</li>\\n\"+\n",
+       "     \"<li>use INLINE resources instead, as so:</li>\\n\"+\n",
+       "     \"</ul>\\n\"+\n",
+       "     \"<code>\\n\"+\n",
+       "     \"from bokeh.resources import INLINE\\n\"+\n",
+       "     \"output_notebook(resources=INLINE)\\n\"+\n",
+       "     \"</code>\\n\"+\n",
+       "     \"</div>\"}};\n",
+       "\n",
+       "  function display_loaded() {\n",
+       "    const el = document.getElementById(\"1271\");\n",
+       "    if (el != null) {\n",
+       "      el.textContent = \"BokehJS is loading...\";\n",
+       "    }\n",
+       "    if (root.Bokeh !== undefined) {\n",
+       "      if (el != null) {\n",
+       "        el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n",
+       "      }\n",
+       "    } else if (Date.now() < root._bokeh_timeout) {\n",
+       "      setTimeout(display_loaded, 100)\n",
+       "    }\n",
+       "  }\n",
+       "\n",
+       "\n",
+       "  function run_callbacks() {\n",
+       "    try {\n",
+       "      root._bokeh_onload_callbacks.forEach(function(callback) {\n",
+       "        if (callback != null)\n",
+       "          callback();\n",
+       "      });\n",
+       "    } finally {\n",
+       "      delete root._bokeh_onload_callbacks\n",
+       "    }\n",
+       "    console.debug(\"Bokeh: all callbacks have finished\");\n",
+       "  }\n",
+       "\n",
+       "  function load_libs(css_urls, js_urls, callback) {\n",
+       "    if (css_urls == null) css_urls = [];\n",
+       "    if (js_urls == null) js_urls = [];\n",
+       "\n",
+       "    root._bokeh_onload_callbacks.push(callback);\n",
+       "    if (root._bokeh_is_loading > 0) {\n",
+       "      console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n",
+       "      return null;\n",
+       "    }\n",
+       "    if (js_urls == null || js_urls.length === 0) {\n",
+       "      run_callbacks();\n",
+       "      return null;\n",
+       "    }\n",
+       "    console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n",
+       "    root._bokeh_is_loading = css_urls.length + js_urls.length;\n",
+       "\n",
+       "    function on_load() {\n",
+       "      root._bokeh_is_loading--;\n",
+       "      if (root._bokeh_is_loading === 0) {\n",
+       "        console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n",
+       "        run_callbacks()\n",
+       "      }\n",
+       "    }\n",
+       "\n",
+       "    function on_error(url) {\n",
+       "      console.error(\"failed to load \" + url);\n",
+       "    }\n",
+       "\n",
+       "    for (let i = 0; i < css_urls.length; i++) {\n",
+       "      const url = css_urls[i];\n",
+       "      const element = document.createElement(\"link\");\n",
+       "      element.onload = on_load;\n",
+       "      element.onerror = on_error.bind(null, url);\n",
+       "      element.rel = \"stylesheet\";\n",
+       "      element.type = \"text/css\";\n",
+       "      element.href = url;\n",
+       "      console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n",
+       "      document.body.appendChild(element);\n",
+       "    }\n",
+       "\n",
+       "    for (let i = 0; i < js_urls.length; i++) {\n",
+       "      const url = js_urls[i];\n",
+       "      const element = document.createElement('script');\n",
+       "      element.onload = on_load;\n",
+       "      element.onerror = on_error.bind(null, url);\n",
+       "      element.async = false;\n",
+       "      element.src = url;\n",
+       "      console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n",
+       "      document.head.appendChild(element);\n",
+       "    }\n",
+       "  };\n",
+       "\n",
+       "  function inject_raw_css(css) {\n",
+       "    const element = document.createElement(\"style\");\n",
+       "    element.appendChild(document.createTextNode(css));\n",
+       "    document.body.appendChild(element);\n",
+       "  }\n",
+       "\n",
+       "  \n",
+       "  const js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-2.4.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.4.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-2.4.0.min.js\"];\n",
+       "  const css_urls = [];\n",
+       "  \n",
+       "\n",
+       "  const inline_js = [\n",
+       "    function(Bokeh) {\n",
+       "      Bokeh.set_log_level(\"info\");\n",
+       "    },\n",
+       "    function(Bokeh) {\n",
+       "    \n",
+       "    \n",
+       "    }\n",
+       "  ];\n",
+       "\n",
+       "  function run_inline_js() {\n",
+       "    \n",
+       "    if (root.Bokeh !== undefined || force === true) {\n",
+       "      \n",
+       "    for (let i = 0; i < inline_js.length; i++) {\n",
+       "      inline_js[i].call(root, root.Bokeh);\n",
+       "    }\n",
+       "    if (force === true) {\n",
+       "        display_loaded();\n",
+       "      }} else if (Date.now() < root._bokeh_timeout) {\n",
+       "      setTimeout(run_inline_js, 100);\n",
+       "    } else if (!root._bokeh_failed_load) {\n",
+       "      console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n",
+       "      root._bokeh_failed_load = true;\n",
+       "    } else if (force !== true) {\n",
+       "      const cell = $(document.getElementById(\"1271\")).parents('.cell').data().cell;\n",
+       "      cell.output_area.append_execute_result(NB_LOAD_WARNING)\n",
+       "    }\n",
+       "\n",
+       "  }\n",
+       "\n",
+       "  if (root._bokeh_is_loading === 0) {\n",
+       "    console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n",
+       "    run_inline_js();\n",
+       "  } else {\n",
+       "    load_libs(css_urls, js_urls, function() {\n",
+       "      console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n",
+       "      run_inline_js();\n",
+       "    });\n",
+       "  }\n",
+       "}(window));"
+      ],
+      "application/vnd.bokehjs_load.v0+json": "\n(function(root) {\n  function now() {\n    return new Date();\n  }\n\n  const force = true;\n\n  if (typeof root._bokeh_onload_callbacks === \"undefined\" || force === true) {\n    root._bokeh_onload_callbacks = [];\n    root._bokeh_is_loading = undefined;\n  }\n\n  \n\n  \n  if (typeof (root._bokeh_timeout) === \"undefined\" || force === true) {\n    root._bokeh_timeout = Date.now() + 5000;\n    root._bokeh_failed_load = false;\n  }\n\n  const NB_LOAD_WARNING = {'data': {'text/html':\n     \"<div style='background-color: #fdd'>\\n\"+\n     \"<p>\\n\"+\n     \"BokehJS does not appear to have successfully loaded. If loading BokehJS from CDN, this \\n\"+\n     \"may be due to a slow or bad network connection. Possible fixes:\\n\"+\n     \"</p>\\n\"+\n     \"<ul>\\n\"+\n     \"<li>re-rerun `output_notebook()` to attempt to load from CDN again, or</li>\\n\"+\n     \"<li>use INLINE resources instead, as so:</li>\\n\"+\n     \"</ul>\\n\"+\n     \"<code>\\n\"+\n     \"from bokeh.resources import INLINE\\n\"+\n     \"output_notebook(resources=INLINE)\\n\"+\n     \"</code>\\n\"+\n     \"</div>\"}};\n\n  function display_loaded() {\n    const el = document.getElementById(\"1271\");\n    if (el != null) {\n      el.textContent = \"BokehJS is loading...\";\n    }\n    if (root.Bokeh !== undefined) {\n      if (el != null) {\n        el.textContent = \"BokehJS \" + root.Bokeh.version + \" successfully loaded.\";\n      }\n    } else if (Date.now() < root._bokeh_timeout) {\n      setTimeout(display_loaded, 100)\n    }\n  }\n\n\n  function run_callbacks() {\n    try {\n      root._bokeh_onload_callbacks.forEach(function(callback) {\n        if (callback != null)\n          callback();\n      });\n    } finally {\n      delete root._bokeh_onload_callbacks\n    }\n    console.debug(\"Bokeh: all callbacks have finished\");\n  }\n\n  function load_libs(css_urls, js_urls, callback) {\n    if (css_urls == null) css_urls = [];\n    if (js_urls == null) js_urls = [];\n\n    root._bokeh_onload_callbacks.push(callback);\n    if (root._bokeh_is_loading > 0) {\n      console.debug(\"Bokeh: BokehJS is being loaded, scheduling callback at\", now());\n      return null;\n    }\n    if (js_urls == null || js_urls.length === 0) {\n      run_callbacks();\n      return null;\n    }\n    console.debug(\"Bokeh: BokehJS not loaded, scheduling load and callback at\", now());\n    root._bokeh_is_loading = css_urls.length + js_urls.length;\n\n    function on_load() {\n      root._bokeh_is_loading--;\n      if (root._bokeh_is_loading === 0) {\n        console.debug(\"Bokeh: all BokehJS libraries/stylesheets loaded\");\n        run_callbacks()\n      }\n    }\n\n    function on_error(url) {\n      console.error(\"failed to load \" + url);\n    }\n\n    for (let i = 0; i < css_urls.length; i++) {\n      const url = css_urls[i];\n      const element = document.createElement(\"link\");\n      element.onload = on_load;\n      element.onerror = on_error.bind(null, url);\n      element.rel = \"stylesheet\";\n      element.type = \"text/css\";\n      element.href = url;\n      console.debug(\"Bokeh: injecting link tag for BokehJS stylesheet: \", url);\n      document.body.appendChild(element);\n    }\n\n    for (let i = 0; i < js_urls.length; i++) {\n      const url = js_urls[i];\n      const element = document.createElement('script');\n      element.onload = on_load;\n      element.onerror = on_error.bind(null, url);\n      element.async = false;\n      element.src = url;\n      console.debug(\"Bokeh: injecting script tag for BokehJS library: \", url);\n      document.head.appendChild(element);\n    }\n  };\n\n  function inject_raw_css(css) {\n    const element = document.createElement(\"style\");\n    element.appendChild(document.createTextNode(css));\n    document.body.appendChild(element);\n  }\n\n  \n  const js_urls = [\"https://cdn.bokeh.org/bokeh/release/bokeh-2.4.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-gl-2.4.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-widgets-2.4.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-tables-2.4.0.min.js\", \"https://cdn.bokeh.org/bokeh/release/bokeh-mathjax-2.4.0.min.js\"];\n  const css_urls = [];\n  \n\n  const inline_js = [\n    function(Bokeh) {\n      Bokeh.set_log_level(\"info\");\n    },\n    function(Bokeh) {\n    \n    \n    }\n  ];\n\n  function run_inline_js() {\n    \n    if (root.Bokeh !== undefined || force === true) {\n      \n    for (let i = 0; i < inline_js.length; i++) {\n      inline_js[i].call(root, root.Bokeh);\n    }\n    if (force === true) {\n        display_loaded();\n      }} else if (Date.now() < root._bokeh_timeout) {\n      setTimeout(run_inline_js, 100);\n    } else if (!root._bokeh_failed_load) {\n      console.log(\"Bokeh: BokehJS failed to load within specified timeout.\");\n      root._bokeh_failed_load = true;\n    } else if (force !== true) {\n      const cell = $(document.getElementById(\"1271\")).parents('.cell').data().cell;\n      cell.output_area.append_execute_result(NB_LOAD_WARNING)\n    }\n\n  }\n\n  if (root._bokeh_is_loading === 0) {\n    console.debug(\"Bokeh: BokehJS loaded, going straight to plotting\");\n    run_inline_js();\n  } else {\n    load_libs(css_urls, js_urls, function() {\n      console.debug(\"Bokeh: BokehJS plotting callback run at\", now());\n      run_inline_js();\n    });\n  }\n}(window));"
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "name": "stderr",
+     "output_type": "stream",
+     "text": [
+      "Na: 100%|███████████████████████████████| 1/1 [00:01<00:00,  1.32s/it]\n",
+      "Cl: 100%|███████████████████████████████| 1/1 [00:01<00:00,  1.29s/it]\n"
+     ]
+    },
+    {
+     "data": {
+      "text/html": [
+       "\n",
+       "\n",
+       "\n",
+       "\n",
+       "\n",
+       "\n",
+       "  <div class=\"bk-root\" id=\"0d717961-4e9b-4709-8ee0-89045018872c\" data-root-id=\"1405\"></div>\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "application/javascript": [
+       "(function(root) {\n",
+       "  function embed_document(root) {\n",
+       "    \n",
+       "  const docs_json = {\"44958406-5566-4c3c-9293-0dd7033a0468\":{\"defs\":[],\"roots\":{\"references\":[{\"attributes\":{\"children\":[{\"id\":\"1404\"},{\"id\":\"1402\"}],\"sizing_mode\":\"scale_both\"},\"id\":\"1405\",\"type\":\"Column\"},{\"attributes\":{},\"id\":\"1347\",\"type\":\"ResetTool\"},{\"attributes\":{\"below\":[{\"id\":\"1335\"}],\"center\":[{\"id\":\"1338\"},{\"id\":\"1342\"},{\"id\":\"1374\"},{\"id\":\"1325\"}],\"left\":[{\"id\":\"1339\"}],\"renderers\":[{\"id\":\"1361\"}],\"sizing_mode\":\"scale_both\",\"title\":{\"id\":\"1363\"},\"toolbar\":{\"id\":\"1350\"},\"toolbar_location\":null,\"x_range\":{\"id\":\"1327\"},\"x_scale\":{\"id\":\"1331\"},\"y_range\":{\"id\":\"1329\"},\"y_scale\":{\"id\":\"1333\"}},\"id\":\"1326\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"1290\",\"type\":\"PanTool\"},{\"attributes\":{\"axis\":{\"id\":\"1282\"},\"coordinates\":null,\"group\":null,\"ticker\":null},\"id\":\"1285\",\"type\":\"Grid\"},{\"attributes\":{},\"id\":\"1327\",\"type\":\"DataRange1d\"},{\"attributes\":{\"coordinates\":null,\"group\":null,\"items\":[{\"id\":\"1322\"}]},\"id\":\"1321\",\"type\":\"Legend\"},{\"attributes\":{},\"id\":\"1317\",\"type\":\"AllLabels\"},{\"attributes\":{\"axis_label\":\"$$\\\\text{Time} / s$$\",\"coordinates\":null,\"formatter\":{\"id\":\"1369\"},\"group\":null,\"major_label_policy\":{\"id\":\"1370\"},\"ticker\":{\"id\":\"1336\"}},\"id\":\"1335\",\"type\":\"LinearAxis\"},{\"attributes\":{\"coordinates\":null,\"group\":null,\"items\":[{\"id\":\"1375\"}]},\"id\":\"1374\",\"type\":\"Legend\"},{\"attributes\":{},\"id\":\"1372\",\"type\":\"Selection\"},{\"attributes\":{\"toolbars\":[{\"id\":\"1297\"},{\"id\":\"1350\"}],\"tools\":[{\"id\":\"1290\"},{\"id\":\"1291\"},{\"id\":\"1292\"},{\"id\":\"1293\"},{\"id\":\"1294\"},{\"id\":\"1295\"},{\"id\":\"1323\"},{\"id\":\"1343\"},{\"id\":\"1344\"},{\"id\":\"1345\"},{\"id\":\"1346\"},{\"id\":\"1347\"},{\"id\":\"1348\"},{\"id\":\"1376\"}]},\"id\":\"1403\",\"type\":\"ProxyToolbar\"},{\"attributes\":{\"axis\":{\"id\":\"1286\"},\"coordinates\":null,\"dimension\":1,\"group\":null,\"ticker\":null},\"id\":\"1289\",\"type\":\"Grid\"},{\"attributes\":{\"coordinates\":null,\"dimension\":\"height\",\"group\":null,\"line_dash\":[6],\"location\":5.98e-13},\"id\":\"1272\",\"type\":\"Span\"},{\"attributes\":{\"line_alpha\":0.1,\"line_color\":\"#1f77b4\",\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1359\",\"type\":\"Line\"},{\"attributes\":{},\"id\":\"1287\",\"type\":\"BasicTicker\"},{\"attributes\":{\"axis_label\":\"$$\\\\text{VACF} / m^{2}/s^{2}$$\",\"coordinates\":null,\"formatter\":{\"id\":\"1313\"},\"group\":null,\"major_label_policy\":{\"id\":\"1314\"},\"ticker\":{\"id\":\"1287\"}},\"id\":\"1286\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1343\",\"type\":\"PanTool\"},{\"attributes\":{\"bottom_units\":\"screen\",\"coordinates\":null,\"fill_alpha\":0.5,\"fill_color\":\"lightgrey\",\"group\":null,\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":1.0,\"line_color\":\"black\",\"line_dash\":[4,4],\"line_width\":2,\"right_units\":\"screen\",\"syncable\":false,\"top_units\":\"screen\"},\"id\":\"1296\",\"type\":\"BoxAnnotation\"},{\"attributes\":{},\"id\":\"1369\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"bottom_units\":\"screen\",\"coordinates\":null,\"fill_alpha\":0.5,\"fill_color\":\"lightgrey\",\"group\":null,\"left_units\":\"screen\",\"level\":\"overlay\",\"line_alpha\":1.0,\"line_color\":\"black\",\"line_dash\":[4,4],\"line_width\":2,\"right_units\":\"screen\",\"syncable\":false,\"top_units\":\"screen\"},\"id\":\"1349\",\"type\":\"BoxAnnotation\"},{\"attributes\":{},\"id\":\"1348\",\"type\":\"HelpTool\"},{\"attributes\":{\"coordinates\":null,\"group\":null},\"id\":\"1310\",\"type\":\"Title\"},{\"attributes\":{},\"id\":\"1319\",\"type\":\"Selection\"},{\"attributes\":{\"below\":[{\"id\":\"1282\"}],\"center\":[{\"id\":\"1285\"},{\"id\":\"1289\"},{\"id\":\"1321\"},{\"id\":\"1272\"}],\"left\":[{\"id\":\"1286\"}],\"renderers\":[{\"id\":\"1308\"}],\"sizing_mode\":\"scale_both\",\"title\":{\"id\":\"1310\"},\"toolbar\":{\"id\":\"1297\"},\"toolbar_location\":null,\"x_range\":{\"id\":\"1274\"},\"x_scale\":{\"id\":\"1278\"},\"y_range\":{\"id\":\"1276\"},\"y_scale\":{\"id\":\"1280\"}},\"id\":\"1273\",\"subtype\":\"Figure\",\"type\":\"Plot\"},{\"attributes\":{},\"id\":\"1291\",\"type\":\"WheelZoomTool\"},{\"attributes\":{\"callback\":null},\"id\":\"1376\",\"type\":\"HoverTool\"},{\"attributes\":{\"axis\":{\"id\":\"1339\"},\"coordinates\":null,\"dimension\":1,\"group\":null,\"ticker\":null},\"id\":\"1342\",\"type\":\"Grid\"},{\"attributes\":{\"tools\":[{\"id\":\"1343\"},{\"id\":\"1344\"},{\"id\":\"1345\"},{\"id\":\"1346\"},{\"id\":\"1347\"},{\"id\":\"1348\"},{\"id\":\"1376\"}]},\"id\":\"1350\",\"type\":\"Toolbar\"},{\"attributes\":{},\"id\":\"1316\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"tools\":[{\"id\":\"1290\"},{\"id\":\"1291\"},{\"id\":\"1292\"},{\"id\":\"1293\"},{\"id\":\"1294\"},{\"id\":\"1295\"},{\"id\":\"1323\"}]},\"id\":\"1297\",\"type\":\"Toolbar\"},{\"attributes\":{\"line_color\":\"#1f77b4\",\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1305\",\"type\":\"Line\"},{\"attributes\":{},\"id\":\"1329\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"1294\",\"type\":\"ResetTool\"},{\"attributes\":{},\"id\":\"1333\",\"type\":\"LinearScale\"},{\"attributes\":{\"coordinates\":null,\"dimension\":\"height\",\"group\":null,\"line_dash\":[6],\"location\":5.98e-13},\"id\":\"1325\",\"type\":\"Span\"},{\"attributes\":{\"children\":[[{\"id\":\"1273\"},0,0],[{\"id\":\"1326\"},0,1]]},\"id\":\"1402\",\"type\":\"GridBox\"},{\"attributes\":{},\"id\":\"1340\",\"type\":\"BasicTicker\"},{\"attributes\":{},\"id\":\"1344\",\"type\":\"WheelZoomTool\"},{\"attributes\":{\"axis_label\":\"$$\\\\text{VACF} / m^{2}/s^{2}$$\",\"coordinates\":null,\"formatter\":{\"id\":\"1366\"},\"group\":null,\"major_label_policy\":{\"id\":\"1367\"},\"ticker\":{\"id\":\"1340\"}},\"id\":\"1339\",\"type\":\"LinearAxis\"},{\"attributes\":{\"line_alpha\":0.2,\"line_color\":\"#1f77b4\",\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1307\",\"type\":\"Line\"},{\"attributes\":{\"data\":{\"x\":{\"__ndarray__\":\"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\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[490]},\"y\":{\"__ndarray__\":\"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\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[490]}},\"selected\":{\"id\":\"1372\"},\"selection_policy\":{\"id\":\"1371\"}},\"id\":\"1357\",\"type\":\"ColumnDataSource\"},{\"attributes\":{},\"id\":\"1367\",\"type\":\"AllLabels\"},{\"attributes\":{\"line_color\":\"#1f77b4\",\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1358\",\"type\":\"Line\"},{\"attributes\":{\"overlay\":{\"id\":\"1349\"}},\"id\":\"1345\",\"type\":\"BoxZoomTool\"},{\"attributes\":{},\"id\":\"1274\",\"type\":\"DataRange1d\"},{\"attributes\":{\"source\":{\"id\":\"1304\"}},\"id\":\"1309\",\"type\":\"CDSView\"},{\"attributes\":{},\"id\":\"1293\",\"type\":\"SaveTool\"},{\"attributes\":{},\"id\":\"1366\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{},\"id\":\"1371\",\"type\":\"UnionRenderers\"},{\"attributes\":{},\"id\":\"1278\",\"type\":\"LinearScale\"},{\"attributes\":{},\"id\":\"1314\",\"type\":\"AllLabels\"},{\"attributes\":{},\"id\":\"1283\",\"type\":\"BasicTicker\"},{\"attributes\":{\"axis\":{\"id\":\"1335\"},\"coordinates\":null,\"group\":null,\"ticker\":null},\"id\":\"1338\",\"type\":\"Grid\"},{\"attributes\":{\"toolbar\":{\"id\":\"1403\"},\"toolbar_location\":\"above\"},\"id\":\"1404\",\"type\":\"ToolbarBox\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1304\"},\"glyph\":{\"id\":\"1305\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1307\"},\"nonselection_glyph\":{\"id\":\"1306\"},\"view\":{\"id\":\"1309\"}},\"id\":\"1308\",\"type\":\"GlyphRenderer\"},{\"attributes\":{},\"id\":\"1276\",\"type\":\"DataRange1d\"},{\"attributes\":{},\"id\":\"1336\",\"type\":\"BasicTicker\"},{\"attributes\":{},\"id\":\"1280\",\"type\":\"LinearScale\"},{\"attributes\":{\"line_alpha\":0.2,\"line_color\":\"#1f77b4\",\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1360\",\"type\":\"Line\"},{\"attributes\":{\"callback\":null},\"id\":\"1323\",\"type\":\"HoverTool\"},{\"attributes\":{\"coordinates\":null,\"data_source\":{\"id\":\"1357\"},\"glyph\":{\"id\":\"1358\"},\"group\":null,\"hover_glyph\":null,\"muted_glyph\":{\"id\":\"1360\"},\"nonselection_glyph\":{\"id\":\"1359\"},\"view\":{\"id\":\"1362\"}},\"id\":\"1361\",\"type\":\"GlyphRenderer\"},{\"attributes\":{\"source\":{\"id\":\"1357\"}},\"id\":\"1362\",\"type\":\"CDSView\"},{\"attributes\":{},\"id\":\"1295\",\"type\":\"HelpTool\"},{\"attributes\":{},\"id\":\"1313\",\"type\":\"BasicTickFormatter\"},{\"attributes\":{\"coordinates\":null,\"group\":null},\"id\":\"1363\",\"type\":\"Title\"},{\"attributes\":{},\"id\":\"1346\",\"type\":\"SaveTool\"},{\"attributes\":{},\"id\":\"1370\",\"type\":\"AllLabels\"},{\"attributes\":{\"label\":{\"value\":\"Na:  5.842E-08 +-  2.848E-10\"},\"renderers\":[{\"id\":\"1308\"}]},\"id\":\"1322\",\"type\":\"LegendItem\"},{\"attributes\":{\"data\":{\"x\":{\"__ndarray__\":\"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\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[490]},\"y\":{\"__ndarray__\":\"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\",\"dtype\":\"float64\",\"order\":\"little\",\"shape\":[490]}},\"selected\":{\"id\":\"1319\"},\"selection_policy\":{\"id\":\"1318\"}},\"id\":\"1304\",\"type\":\"ColumnDataSource\"},{\"attributes\":{\"line_alpha\":0.1,\"line_color\":\"#1f77b4\",\"x\":{\"field\":\"x\"},\"y\":{\"field\":\"y\"}},\"id\":\"1306\",\"type\":\"Line\"},{\"attributes\":{\"axis_label\":\"$$\\\\text{Time} / s$$\",\"coordinates\":null,\"formatter\":{\"id\":\"1316\"},\"group\":null,\"major_label_policy\":{\"id\":\"1317\"},\"ticker\":{\"id\":\"1283\"}},\"id\":\"1282\",\"type\":\"LinearAxis\"},{\"attributes\":{},\"id\":\"1331\",\"type\":\"LinearScale\"},{\"attributes\":{\"label\":{\"value\":\"Cl:  4.864E-08 +-  2.091E-09\"},\"renderers\":[{\"id\":\"1361\"}]},\"id\":\"1375\",\"type\":\"LegendItem\"},{\"attributes\":{},\"id\":\"1318\",\"type\":\"UnionRenderers\"},{\"attributes\":{\"overlay\":{\"id\":\"1296\"}},\"id\":\"1292\",\"type\":\"BoxZoomTool\"}],\"root_ids\":[\"1405\"]},\"title\":\"Bokeh Application\",\"version\":\"2.4.0\"}};\n",
+       "  const render_items = [{\"docid\":\"44958406-5566-4c3c-9293-0dd7033a0468\",\"root_ids\":[\"1405\"],\"roots\":{\"1405\":\"0d717961-4e9b-4709-8ee0-89045018872c\"}}];\n",
+       "  root.Bokeh.embed.embed_items_notebook(docs_json, render_items);\n",
+       "\n",
+       "  }\n",
+       "  if (root.Bokeh !== undefined) {\n",
+       "    embed_document(root);\n",
+       "  } else {\n",
+       "    let attempts = 0;\n",
+       "    const timer = setInterval(function(root) {\n",
+       "      if (root.Bokeh !== undefined) {\n",
+       "        clearInterval(timer);\n",
+       "        embed_document(root);\n",
+       "      } else {\n",
+       "        attempts++;\n",
+       "        if (attempts > 100) {\n",
+       "          clearInterval(timer);\n",
+       "          console.log(\"Bokeh: ERROR: Unable to run BokehJS code because BokehJS library is missing\");\n",
+       "        }\n",
+       "      }\n",
+       "    }, 10, root)\n",
+       "  }\n",
+       "})(window);"
+      ],
+      "application/vnd.bokehjs_exec.v0+json": ""
+     },
+     "metadata": {
+      "application/vnd.bokehjs_exec.v0+json": {
+       "id": "1405"
+      }
+     },
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "{'NaCl_GK': None}"
+      ]
+     },
+     "execution_count": 8,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "project.run.GreenKuboDiffusionCoefficients(\n",
+    "    data_range=490, plot=True, correlation_time=1, integration_range=300\n",
+    ")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "temp_dir.cleanup()"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.8.10"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/examples/scripts/green_kubo_diffusion.py b/examples/scripts/green_kubo_diffusion.py
new file mode 100644
index 00000000..1aca1604
--- /dev/null
+++ b/examples/scripts/green_kubo_diffusion.py
@@ -0,0 +1,76 @@
+"""
+A script to compute the Green-Kubo diffusion coefficients.
+
+Examples
+--------
+>>> python3 green_kubo_diffusion.py
+"""
+import mdsuite as mds
+import urllib.request
+import shutil
+import gzip
+import os
+import tempfile
+
+
+def load_data():
+    """
+    Load simulation data from the server.
+
+    Returns
+    -------
+    Will store simulation data locally for the example.
+    """
+    base_url = 'https://github.com/zincware/ExampleData/raw/main/NaCl_gk_i_q.lammpstraj'
+    filename, headers = urllib.request.urlretrieve(f'{base_url}.gz',
+                                                   filename='NaCl_gk_i_q.lammpstraj.gz')
+    with gzip.open(filename, 'rb') as f_in:
+        with open('NaCl_gk_i_q.lammpstraj', 'wb') as f_out:
+            shutil.copyfileobj(f_in, f_out)
+
+
+def run_example():
+    """
+    Run the GK example.
+
+    Returns
+    -------
+
+    """
+    project = mds.Project("GK_Diffusion_Example")
+    project.add_experiment(
+        experiment='NaCl_GK',
+        timestep=0.002,
+        temperature=1400.0,
+        units='metal',
+        data='NaCl_gk_i_q.lammpstraj'
+    )
+    project.run.GreenKuboDiffusionCoefficients(
+        data_range=100, plot=True, correlation_time=10
+    )
+    project.run.EinsteinDiffusionCoefficients(
+        data_range=100, correlation_time=10, plot=True
+    )
+    project.run.EinsteinHelfandIonicConductivity(
+        data_range=100, correlation_time=10, plot=True
+    )
+
+    project.run.RadialDistributionFunction(
+        number_of_configurations=100, start=0, stop=101, plot=True
+    )
+    project.run.CoordinationNumbers(plot=True)
+    project.run.PotentialOfMeanForce(plot=True)
+    #project.experiments.NaCl_GK.run_visualization()
+    print("Tutorial complete....... Files being deleted now.")
+
+
+if __name__ == '__main__':
+    """
+    Run the example.
+    """
+    temp_dir = tempfile.TemporaryDirectory()
+    os.chdir(temp_dir.name)
+    load_data()  # load the data.
+    run_example()  # run the example.
+    os.chdir('..')
+    temp_dir.cleanup()
diff --git a/examples/scripts/spatial_distribution_function.py b/examples/scripts/spatial_distribution_function.py
new file mode 100644
index 00000000..bd59b220
--- /dev/null
+++ b/examples/scripts/spatial_distribution_function.py
@@ -0,0 +1,33 @@
+import mdsuite as mds
+from mdsuite.transformations.map_molecules import MolecularMap
+
+
+if __name__ == '__main__':
+
+    filename = 'dump.lammpstrj'
+    project = mds.Project(name='sdf_test')
+    project.add_experiment(
+        experiment='bmim_bf4',
+        timestep=0.1,
+        temperature=300.0,
+        units='real',
+        cluster_mode=False
+    )
+
+    # #project.experiments.bmim_bf4.perform_transformation("UnwrapCoordinates")
+    # mapper = MolecularMap(
+    #     project.experiments.bmim_bf4,
+    #     molecules={
+    #         'bmim-7': {'smiles': 'CCCCN1C=C[N+](+C1)C', 'cutoff': 1.9},
+    #         'bf4-7': {'smiles': '[B-](F)(F)(F)F', 'cutoff': 2.4}
+    #               }
+    # )
+    # mapper.run_transformation()
+    project.run_computation.SpatialDistributionFunction(species=['bmim-7', 'bf4-7'],
+                                                        r_min=1.0,
+                                                        r_max=3.0,
+                                                        number_of_configurations=5,
+                                                        start=0,
+                                                        stop=40)
+
+    project.experiments.bmim_bf4.run_visualization()
diff --git a/mdsuite/__init__.py b/mdsuite/__init__.py
index 8d72e3fa..d988566c 100644
--- a/mdsuite/__init__.py
+++ b/mdsuite/__init__.py
@@ -10,20 +10,19 @@
 Python __init__ file
 """
 import logging
-import os
 import sys
 from .project import Project
 from .experiment import Experiment
 from .graph_modules import adjacency_matrix
 from .utils.report_computer_characteristics import Report
+from .utils import config
 
-__all__ = ['Project', 'Experiment', 'adjacency_matrix', 'Report']
+__all__ = ['Project', 'Experiment', 'adjacency_matrix', 'Report', 'config']
 
 logger = logging.getLogger("mdsuite")
 logger.setLevel(logging.INFO)
 
 # Formatter for advanced logging
-# formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
 formatter = logging.Formatter('%(asctime)s - %(levelname)s: %(message)s')
 
 channel = logging.StreamHandler(sys.stdout)
diff --git a/mdsuite/calculators/__init__.py b/mdsuite/calculators/__init__.py
index de065f54..b5748eb5 100644
--- a/mdsuite/calculators/__init__.py
+++ b/mdsuite/calculators/__init__.py
@@ -22,13 +22,14 @@
 from .green_kubo_viscosity_flux import GreenKuboViscosityFlux
 from .green_kubo_distinct_diffusion_coefficients import GreenKuboDistinctDiffusionCoefficients
 from .green_kubo_ionic_conductivity import GreenKuboIonicConductivity
-from .green_kubo_self_diffusion_coefficients import GreenKuboSelfDiffusionCoefficients
+from .green_kubo_self_diffusion_coefficients import GreenKuboDiffusionCoefficients
 from .green_kubo_thermal_conductivity import GreenKuboThermalConductivity
 from .green_kubo_viscosity import GreenKuboViscosity
 from .kirkwood_buff_integrals import KirkwoodBuffIntegral
 from .nernst_einstein_ionic_conductivity import NernstEinsteinIonicConductivity
 from .potential_of_mean_force import PotentialOfMeanForce
 from .radial_distribution_function import RadialDistributionFunction
+from .spatial_distribution_function import SpatialDistributionFunction
 from .structure_factor import StructureFactor
 
 from typing import TYPE_CHECKING, List
@@ -39,9 +40,11 @@
 __all__ = ['Calculator', 'AngularDistributionFunction', 'CoordinationNumbers', 'EinsteinDiffusionCoefficients',
            'EinsteinDistinctDiffusionCoefficients', 'EinsteinHelfandIonicConductivity',
            'EinsteinHelfandThermalConductivity', 'EinsteinHelfandThermalKinaci', 'GreenKuboViscosityFlux',
-           'GreenKuboDistinctDiffusionCoefficients', 'GreenKuboIonicConductivity', 'GreenKuboSelfDiffusionCoefficients',
+           'GreenKuboDistinctDiffusionCoefficients', 'GreenKuboIonicConductivity',
+           'GreenKuboDiffusionCoefficients',
            'GreenKuboThermalConductivity', 'GreenKuboViscosity', 'KirkwoodBuffIntegral',
-           'NernstEinsteinIonicConductivity', 'PotentialOfMeanForce', 'RadialDistributionFunction', 'StructureFactor']
+           'NernstEinsteinIonicConductivity', 'PotentialOfMeanForce', 'RadialDistributionFunction', 'StructureFactor',
+           'SpatialDistributionFunction']
 
 
 class RunComputation:
@@ -110,9 +113,9 @@ def GreenKuboIonicConductivity(self):
         return GreenKuboIonicConductivity(**self.kwargs)
 
     @property
-    def GreenKuboSelfDiffusionCoefficients(self):
+    def GreenKuboDiffusionCoefficients(self):
         """Calculator Property"""
-        return GreenKuboSelfDiffusionCoefficients(**self.kwargs)
+        return GreenKuboDiffusionCoefficients(**self.kwargs)
 
     @property
     def GreenKuboThermalConductivity(self):
@@ -153,3 +156,8 @@ def StructureFactor(self):
     def EinsteinHelfandThermalConductivity(self):
         """Calculator Property"""
         return EinsteinHelfandThermalConductivity(**self.kwargs)
+
+    @property
+    def SpatialDistributionFunction(self):
+        """Calculator Property"""
+        return SpatialDistributionFunction(**self.kwargs)
diff --git a/mdsuite/calculators/angular_distribution_function.py b/mdsuite/calculators/angular_distribution_function.py
index 0aa6f241..48a654c5 100644
--- a/mdsuite/calculators/angular_distribution_function.py
+++ b/mdsuite/calculators/angular_distribution_function.py
@@ -16,7 +16,7 @@
 from mdsuite.calculators.calculator import Calculator, call
 from mdsuite.utils.neighbour_list import get_neighbour_list, get_triu_indicies, get_triplets
 from mdsuite.utils.linalg import get_angles
-import matplotlib.pyplot as plt
+from mdsuite.database.calculator_database import Parameters
 from mdsuite.utils.meta_functions import join_path
 
 log = logging.getLogger(__name__)
@@ -95,8 +95,8 @@ def __init__(self, **kwargs):
 
         self.analysis_name = "Angular_Distribution_Function"
         self.database_group = "Angular_Distribution_Function"
-        self.x_label = r'Angle ($\theta$)'
-        self.y_label = 'ADF /a.u.'
+        self.x_label = r'$$\text{Angle} / \theta $$'
+        self.y_label = r'$$\text{ADF} / a.u.$$'
 
     @call
     def __call__(self,
@@ -109,7 +109,6 @@ def __call__(self,
                  bins: int = 500,
                  species: list = None,
                  use_tf_function: bool = False,
-                 export: bool = False,
                  molecules: bool = False,
                  gpu: bool = False,
                  plot: bool = True,
@@ -143,8 +142,6 @@ def __call__(self,
         norm_power: int
             The power of the normalization factor applied to the ADF histogram.
             If set to zero no distance normalization will be applied.
-        export : bool
-                If true, generate a csv file after the analysis is complete.
         molecules : bool
                 if true, perform the anlaysis on molecules.
         gpu : bool
@@ -160,7 +157,7 @@ def __call__(self,
 
         """
         # Parse the parent class arguments.
-        self.update_user_args(data_range=1, export=export, gpu=gpu, plot=plot)
+        self.update_user_args(data_range=1, gpu=gpu, plot=plot)
 
         # Parse the user arguments.
         self.use_tf_function = use_tf_function
@@ -431,22 +428,22 @@ def _compute_adfs(self, angles, species_indices):
 
             self.data_range = self.n_confs
             log.debug(f"species are {species}")
-            if self.save or self.export:
-                properties = {"Property": self.database_group,
-                              "Analysis": self.analysis_name,
-                              "Subject": [_species[0] for _species in species],
-                              "data_range": self.data_range,
-                              'data': [{'x': x, 'y': y} for x, y in zip(bin_range_to_angles, hist)],
-                              }
-                self._update_properties_file(properties, delete_duplicate=False)
-                log.warning("Delete duplicates is not supported for calculators "
-                            "that involve more then 3 species!")
+
+            properties = Parameters(
+                Property=self.database_group,
+                Analysis=self.analysis_name,
+                data_range=self.data_range,
+                data=[{'angle': x, 'adf': y} for x, y in zip(bin_range_to_angles, hist)],
+                Subject=[_species[0] for _species in species]
+            )
+            self.update_database(properties)
 
             if self.plot:
-                fig, ax = plt.subplots()
-                ax.plot(bin_range_to_angles, hist, label=name)
-                ax.set_title(f"{name} - Max: {bin_range_to_angles[tf.math.argmax(hist)]:.3f}° ")
-                self._plot_fig(fig, ax, title=name)
+                self.run_visualization(
+                    x_data=bin_range_to_angles,
+                    y_data=hist,
+                    title=f"{name} - Max: {bin_range_to_angles[tf.math.argmax(hist)]:.3f} degrees ",
+                )
 
     def run_experimental_analysis(self):
         """
diff --git a/mdsuite/calculators/calculator.py b/mdsuite/calculators/calculator.py
index bf1f4763..2b2c660e 100644
--- a/mdsuite/calculators/calculator.py
+++ b/mdsuite/calculators/calculator.py
@@ -16,17 +16,13 @@
 
 import logging
 import numpy as np
-import os
-import abc
 import random
-import sys
-import matplotlib.figure
 import matplotlib.pyplot as plt
-from matplotlib.axes._subplots import Axes
 from pathlib import Path
 import tensorflow as tf
 import pandas as pd
 from scipy.optimize import curve_fit
+from mdsuite.visualizer.d2_data_visualization import DataVisualizer2D
 from mdsuite.utils.exceptions import RangeExceeded
 from mdsuite.utils.meta_functions import join_path
 from mdsuite.memory_management.memory_manager import MemoryManager
@@ -128,11 +124,14 @@ class Calculator(CalculatorDatabase):
     n_batches : dict
             Number of barthes to use as a dictionary for both serial and
             parallel implementations
+    plot_array : list
+            A list of plot objects to be show together at the end of the
+            species loop.
     """
 
     def __init__(self, experiment: Experiment = None, experiments: List[Experiment] = None, plot: bool = True,
                  save: bool = True, data_range: int = 500,
-                 correlation_time: int = 1, atom_selection: object = np.s_[:], export: bool = True, gpu: bool = False,
+                 correlation_time: int = 1, atom_selection: object = np.s_[:], gpu: bool = False,
                  load_data: bool = False):
         """
         Constructor for the calculator class.
@@ -153,8 +152,6 @@ def __init__(self, experiment: Experiment = None, experiments: List[Experiment]
                 Correlation time to use in the analysis.
         atom_selection : np.s_
                 Atoms to perform the analysis on.
-        export : bool
-                If true, analysis results are exported to a csv file.
         gpu : bool
                 If true, reduce memory usage to what is allowed on the system
                 GPU.
@@ -171,7 +168,6 @@ def __init__(self, experiment: Experiment = None, experiments: List[Experiment]
         self.data_range = data_range
         self.plot = plot
         self.save = save
-        self.export = export
         self.atom_selection = atom_selection
         self.correlation_time = correlation_time
         self.database = None
@@ -211,6 +207,7 @@ def __init__(self, experiment: Experiment = None, experiments: List[Experiment]
         self.x_label: str
         self.y_label: str
         self.analysis_name: str
+        self.plot_array = []
 
         self.database_group = None
         self.analysis_name = None
@@ -225,7 +222,6 @@ def update_user_args(self,
                          correlation_time: int = 1,
                          atom_selection: object = np.s_[:],
                          tau_values: Union[int, List, Any] = np.s_[:],
-                         export: bool = True,
                          gpu: bool = False,
                          *args,
                          **kwargs):
@@ -245,8 +241,6 @@ def update_user_args(self,
                 Correlation time to use in the analysis.
         atom_selection : object
                 Atoms to perform the analysis on.
-        export : bool
-                If true, analysis results are exported to a csv file.
         gpu : bool
                 If true, reduce memory usage to what is allowed on the system
                 GPU.
@@ -263,7 +257,6 @@ def update_user_args(self,
         self.data_range = data_range
         self.plot = plot
         self.save = save
-        self.export = export
         self.gpu = gpu
         self.tau_values = tau_values
         self.correlation_time = correlation_time
@@ -272,6 +265,8 @@ def update_user_args(self,
         # attributes based on user args
         self.time = self._handle_tau_values()  # process selected tau values.
 
+        self.plotter = DataVisualizer2D(title=self.analysis_name)
+
     def _calculate_prefactor(self, species: Union[str, tuple] = None):
         """
         calculate the calculator pre-factor.
@@ -439,26 +434,6 @@ def _build_pandas_dataframe(x: np.array, y: np.array) -> pd.DataFrame:
 
         return pd.DataFrame({'x': x, 'y': y})
 
-    @staticmethod
-    def _export_data(name: str, data: pd.DataFrame):
-        """
-        Export data from the analysis database.
-
-        Parameters
-        ----------
-        name : str
-                name of the tensor_values to save. Usually this is just the
-                analysis name. In the case of species specific analysis,
-                this will be further appended to include the name of the
-                species.
-        data : pd.DataFrame
-                Data to be saved.
-        Returns
-        -------
-        Saves a csv file to disc.
-        """
-        data.to_csv(name)
-
     def _msd_operation(self, ensemble: tf.Tensor, square: bool = True):
         """
         Perform a simple msd operation.
@@ -574,82 +549,26 @@ def _build_table_name(self, species: str = "System"):
         """
         return f"{self.database_group}_{self.analysis_name}_{self.data_range}_{species}"
 
-    def _save_data(self, name: str, data: pd.DataFrame):
+    def run_visualization(
+            self, x_data, y_data, title: str, layouts: object = None
+    ):
         """
-        Save tensor_values to the save tensor_values directory
+        Run a visualization session on the data.
 
         Parameters
         ----------
-        name : str
-                name of the tensor_values to save. Usually this is just the
-                analysis name. In the case of species specific analysis,
-                this will be further appended to include the name of the
-                species.
-        data : pd.DataFrame
-                Data to be saved.
-        """
-        # database = AnalysisDatabase(
-        #     name=os.path.join(self.experiment.database_path,
-        #                       "analysis_database"))
-        # database.add_data(name=name, data_frame=data)
+        x_data
+        y_data
+        title
+        span
 
-    def _plot_fig(self,
-                  fig: matplotlib.figure.Figure,
-                  ax: Axes,
-                  title: str = None,
-                  dpi: int = 600,
-                  filetype: str = 'svg'):
-        """
-        Class based plotting using fig, ax = plt.subplots
-
-        Parameters
-        ----------
-        fig: matplotlib figure
-        ax: matplotlib subplot axes
-            currently only a single axes is supported. Subplots aren't yet!
-        title: str
-            Name of the plot
-        dpi: int
-            matplotlib dpi resolution
-        filetype: str
-            matplotlib filetype / format
-        """
-
-        if title is None:
-            title = f"{self.analysis_name}"
-
-        ax.set_xlabel(rf'{self.x_label}')
-        ax.set_ylabel(rf'{self.y_label}')
-        ax.legend()
-        fig.set_facecolor("w")
-        fig.show()
-
-        fig.savefig(os.path.join(self.experiment.figures_path, f"{title}.svg"),
-                    dpi=dpi, format=filetype)
+        Returns
+        -------
 
-    def _plot_data(self, title: str = None, manual: bool = False,
-                   dpi: int = 600):
-        """
-        Plot the tensor_values generated during the analysis
         """
-
-        if title is None:
-            title = f"{self.analysis_name}"
-
-        if manual:
-            plt.savefig(
-                os.path.join(self.experiment.figures_path,
-                             f"{title}.svg"),
-                dpi=dpi,
-                format='svg')
-        else:
-            plt.xlabel(rf'{self.x_label}')  # set the x label
-            plt.ylabel(rf'{self.y_label}')  # set the y label
-            plt.legend()  # enable the legend
-            plt.savefig(
-                os.path.join(self.experiment.figures_path,
-                             f"{title}.svg"),
-                dpi=dpi, format='svg')
+        self.plot_array.append(self.plotter.construct_plot(
+            x_data=x_data, y_data=y_data, title=title, x_label=self.x_label, y_label=self.y_label, layouts=layouts
+        ))
 
     def _check_input(self):
         """
@@ -739,13 +658,6 @@ def _update_properties_file(self, parameters: dict,
         """
         log.warning("Using depreciated method `_update_properties_file` \t Please use `update_database` instead.")
         self.update_database(parameters=parameters, delete_duplicate=delete_duplicate)
-        #
-        #
-        #
-        # database = PropertiesDatabase(
-        #     name=os.path.join(self.experiment.database_path,
-        #                       'property_database'))
-        # database.add_data(parameters, delete_duplicate)
 
     def _resolve_dependencies(self, dependency):
         """
@@ -862,18 +774,12 @@ def perform_computation(self):
             self._apply_averaging_factor()
             self._post_operation_processes()
 
-            if self.plot:
-                plt.legend()
-                plt.show()
-
         elif self.experimental:
             data_path = [join_path(species,
                                    self.loaded_property) for species
                          in self.experiment.species]
             self._prepare_managers(data_path)
-            output = self.run_experimental_analysis()
-
-            return output
+            self.run_experimental_analysis()
 
         elif self.post_generation:
             self.run_post_generation_analysis()
@@ -914,9 +820,8 @@ def perform_computation(self):
                 self._apply_averaging_factor()
                 self._post_operation_processes(species)
 
-            if self.plot:
-                plt.legend()
-                plt.show()
+        if self.plot:
+            self.plotter.grid_show(self.plot_array)
 
     def run_experimental_analysis(self):
         """
diff --git a/mdsuite/calculators/computations_dict.py b/mdsuite/calculators/computations_dict.py
deleted file mode 100644
index 3be62fb1..00000000
--- a/mdsuite/calculators/computations_dict.py
+++ /dev/null
@@ -1,58 +0,0 @@
-"""
-This program and the accompanying materials are made available under the terms of the
-Eclipse Public License v2.0 which accompanies this distribution, and is available at
-https://www.eclipse.org/legal/epl-v20.html
-
-SPDX-License-Identifier: EPL-2.0
-
-Copyright Contributors to the MDSuite Project.
-
-Module to contain the structured dict for analysis.
-
-Summary
--------
-"""
-from mdsuite.calculators.coordination_number_calculation import CoordinationNumbers
-from mdsuite.calculators.angular_distribution_function import AngularDistributionFunction
-from mdsuite.calculators.einstein_diffusion_coefficients import EinsteinDiffusionCoefficients
-from mdsuite.calculators.einstein_helfand_ionic_conductivity import EinsteinHelfandIonicConductivity
-from mdsuite.calculators.einstein_helfand_thermal_conductivity import EinsteinHelfandThermalConductivity
-from mdsuite.calculators.einstein_helfand_thermal_kinaci import EinsteinHelfandThermalKinaci
-from mdsuite.calculators.green_kubo_viscosity_flux import GreenKuboViscosityFlux
-from mdsuite.calculators.green_kubo_ionic_conductivity import GreenKuboIonicConductivity
-from mdsuite.calculators.green_kubo_self_diffusion_coefficients import GreenKuboSelfDiffusionCoefficients
-from mdsuite.calculators.green_kubo_thermal_conductivity import GreenKuboThermalConductivity
-from mdsuite.calculators.green_kubo_viscosity import GreenKuboViscosity
-from mdsuite.calculators.kirkwood_buff_integrals import KirkwoodBuffIntegral
-from mdsuite.calculators.nernst_einstein_ionic_conductivity import NernstEinsteinIonicConductivity
-from mdsuite.calculators.potential_of_mean_force import PotentialOfMeanForce
-from mdsuite.calculators.radial_distribution_function import RadialDistributionFunction
-from mdsuite.calculators.structure_factor import StructureFactor
-from mdsuite.calculators.green_kubo_distinct_diffusion_coefficients import GreenKuboDistinctDiffusionCoefficients
-from mdsuite.calculators.einstein_distinct_diffusion_coefficients import EinsteinDistinctDiffusionCoefficients
-
-dict_classes_db = {
-    'self_diffusion_coefficients': {'Einstein_Self_Diffusion_Coefficients': {},
-                                    'Green_Kubo_Self_Diffusion': {}},
-    'distinct_diffusion_coefficients': {'Einstein_Distinct_Diffusion_Coefficients': {},
-                                        'Green_Kubo_Distinct_Diffusion_Coefficients': {}},
-    'ionic_conductivity': {},
-    'thermal_conductivity': {},
-    'coordination_numbers': {'Coordination_Numbers': {}},
-    'potential_of_mean_force_values': {'Potential_of_Mean_Force': {}},
-    'radial_distribution_function': {},
-    'kirkwood_buff_integral': {},
-    'structure_factor': {},
-    'viscosity': {}
-}
-
-calculators = [EinsteinDiffusionCoefficients, EinsteinDistinctDiffusionCoefficients, GreenKuboSelfDiffusionCoefficients,
-               GreenKuboDistinctDiffusionCoefficients, GreenKuboIonicConductivity, EinsteinHelfandIonicConductivity,
-               RadialDistributionFunction, CoordinationNumbers, PotentialOfMeanForce, KirkwoodBuffIntegral,
-               GreenKuboThermalConductivity, StructureFactor, EinsteinHelfandThermalConductivity,
-               GreenKuboViscosityFlux,
-               GreenKuboViscosity, AngularDistributionFunction, NernstEinsteinIonicConductivity,
-               EinsteinHelfandThermalKinaci]
-
-dict_classes_computations = {calc.__name__: calc for calc in calculators}
-# TODO depreciate the dict_classes_computations and replace inside the modules with the respective .__name__ !
diff --git a/mdsuite/calculators/coordination_number_calculation.py b/mdsuite/calculators/coordination_number_calculation.py
index 8c253780..1bd37e0c 100644
--- a/mdsuite/calculators/coordination_number_calculation.py
+++ b/mdsuite/calculators/coordination_number_calculation.py
@@ -14,13 +14,13 @@
 """
 import logging
 import numpy as np
-import matplotlib.pyplot as plt
 from scipy.signal import find_peaks
-from typing import Union
+from mdsuite.database.calculator_database import Parameters
 from mdsuite.utils.exceptions import NotApplicableToAnalysis, CannotPerformThisAnalysis
 from mdsuite.calculators.calculator import Calculator, call
 from mdsuite.utils.meta_functions import golden_section_search
 from mdsuite.utils.meta_functions import apply_savgol_filter
+from bokeh.models import BoxAnnotation
 
 log = logging.getLogger(__name__)
 
@@ -88,7 +88,7 @@ def __init__(self, **kwargs):
         self.post_generation = True
 
         self.database_group = 'Coordination_Numbers'
-        self.x_label = r'r ($\AA$)'
+        self.x_label = r'$$\text{r}$$'
         self.y_label = 'CN'
         self.analysis_name = 'Coordination_Numbers'
 
@@ -219,54 +219,15 @@ def _get_coordination_numbers(self):
         second_shell_error = np.std([self.integral_data[self.indices[1][0]],
                                      self.integral_data[self.indices[1][1]]]) / np.sqrt(2)
 
-        # Mean values
-        self._update_properties_file({
-            "Property": self.database_group,
-            "Analysis": self.analysis_name,
-            "subjects": self.species_tuple.split("_"),
-            "data_range": self.data_range,
-            "data": [{"x": idx, "y": shell, "uncertainty": uncertainty} for idx, shell, uncertainty in
-                     [[1, first_shell, first_shell_error], [2, second_shell, second_shell_error]]]
-        })
-
-        # actual data
-        data = [{"x": x, "y": y} for x, y in zip(self.radii[1:], self.integral_data)]
-        self._update_properties_file({
-            "Property": self.database_group,
-            "Analysis": self.analysis_name,
-            "subjects": self.species_tuple.split("_"),
-            "data_range": self.data_range,
-            "data": data,
-            "information": "series"
-        })
 
         return first_shell, first_shell_error
 
-    def _plot_coordination_shells(self, data: tuple):
-        """
-        Plot the calculated coordination numbers on top of the rdfs
-        """
-
-        fig, ax1 = plt.subplots()  # define the plot
-        ax1.plot(self.radii, self.rdf, label=fr"{self.species_tuple}: {data[0]:.3f} $\pm$ {data[1]:.3f} ")
-        ax1.set_ylabel('RDF')  # set the y_axis label on the LHS
-        ax2 = ax1.twinx()  # split the axis
-        ax2.set_ylabel('CN')  # set the RHS y axis label
-        # plot the CN as a continuous function
-        ax2.plot(self.radii[1:], np.array(self.integral_data), 'r')  # , markersize=1, label=f"{self.species_tuple} CN")
-        # Plot the first and second shell values as a small window.
-        ax1.axvspan(self.radii[self.indices[0][0]] - 0.01, self.radii[self.indices[0][1]] + 0.01, color='g')
-        ax1.axvspan(self.radii[self.indices[1][0]] - 0.01, self.radii[self.indices[1][1]] + 0.01, color='b')
-        ax1.set_xlabel(r'r ($\AA$)')  # set the x-axis label
-        ax1.legend()
-        plt.show()
-
     def run_post_generation_analysis(self):
         """
         Calculate the coordination numbers and perform error analysis
         """
-
-        for data in self._get_rdf_data():  # Loop over all existing RDFs
+        calculations = self._get_rdf_data()
+        for data in calculations:  # Loop over all existing RDFs
             log.debug(f"Computing coordination numbers for {data.subjects}")
             self.data_range = data.data_range
             self._load_rdf_from_file(data)  # load the tensor_values from it
@@ -277,14 +238,36 @@ def run_post_generation_analysis(self):
             self._integrate_rdf(density)  # integrate the rdf
             self._find_minimums()  # get the minimums of the rdf being studied
             _data = self._get_coordination_numbers()  # calculate the coordination numbers and update the experiment
+
+            properties = Parameters(
+                Property=self.database_group,
+                Analysis=self.analysis_name,
+                data_range=self.data_range,
+                data=[{'coordination_number': _data[0],
+                       'uncertainty': _data[1]}],
+                Subject=[self.species_tuple]
+            )
+            data = properties.data
+            data += [{'r': x, 'cn': y} for x, y in
+                     zip(self.radii[1:], self.integral_data)]
+            properties.data = data
+            self.update_database(properties)
+
             # Plot the tensor_values if required
             if self.plot:
-                self._plot_coordination_shells(_data)
-
-            # TODO what to save?
-            if self.save:
-                self._save_data(name=self._build_table_name(self.species_tuple),
-                                data=self._build_pandas_dataframe(self.radii[1:], self.integral_data))
-            if self.export:
-                self._export_data(name=self._build_table_name(self.species_tuple),
-                                  data=self._build_pandas_dataframe(self.radii[1:], self.integral_data))
+                model_1 = BoxAnnotation(
+                    left=self.radii[self.indices[0][0]],
+                    right=self.radii[self.indices[0][1]],
+                    fill_alpha=0.1,
+                    fill_color='red')
+                model_2 = BoxAnnotation(
+                    left=self.radii[self.indices[1][0]],
+                    right=self.radii[self.indices[1][1]],
+                    fill_alpha=0.1,
+                    fill_color='red')
+                self.run_visualization(
+                    x_data=self.radii[1:],
+                    y_data=np.array(self.integral_data),
+                    title=fr'{self.species_tuple}: {_data[0]: 0.3E} +- {_data[1]: 0.3E}',
+                    layouts=[model_1, model_2]
+                )
diff --git a/mdsuite/calculators/einstein_diffusion_coefficients.py b/mdsuite/calculators/einstein_diffusion_coefficients.py
index 5f06bea3..13ace76c 100644
--- a/mdsuite/calculators/einstein_diffusion_coefficients.py
+++ b/mdsuite/calculators/einstein_diffusion_coefficients.py
@@ -18,7 +18,6 @@
 """
 from __future__ import annotations
 import logging
-import matplotlib.pyplot as plt
 import numpy as np
 import warnings
 from typing import Union, Any, List
@@ -91,9 +90,9 @@ def __init__(self, **kwargs):
         self.species = None
         self.molecules = None
         self.database_group = 'Diffusion_Coefficients'
-        self.x_label = 'Time (s)'
-        self.y_label = 'MSD (m$^2$)'
-        self.analysis_name = 'Einstein_Self_Diffusion_Coefficients'
+        self.x_label = r'$$ \text{Time} / s$$'
+        self.y_label = r'$$ \text{MSD} / m^{2}$$'
+        self.analysis_name = 'Einstein Self-Diffusion Coefficients'
         self.loop_condition = False
         self.optimize = None
         self.msd_array = None  # define empty msd array
@@ -246,24 +245,16 @@ def _post_operation_processes(self, species: str = None):
             Property=self.database_group,
             Analysis=self.analysis_name,
             data_range=self.data_range,
-            data=[{'x': result[0], 'uncertainty': result[1]}],
+            data=[{'diffusion_coefficient': result[0], 'uncertainty': result[1]}],
             Subject=[species]
         )
-
-        if self.save:
-            data = properties.data
-            data += [{'time': x, 'msd': y} for x, y in zip(self.time, self.msd_array)]
-            properties.data = data
+        data = properties.data
+        data += [{'time': x, 'msd': y} for x, y in zip(self.time, self.msd_array)]
+        properties.data = data
 
         self.update_database(properties)
 
-        if self.export:
-            self._export_data(name=self._build_table_name(species), data=self._build_pandas_dataframe(self.time,
-                                                                                                      self.msd_array))
-
         if self.plot:
-            plt.xlabel(rf'{self.x_label}')  # set the x label
-            plt.ylabel(rf'{self.y_label}')  # set the y label
-            plt.plot(np.array(self.time) * self.experiment.units['time'],
-                     self.msd_array * self.experiment.units['time'],
-                     label=fr"{species}: {result[0]: 0.3E} $\pm$ {result[1]: 0.3E}")
+            self.run_visualization(x_data=np.array(self.time) * self.experiment.units['time'],
+                                   y_data=self.msd_array * self.experiment.units['time'],
+                                   title=f"{species}: {result[0]: 0.3E} +- {result[1]: 0.3E}")
diff --git a/mdsuite/calculators/einstein_distinct_diffusion_coefficients.py b/mdsuite/calculators/einstein_distinct_diffusion_coefficients.py
index d6f42c7c..b9ea77a5 100644
--- a/mdsuite/calculators/einstein_distinct_diffusion_coefficients.py
+++ b/mdsuite/calculators/einstein_distinct_diffusion_coefficients.py
@@ -22,7 +22,7 @@
 from tqdm import tqdm
 import tensorflow as tf
 import itertools
-import matplotlib.pyplot as plt
+from mdsuite.database.calculator_database import Parameters
 from mdsuite.calculators.calculator import Calculator, call
 from mdsuite.utils.meta_functions import join_path
 
@@ -72,8 +72,8 @@ def __init__(self, **kwargs):
         self.loaded_property = 'Unwrapped_Positions'  # Property to be loaded for the analysis
 
         self.database_group = 'Diffusion_Coefficients'
-        self.x_label = 'Time $(s)$'
-        self.y_label = 'VACF $(m^{2}/s^{2})$'
+        self.x_label = r'$$\text{Time} / s $$'
+        self.y_label = r'$$\text{VACF} / m^{2}/s^{2}$$'
         self.analysis_name = 'Einstein_Distinct_Diffusion_Coefficients'
         self.experimental = True
 
@@ -234,41 +234,39 @@ def _post_operation_processes(self, species: Union[str, tuple] = None):
         """
         if np.sign(self.msd_array[-1]) == -1:
             result = self._fit_einstein_curve([self.time, abs(self.msd_array)])
-            properties = {"Property": self.database_group,
-                          "Analysis": self.analysis_name,
-                          "Subject": list(species),
-                          "data_range": self.data_range,
-                          'data': [{'x': -1 * result[0], 'uncertainty': result[1]}]
-                          }
-            self._update_properties_file(properties)
+            properties = Parameters(
+                Property=self.database_group,
+                Analysis=self.analysis_name,
+                data_range=self.data_range,
+                data=[{'diffusion_coefficient': -1*result[0],
+                       'uncertainty': result[1]}],
+                Subject=list(species)
+            )
         else:
             result = self._fit_einstein_curve([self.time, self.msd_array])
-            properties = {"Property": self.database_group,
-                          "Analysis": self.analysis_name,
-                          "Subject": list(species),
-                          "data_range": self.data_range,
-                          'data': [{'x': result[0], 'uncertainty': result[1]}]
-                          }
-            self._update_properties_file(properties)
-
-        if self.save:
-            properties = {"Property": self.database_group,
-                          "Analysis": self.analysis_name,
-                          "Subject": list(species),
-                          "data_range": self.data_range,
-                          'data': [{'x': x, 'y': y} for x, y in zip(self.time, self.msd_array)],
-                          'information': "series"
-                          }
-            self._update_properties_file(properties)
+            properties = Parameters(
+                Property=self.database_group,
+                Analysis=self.analysis_name,
+                data_range=self.data_range,
+                data=[{'diffusion_coefficient': result[0],
+                       'uncertainty': result[1]}],
+                Subject=list(species)
+            )
+
+        data = properties.data
+        data += [{'time': x, 'msd': y} for x, y in
+                 zip(self.time, self.msd_array)]
+        properties.data = data
+
+        self.update_database(properties)
 
         # Update the plot if required
         if self.plot:
-            plt.plot(np.array(self.time) * self.experiment.units['time'], self.msd_array, label=species)
-            plt.show()
-
-        if self.export:
-            self._export_data(name=self._build_table_name(species), data=self._build_pandas_dataframe(self.time,
-                                                                                                      self.msd_array))
+            self.run_visualization(
+                x_data=np.array(self.time) * self.experiment.units['time'],
+                y_data=self.msd_array * self.experiment.units['time'],
+                title=species,
+            )
 
     def _update_output_signatures(self):
         """
diff --git a/mdsuite/calculators/einstein_helfand_ionic_conductivity.py b/mdsuite/calculators/einstein_helfand_ionic_conductivity.py
index 1b76527a..f6368e70 100644
--- a/mdsuite/calculators/einstein_helfand_ionic_conductivity.py
+++ b/mdsuite/calculators/einstein_helfand_ionic_conductivity.py
@@ -16,12 +16,13 @@
 Experiment.einstein_helfand_ionic_conductivity method and all necessary calculations performed.
 """
 import warnings
-import matplotlib.pyplot as plt
 import numpy as np
 import tensorflow as tf
 from tqdm import tqdm
 from mdsuite.calculators.calculator import Calculator, call
 from mdsuite.utils.units import elementary_charge, boltzmann_constant
+from mdsuite.database.calculator_database import Parameters
+
 
 tqdm.monitor_interval = 0
 warnings.filterwarnings("ignore")
@@ -72,9 +73,9 @@ def __init__(self, **kwargs):
         self.system_property = True
 
         self.database_group = 'Ionic_Conductivity'  # Which database_path group to save the tensor_values in
-        self.x_label = 'Time (s)'
-        self.y_label = 'MSD (m$^2$/s)'
-        self.analysis_name = 'Einstein_Helfand_Ionic_Conductivity'
+        self.x_label = r'$$\text{Time} / s$$'
+        self.y_label = r'$$\text{MSD} / m^2/s$$'
+        self.analysis_name = 'Einstein Helfand Ionic Conductivity'
         self.prefactor: float
 
     @call
@@ -167,31 +168,24 @@ def _post_operation_processes(self, species: str = None):
 
         """
         result = self._fit_einstein_curve([self.time, self.msd_array])
-        properties = {"Property": self.database_group,
-                      "Analysis": self.analysis_name,
-                      "Subject": ["System"],
-                      "data_range": self.data_range,
-                      'data': [{'x': result[0], 'uncertainty': result[1]}]
-                      }
-        self._update_properties_file(properties)
+
+        properties = Parameters(
+            Property=self.database_group,
+            Analysis=self.analysis_name,
+            data_range=self.data_range,
+            data=[
+                {'ionic_conductivity': result[0], 'uncertainty': result[1]}],
+            Subject=["System"]
+        )
+        data = properties.data
+        data += [{'time': x, 'msd': y} for x, y in
+                 zip(self.time, self.msd_array)]
+        properties.data = data
+        self.update_database(properties)
 
         # Update the plot if required
         if self.plot:
-            plt.plot(np.array(self.time) * self.experiment.units['time'], self.msd_array,
-                     label=fr'{result[0]:.3E} $\pm$ '
-                           f'{result[1]:.3E}')
-            self._plot_data()
-
-        if self.save:
-            properties = {"Property": self.database_group,
-                          "Analysis": self.analysis_name,
-                          "Subject": ["System"],
-                          "data_range": self.data_range,
-                          'data': [{'x': x, 'y': y} for x, y in zip(self.time, self.msd_array)],
-                          'information': "series"
-                          }
-            self._update_properties_file(properties)
-
-        if self.export:
-            self._export_data(name=self._build_table_name("System"), data=self._build_pandas_dataframe(self.time,
-                                                                                                       self.msd_array))
+            self.run_visualization(
+                x_data=np.array(self.time) * self.experiment.units['time'],
+                y_data=self.msd_array * self.experiment.units['time'],
+                title=f'{result[0]:.3E} +- {result[1]:.3E}')
diff --git a/mdsuite/calculators/einstein_helfand_thermal_conductivity.py b/mdsuite/calculators/einstein_helfand_thermal_conductivity.py
index 5795b3c7..67c4c17b 100644
--- a/mdsuite/calculators/einstein_helfand_thermal_conductivity.py
+++ b/mdsuite/calculators/einstein_helfand_thermal_conductivity.py
@@ -13,12 +13,13 @@
 -------
 """
 import warnings
-import matplotlib.pyplot as plt
 import numpy as np
 import tensorflow as tf
 from tqdm import tqdm
 from mdsuite.calculators.calculator import Calculator, call
 
+from mdsuite.database.calculator_database import Parameters
+
 tqdm.monitor_interval = 0
 warnings.filterwarnings("ignore")
 
@@ -67,11 +68,12 @@ def __init__(self, **kwargs):
         self.dependency = "Unwrapped_Positions"
         self.system_property = True
 
-        self.x_label = 'Time (s)'
-        self.y_label = 'MSD (m$^2$/s)'
-        self.analysis_name = 'Einstein_Helfand_Thermal_Conductivity'
+        self.x_label = r'$$\text{Time} / s$$'
+        self.y_label = r'$$\text{MSD}  / m^2/s$$'
+        self.analysis_name = 'Einstein Helfand Thermal Conductivity'
 
-        self.database_group = 'Thermal_Conductivity'  # Which database_path group to save the tensor_values in
+        # Which database_path group to save the tensor_values in
+        self.database_group = 'Thermal_Conductivity'
 
         self.prefactor: float
 
@@ -99,8 +101,9 @@ def __call__(self, plot=True, data_range=500, save=True, correlation_time=1,
         """
 
         # parse to the experiment class
-        self.update_user_args(plot=plot, data_range=data_range, save=save, correlation_time=correlation_time,
-                              export=export, gpu=gpu)
+        self.update_user_args(
+            plot=plot, data_range=data_range, save=save,
+            correlation_time=correlation_time, export=export, gpu=gpu)
 
         self.msd_array = np.zeros(self.data_range)  # Initialize the msd array
 
@@ -168,29 +171,23 @@ def _post_operation_processes(self, species: str = None):
 
         """
         result = self._fit_einstein_curve([self.time, self.msd_array])
-        properties = {"Property": self.database_group,
-                      "Analysis": self.analysis_name,
-                      "Subject": ["System"],
-                      "data_range": self.data_range,
-                      'data': [{'x': result[0], 'uncertainty': result[1]}]
-                      }
-        self._update_properties_file(properties)
+
+        properties = Parameters(
+            Property=self.database_group,
+            Analysis=self.analysis_name,
+            data_range=self.data_range,
+            data=[{'thermal_conductivity': result[0], 'uncertainty': result[1]}],
+            Subject=["System"]
+        )
+        data = properties.data
+        data += [{'time': x, 'msd': y} for x, y in
+                 zip(self.time, self.msd_array)]
+        properties.data = data
+        self.update_database(properties)
 
         # Update the plot if required
         if self.plot:
-            plt.plot(np.array(self.time) * self.experiment.units['time'], self.msd_array)
-            self._plot_data()
-
-        if self.save:
-            properties = {"Property": self.database_group,
-                          "Analysis": self.analysis_name,
-                          "Subject": ["System"],
-                          "data_range": self.data_range,
-                          'data': [{'x': x, 'y': y} for x, y in zip(self.time, self.msd_array)],
-                          'information': "series"
-                          }
-            self._update_properties_file(properties)
-
-        if self.export:
-            self._export_data(name=self._build_table_name("System"),
-                              data=self._build_pandas_dataframe(self.time, self.msd_array))
+            self.run_visualization(
+                x_data=np.array(self.time) * self.experiment.units['time'],
+                y_data=self.msd_array * self.experiment.units['time'],
+                title=f'{result[0]} += {result[1]}')
diff --git a/mdsuite/calculators/einstein_helfand_thermal_kinaci.py b/mdsuite/calculators/einstein_helfand_thermal_kinaci.py
index 2b5a4db9..b1f6f35f 100644
--- a/mdsuite/calculators/einstein_helfand_thermal_kinaci.py
+++ b/mdsuite/calculators/einstein_helfand_thermal_kinaci.py
@@ -19,6 +19,8 @@
 from tqdm import tqdm
 
 from mdsuite.calculators.calculator import Calculator, call
+from mdsuite.database.calculator_database import Parameters
+
 
 tqdm.monitor_interval = 0
 warnings.filterwarnings("ignore")
@@ -69,9 +71,9 @@ def __init__(self, **kwargs):
         self.dependency = "Unwrapped_Positions"
         self.system_property = True
 
-        self.x_label = 'Time (s)'
-        self.y_label = 'MSD (m$^2$/s)'
-        self.analysis_name = 'Einstein_Helfand_Thermal_Conductivity_Kinaci'
+        self.x_label = r'$$\text{Time} / s)$$'
+        self.y_label = r'$$\text{MSD} / m^2/s$$'
+        self.analysis_name = 'Einstein Helfand Thermal Conductivity Kinaci'
 
         self.database_group = 'Thermal_Conductivity'  # Which database_path group to save the tensor_values in
 
@@ -168,29 +170,23 @@ def _post_operation_processes(self, species: str = None):
 
         """
         result = self._fit_einstein_curve([self.time, self.msd_array])
-        properties = {"Property": self.database_group,
-                      "Analysis": self.analysis_name,
-                      "Subject": ["System"],
-                      "data_range": self.data_range,
-                      'data': [{'x': result[0], 'uncertainty': result[1]}]
-                      }
-        self._update_properties_file(properties)
+
+        properties = Parameters(
+            Property=self.database_group,
+            Analysis=self.analysis_name,
+            data_range=self.data_range,
+            data=[
+                {'thermal_conductivity': result[0], 'uncertainty': result[1]}],
+            Subject=["System"]
+        )
+        data = properties.data
+        data += [{'time': x, 'msd': y} for x, y in
+                 zip(self.time, self.msd_array)]
+        properties.data = data
+        self.update_database(properties)
 
         # Update the plot if required
         if self.plot:
-            plt.plot(np.array(self.time) * self.experiment.units['time'], self.msd_array)
-            self._plot_data()
-
-        if self.save:
-            properties = {"Property": self.database_group,
-                          "Analysis": self.analysis_name,
-                          "Subject": ["System"],
-                          "data_range": self.data_range,
-                          'data': [{'x': x, 'y': y} for x, y in zip(self.time, self.msd_array)],
-                          'information': "series"
-                          }
-            self._update_properties_file(properties)
-
-        if self.export:
-            self._export_data(name=self._build_table_name("System"), data=self._build_pandas_dataframe(self.time,
-                                                                                                       self.msd_array))
+            self.run_visualization(x_data=np.array(self.time) * self.experiment.units['time'],
+                                   y_data=self.msd_array * self.experiment.units['time'],
+                                   title=f"{result[0]} +- {result[1]}")
diff --git a/mdsuite/calculators/green_kubo_distinct_diffusion_coefficients.py b/mdsuite/calculators/green_kubo_distinct_diffusion_coefficients.py
index c84bbdc4..09905ec8 100644
--- a/mdsuite/calculators/green_kubo_distinct_diffusion_coefficients.py
+++ b/mdsuite/calculators/green_kubo_distinct_diffusion_coefficients.py
@@ -22,8 +22,8 @@ class can then be called by the Experiment.green_kubo_diffusion_coefficients
 import tensorflow as tf
 import itertools
 from scipy import signal
-import tensorflow_probability as tfp
-import matplotlib.pyplot as plt
+from bokeh.models import Span
+from mdsuite.database.calculator_database import Parameters
 from mdsuite.calculators.calculator import Calculator, call
 from mdsuite.utils.meta_functions import join_path
 
@@ -69,8 +69,8 @@ def __init__(self, **kwargs):
         self.loaded_property = "Velocities"
 
         self.database_group = "Diffusion_Coefficients"
-        self.x_label = "Time $(s)$"
-        self.y_label = "VACF $(m^{2}/s^{2})$"
+        self.x_label = r"$$\text{Time} / s$$"
+        self.y_label = r"\text{VACF}  / m^{2}/s^{2}$$"
         self.analysis_name = "Green_Kubo_Distinct_Diffusion_Coefficients"
         self.experimental = True
 
@@ -274,50 +274,34 @@ def _post_operation_processes(self, species: Union[str, tuple] = None):
         """
         result = self.prefactor * np.array(self.sigma)
 
-        properties = {
-            "Property": self.database_group,
-            "Analysis": self.analysis_name,
-            "Subject": list(species),
-            "data_range": self.data_range,
-            "data": [
-                {
-                    "x": np.mean(result),
-                    "uncertainty": np.std(result) / (np.sqrt(len(result))),
-                }
-            ],
-        }
-        self._update_properties_file(properties)
+        properties = Parameters(
+            Property=self.database_group,
+            Analysis=self.analysis_name,
+            data_range=self.data_range,
+            data=[{'diffusion_coefficient': np.mean(result),
+                   'uncertainty': np.std(result) / (np.sqrt(len(result)))}],
+            Subject=list(species)
+            )
+
+        data = properties.data
+        data += [{'time': x, 'acf': y} for x, y in
+                 zip(self.time, self.vacf)]
+        properties.data = data
+        self.update_database(properties)
+
         # Update the plot if required
         if self.plot:
-            plt.plot(
-                np.array(self.time) * self.experiment.units["time"],
-                self.vacf,
-                label=species,
-            )
-            plt.vlines(
-                (np.array(self.time) * self.experiment.units["time"])[
-                    self.integration_range
-                ],
-                min(self.vacf),
-                max(self.vacf),
+            span = Span(
+                location=(np.array(self.time) * self.experiment.units["time"])[
+                    self.integration_range - 1],
+                dimension='height',
+                line_dash='dashed'
             )
-            plt.savefig(f"{species}.pdf")
-
-        if self.save:
-            properties = {
-                "Property": self.database_group,
-                "Analysis": self.analysis_name,
-                "Subject": list(species),
-                "data_range": self.data_range,
-                "data": [{"x": x, "y": y} for x, y in zip(self.time, self.vacf)],
-                "information": "series",
-            }
-            self._update_properties_file(properties)
-
-        if self.export:
-            self._export_data(
-                name=self._build_table_name(species),
-                data=self._build_pandas_dataframe(self.time, self.vacf),
+            self.run_visualization(
+                x_data=np.array(self.time) * self.experiment.units['time'],
+                y_data=self.vacf.numpy(),
+                title=f"{species}: {np.mean(result): .3E} +- {np.std(result) / (np.sqrt(len(result))): .3E}",
+                layouts=[span]
             )
 
     def _update_output_signatures(self):
diff --git a/mdsuite/calculators/green_kubo_ionic_conductivity.py b/mdsuite/calculators/green_kubo_ionic_conductivity.py
index 07560074..ade23b06 100644
--- a/mdsuite/calculators/green_kubo_ionic_conductivity.py
+++ b/mdsuite/calculators/green_kubo_ionic_conductivity.py
@@ -22,6 +22,8 @@
 import tensorflow_probability as tfp
 from mdsuite.utils.units import boltzmann_constant, elementary_charge
 from mdsuite.calculators.calculator import Calculator, call
+from mdsuite.database.calculator_database import Parameters
+from bokeh.models import Span
 
 
 class GreenKuboIonicConductivity(Calculator):
@@ -68,8 +70,8 @@ def __init__(self, **kwargs):
         self.system_property = True
 
         self.database_group = "Ionic_Conductivity"
-        self.x_label = "Time (s)"
-        self.y_label = r"JACF ($C^{2}\cdot m^{2}/s^{2}$)"
+        self.x_label = r"$$\text{Time} / s"
+        self.y_label = r"$$\text{JACF} / C^{2}\cdot m^{2}/s^{2}$$"
         self.analysis_name = "Green_Kubo_Ionic_Conductivity"
 
         self.prefactor: float
@@ -204,49 +206,31 @@ def _post_operation_processes(self, species: str = None):
         """
         result = self.prefactor * np.array(self.sigma)
 
-        properties = {
-            "Property": self.database_group,
-            "Analysis": self.analysis_name,
-            "Subject": ["System"],
-            "data_range": self.data_range,
-            "data": [
-                {
-                    "x": np.mean(result),
-                    "uncertainty": np.std(result) / (np.sqrt(len(result))),
-                }
-            ],
-        }
-        self._update_properties_file(properties)
+        properties = Parameters(
+            Property=self.database_group,
+            Analysis=self.analysis_name,
+            data_range=self.data_range,
+            data=[{'ionic_conductivity': result[0],
+                   'uncertainty': result[1]}],
+            Subject=["System"]
+        )
+        data = properties.data
+        data += [{'time': x, 'acf': y} for x, y in
+                 zip(self.time, self.jacf)]
+        properties.data = data
+        self.update_database(properties)
 
         # Update the plot if required
         if self.plot:
-            plt.plot(
-                np.array(self.time) * self.experiment.units["time"],
-                self.jacf,
-                label=fr"{np.mean(result): 0.3E} $\pm$ {np.std(result) / np.sqrt(len(result)): 0.3E}",
-            )
-            plt.vlines(
-                (np.array(self.time) * self.experiment.units["time"])[
-                    self.integration_range
-                ],
-                min(self.jacf),
-                max(self.jacf),
+            span = Span(
+                location=(np.array(self.time) * self.experiment.units["time"])[
+                    self.integration_range - 1],
+                dimension='height',
+                line_dash='dashed'
             )
-            self._plot_data()
-
-        if self.save:
-            properties = {
-                "Property": self.database_group,
-                "Analysis": self.analysis_name,
-                "Subject": ["System"],
-                "data_range": self.data_range,
-                "data": [{"x": x, "y": y} for x, y in zip(self.time, self.jacf)],
-                "information": "series",
-            }
-            self._update_properties_file(properties)
-
-        if self.export:
-            self._export_data(
-                name=self._build_table_name("System"),
-                data=self._build_pandas_dataframe(self.time, self.jacf),
+            self.run_visualization(
+                x_data=np.array(self.time) * self.experiment.units['time'],
+                y_data=self.jacf.numpy(),
+                title=f"{np.mean(result): 0.3E} +- {np.std(result) / np.sqrt(len(result)): 0.3E}",
+                layouts=[span]
             )
diff --git a/mdsuite/calculators/green_kubo_self_diffusion_coefficients.py b/mdsuite/calculators/green_kubo_self_diffusion_coefficients.py
index 93e9d41f..2a60ebab 100644
--- a/mdsuite/calculators/green_kubo_self_diffusion_coefficients.py
+++ b/mdsuite/calculators/green_kubo_self_diffusion_coefficients.py
@@ -15,14 +15,15 @@
 class can then be called by the Experiment.green_kubo_diffusion_coefficients
 method and all necessary calculations performed.
 """
-import matplotlib.pyplot as plt
 import numpy as np
 import tensorflow_probability as tfp
 import tensorflow as tf
+from bokeh.models import Span
 from mdsuite.calculators.calculator import Calculator, call
+from mdsuite.database.calculator_database import Parameters
 
 
-class GreenKuboSelfDiffusionCoefficients(Calculator):
+class GreenKuboDiffusionCoefficients(Calculator):
     """
     Class for the Green-Kubo diffusion coefficient implementation
     Attributes
@@ -64,9 +65,9 @@ def __init__(self, **kwargs):
         self.scale_function = {"linear": {"scale_factor": 150}}
 
         self.database_group = "Diffusion_Coefficients"
-        self.x_label = "Time $(s)$"
-        self.y_label = "VACF $(m^{2}/s^{2})$"
-        self.analysis_name = "Green_Kubo_Self_Diffusion_Coefficients"
+        self.x_label = r"$$\text{Time} / s$$"
+        self.y_label = r"$$\text{VACF} / m^{2}/s^{2}$$"
+        self.analysis_name = "Green Kubo Self-Diffusion Coefficients"
 
     @call
     def __call__(
@@ -231,51 +232,30 @@ def _post_operation_processes(self, species: str = None):
         """
         result = self.prefactor * np.array(self.sigma)
 
-        properties = {
-            "Property": self.database_group,
-            "Analysis": self.analysis_name,
-            "Subject": [species],
-            "data_range": self.data_range,
-            "data": [
-                {
-                    "x": np.mean(result),
-                    "uncertainty": np.std(result) / (np.sqrt(len(result))),
-                }
-            ],
-        }
-        self._update_properties_file(properties)
+        properties = Parameters(
+            Property=self.database_group,
+            Analysis=self.analysis_name,
+            data_range=self.data_range,
+            data=[{'diffusion_coefficient': result[0],
+                   'uncertainty': result[1]}],
+            Subject=[species]
+        )
+        data = properties.data
+        data += [{'time': x, 'acf': y} for x, y in
+                 zip(self.time, self.vacf)]
+        properties.data = data
+        self.update_database(properties)
 
         # Update the plot if required
         if self.plot:
-            plt.xlabel(rf"{self.x_label}")
-            plt.ylabel(rf"{self.y_label}")
-            plt.vlines(
-                (np.array(self.time) * self.experiment.units["time"])[
-                    self.integration_range
-                ],
-                min(self.vacf),
-                max(self.vacf),
-            )
-            plt.plot(
-                np.array(self.time) * self.experiment.units["time"],
-                self.vacf,
-                label=fr"{species}: {np.mean(result): .3E} $\pm$ "
-                fr"{np.std(result) / (np.sqrt(len(result))): .3E}",
+            span = Span(
+                location=(np.array(self.time) * self.experiment.units["time"])[self.integration_range - 1],
+                dimension='height',
+                line_dash='dashed'
             )
-
-        if self.save:
-            properties = {
-                "Property": self.database_group,
-                "Analysis": self.analysis_name,
-                "Subject": [species],
-                "data_range": self.data_range,
-                "data": [{"x": x, "y": y} for x, y in zip(self.time, self.vacf)],
-                "information": "series",
-            }
-            self._update_properties_file(properties)
-
-        if self.export:
-            self._export_data(
-                name=self._build_table_name(species),
-                data=self._build_pandas_dataframe(self.time, self.vacf),
+            self.run_visualization(
+                x_data=np.array(self.time) * self.experiment.units['time'],
+                y_data=self.vacf.numpy(),
+                title=f"{species}: {np.mean(result): .3E} +- {np.std(result) / (np.sqrt(len(result))): .3E}",
+                layouts=[span]
             )
diff --git a/mdsuite/calculators/green_kubo_thermal_conductivity.py b/mdsuite/calculators/green_kubo_thermal_conductivity.py
index 9bb4707a..37ae7253 100644
--- a/mdsuite/calculators/green_kubo_thermal_conductivity.py
+++ b/mdsuite/calculators/green_kubo_thermal_conductivity.py
@@ -18,12 +18,13 @@ class can then be called by the Experiment.green_kubo_thermal_conductivity
  method and all necessary calculations performed.
 """
 import warnings
-import matplotlib.pyplot as plt
 import numpy as np
 import tensorflow as tf
 from tqdm import tqdm
 from mdsuite.calculators.calculator import Calculator, call
 import tensorflow_probability as tfp
+from mdsuite.database.calculator_database import Parameters
+from bokeh.models import Span
 
 tqdm.monitor_interval = 0
 warnings.filterwarnings("ignore")
@@ -70,8 +71,8 @@ def __init__(self, **kwargs):
         self.database_group = "Thermal_Conductivity"
         self.system_property = True
 
-        self.x_label = "Time (s)"
-        self.y_label = r"JACF ($C^{2}\cdot m^{2}/s^{2}$)"
+        self.x_label = r"$$\text{Time} / s$$"
+        self.y_label = r"$$\text{JACF} / ($C^{2}\cdot m^{2}/s^{2}$$"
         self.analysis_name = "Green_Kubo_Thermal_Conductivity"
 
     @call
@@ -81,7 +82,6 @@ def __call__(
         data_range=500,
         save=True,
         correlation_time: int = 1,
-        export: bool = False,
         gpu: bool = False,
         integration_range: int = None,
     ):
@@ -111,7 +111,6 @@ def __call__(
             data_range=data_range,
             save=save,
             correlation_time=correlation_time,
-            export=export,
             gpu=gpu,
         )
 
@@ -211,43 +210,32 @@ def _post_operation_processes(self, species: str = None):
 
         """
         result = self.prefactor * np.array(self.sigma)
-        properties = {
-            "Property": self.database_group,
-            "Analysis": self.analysis_name,
-            "Subject": ["System"],
-            "data_range": self.data_range,
-            "data": [
-                {
-                    "x": np.mean(result),
-                    "uncertainty": np.std(result) / (np.sqrt(len(result))),
-                }
-            ],
-        }
-        self._update_properties_file(properties)
+
+        properties = Parameters(
+            Property=self.database_group,
+            Analysis=self.analysis_name,
+            data_range=self.data_range,
+            data=[{'thermal_conductivity': result[0],
+                   'uncertainty': result[1]}],
+            Subject=["System"]
+        )
+        data = properties.data
+        data += [{'time': x, 'acf': y} for x, y in
+                 zip(self.time, self.jacf)]
+        properties.data = data
+        self.update_database(properties)
 
         # Update the plot if required
         if self.plot:
-            plt.plot(np.array(self.time) * self.experiment.units["time"], self.jacf)
-            plt.vlines(
-                (np.array(self.time) * self.experiment.units["time"])[
-                    self.integration_range
-                ],
-                min(self.jacf),
-                max(self.jacf),
+            span = Span(
+                location=(np.array(self.time) * self.experiment.units["time"])[
+                    self.integration_range - 1],
+                dimension='height',
+                line_dash='dashed'
             )
-            self._plot_data()
-        if self.save:
-            properties = {
-                "Property": self.database_group,
-                "Analysis": self.analysis_name,
-                "Subject": ["System"],
-                "data_range": self.data_range,
-                "data": [{"x": x, "y": y} for x, y in zip(self.time, self.jacf)],
-                "information": "series",
-            }
-            self._update_properties_file(properties)
-        if self.export:
-            self._export_data(
-                name=self._build_table_name("System"),
-                data=self._build_pandas_dataframe(self.time, self.jacf),
+            self.run_visualization(
+                x_data=np.array(self.time) * self.experiment.units['time'],
+                y_data=self.jacf.numpy(),
+                title=f"{result[0]} +- {result[1]}",
+                layouts=[span]
             )
diff --git a/mdsuite/calculators/green_kubo_viscosity.py b/mdsuite/calculators/green_kubo_viscosity.py
index 26817178..41c7d3b1 100644
--- a/mdsuite/calculators/green_kubo_viscosity.py
+++ b/mdsuite/calculators/green_kubo_viscosity.py
@@ -13,11 +13,12 @@
 This module contains the code for the Green-Kubo viscosity class.
 This class is called by the experiment class.
 """
-import matplotlib.pyplot as plt
 import numpy as np
 import tensorflow as tf
 import tensorflow_probability as tfp
 from mdsuite.calculators.calculator import Calculator, call
+from mdsuite.database.calculator_database import Parameters
+from bokeh.models import Span
 
 
 class GreenKuboViscosity(Calculator):
@@ -61,8 +62,8 @@ def __init__(self, **kwargs):
         self.database_group = "Viscosity"
         self.system_property = True
 
-        self.x_label = "Time (s)"
-        self.y_label = r"SACF ($C^{2}\cdot m^{2}/s^{2}$)"
+        self.x_label = r"$$\text{Time} / s$$"
+        self.y_label = r"\text{SACF} / C^{2}\cdot m^{2}/s^{2}$$"
         self.analysis_name = "Green_Kubo_Viscosity"
         self.prefactor: float
 
@@ -73,7 +74,6 @@ def __call__(
         data_range=500,
         save=True,
         correlation_time: int = 1,
-        export: bool = False,
         gpu: bool = False,
         integration_range: int = None,
     ):
@@ -94,7 +94,6 @@ def __call__(
             data_range=data_range,
             save=save,
             correlation_time=correlation_time,
-            export=export,
             gpu=gpu,
         )
 
@@ -195,43 +194,30 @@ def _post_operation_processes(self, species: str = None):
         """
         result = self.prefactor * np.array(self.sigma)
 
-        properties = {
-            "Property": self.database_group,
-            "Analysis": self.analysis_name,
-            "Subject": ["System"],
-            "data_range": self.data_range,
-            "data": [
-                {
-                    "x": np.mean(result),
-                    "uncertainty": np.std(result) / (np.sqrt(len(result))),
-                }
-            ],
-        }
-        self._update_properties_file(properties)
+        properties = Parameters(
+            Property=self.database_group,
+            Analysis=self.analysis_name,
+            data_range=self.data_range,
+            data=[{'viscosity': result[0],
+                   'uncertainty': result[1]}],
+            Subject=["System"]
+        )
+        data = properties.data
+        data += [{'time': x, 'acf': y} for x, y in
+                 zip(self.time, self.jacf)]
+        properties.data = data
+        self.update_database(properties)
         # Update the plot if required
         if self.plot:
-            plt.plot(np.array(self.time) * self.experiment.units["time"], self.jacf)
-            plt.vlines(
-                (np.array(self.time) * self.experiment.units["time"])[
-                    self.integration_range
-                ],
-                min(self.jacf),
-                max(self.jacf),
+            span = Span(
+                location=(np.array(self.time) * self.experiment.units["time"])[
+                    self.integration_range - 1],
+                dimension='height',
+                line_dash='dashed'
             )
-            self._plot_data()
-
-        if self.save:
-            properties = {
-                "Property": self.database_group,
-                "Analysis": self.analysis_name,
-                "Subject": ["System"],
-                "data_range": self.data_range,
-                "data": [{"x": x, "y": y} for x, y in zip(self.time, self.jacf)],
-                "information": "series",
-            }
-            self._update_properties_file(properties)
-        if self.export:
-            self._export_data(
-                name=self._build_table_name("System"),
-                data=self._build_pandas_dataframe(self.time, self.jacf),
+            self.run_visualization(
+                x_data=np.array(self.time) * self.experiment.units['time'],
+                y_data=self.jacf.numpy(),
+                title=f"{result[0]} +- {result[1]}",
+                layouts=[span]
             )
diff --git a/mdsuite/calculators/green_kubo_viscosity_flux.py b/mdsuite/calculators/green_kubo_viscosity_flux.py
index 07bb5a6a..b077ccd4 100644
--- a/mdsuite/calculators/green_kubo_viscosity_flux.py
+++ b/mdsuite/calculators/green_kubo_viscosity_flux.py
@@ -17,13 +17,13 @@
 calculations performed.
 """
 import warnings
-import matplotlib.pyplot as plt
 import numpy as np
 from tqdm import tqdm
 import tensorflow as tf
-from scipy import signal
-
+import tensorflow_probability as tfp
+from mdsuite.database.calculator_database import Parameters
 from mdsuite.calculators.calculator import Calculator, call
+from bokeh.models import Span
 
 tqdm.monitor_interval = 0
 warnings.filterwarnings("ignore")
@@ -69,16 +69,21 @@ def __init__(self, **kwargs):
 
         self.database_group = 'Viscosity'  # Which database_path group to save the tensor_values in
         self.analysis_name = 'Viscosity_Flux'
-        self.x_label = 'Time (s)'
-        self.y_label = 'JACF ($C^{2}\\cdot m^{2}/s^{2}$)'
+        self.x_label = r'$$\text{Time} / s$$'
+        self.y_label = r'$$\text{JACF} / C^{2}\\cdot m^{2}/s^{2}$$'
 
         self.prefactor: float
         self.jacf = np.zeros(self.data_range)
         self.sigma = []
 
     @call
-    def __call__(self, plot=False, data_range=500, correlation_time=1, save=True, export: bool = False,
-                 gpu: bool = False):
+    def __call__(self,
+                 plot=False,
+                 data_range=500,
+                 correlation_time=1,
+                 save=True,
+                 gpu: bool = False,
+                 integration_range: int = None):
         """
         Python constructor for the experiment class.
 
@@ -90,12 +95,18 @@ def __call__(self, plot=False, data_range=500, correlation_time=1, save=True, ex
                 Number of configurations to include in each ensemble
         """
 
-        self.update_user_args(plot=plot, data_range=data_range, save=save, correlation_time=correlation_time,
-                              export=export, gpu=gpu)
+        self.update_user_args(
+            plot=plot, data_range=data_range, save=save,
+            correlation_time=correlation_time, gpu=gpu)
 
         self.jacf = np.zeros(self.data_range)
         self.sigma = []
 
+        if integration_range is None:
+            self.integration_range = self.data_range
+        else:
+            self.integration_range = integration_range
+
     def _update_output_signatures(self):
         """
         Update the output signature for the IC.
@@ -149,17 +160,18 @@ def _apply_operation(self, ensemble, index):
         -------
         updates class vacf with the tensor_values.
         """
-        jacf = (signal.correlate(ensemble[:, 0],
-                                 ensemble[:, 0],
-                                 mode='full', method='auto') +
-                signal.correlate(ensemble[:, 1],
-                                 ensemble[:, 1],
-                                 mode='full', method='auto') +
-                signal.correlate(ensemble[:, 2],
-                                 ensemble[:, 2],
-                                 mode='full', method='auto'))
+        jacf = self.data_range * tf.reduce_sum(
+            tfp.stats.auto_correlation(ensemble, normalize=False, axis=0,
+                                       center=False),
+            axis=-1,
+        )
         self.jacf += jacf[int(self.data_range - 1):]
-        self.sigma.append(np.trapz(jacf[int(self.data_range - 1):], x=self.time))
+        self.sigma.append(
+            np.trapz(
+                jacf[: self.integration_range],
+                x=self.time[: self.integration_range]
+            )
+        )
 
     def _post_operation_processes(self, species: str = None):
         """
@@ -170,29 +182,31 @@ def _post_operation_processes(self, species: str = None):
         """
         result = self.prefactor*np.array(self.sigma)
 
-        properties = {"Property": self.database_group,
-                      "Analysis": self.analysis_name,
-                      "Subject": ["System"],
-                      "data_range": self.data_range,
-                      'data': [{'x': np.mean(result), 'uncertainty': np.std(result)/(np.sqrt(len(result)))}]
-                      }
-        self._update_properties_file(properties)
+        properties = Parameters(
+            Property=self.database_group,
+            Analysis=self.analysis_name,
+            data_range=self.data_range,
+            data=[{'viscosity': result[0],
+                   'uncertainty': result[1]}],
+            Subject=["System"]
+        )
+        data = properties.data
+        data += [{'time': x, 'acf': y} for x, y in
+                 zip(self.time, self.jacf)]
+        properties.data = data
+        self.update_database(properties)
 
         # Update the plot if required
         if self.plot:
-            plt.plot(np.array(self.time) * self.experiment.units['time'], self.jacf)
-            self._plot_data()
-
-        if self.save:
-            properties = {"Property": self.database_group,
-                          "Analysis": self.analysis_name,
-                          "Subject": ["System"],
-                          "data_range": self.data_range,
-                          'data': [{'x': x, 'y': y} for x, y in zip(self.time, self.jacf)],
-                          'information': "series"
-                          }
-            self._update_properties_file(properties)
-
-        if self.export:
-            self._export_data(name=self._build_table_name("System"), data=self._build_pandas_dataframe(self.time,
-                                                                                                       self.jacf))
+            span = Span(
+                location=(np.array(self.time) * self.experiment.units["time"])[
+                    self.integration_range - 1],
+                dimension='height',
+                line_dash='dashed'
+            )
+            self.run_visualization(
+                x_data=np.array(self.time) * self.experiment.units['time'],
+                y_data=self.jacf.numpy(),
+                title=f"{result[0]} +- {result[1]}",
+                layouts=[span]
+            )
diff --git a/mdsuite/calculators/kirkwood_buff_integrals.py b/mdsuite/calculators/kirkwood_buff_integrals.py
index d0cff306..2273c1e5 100644
--- a/mdsuite/calculators/kirkwood_buff_integrals.py
+++ b/mdsuite/calculators/kirkwood_buff_integrals.py
@@ -11,8 +11,7 @@
 """
 import logging
 import numpy as np
-import matplotlib.pyplot as plt
-from typing import Union
+from mdsuite.database.calculator_database import Parameters
 from mdsuite.utils.exceptions import NotApplicableToAnalysis
 from mdsuite.calculators.calculator import Calculator, call
 
@@ -74,8 +73,8 @@ def __init__(self, **kwargs):
         self.species_tuple = None
         self.kb_integral = None
         self.database_group = "Kirkwood_Buff_Integral"
-        self.x_label = r"r ($\AA$)"
-        self.y_label = r"$G(\mathbf{r})$"
+        self.x_label = r"$$ \text{r} / \AA$$"
+        self.y_label = r"$$\text{G}(\mathbf{r})$$"
         self.analysis_name = "Kirkwood-Buff_Integral"
 
         self.post_generation = True
@@ -128,89 +127,31 @@ def run_post_generation_analysis(self):
         """
         Calculate the potential of mean-force and perform error analysis
         """
-
-        for data in self._get_rdf_data():  # Loop over all existing RDFs
+        calculations = self._get_rdf_data()
+        for data in calculations:  # Loop over all existing RDFs
             self.species_tuple = "_".join(data.subjects)
             self.data_range = data.data_range
-
             self._load_rdf_from_file(data)  # load the tensor_values from it
-
             self._calculate_kb_integral()  # Integrate the rdf and calculate the KB integral
 
+            data = [
+                {"r": x, "kb_integral": y} for x, y in
+                zip(self.radii[1:], self.kb_integral)
+            ]
+            log.debug(f"Writing {self.analysis_name} to database!")
+            properties = Parameters(
+                Property=self.database_group,
+                Analysis=self.analysis_name,
+                data_range=self.data_range,
+                data=data,
+                Subject=[self.species_tuple]
+            )
+            self.update_database(properties)
+
             # Plot if required
             if self.plot:
-                plt.plot(
-                    self.radii[1:], self.kb_integral, label=f"{self.species_tuple}"
+                self.run_visualization(
+                    x_data=self.radii[1:],
+                    y_data=self.kb_integral,
+                    title=f"{self.species_tuple}",
                 )
-                self._plot_data(title=f"{self.analysis_name}_{self.species_tuple}")
-
-            if self.save or self.export:
-                data = [
-                    {"x": x, "y": y} for x, y in zip(self.radii[1:], self.kb_integral)
-                ]
-                log.debug(f"Writing {self.analysis_name} to database!")
-                self._update_properties_file(
-                    {
-                        "Property": self.system_property,
-                        "Analysis": self.analysis_name,
-                        "subjects": self.species_tuple.split("_"),
-                        "data_range": self.data_range,
-                        "data": data,
-                    }
-                )
-
-    def _calculate_prefactor(self, species: Union[str, tuple] = None):
-        """
-        calculate the calculator pre-factor.
-
-        Parameters
-        ----------
-        species : str
-                Species property if required.
-        Returns
-        -------
-
-        """
-        raise NotImplementedError
-
-    def _apply_operation(self, data, index):
-        """
-        Perform operation on an ensemble.
-
-        Parameters
-        ----------
-        One tensor_values range of tensor_values to operate on.
-
-        Returns
-        -------
-
-        """
-        raise NotImplementedError
-
-    def _apply_averaging_factor(self):
-        """
-        Apply an averaging factor to the tensor_values.
-        Returns
-        -------
-        averaged copy of the tensor_values
-        """
-        raise NotImplementedError
-
-    def _post_operation_processes(self, species: Union[str, tuple] = None):
-        """
-        call the post-op processes
-        Returns
-        -------
-
-        """
-        raise NotImplementedError
-
-    def _update_output_signatures(self):
-        """
-        After having run _prepare managers, update the output signatures.
-
-        Returns
-        -------
-
-        """
-        raise NotImplementedError
diff --git a/mdsuite/calculators/potential_of_mean_force.py b/mdsuite/calculators/potential_of_mean_force.py
index dc5252c6..d280aa97 100644
--- a/mdsuite/calculators/potential_of_mean_force.py
+++ b/mdsuite/calculators/potential_of_mean_force.py
@@ -20,13 +20,13 @@
 import logging
 import numpy as np
 from scipy.signal import find_peaks
-import matplotlib.pyplot as plt
-from typing import Union
+from mdsuite.database.calculator_database import Parameters
 from mdsuite.utils.exceptions import NotApplicableToAnalysis
 from mdsuite.calculators.calculator import Calculator, call
 from mdsuite.utils.meta_functions import golden_section_search
 from mdsuite.utils.meta_functions import apply_savgol_filter
 from mdsuite.utils.units import boltzmann_constant
+from bokeh.models import BoxAnnotation
 
 log = logging.getLogger(__name__)
 
@@ -35,23 +35,27 @@ class PotentialOfMeanForce(Calculator):
     """
     Class for the calculation of the potential of mean-force
 
-    The potential of mean-force is a measure of the binding strength between atomic species in a experiment.
+    The potential of mean-force is a measure of the binding strength between
+    atomic species in a experiment.
 
     Attributes
     ----------
     experiment : class object
                         Class object of the experiment.
     data_range : int (default=500)
-                        Range over which the property should be evaluated. This is not applicable to the current
-                        analysis as the full rdf will be calculated.
+                        Range over which the property should be evaluated.
+                        This is not applicable to the current analysis as the
+                        full rdf will be calculated.
     x_label : str
                         How to label the x axis of the saved plot.
     y_label : str
                         How to label the y axis of the saved plot.
     analysis_name : str
-                        Name of the analysis. used in saving of the tensor_values and figure.
+                        Name of the analysis. used in saving of the
+                        tensor_values and figure.
     file_to_study : str
-                        The tensor_values file corresponding to the rdf being studied.
+                        The tensor_values file corresponding to the rdf being
+                        studied.
     data_files : list
                         list of files to be analyzed.
     rdf = None : list
@@ -91,8 +95,8 @@ def __init__(self, **kwargs):
         self.pomf = None
         self.indices = None
         self.database_group = 'Potential_Of_Mean_Force'
-        self.x_label = r'r ($\AA$)'
-        self.y_label = r'$w^{(2)}(r)$'
+        self.x_label = r'$$\text{r| /  \AA$$'
+        self.y_label = r'$$w^{(2)}(r)$$'
         self.analysis_name = 'Potential_of_Mean_Force'
         self.post_generation = True
 
@@ -144,9 +148,12 @@ def _get_max_values(self):
         """
         Calculate the maximums of the rdf
         """
-        filtered_data = apply_savgol_filter(self.pomf, order=self.savgol_order, window_length=self.savgol_window_length)
+        filtered_data = apply_savgol_filter(
+            self.pomf, order=self.savgol_order,
+            window_length=self.savgol_window_length)
 
-        peaks = find_peaks(filtered_data)[0]  # Find the maximums in the filtered dataset
+        # Find the maximums in the filtered dataset
+        peaks = find_peaks(filtered_data)[0]
 
         return [peaks[0], peaks[1]]
 
@@ -154,8 +161,8 @@ def _find_minimum(self):
         """
         Find the minimum of the pomf function
 
-        This function calls an implementation of the Golden-section search algorithm to determine the minimum of the
-        potential of mean-force function.
+        This function calls an implementation of the Golden-section search
+        algorithm to determine the minimum of the potential of mean-force function.
 
         Returns
         -------
@@ -184,32 +191,16 @@ def _get_pomf_value(self):
         pomf_value = np.mean([self.pomf[self.indices[0]], self.pomf[self.indices[1]]])
         pomf_error = np.std([self.pomf[self.indices[0]], self.pomf[self.indices[1]]]) / np.sqrt(2)
 
-        # Update the experiment class
-        properties = {"Property": self.database_group,
-                      "Analysis": self.analysis_name,
-                      "Subject": self.species_tuple.split('_'),
-                      "data_range": self.data_range,
-                      'data': [{'x': pomf_value, 'uncertainty': pomf_error}]
-                      }
-        self._update_properties_file(properties)
-
         return pomf_value, pomf_error
 
-    def _plot_fits(self, data: list):
-        """
-        Plot the predicted minimum value before parsing the other tensor_values for plotting
-        """
-        plt.plot(self.radii, self.pomf, label=fr'{self.species_tuple}: {data[0]: 0.3E} $\pm$ {data[1]: 0.3E}')
-        plt.axvspan(self.radii[self.indices[0]], self.radii[self.indices[1]], color='y', alpha=0.5, lw=0)
-
     def run_post_generation_analysis(self):
         """
         Calculate the potential of mean-force and perform error analysis
         """
 
         # fill the tensor_values array with tensor_values
-
-        for data in self._get_rdf_data():
+        calculations = self._get_rdf_data()
+        for data in calculations:
             self.file_to_study = data  # Set the correct tensor_values file in the class
             self.species_tuple = "_".join(data.subjects)
             self.data_range = data.data_range
@@ -218,25 +209,30 @@ def run_post_generation_analysis(self):
             self._calculate_potential_of_mean_force()  # calculate the potential of mean-force
             _data = self._get_pomf_value()  # Determine the min values of the function and update experiment
 
-            # Update the experiment class
-
-            if self.save:
-                properties = {"Property": self.database_group,
-                              "Analysis": self.analysis_name,
-                              "Subject": self.species_tuple.split('_'),
-                              "data_range": self.data_range,
-                              'data': [{'x': x, 'y': y} for x, y in zip(self.radii, self.pomf)],
-                              'information': 'series'
-                              }
-                self._update_properties_file(properties)
-
-            if self.export:
-                self._export_data(name=self._build_table_name(self.species_tuple),
-                                  data=self._build_pandas_dataframe(self.radii, self.pomf))
+            properties = Parameters(
+                Property=self.database_group,
+                Analysis=self.analysis_name,
+                data_range=self.data_range,
+                data=[{'min_pomf': _data[0],
+                       'uncertainty': _data[1]}],
+                Subject=[self.species_tuple]
+            )
+            data = properties.data
+            data += [{'r': x, 'pomf': y} for x, y in
+                     zip(self.radii, self.pomf)]
+            properties.data = data
+            self.update_database(properties)
 
             if self.plot:
-                self._plot_fits(_data)
+                model = BoxAnnotation(
+                    left=self.radii[self.indices[0]],
+                    right=self.radii[self.indices[1]],
+                    fill_alpha=0.1,
+                    fill_color='red')
+                self.run_visualization(
+                    x_data=self.radii,
+                    y_data=self.pomf,
+                    title=fr'{self.species_tuple}: {_data[0]: 0.3E} +- {_data[1]: 0.3E}',
+                    layouts=[model]
+                )
 
-        if self.plot:
-            plt.legend()
-            plt.show()
diff --git a/mdsuite/calculators/radial_distribution_function.py b/mdsuite/calculators/radial_distribution_function.py
index 013aeb3a..cf5751b3 100644
--- a/mdsuite/calculators/radial_distribution_function.py
+++ b/mdsuite/calculators/radial_distribution_function.py
@@ -23,7 +23,6 @@
 from abc import ABC
 from typing import Union
 import numpy as np
-import matplotlib.pyplot as plt
 import warnings
 
 # Import user packages
@@ -95,8 +94,8 @@ def __init__(self, **kwargs):
         self.scale_function = {'quadratic': {'outer_scale_factor': 1}}
         self.loaded_property = 'Positions'
         self.database_group = 'Radial_Distribution_Function'  # Which database_path group to save the tensor_values in
-        self.x_label = r'r ($\AA$)'
-        self.y_label = 'g(r)'
+        self.x_label = r'$$r / \AA$$'
+        self.y_label = r'$$g(r)$$'
         self.analysis_name = 'Radial_Distribution_Function'
         self.experimental = True
 
@@ -131,7 +130,6 @@ def __call__(self,
                  start=0,
                  stop=None,
                  number_of_configurations=500,
-                 export: bool = False,
                  minibatch: int = -1,
                  species: list = None,
                  molecules: bool = False,
@@ -163,8 +161,6 @@ def __call__(self,
         number_of_configurations: int
             The number of uniformly sampled configuration between start and
             stop to be used for the RDF.
-        export: bool
-            If true, the outcome is immediately exported to a csv file.
         minibatch: int
             Size of a minibatch over atoms in the batch over configurations.
             Decrease this value if you run into memory
@@ -191,7 +187,6 @@ def __call__(self,
         self.update_user_args(plot=plot,
                               save=save,
                               data_range=data_range,
-                              export=export,
                               gpu=gpu)
 
         # kwarg parsing
@@ -343,30 +338,27 @@ def _calculate_radial_distribution_functions(self):
             prefactor = self._calculate_prefactor(names)  # calculate the prefactor
 
             self.rdf.update({names: self.rdf.get(names) * prefactor})  # Apply the prefactor
-
-            if self.plot:
-                fig, ax = plt.subplots()
-                ax.plot(np.linspace(0.0, self.cutoff, self.number_of_bins), self.rdf.get(names), label=names)
-                self._plot_fig(fig, ax, title=names)  # Plot the tensor_values if necessary
+            log.debug("Writing RDF to database!")
 
             self.data_range = self.number_of_configurations
-            if self.save:
-                data = [{"x": x, "y": y} for x, y in
-                        zip(np.linspace(0.0, self.cutoff, self.number_of_bins), self.rdf.get(names))]
-                log.debug("Writing RDF to database!")
-
-                params = Parameters(
-                    Property=self.database_group,
-                    Analysis=self.analysis_name,
-                    data_range=self.data_range,
-                    data=data,
-                    Subject=names.split("_"))
-
-                self.update_database(params)
-            if self.export:
-                self._export_data(name=self._build_table_name(names),
-                                  data=self._build_pandas_dataframe(np.linspace(0.0, self.cutoff, self.number_of_bins),
-                                                                    self.rdf.get(names)))
+            data = [{"x": x, "y": y} for x, y in
+                    zip(np.linspace(0.0, self.cutoff, self.number_of_bins),
+                        self.rdf.get(names))]
+            params = Parameters(
+                Property=self.database_group,
+                Analysis=self.analysis_name,
+                data_range=self.data_range,
+                data=data,
+                Subject=names.split("_"))
+
+            self.update_database(params)
+
+            if self.plot:
+                self.run_visualization(
+                    x_data=np.linspace(0.0, self.cutoff, self.number_of_bins),
+                    y_data=self.rdf.get(names),
+                    title=f"{names}",
+                )
 
         self.experiment.radial_distribution_function_state = True  # update the state
 
diff --git a/mdsuite/calculators/spatial_distribution_function.py b/mdsuite/calculators/spatial_distribution_function.py
new file mode 100644
index 00000000..be547e63
--- /dev/null
+++ b/mdsuite/calculators/spatial_distribution_function.py
@@ -0,0 +1,169 @@
+"""
+This program and the accompanying materials are made available under the terms of the
+Eclipse Public License v2.0 which accompanies this distribution, and is available at
+https://www.eclipse.org/legal/epl-v20.html
+SPDX-License-Identifier: EPL-2.0
+
+Copyright Contributors to the Zincware Project.
+
+Description: A spatial distribution function calculator
+"""
+from __future__ import annotations
+
+import logging
+
+from mdsuite.calculators.calculator import Calculator, call
+from mdsuite.utils.tensorflow.layers import NLLayer
+from mdsuite.utils.meta_functions import join_path
+from tqdm import tqdm
+import numpy as np
+import tensorflow as tf
+from mdsuite.visualizer.d3_data_visualizer import DataVisualizer3D
+
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from mdsuite import Experiment
+
+log = logging.getLogger(__name__)
+
+
+class SpatialDistributionFunction(Calculator):
+    """Spatial Distribution Function Calculator based on the r_ij matrix"""
+
+    def __init__(
+            self, experiment: Experiment, experiments=None, load_data: bool = False
+    ):
+        """Constructor of the SpatialDistributionFunction
+
+        Parameters
+        ----------
+        experiment: Experiment
+            managed by RunComputation
+        experiments:
+            list of Experiments, managed by RunComputation
+        load_data: bool
+            managed by RunComputation
+
+        """
+        super().__init__(experiment, experiments=experiments, load_data=load_data)
+
+        self.scale_function = {'quadratic': {'outer_scale_factor': 1}}
+        self.loaded_property = 'Positions'
+        self.database_group = 'Spatial_Distribution_Function'
+        self.x_label = r'$$\text{r} /  \AA$$'  # None
+        self.y_label = r'$$\text{g(r)}$$'  # None
+        self.analysis_name = 'Spatial_Distribution_Function'
+        self.experimental = True
+
+        self._dtype = tf.float32
+
+    @call
+    def __call__(
+            self, molecules: bool = False, start: int = 1, stop: int = 10,
+            number_of_configurations: int = 5, r_min: float = 4.0,
+            r_max: float = 4.5, species: list = None, **kwargs):
+        """
+        User Interface to the Spatial Distribution Function
+
+        Parameters
+        ----------
+        molecules
+        start: int
+            Index of the first configuration
+        stop: int
+            Index of the last configuration
+        number_of_configurations: int
+            Number of configurations to sample between start and stop
+        r_min: float
+            Minimal distance for the SDF
+        r_max: float
+            Maximal distance for the SDF
+        species: list
+            List of species to use, for computing the SDF,
+            if None a single SDF of all available species will be computed
+        kwargs
+        """
+        self.species = species
+        self.molecules = molecules
+        self.r_min = r_min
+        self.r_max = r_max
+
+        # choose sampled configurations
+        self.sample_configurations = np.linspace(start,
+                                                 stop,
+                                                 number_of_configurations,
+                                                 dtype=np.int)
+
+        self.update_user_args()
+        self._check_input()
+
+    def _load_positions(self, indices: list) -> tf.Tensor:
+        """
+        Load the positions matrix
+
+        This function is here to optimize calculation speed
+
+        Parameters
+        ----------
+        indices : list
+                List of indices to take from the database_path
+        Returns
+        -------
+        loaded_data : tf.Tensor
+                tf.Tensor of tensor_values loaded from the hdf5 database_path
+        """
+        path_list = [join_path(species, "Positions") for species in self.species]
+        data = self.experiment.load_matrix(
+            "Positions", path=path_list, select_slice=np.s_[:, indices]
+        )
+        if len(self.species) == 1:
+            return tf.cast(data, dtype=self.dtype)
+        else:
+            return tf.cast(tf.concat(data, axis=0), dtype=self.dtype)
+
+    def _check_input(self):
+        """Check and correct the user input"""
+        if self.species is None:
+            self.species = list(self.experiment.species)
+
+    def run_experimental_analysis(self):
+        """Run the computation"""
+
+        # Iterate over batches
+        sdf_values = []
+
+        nllayer = NLLayer()
+
+        for idx, sample_configuration in tqdm(
+                enumerate(np.array_split(self.sample_configurations, self.n_batches)), ncols=70
+        ):
+            positions_tensor = self._load_positions(sample_configuration)
+
+            # make it (configurations, n_atoms, 3)
+            positions_tensor = tf.transpose(positions_tensor, perm=(1, 0, 2))
+            cell = tf.linalg.set_diag(tf.zeros((3, 3)), self.experiment.box_array, )
+            cell = tf.repeat(cell[None], positions_tensor.shape[0], axis=0)
+            # TODO slice r_ij that only the selected species distances are
+            #  still available, e.g. for species1 != species2 maybe use a
+            #  dictionary in the end
+
+            r_ij = nllayer({"positions": positions_tensor, "cell": cell})
+
+            d_ij = tf.linalg.norm(r_ij, axis=-1)
+            # apply minimal and maximal distance and remove the diagonal elements of 0
+            mask = (d_ij > self.r_min) & (d_ij < self.r_max) & (d_ij != 0)
+            r_ij_cut = r_ij[mask]
+
+            sdf_values.append(r_ij_cut)
+
+        sdf_values = tf.concat(sdf_values, axis=0)
+
+        self._run_visualization(sdf_values)
+
+    def _run_visualization(self, plot_data: tf.Tensor):
+        """
+        Run the visualizer.
+        """
+        visualizer = DataVisualizer3D(data=plot_data.numpy(), title='test')
+        visualizer.plot()
diff --git a/mdsuite/calculators/structure_factor.py b/mdsuite/calculators/structure_factor.py
index 5b21e37d..bc58a6cb 100644
--- a/mdsuite/calculators/structure_factor.py
+++ b/mdsuite/calculators/structure_factor.py
@@ -11,22 +11,15 @@
  """
 import logging
 import numpy as np
-import os
 import pandas as pd
 from scipy.integrate import simps
 from scipy.integrate import cumtrapz
-import matplotlib.pyplot as plt
-from typing import Union
+from mdsuite.database.calculator_database import Parameters
 from tqdm import tqdm
-
 from mdsuite.utils.exceptions import NotApplicableToAnalysis
 from mdsuite.calculators.calculator import Calculator
 from mdsuite import data as static_data
 from importlib.resources import open_text
-from mdsuite.database.property_database import PropertiesDatabase
-from mdsuite.database.database_scheme import SystemProperty
-
-plt.rcParams['figure.facecolor'] = 'white'
 
 log = logging.getLogger(__name__)
 
@@ -94,8 +87,8 @@ def __init__(self, **kwargs):
         self.post_generation = True
 
         self.database_group = 'structure_factor'
-        self.x_label = r'Q ($\AA ^{-1}$)'
-        self.y_label = r'S(Q)'
+        self.x_label = r'$$\text{Q} / \AA ^{-1}$$'
+        self.y_label = r'$$\text{S(Q)}$$'
         self.analysis_name = 'total_structure_factor'
 
         self.rho = None
@@ -160,7 +153,8 @@ def gauss(a, b, scattering_scalar):
 
     def atomic_form_factors(self, scattering_scalar):
         """
-        Calculates the atomic form factors for all elements in the species dictionary and returns it
+        Calculates the atomic form factors for all elements in the species
+        dictionary and returns it
         """
         atomic_form_di = {}
         for el in list(self.experiment.species):
@@ -185,8 +179,8 @@ def atomic_form_factors(self, scattering_scalar):
 
     def molar_fractions(self):
         """
-        Calculates the molar fractions for all elements in the species dictionary and add it to the species
-        dictionary
+        Calculates the molar fractions for all elements in the species
+        dictionary and add it to the species dictionary
         """
         species = dict(self.experiment.species)
 
@@ -199,8 +193,8 @@ def molar_fractions(self):
     def species_densities(self):
         """Calculates the particle densities
 
-        Calculates the particle densities for all the species in the species dictionary and add it to the species
-        dictionary
+        Calculates the particle densities for all the species in the species
+        dictionary and add it to the species dictionary
         """
         log.warning("Updating particle_density")
         species = dict(self.experiment.species)
@@ -259,7 +253,8 @@ def weight_factor(self, scattering_scalar, species_lst):
 
     def total_structure_factor(self, scattering_scalar):
         """
-        Calculates the total structure factor by summing the products of weight_factor * partial_structure_factor
+        Calculates the total structure factor by summing the products of
+        weight_factor * partial_structure_factor
         """
         self.atomic_form_factors(scattering_scalar)
         total_struc_fac = 0
@@ -281,60 +276,31 @@ def total_structure_factor(self, scattering_scalar):
 
     def run_post_generation_analysis(self):
         """
-        Calculates the total structure factor for all the different Q-values of the Q_arr
-        (magnitude of the scattering vector)
-        """
-        test = False
-        if test:
-            self.run_test()
-        else:
-            self._get_rdf_data()
-            self.molar_fractions()
-            self.species_densities()
-            total_structure_factor_li = []
-            for counter, scattering_scalar in tqdm(enumerate(self.Q_arr), total=len(self.Q_arr),
-                                                   desc="Structure factor calculation"):
-                total_structure_factor_li.append(self.total_structure_factor(scattering_scalar))
-
-            total_structure_factor_li = np.array(total_structure_factor_li)
-            if self.plot:
-                plt.plot(self.Q_arr, total_structure_factor_li, label='total structure factor')
-                plt.xlabel(rf'{self.x_label}')  # set the x label
-                plt.ylabel(rf'{self.y_label}')  # set the y label
-                plt.show()
-
-            if self.save:
-                self._save_data(name=self._build_table_name("System"),
-                                data=self._build_pandas_dataframe(self.Q_arr, total_structure_factor_li))
-            if self.export:
-                self._export_data(name=self._build_table_name("System"),
-                                  data=self._build_pandas_dataframe(self.Q_arr, total_structure_factor_li))
-
-    def run_test(self):
-        """
-        A function that can be used to test the correctness of the structure_factor class
+        Calculates the total structure factor for all the different Q-values
+        of the Q_arr (magnitude of the scattering vector)
         """
-        print('\nStarting structure factor test \n')
-
         self._get_rdf_data()
         self.molar_fractions()
         self.species_densities()
-        print('rho ', self.rho)
-        form_facs = self.atomic_form_factors(10)
-        print('Q: 10,', ' form factors: ', form_facs)
-        avg_atom_f_factor = self.average_atomic_form_factor(10)
-        print('Q: 10', 'average atomic form factor: ', avg_atom_f_factor)
-
-        scattering_scalar = 0.5
-        self.file_to_study = self._get_rdf_data()[2]
-        print(self.file_to_study)
-        self._load_rdf_from_file()
-        s_12, running_integral, integral = self.partial_structure_factor(scattering_scalar)
-        print('partial structure factor', s_12)
-        print('integral: ', integral)
-        print('weight', self.weight_factor(scattering_scalar))
-        plt.figure()
-        plt.plot(self.radii, self.rdf, label='rdf')
-        plt.plot(self.radii, running_integral, label='running integral')
-        plt.legend()
-        plt.show()
+        total_structure_factor_li = []
+        for counter, scattering_scalar in tqdm(enumerate(self.Q_arr), total=len(self.Q_arr),
+                                               desc="Structure factor calculation"):
+            total_structure_factor_li.append(self.total_structure_factor(scattering_scalar))
+        total_structure_factor_li = np.array(total_structure_factor_li)
+
+        properties = Parameters(
+            Property=self.database_group,
+            Analysis=self.analysis_name,
+            data_range=self.data_range,
+            data=[{'q': x, 's(q)': y} for x, y in
+                 zip(self.Q_arr, total_structure_factor_li)],
+            Subject=["System"]
+        )
+        self.update_database(properties)
+
+        if self.plot:
+            self.run_visualization(
+                x_data=self.Q_arr,
+                y_data=total_structure_factor_li,
+                title=f"total structure factor",
+            )
diff --git a/mdsuite/experiment/experiment.py b/mdsuite/experiment/experiment.py
index 24468574..93661d16 100644
--- a/mdsuite/experiment/experiment.py
+++ b/mdsuite/experiment/experiment.py
@@ -15,12 +15,12 @@
 """
 import logging
 from pathlib import Path
-from mdsuite.calculators.computations_dict import dict_classes_db
 from mdsuite.calculators import RunComputation
 from mdsuite.time_series import time_series_dict
 from mdsuite.transformations.transformation_dict import transformations_dict
 from mdsuite.utils.units import units_dict
 from mdsuite.database.experiment_database import ExperimentDatabase
+from mdsuite.visualizer.trajectory_visualizer import SimulationVisualizer
 
 from .add_files import ExperimentAddingFiles
 from .run_module import RunModule
@@ -123,8 +123,6 @@ def __init__(self, project, experiment_name, storage_path='./', time_step=None,
         self.kirkwood_buff_integral_state = False  # Set true if it has been calculated
         self.structure_factor_state = False
 
-        self._results = list(dict_classes_db.keys())
-
         # Memory properties
         self.memory_requirements = {}  # TODO I think this can be removed. - Not until all calcs are under tf Dataset
 
@@ -243,29 +241,6 @@ def _load_or_build(self) -> bool:
     def save_class(self):
         log.warning("Using depreciated method `save_class`!")
 
-    # def map_elements(self, mapping: dict = None):
-    #     """
-    #     Map numerical keys to element names in the Experiment class and database_path.
-    #
-    #     Returns
-    #     -------
-    #     Updates the class
-    #     """
-    #
-    #     if mapping is None:
-    #         log.info("Must provide a mapping")
-    #         return
-    #
-    #     # rename keys in species dictionary
-    #     for item in mapping:
-    #         self.species[mapping[item]] = self.species.pop(item)
-    #
-    #     # rename database_path groups
-    #     db_object = Database(name=os.path.join(self.database_path, "database_path.hdf5"))
-    #     db_object.change_key_names(mapping)
-    #
-    #     self.save_class()  # update the class state
-    #
     def perform_transformation(self, transformation_name, **kwargs):
         """
         Perform a transformation on the experiment.
@@ -293,6 +268,65 @@ def perform_transformation(self, transformation_name, **kwargs):
         transformation_run = transformation(self, **kwargs)
         transformation_run.run_transformation()  # perform the transformation
 
+    def run_visualization(
+            self,
+            species: list = None,
+            molecules: bool = False,
+            unwrapped: bool = False,
+            starting_configuration: int = None):
+        """
+        Perform a trajectory visualization.
+
+        Parameters
+        ----------
+        species : list
+                A list of species of molecules to study.
+        molecules : bool
+                If true, molecule groups will be used.
+        unwrapped : bool
+                If true, unwrapped coordinates will be visualized.
+        starting_configuration : int
+                Starting configuration for the visualizer.
+
+        Returns
+        -------
+        Displays a visualization app.
+        """
+        if molecules:
+            if species is None:
+                species = list(self.species)
+        if species is None:
+            species = list(self.species)
+
+        visualizer = SimulationVisualizer(
+            self, species=species, molecules=molecules, unwrapped=unwrapped,
+            number_of_configurations=self.number_of_configurations)
+        visualizer.run_app(starting_configuration=starting_configuration)
+
+    # def map_elements(self, mapping: dict = None):
+    #     """
+    #     Map numerical keys to element names in the Experiment class and database_path.
+    #
+    #     Returns
+    #     -------
+    #     Updates the class
+    #     """
+    #
+    #     if mapping is None:
+    #         log.info("Must provide a mapping")
+    #         return
+    #
+    #     # rename keys in species dictionary
+    #     for item in mapping:
+    #         self.species[mapping[item]] = self.species.pop(item)
+    #
+    #     # rename database_path groups
+    #     db_object = Database(name=os.path.join(self.database_path, "database_path.hdf5"))
+    #     db_object.change_key_names(mapping)
+    #
+    #     self.save_class()  # update the class state
+    #
+
     #
     # def print_class_attributes(self):
     #     """
diff --git a/mdsuite/project/project.py b/mdsuite/project/project.py
index c4b466bb..5251943f 100644
--- a/mdsuite/project/project.py
+++ b/mdsuite/project/project.py
@@ -17,23 +17,17 @@
 """
 from __future__ import annotations
 import logging
-import os
-import pickle
 from datetime import datetime
 from pathlib import Path
 from typing import Union
-import shutil
-from mdsuite.experiment import Experiment
-from mdsuite.utils.meta_functions import DotDict, simple_file_read, find_item
+from mdsuite.utils.meta_functions import DotDict
 from mdsuite.calculators import RunComputation
 from mdsuite.database.project_database import ProjectDatabase
 import mdsuite.database.scheme as db
-from dataclasses import make_dataclass
-
-from typing import TYPE_CHECKING, Dict, List
+from mdsuite.experiment import Experiment
+from mdsuite.utils import config
 
-if TYPE_CHECKING:
-    from mdsuite.experiment import Experiment
+from typing import Dict
 
 log = logging.getLogger(__name__)
 
@@ -42,9 +36,11 @@ class Project(ProjectDatabase):
     """
     Class for the main container of all experiments.
 
-    The Project class acts as the encompassing class for analysis with MDSuite. It contains all method required to add
-    and analyze new experiments. These experiments may then be compared with one another quickly. The state of the class
-    is saved and updated after each operation in order to retain the most current state of the analysis.
+    The Project class acts as the encompassing class for analysis with MDSuite.
+    It contains all method required to add and analyze new experiments. These
+    experiments may then be compared with one another quickly. The state of the
+    class is saved and updated after each operation in order to retain the
+    most current state of the analysis.
 
     Attributes
     ----------
@@ -55,9 +51,9 @@ class Project(ProjectDatabase):
             A short description of the project
 
     storage_path : str
-            Where to store the tensor_values and databases. This may not simply be the current directory if the
-            databases are expected to be quite large.
-
+            Where to store the tensor_values and databases. This may not simply
+            be the current directory if the databases are expected to be
+            quite large.
     experiments : dict
             A dict of class objects. Class objects are instances of the experiment class for different
             experiments.
@@ -67,17 +63,18 @@ def __init__(self, name: str = None, storage_path: str = "./", description: str
         """
         Project class constructor
 
-        The constructor will check to see if the project already exists, if so, it will load the state of each of the
-        classes so that they can be used again. If the project is new, the constructor will build the necessary file
-        structure for the project.
+        The constructor will check to see if the project already exists, if so,
+        it will load the state of each of the classes so that they can be used
+        again. If the project is new, the constructor will build the necessary
+        file structure for the project.
 
         Parameters
         ----------
         name : str
                 The name of the project.
         storage_path : str
-                Where to store the tensor_values and databases. This should be a place with sufficient storage space
-                for the full analysis.
+                Where to store the tensor_values and databases. This should be
+                a place with sufficient storage space for the full analysis.
         """
         super().__init__()
         if name is None:
@@ -122,7 +119,8 @@ def add_experiment(self, experiment: str = None, timestep: float = None, tempera
         Parameters
         ----------
         data:
-            data that should be added to the experiment. If dict, {file:<file>, format:<format>}
+            data that should be added to the experiment. If dict,
+            {file:<file>, format:<format>}
         active: bool, default = True
                 Activate the experiment when added
         cluster_mode : bool
@@ -132,7 +130,8 @@ def add_experiment(self, experiment: str = None, timestep: float = None, tempera
         timestep : float
                 Timestep used during the simulation.
         temperature : float
-                Temperature the simulation was performed at and is to be used in calculation.
+                Temperature the simulation was performed at and is to be used
+                in calculation.
         units : str
                 LAMMPS units used
         """
@@ -197,11 +196,12 @@ def activate_experiments(self, names: Union[str, list]):
 
         Parameters
         ----------
-        names: Name or list of names of experiments that should be instantiated and loaded into self.experiments
+        names: Name or list of names of experiments that should be instantiated
+               and loaded into self.experiments.
 
         Returns
         -------
-
+        Updates the class state.
         """
 
         if isinstance(names, str):
@@ -215,7 +215,8 @@ def disable_experiments(self, names: Union[str, list]):
 
         Parameters
         ----------
-        names: Name or list of names of experiments that should be instantiated and loaded into self.experiments
+        names: Name or list of names of experiments that should be instantiated
+               and loaded into self.experiments
 
         Returns
         -------
@@ -230,16 +231,19 @@ def disable_experiments(self, names: Union[str, list]):
 
     def add_data(self, data_sets: dict, file_format='lammps_traj'):
         """
-        Add data to an experiment. This is a method so that parallelization is possible amongst data addition to
-        different experiments at the same time.
+        Add data to an experiment. This is a method so that parallelization is
+        possible amongst data addition to different experiments at the same
+        time.
 
         Parameters
         ----------
         data_sets: dict
-            Dictionary containing the name of the experiment as key and the data path as value
+            Dictionary containing the name of the experiment as key and the
+            data path as value
         file_format: dict or str
-            Dictionary containing the name of the experiment as key and the file_format as value.
-            Alternativly only a string of the file_format if all files have the same format.
+            Dictionary containing the name of the experiment as key and the
+            file_format as value. Alternativly only a string of the
+            file_format if all files have the same format.
 
         Returns
         -------
@@ -278,7 +282,9 @@ def load(self) -> RunComputation:
             if called, return List[db.Computation]
 
         """
-        return RunComputation(experiments=[x for x in self.experiments.values()], load_data=True)
+        return RunComputation(
+            experiments=[x for x in self.experiments.values()], load_data=True
+        )
 
     # def get_results(self, key_to_find):
     #     """
diff --git a/mdsuite/project_methods/compare_properties.py b/mdsuite/project_methods/compare_properties.py
deleted file mode 100644
index 16dbd3c3..00000000
--- a/mdsuite/project_methods/compare_properties.py
+++ /dev/null
@@ -1,31 +0,0 @@
-"""
-This program and the accompanying materials are made available under the terms of the
-Eclipse Public License v2.0 which accompanies this distribution, and is available at
-https://www.eclipse.org/legal/epl-v20.html
-
-SPDX-License-Identifier: EPL-2.0
-
-Copyright Contributors to the MDSuite Project.
-
-Methods for the project class to use
-
-Summary
--------
-"""
-
-
-class CompareProperties:
-    """
-    Methods used by the project class to compare physical properties of the experiments
-    """
-
-    def __init__(self):
-        """
-        Python constructor
-        """
-
-        self.x_data = None
-        self.y_data_property = None
-        self.y_data = None
-
-        raise NotImplementedError
diff --git a/mdsuite/transformations/map_molecules.py b/mdsuite/transformations/map_molecules.py
index 5e04fa5a..91feed6e 100644
--- a/mdsuite/transformations/map_molecules.py
+++ b/mdsuite/transformations/map_molecules.py
@@ -100,7 +100,8 @@ def _get_molecular_mass(self, species_dict: dict) -> float:
         Parameters
         ----------
         species_dict : dict
-                Dictionary of species information along with number of species present in the system.
+                Dictionary of species information along with number of species
+                present in the system.
         Returns
         -------
         mass : float
@@ -243,7 +244,8 @@ def _build_indices_dict(self, indices: List[int], species: List[str]) -> dict:
         Returns
         -------
         group_dict : dict
-                A dictionary of atoms and indices that specify that indices of this species is in a molecule.
+                A dictionary of atoms and indices that specify that indices of
+                this species is in a molecule.
         """
         indices_dict = {}
         lengths = []
@@ -276,5 +278,7 @@ def run_transformation(self):
         self._build_configuration_graphs()
         self._get_molecule_indices()
         self._map_molecules()
-        self.experiment.perform_transformation('WrapCoordinates', species=[item for item in self.molecules])
+        self.experiment.perform_transformation(
+            'WrapCoordinates', species=[item for item in self.molecules]
+        )
         self.experiment.save_class()
diff --git a/mdsuite/utils/__init__.py b/mdsuite/utils/__init__.py
index 109c33a7..94c7034f 100644
--- a/mdsuite/utils/__init__.py
+++ b/mdsuite/utils/__init__.py
@@ -9,3 +9,7 @@
 
 Python __init__ file for the utils package
 """
+
+from .config import config
+
+__all__ = ["config"]
diff --git a/mdsuite/utils/config.py b/mdsuite/utils/config.py
new file mode 100644
index 00000000..bec03070
--- /dev/null
+++ b/mdsuite/utils/config.py
@@ -0,0 +1,25 @@
+"""
+This program and the accompanying materials are made available under the terms of the
+Eclipse Public License v2.0 which accompanies this distribution, and is available at
+https://www.eclipse.org/legal/epl-v20.html
+SPDX-License-Identifier: EPL-2.0
+
+Copyright Contributors to the Zincware Project.
+
+Description: Configuration for MDSuite
+"""
+from dataclasses import dataclass
+
+
+@dataclass
+class Config:
+    """Collection of MDSuite configurations"""
+    jupyter: bool = False
+
+
+config = Config()
+
+try:
+    config.jupyter = get_ipython().__class__.__name__ == "ZMQInteractiveShell"
+except NameError:
+    pass
diff --git a/mdsuite/utils/tensorflow/helpers.py b/mdsuite/utils/tensorflow/helpers.py
new file mode 100644
index 00000000..8173f76d
--- /dev/null
+++ b/mdsuite/utils/tensorflow/helpers.py
@@ -0,0 +1,27 @@
+"""
+This program and the accompanying materials are made available under the terms of the
+Eclipse Public License v2.0 which accompanies this distribution, and is available at
+https://www.eclipse.org/legal/epl-v20.html
+SPDX-License-Identifier: EPL-2.0
+
+Copyright Contributors to the Zincware Project.
+
+Description: TensorFlow helper functions
+"""
+
+import tensorflow as tf
+
+
+def triu_mask(n, k=0, m=None):
+    """Compute the triu mask"""
+    if m is None:
+        m = n
+    bool_mat = tf.ones((n, m), dtype=tf.bool)
+    # Just construct a boolean true matrix the size of one timestep
+    if k == 0:
+        return tf.linalg.band_part(bool_mat, 0, -1)
+    return ~tf.linalg.band_part(bool_mat, tf.cast(-1, dtype=tf.int32), k - 1)
+
+def triu_indices(n, k=0, m=None):
+    """Replicate of np.triu_mask"""
+    return tf.where(triu_mask(n, k, m))
diff --git a/mdsuite/utils/tensorflow/layers.py b/mdsuite/utils/tensorflow/layers.py
new file mode 100644
index 00000000..550f89ae
--- /dev/null
+++ b/mdsuite/utils/tensorflow/layers.py
@@ -0,0 +1,82 @@
+"""
+This program and the accompanying materials are made available under the terms of the
+Eclipse Public License v2.0 which accompanies this distribution, and is available at
+https://www.eclipse.org/legal/epl-v20.html
+SPDX-License-Identifier: EPL-2.0
+
+Copyright Contributors to the Zincware Project.
+
+Description:
+"""
+import tensorflow as tf
+from .helpers import triu_indices as compute_triu
+
+
+class NLLayer(tf.keras.layers.Layer):
+    """Convert positions to an r_ij distance matrix"""
+
+    def __init__(self, dense: bool = True, **kwargs):
+        """
+        Parameters
+        ----------
+        dense: bool
+            Return the flat_rij or a dense r_ij
+        """
+        super().__init__(**kwargs)
+        self.dense = dense
+
+    def call(self, inputs, *args, **kwargs):
+        """Convert positions to r_ij distance matrix
+
+        Parameters
+        ----------
+        inputs: dict
+            containing the keys positions and cell with e.g., positions shape (None, n_atoms, 3)
+
+        Returns
+        -------
+
+        flat_rij, triu_indices, n_atoms: tf.Tensor, tf.Tensor, tf.Tenosr
+            The distances flattend out, of shape (x, 3) and the corresponding indices in the r_ij matrix
+            of shape (x, 2) as well as the number of atoms
+
+        """
+        positions = tf.cast(inputs['positions'], self.dtype)
+        cell = tf.cast(inputs['cell'], self.dtype)
+
+        n_atoms = tf.shape(positions)[1]
+        triu_indices = compute_triu(n_atoms, k=1)
+
+        flat_rij = tf.gather(
+            positions, triu_indices[:, 0], axis=1
+        ) - tf.gather(positions, triu_indices[:, 1], axis=1)
+
+        cell = tf.linalg.diag_part(cell)
+
+        flat_rij -= tf.math.rint(flat_rij / cell[:, None]) * cell[:, None]
+
+        if self.dense:
+            def to_dense(flat_rij):
+                """Convert the flattened output to a dense r_ij matrix
+
+                Parameters
+                ----------
+                flat_rij: tf.Tensor
+                    The flat r_ij matrix
+
+                Returns
+                -------
+
+                r_ij: tf.Tensor
+                    A dense r_ij tensor
+
+                """
+                r_ij = tf.scatter_nd(
+                    indices=triu_indices, updates=flat_rij, shape=(n_atoms, n_atoms, 3)
+                )
+                r_ij -= tf.transpose(r_ij, (1, 0, 2))
+
+                return r_ij
+
+            return tf.vectorized_map(to_dense, flat_rij)
+        return flat_rij, triu_indices, n_atoms
diff --git a/mdsuite/project_methods/__init__.py b/mdsuite/visualizer/__init__.py
similarity index 80%
rename from mdsuite/project_methods/__init__.py
rename to mdsuite/visualizer/__init__.py
index 45b15aa7..1633feeb 100644
--- a/mdsuite/project_methods/__init__.py
+++ b/mdsuite/visualizer/__init__.py
@@ -2,8 +2,9 @@
 This program and the accompanying materials are made available under the terms of the
 Eclipse Public License v2.0 which accompanies this distribution, and is available at
 https://www.eclipse.org/legal/epl-v20.html
-
 SPDX-License-Identifier: EPL-2.0
 
-Copyright Contributors to the MDSuite Project.
+Copyright Contributors to the Zincware Project.
+
+Description: 
 """
diff --git a/mdsuite/visualizer/d2_data_visualization.py b/mdsuite/visualizer/d2_data_visualization.py
new file mode 100644
index 00000000..e2671eb1
--- /dev/null
+++ b/mdsuite/visualizer/d2_data_visualization.py
@@ -0,0 +1,88 @@
+"""
+Copyright Contributors to the Zincware Project.
+
+Description: Visualize the output of a calculator.
+"""
+import numpy as np
+from bokeh.plotting import figure, show
+from bokeh.layouts import gridplot
+from bokeh.io import output_notebook, output_file
+from bokeh.models import HoverTool
+from typing import Union, List
+from mdsuite.utils import config
+
+
+class DataVisualizer2D:
+    """
+    Visualizer for two-dimensional data.
+    """
+
+    def __init__(self, title: str):
+        """
+        Constructor for the data visualizer.
+
+        Parameters
+        ----------
+        title : str
+                title of the plot.
+        """
+        if config.jupyter:
+            output_notebook()
+        else:
+            output_file(f"{title}.html", title=title)
+
+    def construct_plot(
+            self,
+            x_data: Union[list, np.ndarray],
+            y_data: Union[list, np.ndarray],
+            x_label: str,
+            y_label: str,
+            title: str,
+            layouts: List= None
+    ) -> figure:
+        """
+        Generate a plot.
+
+        Parameters
+        ----------
+        x_data : Union[list, np.ndarray, tf.Tensor]
+                data to plot along the x axis.
+        y_data : Union[list, np.ndarray, tf.Tensor]
+                data to plot along the y axis.
+        x_label : str
+                label for the x axis
+        y_label : str
+                label of the y axis.
+        title : str
+                name of the specific plot.
+        Returns
+        -------
+        figure : figure
+                A bokeh figure object.
+        """
+        fig = figure(
+            x_axis_label=x_label, y_axis_label=y_label
+        )
+        fig.line(x_data, y_data, legend_label=title)
+        fig.add_tools(HoverTool())
+        if layouts is not None:
+            for item in layouts:
+                fig.add_layout(item)
+
+        return fig
+
+    def grid_show(self, figures: list):
+        """
+        Display a list of figures in a grid.
+
+        Parameters
+        ----------
+        figures : list
+                A list of figures to display.
+
+        Returns
+        -------
+
+        """
+        grid = gridplot(figures, ncols=3, sizing_mode='scale_both')
+        show(grid)
diff --git a/mdsuite/visualizer/d3_data_visualizer.py b/mdsuite/visualizer/d3_data_visualizer.py
new file mode 100644
index 00000000..a3219e05
--- /dev/null
+++ b/mdsuite/visualizer/d3_data_visualizer.py
@@ -0,0 +1,67 @@
+"""
+Visualizer for three dimensional data.
+"""
+import open3d as o3d
+import open3d.visualization.gui as gui
+import numpy as np
+
+
+class DataVisualizer3D:
+    """
+    Class for the visualizer of three dimensional data.
+    """
+
+    def __init__(self,
+                 data,
+                 title: str):
+        """
+        Constructor for the data visualizer.
+
+        Parameters
+        ----------
+        data : np.ndarray
+                data to plot.
+        title : str
+                title of the plot.
+        """
+        self.data =data
+        self.title = title
+
+        self.point_cloud = o3d.geometry.PointCloud()
+        self.point_cloud.points = o3d.utility.Vector3dVector(self.data)
+
+        self._build_app()
+
+    def _build_app(self):
+        """
+        Build the app window and update the class.
+
+        Returns
+        -------
+        Updates the class.
+        """
+        self.app = gui.Application.instance
+        self.app.initialize()
+
+        self.vis = o3d.visualization.O3DVisualizer("MDSuite Visualizer",
+                                                   1024,
+                                                   768)
+        self.vis.show_settings = True
+        # Add meshes
+        self.vis.reset_camera_to_default()
+
+        self.app.add_window(self.vis)
+
+    def plot(self):
+        """
+        Plot the data.
+
+        Returns
+        -------
+
+        """
+        #self.vis.draw_geometries([self.point_cloud])
+        self.vis.add_geometry("Points", self.point_cloud)
+
+        self.vis.reset_camera_to_default()
+        self.app.run()
diff --git a/mdsuite/visualizer/trajectory_visualizer.py b/mdsuite/visualizer/trajectory_visualizer.py
new file mode 100644
index 00000000..25a14aa9
--- /dev/null
+++ b/mdsuite/visualizer/trajectory_visualizer.py
@@ -0,0 +1,209 @@
+"""
+This program and the accompanying materials are made available under the terms
+of the Eclipse Public License v2.0 which accompanies this distribution, and is
+available at https://www.eclipse.org/legal/epl-v20.html.
+SPDX-License-Identifier: EPL-2.0
+
+Copyright Contributors to the Zincware Project.
+
+Description: Visualize a simulation.
+"""
+from mdsuite.database.simulation_database import Database
+from PIL.ImageColor import getcolor
+import importlib.resources
+import json
+import numpy as np
+from mdsuite.utils.meta_functions import join_path
+import open3d as o3d
+import open3d.visualization.gui as gui
+
+
+class SimulationVisualizer:
+    """
+    Visualize a simulation.
+    """
+
+    def __init__(self,
+                 experiment: object,
+                 species: list = None,
+                 molecules: bool = False,
+                 unwrapped: bool = False,
+                 number_of_configurations: int = None):
+        """
+        Constructor for the visualizer.
+
+        Parameters
+        ----------
+        experiment : object
+                 Experiment object from which the visualizer will gather
+                 information.
+        species : list
+                A list of species to visualize.
+        molecules : list
+                If true, molecules will be visualized.
+        unwrapped : bool
+                If true, unwrapped coordinates are studied.
+        """
+        self.counter = 0
+        # Particle information
+        self.experiment = experiment
+        self.database = Database(name=join_path(
+            self.experiment.database_path, "database.hdf5")
+        )
+        self.molecules = molecules
+        self.species = species
+        self.number_of_configurations = number_of_configurations
+        if unwrapped:
+            self.identifier = "Unwrapped_Positions"
+        else:
+            self.identifier = "Positions"
+        self._check_input()  # check inputs and build data structure.
+        self._fill_species_properties()
+        self._build_trajectory()
+
+        # App attributes
+        self.app = None
+        self.vis = None
+        self._build_app()
+
+    def _fill_species_properties(self):
+        """
+        Add colours and sizes to species.
+
+        Returns
+        -------
+        Updates the class attributes.
+        """
+        data_name = "mdsuite.data"
+        json_name = 'PubChemElements_all.json'
+        with importlib.resources.open_text(data_name, json_name) as json_file:
+            pse = json.loads(json_file.read())
+
+        # Try to get the species tensor_values
+        for element in self.species:
+            for entry in pse:
+                if pse[entry][1] == element:
+                    self.data[element]['colour'] = getcolor(
+                        f'#{pse[entry][4]}', 'RGB'
+                    )
+                    self.data[element]['mass'] = float(pse[entry][3]) / 25
+                    self.data[element]['particles'] = self.database.load_data(
+                        path_list=[join_path(element, self.identifier)],
+                        select_slice=np.s_[:]
+                    )
+
+    def _check_input(self):
+        """
+        Check the input of the class and update attributes to defaults if
+        necessary.
+
+        Returns
+        -------
+        Will update the class state if necessary.
+        """
+        self.data = {}
+        for item in self.species:
+            self.data[item] = {}
+
+    def _mesh(self, location: np.ndarray, radius: float, colour: np.ndarray):
+        """
+        Return a mesh object coloured by element.
+        """
+        mesh = o3d.geometry.TriangleMesh.create_sphere(radius=radius,
+                                                       resolution=10)
+        mesh.compute_vertex_normals()
+        mesh.translate(location)
+        mesh.paint_uniform_color(colour)
+
+        return mesh
+
+    def _build_particles(self, configuration: int):
+        """
+        Add the initial atoms to the system.
+
+        Parameters
+        ----------
+        configuration : int
+                Configuration number to load.
+        Returns
+        -------
+        """
+        output = []
+        for element in self.species:
+            colour = np.array(self.data[element]['colour']) / 255
+            radius = self.data[element]['mass']
+            for atom in self.data[element]['particles']:
+                output.append(self._mesh(location=atom[configuration],
+                                         colour=colour,
+                                         radius=radius))
+
+        return output
+
+    def _build_trajectory(self):
+        """
+        Build the simulation array.
+        Returns
+        -------
+        Updates the class state.
+        """
+        self.trajectory = [
+            self._build_particles(i) for i in range(
+                self.number_of_configurations
+            )
+        ]
+
+    def _build_app(self):
+        """
+        Build the app window and update the class.
+
+        Returns
+        -------
+        Updates the class.
+        """
+        self.app = gui.Application.instance
+        self.app.initialize()
+
+        self.vis = o3d.visualization.O3DVisualizer("MDSuite Visualizer",
+                                                   1024,
+                                                   768)
+        self.vis.show_settings = True
+        # Add meshes
+        self.vis.reset_camera_to_default()
+
+        self.app.add_window(self.vis)
+
+    def _updated_scene(self, configuration: int = None):
+        """
+        Update the scene.
+        Returns
+        -------
+
+        """
+        if configuration is None:
+            if self.counter + 1 == self.number_of_configurations:
+                self.counter = 0
+
+            for i in range(len(self.trajectory[self.counter])):
+                self.vis.add_geometry(f"sphere_{i}",
+                                      self.trajectory[self.counter][i])
+            self.counter += 1
+        else:
+            if configuration > self.number_of_configurations - 1:
+                configuration = self.number_of_configurations - 1
+
+            for i in range(len(self.trajectory[configuration])):
+                self.vis.add_geometry(f"sphere_{i}",
+                                      self.trajectory[configuration][i])
+
+    def run_app(self, starting_configuration: int = None):
+        """
+        Run the app.
+
+        Returns
+        -------
+        Launches the app.
+        """
+        self._updated_scene(configuration=starting_configuration)
+        self.vis.reset_camera_to_default()
+
+        self.app.run()
diff --git a/requirements.txt b/requirements.txt
index f2a2a9f4..d9c097ac 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -21,4 +21,6 @@ sqlalchemy>=1.4
 pandas
 IPython
 pandoc
-tensorflow_probability
\ No newline at end of file
+tensorflow_probability
+open3d
+bokeh>=2.4.0
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 74c810e1..45392c2b 100644
--- a/setup.py
+++ b/setup.py
@@ -1,28 +1,9 @@
+"""
+Setup.py module for the MDSuite package.
+"""
 import setuptools
 from os import path
 
-from setuptools.command.develop import develop
-from setuptools.command.install import install
-import subprocess
-import sys
-
-
-class PostDevelopCommand(develop):
-    """Post-installation for development mode."""
-
-    def run(self):
-        develop.run(self)
-        subprocess.check_call([sys.executable, "-m", "pip", "install", "h5py", "--upgrade", "--no-dependencies"])
-
-
-class PostInstallCommand(install):
-    """Post-installation for installation mode."""
-
-    def run(self):
-        install.run(self)
-        subprocess.check_call([sys.executable, "-m", "pip", "install", "h5py", "--upgrade", "--no-dependencies"])
-
-
 here = path.abspath(path.dirname(__file__))
 with open(path.join(here, 'requirements.txt')) as requirements_file:
     # Parse requirements.txt, ignoring any commented-out lines.
@@ -52,8 +33,4 @@ def run(self):
     include_package_data=True,
     python_requires='>=3.7',
     install_requires=requirements,
-    cmdclass={'install': PostInstallCommand, 'develop': PostDevelopCommand}
-    # force install of the newest h5py after the dependencies are installed
-    # See https://github.com/tensorflow/tensorflow/issues/47303 for further information
-    # TODO remove if tensorflow supports h5py > 3
 )


Test patch:
diff --git a/CI/integration_tests/NaCl/tmp_nacl_liquid.py b/CI/integration_tests/NaCl/tmp_nacl_liquid.py
index e1bfe1b9..096f0f6b 100644
--- a/CI/integration_tests/NaCl/tmp_nacl_liquid.py
+++ b/CI/integration_tests/NaCl/tmp_nacl_liquid.py
@@ -19,6 +19,7 @@
 
 # TODO run in temporary directory!
 
+
 class TestLiquidNaCl(unittest.TestCase):
     """
     Perform an integration test on liquid NaCl.
diff --git a/CI/integration_tests/calculators/test_green_kubo_self_diffusion_coefficients.py b/CI/integration_tests/calculators/test_green_kubo_self_diffusion_coefficients.py
index 707f8ed6..3e53b786 100644
--- a/CI/integration_tests/calculators/test_green_kubo_self_diffusion_coefficients.py
+++ b/CI/integration_tests/calculators/test_green_kubo_self_diffusion_coefficients.py
@@ -66,10 +66,11 @@ def test_gksd_project(traj_files, true_values, tmp_path):
     """Test the green_kubo_self_diffusion called from the project class"""
     os.chdir(tmp_path)
     project = mds.Project()
-    project.add_experiment("NaCl", data=traj_files[0], timestep=0.002, temperature=1400)
-
-    project.run.GreenKuboSelfDiffusionCoefficients(plot=False)
+    project.add_experiment(
+        "NaCl", data=traj_files[0], timestep=0.002, temperature=1400
+    )
 
+    project.run.GreenKuboSelfDiffusionCoefficients(plot=True)
     data_dict = project.load.GreenKuboSelfDiffusionCoefficients()["NaCl"][0].data_dict
 
     np.testing.assert_array_almost_equal(data_dict['x'], true_values['x'])