Repository: yaml2sbml-dev/yaml2sbml

Issue:
Example Notebook(s) for the model editor

Discussion:

Patch:
diff --git a/.gitignore b/.gitignore
index 2c3639d..6401eba 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,8 +26,15 @@ yaml2sbml/tests/test_yaml2sbml/sbml_test.xml
 ./doc/examples/Lotka_Volterra_CLI/Lotka_Volterra_PEtab/*.xml
 
 
+## Lotka Volterra Model Editor
+./doc/examples/Lotka_Volterra_Model_Editor/*.yaml
+./doc/examples/Lotka_Volterra_Model_Editor/*.xml
+./doc/examples/Lotka_Volterra_Model_Editor/Lotka_Volterra_PEtab
+
+
 ## Sorensen example
 ./doc/examples/Sorensen/*.xml
+./doc/examples/Sorensen/amici_models
 
 
 ## Format Features
diff --git a/doc/examples/Lotka_Volterra_Model_Editor/Lotka_Volterra_Model_Editor.ipynb b/doc/examples/Lotka_Volterra_Model_Editor/Lotka_Volterra_Model_Editor.ipynb
new file mode 100644
index 0000000..787a336
--- /dev/null
+++ b/doc/examples/Lotka_Volterra_Model_Editor/Lotka_Volterra_Model_Editor.ipynb
@@ -0,0 +1,205 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Model Editor\n",
+    "\n",
+    "This notebook demonstrates functionality of the `yaml2sbml` model editor using the Lotka Volterra equations as an example. The \"Lotka-Volterra\" equations are given by \n",
+    "\n",
+    "\\begin{align*}\n",
+    "\\frac{d}{dt} x_1 &= \\alpha x_1 - \\beta x_1x_2, \\\\\n",
+    "\\frac{d}{dt} x_2 &= \\delta x_1x_2 - \\gamma x_2.\n",
+    "\\end{align*}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## ODE model\n",
+    "\n",
+    "We first generate a basic model, that only contains all necessary information for generating an SBML file for model simulation."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "from yaml2sbml import YamlModel\n",
+    "\n",
+    "# generate model\n",
+    "model = YamlModel()\n",
+    "\n",
+    "# add ODEs\n",
+    "model.add_ode(state_id='x_1', \n",
+    "              right_hand_side='alpha*x_1 - beta*x_1*x_2', \n",
+    "              initial_value=2)\n",
+    "model.add_ode(state_id='x_2', \n",
+    "              right_hand_side='delta*x_1*x_2 - gamma*x_2', \n",
+    "              initial_value=2)\n",
+    "\n",
+    "# add parameters\n",
+    "model.add_parameter(parameter_id='alpha', nominal_value=2)\n",
+    "model.add_parameter(parameter_id='beta', nominal_value=4)\n",
+    "model.add_parameter(parameter_id='gamma', nominal_value=3)\n",
+    "model.add_parameter(parameter_id='delta', nominal_value=3)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "`yaml2sbml` can export the `model` object either to YAML or to SBML directly, via  "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# write to YAML\n",
+    "model.write_to_yaml('Lotka_Volterra_basic.yaml', overwrite=True)\n",
+    "\n",
+    "# write to SBML\n",
+    "model.write_to_sbml('Lotka_Volterra_basic.xml', overwrite=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "There are further functions to: \n",
+    "* get all `parameter_id`s via `model.get_parameter_ids()` \n",
+    "* get a parameter by its id (`model.get_parameter_by_id('alpha')`) \n",
+    "* delete a parameter by its id (`model.delete_parameter('alpha')`)\n",
+    "\n",
+    "Similar functions also exist for the other model components."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Parameter Estimation Problem\n",
+    "\n",
+    "Now we want to extend the current `model` to include all the necessary information for parameter estimation in PEtab. Therefore we load the model from the `.yaml` file and modify the parameters, such that it also contains all information that is going to be written into the PEtab parameter table."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "model = YamlModel.load_from_yaml('Lotka_Volterra_basic.yaml')\n",
+    "\n",
+    "# extend parameters \n",
+    "model.add_parameter(parameter_id='alpha',\n",
+    "                    nominal_value=2,\n",
+    "                    parameter_scale='log10',\n",
+    "                    lower_bound=0.1,\n",
+    "                    upper_bound=10,\n",
+    "                    estimate=1, \n",
+    "                    overwrite=True)\n",
+    "\n",
+    "model.add_parameter(parameter_id='beta',\n",
+    "                    nominal_value=4,\n",
+    "                    parameter_scale='log10',\n",
+    "                    lower_bound=0.1,\n",
+    "                    upper_bound=10,\n",
+    "                    estimate=1, \n",
+    "                    overwrite=True)\n",
+    "\n",
+    "model.add_parameter(parameter_id='gamma',\n",
+    "                    nominal_value=3,\n",
+    "                    parameter_scale='log10',\n",
+    "                    lower_bound=0.1,\n",
+    "                    upper_bound=10,\n",
+    "                    estimate=1,\n",
+    "                    overwrite=True)\n",
+    "\n",
+    "model.add_parameter(parameter_id='delta',\n",
+    "                    nominal_value=3,\n",
+    "                    parameter_scale='log10',\n",
+    "                    lower_bound=0.1,\n",
+    "                    upper_bound=10,\n",
+    "                    estimate=1,\n",
+    "                    overwrite=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "A parameter fitting problem in PEtab allows for the specification of observables and experimental conditions:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# specify an observable:\n",
+    "model.add_observable(observable_id='prey_measured', \n",
+    "                     observable_formula='log10(x_1)',\n",
+    "                     noise_formula='noiseParameter1_prey_measured', \n",
+    "                     noise_distribution='normal',\n",
+    "                     observable_transformation='lin')\n",
+    "\n",
+    "# specify trivial condition\n",
+    "model.add_condition(condition_id='condition1', \n",
+    "                    condition_dict={})"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The modified model can either be exported to YAML or PEtab via"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "# write to YAML\n",
+    "model.write_to_yaml('Lotka_Volterra_PEtab.yaml', overwrite=True)\n",
+    "\n",
+    "# write to PEtab\n",
+    "model.write_to_petab(output_dir='./Lotka_Volterra_PEtab',\n",
+    "                     model_name='Lotka_Volterra',\n",
+    "                     petab_yaml_name='Lotka_Volterra_problem', \n",
+    "                     measurement_table_name='measurement.tsv')"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.7.1"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}
diff --git a/doc/examples/README.md b/doc/examples/README.md
index 3a305df..fee3485 100644
--- a/doc/examples/README.md
+++ b/doc/examples/README.md
@@ -4,11 +4,13 @@ The [`yaml2sbml`](https://github.com/yaml2sbml-dev/yaml2sbml) package translates
 
 # Scope of the Notebooks
 
-* [Lotka Volterra](./Lotka_Volterra_python/Lotka_Volterra.ipynb) (Python)
+* [Lotka Volterra (Python) ](./Lotka_Volterra_python/Lotka_Volterra.ipynb)
     * Introduces the input format, syntax & capabilities of `yaml2sbml`
     * Showcases ODE simulation & parameter fitting using [AMICI](https://github.com/AMICI-dev/AMICI) & [pyPESTO](https://github.com/ICB-DCM/pyPESTO)
 * [Lotka Volterra CLI](./Lotka_Volterra_CLI/Lotka_Volterra_CLI.ipynb)
     * Shows the command line interface of `yaml2sbml`
+* [Lotka Volterra Model Editor](.Lotka_Volterra_Model_Editor/Lotka_Volterra_Model_Editor.ipynb)
+    * Shows model construction using the model editor of `yaml2sbml`
 * [Sorensen](./Sorensen.yaml2sbml_Sorensen.ipynb)
     * Application example, model of glucose and insulin metabolism
     * Shows the extension of an existing YAML model by the model editor
diff --git a/doc/examples/Sorensen/yaml2sbml_Sorensen.ipynb b/doc/examples/Sorensen/yaml2sbml_Sorensen.ipynb
index b19530c..a23849c 100644
--- a/doc/examples/Sorensen/yaml2sbml_Sorensen.ipynb
+++ b/doc/examples/Sorensen/yaml2sbml_Sorensen.ipynb
@@ -88,7 +88,7 @@
     "yaml_model.add_ode(state_id='GlucH', \n",
     "                   right_hand_side=gluch_rhs_new, \n",
     "                   initial_value=gluch_ode_old['initialValue'], \n",
-    "                   over_write=True)"
+    "                   overwrite=True)"
    ]
   },
   {
@@ -105,7 +105,7 @@
    "outputs": [],
    "source": [
     "# Write to SBML\n",
-    "yaml_model.write_to_sbml('Fig71_Sorensen1985.xml', over_write=True)"
+    "yaml_model.write_to_sbml('Fig71_Sorensen1985.xml', overwrite=True)"
    ]
   },
   {
diff --git a/yaml2sbml/YamlModel.py b/yaml2sbml/YamlModel.py
index eb1f718..f38b935 100644
--- a/yaml2sbml/YamlModel.py
+++ b/yaml2sbml/YamlModel.py
@@ -44,10 +44,7 @@ class YamlModel:
         # read in yaml_file
         with open(yaml_file, 'r') as f_in:
             yaml_contents = f_in.read()
-            yaml_dict_from_file = yaml.full_load(yaml_contents)
-
-        for key in yaml_dict_from_file.keys():
-            new_model._yaml_model[key] = yaml_dict_from_file[key]
+            new_model._yaml_model.update(yaml.full_load(yaml_contents))
 
         # check, if the model is valid
         new_model.validate_model()
@@ -56,14 +53,14 @@ class YamlModel:
 
     def write_to_yaml(self,
                       yaml_dir: str,
-                      over_write: bool = False):
+                      overwrite: bool = False):
         """
         Write model to yaml file given as directory yaml_dir.
 
         Arguments:
         yaml_dir:
             path/file, where the yaml should be written
-        over_write:
+        overwrite:
             Indicates, whether an existing yaml should be overwritten
 
         Returns:
@@ -76,10 +73,10 @@ class YamlModel:
             raise ValueError('yaml_dir should contain path to the yaml '
                              'and hence end with .yaml or .yml')
 
-        if (not over_write) and os.path.exists(yaml_dir):
+        if (not overwrite) and os.path.exists(yaml_dir):
             raise FileExistsError(f'Can not write yaml model. File {yaml_dir}'
                                   f' already exists. Consider to set '
-                                  f'over_write=True.')
+                                  f'overwrite=True.')
 
         reduced_model_dict = self._get_reduced_model_dict()
 
@@ -99,14 +96,14 @@ class YamlModel:
 
     def write_to_sbml(self,
                       sbml_dir: str,
-                      over_write: bool = False):
+                      overwrite: bool = False):
         """
         Writes the model as an SBML file to the directory given in sbml_dir.
 
         Arguments:
         sbml_dir:
             path/file, where the sbml should be written
-        over_write:
+        overwrite:
             Indicates, whether an existing yaml should be overwritten
 
         Returns:
@@ -120,10 +117,10 @@ class YamlModel:
             raise ValueError('sbml_dir should contain path to the sbml '
                              'and hence end with .xml or .sbml')
 
-        if (not over_write) and os.path.exists(sbml_dir):
+        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 '
-                                  f'over_write=True.')
+                                  f'overwrite=True.')
 
         # generate SBML as string
         reduced_model_dict = self._get_reduced_model_dict()
@@ -212,7 +209,7 @@ class YamlModel:
     # functions adding a value
     def add_parameter(self,
                       parameter_id: str,
-                      over_write: bool = False,
+                      overwrite: bool = False,
                       nominal_value: float = None,
                       parameter_name: str = None,
                       parameter_scale: str = None,
@@ -221,11 +218,11 @@ class YamlModel:
                       estimate: float = None):
         """
         Adds a parameter. Overwrites an existing parameter with the same id,
-        if over_write=True.
+        if overwrite=True.
         """
         # if parameter exists: delete if overwrite
         if parameter_id in self.get_parameter_ids():
-            if over_write:
+            if overwrite:
                 self.delete_parameter(parameter_id)
             else:
                 raise ValueError('Could not add parameter with id'
@@ -246,14 +243,14 @@ class YamlModel:
                 state_id: str,
                 right_hand_side: Union[float, str],
                 initial_value: Union[float, str],
-                over_write: bool = False):
+                overwrite: bool = False):
         """
         Adds a state/ODE. Overwrites an existing state/ODE with the same id,
-        if over_write=True.
+        if overwrite=True.
         """
-        # if state exists: delete if over_write
+        # if state exists: delete if overwrite
         if state_id in self.get_ode_ids():
-            if over_write:
+            if overwrite:
                 self.delete_ode(state_id)
             else:
                 raise ValueError(f'Could not add state/ODE with id {state_id}:'
@@ -268,14 +265,14 @@ class YamlModel:
     def add_assignment(self,
                        assignment_id: str,
                        formula: str,
-                       over_write: bool = False):
+                       overwrite: bool = False):
         """
         Adds an assignment. Overwrites an existing assignment with the same id,
-        if over_write=True.
+        if overwrite=True.
         """
-        # if assignment exists: delete if over_write
+        # if assignment exists: delete if overwrite
         if assignment_id in self.get_assignment_ids():
-            if over_write:
+            if overwrite:
                 self.delete_assignment(assignment_id)
             else:
                 raise ValueError('Could not add assignment with id '
@@ -291,14 +288,14 @@ class YamlModel:
                      function_id: str,
                      arguments: str,
                      formula: str,
-                     over_write: bool = False):
+                     overwrite: bool = False):
         """
         Adds a function. Overwrites an existing function with the same id,
-        if over_write=True.
+        if overwrite=True.
         """
-        # if function exists: delete if over_write
+        # if function exists: delete if overwrite
         if function_id in self.get_function_ids():
-            if over_write:
+            if overwrite:
                 self.delete_function(function_id)
             else:
                 raise ValueError('Could not add function with id '
@@ -315,17 +312,17 @@ class YamlModel:
                        observable_id: str,
                        observable_formula: str,
                        noise_formula: str,
-                       over_write: bool = False,
+                       overwrite: bool = False,
                        observable_name: str = None,
                        observable_transformation: str = None,
                        noise_distribution: str = None):
         """
         Adds an observable. Overwrites an existing observable with the same id,
-        if over_write=True.
+        if overwrite=True.
         """
-        # if observable exists: delete if over_write
+        # if observable exists: delete if overwrite
         if observable_id in self.get_observable_ids():
-            if over_write:
+            if overwrite:
                 self.delete_observable(observable_id)
             else:
                 raise ValueError('Could not add observable with id '
@@ -344,12 +341,12 @@ class YamlModel:
     def add_condition(self,
                       condition_id: str,
                       condition_dict: dict,
-                      over_write: bool = False,
+                      overwrite: bool = False,
                       condition_name: str = None):
 
         """
         Adds a condition. Overwrites an existing condition with the same id,
-        if over_write=True.
+        if overwrite=True.
 
         Arguments:
         condition_id:
@@ -358,16 +355,16 @@ class YamlModel:
             dict, of the form {<parameter or state id>: <value>}.
             Corresponds to entries in the PEtab condition table.
             See details there.
-        over_write:
+        overwrite:
             bool, indicates if an existing condition should be overwritten
         condition_name:
             Condition name. Optional.
 
         Returns:
         """
-        # if condition exists: delete if over_write
+        # if condition exists: delete if overwrite
         if condition_id in self.get_condition_ids():
-            if over_write:
+            if overwrite:
                 self.delete_condition(condition_id)
             else:
                 raise ValueError('Could not add condition with id '
@@ -385,7 +382,7 @@ class YamlModel:
                    block_key: str):
         """
         Adds the entry in 'entry_dict' to the block indexed by 'block_key'.
-        If 'over_write=True', an existing value for that key is overwritten.
+        If 'overwrite=True', an existing value for that key is overwritten.
 
         Arguments:
         entry_dict:


Test patch:
diff --git a/tests/test_YamlModel.py b/tests/test_YamlModel.py
index 5d484f5..fb0f851 100644
--- a/tests/test_YamlModel.py
+++ b/tests/test_YamlModel.py
@@ -71,13 +71,13 @@ class TestYamlModel(unittest.TestCase):
         # test get_parameter_by_id
         self.assertIsInstance(model.get_parameter_by_id(parameter_id), dict)
 
-        # test over_write
+        # test overwrite
         with self.assertRaises(ValueError):
             model.add_parameter(parameter_id=parameter_id)
 
         # now over write parameter
         model.add_parameter(parameter_id=parameter_id,
-                            over_write=True)
+                            overwrite=True)
 
         # test get_parameter_ids
         self.assertListEqual(model.get_parameter_ids(),
@@ -102,7 +102,7 @@ class TestYamlModel(unittest.TestCase):
         # test get_ode_by_id
         self.assertIsInstance(model.get_ode_by_id(state_id), dict)
 
-        # test over_write
+        # test overwrite
         with self.assertRaises(ValueError):
             model.add_ode(state_id, right_hand_side, initial_value)
 
@@ -110,7 +110,7 @@ class TestYamlModel(unittest.TestCase):
         model.add_ode(state_id,
                       right_hand_side,
                       initial_value,
-                      over_write=True)
+                      overwrite=True)
 
         # test get_ode_ids
         self.assertListEqual(model.get_ode_ids(),
@@ -134,12 +134,12 @@ class TestYamlModel(unittest.TestCase):
         # test get_assignment_by_id
         self.assertIsInstance(model.get_assignment_by_id(assignment_id), dict)
 
-        # test over_write
+        # test overwrite
         with self.assertRaises(ValueError):
             model.add_assignment(assignment_id, formula)
 
         # now over write assignment
-        model.add_assignment(assignment_id, formula, over_write=True)
+        model.add_assignment(assignment_id, formula, overwrite=True)
 
         # test get_assignment_ids
         self.assertListEqual(model.get_assignment_ids(),
@@ -164,12 +164,12 @@ class TestYamlModel(unittest.TestCase):
         # test get_function_by_id
         self.assertIsInstance(model.get_function_by_id(function_id), dict)
 
-        # test over_write
+        # test overwrite
         with self.assertRaises(ValueError):
             model.add_function(function_id, arguments, formula)
 
         # now over write function
-        model.add_function(function_id, arguments, formula, over_write=True)
+        model.add_function(function_id, arguments, formula, overwrite=True)
 
         # test get_function_ids
         self.assertListEqual(model.get_function_ids(),
@@ -198,7 +198,7 @@ class TestYamlModel(unittest.TestCase):
         # test get_observable_by_id
         self.assertIsInstance(model.get_observable_by_id(observable_id), dict)
 
-        # test over_write
+        # test overwrite
         with self.assertRaises(ValueError):
             model.add_observable(observable_id,
                                  observable_formula,
@@ -209,7 +209,7 @@ class TestYamlModel(unittest.TestCase):
         model.add_observable(observable_id,
                              observable_formula,
                              noise_formula,
-                             over_write=True,
+                             overwrite=True,
                              observable_name=observable_name)
 
         # test get_observable_ids
@@ -237,7 +237,7 @@ class TestYamlModel(unittest.TestCase):
         # test get_condition_by_id
         self.assertIsInstance(model.get_condition_by_id(condition_id), dict)
 
-        # test over_write
+        # test overwrite
         with self.assertRaises(ValueError):
             model.add_condition(condition_id,
                                 condition_dict,
@@ -247,7 +247,7 @@ class TestYamlModel(unittest.TestCase):
         model.add_condition(condition_id,
                             condition_dict,
                             condition_name=condition_name,
-                            over_write=True)
+                            overwrite=True)
 
         # test get_condition_ids
         self.assertListEqual(model.get_condition_ids(),