Repository: yaml2sbml-dev/yaml2sbml (language: python)

Issue:
Definition of Model ID for use with pyPESTO and AMICI
Currently when a new model is created after using this wonderful tool the model field read as
`<model>`

This leads (when used together with pyPESTO and AMICI) to the generation of a random name which is used as name for the model in the amici folder, e.g. `amici_models/tmpiouv7fa5`.
The problem here is that every time one will load the model, the random name is changing, therefore the already compiled folder is not detected, and the model is compiled again.

This has a very simple fix. During the model creation you can add the model ID, such that the final sbml model will contain
`<model id="MyModelName" name="MyModelName">`
instead of
`<model>`

I do not know how this behaves for other tools, but for pyPESTO/AMICI this would be a valuable fix.


Discussion / hints:

Fix patch:
diff --git a/.codacy.yml b/.codacy.yml
index 8e3aa2f..a6cb3de 100644
--- a/.codacy.yml
+++ b/.codacy.yml
@@ -2,3 +2,4 @@
 exclude_paths:
   - 'README.md'
   - 'format_documentation.md'
+  - 'doc/examples/README.md'
diff --git a/.flake8 b/.flake8
new file mode 100644
index 0000000..1eb19f0
--- /dev/null
+++ b/.flake8
@@ -0,0 +1,13 @@
+########################
+# Flake8 Configuration #
+########################
+
+[flake8]
+
+ignore =
+    # print
+    T001
+
+per-file-ignores =
+    # imported but unused
+    */__init__.py:F401
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000..f416c01
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,29 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**What are the steps to reproduce the bug?**
+A clear and concise description of how to reproduce the bug.
+
+**Which output did you expect?**
+A clear and concise description of what you expected to happen.
+
+**What happened instead?**
+A clear and concise description of what you observed instead.
+
+**What could be a solution?**
+If possible, a clear and concise description of a possible solution to the problem.
+
+**Environment**
+ - Operating system, e.g. Ubuntu 20.04, macOS 11.2.1  
+ - `yaml2sbml` version (`pip show yaml2sbml`), e.g. 0.2.1
+ - Python version (`python3 -V`)
+ - If relevant: versions of other tools involved (e.g. `libsbml)
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..565d3f6
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,100 @@
+name: CI
+
+on: [pull_request]
+
+jobs:
+  base:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: [3.6, 3.9]
+
+    steps:
+    - name: Check out repository
+      uses: actions/checkout@v2
+
+    - name: Prepare python ${{ matrix.python-version }}
+      uses: actions/setup-python@v1
+      with:
+        python-version: ${{ matrix.python-version }}
+
+    - name: Cache
+      uses: actions/cache@v1
+      with:
+        path: ~/.cache/pip
+        key: ${{ runner.os }}-${{ matrix.python-version }}-ci-base
+
+    - name: Install tox
+      run: pip install tox
+
+    - name: Run tests
+      run: tox
+
+    - name: Codecov
+      uses: codecov/codecov-action@v1
+      with:
+        token: ${{ secrets.CODECOV_TOKEN }}
+        file: ./coverage.xml
+
+  notebooks:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: [3.9]
+
+    steps:
+    - name: Check out repository
+      uses: actions/checkout@v2
+
+    - name: Prepare python ${{ matrix.python-version }}
+      uses: actions/setup-python@v1
+      with:
+        python-version: ${{ matrix.python-version }}
+
+    - name: Cache
+      uses: actions/cache@v1
+      with:
+        path: ~/.cache/pip
+        key: ${{ runner.os }}-${{ matrix.python-version }}-ci-notebooks
+
+    - name: Install apt dependencies
+      run: |
+        .github/workflows/install_deps.sh amici
+
+    - name: Install tox
+      run: pip install tox
+
+    - name: Test notebooks
+      run: tox -e notebooks
+
+  doc:
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: [3.9]
+
+    steps:
+    - name: Check out repository
+      uses: actions/checkout@v2
+
+    - name: Prepare python ${{ matrix.python-version }}
+      uses: actions/setup-python@v1
+      with:
+        python-version: ${{ matrix.python-version }}
+
+    - name: Cache
+      uses: actions/cache@v1
+      with:
+        path: ~/.cache/pip
+        key: ${{ runner.os }}-${{ matrix.python-version }}-ci-doc
+
+    - name: Install apt dependencies
+      run: |
+        .github/workflows/install_deps.sh doc
+
+    - name: Install tox
+      run: pip install tox
+
+    - name: Test documentation build
+      run: tox -e doc
+
diff --git a/.github/workflows/ci_push.yml b/.github/workflows/ci_push.yml
deleted file mode 100644
index ad37df8..0000000
--- a/.github/workflows/ci_push.yml
+++ /dev/null
@@ -1,49 +0,0 @@
-name: CI
-
-on: [push]
-
-jobs:
-  base:
-    runs-on: ubuntu-latest
-    strategy:
-      matrix:
-        python-version: [3.6, 3.7, 3.8, 3.9]
-
-    steps:
-    - name: Check out repository
-      uses: actions/checkout@v2
-
-    - name: Prepare python ${{ matrix.python-version }}
-      uses: actions/setup-python@v1
-      with:
-        python-version: ${{ matrix.python-version }}
-
-    - name: Cache
-      uses: actions/cache@v1
-      with:
-        path: ~/.cache/pip
-        key: ${{ runner.os }}-${{ matrix.python-version }}-ci-base
-
-    - name: Install dependencies
-      run: |
-        .github/workflows/install_deps.sh amici
-        pip install .
-
-    - name: flake8
-      run: |
-        ./run_flake8.sh
-
-    - name: Run tests
-      timeout-minutes: 5
-      run: |
-        python -m pytest --nbmake --cov=yaml2sbml --cov-report=xml tests -s
-
-    - name: Test CLI
-      run: |
-        ./tests/test_CLI.sh
-
-    - name: Codecov
-      uses: codecov/codecov-action@v1
-      with:
-        token: ${{ secrets.CODECOV_TOKEN }}
-        file: ./coverage.xml
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 0000000..e972fcc
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,28 @@
+name: Deploy
+
+on:
+  release:
+    types: [published]
+
+jobs:
+  deploy:
+    runs-on: ubuntu-latest
+
+    steps:
+    - name: Check out repository
+      uses: actions/checkout@v2
+    - name: Set up python
+      uses: actions/setup-python@v1
+      with:
+        python-version: 3.8
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip
+        pip install setuptools wheel twine
+    - name: Build and publish
+      env:
+        TWINE_USERNAME: ${{ secrets.PYPI_USERNAME }}
+        TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
+      run: |
+        python setup.py sdist bdist_wheel
+        twine upload dist/*
diff --git a/.github/workflows/install_deps.sh b/.github/workflows/install_deps.sh
index 3eca077..93b16cb 100755
--- a/.github/workflows/install_deps.sh
+++ b/.github/workflows/install_deps.sh
@@ -1,16 +1,5 @@
 #!/bin/sh
 
-# Install CI dependencies
-
-# required for wheel packages
-pip install wheel
-
-# required for tests
-pip install pytest pytest-cov
-
-pip install flake8
-pip install nbmake
-
 # iterate over optional dependencies
 for par in "$@"; do
   case $par in
@@ -18,9 +7,11 @@ for par in "$@"; do
       # for amici
       sudo apt-get install \
         swig3.0 libatlas-base-dev libhdf5-serial-dev
+    ;;
 
-      # install latest amici release
-      pip install amici
+    doc)
+      # documentation
+      sudo apt-get install pandoc
     ;;
 
     *)
diff --git a/.gitignore b/.gitignore
index 395603f..09b9f02 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,9 +1,14 @@
 __pycache__/
+*.egg-info/
+
+# PyCharm
 .idea/
 
 # files generated by tests
 yaml2sbml/tests/test_yaml2sbml/sbml_test.xml
 
+# Documentation build
+/doc/build
 
 ### Files generated when running the notebooks (AMICI + PEtab files)
 
@@ -19,24 +24,20 @@ yaml2sbml/tests/test_yaml2sbml/sbml_test.xml
 ./doc/examples/Lotka_Volterra/Lotka_Volterra_python/Lotka_Volterra_PEtab/*
 !./doc/examples/Lotka_Volterra/Lotka_Volterra_python/Lotka_Volterra_PEtab/measurement_table.tsv
 
-
 ## Lotka Volterra CLI
 ./doc/examples/Lotka_Volterra/Lotka_Volterra_CLI/Lotka_Volterra_PEtab/*
 ./doc/examples/Lotka_Volterra/Lotka_Volterra_CLI/Lotka_Volterra_PEtab.yml
 ./doc/examples/Lotka_Volterra/Lotka_Volterra_CLI/Lotka_Volterra_PEtab/*.xml
 
-
 ## Lotka Volterra Model Editor
 ./doc/examples/Lotka_Volterra/Lotka_Volterra_Model_Editor/*.yaml
 ./doc/examples/Lotka_Volterra/Lotka_Volterra_Model_Editor/*.xml
 ./doc/examples/Lotka_Volterra/Lotka_Volterra_Model_Editor/Lotka_Volterra_PEtab
 
-
 ## Sorensen example
 ./doc/examples/Sorensen/*.xml
 ./doc/examples/Sorensen/amici_models
 
-
 ## FSP examples
 ./doc/examples/Finite_State_Projection/amici_models
 ./doc/examples/Finite_State_Projection/*.xml
@@ -48,8 +49,6 @@ yaml2sbml/tests/test_yaml2sbml/sbml_test.xml
 
 ./doc/examples/Format_Features/*.xml
 
-
-
 ### JupyterNotebooks ###
 # gitignore template for Jupyter Notebooks
 # website: http://jupyter.org/
@@ -92,33 +91,6 @@ Network Trash Folder
 Temporary Items
 .apdisk
 
-### PyCharm ###
-# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
-# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
-
-# User-specific stuff
-.idea/**/workspace.xml
-.idea/**/tasks.xml
-.idea/**/usage.statistics.xml
-.idea/**/dictionaries
-.idea/**/shelf
-
-# Generated files
-.idea/**/contentModel.xml
-
-# Sensitive or high-churn files
-.idea/**/dataSources/
-.idea/**/dataSources.ids
-.idea/**/dataSources.local.xml
-.idea/**/sqlDataSources.xml
-.idea/**/dynamic.xml
-.idea/**/uiDesigner.xml
-.idea/**/dbnavigator.xml
-
-# Gradle
-.idea/**/gradle.xml
-.idea/**/libraries
-
 # Gradle and Maven with auto-import
 # When using Gradle or Maven with auto-import, you should exclude module files,
 # since they will be recreated, and may cause churn.  Uncomment if using
@@ -135,9 +107,6 @@ Temporary Items
 # CMake
 cmake-build-*/
 
-# Mongo Explorer plugin
-.idea/**/mongoSettings.xml
-
 # File-based project format
 *.iws
 
@@ -150,47 +119,19 @@ out/
 # JIRA plugin
 atlassian-ide-plugin.xml
 
-# Cursive Clojure plugin
-.idea/replstate.xml
-
 # Crashlytics plugin (for Android Studio and IntelliJ)
 com_crashlytics_export_strings.xml
 crashlytics.properties
 crashlytics-build.properties
 fabric.properties
 
-# Editor-based Rest Client
-.idea/httpRequests
-
-# Android studio 3.1+ serialized cache file
-.idea/caches/build_file_checksums.ser
-
-### PyCharm Patch ###
-# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721
-
-# *.iml
-# modules.xml
-# .idea/misc.xml
-# *.ipr
-
-# Sonarlint plugin
-# https://plugins.jetbrains.com/plugin/7973-sonarlint
-.idea/**/sonarlint/
-
-# SonarQube Plugin
-# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin
-.idea/**/sonarIssues.xml
-
-# Markdown Navigator plugin
-# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced
-.idea/**/markdown-navigator.xml
-.idea/**/markdown-navigator-enh.xml
-.idea/**/markdown-navigator/
+# vim
+*.swp
 
-# Cache file creation bug
-# See https://youtrack.jetbrains.com/issue/JBR-2257
-.idea/$CACHE_FILE$
+# tox
+/.tox
 
-# CodeStream plugin
-# https://plugins.jetbrains.com/plugin/12206-codestream
-.idea/codestream.xml
+# coverage
+/.coverage
+/htmlcov
+/coverage.xml
diff --git a/.readthedocs.yml b/.readthedocs.yml
new file mode 100644
index 0000000..f9ac3e5
--- /dev/null
+++ b/.readthedocs.yml
@@ -0,0 +1,24 @@
+# .readthedocs.yml
+# readthedocs configuration file
+# see https://docs.readthedocs.io/en/stable/config-file/v2.html for details
+
+# Format version
+version: 2
+
+# Build documentation in the docs/ directory with Sphinx
+sphinx:
+  configuration: doc/conf.py
+
+# Python requirements
+python:
+  version: 3.7
+  install:
+    - method: pip
+      path: .
+      extra_requirements:
+        - doc
+
+# What to build
+formats:
+  - htmlzip
+  - pdf
diff --git a/CONTRIBUTE.rst b/CONTRIBUTE.rst
new file mode 100644
index 0000000..6a5ec3d
--- /dev/null
+++ b/CONTRIBUTE.rst
@@ -0,0 +1,86 @@
+Contribute
+==========
+
+Documentation
+-------------
+
+To make yaml2sbml easily usable, we try to provide good documentation,
+including code annotation and usage examples.
+The documentation is hosted on
+`yaml2sbml.readthedocs.io <https://yaml2sbml.readthedocs.io>`_
+and updated automatically every time the ``main`` branch is updated.
+To create the documentation locally, first install the requirements via::
+
+    pip install .[doc]
+
+and then compile the documentation via::
+
+    cd doc
+    make html
+
+Test environment
+----------------
+
+We use the virtual testing tool `tox <https://tox.readthedocs.io/en/latest/>`_
+for all unit tests, format and quality checks and notebooks.
+Its configuration is specified in ``tox.ini``. To run it locally, first
+install::
+
+    pip install tox
+
+and then simply execute::
+
+    tox
+
+To run only selected tests (see ``tox.ini`` for what is tested), use e.g.::
+
+    tox -e pyroma,flake8
+
+For continuous integration testing we use GitHub Actions. All tests are run
+there on pull requests and required to pass. The configuration is specified
+in `.github/workflows/ci.yml`.
+
+Unit tests
+----------
+
+Unit tests are located in the ``tests`` folder. All files starting with
+``test_`` contain tests and are automatically run on GitHub Actions.
+Run them locally via::
+
+    tox -e unittests
+
+which boils mostly down to::
+
+    python3 -m pytest tests
+
+You can also run only specific tests.
+
+Unit tests can be written with `pytest <https://docs.pytest.org/en/latest/>`_
+or `unittest <https://docs.python.org/3/library/unittest.html>`_.
+
+PEP8
+----
+
+We try to respect the `PEP8 <https://www.python.org/dev/peps/pep-0008>`_
+coding standards. We run `flake8 <https://flake8.pycqa.org>`_ as part of the
+tests. The flake8 plugins used are specified in ``tox.ini`` and the flake8
+configuration given in ``.flake8``.
+You can run it locally via::
+
+    tox -e flake8
+
+Workflow
+--------
+
+If you start working on a new feature or a fix, please create an issue on
+GitHub shortly describing the issue and assign yourself.
+
+To get your code merged, please:
+
+1. create a pull request to develop
+2. if not already done in a commit message, use the pull request
+   description to reference and automatically close the respective issue
+   (see https://help.github.com/articles/closing-issues-using-keywords/)
+3. check that all tests pass
+4. check that the documentation is up-to-date
+5. request a code review
diff --git a/INSTALL.rst b/INSTALL.rst
new file mode 100644
index 0000000..1743831
--- /dev/null
+++ b/INSTALL.rst
@@ -0,0 +1,34 @@
+Installation
+============
+
+This package requires Python 3.6 or later. Miniconda_ provides a small
+installation.
+
+Install from PyPI
+-----------------
+
+Install yaml2sbml from PyPI_ via::
+
+    pip install yaml2sbml
+
+Install from GitHub
+-------------------
+
+To work with the latest development version, install yaml2sbml from
+GitHub_ via::
+
+    pip install https://github.com/yaml2sbml-dev/yaml2sbml/archive/develop.zip
+
+or clone the repository and install from local via::
+
+    git clone https://github.com/yaml2sbml-dev/yaml2sbml
+    cd yaml2sbml
+    git checkout develop
+    pip install -e .
+
+where ``-e`` is short for ``--editable`` and links the installed package to
+the current location, such that changes there take immediate effect.
+
+.. _Miniconda: http://conda.pydata.org/miniconda.html
+.. _PyPI: https://pypi.org/project/yaml2sbml
+.. _GitHub: https://github.com/yaml2sbml-dev/yaml2sbml
diff --git a/README.md b/README.md
index 42a9763..d475fb6 100644
--- a/README.md
+++ b/README.md
@@ -1,123 +1,83 @@
-# yaml2sbml
+![yaml2sbml logo](https://github.com/yaml2sbml-dev/yaml2sbml/blob/master/doc/logo/logo_yaml2sbml_long.png?raw=true)
 
-[![Codacy Badge](https://app.codacy.com/project/badge/Grade/dc25c9a84ba54710bbb23a6e08ab5d22)](https://www.codacy.com/manual/martamatos/yaml2sbml/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=yaml2sbml-dev/yaml2sbml&amp;utm_campaign=Badge_Grade)
-![CI](https://github.com/yannikschaelte/yaml2sbml/workflows/CI/badge.svg)
-[![codecov](https://codecov.io/gh/yannikschaelte/yaml2sbml/branch/master/graph/badge.svg?token=AORT3GREQJ)](https://codecov.io/gh/yannikschaelte/yaml2sbml)
+[![CI](https://github.com/yaml2sbml-dev/yaml2sbml/workflows/CI/badge.svg)](https://github.com/yaml2sbml-dev/yaml2sbml/actions)
+[![codecov](https://codecov.io/gh/yaml2sbml-dev/yaml2sbml/branch/master/graph/badge.svg)](https://codecov.io/gh/yaml2sbml-dev/yaml2sbml)
+[![Documentation Status](https://readthedocs.org/projects/yaml2sbml/badge/?version=latest)](https://yaml2sbml.readthedocs.io/en/latest/?badge=latest)
+[![Codacy Badge](https://app.codacy.com/project/badge/Grade/632acdc8d4ef4f50bf69892b8862fd24)](https://www.codacy.com/gh/yaml2sbml-dev/yaml2sbml/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=yaml2sbml-dev/yaml2sbml&amp;utm_campaign=Badge_Grade)
 
 ## Table of contents
 
-* [Introduction](#introduction)
+* [About](#about)
 
 * [Installation](#installation)
 
-* [Usage](#usage)
+* [Getting Started](#getting-started)
 
-  * [Command line interface](#Command_line_interface)
+* [Basic Syntax](#basic-syntax)
 
-  * [Python](#Python)
+* [Contact](#contact)
 
-* [Known issues and limitations](#known-issues-and-limitations)
 
-## Introduction
+## About
 
-yaml2sbml is a small package to convert an ODE model specified in a yaml file into an [**SBML**](http://www.sbml.org/) for ODE simulation and into [**PEtab**](https://github.com/martamatos/yaml2sbml) for parameter fitting. These file formats can be used with other packages.
+`yaml2sbml` is a small package in Python to convert an ODE model specified in a YAML file into 
+[**SBML**](http://www.sbml.org/) for ODE simulation and into 
+[**PEtab**](https://github.com/martamatos/yaml2sbml) for parameter fitting. `yaml2sbml` offers:
 
-* [format_documentation.md](format_documentation.md) provides a documentation of the format of the input yaml. 
 
-* Examples can be found in the `examples` folder.
+* a translator of ODE models specified in YAML into SBML/PEtab via a Python and a command-line interface;
+* a format validator for the input YAML; and
+* a model editor, which provides a simplified interface to generate, import and export YAML models.
 
 ## Installation
 
-To install go to the main folder and do:
-
-```pip install .```
-
-## Requirements
-
- * Python 3.6+
- * python-libsbml>=5.18.0
- * PyYAML>=5.3
- * pandas >= 1.0.1 
- * PEtab >= 0.1.4
-
-#### Requirements files
-
- * `requirements.txt` 
-
-#### How to get Python 3.6+
-
-If you don't have Python 3.6 or higher in your system, the best way to get it is either using pyenv or conda.
-
-With conda you can create a virtual environment with a specific python version. To do so start by installing miniconda if you don't have any sort of conda installed yet, and then create a virtual environment using a specific version of python:
-
-``` conda create -n <virtual_env_name> python=3.7```
-
-To activate the virtual environment do
-
-``` source activate <virtual_env_ńame>```
-
-To install packages use either pip or conda: 
-
-``` conda/pip install <package_name> ```
-
-## Usage
-
-You can either call `yaml2sbml` via its [command line interface](#Command_line_interface) or within your [python code](#Python): 
-
-### Command line interface
-
-#### yaml2sbml command
-
-To convert an ODE model encoded in a yaml file to SBML using the terminal, go to the `yaml2sbml` folder and run:
+`yaml2sbml` can be installed from PyPI with
 
 ```shell
- yaml2sbml <yaml_input_file> <sbml_output_file>
+pip install yaml2sbml
 ```
+For more info see the [docs](https://yaml2sbml.readthedocs.io/en/latest/).
 
-For instance, using the yaml file in the examples folder:
+## Getting Started
 
-```shell
- yaml2sbml ../examples/ode_input1.yaml ../examples/sbml_out.xml
-```
-
-#### yaml2PEtab command
+* The documentation of the tool is hosted on [Read the Docs](https://yaml2sbml.readthedocs.io/en/latest/).
+* The [format documentation](https://yaml2sbml.readthedocs.io/en/latest/format_specification.html) describes the input YAML. 
 
-If you want to generate PEtab parameter, observable and condition tables, additionally to the SBML file using the terminal, go to the `yaml2sbml` folderand run:
+* Jupyter notebooks containing examples can be found under [`doc/examples`](https://github.com/yaml2sbml-dev/yaml2sbml/tree/main/doc/examples).  Most notably:
+    * [Lotka_Volterra.ipynb](https://github.com/yaml2sbml-dev/yaml2sbml/tree/main/doc/examples/Lotka_Volterra/Lotka_Volterra_python/Lotka_Volterra.ipynb) showing the Python package,
+    * [Lotka_Volterra_CLI.ipynb](https://github.com/yaml2sbml-dev/yaml2sbml/tree/main/doc/examples/Lotka_Volterra/Lotka_Volterra_CLI/Lotka_Volterra_CLI.ipynb) showing the command-line interface, and
+    * [Lotka_Volterra_Model_Editor.ipynb](https://github.com/yaml2sbml-dev/yaml2sbml/tree/main/doc/examples/Lotka_Volterra/Lotka_Volterra_Model_Editor/Lotka_Volterra_Model_Editor.ipynb) demonstrates the Model Editor.
 
-```shell
- yaml2petab <yaml_input_file> <petab_output_directory> <model_name>
-```
-
-For instance, again using the yaml file in the examples folder:
-```shell
- yaml2petab ../examples/ode_input1.yaml ../examples/ example_model.xml
-```
+## Basic Syntax
 
 ### Python
 
-Alternatively you can call `yaml2sbml` within your python code via
-
+A YAML model can be translated to SBML/PEtab in Python via
 ```python
 import yaml2sbml
 
-yaml2sbml.yaml2sbml(yaml_file, sbml_file)
-```
-
-Here all inputs ar given as strings.
+# SBML conversion
+yaml2sbml.yaml2sbml(yaml_dir, sbml_dir)
 
-To generate PEtab files call `yaml2petab` via
+#PEtab conversion
+yaml2sbml.yaml2petab(yaml_dir, 
+                     output_dir,
+                     sbml_name)
+```
+### Command-Line Interface
 
-```python
-import yaml2sbml
+and in the command-line via 
+```shell
+# SBML conversion
+yaml2sbml <yaml_dir> <sbml_dir>
 
-yaml2sbml.yaml2petab(yaml_file,
-                     output_dir,
-                     model_name)
+#PEtab conversion
+yaml2petab <yaml_dir> <output_dir> <sbml_name>
 ```
-Here `yaml_file, output_dir` and `model_name` are strings.
 
-## Known issues and limitations
+### Format Validation
+
+Format validation is possible in Python via `yaml2sbml.validate_yaml` and in the command-line via `yaml2sbml_validate`.
 
-* **Compartments** are not supported.
-* **Units** are not supported, all quantities are dimensionless.
-* Specification of PEtab **data tables** are not in the scope of this tool. 
+## Contact
+If you have a question regarding the tool: Please drop us an [issue](https://github.com/yaml2sbml-dev/yaml2sbml/issues/new) or a [mail](mailto:jakob.vanhoefer@uni-bonn.de), we are happy to help.
diff --git a/doc/Makefile b/doc/Makefile
new file mode 100644
index 0000000..f827265
--- /dev/null
+++ b/doc/Makefile
@@ -0,0 +1,21 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = .
+BUILDDIR      = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
diff --git a/doc/api_doc.rst b/doc/api_doc.rst
new file mode 100644
index 0000000..9697e76
--- /dev/null
+++ b/doc/api_doc.rst
@@ -0,0 +1,33 @@
+API documentation
+===================
+
+
+Convert YAML to SBML
+---------------------
+
+.. automodule:: yaml2sbml.yaml2sbml
+    :members: 
+
+
+Validate YAML file
+---------------------
+
+.. autofunction:: yaml2sbml.validate_yaml
+
+
+Convert YAML to PEtab
+----------------------------------
+
+.. autofunction:: yaml2sbml.yaml2petab
+
+
+Validate PEtab tables
+----------------------------------
+
+.. autofunction:: yaml2sbml.validate_petab_tables
+
+
+Model editor
+----------------------------------
+.. autoclass:: yaml2sbml.YamlModel
+    :members:
diff --git a/doc/conf.py b/doc/conf.py
new file mode 100644
index 0000000..343e78a
--- /dev/null
+++ b/doc/conf.py
@@ -0,0 +1,82 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+import os
+import sys
+sys.path.insert(0, os.path.abspath('..'))
+
+
+# -- Project information -----------------------------------------------------
+
+project = 'yaml2sbml'
+copyright = '2021, The yaml2sbml authors'
+author = 'Jakob Vanhoefer, Marta R. A. Matos, Dilan Pathirana, Yannik Schälte'
+
+# The version info for the project you're documenting, acts as replacement for
+# |version| and |release|, also used in various other places throughout the
+# built documents.
+#
+# The short X.Y version.
+import yaml2sbml
+version = yaml2sbml.__version__
+# The full version, including alpha/beta/rc tags.
+release = version
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = ['sphinx.ext.autodoc',
+              'sphinx.ext.coverage',
+              'sphinx.ext.napoleon',
+              'sphinx_autodoc_typehints',
+              'nbsphinx',
+              'sphinx.ext.mathjax',
+              # link to code
+              'sphinx.ext.viewcode',
+              # link to other projects' docs
+              'sphinx.ext.intersphinx',
+              # code highlighting in jupyter cells
+              'IPython.sphinxext.ipython_console_highlighting',
+              ]
+
+# links for intersphinx
+intersphinx_mapping = {
+    'python': ('https://docs.python.org/3', None),
+    'numpy': ('https://numpy.org/devdocs/', None),
+    'scipy': ('https://docs.scipy.org/doc/scipy/reference/', None),
+    'pandas': ('https://pandas.pydata.org/docs/', None),
+    'petab': ('https://petab.readthedocs.io/en/stable/', None),
+    'amici': ('https://amici.readthedocs.io/en/latest/', None),
+}
+
+# Add any paths that contain templates here, relative to this directory.
+#templates_path = ['_templates']
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = ['build', 'Thumbs.db', '.DS_Store']
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'sphinx_rtd_theme'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+#html_static_path = ['_static']
diff --git a/doc/contact.rst b/doc/contact.rst
new file mode 100644
index 0000000..f3a9981
--- /dev/null
+++ b/doc/contact.rst
@@ -0,0 +1,6 @@
+Contact
+=======
+
+Feel free to submit an
+`issue <https://github.com/yaml2sbml-dev/yaml2sbml/issues>`_
+or send us an `e-mail <mailto:marta.ra.matos@gmail.com;jakob.vanhoefer@uni-bonn.de>`_.
diff --git a/doc/contribute.rst b/doc/contribute.rst
new file mode 120000
index 0000000..a8c384e
--- /dev/null
+++ b/doc/contribute.rst
@@ -0,0 +1,1 @@
+../CONTRIBUTE.rst
\ No newline at end of file
diff --git a/doc/deploy.rst b/doc/deploy.rst
new file mode 100644
index 0000000..36ddd3b
--- /dev/null
+++ b/doc/deploy.rst
@@ -0,0 +1,77 @@
+Deploy
+======
+
+New production releases should be created every time the ``main`` branch is
+updated.
+
+Versions
+--------
+
+On every merge to ``main``, the version number in ``yaml2sbml/version.py`` should
+be incremented. We use version numbers ``A.B.C``, where roughly
+
+* ``C`` is increased for bug fixes,
+* ``B`` is increased for new features and minor API breaking changes,
+* ``A`` is increased for major API breaking changes.
+
+as suggested by the
+`Python packaging guide <https://packaging.python.org>`_.
+
+Create a new release
+--------------------
+
+After new commits have been added via pull requests to the ``develop`` branch,
+changes can be merged to ``main`` and a new version of yaml2sbml can be
+released.
+
+Merge into main
+~~~~~~~~~~~~~~~
+
+1. create a pull request from ``develop`` to ``main``,
+2. check that all tests pass,
+3. check that the documentation is up-to-date,
+4. update the version number in ``yaml2sbml/version.py`` (see above),
+5. update the release notes in ``doc/release_notes.rst``,
+6. request a code review.
+
+To be able to actually perform the merge, sufficient rights may be required.
+Also, at least one review is required.
+
+Create a release on GitHub
+~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+After merging into ``main``, create a new release on GitHub. This can be done
+either directly on the project GitHub website, or via the CLI as described
+in
+`Git Basics - Tagging <https://git-scm.com/book/en/v2/Git-Basics-Tagging>`_.
+In the release form,
+
+* specify a tag with the new version as specified in ``yaml2sbml/version.py``,
+* include the latest additions to ``doc/release_notes.rst`` in the release
+  description.
+
+Upload to PyPI
+--------------
+
+The upload to the Python package index PyPI has been automated via GitHub
+Actions, specified in ``.github/workflows/deploy.yml``,
+and is triggered whenever a new release tag is created.
+
+To manually upload a new version to PyPI, proceed as follows:
+First, create a so-called "wheel" via::
+
+    python setup.py sdist bdist_wheel
+
+A wheel is essentially a ZIP archive which contains the source code
+and the binaries (if any).
+
+Then upload the archive::
+
+    twine upload dist/yaml2sbml-x.y.z-py3-non-any.wheel
+
+replacing x.y.z by the respective version number.
+
+See also the
+`section on distributing packages 
+<https://packaging.python.org/tutorials/distributing-packages>`_
+of the Python packaging guide.
diff --git a/doc/examples/Finite_State_Projection/utils.py b/doc/examples/Finite_State_Projection/utils.py
index 256c7ae..011040d 100644
--- a/doc/examples/Finite_State_Projection/utils.py
+++ b/doc/examples/Finite_State_Projection/utils.py
@@ -1,3 +1,4 @@
+"""Utility functions, that simulate and plot the FSP example."""
 import amici
 import amici.plotting
 
@@ -12,9 +13,7 @@ def plot_AMICI(sbml_dir: str,
                t: np.ndarray,
                r_max: int,
                p_max: int):
-    """
-    Compiles, simulates and plots the AMICI model for the FSP example.
-    """
+    """Compile, simulate and plot the AMICI model for the FSP example."""
     n_t = len(t)
     # compile and Simulate model
     simulation, model = compile_and_simulate(sbml_dir,
@@ -30,11 +29,14 @@ def get_obs_idx_by_id(obs_id: str):
 
     for i in range(n_t):
 
-        r_marginal = [simulation.y[i, get_obs_idx_by_id(f'x_r{r}')] for r in range(r_max)]
-        p_marginal = [simulation.y[i, get_obs_idx_by_id(f'x_p{p}')] for p in range(p_max)]
+        r_marginal = [simulation.y[i, get_obs_idx_by_id(f'x_r{r}')]
+                      for r in range(r_max)]
+        p_marginal = [simulation.y[i, get_obs_idx_by_id(f'x_p{p}')]
+                      for p in range(p_max)]
         # rna
         plt.subplot(n_t, 2, 2*i+1)
-        plt.fill_between(np.arange(r_max), 0, r_marginal, facecolor='blue', alpha=0.5)
+        plt.fill_between(np.arange(r_max), 0, r_marginal,
+                         facecolor='blue', alpha=0.5)
         plt.plot(r_marginal)
         plt.ylabel(f't={int(t[i])}')
         plt.yticks([])
@@ -48,7 +50,8 @@ def get_obs_idx_by_id(obs_id: str):
 
         # protein
         plt.subplot(n_t, 2, 2*(i+1))
-        plt.fill_between(np.arange(p_max), 0, p_marginal, facecolor='blue', alpha=0.5)
+        plt.fill_between(np.arange(p_max), 0, p_marginal,
+                         facecolor='blue', alpha=0.5)
         plt.plot(p_marginal)
         plt.yticks([ ])
         plt.xlim(0, p_max-1)
@@ -66,9 +69,7 @@ def compile_and_simulate(sbml_dir: str,
                          t: np.ndarray,
                          r_max: int,
                          p_max: int):
-    """
-    Utility function, that compiles and simulates the FSP model.
-    """
+    """Compile and simulate the FSP model."""
     model_name = 'Gene_regulation_FSP'
 
     # define marginal rna concentrations
diff --git a/doc/examples/Format_Features/simulation_and_plotting.py b/doc/examples/Format_Features/simulation_and_plotting.py
index 5b4331d..af21227 100644
--- a/doc/examples/Format_Features/simulation_and_plotting.py
+++ b/doc/examples/Format_Features/simulation_and_plotting.py
@@ -1,3 +1,4 @@
+"""Utility functions for simulation and plotting in AMICI."""
 import amici
 import amici.plotting
 import matplotlib.pyplot as plt
@@ -29,7 +30,8 @@ def simulate_AMICI(sbml_name: str):
     amici_model = model_module.getModel()
     solver = amici_model.getSolver()
 
-    # Define time points ans run simulation using default model parameters/solver options
+    # Define time points and run simulation using default
+    # model parameters/solver options
     amici_model.setTimepoints(np.linspace(0, 5, 101))
     rdata = amici.runAmiciSimulation(amici_model, solver)
 
@@ -39,10 +41,7 @@ def simulate_AMICI(sbml_name: str):
 def plot_AMICI(amici_model,
                rdata,
                title: str):
-    """
-    Plots the AMICI simulation from the given rdata...
-    """
-
+    """Plot the AMICI simulation from the given rdata."""
     fig, ax = plt.subplots()
     amici.plotting.plotStateTrajectories(rdata, ax=ax)
 
diff --git a/doc/examples/Lotka_Volterra/Lotka_Volterra_python/Lotka_Volterra.ipynb b/doc/examples/Lotka_Volterra/Lotka_Volterra_python/Lotka_Volterra.ipynb
index d5150a5..0e51406 100644
--- a/doc/examples/Lotka_Volterra/Lotka_Volterra_python/Lotka_Volterra.ipynb
+++ b/doc/examples/Lotka_Volterra/Lotka_Volterra_python/Lotka_Volterra.ipynb
@@ -67,22 +67,27 @@
   },
   {
    "cell_type": "code",
-   "execution_count": 1,
+   "execution_count": 2,
    "metadata": {},
    "outputs": [
     {
-     "name": "stdout",
-     "output_type": "stream",
-     "text": [
-      "YAML file is valid ✅\n"
-     ]
+     "ename": "FileNotFoundError",
+     "evalue": "[Errno 2] No such file or directory: 'Lotka_Volterra_basic.yml'",
+     "traceback": [
+      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+      "\u001b[0;31mFileNotFoundError\u001b[0m                         Traceback (most recent call last)",
+      "\u001b[0;32m<ipython-input-2-9f1df3c2ade9>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      2\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      3\u001b[0m \u001b[0myaml_file_basic\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m'Lotka_Volterra_basic.yml'\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 4\u001b[0;31m \u001b[0myaml2sbml\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mvalidate_yaml\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0myaml_dir\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0myaml_file_basic\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+      "\u001b[0;32m~/Dokumente/PhD/Code/GitHub/yaml2sbml/yaml2sbml/yaml2sbml/yaml_validation.py\u001b[0m in \u001b[0;36mvalidate_yaml\u001b[0;34m(yaml_dir)\u001b[0m\n\u001b[1;32m     23\u001b[0m     \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     24\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 25\u001b[0;31m         \u001b[0;32mwith\u001b[0m \u001b[0mopen\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0myaml_dir\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m'r'\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;32mas\u001b[0m \u001b[0mf_in\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     26\u001b[0m             \u001b[0myaml_contents\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mf_in\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mread\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     27\u001b[0m             \u001b[0myaml_dict\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0myaml\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfull_load\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0myaml_contents\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
+      "\u001b[0;31mFileNotFoundError\u001b[0m: [Errno 2] No such file or directory: 'Lotka_Volterra_basic.yml'"
+     ],
+     "output_type": "error"
     }
    ],
    "source": [
     "import yaml2sbml\n",
     "\n",
     "yaml_file_basic = 'Lotka_Volterra_basic.yml'\n",
-    "yaml2sbml.validate_yaml(yaml_file=yaml_file_basic)"
+    "yaml2sbml.validate_yaml(yaml_dir=yaml_file_basic)"
    ]
   },
   {
diff --git a/doc/examples/README.md b/doc/examples/README.md
index e94b30f..4202121 100644
--- a/doc/examples/README.md
+++ b/doc/examples/README.md
@@ -2,7 +2,7 @@
 
 The [`yaml2sbml`](https://github.com/yaml2sbml-dev/yaml2sbml) package translates ODEs specified in YAML into [SBML](http://sbml.org/) for model simulation and [PEtab](https://github.com/PEtab-dev/PEtab) for parameter fitting. This folder contains several example notebooks for the usage of `yaml2sbml`.
 
-# Scope of the Notebooks
+## Scope of the Notebooks
 
 *   _Lotka Volterra Notebooks_
     *   [Lotka Volterra (Python)](./Lotka_Volterra/Lotka_Volterra_python/Lotka_Volterra.ipynb)
diff --git a/doc/examples/Sorensen/utils.py b/doc/examples/Sorensen/utils.py
index c851be9..8807f17 100644
--- a/doc/examples/Sorensen/utils.py
+++ b/doc/examples/Sorensen/utils.py
@@ -1,3 +1,4 @@
+"""Utlity functions for simulating and plotting the Sorensen model."""
 import amici
 import amici.plotting
 
@@ -9,8 +10,8 @@
 
 
 def simulate_and_plot_sorensen(sbml_dir: str):
-    """Plots and simulates the Sorensen model.
-    
+    """Plot and simulate the Sorensen model.
+
     This function is highly specific to the Sorensen model.
 
     Parameters:
@@ -26,7 +27,7 @@ def simulate_and_plot_sorensen(sbml_dir: str):
     simulation = amici.runAmiciSimulation(model, solver)
 
     # Reproduce the figure.
-    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))
+    _, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 5))
 
     amici.plotting.plotObservableTrajectories(
         simulation,
@@ -54,7 +55,8 @@ def simulate_and_plot_sorensen(sbml_dir: str):
 
 
 def compile_model(sbml_dir: str):
-    """Compiles the Sorensen model and specifies the observables.
+    """
+    Compile the Sorensen model and specify the observables.
 
     Parameters:
     -----------
@@ -86,7 +88,7 @@ def compile_model(sbml_dir: str):
     sbml_importer = amici.sbml_import.SbmlImporter(sbml_dir)
     sbml_importer.sbml2amici(
         model_name='Fig71_Sorensen1985',
-        output_dir=os.path.join('amici_models', sbml_dir),
+        output_dir=os.path.join('amici_models', 'Fig71_Sorensen1985'),
         observables=observables,
     )
 
diff --git a/doc/examples/examples.rst b/doc/examples/examples.rst
new file mode 100644
index 0000000..4371501
--- /dev/null
+++ b/doc/examples/examples.rst
@@ -0,0 +1,28 @@
+Examples
+===============================================
+
+We provide the following Jupyter notebooks with examples:
+
+* Three notebooks use the Lotka Volterra equations as a running example for the Python tool, CLI and the Model Editor.
+* A notebook demonstrating format features with minimal examples.
+* A notebook showing the Sorensen model as a real world application.
+* A notebook implementing the Finite State Projection.
+
+These examples can also be found on Github `here <https://github.com/yaml2sbml-dev/yaml2sbml_examples>`_.
+
+ .. toctree::
+   :maxdepth: 1
+
+   Lotka_Volterra/Lotka_Volterra_python/Lotka_Volterra
+   Lotka_Volterra/Lotka_Volterra_CLI/Lotka_Volterra_CLI
+   Lotka_Volterra/Lotka_Volterra_Model_Editor/Lotka_Volterra_Model_Editor
+   Format_Features/Format_Features
+   Sorensen/yaml2sbml_Sorensen
+   Finite_State_Projection/Finite_State_Projection
+
+
+
+
+
+
+
diff --git a/doc/format_documentation.md b/doc/format_documentation.md
deleted file mode 100644
index 4a3973f..0000000
--- a/doc/format_documentation.md
+++ /dev/null
@@ -1,153 +0,0 @@
-# Input Format for yaml2sbml
-
-## Table of contents
-
-* [General scope](#general-scope)
-* [time](#time-\[optional\])
-* [parameters](#parameters-\[optional\])
-* [odes](#odes)
-* [assignments](#assignments-\[optional\])
-* [functions](#functions-\[optional\])
-* [observables](#observables-\[optional\])
-* [conditions](#conditons-\[optional\])
-* [Parsing of Mathematical Equations](#parsing-of-mathematical-equations)
-
-
-## General scope
-
-*  `yaml2sbml`: Translates ODEs (Initial value problems) of the form `x' = f(t, x, p)` with time `t`, states `x` and (potentially) unknown parameters `p` into a SBML file for simulation purpose.
-
-*  `yaml2PEtab`: Defines a fitting problem of the form `y(t_i) = h(x(t_i), p) + eps_i` with independent Normal or Laplace distributed error terms `eps`. `h` denotes the mapping from system states to observables. PEtab allows to formulate MLE and MAP based fitting problems.
-
-
-## time \[optional\]
-
-```yaml
-time:
-   variable: t
-```
-
-Defines a **time variable**, in case the right hand side of the ODE is time-dependent.
-  
-## parameters \[optional\]
-
-```yaml
-parameters: 
-   - parameterId: p_1
-     nominalValue: 1
-  
-   - parameterId: p_2
-     ...     
- ```
-
-Defines a **parameter**. `nominalValue` is optional for SBML/PEtab generation, but will be needed for model simulation. Further optional entries are `parameterName, parameterScale, lowerBound, upperBound, estimate` and entries regarding priors. These entries will be written in the corresponding column of the _parameter table_ by `yaml2PEtab.`
-
-For a detailed description see the documentation of the [PEtab parameter table](https://github.com/PEtab-dev/PEtab/blob/master/doc/documentation_data_format.rst#parameter-table "PEtab parameter table documentation"). 
-
-Further entries are possible and will be written to the _parameter table_ as well but are currently not part of the PEtab standard. 
-
-## odes
-
-```yaml
-odes:
-    - stateId: x_1
-      rightHandSide: p_1 * x_1
-      initialValue: 1
-
-    - stateId: x_2
-      ...      
-```
-
-Defines the **ODE**. An ODE consists of a `stateId` (string), a `rightHandSide` (string, encoding a mathematical expression), and an `initial value`. Initial values can be either numerical values or parameter Ids. 
-
-For a more detailed description of the parsing of mathematical expressions ( for  `rightHandSide`) we refer to the [corresponding section](#parsing-of-mathematical-equations) of this documentation.
-
-## assignments \[optional\]
-
-```yaml
-assignments:
-    - assignmentId: sum_of_states
-      formula: x_1 + x_2
-
-    - assignmentId: ...
-      ...
-```
-
-**Assigns** the term `assignmentId` to the value given in `formula`. The value is dynamically updated and can depend on parameters, states and time. In the SBML assignments are represented via parameter assignment rules.
-
-For a more detailed description of the parsing of mathematical expressions (e.g. for `formula`) we refer to the [corresponding section](#parsing-of-mathematical-equations) of this documentation.
-
-## functions \[optional\]
-
-``` yaml
-functions:
-    - functionId: g_1
-      arguments: x_1, s
-      formula: s * x_1 + 1
-
-    - functionId: g_2
-      ...
-```
-
-Defines a **function**, that can be called in other parts of the ODE definitions, e.g. in the example above via  `g_1(x_1, s)`. 
-
-**Please note**, that all unknowns appearing in the formula (e.g. also parameters or the time variable) also have to be arguments of the function.  
-
-For a more detailed description of the parsing of mathematical expressions (e.g. for  `formula`) we refer to the [corresponding section](#parsing-of-mathematical-equations) of this documentation.
-
-## observables \[optional\]
-
-``` yaml
-observables:
-
-    - observableId: Obs_1
-      observableFormula: x_1 + x_2
-
-      noiseFormula: noiseParameter1
-      noiseDistribution: normal
-
-    - observableId: Obs_2
-      ...
-```
-
-Defines the **observables**. Observables are not part of the SBML standard and hence only influence the _observable table_ generated in `yaml2PEtab`. The entries are written to the corresponding columns of the PEtab observable table. According to the PEtab standard, an observable table can take the following entries:  `observableId, observableName, observableFormula, observableTransformation, noiseFormula, noiseDistribution`. 
-
-For a detailed discussion see the [corresponding part of the PEtab documentation](https://github.com/PEtab-dev/PEtab/blob/master/doc/documentation_data_format.rst#observables-table).
-
-## conditons \[optional\]
-
-```yaml
-conditions:
-
-    - conditionId: condition1
-      p_1: 1
-      x_1: 2
-      ...
-```
-
-Conditions allows to set parameters or initial conditions of states to a numeric value/unknown parameter. This allows to specify different experimental setups in the data generation (e.g. different initial conditions for different runs of an experiment). 
-
-The "trivial condition table" (if only one setup exist) is generated by:
-
-```yaml
-conditions:
-      - conditionId: condition1
-```
-
-For a detailed discussion see the [corresponding part of the PEtab documentation](https://github.com/PEtab-dev/PEtab/blob/master/doc/documentation_data_format.rst#condition-table).
-
-## Parsing of Mathematical Equations
-
- Throughout `yaml2sbml` formulas are parsed by `libsbml's` `parseL3Formula` function. Further information on the syntax are given by:
-
-*  the [working with math](http://sbml.org/Special/Software/libSBML/docs/formatted/python-api/libsbml-math.html)-section of the `libsbml` documentation.
-*  the [documentation](http://sbml.org/Special/Software/libSBML/docs/formatted/python-api/namespacelibsbml.html#ae79acc3be958963c55f1d03944add36b) of `libsbml.parseL3Formula`.
-
-
-This gives access to e.g.:
-
-*  +, -, *, / and power,
-*  trigonometric/hyperbolic functions, 
-*  exponential/logarithmic functions,
-*  piecewise defined functions,
-*  boolean expressions like "<".
diff --git a/doc/format_specification.rst b/doc/format_specification.rst
new file mode 100644
index 0000000..a3ab99b
--- /dev/null
+++ b/doc/format_specification.rst
@@ -0,0 +1,179 @@
+Input Format for yaml2sbml
+==========================
+
+
+General scope
+-------------
+
+*  `yaml2sbml`: translate ODEs (initial value problems) of the form `x' = f(t, x, p)` with time `t`, states `x` and (potentially) unknown parameters `p` into an SBML file for simulation purpose.
+
+*  `yaml2PEtab`: define a fitting problem of the form `y(t_i) = h(x(t_i), p) + eps_i` with independent normal- or Laplace-distributed error terms `eps`. `h` denotes the mapping from system states to observables. PEtab allows one to formulate MLE and MAP based fitting problems.
+
+General remarks
+---------------
+
+* All identifiers of states, parameters etc. need to be valid SBML identifiers. Therefore, identifiers must consist of only upper and lower case letters, digits and underscores, and must not start with a digit.
+* Mathematical equations are parsed by `libsbml`s `parseL3Formula`. Hence for correct syntax see its `documentation <http://sbml.org/Special/Software/libSBML/docs/formatted/python-api/namespacelibsbml.html#ae79acc3be958963c55f1d03944add36b>`_ and the corresponding section of the format specification.
+* Equations starting with a minus must be surrounded by brackets or quotation marks, since a leading minus also has a syntactic meaning in YAML and the YAML file will not be valid otherwise.
+
+time \[optional\]
+-----------------
+
+
+.. code-block:: yaml
+
+  time:
+    variable: t
+
+
+Define the **time variable**, in case the right hand side of the ODE is time-dependent.
+  
+
+  
+parameters \[optional\]
+-----------------------
+
+.. code-block:: yaml
+
+  parameters: 
+    - parameterId: p_1
+      nominalValue: 1
+    
+    - parameterId: p_2
+      ...     
+
+
+Define **parameters**. `nominalValue` is optional for SBML/PEtab generation, but will be needed for model simulation. Further optional entries are `parameterName, parameterScale, lowerBound, upperBound, estimate` and entries regarding priors. These entries will be written in the corresponding column of the _parameter table_ by `yaml2PEtab.`
+
+For a detailed description see the documentation of the `PEtab parameter table <https://github.com/PEtab-dev/PEtab/blob/master/doc/documentation_data_format.rst#parameter-table>`_ "PEtab parameter table documentation"). 
+
+Further entries are possible and will be written to the _parameter table_ as well but are currently not part of the PEtab standard. 
+
+
+
+odes
+----
+
+.. code-block:: yaml
+
+  odes:
+      - stateId: x_1
+        rightHandSide: p_1 * x_1
+        initialValue: 1
+
+      - stateId: x_2
+        ...      
+
+
+Define **ODEs** (and states). An ODE consists of a `stateId` (string), a `rightHandSide` (string, encoding a mathematical expression), and an `initial value`. Initial values can be either numerical values or parameter Ids.
+
+For a more detailed description of the parsing of mathematical expressions ( for  `rightHandSide`) we refer to the [corresponding section](#parsing-of-mathematical-equations) of this documentation.
+
+
+
+assignments \[optional\]
+------------------------
+
+.. code-block:: yaml
+
+  assignments:
+      - assignmentId: sum_of_states
+        formula: x_1 + x_2
+
+      - assignmentId: ...
+        ...
+
+
+**Assign** the mathematical expression `formula` to the term `assignmentId`. The value is dynamically updated and can depend on parameters, states and time. In SBML, assignments are represented via parameter assignment rules.
+
+For a more detailed description of the parsing of mathematical expressions (e.g. for `formula`) we refer to the [corresponding section](#parsing-of-mathematical-equations) of this documentation.
+
+
+
+functions \[optional\]
+----------------------
+
+.. code-block:: yaml
+
+  functions:
+      - functionId: g_1
+        arguments: x_1, s
+        formula: s * x_1 + 1
+
+      - functionId: g_2
+        ...
+
+Define **functions**, which can be called in other parts of the ODE definitions, e.g. in the example above via  `g_1(x_1, s)`.
+
+**Please note** that all unknowns appearing in the formula (e.g. also parameters or the time variable) also have to be arguments of the function.
+
+For a more detailed description of the parsing of mathematical expressions (e.g. for  `formula`) we refer to the [corresponding section](#parsing-of-mathematical-equations) of this documentation.
+
+
+
+observables \[optional\]
+------------------------
+
+.. code-block:: yaml
+
+  observables:
+
+      - observableId: Obs_1
+        observableFormula: x_1 + x_2
+
+        noiseFormula: noiseParameter1
+        noiseDistribution: normal
+
+      - observableId: Obs_2
+        ...
+
+Define **observables**. Observables are not part of the SBML standard. If the SBML is generated via the `yaml2sbml.yaml2sbml` command and the `observables_as_assignments` flag is set to `True`, observables are represented as assignments to parameters of the form observable_<observable_id>.
+If the SBML is created via `yaml2sbml.yaml2petab`, observables are represented in the PEtab observables table. The entries are written to the corresponding columns of the PEtab observable table. According to the PEtab standard, an observable table can take the following entries:  `observableId, observableName, observableFormula, observableTransformation, noiseFormula, noiseDistribution`.
+
+For a detailed discussion see the `corresponding part of the PEtab documentation <https://github.com/PEtab-dev/PEtab/blob/master/doc/documentation_data_format.rst#observables-table>`_.
+
+
+
+conditions \[optional\]
+----------------------
+
+.. code-block:: yaml
+
+  conditions:
+
+      - conditionId: condition1
+        p_1: 1
+        x_1: 2
+        ...
+
+
+Conditions allow one to set parameters or initial conditions of states to a numeric value/unknown parameter. This allows for the specification of different experimental setups in the data generation (e.g. different initial conditions for different runs of an experiment).
+
+The "trivial condition table" (if only one setup exists) is generated by:
+
+.. code-block:: yaml
+
+  conditions:
+        - conditionId: condition1
+
+
+For a detailed discussion see the `corresponding part of the PEtab documentation <https://github.com/PEtab-dev/PEtab/blob/master/doc/documentation_data_format.rst#condition-table>`_.
+
+
+
+Parsing of mathematical equations
+---------------------------------
+
+ Throughout `yaml2sbml` formulas are parsed by `libsbml's` `parseL3Formula` function. Further information on the syntax are given by:
+
+*  the `working with math <http://sbml.org/Special/Software/libSBML/docs/formatted/python-api/libsbml-math.html>`_ - section of the `libsbml` documentation.
+*  the `documentation <http://sbml.org/Special/Software/libSBML/docs/formatted/python-api/namespacelibsbml.html#ae79acc3be958963c55f1d03944add36b>`_ of `libsbml.parseL3Formula`.
+
+
+This gives access to e.g.:
+
+*  `+`, `-`, `*`, `/`, and `power;
+*  trigonometric/hyperbolic functions;
+*  exponential/logarithmic functions;
+*  piecewise defined functions (via `piecewise`); and
+*  boolean expressions like "<".
diff --git a/doc/index.rst b/doc/index.rst
new file mode 100644
index 0000000..74eff03
--- /dev/null
+++ b/doc/index.rst
@@ -0,0 +1,59 @@
+yaml2sbml
+=========
+
+.. image:: https://github.com/yaml2sbml-dev/yaml2sbml/workflows/CI/badge.svg
+   :target: https://github.com/yaml2sbml-dev/yaml2sbml/actions
+   :alt: Build status
+.. image:: https://codecov.io/gh/yaml2sbml-dev/yaml2sbml/branch/master/graph/badge.svg
+   :target: https://codecov.io/gh/yaml2sbml-dev/yaml2sbml
+   :alt: Code coverage
+.. image:: https://readthedocs.org/projects/yaml2sbml/badge/?version=latest
+   :target: https://yaml2sbml.readthedocs.io/en/latest/?badge=latest
+   :alt: Documentation Status
+.. image:: https://app.codacy.com/project/badge/Grade/632acdc8d4ef4f50bf69892b8862fd24
+   :target: https://www.codacy.com/gh/yaml2sbml-dev/yaml2sbml/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=yaml2sbml-dev/yaml2sbml&amp;utm_campaign=Badge_Grade
+   :alt: Code quality
+
+:Release: |version|
+:Source code: https://github.com/yaml2sbml-dev/yaml2sbml
+
+.. image:: logo/logo_yaml2sbml_long.png
+   :alt: yaml2sbml logo
+   :align: center
+   :scale: 30 %
+
+**yaml2sbml** allows the user to convert a system of ODEs specified in a YAML_
+file to SBML_.
+In addition, if experimental data is provided in the YAML file, it can also be
+converted to PEtab_.
+
+.. toctree::
+   :caption: Main
+   :maxdepth: 1
+   :hidden:
+
+   install
+   format_specification
+   examples/examples
+   api_doc
+
+.. toctree::
+   :caption: About
+   :maxdepth: 1
+   :hidden:
+
+   release_notes
+   license
+   contact
+
+.. toctree::
+   :caption: Developers
+   :maxdepth: 1
+   :hidden:
+
+   contribute
+   deploy
+
+.. _YAML: https://yaml.org
+.. _SBML: http://sbml.org
+.. _PEtab: https://petab.readthedocs.io/en/stable/
diff --git a/doc/install.rst b/doc/install.rst
new file mode 120000
index 0000000..356deb7
--- /dev/null
+++ b/doc/install.rst
@@ -0,0 +1,1 @@
+../INSTALL.rst
\ No newline at end of file
diff --git a/doc/license.rst b/doc/license.rst
new file mode 100644
index 0000000..4953066
--- /dev/null
+++ b/doc/license.rst
@@ -0,0 +1,7 @@
+License
+=======
+
+This package is licensed under an MIT license.
+
+.. literalinclude:: ../LICENSE
+   :language: none
diff --git a/doc/logo/logo_yaml2sbml_icon.png b/doc/logo/logo_yaml2sbml_icon.png
new file mode 100644
index 0000000..c300c18
Binary files /dev/null and b/doc/logo/logo_yaml2sbml_icon.png differ
diff --git a/doc/logo/logo_yaml2sbml_icon.svg b/doc/logo/logo_yaml2sbml_icon.svg
new file mode 100644
index 0000000..0f235e5
--- /dev/null
+++ b/doc/logo/logo_yaml2sbml_icon.svg
@@ -0,0 +1,117 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="44.38686mm"
+   height="42.568233mm"
+   viewBox="0 0 44.386857 42.568233"
+   version="1.1"
+   id="svg8"
+   inkscape:version="1.0.1 (0767f8302a, 2020-10-17)"
+   sodipodi:docname="logo_yaml2sbml_icon.svg"
+   inkscape:export-filename="/home/yannik/yaml2sbml/doc/logo/logo_yaml2sbml_icon.svg.png"
+   inkscape:export-xdpi="300"
+   inkscape:export-ydpi="300">
+  <defs
+     id="defs2" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.99854827"
+     inkscape:cx="-136.27074"
+     inkscape:cy="152.63026"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     inkscape:document-rotation="0"
+     showgrid="false"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:window-width="1864"
+     inkscape:window-height="1043"
+     inkscape:window-x="56"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid881"
+       originx="-113.90784"
+       originy="-93.040822" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-113.90787,-93.040833)">
+    <path
+       style="fill:#ececec;fill-opacity:1;stroke:none;stroke-width:0.0473071;stroke-miterlimit:4;stroke-dasharray:0.0473071, 0.0473071;stroke-dashoffset:0;stroke-opacity:1"
+       d=""
+       id="path1160"
+       transform="matrix(0.26458333,0,0,0.26458333,32.233845,35.206178)" />
+    <path
+       style="fill:#ececec;fill-opacity:1;stroke:none;stroke-width:0.00836278;stroke-miterlimit:4;stroke-dasharray:0.00836278, 0.00836278;stroke-dashoffset:0;stroke-opacity:1"
+       d=""
+       id="path1166"
+       transform="matrix(0.26458333,0,0,0.26458333,32.233845,35.206178)" />
+    <g
+       id="g971-9"
+       transform="rotate(-46.605884,227.86731,278.79937)">
+      <path
+         style="fill:none;stroke:#3576b7;stroke-width:1.91899;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 300.87549,95.298108 -3.74337,5.007532 3.76342,4.99245"
+         id="path891-5-9-3"
+         sodipodi:nodetypes="ccc" />
+      <path
+         style="fill:none;stroke:#cb171e;stroke-width:1.92272;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 263.6775,106.85156 c 0,0 1.90228,0.38319 6.29906,-0.908 2.84779,-0.8363 5.55754,-2.40794 5.55754,-2.40794"
+         id="path1291-2-5-1"
+         sodipodi:nodetypes="csc"
+         inkscape:connector-curvature="0" />
+      <path
+         style="fill:none;stroke:#cb171e;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 259.964,92.999929 c 0,0 4.25247,-0.06624 8.74441,1.135249 4.70182,1.257598 6.89807,3.09273 6.89807,3.09273"
+         id="path933-6-0-9"
+         sodipodi:nodetypes="csc"
+         inkscape:connector-curvature="0" />
+      <path
+         style="fill:none;stroke:#cb171e;stroke-width:1.99567;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 263.33501,100.40508 12.21938,-0.008"
+         id="path889-7-0-4" />
+      <path
+         d="m 308.24661,101.63221 c 0.84268,-0.78008 0.84234,-1.949939 -4.8e-4,-2.721218 -1.70086,-1.556359 -5.31698,-4.362835 -10.42396,-5.676838 -2.60349,-0.66982 -5.92293,-0.908188 -9.10721,-0.653976 -0.0392,0.0033 -0.0781,0.0067 -0.11718,0.01012 l -3.54095,-2.483726 c -0.58289,-0.408809 -1.34833,-0.568428 -2.10002,-0.437993 l -5.63341,0.977646 c -0.57012,0.09892 -1.03564,0.425984 -1.24534,0.874806 -0.20966,0.448905 -0.12972,0.947061 0.21393,1.332675 l 2.3409,2.62599 c -0.0633,0.03668 -0.12717,0.07328 -0.18999,0.110597 -0.64353,0.382269 -1.02764,1.002771 -1.02747,1.659943 l 10e-4,6.364614 c -1.2e-4,0.54484 0.26434,1.06182 0.7216,1.43662 0.094,0.0773 0.19663,0.14837 0.30662,0.21259 0.0628,0.0367 0.12641,0.0726 0.19005,0.10861 l -2.33956,2.65074 c -0.34319,0.38942 -0.42327,0.88824 -0.21342,1.33481 0.0874,0.18586 0.21881,0.35003 0.38371,0.48526 0.23168,0.18975 0.52904,0.32235 0.86196,0.37663 l 5.63386,0.91872 c 0.75183,0.12257 1.51712,-0.0453 2.09974,-0.46002 l 3.53989,-2.5207 c 0.039,0.004 0.0779,0.009 0.11725,0.009 3.18441,0.22097 6.50377,-0.0519 9.10698,-0.74915 5.10664,-1.36745 8.72143,-4.2119 10.42162,-5.7858 z m -24.91408,8.00144 -5.38915,-0.87882 2.28007,-2.58329 c 1.77438,0.78936 3.77647,1.36524 5.93363,1.70645 l -2.30604,1.64212 c -0.1438,0.10252 -0.33288,0.14383 -0.51848,0.11358 z m -4.05657,-6.02833 -10e-4,-6.364637 c 10e-4,-0.162393 0.0928,-0.314559 0.24826,-0.406868 0.80025,-0.475316 1.44531,-0.881665 2.28067,-1.211375 1.34333,3.362945 1.119,6.5017 0.18774,9.56022 -1.00311,-0.38582 -1.82247,-0.79711 -2.46695,-1.17309 -0.15557,-0.0911 -0.2484,-0.24184 -0.24844,-0.40425 z m 4.57117,-12.327157 2.30667,1.61796 c -2.15704,0.363715 -4.15889,0.960528 -5.93294,1.768444 l -2.28111,-2.559427 5.38884,-0.935134 c 0.18568,-0.03221 0.37468,0.0073 0.51853,0.10815 z m 5.03823,15.360017 c -1.94821,-0.13525 -3.62018,-0.44842 -5.03436,-0.8426 0.48956,-3.26473 0.90568,-6.579331 -0.002,-10.793335 1.84797,-0.532116 3.62232,-0.782104 5.03389,-0.894793 2.9468,-0.235294 5.9997,-0.01957 8.37594,0.591831 4.68713,1.205891 8.02833,3.802772 9.60311,5.243208 0.20845,0.190629 0.2085,0.479889 -4.5e-4,0.672779 -1.574,1.45702 -4.91428,4.08861 -9.6009,5.34355 -2.3759,0.63625 -5.42867,0.88385 -8.37551,0.67938 z"
+         id="path1155-1-7"
+         style="fill:#6e6e6e;fill-opacity:1;stroke-width:0.0563068"
+         sodipodi:nodetypes="scssccccsccsccssccsscccccsssccccccccscccsccccccscsccscsssss" />
+      <path
+         style="fill:none;stroke:#3576b7;stroke-width:1.91899;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 308.6926,95.29811 3.76346,4.99247 -3.74339,5.00751"
+         id="path907-1-3-8"
+         sodipodi:nodetypes="ccc" />
+      <path
+         style="fill:none;stroke:#3576b7;stroke-width:1.91899;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 306.19899,93.297557 -2.80984,14.001083"
+         id="path893-5-6-4"
+         sodipodi:nodetypes="cc" />
+    </g>
+  </g>
+</svg>
diff --git a/doc/logo/logo_yaml2sbml_long.png b/doc/logo/logo_yaml2sbml_long.png
new file mode 100644
index 0000000..7e7ff39
Binary files /dev/null and b/doc/logo/logo_yaml2sbml_long.png differ
diff --git a/doc/logo/logo_yaml2sbml_long.svg b/doc/logo/logo_yaml2sbml_long.svg
new file mode 100644
index 0000000..273d2f2
--- /dev/null
+++ b/doc/logo/logo_yaml2sbml_long.svg
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="216.22427mm"
+   height="42.568233mm"
+   viewBox="0 0 216.22426 42.568234"
+   version="1.1"
+   id="svg8"
+   inkscape:version="1.0.1 (0767f8302a, 2020-10-17)"
+   sodipodi:docname="logo_yaml2sbml_long.svg"
+   inkscape:export-filename="/home/user/Documents/projects/yaml2sbml/logo/logo_yaml2sbml_inkscape.svg.png"
+   inkscape:export-xdpi="300"
+   inkscape:export-ydpi="300">
+  <defs
+     id="defs2" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.70608025"
+     inkscape:cx="422.64241"
+     inkscape:cy="118.49081"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     inkscape:document-rotation="0"
+     showgrid="false"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:window-width="1864"
+     inkscape:window-height="1043"
+     inkscape:window-x="56"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid881"
+       originx="-113.90786"
+       originy="-93.040831" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-113.90787,-93.040833)">
+    <path
+       style="fill:#ececec;fill-opacity:1;stroke:none;stroke-width:0.0473071;stroke-miterlimit:4;stroke-dasharray:0.0473071, 0.0473071;stroke-dashoffset:0;stroke-opacity:1"
+       d=""
+       id="path1160"
+       transform="matrix(0.26458333,0,0,0.26458333,32.233845,35.206178)" />
+    <path
+       style="fill:#ececec;fill-opacity:1;stroke:none;stroke-width:0.00836278;stroke-miterlimit:4;stroke-dasharray:0.00836278, 0.00836278;stroke-dashoffset:0;stroke-opacity:1"
+       d=""
+       id="path1166"
+       transform="matrix(0.26458333,0,0,0.26458333,32.233845,35.206178)" />
+    <g
+       id="g987-3"
+       transform="matrix(1.075782,0,0,1.075782,-171.08254,10.564205)">
+      <g
+         aria-label="yaml"
+         id="text871-8-7"
+         style="font-size:14.1111px;line-height:1.25;font-family:sans-serif;fill:#cb171e;fill-opacity:1;stroke-width:0.264583"
+         transform="matrix(1.9777516,0,0,1.9777516,625.82695,219.46681)">
+        <path
+           d="m -153.10462,-56.843926 q -0.53743,1.378038 -1.0473,1.798339 -0.50988,0.420301 -1.36426,0.420301 h -1.01286 v -1.061088 h 0.74414 q 0.52366,0 0.81304,-0.248047 0.28939,-0.248047 0.64079,-1.171332 l 0.22738,-0.578775 -3.12126,-7.592985 h 1.34359 l 2.41156,6.035803 2.41157,-6.035803 h 1.34359 z"
+           style="fill:#cb171e;fill-opacity:1;stroke-width:0.264583"
+           id="path1337-6-5" />
+        <path
+           d="m -144.45743,-61.439679 q -1.53651,0 -2.12907,0.351399 -0.59256,0.3514 -0.59256,1.198892 0,0.675239 0.44098,1.074869 0.44786,0.392741 1.21267,0.392741 1.0542,0 1.68809,-0.74414 0.64079,-0.75103 0.64079,-1.991264 v -0.282497 z m 2.5287,-0.523655 v 4.402829 h -1.2678 v -1.171331 q -0.43408,0.702798 -1.08176,1.040418 -0.64768,0.330728 -1.58474,0.330728 -1.18511,0 -1.88791,-0.661457 -0.69591,-0.668348 -0.69591,-1.784558 0,-1.302246 0.86816,-1.963703 0.87506,-0.661458 2.60449,-0.661458 h 1.77767 v -0.124024 q 0,-0.875053 -0.57877,-1.350476 -0.57189,-0.482313 -1.61231,-0.482313 -0.66146,0 -1.28846,0.158474 -0.62701,0.158475 -1.20578,0.475423 v -1.171331 q 0.6959,-0.268718 1.35047,-0.399631 0.65457,-0.137804 1.27469,-0.137804 1.67431,0 2.50113,0.868164 0.82683,0.868163 0.82683,2.63205 z"
+           style="fill:#cb171e;fill-opacity:1;stroke-width:0.264583"
+           id="path1339-2-9" />
+        <path
+           d="m -133.30222,-63.796123 q 0.47542,-0.854383 1.13688,-1.260904 0.66146,-0.406521 1.55718,-0.406521 1.20578,0 1.86035,0.847493 0.65457,0.840603 0.65457,2.397785 v 4.657765 h -1.27469 v -4.616424 q 0,-1.10932 -0.39274,-1.646755 -0.39274,-0.537434 -1.19889,-0.537434 -0.9853,0 -1.55718,0.654567 -0.57189,0.654568 -0.57189,1.784559 v 4.361487 h -1.27468 v -4.616424 q 0,-1.11621 -0.39274,-1.646755 -0.39274,-0.537434 -1.21267,-0.537434 -0.97152,0 -1.54341,0.661458 -0.57188,0.654567 -0.57188,1.777668 v 4.361487 h -1.27469 v -7.717008 h 1.27469 v 1.198892 q 0.43408,-0.709689 1.04042,-1.047308 0.60633,-0.337619 1.44005,-0.337619 0.8406,0 1.42626,0.427192 0.59256,0.427191 0.87506,1.240233 z"
+           style="fill:#cb171e;fill-opacity:1;stroke-width:0.264583"
+           id="path1341-1-2" />
+        <path
+           d="m -125.55765,-68.281634 h 1.26779 v 10.721129 h -1.26779 z"
+           style="fill:#cb171e;fill-opacity:1;stroke-width:0.264583"
+           id="path1343-5-2" />
+      </g>
+      <g
+         aria-label="2"
+         id="text875-6-8"
+         style="font-size:14.1111px;line-height:1.25;font-family:sans-serif;fill:#6e6e6e;fill-opacity:1;stroke-width:0.264583"
+         transform="matrix(1.9777516,0,0,1.9777516,625.30367,219.46681)">
+        <path
+           d="m -119.85241,-58.732611 h 4.85758 v 1.171332 h -6.5319 v -1.171332 q 0.79237,-0.819932 2.15663,-2.197969 1.37115,-1.384927 1.72255,-1.784558 0.66834,-0.75103 0.93017,-1.267794 0.26872,-0.523654 0.26872,-1.026638 0,-0.819932 -0.57878,-1.336696 -0.57188,-0.516764 -1.49517,-0.516764 -0.65457,0 -1.38493,0.227376 -0.72346,0.227376 -1.55029,0.689019 v -1.405598 q 0.84061,-0.337619 1.57097,-0.509874 0.73035,-0.172254 1.33669,-0.172254 1.59852,0 2.54937,0.799261 0.95085,0.799262 0.95085,2.135958 0,0.633897 -0.24116,1.205782 -0.23427,0.564995 -0.86127,1.336696 -0.17226,0.199815 -1.09554,1.157551 -0.92329,0.950846 -2.60449,2.666502 z"
+           style="fill:#6e6e6e;fill-opacity:1;stroke-width:0.264583"
+           id="path1346-8-9" />
+      </g>
+      <g
+         aria-label="sbml"
+         id="text879-7-7"
+         style="font-size:14.1111px;line-height:1.25;font-family:sans-serif;fill:#3576b7;fill-opacity:1;stroke-width:0.264583"
+         transform="matrix(1.9777516,0,0,1.9777516,624.78042,219.46681)">
+        <path
+           d="m -106.74714,-65.044045 v 1.198893 q -0.53743,-0.275608 -1.11621,-0.413411 -0.57877,-0.137804 -1.19889,-0.137804 -0.94396,0 -1.41938,0.289388 -0.46853,0.289387 -0.46853,0.868163 0,0.440972 0.33762,0.695909 0.33762,0.248046 1.35736,0.475423 l 0.43409,0.09646 q 1.35047,0.289388 1.91547,0.819932 0.57188,0.523654 0.57188,1.46761 0,1.074869 -0.85438,1.701876 -0.84749,0.627007 -2.33577,0.627007 -0.62012,0 -1.29536,-0.124024 -0.66835,-0.117133 -1.41249,-0.358289 v -1.309136 q 0.7028,0.36518 1.38493,0.551215 0.68213,0.179145 1.35048,0.179145 0.89572,0 1.37804,-0.303168 0.48231,-0.310059 0.48231,-0.868164 0,-0.516764 -0.3514,-0.792371 -0.34451,-0.275607 -1.52273,-0.530544 l -0.44097,-0.103353 q -1.17823,-0.248047 -1.70188,-0.75792 -0.52365,-0.516764 -0.52365,-1.412489 0,-1.088649 0.7717,-1.681205 0.7717,-0.592556 2.19108,-0.592556 0.70279,0 1.32291,0.103353 0.62012,0.103353 1.14377,0.310058 z"
+           style="fill:#3576b7;fill-opacity:1;stroke-width:0.264583"
+           id="path1349-1-3" />
+        <path
+           d="m -98.768303,-61.406027 q 0,-1.398707 -0.578775,-2.191079 -0.571886,-0.799261 -1.577852,-0.799261 -1.00597,0 -1.58474,0.799261 -0.57189,0.792372 -0.57189,2.191079 0,1.398708 0.57189,2.19797 0.57877,0.792371 1.58474,0.792371 1.005966,0 1.577852,-0.792371 0.578775,-0.799262 0.578775,-2.19797 z m -4.313257,-2.694062 q 0.39963,-0.689019 1.00597,-1.019748 0.61322,-0.337619 1.46072,-0.337619 1.405595,0 2.280649,1.11621 0.881944,1.11621 0.881944,2.935219 0,1.819009 -0.881944,2.935219 -0.875054,1.116211 -2.280649,1.116211 -0.8475,0 -1.46072,-0.330729 -0.60634,-0.337619 -1.00597,-1.026638 v 1.157551 h -1.27468 v -10.721129 h 1.27468 z"
+           style="fill:#3576b7;fill-opacity:1;stroke-width:0.264583"
+           id="path1351-3-6" />
+        <path
+           d="m -89.342529,-63.790031 q 0.475423,-0.854383 1.136881,-1.260904 0.661458,-0.406521 1.557182,-0.406521 1.205782,0 1.86035,0.847493 0.654568,0.840603 0.654568,2.397785 v 4.657765 h -1.274685 v -4.616424 q 0,-1.10932 -0.39274,-1.646755 -0.392741,-0.537434 -1.198892,-0.537434 -0.985297,0 -1.557182,0.654568 -0.571886,0.654567 -0.571886,1.784558 v 4.361487 h -1.274684 v -4.616424 q 0,-1.11621 -0.392741,-1.646755 -0.39274,-0.537434 -1.212672,-0.537434 -0.971517,0 -1.543402,0.661458 -0.571885,0.654567 -0.571885,1.777668 v 4.361487 h -1.274685 v -7.717008 h 1.274685 v 1.198893 q 0.434081,-0.70969 1.040418,-1.047309 0.606336,-0.337619 1.440048,-0.337619 0.840603,0 1.426269,0.427192 0.592556,0.427191 0.875053,1.240233 z"
+           style="fill:#3576b7;fill-opacity:1;stroke-width:0.264583"
+           id="path1353-4-1" />
+        <path
+           d="m -81.597961,-68.275542 h 1.267794 v 10.721129 h -1.267794 z"
+           style="fill:#3576b7;fill-opacity:1;stroke-width:0.264583"
+           id="path1355-7-2" />
+      </g>
+    </g>
+    <g
+       id="g971-9"
+       transform="rotate(-46.605884,227.86731,278.79937)">
+      <path
+         style="fill:none;stroke:#3576b7;stroke-width:1.91899;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 300.87549,95.298108 -3.74337,5.007532 3.76342,4.99245"
+         id="path891-5-9-3"
+         sodipodi:nodetypes="ccc" />
+      <path
+         style="fill:none;stroke:#cb171e;stroke-width:1.92272;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 263.6775,106.85156 c 0,0 1.90228,0.38319 6.29906,-0.908 2.84779,-0.8363 5.55754,-2.40794 5.55754,-2.40794"
+         id="path1291-2-5-1"
+         sodipodi:nodetypes="csc"
+         inkscape:connector-curvature="0" />
+      <path
+         style="fill:none;stroke:#cb171e;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 259.964,92.999929 c 0,0 4.25247,-0.06624 8.74441,1.135249 4.70182,1.257598 6.89807,3.09273 6.89807,3.09273"
+         id="path933-6-0-9"
+         sodipodi:nodetypes="csc"
+         inkscape:connector-curvature="0" />
+      <path
+         style="fill:none;stroke:#cb171e;stroke-width:1.99567;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 263.33501,100.40508 12.21938,-0.008"
+         id="path889-7-0-4" />
+      <path
+         d="m 308.24661,101.63221 c 0.84268,-0.78008 0.84234,-1.949939 -4.8e-4,-2.721218 -1.70086,-1.556359 -5.31698,-4.362835 -10.42396,-5.676838 -2.60349,-0.66982 -5.92293,-0.908188 -9.10721,-0.653976 -0.0392,0.0033 -0.0781,0.0067 -0.11718,0.01012 l -3.54095,-2.483726 c -0.58289,-0.408809 -1.34833,-0.568428 -2.10002,-0.437993 l -5.63341,0.977646 c -0.57012,0.09892 -1.03564,0.425984 -1.24534,0.874806 -0.20966,0.448905 -0.12972,0.947061 0.21393,1.332675 l 2.3409,2.62599 c -0.0633,0.03668 -0.12717,0.07328 -0.18999,0.110597 -0.64353,0.382269 -1.02764,1.002771 -1.02747,1.659943 l 10e-4,6.364614 c -1.2e-4,0.54484 0.26434,1.06182 0.7216,1.43662 0.094,0.0773 0.19663,0.14837 0.30662,0.21259 0.0628,0.0367 0.12641,0.0726 0.19005,0.10861 l -2.33956,2.65074 c -0.34319,0.38942 -0.42327,0.88824 -0.21342,1.33481 0.0874,0.18586 0.21881,0.35003 0.38371,0.48526 0.23168,0.18975 0.52904,0.32235 0.86196,0.37663 l 5.63386,0.91872 c 0.75183,0.12257 1.51712,-0.0453 2.09974,-0.46002 l 3.53989,-2.5207 c 0.039,0.004 0.0779,0.009 0.11725,0.009 3.18441,0.22097 6.50377,-0.0519 9.10698,-0.74915 5.10664,-1.36745 8.72143,-4.2119 10.42162,-5.7858 z m -24.91408,8.00144 -5.38915,-0.87882 2.28007,-2.58329 c 1.77438,0.78936 3.77647,1.36524 5.93363,1.70645 l -2.30604,1.64212 c -0.1438,0.10252 -0.33288,0.14383 -0.51848,0.11358 z m -4.05657,-6.02833 -10e-4,-6.364637 c 10e-4,-0.162393 0.0928,-0.314559 0.24826,-0.406868 0.80025,-0.475316 1.44531,-0.881665 2.28067,-1.211375 1.34333,3.362945 1.119,6.5017 0.18774,9.56022 -1.00311,-0.38582 -1.82247,-0.79711 -2.46695,-1.17309 -0.15557,-0.0911 -0.2484,-0.24184 -0.24844,-0.40425 z m 4.57117,-12.327157 2.30667,1.61796 c -2.15704,0.363715 -4.15889,0.960528 -5.93294,1.768444 l -2.28111,-2.559427 5.38884,-0.935134 c 0.18568,-0.03221 0.37468,0.0073 0.51853,0.10815 z m 5.03823,15.360017 c -1.94821,-0.13525 -3.62018,-0.44842 -5.03436,-0.8426 0.48956,-3.26473 0.90568,-6.579331 -0.002,-10.793335 1.84797,-0.532116 3.62232,-0.782104 5.03389,-0.894793 2.9468,-0.235294 5.9997,-0.01957 8.37594,0.591831 4.68713,1.205891 8.02833,3.802772 9.60311,5.243208 0.20845,0.190629 0.2085,0.479889 -4.5e-4,0.672779 -1.574,1.45702 -4.91428,4.08861 -9.6009,5.34355 -2.3759,0.63625 -5.42867,0.88385 -8.37551,0.67938 z"
+         id="path1155-1-7"
+         style="fill:#6e6e6e;fill-opacity:1;stroke-width:0.0563068"
+         sodipodi:nodetypes="scssccccsccsccssccsscccccsssccccccccscccsccccccscsccscsssss" />
+      <path
+         style="fill:none;stroke:#3576b7;stroke-width:1.91899;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 308.6926,95.29811 3.76346,4.99247 -3.74339,5.00751"
+         id="path907-1-3-8"
+         sodipodi:nodetypes="ccc" />
+      <path
+         style="fill:none;stroke:#3576b7;stroke-width:1.91899;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 306.19899,93.297557 -2.80984,14.001083"
+         id="path893-5-6-4"
+         sodipodi:nodetypes="cc" />
+    </g>
+  </g>
+</svg>
diff --git a/doc/logo/logo_yaml2sbml_short.png b/doc/logo/logo_yaml2sbml_short.png
new file mode 100644
index 0000000..f275b34
Binary files /dev/null and b/doc/logo/logo_yaml2sbml_short.png differ
diff --git a/doc/logo/logo_yaml2sbml_short.svg b/doc/logo/logo_yaml2sbml_short.svg
new file mode 100644
index 0000000..0c49db8
--- /dev/null
+++ b/doc/logo/logo_yaml2sbml_short.svg
@@ -0,0 +1,175 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   width="46.424816mm"
+   height="54.298767mm"
+   viewBox="0 0 46.424813 54.298767"
+   version="1.1"
+   id="svg8"
+   inkscape:version="1.0.1 (0767f8302a, 2020-10-17)"
+   sodipodi:docname="logo_yaml2sbml_short.svg"
+   inkscape:export-filename="/home/yannik/yaml2sbml/doc/logo/logo_yaml2sbml_long.svg.png"
+   inkscape:export-xdpi="300"
+   inkscape:export-ydpi="300">
+  <defs
+     id="defs2" />
+  <sodipodi:namedview
+     id="base"
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1.0"
+     inkscape:pageopacity="0.0"
+     inkscape:pageshadow="2"
+     inkscape:zoom="0.99854827"
+     inkscape:cx="-132.41949"
+     inkscape:cy="152.63026"
+     inkscape:document-units="mm"
+     inkscape:current-layer="layer1"
+     inkscape:document-rotation="0"
+     showgrid="false"
+     showguides="true"
+     inkscape:guide-bbox="true"
+     inkscape:window-width="1864"
+     inkscape:window-height="1043"
+     inkscape:window-x="56"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1">
+    <inkscape:grid
+       type="xygrid"
+       id="grid881"
+       originx="-112.88887"
+       originy="-93.040826" />
+  </sodipodi:namedview>
+  <metadata
+     id="metadata5">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title></dc:title>
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <g
+     inkscape:label="Layer 1"
+     inkscape:groupmode="layer"
+     id="layer1"
+     transform="translate(-112.88889,-93.040833)">
+    <path
+       style="fill:#ececec;fill-opacity:1;stroke:none;stroke-width:0.0473071;stroke-miterlimit:4;stroke-dasharray:0.0473071, 0.0473071;stroke-dashoffset:0;stroke-opacity:1"
+       d=""
+       id="path1160"
+       transform="matrix(0.26458333,0,0,0.26458333,32.233845,35.206178)" />
+    <path
+       style="fill:#ececec;fill-opacity:1;stroke:none;stroke-width:0.00836278;stroke-miterlimit:4;stroke-dasharray:0.00836278, 0.00836278;stroke-dashoffset:0;stroke-opacity:1"
+       d=""
+       id="path1166"
+       transform="matrix(0.26458333,0,0,0.26458333,32.233845,35.206178)" />
+    <g
+       id="g987-3"
+       transform="matrix(0.30738346,0,0,0.30738346,16.101511,113.08738)">
+      <g
+         aria-label="yaml"
+         id="text871-8-7"
+         style="font-size:14.1111px;line-height:1.25;font-family:sans-serif;fill:#cb171e;fill-opacity:1;stroke-width:0.264583"
+         transform="matrix(1.9777516,0,0,1.9777516,625.82695,219.46681)">
+        <path
+           d="m -153.10462,-56.843926 q -0.53743,1.378038 -1.0473,1.798339 -0.50988,0.420301 -1.36426,0.420301 h -1.01286 v -1.061088 h 0.74414 q 0.52366,0 0.81304,-0.248047 0.28939,-0.248047 0.64079,-1.171332 l 0.22738,-0.578775 -3.12126,-7.592985 h 1.34359 l 2.41156,6.035803 2.41157,-6.035803 h 1.34359 z"
+           style="fill:#cb171e;fill-opacity:1;stroke-width:0.264583"
+           id="path1337-6-5" />
+        <path
+           d="m -144.45743,-61.439679 q -1.53651,0 -2.12907,0.351399 -0.59256,0.3514 -0.59256,1.198892 0,0.675239 0.44098,1.074869 0.44786,0.392741 1.21267,0.392741 1.0542,0 1.68809,-0.74414 0.64079,-0.75103 0.64079,-1.991264 v -0.282497 z m 2.5287,-0.523655 v 4.402829 h -1.2678 v -1.171331 q -0.43408,0.702798 -1.08176,1.040418 -0.64768,0.330728 -1.58474,0.330728 -1.18511,0 -1.88791,-0.661457 -0.69591,-0.668348 -0.69591,-1.784558 0,-1.302246 0.86816,-1.963703 0.87506,-0.661458 2.60449,-0.661458 h 1.77767 v -0.124024 q 0,-0.875053 -0.57877,-1.350476 -0.57189,-0.482313 -1.61231,-0.482313 -0.66146,0 -1.28846,0.158474 -0.62701,0.158475 -1.20578,0.475423 v -1.171331 q 0.6959,-0.268718 1.35047,-0.399631 0.65457,-0.137804 1.27469,-0.137804 1.67431,0 2.50113,0.868164 0.82683,0.868163 0.82683,2.63205 z"
+           style="fill:#cb171e;fill-opacity:1;stroke-width:0.264583"
+           id="path1339-2-9" />
+        <path
+           d="m -133.30222,-63.796123 q 0.47542,-0.854383 1.13688,-1.260904 0.66146,-0.406521 1.55718,-0.406521 1.20578,0 1.86035,0.847493 0.65457,0.840603 0.65457,2.397785 v 4.657765 h -1.27469 v -4.616424 q 0,-1.10932 -0.39274,-1.646755 -0.39274,-0.537434 -1.19889,-0.537434 -0.9853,0 -1.55718,0.654567 -0.57189,0.654568 -0.57189,1.784559 v 4.361487 h -1.27468 v -4.616424 q 0,-1.11621 -0.39274,-1.646755 -0.39274,-0.537434 -1.21267,-0.537434 -0.97152,0 -1.54341,0.661458 -0.57188,0.654567 -0.57188,1.777668 v 4.361487 h -1.27469 v -7.717008 h 1.27469 v 1.198892 q 0.43408,-0.709689 1.04042,-1.047308 0.60633,-0.337619 1.44005,-0.337619 0.8406,0 1.42626,0.427192 0.59256,0.427191 0.87506,1.240233 z"
+           style="fill:#cb171e;fill-opacity:1;stroke-width:0.264583"
+           id="path1341-1-2" />
+        <path
+           d="m -125.55765,-68.281634 h 1.26779 v 10.721129 h -1.26779 z"
+           style="fill:#cb171e;fill-opacity:1;stroke-width:0.264583"
+           id="path1343-5-2" />
+      </g>
+      <g
+         aria-label="2"
+         id="text875-6-8"
+         style="font-size:14.1111px;line-height:1.25;font-family:sans-serif;fill:#6e6e6e;fill-opacity:1;stroke-width:0.264583"
+         transform="matrix(1.9777516,0,0,1.9777516,625.30367,219.46681)">
+        <path
+           d="m -119.85241,-58.732611 h 4.85758 v 1.171332 h -6.5319 v -1.171332 q 0.79237,-0.819932 2.15663,-2.197969 1.37115,-1.384927 1.72255,-1.784558 0.66834,-0.75103 0.93017,-1.267794 0.26872,-0.523654 0.26872,-1.026638 0,-0.819932 -0.57878,-1.336696 -0.57188,-0.516764 -1.49517,-0.516764 -0.65457,0 -1.38493,0.227376 -0.72346,0.227376 -1.55029,0.689019 v -1.405598 q 0.84061,-0.337619 1.57097,-0.509874 0.73035,-0.172254 1.33669,-0.172254 1.59852,0 2.54937,0.799261 0.95085,0.799262 0.95085,2.135958 0,0.633897 -0.24116,1.205782 -0.23427,0.564995 -0.86127,1.336696 -0.17226,0.199815 -1.09554,1.157551 -0.92329,0.950846 -2.60449,2.666502 z"
+           style="fill:#6e6e6e;fill-opacity:1;stroke-width:0.264583"
+           id="path1346-8-9" />
+      </g>
+      <g
+         aria-label="sbml"
+         id="text879-7-7"
+         style="font-size:14.1111px;line-height:1.25;font-family:sans-serif;fill:#3576b7;fill-opacity:1;stroke-width:0.264583"
+         transform="matrix(1.9777516,0,0,1.9777516,624.78042,219.46681)">
+        <path
+           d="m -106.74714,-65.044045 v 1.198893 q -0.53743,-0.275608 -1.11621,-0.413411 -0.57877,-0.137804 -1.19889,-0.137804 -0.94396,0 -1.41938,0.289388 -0.46853,0.289387 -0.46853,0.868163 0,0.440972 0.33762,0.695909 0.33762,0.248046 1.35736,0.475423 l 0.43409,0.09646 q 1.35047,0.289388 1.91547,0.819932 0.57188,0.523654 0.57188,1.46761 0,1.074869 -0.85438,1.701876 -0.84749,0.627007 -2.33577,0.627007 -0.62012,0 -1.29536,-0.124024 -0.66835,-0.117133 -1.41249,-0.358289 v -1.309136 q 0.7028,0.36518 1.38493,0.551215 0.68213,0.179145 1.35048,0.179145 0.89572,0 1.37804,-0.303168 0.48231,-0.310059 0.48231,-0.868164 0,-0.516764 -0.3514,-0.792371 -0.34451,-0.275607 -1.52273,-0.530544 l -0.44097,-0.103353 q -1.17823,-0.248047 -1.70188,-0.75792 -0.52365,-0.516764 -0.52365,-1.412489 0,-1.088649 0.7717,-1.681205 0.7717,-0.592556 2.19108,-0.592556 0.70279,0 1.32291,0.103353 0.62012,0.103353 1.14377,0.310058 z"
+           style="fill:#3576b7;fill-opacity:1;stroke-width:0.264583"
+           id="path1349-1-3" />
+        <path
+           d="m -98.768303,-61.406027 q 0,-1.398707 -0.578775,-2.191079 -0.571886,-0.799261 -1.577852,-0.799261 -1.00597,0 -1.58474,0.799261 -0.57189,0.792372 -0.57189,2.191079 0,1.398708 0.57189,2.19797 0.57877,0.792371 1.58474,0.792371 1.005966,0 1.577852,-0.792371 0.578775,-0.799262 0.578775,-2.19797 z m -4.313257,-2.694062 q 0.39963,-0.689019 1.00597,-1.019748 0.61322,-0.337619 1.46072,-0.337619 1.405595,0 2.280649,1.11621 0.881944,1.11621 0.881944,2.935219 0,1.819009 -0.881944,2.935219 -0.875054,1.116211 -2.280649,1.116211 -0.8475,0 -1.46072,-0.330729 -0.60634,-0.337619 -1.00597,-1.026638 v 1.157551 h -1.27468 v -10.721129 h 1.27468 z"
+           style="fill:#3576b7;fill-opacity:1;stroke-width:0.264583"
+           id="path1351-3-6" />
+        <path
+           d="m -89.342529,-63.790031 q 0.475423,-0.854383 1.136881,-1.260904 0.661458,-0.406521 1.557182,-0.406521 1.205782,0 1.86035,0.847493 0.654568,0.840603 0.654568,2.397785 v 4.657765 h -1.274685 v -4.616424 q 0,-1.10932 -0.39274,-1.646755 -0.392741,-0.537434 -1.198892,-0.537434 -0.985297,0 -1.557182,0.654568 -0.571886,0.654567 -0.571886,1.784558 v 4.361487 h -1.274684 v -4.616424 q 0,-1.11621 -0.392741,-1.646755 -0.39274,-0.537434 -1.212672,-0.537434 -0.971517,0 -1.543402,0.661458 -0.571885,0.654567 -0.571885,1.777668 v 4.361487 h -1.274685 v -7.717008 h 1.274685 v 1.198893 q 0.434081,-0.70969 1.040418,-1.047309 0.606336,-0.337619 1.440048,-0.337619 0.840603,0 1.426269,0.427192 0.592556,0.427191 0.875053,1.240233 z"
+           style="fill:#3576b7;fill-opacity:1;stroke-width:0.264583"
+           id="path1353-4-1" />
+        <path
+           d="m -81.597961,-68.275542 h 1.267794 v 10.721129 h -1.267794 z"
+           style="fill:#3576b7;fill-opacity:1;stroke-width:0.264583"
+           id="path1355-7-2" />
+      </g>
+    </g>
+    <g
+       id="g971-9"
+       transform="rotate(-46.605884,227.86731,278.79937)">
+      <path
+         style="fill:none;stroke:#3576b7;stroke-width:1.91899;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 300.87549,95.298108 -3.74337,5.007532 3.76342,4.99245"
+         id="path891-5-9-3"
+         sodipodi:nodetypes="ccc" />
+      <path
+         style="fill:none;stroke:#cb171e;stroke-width:1.92272;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 263.6775,106.85156 c 0,0 1.90228,0.38319 6.29906,-0.908 2.84779,-0.8363 5.55754,-2.40794 5.55754,-2.40794"
+         id="path1291-2-5-1"
+         sodipodi:nodetypes="csc"
+         inkscape:connector-curvature="0" />
+      <path
+         style="fill:none;stroke:#cb171e;stroke-width:2;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 259.964,92.999929 c 0,0 4.25247,-0.06624 8.74441,1.135249 4.70182,1.257598 6.89807,3.09273 6.89807,3.09273"
+         id="path933-6-0-9"
+         sodipodi:nodetypes="csc"
+         inkscape:connector-curvature="0" />
+      <path
+         style="fill:none;stroke:#cb171e;stroke-width:1.99567;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 263.33501,100.40508 12.21938,-0.008"
+         id="path889-7-0-4" />
+      <path
+         d="m 308.24661,101.63221 c 0.84268,-0.78008 0.84234,-1.949939 -4.8e-4,-2.721218 -1.70086,-1.556359 -5.31698,-4.362835 -10.42396,-5.676838 -2.60349,-0.66982 -5.92293,-0.908188 -9.10721,-0.653976 -0.0392,0.0033 -0.0781,0.0067 -0.11718,0.01012 l -3.54095,-2.483726 c -0.58289,-0.408809 -1.34833,-0.568428 -2.10002,-0.437993 l -5.63341,0.977646 c -0.57012,0.09892 -1.03564,0.425984 -1.24534,0.874806 -0.20966,0.448905 -0.12972,0.947061 0.21393,1.332675 l 2.3409,2.62599 c -0.0633,0.03668 -0.12717,0.07328 -0.18999,0.110597 -0.64353,0.382269 -1.02764,1.002771 -1.02747,1.659943 l 10e-4,6.364614 c -1.2e-4,0.54484 0.26434,1.06182 0.7216,1.43662 0.094,0.0773 0.19663,0.14837 0.30662,0.21259 0.0628,0.0367 0.12641,0.0726 0.19005,0.10861 l -2.33956,2.65074 c -0.34319,0.38942 -0.42327,0.88824 -0.21342,1.33481 0.0874,0.18586 0.21881,0.35003 0.38371,0.48526 0.23168,0.18975 0.52904,0.32235 0.86196,0.37663 l 5.63386,0.91872 c 0.75183,0.12257 1.51712,-0.0453 2.09974,-0.46002 l 3.53989,-2.5207 c 0.039,0.004 0.0779,0.009 0.11725,0.009 3.18441,0.22097 6.50377,-0.0519 9.10698,-0.74915 5.10664,-1.36745 8.72143,-4.2119 10.42162,-5.7858 z m -24.91408,8.00144 -5.38915,-0.87882 2.28007,-2.58329 c 1.77438,0.78936 3.77647,1.36524 5.93363,1.70645 l -2.30604,1.64212 c -0.1438,0.10252 -0.33288,0.14383 -0.51848,0.11358 z m -4.05657,-6.02833 -10e-4,-6.364637 c 10e-4,-0.162393 0.0928,-0.314559 0.24826,-0.406868 0.80025,-0.475316 1.44531,-0.881665 2.28067,-1.211375 1.34333,3.362945 1.119,6.5017 0.18774,9.56022 -1.00311,-0.38582 -1.82247,-0.79711 -2.46695,-1.17309 -0.15557,-0.0911 -0.2484,-0.24184 -0.24844,-0.40425 z m 4.57117,-12.327157 2.30667,1.61796 c -2.15704,0.363715 -4.15889,0.960528 -5.93294,1.768444 l -2.28111,-2.559427 5.38884,-0.935134 c 0.18568,-0.03221 0.37468,0.0073 0.51853,0.10815 z m 5.03823,15.360017 c -1.94821,-0.13525 -3.62018,-0.44842 -5.03436,-0.8426 0.48956,-3.26473 0.90568,-6.579331 -0.002,-10.793335 1.84797,-0.532116 3.62232,-0.782104 5.03389,-0.894793 2.9468,-0.235294 5.9997,-0.01957 8.37594,0.591831 4.68713,1.205891 8.02833,3.802772 9.60311,5.243208 0.20845,0.190629 0.2085,0.479889 -4.5e-4,0.672779 -1.574,1.45702 -4.91428,4.08861 -9.6009,5.34355 -2.3759,0.63625 -5.42867,0.88385 -8.37551,0.67938 z"
+         id="path1155-1-7"
+         style="fill:#6e6e6e;fill-opacity:1;stroke-width:0.0563068"
+         sodipodi:nodetypes="scssccccsccsccssccsscccccsssccccccccscccsccccccscsccscsssss" />
+      <path
+         style="fill:none;stroke:#3576b7;stroke-width:1.91899;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 308.6926,95.29811 3.76346,4.99247 -3.74339,5.00751"
+         id="path907-1-3-8"
+         sodipodi:nodetypes="ccc" />
+      <path
+         style="fill:none;stroke:#3576b7;stroke-width:1.91899;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+         d="m 306.19899,93.297557 -2.80984,14.001083"
+         id="path893-5-6-4"
+         sodipodi:nodetypes="cc" />
+    </g>
+  </g>
+</svg>
diff --git a/doc/make.bat b/doc/make.bat
new file mode 100644
index 0000000..2119f51
--- /dev/null
+++ b/doc/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=.
+set BUILDDIR=_build
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+	echo.
+	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+	echo.installed, then set the SPHINXBUILD environment variable to point
+	echo.to the full path of the 'sphinx-build' executable. Alternatively you
+	echo.may add the Sphinx directory to PATH.
+	echo.
+	echo.If you don't have Sphinx installed, grab it from
+	echo.http://sphinx-doc.org/
+	exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 8bfd554..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-python-libsbml>=5.18.0
-PyYAML>=5.3
-pandas>=1.0.1
-petab>=0.1.2
-jsonschema >= 3.0.1
diff --git a/run_flake8.sh b/run_flake8.sh
deleted file mode 100755
index e8a0a27..0000000
--- a/run_flake8.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env bash
-
-python3 -m flake8 \
-    --exclude=build,doc,examples,source \
-    --per-file-ignores='*/__init__.py:F401 test/*:T001,S101'
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 0000000..cd461cc
--- /dev/null
+++ b/setup.cfg
@@ -0,0 +1,81 @@
+##########################
+# Setup.py Configuration #
+##########################
+
+[metadata]
+name = yaml2sbml
+version = attr: yaml2sbml.version.__version__
+description = A small package to convert ODEs specified in a YAML file to SBML/PEtab
+long_description = file: README.md
+long_description_content_type = text/markdown
+
+# URLs
+url = https://github.com/yaml2sbml-dev/yaml2sbml
+download_url = https://github.com/yaml2sbml-dev/yaml2sbml/releases
+project_urls =
+    Bug Tracker = https://github.com/yaml2sbml-dev/yaml2sbml/issues
+    Documentation = https://yaml2sbml.readthedocs.io
+    Changelog = https://yaml2sbml.readthedocs.io/en/latest/release_notes.html
+
+# Author information
+author = Jakob Vanhoefer, Marta R. A. Matos, Dilan Pathirana, Yannik Schälte
+author_email = jakob.vanhoefer@uni-bonn.de,marta.ra.matos@gmail.com
+
+# License information
+license = MIT
+license_file = LICENSE
+
+# Search tags
+classifiers =
+    Development Status :: 4 - Beta
+    Environment :: Console
+    Intended Audience :: Science/Research
+    License :: OSI Approved :: MIT License
+    Operating System :: OS Independent
+    Programming Language :: Python
+    Programming Language :: Python :: 3.6
+    Programming Language :: Python :: 3.7
+    Programming Language :: Python :: 3.8
+    Programming Language :: Python :: 3.9
+keywords = YAML, SBML, PEtab, ODE
+
+[options]
+install_requires =
+    pandas >= 1.0.1
+    PyYAML >= 5.1
+    python-libsbml >= 5.18.0
+    petab >= 0.1.4
+
+zip_safe = False
+include_package_data = True
+python_requires = >=3.6
+
+# Where is my code
+packages = find:
+
+[options.extras_require]
+examples =
+    numpy >= 1.19.4
+    scipy >= 1.6.0
+    notebook >= 6.1.4
+    # TODO Unfix when AMICI piecewise has been solved
+    amici == 0.11.10
+    pypesto >= 0.2.3
+    matplotlib >= 3.1.0
+doc =
+    sphinx >= 3.4.3
+    nbsphinx >= 0.8.0
+    nbconvert >= 6.0.7
+    sphinx-rtd-theme >= 0.5.1
+    sphinx_autodoc_typehints >= 1.11.1
+    ipython >= 7.18.1
+
+[options.entry_points]
+console_scripts =
+    yaml2sbml = yaml2sbml.yaml2sbml:main
+    yaml2petab = yaml2sbml.yaml2PEtab:main
+    yaml2sbml_validate = yaml2sbml.yaml_validation:main
+
+[bdist_wheel]
+# Requires python 3
+universal = False
diff --git a/setup.py b/setup.py
index 9d58794..60dfdf2 100644
--- a/setup.py
+++ b/setup.py
@@ -1,48 +1,5 @@
-import setuptools
-import os
-
-ENTRY_POINTS = {
-    'console_scripts': [
-        'yaml2sbml = yaml2sbml.yaml2sbml:main',
-        'yaml2petab = yaml2sbml.yaml2PEtab:main',
-        'yaml2sbml_validate = yaml2sbml.yaml_validation:main'
-    ]
-}
+"""Setup module."""
 
-with open(os.path.join(os.path.dirname(__file__),
-          "yaml2sbml", "version.py")) as f:
-    version = f.read().split('\n')[0].split('=')[-1].strip(' ').strip('"')
+import setuptools
 
-setuptools.setup(
-    name="yaml2sbml",
-    version=version,
-    author="Jakob Vanhoefer, Marta R. A. Matos",
-    author_email="marta.ra.matos@gmail.com",
-    description="A small package to convert ODEs specified in "
-                "a yaml file to SBML/PEtab.",
-    url="https://github.com/martamatos/yaml2sbml",
-    packages=setuptools.find_packages(),
-    install_requires=["python-libsbml>=5.18.0",
-                      "PyYAML>=5.1",
-                      "pandas>=1.0.1",
-                      "petab>=0.1.4"],
-    tests_require=["amici>=0.11.10",
-                   "pypesto>=0.2.2"
-                   "numpy>=1.19.4",
-                   "matplotlib>=3.1.0",
-                   "flake8>=3.7.2",
-                   "nbmake>=0.1.0",
-                   "scipy>=1.6.0"],
-    extras_require={'examples': ["amici>=0.11.10",
-                                 "numpy>=1.19.4",
-                                 "matplotlib>=3.1.0",
-                                 "scipy>=1.6.0"]},
-    python_requires='>=3.6',
-    classifiers=[
-        "Programming Language :: Python :: 3.6+",
-        "License :: OSI Approved :: MIT License",
-        "Operating System :: Unix",
-    ],
-    entry_points=ENTRY_POINTS,
-    include_package_data=True
-)
+setuptools.setup()
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..127a7c8
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,85 @@
+# tox (https://tox.readthedocs.io/) is a tool for running tests
+# in multiple virtualenvs. This configuration file will run the
+# test suite on all supported python versions. To use it, "pip install tox"
+# and then run "tox" from this directory.
+# See https://tox.readthedocs.io/en/latest/config.html for reference.
+#
+# running specif tests is possible e.g. via tox -e flake8
+[tox]
+
+# Environments run by default and in this order
+#  unless specified via CLI -eENVLIST
+#  or environment variable TOXENV
+envlist =
+    pyroma
+    flake8
+    unittests
+
+# Base-environment
+
+[testenv]
+# Dependencies
+deps =
+    coverage
+    pytest
+    pytest-cov
+    pytest-console-scripts
+
+# Sub-environments
+#  inherit settings defined in the base
+
+[testenv:pyroma]
+# No installation of yaml2sbml required
+skip_install = true
+deps =
+    pygments
+    pyroma
+commands = pyroma --min=10 .
+description =
+    Run pyroma to check the package friendliness
+
+[testenv:flake8]
+skip_install = true
+deps =
+    flake8
+    flake8-bandit
+    flake8-bugbear
+    flake8-colors
+    flake8-commas
+    flake8-comprehensions
+    flake8-print
+    flake8-use-fstring
+    pep8-naming
+    pydocstyle
+commands =
+    flake8 yaml2sbml tests setup.py
+description =
+    Run flake8 with various plugins
+
+[testenv:unittests]
+commands =
+    coverage erase
+    pytest \
+        --cov=yaml2sbml --cov-report=xml --cov-report=html \
+        tests
+description =
+    Run unit tests
+
+[testenv:notebooks]
+deps =
+    nbmake
+# Extras required from yaml2sbml
+extras = examples
+commands =
+    pytest --nbmake doc/examples
+description =
+    Test notebooks execution
+
+[testenv:doc]
+extras =
+    doc
+commands =
+    # Add -W to treat warnings as errors
+    sphinx-build -b html doc/ doc/build/html
+description =
+    Test whether docs build passes
diff --git a/yaml2sbml/YamlModel.py b/yaml2sbml/YamlModel.py
index f38b935..37fa606 100644
--- a/yaml2sbml/YamlModel.py
+++ b/yaml2sbml/YamlModel.py
@@ -1,22 +1,20 @@
+"""A Model Editor for creating YAML models."""
 import yaml
 import os.path
-from typing import Union
 import copy
+from typing import Union
+from pathlib import Path
 
-from .yaml2sbml import _parse_yaml_dict
+from .yaml2sbml import _parse_yaml_dict, _load_yaml_file
 from .yaml2PEtab import _yaml2petab
 from .yaml_validation import _validate_yaml_from_dict
 
 
 class YamlModel:
-    """
-    Functionality to set up, edit, load and write yaml models.
-    """
+    """Functionality to set up, edit, load and write yaml models."""
 
     def __init__(self):
-        """
-        Set up yaml model.
-        """
+        """Initialize a YAML model."""
         self._yaml_model = {'time': {},
                             'odes': [],
                             'parameters': [],
@@ -26,25 +24,22 @@ def __init__(self):
                             'conditions': []}
 
     @staticmethod
-    def load_from_yaml(yaml_file):
+    def load_from_yaml(yaml_dir: str):
         """
-        Creates a model instance from a yaml file.
+        Create a model instance from a YAML file.
 
         Arguments:
-        yaml_dir:
-            directory to the yaml file, that should be imported
+            yaml_dir:
+                directory to the YAML file, that should be imported
 
         Returns:
-        cls:
-            new model
+            cls:
+                new model
         """
-
         new_model = YamlModel()
 
-        # read in yaml_file
-        with open(yaml_file, 'r') as f_in:
-            yaml_contents = f_in.read()
-            new_model._yaml_model.update(yaml.full_load(yaml_contents))
+        yaml_contents = _load_yaml_file(yaml_dir)
+        new_model._yaml_model.update(yaml_contents)
 
         # check, if the model is valid
         new_model.validate_model()
@@ -55,15 +50,13 @@ def write_to_yaml(self,
                       yaml_dir: str,
                       overwrite: bool = False):
         """
-        Write model to yaml file given as directory yaml_dir.
+        Write the model to a YAML file given as `yaml_dir`.
 
         Arguments:
-        yaml_dir:
-            path/file, where the yaml should be written
-        overwrite:
-            Indicates, whether an existing yaml should be overwritten
-
-        Returns:
+            yaml_dir:
+                path/file, where the YAML should be written
+            overwrite:
+                Indicates, whether an existing YAML should be overwritten
 
         Raises:
             ValueError
@@ -98,15 +91,13 @@ def write_to_sbml(self,
                       sbml_dir: str,
                       overwrite: bool = False):
         """
-        Writes the model as an SBML file to the directory given in sbml_dir.
+        Write the model as an SBML file to the directory given in `sbml_dir`.
 
         Arguments:
-        sbml_dir:
-            path/file, where the sbml should be written
-        overwrite:
-            Indicates, whether an existing yaml should be overwritten
-
-        Returns:
+            sbml_dir:
+                path/file, where the sbml should be written
+            overwrite:
+                Indicates, whether an existing yaml should be overwritten
 
         Raises:
             ValueError
@@ -117,6 +108,9 @@ def write_to_sbml(self,
             raise ValueError('sbml_dir should contain path to the sbml '
                              'and hence end with .xml or .sbml')
 
+        # model name = sbml name without file extension
+        model_name = Path(sbml_dir).stem
+
         if (not overwrite) and os.path.exists(sbml_dir):
             raise FileExistsError(f'Can not write SBML model. File {sbml_dir}'
                                   f' already exists. Consider to set '
@@ -124,7 +118,8 @@ def write_to_sbml(self,
 
         # generate SBML as string
         reduced_model_dict = self._get_reduced_model_dict()
-        sbml_as_string = _parse_yaml_dict(reduced_model_dict)
+        sbml_as_string = _parse_yaml_dict(reduced_model_dict,
+                                          model_name)
 
         with open(sbml_dir, 'w') as f_out:
             f_out.write(sbml_as_string)
@@ -135,17 +130,19 @@ def write_to_petab(self,
                        petab_yaml_name: str = None,
                        measurement_table_name: str = None):
         """
-        Writes the YamlModel as a petab problem. Equivalent to calling
-        yaml2petab on the file produced by the yaml output.
+        Write the YamlModel as a PEtab problem.
 
-        If a petab_yaml_name is given, a .yaml file is created, that organizes
-        the petab problem. If additionally a measurement_table_file_name is
-        specified, this file name is written into the created .yaml file.
+        Equivalent to calling `yaml2petab on the file produced by the YAML
+        output.
+
+        If a `petab_yaml_name is given, a YAML file is created, that organizes
+        the petab problem. If additionally a `measurement_table_file_name is
+        specified, this file name is written into the created YAML file.
 
         Arguments:
             output_dir: path the output file(s) are be written out
             model_name: name of SBML model
-            petab_yaml_name: name of yaml organizing the PEtab problem.
+            petab_yaml_name: name of the YAML organizing the PEtab problem.
             measurement_table_name: Name of measurement table
         """
         reduced_model_dict = self._get_reduced_model_dict()
@@ -158,25 +155,18 @@ def write_to_petab(self,
 
     def validate_model(self):
         """
-        Validates the yaml model.
-
-        Arguments:
-
-        Returns:
+        Validate the YAML model.
 
         Raises:
             ValidationError
         """
-        _validate_yaml_from_dict(self._yaml_model)
+        _validate_yaml_from_dict(self._get_reduced_model_dict())
 
     def _get_reduced_model_dict(self) -> dict:
         """
-        Returns a reduced model dict, where keys without an entry are deleted.
-        (Returns a copy of the model dict!!)
-
-        Arguments:
+        Return a reduced model dict, where keys without an entry are deleted.
 
-        Raises:
+        Return a copy of the model dict!!
 
         Returns:
             reduced_model_dict
@@ -215,10 +205,32 @@ def add_parameter(self,
                       parameter_scale: str = None,
                       lower_bound: float = None,
                       upper_bound: float = None,
-                      estimate: float = None):
+                      estimate: int = None):
         """
-        Adds a parameter. Overwrites an existing parameter with the same id,
-        if overwrite=True.
+        Add a parameter.
+
+        Overwrite an existing parameter with the same id, if `overwrite==True`.
+
+        Arguments:
+            parameter_id:
+                str, parameter id
+            overwrite:
+                bool, indicates if an existing state/ODE should be overwritten
+            nominal_value:
+                float, nominal value of the parameter.
+            parameter_name:
+                str, name of parameter in PEtab parameter table, optional.
+            parameter_scale
+                str. scale of parameter in PEtab parameter table, optional.
+            lower_bound:
+                float, lower bound of parameter in PEtab parameter table,
+                optional.
+            upper_bound:
+                float, upper bound of parameter in PEtab parameter table,
+                optional.
+            estimate:
+                int, estimate flag of parameter in PEtab parameter table,
+                optional.
         """
         # if parameter exists: delete if overwrite
         if parameter_id in self.get_parameter_ids():
@@ -245,8 +257,19 @@ def add_ode(self,
                 initial_value: Union[float, str],
                 overwrite: bool = False):
         """
-        Adds a state/ODE. Overwrites an existing state/ODE with the same id,
-        if overwrite=True.
+        Add state/ODE.
+
+        Overwrite an existing state/ODE with the same id, if `overwrite==True`.
+
+        Arguments:
+            state_id:
+                str, state id
+            right_hand_side:
+                str or float, right hand side of the ODE.
+            initial_value:
+                str or float, initial value of the ODE at t=0
+            overwrite:
+                bool, indicates if an existing state/ODE should be overwritten
         """
         # if state exists: delete if overwrite
         if state_id in self.get_ode_ids():
@@ -267,8 +290,18 @@ def add_assignment(self,
                        formula: str,
                        overwrite: bool = False):
         """
-        Adds an assignment. Overwrites an existing assignment with the same id,
-        if overwrite=True.
+        Add assignment.
+
+        Overwrite an existing assignment with the same id, if
+        `overwrite==True`.
+
+        Arguments:
+            assignment_id:
+                str, function id
+            formula:
+                str, right hand side of assignment definition.
+            overwrite:
+                bool, indicates if an existing assignment should be overwritten
         """
         # if assignment exists: delete if overwrite
         if assignment_id in self.get_assignment_ids():
@@ -290,8 +323,19 @@ def add_function(self,
                      formula: str,
                      overwrite: bool = False):
         """
-        Adds a function. Overwrites an existing function with the same id,
-        if overwrite=True.
+        Add function.
+
+        Overwrite an existing function with the same id, if `overwrite==True`.
+
+        Arguments:
+            function_id:
+                str, function id
+            arguments:
+                str, arguments, separated by a comma
+            formula:
+                str, right hand side of the function definition
+            overwrite:
+                bool, indicates if an existing function should be overwritten
         """
         # if function exists: delete if overwrite
         if function_id in self.get_function_ids():
@@ -317,8 +361,27 @@ def add_observable(self,
                        observable_transformation: str = None,
                        noise_distribution: str = None):
         """
-        Adds an observable. Overwrites an existing observable with the same id,
-        if overwrite=True.
+        Add observable.
+
+        Observables are not represented inside an SBML and only play a role
+        when generating a PEtab problem see PEtabs observable table).
+
+        Overwrite an existing observable with the same id, if
+        `overwrite==True`.
+
+        Arguments:
+            observable_id:
+                str, observable id
+            observable_formula:
+                str, formula of the observable function
+            noise_formula:
+                str, formula of the noise
+            overwrite:
+                bool, indicates if an existing observable should be overwritten
+            observable_name:
+                Observable name. Optional.
+            observable_transformation:
+                Observable transformation ('lin'/'log'/'log10'). Optional
         """
         # if observable exists: delete if overwrite
         if observable_id in self.get_observable_ids():
@@ -343,24 +406,25 @@ def add_condition(self,
                       condition_dict: dict,
                       overwrite: bool = False,
                       condition_name: str = None):
-
         """
-        Adds a condition. Overwrites an existing condition with the same id,
-        if overwrite=True.
+        Add condition `condition_id`.
 
-        Arguments:
-        condition_id:
-            str, condition id
-        condition_dict:
-            dict, of the form {<parameter or state id>: <value>}.
-            Corresponds to entries in the PEtab condition table.
-            See details there.
-        overwrite:
-            bool, indicates if an existing condition should be overwritten
-        condition_name:
-            Condition name. Optional.
+        Conditions are not represented inside an SBML and only play a role
+        when generating a PEtab problem (see PEtabs condition table).
 
-        Returns:
+        Overwrite an existing condition with the same id, if `overwrite==True`.
+
+        Arguments:
+            condition_id:
+                str, condition id
+            condition_dict:
+                dict, of the form {<parameter or state id>: <value>}.
+                Corresponds to entries in the PEtab condition table.
+                See details there.
+            overwrite:
+                bool, indicates if an existing condition should be overwritten
+            condition_name:
+                Condition name. Optional.
         """
         # if condition exists: delete if overwrite
         if condition_id in self.get_condition_ids():
@@ -381,7 +445,8 @@ def _add_entry(self,
                    entry_dict: dict,
                    block_key: str):
         """
-        Adds the entry in 'entry_dict' to the block indexed by 'block_key'.
+        Add the entry in 'entry_dict' to the block indexed by 'block_key'.
+
         If 'overwrite=True', an existing value for that key is overwritten.
 
         Arguments:
@@ -389,8 +454,6 @@ def _add_entry(self,
             dict, that stores the new entry
         block_key:
             name, where the ids should be searched (e.g. 'parameters')
-
-        Returns:
         """
         # filter out None values and append
         filtered_dict = _filter_none_values(entry_dict)
@@ -398,35 +461,34 @@ def _add_entry(self,
 
     # functionalities to get ids
     def get_parameter_ids(self):
-        """Returns a list with all parameter ids."""
+        """Return a list with all parameter ids."""
         return self._get_ids('parameters', 'parameterId')
 
     def get_ode_ids(self):
-        """Returns a list with all state ids."""
+        """Return a list with all state ids."""
         return self._get_ids('odes', 'stateId')
 
     def get_assignment_ids(self):
-        """Returns a list with all assignment ids."""
+        """Return a list with all assignment ids."""
         return self._get_ids('assignments', 'assignmentId')
 
     def get_function_ids(self):
-        """Returns a list with all function ids."""
+        """Return a list with all function ids."""
         return self._get_ids('functions', 'functionId')
 
     def get_observable_ids(self):
-        """Returns a list with all observable ids."""
+        """Return a list with all observable ids."""
         return self._get_ids('observables', 'observableId')
 
     def get_condition_ids(self):
-        """Returns a list with all conditions ids."""
+        """Return a list with all conditions ids."""
         return self._get_ids('conditions', 'conditionId')
 
     def _get_ids(self,
                  block_key: str,
                  index_key: str):
         """
-        Returns all ids in the block named `block_key` in self._yaml_model.
-        The ids have the key index_key.
+        Return all ids in the corresponding block.
 
         Arguments:
         block_key:
@@ -443,7 +505,11 @@ def _get_ids(self,
     # functionalities to get entry by Id:
     def get_parameter_by_id(self,
                             parameter_id: str):
-        """returns dict for corresponding parameter."""
+        """
+        Return dict for corresponding parameter.
+
+        Raise a `ValueError, if the parameter does not exist.
+        """
         if parameter_id not in self.get_parameter_ids():
             raise IndexError(f'Could not find parameter {parameter_id}.')
 
@@ -453,7 +519,11 @@ def get_parameter_by_id(self,
 
     def get_ode_by_id(self,
                       state_id: str):
-        """returns dict for corresponding ODE/state."""
+        """
+        Return dict for corresponding ODE/state.
+
+        Raise a `ValueError`, if the ODE/state does not exist.
+        """
         if state_id not in self.get_ode_ids():
             raise IndexError(f'Could not find state/ODE {state_id}.')
 
@@ -463,7 +533,11 @@ def get_ode_by_id(self,
 
     def get_assignment_by_id(self,
                              assignment_id: str):
-        """returns dict for corresponding assignment."""
+        """
+        Return dict for corresponding assignment.
+
+        Raise a `ValueError`, if the assignment does not exist.
+        """
         if assignment_id not in self.get_assignment_ids():
             raise IndexError(f'Could not find assignment {assignment_id}.')
 
@@ -473,7 +547,11 @@ def get_assignment_by_id(self,
 
     def get_function_by_id(self,
                            function_id: str):
-        """returns dict for corresponding function."""
+        """
+        Return dict for corresponding function.
+
+        Raise a `ValueError`, if the function does not exist.
+        """
         if function_id not in self.get_function_ids():
             raise IndexError(f'Could not find function {function_id}.')
 
@@ -483,7 +561,11 @@ def get_function_by_id(self,
 
     def get_observable_by_id(self,
                              observable_id: str):
-        """returns dict for corresponding observable."""
+        """
+        Return dict for corresponding observable.
+
+        Raise a `ValueError`, if the observable does not exist.
+        """
         if observable_id not in self.get_observable_ids():
             raise IndexError(f'Could not find observable {observable_id}.')
 
@@ -493,7 +575,11 @@ def get_observable_by_id(self,
 
     def get_condition_by_id(self,
                             condition_id: str):
-        """returns dict for corresponding condition."""
+        """
+        Return dict for corresponding condition.
+
+        Raise a `ValueError`, if the condition does not exist.
+        """
         if condition_id not in self.get_condition_ids():
             raise IndexError(f'Could not find condition {condition_id}.')
 
@@ -506,6 +592,8 @@ def _get_entry_by_id(self,
                          index_key: str,
                          entry_id: str):
         """
+        Get entry by id in corresponding block.
+
         Returns the entry with id 'entry_id' in the block named
         `block_key` in self._yaml_model. 'index_key' gives the name of the
         id-key.
@@ -532,7 +620,9 @@ def _get_entry_by_id(self,
     def delete_parameter(self,
                          parameter_id: str):
         """
-        Deletes a parameter. Raises a ValueError, if parameter does not exist.
+        Delete a parameter.
+
+        Raise a ValueError, if parameter does not exist.
         """
         if not self._delete_entry('parameters',
                                   'parameterId',
@@ -544,7 +634,9 @@ def delete_parameter(self,
     def delete_ode(self,
                    state_id: str):
         """
-        Deletes a state + ODE. Raises a ValueError, if state does not exist.
+        Delete a state + ODE.
+
+        Raise a ValueError, if state does not exist.
         """
         if not self._delete_entry('odes',
                                   'stateId',
@@ -556,8 +648,9 @@ def delete_ode(self,
     def delete_assignment(self,
                           assignment_id: str):
         """
-        Deletes an assignment. Raises a ValueError,
-        if assignment does not exist.
+        Delete an assignment.
+
+        Raise a ValueError, if assignment does not exist.
         """
         if not self._delete_entry('assignments',
                                   'assignmentId',
@@ -569,7 +662,9 @@ def delete_assignment(self,
     def delete_function(self,
                         function_id: str):
         """
-        Deletes a function. Raises a ValueError, if function does not exist.
+        Delete a function.
+
+        Raise a ValueError, if function does not exist.
         """
         if not self._delete_entry('functions',
                                   'functionId',
@@ -581,8 +676,9 @@ def delete_function(self,
     def delete_observable(self,
                           observable_id: str):
         """
-        Deletes an observable. Raises a ValueError,
-        if observable does not exist.
+        Delete an observable.
+
+        Raise a ValueError, if observable does not exist.
         """
         if not self._delete_entry('observables',
                                   'observableId',
@@ -594,7 +690,9 @@ def delete_observable(self,
     def delete_condition(self,
                          condition_id: str):
         """
-        Deletes a condition. Raises a ValueError, if condition does not exist.
+        Delete a condition.
+
+        Raise a ValueError, if condition does not exist.
         """
         if not self._delete_entry('conditions',
                                   'conditionId',
@@ -608,7 +706,9 @@ def _delete_entry(self,
                       index_key: str,
                       deleted_object_id: str):
         """
-        Deletes the entry with id 'deleted_object_id' in the block named
+        Delete entry in block.
+
+        Delete the entry with id 'deleted_object_id' in the block named
         `block_key` in self._yaml_model. 'index_key' gives the name of the
         id-key.
 
@@ -623,7 +723,6 @@ def _delete_entry(self,
         Returns:
             Bool, that indicates, whether deletion was successful.
         """
-
         # Check if block exists
         for i, entry in enumerate(self._yaml_model[block_key]):
 
@@ -637,7 +736,7 @@ def _delete_entry(self,
 
 def _filter_none_values(d: dict):
     """
-    Filters out the key-value pairs with None  as value.
+    Filter out the key-value pairs with `None` as value.
 
     Arguments:
     d
diff --git a/yaml2sbml/__init__.py b/yaml2sbml/__init__.py
index b11f69d..bcf060d 100644
--- a/yaml2sbml/__init__.py
+++ b/yaml2sbml/__init__.py
@@ -4,7 +4,7 @@
 from .version import __version__   # noqa: F401
 
 # API
-from yaml2sbml.yaml2sbml import yaml2sbml
-from yaml2sbml.yaml2PEtab import yaml2petab, validate_petab_tables
-from yaml2sbml.yaml_validation import validate_yaml
-from yaml2sbml.YamlModel import YamlModel
+from .yaml2sbml import yaml2sbml
+from .yaml2PEtab import yaml2petab, validate_petab_tables
+from .yaml_validation import validate_yaml
+from .YamlModel import YamlModel
diff --git a/yaml2sbml/version.py b/yaml2sbml/version.py
index 7fd229a..fc79d63 100644
--- a/yaml2sbml/version.py
+++ b/yaml2sbml/version.py
@@ -1,1 +1,1 @@
-__version__ = '0.2.0'
+__version__ = '0.2.1'
diff --git a/yaml2sbml/yaml2PEtab.py b/yaml2sbml/yaml2PEtab.py
index 9a709df..51cd04e 100644
--- a/yaml2sbml/yaml2PEtab.py
+++ b/yaml2sbml/yaml2PEtab.py
@@ -1,47 +1,46 @@
+"""Translate ODEs in the YAML format into PEtab."""
 import argparse
 import os
-import pandas as pd
 import warnings
 
 import libsbml as sbml
+import pandas as pd
 import petab
 import yaml
+from pathlib import Path
 
 
 from .yaml2sbml import _parse_yaml_dict, _load_yaml_file
 from .yaml_validation import _validate_yaml_from_dict
 
 
-def yaml2petab(yaml_file: str,
+def yaml2petab(yaml_dir: str,
                output_dir: str,
-               model_name: str,
+               sbml_name: str,
                petab_yaml_name: str = None,
                measurement_table_name: str = None):
     """
-    Takes in a yaml file with the ODE specification, parses it, converts
+    Translate a YAML model into a PEtab model.
+
+    Takes a YAML file with the ODE specification, parses it, converts
     it into SBML format, and writes the SBML file. Further it translates the
     given information into PEtab tables.
 
-    If a petab_yaml_name is given, a .yaml file is created, that organizes
-    the petab problem. If additionally a measurement_table_file_name is
-    specified, this file name is written into the created .yaml file.
+    If a `petab_yaml_name` is given, a YAML file is created, that organizes
+    the PEtab problem. If additionally a `measurement_table_file_name` is
+    specified, this file name is written into the created YAML file.
 
     Arguments:
-        yaml_file : path to the yaml file with the ODEs specification
+        yaml_dir : path to the YAML file with the ODEs specification
         output_dir: path the output file(s) are be written out
-        model_name: name of SBML model
-        petab_yaml_name: name of yaml organizing the PEtab problem.
+        sbml_name: name of SBML model
+        petab_yaml_name: name of YAML organizing the PEtab problem.
         measurement_table_name: Name of measurement table
-
-    Returns:
-
-    Raises:
-
     """
-    yaml_model_dict = _load_yaml_file(yaml_file)
+    yaml_model_dict = _load_yaml_file(yaml_dir)
     _yaml2petab(yaml_model_dict,
                 output_dir,
-                model_name,
+                sbml_name,
                 petab_yaml_name,
                 measurement_table_name)
 
@@ -52,12 +51,7 @@ def _yaml2petab(yaml_model_dict: dict,
                 petab_yaml_name: str = None,
                 measurement_table_name: str = None):
     """
-    Similar to 'yaml2petab', but takes a yaml_model_dict as input,
-    instead of a file path.
-
-    Takes in a yaml model dict with the ODE specification, parses it, converts
-    it into SBML format, and writes the SBML file. Further it translates the
-    given information into PEtab tables.
+    Similar to 'yaml2petab', but takes a yaml_model_dict as input.
 
     If a petab_yaml_name is given, a .yaml file is created, that organizes
     the petab problem. If additionally a measurement_table_file_name is
@@ -69,11 +63,6 @@ def _yaml2petab(yaml_model_dict: dict,
         model_name: name of SBML model
         petab_yaml_name: name of yaml organizing the PEtab problem.
         measurement_table_name: Name of measurement table
-
-    Returns:
-
-    Raises:
-
     """
     # validate yaml
     _validate_yaml_from_dict(yaml_model_dict)
@@ -89,13 +78,19 @@ def _yaml2petab(yaml_model_dict: dict,
 
     with warnings.catch_warnings():
         warnings.simplefilter("ignore", category=UserWarning)
-        sbml_as_string = _parse_yaml_dict(yaml_model_dict)
+        sbml_as_string = _parse_yaml_dict(yaml_model_dict,
+                                          model_name)
 
     with open(sbml_dir, 'w') as f_out:
         f_out.write(sbml_as_string)
 
     # create petab tsv files:
-    _create_petab_tables_from_yaml(yaml_model_dict, output_dir)
+    if model_name.endswith('.xml') or model_name.endswith('.sbml'):
+        model_name = Path(model_name).stem
+
+    _create_petab_tables_from_yaml(yaml_model_dict,
+                                   output_dir,
+                                   model_name)
 
     # create yaml file, that organizes the petab problem:
     if (petab_yaml_name is None) and (measurement_table_name is not None):
@@ -109,25 +104,30 @@ def _yaml2petab(yaml_model_dict: dict,
                                    output_dir,
                                    sbml_dir,
                                    petab_yaml_name,
+                                   model_name,
                                    measurement_table_name)
     # validate PEtab tables:
     validate_petab_tables(sbml_dir, output_dir)
 
 
 def _create_petab_tables_from_yaml(yaml_dict: dict,
-                                   output_dir: str):
+                                   output_dir: str,
+                                   model_name: str):
     """
-    Parses the yaml dict to a PEtab observable/parameter table.
+    Parse the YAML dict to a PEtab observable/parameter table.
+
+    The files will be named (if the corresponding information is in the YAML):
+    `parameters_<model_name>.tsv`,
+    `observables_<model_name>.tsv` and
+    `experimental_conditions_<model_name>.tsv`.
 
     Arguments:
         yaml_dict: dict, that contains the yaml file.
         output_dir: directory, where the PEtab tables should be written.
-
-    Raises:
-
     """
     parameter_table = _create_parameter_table(yaml_dict)
-    parameter_table.to_csv(os.path.join(output_dir, 'parameter_table.tsv'),
+    parameter_table.to_csv(os.path.join(output_dir,
+                                        f'parameters_{model_name}.tsv'),
                            sep='\t',
                            index=False)
 
@@ -135,39 +135,39 @@ def _create_petab_tables_from_yaml(yaml_dict: dict,
     if 'observables' in yaml_dict.keys():
         observable_table = _create_observable_table(yaml_dict)
         observable_table.to_csv(os.path.join(output_dir,
-                                             'observable_table.tsv'),
+                                             f'observables_{model_name}.tsv'),
                                 sep='\t',
                                 index=False)
 
     # create PEtab condition table, if conditions occur in the yaml file.
     if 'conditions' in yaml_dict.keys():
         condition_table = _create_condition_table(yaml_dict)
-        condition_table.to_csv(os.path.join(output_dir, 'condition_table.tsv'),
-                               sep='\t',
-                               index=False)
+        condition_table.to_csv(
+            os.path.join(output_dir,
+                         f'experimental_conditions_{model_name}.tsv'),
+            sep='\t',
+            index=False)
 
 
 def _create_petab_problem_yaml(yaml_dict: dict,
                                output_dir: str,
                                sbml_dir: str,
                                petab_yaml_name: str,
+                               model_name: str,
                                measurement_table_name: str = None):
     """
-    Creates the yaml file, that can be used for structuring a PEtab problem in
-    the corresponding directory.
+    Create a YAML file, that can be used for defining a PEtab problem.
 
     Arguments:
-        yaml_dict: dict, that contains the yaml file.
+        yaml_dict: dict, that contains the YAML file.
         output_dir: directory, where the PEtab tables should be written.
         sbml_dir: directory of the SBML model.
-        petab_yaml_name: name of file, where PEtab yaml is written.
+        petab_yaml_name: name of file, where PEtab YAML is written.
+        model_name: name of the model, in order to name the PEtab tables.
         measurement_table_name: directory of the  measurement table.
-
-    Raises:
-
     """
     petab_yaml_dict = {'format_version': 1,
-                       'parameter_file': 'parameter_table.tsv',
+                       'parameter_file': f'parameters_{model_name}.tsv',
                        'problems': [{'sbml_files':
                                          [os.path.basename(sbml_dir)]}]}
 
@@ -175,11 +175,11 @@ def _create_petab_problem_yaml(yaml_dict: dict,
 
     if 'observables' in yaml_dict.keys():
         petab_yaml_dict['problems'][0]['observable_files'] = \
-            ['observable_table.tsv']
+            [f'observables_{model_name}.tsv']
 
     if 'conditions' in yaml_dict.keys():
         petab_yaml_dict['problems'][0]['condition_files'] = \
-            ['condition_table.tsv']
+            [f'experimental_conditions_{model_name}.tsv']
 
     if measurement_table_name is not None:
         petab_yaml_dict['problems'][0]['measurement_files'] = \
@@ -193,18 +193,14 @@ def _create_petab_problem_yaml(yaml_dict: dict,
 
 def _create_parameter_table(yaml_dict: dict):
     """
-    Creates a parameter table from the parameter block in the given yaml_dict.
+    Create a parameter table from the parameter block in `yaml_dict.
 
-    Arguments:
+    Arguments:`
         yaml_dict
 
     Returns:
         parameter_table: pandas data frame containing the parameter table.
-
-    Raises:
-
     """
-
     return _create_petab_table(yaml_dict['parameters'],
                                petab.PARAMETER_DF_REQUIRED_COLS,
                                petab.PARAMETER_DF_OPTIONAL_COLS)
@@ -212,8 +208,7 @@ def _create_parameter_table(yaml_dict: dict):
 
 def _create_observable_table(yaml_dict: dict):
     """
-    Creates an observable table from the observable block
-    in the given yaml_dict.
+    Create an observable table from the observable block in `yaml_dict`.
 
     Arguments:
         yaml_dict
@@ -221,11 +216,7 @@ def _create_observable_table(yaml_dict: dict):
     Returns:
         observable_table: pandas data frame containing the observable table.
             (if observable block is not empty, else None)
-
-    Raises:
-
     """
-
     return _create_petab_table(yaml_dict['observables'],
                                petab.OBSERVABLE_DF_REQUIRED_COLS,
                                petab.OBSERVABLE_DF_OPTIONAL_COLS)
@@ -233,7 +224,7 @@ def _create_observable_table(yaml_dict: dict):
 
 def _create_condition_table(yaml_dict: dict):
     """
-    Creates a condition table from the observable block in the given yaml_dict.
+    Create a condition table from the condition block in `yaml_dict.
 
     Arguments:
         yaml_dict
@@ -241,9 +232,6 @@ def _create_condition_table(yaml_dict: dict):
     Returns:
         condition_table: pandas data frame containing the condition table.
             (if condition block is not empty, else None)
-
-    Raises:
-
     """
     mandatory_id_list = [petab.CONDITION_ID]
 
@@ -256,26 +244,30 @@ def _create_condition_table(yaml_dict: dict):
                                optional_id_list)
 
 
-def validate_petab_tables(sbml_dir: str, output_dir: str):
+def validate_petab_tables(sbml_dir: str,
+                          output_dir: str):
     """
-    Validates the petab tables via petab.lint. Throws an error,
-    if the petab tables do not follow petab format standard.
+    Validate the PEtab tables via `petab.lint`.
+
+    Throws an error if the PEtab tables do not follow the PEtab format
+    standard.
 
     Arguments:
         sbml_dir: directory of the sbml
         output_dir: output directory for petab files
 
-    Returns:
-
     Raises:
         Errors are raised by lint, if PEtab files are invalid...
-
     """
     model = sbml.readSBML(sbml_dir).getModel()
+    model_name = model.getId()
 
-    parameter_file_dir = os.path.join(output_dir, 'parameter_table.tsv')
-    observable_file_dir = os.path.join(output_dir, 'observable_table.tsv')
-    condition_table_dir = os.path.join(output_dir, 'condition_table.tsv')
+    parameter_file_dir = \
+        os.path.join(output_dir, f'parameters_{model_name}.tsv')
+    observable_file_dir = \
+        os.path.join(output_dir, f'observables_{model_name}.tsv')
+    condition_table_dir = \
+        os.path.join(output_dir, f'experimental_conditions_{model_name}.tsv')
 
     # check observable table, if the table exists
     if os.path.exists(observable_file_dir):
@@ -298,6 +290,7 @@ def validate_petab_tables(sbml_dir: str, output_dir: str):
     parameter_df = pd.read_csv(parameter_file_dir,
                                sep='\t',
                                index_col='parameterId')
+
     petab.lint.check_parameter_df(parameter_df,
                                   sbml_model=model,
                                   observable_df=observable_df)
@@ -307,7 +300,7 @@ def _create_petab_table(block_list: list,
                         mandatory_id_list: list,
                         optional_id_list: id):
     """
-    Creates a PEtab table from the block_list in the yaml_dict.
+    Create a PEtab table from the block_list in the yaml_dict.
 
     Arguments:
         block_list: entry from yaml_dict.
@@ -316,11 +309,7 @@ def _create_petab_table(block_list: list,
 
     Returns:
         petab_table: pandas data frame containing the petab table.
-
-    Raises:
-
     """
-
     petab_table = pd.DataFrame({col_id: [] for col_id in mandatory_id_list})
 
     for row in block_list:
@@ -336,8 +325,9 @@ def _create_petab_table(block_list: list,
 
 def _petab_table_add_row(petab_table: pd.DataFrame, row_dict: dict):
     """
-    Adds a row defined by row_dict to the petab_table given by petab_table.
-    In the end the last row of petab_table has the entries
+    Add a row defined by row_dict to the petab_table given by petab_table.
+
+    After the function call the last row of petab_table has the entries
 
         petab_table[-1, key] = row_dict[key]
 
@@ -346,11 +336,6 @@ def _petab_table_add_row(petab_table: pd.DataFrame, row_dict: dict):
     Arguments:
         petab_table: pd.DataFrame containing the current petab table.
         row_dict: dict, that contains the values of the new row.
-
-    Returns:
-
-    Raises:
-
     """
     n_rows = petab_table.shape[0]
 
@@ -359,7 +344,7 @@ def _petab_table_add_row(petab_table: pd.DataFrame, row_dict: dict):
 
 
 def main():
-
+    """Command-Line Interface."""
     parser = argparse.ArgumentParser(description='Takes in an ODE model in '
                                      '.yaml and converts it to a PEtab file.')
 
diff --git a/yaml2sbml/yaml2sbml.py b/yaml2sbml/yaml2sbml.py
index ed879ad..9c5b283 100644
--- a/yaml2sbml/yaml2sbml.py
+++ b/yaml2sbml/yaml2sbml.py
@@ -1,40 +1,59 @@
+"""Translate ODEs in the YAML format into SBML."""
 import argparse
 import warnings
+from pathlib import Path
 
 import libsbml as sbml
 import yaml
+from yaml.scanner import ScannerError
 
 from .yaml_validation import _validate_yaml_from_dict
 
 
-def yaml2sbml(yaml_file: str, sbml_file: str):
+def yaml2sbml(yaml_dir: str,
+              sbml_dir: str,
+              observables_as_assignments: bool = False):
     """
-    Takes in a yaml file with the ODE specification, parses it, converts it
-    into SBML format, and writes the SBML file.
+    Parse a YAML file with the specification of ODEs and write it to SBML.
+
+    SBML is written within this function. If `observables_as_assignments=True`,
+    observables will be translated into parameter assignments of the form
+    `observable_<observable_id>`.
 
     Arguments:
-        yaml_file : path to the yaml file with the ODEs specification
-        sbml_file: path to the SBML file to be written out
+        yaml_dir: directory to the YAML file with the ODEs specification
+        sbml_dir: directory to the SBML file to be written out
+        observables_as_assignments: indicates whether there should be
+            parameter assignments of the form `observable_<observable_id>`.
+    """
+    # check file extension in sbml_dir
+    if not (sbml_dir.endswith('.xml') or sbml_dir.endswith('.sbml')):
+        raise ValueError('sbml_dir should end with .xml or .sbml.')
 
-    Returns:
+    model_name = Path(sbml_dir).stem
 
-    Raises:
-
-    """
-    sbml_as_string = _parse_yaml(yaml_file)
+    sbml_as_string = _parse_yaml(yaml_dir,
+                                 model_name,
+                                 observables_as_assignments)
 
     # write sbml file
-    with open(sbml_file, 'w') as f_out:
+    with open(sbml_dir, 'w') as f_out:
         f_out.write(sbml_as_string)
 
 
-def _parse_yaml(yaml_file: str) -> str:
+def _parse_yaml(yaml_dir: str,
+                model_name: str,
+                observables_as_assignments: bool = False) -> str:
     """
-    Takes in a yaml file with the specification of ODEs, parses it, and
-    returns the corresponding SBML string.
+    Parse a YAML file with the specification of ODEs to SBML.
+
+    The SBML is returned as string.
 
     Arguments:
-        yaml_file: path to the yaml file with the ODEs specification
+        yaml_dir: path to the YAML file with the ODEs specification
+        model_name: model name as specified in the SBML
+        observables_as_assignments: indicates if observables should be
+            translated into parameter assignments
 
     Returns:
         sbml_string: a string containing the ODEs in SBML format
@@ -42,35 +61,52 @@ def _parse_yaml(yaml_file: str) -> str:
     Raises:
         SystemExit
     """
-    yaml_dict = _load_yaml_file(yaml_file)
+    yaml_dict = _load_yaml_file(yaml_dir)
     _validate_yaml_from_dict(yaml_dict)
-    sbml_string = _parse_yaml_dict(yaml_dict)
+
+    sbml_string = _parse_yaml_dict(yaml_dict,
+                                   model_name,
+                                   observables_as_assignments)
 
     return sbml_string
 
 
-def _parse_yaml_dict(yaml_dict) -> str:
+def _parse_yaml_dict(yaml_dict: dict,
+                     model_name: str,
+                     observables_as_assignments: bool = False) -> str:
     """
-    Generates a string, containing the SBML from a yaml_dict.
+    Generate a string, containing the SBML from a `yaml_dict.
 
     Arguments:
-        yaml_dict: dictionary, containing to the yaml file with the ODEs
-                   specification.
+        yaml_dict: dictionary, containing to the YAML file with the ODEs
+                   specification.`
+        model_name: model name as specified in the SBML
+        observables_as_assignments: indicates if observables should be
+            translated into parameter assignments
 
     Returns:
         sbml_string: a string containing the ODEs in SBML format.
 
     """
-
     try:
         document = sbml.SBMLDocument(3, 1)
     except ValueError:
         raise SystemExit('Could not create SBMLDocument object')
 
     model = document.createModel()
+
+    # remove file extension
+    if model_name.endswith('.xml') or model_name.endswith('.sbml'):
+        model_name = Path(model_name).stem
+
+    model.setId(model_name)
+    model.setName(model_name)
+
     model = _create_compartment(model)
 
-    _convert_yaml_blocks_to_sbml(model, yaml_dict)
+    _convert_yaml_blocks_to_sbml(model,
+                                 yaml_dict,
+                                 observables_as_assignments)
 
     # check consistency and give warnings for errors in SBML:
     if document.checkConsistency():
@@ -88,17 +124,15 @@ def _parse_yaml_dict(yaml_dict) -> str:
 
 def _create_compartment(model: sbml.Model):
     """
-    Creates a default compartment for the model.
-    We don't support multiple compartments at the moment.
+    Create a default compartment for the model.
+
+    yaml2sbml doesn't support multiple compartments.
 
     Arguments:
         model: SBML model
 
     Returns:
         model: SBML model with added compartment
-
-    Raises:
-
     """
     c = model.createCompartment()
     c.setId('Compartment')
@@ -110,66 +144,72 @@ def _create_compartment(model: sbml.Model):
 
 def _load_yaml_file(yaml_file: str) -> dict:
     """
-    Loads yaml file and returns the resulting dictionary.
+    Load YAML file from a dictionary.
 
     Arguments:
-        yaml_file: SBML model
+        yaml_file: directory to the YAML model
 
     Returns:
-        yaml_dic: dictionary with parsed yaml file contents
-
+        yaml_dic: dictionary with parsed YAML file contents
     Raises:
-
+        RuntimeError, if YAML can not be parsed, e.g. due to incorrectly
+            formatted entries
     """
-    with open(yaml_file, 'r') as f_in:
-        yaml_contents = f_in.read()
-        yaml_dic = yaml.full_load(yaml_contents)
+    try:
 
-    return yaml_dic
+        with open(yaml_file, 'r') as f_in:
+            yaml_contents = f_in.read()
+            yaml_dict = yaml.full_load(yaml_contents)
 
+    except ScannerError:
+        raise RuntimeError('YAML file can not be parsed due to a Scanner '
+                           'Error. This commonly happens if formulas begin '
+                           'with a minus. Please set them inside of brackets '
+                           '"(...)" or quotation marks.')
+    return yaml_dict
 
-def _convert_yaml_blocks_to_sbml(model: sbml.Model, yaml_dic: dict):
+
+def _convert_yaml_blocks_to_sbml(model: sbml.Model,
+                                 yaml_dict: dict,
+                                 observables_as_assignments):
     """
-    Converts each block in the yaml dictionary to SBML.
+    Convert each block in the YAML dictionary to SBML.
 
     Arguments:
         model: SBML model
-        yaml_dic: dictionary with yaml contents
+        yaml_dict: dictionary with YAML contents
 
     Returns:
         model: SBML model with added entities
+    """
 
-    Raises:
+    def _read_observables_with_assignments(model, block):
+        return _read_observables_block(model,
+                                       block,
+                                       observables_as_assignments)
 
-    """
     function_dict = {'time': _read_time_block,
                      'parameters': _read_parameters_block,
                      'assignments': _read_assignments_block,
                      'functions': _read_functions_block,
-                     'observables': _read_observables_block,
+                     'observables': _read_observables_with_assignments,
                      'odes': _read_odes_block,
                      'conditions': _read_conditions_block}
 
-    for block in yaml_dic:
-        function_dict[block](model, yaml_dic[block])
+    for block in yaml_dict:
+        function_dict[block](model, yaml_dict[block])
 
     return model
 
 
 def _read_time_block(model: sbml.Model, time_dic: dict):
     """
-    Reads and processes the time block.
+    Read and process the time block.
 
     Arguments:
         model: SBML model to which the rate rule will be added.
-        time_dic: a dictionary with the time block in the ODE yaml file.
-
-    Returns:
-
-    Raises:
-
+        time_dic: a dictionary with the time block in the ODE YAML file.
     """
-
     if time_dic['variable'] == 'time':
         return
     else:
@@ -178,16 +218,11 @@ def _read_time_block(model: sbml.Model, time_dic: dict):
 
 def _create_time(model: sbml.Model, time_var: str):
     """
-    Creates the time variable, add assignment to 'time'
+    Create the time variable, add assignment to 'time'.
 
     Arguments:
         model: the SBML model to which the species will be added.
         time_var: str, the time variable
-
-    Returns:
-
-    Raises:
-
     """
     time_parameter = model.createParameter()
     time_parameter.setId(time_var)
@@ -201,19 +236,14 @@ def _create_time(model: sbml.Model, time_var: str):
 
 def _read_parameters_block(model: sbml.Model, parameter_list: list):
     """
-    Reads and processes the parameters block in the ODE yaml file. In
-    particular, it reads the parameters and adds them to the given SBML model.
+    Read and process the parameters block in the YAML file.
+
     The expected format for parameter definition is
     {'parameterId': <parameterId>, 'nominalValue': <nominalValue>}
 
     Arguments:
         model: the SBML model
         parameter_list: block containing the parameter definitions
-
-    Returns:
-
-    Raises:
-
     """
     for parameter_def in parameter_list:
         if 'nominalValue' in parameter_def.keys():
@@ -226,18 +256,14 @@ def _read_parameters_block(model: sbml.Model, parameter_list: list):
 
 def _create_parameter(model: sbml.Model, parameter_id: str, value: str = None):
     """
-    Creates a parameter and adds it to the given SBML model.
+    Create a parameter and add it to the given SBML model.
+
     Units are set as dimensionless by default.
 
     Arguments:
         model: the SBML model to which the parameter will be added.
         parameter_id: the parameter ID
         value: the parameter value, if value is None, no parameter is set.
-
-    Returns:
-
-    Raises:
-
     """
     k = model.createParameter()
     k.setId(parameter_id)
@@ -252,22 +278,17 @@ def _create_parameter(model: sbml.Model, parameter_id: str, value: str = None):
 
 def _read_assignments_block(model: sbml.Model, assignment_list: list):
     """
-    Reads and processes the assignments block in the ODE yaml file. In
-    particular, it reads the assignments and adds them to the given SBML file.
+    Read and process the assignments block in the YAML file.
+
     The expected format of a state definition is:
     {'assignmentId': <assignmentId>, 'formula': <formula>}
 
-    This is used to assign a formula (probably time-dependent) to a variable.
+    This is used to assign a formula (possibly time-dependent) to a variable.
 
     Arguments:
         model: the SBML model
         assignment_list: a list of dictionaries where each entry is an
                          assignment definition
-
-    Returns:
-
-    Raises:
-
     """
     for assignment_def in assignment_list:
         _create_assignment(model,
@@ -277,16 +298,12 @@ def _read_assignments_block(model: sbml.Model, assignment_list: list):
 
 def _create_assignment(model: sbml.Model, assignment_id: str, formula: str):
     """
-    Creates an  assignment rule, that assigns id to formula.
+    Create an assignment rule, that assigns <id> to <formula>.
 
     Arguments:
         model: SBML model to which the assignment rule will be added.
         assignment_id: str, the id of the assignment rule
         formula: str: contains the equation for the assignment rule
-    Returns:
-
-    Raises:
-
     """
     assignment_parameter = model.createParameter()
     assignment_parameter.setId(assignment_id)
@@ -301,9 +318,8 @@ def _create_assignment(model: sbml.Model, assignment_id: str, formula: str):
 
 def _read_functions_block(model: sbml.Model, functions_list: list):
     """
-    Reads and processes the functions block in the ODE yaml file.
-    In particular, it reads the functions and adds them to the given SBML file
-    as functionDefinitions.
+    Read and process the functions block in the YAML file.
+
     The expected format of a function definition is:
         {'functionId': <functionId>,
          'arguments': <arguments>,
@@ -313,11 +329,6 @@ def _read_functions_block(model: sbml.Model, functions_list: list):
         model: a SBML model
         functions_list: a list of dictionaries where each entry is a
                         function definition
-
-    Returns:
-
-    Raises:
-
     """
     for function_def in functions_list:
         _create_function(model,
@@ -331,18 +342,13 @@ def _create_function(model: sbml.Model,
                      arguments: str,
                      formula: str):
     """
-    Creates a functionDefinition and adds it to the given SBML model.
+    Create a function definition and add it to the SBML model.
 
     Arguments:
         model: SBML model to which the function will be added.
         function_id: the function id/name
         arguments: the arguments of the function (species AND parameters)
         formula: the formula of the function
-
-    Returns:
-
-    Raises:
-
     """
     f = model.createFunctionDefinition()
     f.setId(function_id)
@@ -352,8 +358,9 @@ def _create_function(model: sbml.Model,
 
 def _read_odes_block(model: sbml.Model, odes_list: list):
     """
-    Reads and processes the odes block in the ODE yaml file.
-    In particular, it reads the odes and adds a species for the corresponding
+    Read and process the odes block in the YAML file.
+
+    In particular, read the ODEs and add a species for the corresponding
     state and the right hand side as rateRules to the given SBML file.
 
     The expected format of an ode definition is:
@@ -363,11 +370,6 @@ def _read_odes_block(model: sbml.Model, odes_list: list):
     Arguments:
         model: a SBML model
         odes_list: block of ODE definitions
-
-    Returns:
-
-    Raises:
-
     """
     for ode_def in odes_list:
         _create_species(model, ode_def['stateId'], ode_def['initialValue'])
@@ -376,7 +378,8 @@ def _read_odes_block(model: sbml.Model, odes_list: list):
 
 def _create_species(model: sbml.Model, species_id: str, initial_amount: str):
     """
-    Creates a species and adds it to the given SBML model.
+    Create a species and add it to the SBML model.
+
     Units are set as dimensionless by default.
 
     Arguments:
@@ -386,9 +389,6 @@ def _create_species(model: sbml.Model, species_id: str, initial_amount: str):
 
     Returns:
         s: the SBML species
-
-    Raises:
-
     """
     s = model.createSpecies()
     s.setId(species_id)
@@ -413,18 +413,14 @@ def _create_species(model: sbml.Model, species_id: str, initial_amount: str):
 
 def _create_rate_rule(model: sbml.Model, species: str, formula: str):
     """
-    Creates a SBML rateRule for a species and adds it to the given model.
-    This is where the ODEs from the text file are encoded.
+    Create an SBML rateRule for a species and add it to the SBML model.
+
+    This is where the ODEs are encoded.
 
     Arguments:
         model: SBML model to which the rate rule will be added.
         species: the species name of the ODE
         formula: the right-hand-side of the ODE
-
-    Returns:
-
-    Raises:
-
     """
     r = model.createRateRule()
     r.setId('d/dt_' + species)
@@ -433,9 +429,12 @@ def _create_rate_rule(model: sbml.Model, species: str, formula: str):
     r.setMath(math_ast)
 
 
-def _read_observables_block(model: sbml.Model, observable_list: list):
+def _read_observables_block(model: sbml.Model,
+                            observable_list: list,
+                            observables_as_assignments: bool):
     """
-    Reads an processes the observables block in the ODE yaml file.
+    Read and process the observables block in the YAML file.
+
     Since the observables are not represented in the SBML, it only gives
     a warning to inform the user.
 
@@ -443,51 +442,60 @@ def _read_observables_block(model: sbml.Model, observable_list: list):
         model: SBML model (libsbml)
         observable_list: observables block containing all
                          observable definitions.
-
-    Returns:
-
-    Raises:
-
-    """
-    warnings.warn('Observables are not represented in the SBML and therefore '
-                  'only have an effect the output, when called via yaml2PEtab')
+        observables_as_assignments: indicates whether there should be
+            parameter assignments of the form `observable_<observable_id>`.
+    """
+    if observables_as_assignments:
+        for observable_def in observable_list:
+            _create_assignment(model,
+                               f'observable_{observable_def["observableId"]}',
+                               observable_def['observableFormula'])
+    else:
+        warnings.warn(
+            'Observables are not represented in the SBML and therefore only '
+            'have an effect on the output when called via yaml2petab')
 
 
 def _read_conditions_block(model: sbml.Model, conditions_list: list):
     """
-    Reads an processes the conditions block in the ODE yaml file.
-    Since the conditions are not represented in the SBML, it only gives
+    Read and process the conditions block in the YAML file.
+
+    Since conditions are not represented in the SBML, it only gives
     a warning to inform the user.
 
     Arguments:
         model: SBML model (libsbml)
         conditions_list: conditions block containing all
                          conditions definitions.
-
-    Returns:
-
-    Raises:
-
     """
-    warnings.warn('Conditions are not represented in the SBML and therefore '
-                  'only have an effect the output, when called via yaml2PEtab')
+    warnings.warn(
+        'Conditions are not represented in the SBML and therefore only have '
+        'an effect on the output when called via yaml2petab')
 
 
 def main():
+    """Command-Line Interface."""
     parser = argparse.ArgumentParser(
         description='Takes in an ODE model in .yaml and converts it to SBML.')
     parser.add_argument('yaml_file', type=str, help='Path to input YAML file.')
     parser.add_argument('sbml_file', type=str,
                         help='Path to output SBML file.')
+    parser.add_argument('-o', '--observables_as_assignments',
+                        action='store_true',
+                        help='Optional argument, flag, which indicates, if '
+                             'observables should be represented in the SBML'
+                             'as assignments. Potential Values: 1/0 (yes/no).')
 
     args = parser.parse_args()
 
-    print(f'Path to yaml file: {args.yaml_file}')
-    print(f'Path to sbml file: {args.sbml_file}')
+    print(f'Path to YAML file: {args.yaml_file}')
+    print(f'Path to SBML file: {args.sbml_file}')
 
     print('Converting...')
 
-    yaml2sbml(args.yaml_file, args.sbml_file)
+    yaml2sbml(args.yaml_file,
+              args.sbml_file,
+              args.observables_as_assignments)
 
 
 if __name__ == '__main__':
diff --git a/yaml2sbml/yaml_validation.py b/yaml2sbml/yaml_validation.py
index 38ea14a..31ecf24 100644
--- a/yaml2sbml/yaml_validation.py
+++ b/yaml2sbml/yaml_validation.py
@@ -1,46 +1,50 @@
+"""Validator of the input yaml."""
 import os
 import yaml
 import jsonschema
+from yaml.scanner import ScannerError
 import argparse
 
+
 SCHEMA = os.path.join(os.path.dirname(os.path.abspath(__file__)),
                       "yaml_schema.yaml")
 
 
-def validate_yaml(yaml_file: str):
+def validate_yaml(yaml_dir: str):
     """
-    Validates the syntax of the yaml file.
+    Validate the syntax of the YAML file.
 
     Arguments:
-        yaml_file: path to yaml file to be validated
+        yaml_dir: path to YAML file to be validated
 
     Returns:
         jsonschema.validate
+    """
+    try:
 
-    Raises:
+        with open(yaml_dir, 'r') as f_in:
+            yaml_contents = f_in.read()
+            yaml_dict = yaml.full_load(yaml_contents)
 
-    """
-    # read in yaml_file
-    with open(yaml_file, 'r') as f_in:
-        yaml_contents = f_in.read()
-        yaml_in = yaml.full_load(yaml_contents)
+    except ScannerError:
+        raise RuntimeError('YAML file can not be parsed due to a Scanner '
+                           'Error. This commonly happens if formulas begin '
+                           'with a minus. Please set them inside of brackets '
+                           '"(...)" or quotation marks.')
 
-    _validate_yaml_from_dict(yaml_in)
+    _validate_yaml_from_dict(yaml_dict)
     print('YAML file is valid ✅')
 
 
 def _validate_yaml_from_dict(yaml_dict: dict):
     """
-    Validates the syntax of the yaml file, using a dict as input.
+    Validate the syntax of the YAML file, using a dict as input.
 
     Arguments:
-        yaml_dict: yaml model as dict.
+        yaml_dict: YAML model as dict.
 
     Returns:
         jsonschema.validate
-
-    Raises:
-
     """
     # read in SCHEMA
     with open(SCHEMA, 'r') as f_in:
@@ -51,9 +55,7 @@ def _validate_yaml_from_dict(yaml_dict: dict):
 
 
 def main():
-    """
-    Function called by the CLI.
-    """
+    """Command-Line Interface."""
     parser = argparse.ArgumentParser(
         description='Validates a yaml model '
                     'so that it can be used by yaml2bsml.')


Test patch:
diff --git a/tests/test_CLI.py b/tests/test_CLI.py
new file mode 100644
index 0000000..bf0c8e1
--- /dev/null
+++ b/tests/test_CLI.py
@@ -0,0 +1,61 @@
+import os
+import shutil
+
+from yaml2sbml.yaml2sbml import _parse_yaml
+
+
+def test_yaml2sbml_cli(script_runner):
+    """Test the command line command `yaml2sbml`."""
+    path = os.path.dirname(os.path.abspath(__file__))
+
+    yaml_dir = os.path.join(path, 'test_yaml2sbml', 'ode_input1.yaml')
+    sbml_dir = os.path.join(path, 'test_sbml.xml')
+
+    # run CLI command
+    script_runner.run('yaml2sbml', yaml_dir, sbml_dir)
+
+    # test if SBML created by the python yaml2sbml command is the same.
+    sbml_from_python = _parse_yaml(yaml_dir, 'test_sbml')
+
+    with open(sbml_dir, 'r') as f_in:
+        sbml_from_cli = f_in.read()
+
+    for line_cli, line_python in zip(sbml_from_cli.split('\n'),
+                                     sbml_from_python.split('\n')):
+
+        # the line containing name & id will not match...
+        if not line_cli == line_python:
+            raise AssertionError('SBML from CLI and '
+                                 'Python interface differ.')
+
+    os.remove(sbml_dir)
+
+
+def test_yaml2petab_cli(script_runner):
+    """Test the command line command `yaml2petab."""
+    path = os.path.dirname(os.path.abspath(__file__))
+    yaml_dir = os.path.join(path, 'test_yaml2sbml', 'ode_input2.yaml')
+    output_dir = os.path.join(path, 'test_output')
+    model_name = 'petab_test_sbml.xml'
+
+    # run with no optional Arguments
+    script_runner.run('yaml2petab', yaml_dir, output_dir, model_name)
+
+    # run with optional Arguments
+    script_runner.run('yaml2petab',
+                      yaml_dir,
+                      output_dir,
+                      model_name,
+                      '-y test_yaml.yml')
+
+    # delete the generated files
+    shutil.rmtree(output_dir)
+
+
+def test_yaml2sbml_validate_cli(script_runner):
+    """Test the command line command ``yaml2sbml_validate`"""
+
+    path = os.path.dirname(os.path.abspath(__file__))
+    yaml_dir = os.path.join(path, 'test_yaml2sbml/ode_input1.yaml')
+
+    script_runner.run('yaml2sbml_validate', yaml_dir)
diff --git a/tests/test_YamlModel.py b/tests/test_YamlModel.py
index fb0f851..cfb1600 100644
--- a/tests/test_YamlModel.py
+++ b/tests/test_YamlModel.py
@@ -5,12 +5,11 @@
 import tempfile
 
 from yaml2sbml.YamlModel import YamlModel
+from yaml2sbml.yaml_validation import validate_yaml
 
 
 class TestYamlModel(unittest.TestCase):
-    """
-    TestCase class for testing YamlModel.
-    """
+    """TestCase class for testing YamlModel."""
 
     def setUp(self):
         # input directory
@@ -258,9 +257,7 @@ def test_condition(self):
         self.assertListEqual(model.get_condition_ids(), [])
 
     def test_valid_model(self):
-        """
-        Tests, whether the resulting models are valid.
-        """
+        """Test whether the resulting models are valid."""
         model = YamlModel()
 
         model.set_time('t')
@@ -272,6 +269,21 @@ def test_valid_model(self):
 
         model.validate_model()
 
+    def test_minus_in_formula(self):
+        """Test writing a model for a right hand side starting with a minus."""
+        model = YamlModel()
+
+        model.add_ode(state_id='x',
+                      right_hand_side='-x',
+                      initial_value=1)
+
+        model.validate_model()
+
+        yaml_dir = os.path.join(self.test_dir, 'test_minus_sign.yaml')
+        model.write_to_yaml(yaml_dir)
+
+        validate_yaml(yaml_dir)
+
 
 if __name__ == '__main__':
     suite = unittest.TestSuite()
diff --git a/tests/test_yaml2petab.py b/tests/test_yaml2petab.py
index 6084d6a..94f5103 100644
--- a/tests/test_yaml2petab.py
+++ b/tests/test_yaml2petab.py
@@ -1,4 +1,5 @@
 import os
+import shutil
 import unittest
 
 import yaml2sbml.yaml2PEtab as yaml2PEtab
@@ -12,26 +13,25 @@ class TestYaml2PEtab(unittest.TestCase):
 
     def setUp(self):
         this_dir, _ = os.path.split(__file__)
-        self.test_folder = os.path.join(this_dir, 'test_yaml2sbml')
+        self.input_folder = os.path.join(this_dir, 'test_yaml2sbml')
+        self.output_folder = os.path.join(this_dir, 'test_yaml2sbml_output')
+
+    def tearDown(self):
+        shutil.rmtree(self.output_folder)
 
     def test_petab_export(self):
         """
         Test PEtab export
         """
-        ode_file = os.path.join(self.test_folder, 'ode_input2.yaml')
+        input_yaml_dir = os.path.join(self.input_folder, 'ode_input2.yaml')
 
-        yaml2PEtab.yaml2petab(ode_file,
-                              self.test_folder,
+        yaml2PEtab.yaml2petab(input_yaml_dir,
+                              self.output_folder,
                               'sbml_test.xml')
 
         yaml2PEtab.validate_petab_tables(
-            os.path.join(self.test_folder, 'sbml_test.xml'), self.test_folder)
-
-        for f in ['observable_table.tsv',
-                  'parameter_table.tsv',
-                  'condition_table.tsv',
-                  'sbml_test.xml']:
-            os.remove(os.path.join(self.test_folder, f))
+            os.path.join(self.output_folder, 'sbml_test.xml'),
+            self.output_folder)
 
 
 if __name__ == '__main__':
diff --git a/tests/test_yaml2sbml.py b/tests/test_yaml2sbml.py
index c795895..6348a8c 100644
--- a/tests/test_yaml2sbml.py
+++ b/tests/test_yaml2sbml.py
@@ -1,7 +1,7 @@
 import os
 import unittest
 
-from yaml2sbml.yaml2sbml import _parse_yaml
+from yaml2sbml.yaml2sbml import yaml2sbml
 
 
 class TestYaml2SBML(unittest.TestCase):
@@ -18,46 +18,63 @@ def test_yaml_import(self):
         """
         Test yaml import/SBML generation...
         """
-        ode_file = os.path.join(self.test_folder, 'ode_input1.yaml')
+        yaml_dir = os.path.join(self.test_folder, 'ode_input1.yaml')
         expected_result_file = os.path.join(self.test_folder,
                                             'true_sbml_output.xml')
 
-        sbml_contents = _parse_yaml(ode_file)
+        sbml_test_dir = os.path.join(self.test_folder, 'sbml_test.xml')
+
+        yaml2sbml(yaml_dir, sbml_test_dir)
 
         with open(expected_result_file, 'r') as f_in:
-            expected_sbml_contents = f_in.read()
+            expected_sbml = f_in.read()
 
-        sbml_test_dir = os.path.join(self.test_folder, 'sbml_test.xml')
-        with open(sbml_test_dir, 'w') as f_out:
-            f_out.write(sbml_contents)
+        with open(sbml_test_dir, 'r') as f_out:
+            observed_sbml = f_out.read()
+
+        # check if lines coincide
+        for line_true, line_tested in zip(expected_sbml.split('\n'),
+                                          observed_sbml.split('\n')):
 
-        self.assertEqual(expected_sbml_contents, sbml_contents)
+            # the line containing name & id will not match...
+            if not line_true.startswith('  <model id='):
+                self.assertEqual(line_true, line_tested)
 
         os.remove(sbml_test_dir)
 
     def test_yaml_import_observables(self):
         """
-        Test yaml import/export for a model containing observables
-        (that are not translated).
+        Test yaml import/export for a model containing observables.
         """
-        ode_file = os.path.join(self.test_folder, 'ode_input2.yaml')
+        yaml_dir = os.path.join(self.test_folder, 'ode_input2.yaml')
 
         expected_result_file = \
             os.path.join(self.test_folder, 'true_sbml_output.xml')
 
-        test_sbml_dir = os.path.join(self.test_folder, 'sbml_test.xml')
+        sbml_test_dir = os.path.join(self.test_folder, 'sbml_test.xml')
+
+        # Call yaml2sbml with observables as assignments
+        yaml2sbml(yaml_dir,
+                  sbml_test_dir,
+                  observables_as_assignments=True)
 
-        sbml_contents = _parse_yaml(ode_file)
+        # Call yaml2sbml with observables not translated
+        yaml2sbml(yaml_dir, sbml_test_dir)
 
         with open(expected_result_file, 'r') as f_in:
-            expected_sbml_contents = f_in.read()
+            expected_sbml = f_in.read()
 
-        with open(test_sbml_dir, 'w') as f_out:
-            f_out.write(sbml_contents)
+        with open(sbml_test_dir, 'r') as f_out:
+            observed_sbml = f_out.read()
 
-        self.assertEqual(expected_sbml_contents, sbml_contents)
+        for line_true, line_tested in zip(expected_sbml.split('\n'),
+                                          observed_sbml.split('\n')):
 
-        os.remove(test_sbml_dir)
+            # the line containing name & id will not match...
+            if not line_true.startswith('  <model id='):
+                self.assertEqual(line_true, line_tested)
+
+        os.remove(sbml_test_dir)
 
 
 if __name__ == '__main__':
diff --git a/tests/test_yaml2sbml/true_sbml_output.xml b/tests/test_yaml2sbml/true_sbml_output.xml
index f0485c4..c5fed16 100644
--- a/tests/test_yaml2sbml/true_sbml_output.xml
+++ b/tests/test_yaml2sbml/true_sbml_output.xml
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <sbml xmlns="http://www.sbml.org/sbml/level3/version1/core" level="3" version="1">
-  <model>
+  <model id="true_sbml_output" name="true_sbml_output">
     <listOfFunctionDefinitions>
       <functionDefinition id="hill">
         <math xmlns="http://www.w3.org/1998/Math/MathML">