Repository: flechtenberg/pulpo (language: python)

Issue:
Implement a first set of test functions
Created a set if test functions for the PULPO package. For that purpose, a sample database with 5 processes, 4 environmental flows and 3 lcia methods has been introduced. 


Discussion / hints:

Fix patch:
diff --git a/LICENSE b/LICENSE
index 052190e..f685224 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,21 +1,29 @@
-MIT License
+BSD 3-Clause License
 
-Copyright (c) 2023 Fabian Lechtenberg
+Copyright (c) 2023, Fabian Lechtenberg
+All rights reserved.
 
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
 
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
+1. Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
 
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
\ No newline at end of file
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\ No newline at end of file
diff --git a/README.md b/README.md
index b78663e..4c350a0 100644
--- a/README.md
+++ b/README.md
@@ -1,38 +1,179 @@
-# Pulpo
+<div align="center">
+<h1 align="center">
+<img src="https://raw.githubusercontent.com/PKief/vscode-material-icon-theme/ec559a9f6bfd399b82bb44393651661b08aaf7ba/icons/folder-markdown-open.svg" width="100" />
+<br>pulpo</h1>
+<h3>◦ PULPO: Python-based User-defined Lifecycle Production Optimization!</h3>
+<h3>◦ Developed with the software and tools below.</h3>
 
-Pulpo is a Python package for python-based user-defined lifecycle optimization.
+<p align="center">
+<img src="https://img.shields.io/badge/Jupyter-F37626.svg?style&logo=Jupyter&logoColor=white" alt="Jupyter" />
+<img src="https://img.shields.io/badge/Python-3776AB.svg?style&logo=Python&logoColor=white" alt="Python" />
+<img src="https://img.shields.io/badge/Markdown-000000.svg?style&logo=Markdown&logoColor=white" alt="Markdown" />
+</p>
+<img src="https://img.shields.io/github/license/flechtenberg/pulpo?style&color=5D6D7E" alt="GitHub license" />
+<img src="https://img.shields.io/github/last-commit/flechtenberg/pulpo?style&color=5D6D7E" alt="git-last-commit" />
+<img src="https://img.shields.io/github/commit-activity/m/flechtenberg/pulpo?style&color=5D6D7E" alt="GitHub commit activity" />
+<img src="https://img.shields.io/github/languages/top/flechtenberg/pulpo?style&color=5D6D7E" alt="GitHub top language" />
+</div>
 
-## Installation
+---
 
-Currently, pulpo is still under development and not yet uploaded to pypi index. Once a stable version is released, it will be installable via:
+## 📖 Table of Contents
+- [📍 Overview](#-overview)
+- [📂 Repository Structure](#-repository-structure)
+- [⚙️ Modules](#modules)
+- [🚀 Getting Started](#-getting-started)
+    - [🔧 Installation](#-installation)
+    - [🤖 Running pulpo](#-running-pulpo)
+    - [🧪 Tests](#-tests)
+- [🛣 Roadmap](#-roadmap)
+- [🤝 Contributing](#-contributing)
+- [📄 License](#-license)
+- [👏 Acknowledgments](#-acknowledgments)
 
-```bash
+---
+
+
+## 📍 Overview
+
+The project, "pulpo," is a life cycle assessment (LCA) optimization tool that aims to import, optimize, solve, retrieve, save, and summarize LCA results. It provides functionalities for data import from the Ecoinvent database, conversion of inputs into a dictionary for the optimization model, definition and solving of the optimization model using the pyomo package, and saving and summarizing of the model results. The tool's value lies in streamlining LCA processes, enabling optimization and analysis of environmental impacts, and facilitating the decision-making process for sustainable solutions.
+
+
+---
+
+
+## 📂 Repository Structure
+
+```sh
+└── pulpo/
+    ├── .gitattributes
+    ├── .gitconfig
+    ├── .gitignore
+    ├── desktop.ini
+    ├── LICENSE
+    ├── notebooks/
+    │   └── hydrogen_showcase.ipynb
+    ├── pulpo/
+    │   ├── __init__.py
+    │   ├── pulpo.py
+    │   └── utils/
+    ├── README.md
+    ├── setup.cfg
+    └── tests/
+        ├── __init__.py
+        └── test_functions.py
+```
+
+
+---
+
+## ⚙️ Modules
+
+<details closed><summary>Root</summary>
+
+| File                                                                                                         | Summary                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
+| ---                                                                                                          | ---                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
+| [.gitconfig](https://github.com/flechtenberg/pulpo/blob/main/.gitconfig)                                     | This code fragment configures a git filter to clean Jupyter Notebook files in the.gitconfig file. It uses the Jupyter nbconvert command to remove the output cells and smudge to display the file's contents.                                                                                                                                                                                                                                                                                                                                              |                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
+| [pulpo.py](https://github.com/flechtenberg/pulpo/blob/main/pulpo\pulpo.py)                                   | The code implements a PulpoOptimizer class that provides functionalities for data import, optimization, solving, retrieval, saving and summarizing results related to life cycle assessments. It uses modules like optimizer, bw_parser, converter, and saver for different operations.                                                                                                                                                                                                                                                                    |
+| [bw_parser.py](https://github.com/flechtenberg/pulpo/blob/main/pulpo\utils\bw_parser.py)                     | The code in `bw_parser.py` provides functions for importing, saving, and retrieving life cycle inventory (LCI) data from the Ecoinvent database using the Brightway2 library. It includes functions for importing data, checking if data needs to be reloaded, performing LCA calculations, saving LCI data to files, and reading LCI data from files. Additionally, it provides functions for retrieving activities and environmental flows from the database based on specified criteria. The `main` function demonstrates the usage of these functions. |
+| [converter.py](https://github.com/flechtenberg/pulpo/blob/main/pulpo\utils\converter.py)                     | The code in pulpo\utils\converter.py combines various inputs into a dictionary for an optimization model. It converts sparse matrices to dictionaries, modifies the technosphere matrix, creates sets, specifies demand, limits, and supply, assigns weights, and assembles the final data dictionary for the model. This function serves as a crucial step in preparing the inputs for the optimization process.                                                                                                                                          |
+| [optimizer.py](https://github.com/flechtenberg/pulpo/blob/main/pulpo\utils\optimizer.py)                     | The code defines an optimization model using the pyomo package. It includes sets, parameters, variables, constraints, and an objective function. The model is created and solved using different solvers.                                                                                                                                                                                                                                                                                                                                                  |
+| [saver.py](https://github.com/flechtenberg/pulpo/blob/main/pulpo\utils\saver.py)                             | The code provides two main functionalities:1. save_results: Saves the results of a Pyomo model to an Excel file, including raw results, metadata, and constraints.2. summarize_results: Prints a summary of the model results, including demand, impacts, choices, and constraints.                                                                                                                                                                                                                                                                        |
+
+</details>
+
+---
+
+## 🚀 Getting Started
+
+### 🔧 Installation
+Soon PULPO will be installable via
+```sh
 pip install pulpo
 ```
+But for the time being, please get the repo by cloning. It is highly recommended to create a new and separate conda 
+environment for PULPO. The requirements.txt comes from the current stable release and will be updated with each release. 
+A long-term task is to reduce the requirements.txt to a minimum, stating only the important packages.
+
+1. Clone the pulpo repository:
+```sh
+git clone https://github.com/flechtenberg/pulpo
+```
+
+2. Change to the project directory:
+```sh
+cd pulpo
+```
+
+3. Install the dependencies:
+```sh
+pip install -r requirements.txt
+```
 
-## Usage
+### 🤖 Running pulpo
 
-## Features
+Find example notebooks for a [hydrogen case](https://github.com/flechtenberg/pulpo/blob/master/notebooks/hydrogen_showcase.ipynb) and an [electricity case](https://github.com/flechtenberg/pulpo/blob/master/notebooks/electricity_showcase.ipynb) here.
 
-## Documentation
+### 🧪 Tests
+
+Calling from the package folder: 
+
+```sh
+python -m unittest discover -s tests
+```
 
-## Contributing
+---
 
-## Issues
 
-## Usage / Examples
+## 🛣 Roadmap
 
-You can find example notebooks [here](https://github.com/flechtenberg/pulpo/tree/master/notebooks/)
+> - [ ] `ℹ️  Task 1: Implement integer cuts to allow a fast calculation of a ranked list of best options`
+> - [ ] `ℹ️  Task 2: Implement functionality to treat uncertainty in the optimization problem (robust)`
+> - [ ] `ℹ️ ... Requests are welcome.`
+
+
+---
+
+## 🤝 Contributing
+
+Contributions are always welcome! Please follow these steps:
+1. Fork the project repository. This creates a copy of the project on your account that you can modify without affecting the original project.
+2. Clone the forked repository to your local machine using a Git client like Git or GitHub Desktop.
+3. Create a new branch with a descriptive name (e.g., `new-feature-branch` or `bugfix-issue-123`).
+```sh
+git checkout -b new-feature-branch
+```
+4. Make changes to the project's codebase.
+5. Commit your changes to your local branch with a clear commit message that explains the changes you've made.
+```sh
+git commit -m 'Implemented new feature.'
+```
+6. Push your changes to your forked repository on GitHub using the following command
+```sh
+git push origin new-feature-branch
+```
+7. Create a new pull request to the original project repository. In the pull request, describe the changes you've made and why they're necessary.
+The project maintainers will review your changes and provide feedback or merge them into the main branch.
+
+---
+
+## 📄 License
+
+This project is licensed under the `ℹ️  BSD 3-Clause` License. See the [LICENSE](LICENSE) file for additional info.
+Copyright (c) 2023, Fabian Lechtenberg. All rights reserved.
+
+---
+
+## 👏 Acknowledgments
 
-## Acknowledgements
 We would like to acknowledge the authors and contributors of these main packages that pulpo is based on:
  - [pyomo](https://github.com/Pyomo/pyomo)
  - [premise](https://github.com/polca/premise)
  - [brightway2](https://github.com/brightway-lca/brightway2)
-
+---
 ## Authors
-
 - [@flechtenberg](https://www.github.com/flechtenberg)
 - [@robyistrate](https://www.github.com/robyistrate)
 - [@vtulus](https://www.github.com/vtulus)
-
+---
+[↑ Return](#Top)
\ No newline at end of file
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 0000000..d4bb2cb
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# 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/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..4fc6033
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,33 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# For the full list of built-in configuration values, see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Project information -----------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
+
+import os
+import sys
+
+sys.path.insert(0, os.path.abspath(".."))
+
+project = 'pulpo documentation'
+copyright = '2023, Fabian Lechtenberg'
+author = 'Fabian Lechtenberg'
+release = '0.0.1'
+
+# -- General configuration ---------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
+
+extensions = ["sphinx.ext.todo", "sphinx.ext.viewcode", "sphinx.ext.autodoc"]
+
+templates_path = ['_templates']
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+
+
+# -- Options for HTML output -------------------------------------------------
+# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
+
+html_theme = 'sphinx_rtd_theme'
+html_static_path = ['_static']
diff --git a/docs/index.rst b/docs/index.rst
new file mode 100644
index 0000000..4179d49
--- /dev/null
+++ b/docs/index.rst
@@ -0,0 +1,20 @@
+.. pulpo documentation documentation master file, created by
+   sphinx-quickstart on Wed Jul  5 13:38:50 2023.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Welcome to pulpo documentation's documentation!
+===============================================
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents:
+
+   pulpo
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 0000000..954237b
--- /dev/null
+++ b/docs/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
+
+%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.https://www.sphinx-doc.org/
+	exit /b 1
+)
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/docs/modules.rst b/docs/modules.rst
new file mode 100644
index 0000000..1791754
--- /dev/null
+++ b/docs/modules.rst
@@ -0,0 +1,7 @@
+pulpo
+=====
+
+.. toctree::
+   :maxdepth: 4
+
+   pulpo
diff --git a/docs/pulpo.rst b/docs/pulpo.rst
new file mode 100644
index 0000000..fe49214
--- /dev/null
+++ b/docs/pulpo.rst
@@ -0,0 +1,29 @@
+pulpo package
+=============
+
+Subpackages
+-----------
+
+.. toctree::
+   :maxdepth: 4
+
+   pulpo.utils
+
+Submodules
+----------
+
+pulpo.pulpo module
+------------------
+
+.. automodule:: pulpo.pulpo
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Module contents
+---------------
+
+.. automodule:: pulpo
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/docs/pulpo.utils.rst b/docs/pulpo.utils.rst
new file mode 100644
index 0000000..d827738
--- /dev/null
+++ b/docs/pulpo.utils.rst
@@ -0,0 +1,37 @@
+pulpo.utils package
+===================
+
+Submodules
+----------
+
+pulpo.utils.bw\_parser module
+-----------------------------
+
+.. automodule:: pulpo.utils.bw_parser
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+pulpo.utils.converter module
+----------------------------
+
+.. automodule:: pulpo.utils.converter
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+pulpo.utils.optimizer module
+----------------------------
+
+.. automodule:: pulpo.utils.optimizer
+   :members:
+   :undoc-members:
+   :show-inheritance:
+
+Module contents
+---------------
+
+.. automodule:: pulpo.utils
+   :members:
+   :undoc-members:
+   :show-inheritance:
diff --git a/notebooks/electricity_showcase.ipynb b/notebooks/electricity_showcase.ipynb
new file mode 100644
index 0000000..ccced40
--- /dev/null
+++ b/notebooks/electricity_showcase.ipynb
@@ -0,0 +1,1570 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "5dcdd302",
+   "metadata": {},
+   "source": [
+    "Electricity showcase for basic PULPO\n",
+    "\n",
+    "Written by Fabian Lechtenberg, 18.09.2023\n",
+    "\n",
+    "Last Update: 23.09.2023"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "1ed6b95f",
+   "metadata": {},
+   "source": [
+    "<div style=\"text-align: center; background-color: #f0f0f0; padding: 10px;\">\n",
+    "    <h2 style=\"font-family: 'Arial', sans-serif; font-weight: bold; color: #555;\">(1) Selection of LCI Data</h2>\n",
+    "</div>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4f8eff9d",
+   "metadata": {},
+   "source": [
+    "### Import section"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "448abc38",
+   "metadata": {},
+   "source": [
+    "In this working version of the pulpo repository, pulpo musst be imported from the folder above, which can be done by appending \"..\" to the system path. Later, only the import after installation via pip will be necessary."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "606b78ed",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:41.190768Z",
+     "iopub.status.busy": "2023-09-27T11:36:41.190768Z",
+     "iopub.status.idle": "2023-09-27T11:36:42.652423Z",
+     "shell.execute_reply": "2023-09-27T11:36:42.652423Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:41.190768Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "import os\n",
+    "import sys\n",
+    "sys.path.append('../')\n",
+    "from pulpo import pulpo\n",
+    "\n",
+    "import pandas as pd\n",
+    "pd.set_option('display.max_colwidth', None)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "0da2fe63",
+   "metadata": {},
+   "source": [
+    "### Setup"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "84d12867",
+   "metadata": {},
+   "source": [
+    "Specify the project, database and method to be used. Also indicate the folder where the working data should be stored. For this example to work, you need a project \"**pulpo**\" with a database \"**cutoff38**\", which is the ecoinvent 3.8 cutoff system model."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "ead64289",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:42.653424Z",
+     "iopub.status.busy": "2023-09-27T11:36:42.653424Z",
+     "iopub.status.idle": "2023-09-27T11:36:42.668372Z",
+     "shell.execute_reply": "2023-09-27T11:36:42.668372Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:42.653424Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "project = \"pulpo\"\n",
+    "database = \"cutoff38\"\n",
+    "methods = \"('IPCC 2013', 'climate change', 'GWP 100a')\"\n",
+    "\n",
+    "# Substitute with your working directory of choice\n",
+    "notebook_dir = os.path.dirname(os.getcwd())\n",
+    "directory = os.path.join(notebook_dir, 'data')\n",
+    "\n",
+    "# Substitute with your GAMS path\n",
+    "GAMS_PATH = \"C:/GAMS/37/gams.exe\""
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "866393e7",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "directory"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "0e5e5da2",
+   "metadata": {},
+   "source": [
+    "Create a pulpo object called \"pulpo_worker\". This object is an element of the class \"PulpoOptimizer\", a class that links the different utilitiy modules containing code for retrieving, preparing and adjusting the data, preparing and running the optimization problem, as well as saving the results."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "ae4b5787",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:42.670371Z",
+     "iopub.status.busy": "2023-09-27T11:36:42.669374Z",
+     "iopub.status.idle": "2023-09-27T11:36:42.684324Z",
+     "shell.execute_reply": "2023-09-27T11:36:42.684324Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:42.670371Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker = pulpo.PulpoOptimizer(project, database, methods, directory)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "2a9fcd18",
+   "metadata": {},
+   "source": [
+    "Retrieve the data. If data is already loaded, this step is automatically skipped. "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "80697078",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:42.685321Z",
+     "iopub.status.busy": "2023-09-27T11:36:42.685321Z",
+     "iopub.status.idle": "2023-09-27T11:36:43.682063Z",
+     "shell.execute_reply": "2023-09-27T11:36:43.682063Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:42.685321Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.get_lci_data()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e16f8296",
+   "metadata": {},
+   "source": [
+    "<div style=\"text-align: center; background-color: #f0f0f0; padding: 10px;\">\n",
+    "    <h2 style=\"font-family: 'Arial', sans-serif; font-weight: bold; color: #555;\">(2) User Specifications</h2>\n",
+    "</div>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "57a9941a-b0d3-42c4-8bc7-aaa2a3d6d824",
+   "metadata": {},
+   "source": [
+    "<center><img src=\"images/electricity_showcase_1.png\" alt=\"Alternative text\" width=\"400\"/>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a8492b7e",
+   "metadata": {},
+   "source": [
+    "### Specify the **functional unit**"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "de935003",
+   "metadata": {},
+   "source": [
+    "Retrieve the market activity for liquid hydrogen in Europe (RER). Use the function \"**<span style=\"color: red;\">retrieve_activities</span>**\" for this purpose. The function takes 4 optional arguments: \"keys\" (🔑) --> \"activities\" (⚙️) --> \"reference_products\" (📦) --> \"locations\" (🗺️). The activities are retrieved by this order. \n",
+    "\n",
+    "Since the key is unique, a single activity for each passed key will be returned. Activity names, reference_prduct and locations are not unique, so the best match for the passed data will be returned. "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "2fc40bd9",
+   "metadata": {},
+   "source": [
+    "#### Passing keys  🔑"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "80fce33f",
+   "metadata": {},
+   "source": [
+    "Keys can be obtained e.g. directly from **activity browser** and several keys can be passed at the same time."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "167a2bfb",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:43.684057Z",
+     "iopub.status.busy": "2023-09-27T11:36:43.683060Z",
+     "iopub.status.idle": "2023-09-27T11:36:43.698049Z",
+     "shell.execute_reply": "2023-09-27T11:36:43.698049Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:43.684057Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "keys = [\"('cutoff38', '962727b9a36bcaa186f222b29b57f6a3')\", \"('cutoff38', '473d4bb488e8f903b58203f3e5161636')\"]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b46fa327",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:43.700042Z",
+     "iopub.status.busy": "2023-09-27T11:36:43.699045Z",
+     "iopub.status.idle": "2023-09-27T11:36:44.551213Z",
+     "shell.execute_reply": "2023-09-27T11:36:44.551213Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:43.700042Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.retrieve_activities(keys=keys)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "daf88526",
+   "metadata": {},
+   "source": [
+    "#### Passing activity  name (⚙️), reference_product (📦) and/or location (🗺️)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4074fec3",
+   "metadata": {},
+   "source": [
+    "Instead of passing the keys, a combination of activities, reference_products and locations can be passed. A best match (all existing combinations) will be returned. "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "4d445dfc",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:44.552181Z",
+     "iopub.status.busy": "2023-09-27T11:36:44.552181Z",
+     "iopub.status.idle": "2023-09-27T11:36:44.566575Z",
+     "shell.execute_reply": "2023-09-27T11:36:44.566575Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:44.552181Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "activities = [\"market for electricity, high voltage\"]\n",
+    "reference_products = [\"electricity, high voltage\"]\n",
+    "locations = [\"DE\"]"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5d0976d1",
+   "metadata": {},
+   "source": [
+    "It is also possible to pass only partial information such as only reference product or only activity name:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "0cb7d84f",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:44.567576Z",
+     "iopub.status.busy": "2023-09-27T11:36:44.567576Z",
+     "iopub.status.idle": "2023-09-27T11:36:45.452617Z",
+     "shell.execute_reply": "2023-09-27T11:36:45.452617Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:44.567576Z"
+    },
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.retrieve_activities(activities=activities)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "b211d17a",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:45.453612Z",
+     "iopub.status.busy": "2023-09-27T11:36:45.453612Z",
+     "iopub.status.idle": "2023-09-27T11:36:47.098154Z",
+     "shell.execute_reply": "2023-09-27T11:36:47.098154Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:45.453612Z"
+    },
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.retrieve_activities(reference_products=reference_products)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "03e8a151",
+   "metadata": {},
+   "source": [
+    "Let's retrieve the activity of our functional unit and specify the demand as a dictionary:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "332b2f95",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:47.099151Z",
+     "iopub.status.busy": "2023-09-27T11:36:47.099151Z",
+     "iopub.status.idle": "2023-09-27T11:36:48.000703Z",
+     "shell.execute_reply": "2023-09-27T11:36:48.000703Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:47.099151Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "electricity_market = pulpo_worker.retrieve_activities(activities=activities, reference_products=reference_products, locations=locations)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "1959b64f",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:48.002701Z",
+     "iopub.status.busy": "2023-09-27T11:36:48.001726Z",
+     "iopub.status.idle": "2023-09-27T11:36:48.017483Z",
+     "shell.execute_reply": "2023-09-27T11:36:48.016660Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:48.002701Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "electricity_market"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "7cf0adef",
+   "metadata": {},
+   "source": [
+    "Setting a demand of 128,819.0 GWh of electricity according to [Germany electricity demand 2018](https://www.destatis.de/EN/Themes/Society-Environment/Environment/Material-Energy-Flows/Tables/electricity-consumption-households.html)\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "223e1cf1",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:48.018483Z",
+     "iopub.status.busy": "2023-09-27T11:36:48.018483Z",
+     "iopub.status.idle": "2023-09-27T11:36:48.032299Z",
+     "shell.execute_reply": "2023-09-27T11:36:48.032299Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:48.018483Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "demand = {electricity_market[0]: 1.28819e+11}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "992d6029",
+   "metadata": {},
+   "source": [
+    "### Specify the **choices**"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4b6d561a",
+   "metadata": {},
+   "source": [
+    "The choices are specified similar to the demand / functional unit. First, search for the processes with equivalent products:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "d704a9a5",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:48.034297Z",
+     "iopub.status.busy": "2023-09-27T11:36:48.034297Z",
+     "iopub.status.idle": "2023-09-27T11:36:48.851726Z",
+     "shell.execute_reply": "2023-09-27T11:36:48.851726Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:48.034297Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "activities = [\"electricity production, lignite\", \n",
+    "             \"electricity production, hard coal\",\n",
+    "             \"electricity production, nuclear, pressure water reactor\",\n",
+    "             \"electricity production, wind, 1-3MW turbine, onshore\"]\n",
+    "reference_products = [\"electricity, high voltage\"]\n",
+    "locations = [\"DE\"]\n",
+    "\n",
+    "electricity_activities = pulpo_worker.retrieve_activities(activities=activities, reference_products=reference_products, locations=locations)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9d8fac69",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:48.852724Z",
+     "iopub.status.busy": "2023-09-27T11:36:48.852724Z",
+     "iopub.status.idle": "2023-09-27T11:36:48.867677Z",
+     "shell.execute_reply": "2023-09-27T11:36:48.867677Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:48.852724Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "electricity_activities"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "12c4d7a7-826d-4168-a9e5-902cf21537d1",
+   "metadata": {},
+   "source": [
+    "These are the currently four most employed technologies for electricity production in Germany (lignite: 24.2%, wind: 15.4%, coal: 11.9%, nuclear: 10.6%) according to the \"_market for electricity, high voltage (DE)_\""
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d9a512a5",
+   "metadata": {},
+   "source": [
+    "Specify also the choices as a dictionary. Be aware, that this time we are dealing with a dictionary of dictionaries. Each inner dictionary corresponds to one type of choice in the background! Here, we only consider choices between electricity production activities, so we assign the key \"electricity\" to the equivalent product they produce. \n",
+    "\n",
+    "The assigned value in the inner dictionary is the capacity limit of this activity, which for now is set to a very high value, to consider an unconstrained situation. "
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "bfae6af4",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:48.868671Z",
+     "iopub.status.busy": "2023-09-27T11:36:48.868671Z",
+     "iopub.status.idle": "2023-09-27T11:36:48.883662Z",
+     "shell.execute_reply": "2023-09-27T11:36:48.883662Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:48.868671Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "choices  = {'electricity': {electricity_activities[0]: 1e16,\n",
+    "                         electricity_activities[1]: 1e16,\n",
+    "                         electricity_activities[2]: 1e16,\n",
+    "                         electricity_activities[3]: 1e16}}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "25f8cf77",
+   "metadata": {},
+   "source": [
+    "<div style=\"text-align: center; background-color: #f0f0f0; padding: 10px;\">\n",
+    "    <h2 style=\"font-family: 'Arial', sans-serif; font-weight: bold; color: #555;\">(3) Solution</h2>\n",
+    "</div>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3a90008f",
+   "metadata": {},
+   "source": [
+    "### Instantiate the worker"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "be46ea2d",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:48.884662Z",
+     "iopub.status.busy": "2023-09-27T11:36:48.884662Z",
+     "iopub.status.idle": "2023-09-27T11:36:52.480948Z",
+     "shell.execute_reply": "2023-09-27T11:36:52.480948Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:48.884662Z"
+    },
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "instance = pulpo_worker.instantiate(choices=choices, demand=demand)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "20353834",
+   "metadata": {},
+   "source": [
+    "### Solve the instance"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "43da9882",
+   "metadata": {},
+   "source": [
+    "When specifying a valid GAMS_PATH with a licence for CPLEX, as shown below, CPLEX with fine-tuned parameters is automatically selected to solve the Linear Problem (**LP**).\n",
+    "\n",
+    "If no GAMS_PATH is specified, the \"[HiGHS](https://highs.dev/)\" solver is automatically used. It has almost double the run time of \"CPLEX\"."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "eb13ed22",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:52.482941Z",
+     "iopub.status.busy": "2023-09-27T11:36:52.482941Z",
+     "iopub.status.idle": "2023-09-27T11:36:58.142738Z",
+     "shell.execute_reply": "2023-09-27T11:36:58.142738Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:52.482941Z"
+    },
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "results = pulpo_worker.solve()\n",
+    "# Alternatively using GAMS (cplex) solvers:\n",
+    "# results = pulpo_worker.solve(GAMS_PATH=GAMS_PATH)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "916c7e48",
+   "metadata": {},
+   "source": [
+    "### Save and summarize the results 💾📈"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "cba63697",
+   "metadata": {},
+   "source": [
+    "The \"**save_results()**\" function will save the results in an processed format to an excel file in the data folder that has been specified at the beginning."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "cf427cea",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:58.143747Z",
+     "iopub.status.busy": "2023-09-27T11:36:58.143747Z",
+     "iopub.status.idle": "2023-09-27T11:36:58.860906Z",
+     "shell.execute_reply": "2023-09-27T11:36:58.860906Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:58.143747Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.save_results(choices=choices, demand=demand, name='electricity_showcase_results.xlsx')"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "20b00f17",
+   "metadata": {},
+   "source": [
+    "You can inspect the generated excel file."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4c4a1df1",
+   "metadata": {},
+   "source": [
+    "There is another function to summarize the results in dataframe form within jupyter notbeooks called \"summarize_results\". This function has similar inputs to the \"save_results\" function, but does not require the specification of a filename. Additionally, by specifying the \"*zeroes*\" parameter to \"*True*\" all the not-selected choices are omitted in the summary."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "f6141497",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:58.861906Z",
+     "iopub.status.busy": "2023-09-27T11:36:58.861906Z",
+     "iopub.status.idle": "2023-09-27T11:36:58.909747Z",
+     "shell.execute_reply": "2023-09-27T11:36:58.908750Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:58.861906Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.summarize_results(choices=choices, demand=demand, zeroes=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "f5893613",
+   "metadata": {},
+   "source": [
+    "# Closing Remarks"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c1b2f442",
+   "metadata": {},
+   "source": [
+    "This is the end of the very basic PULPO functionalities using the electricity case study. "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b5298095",
+   "metadata": {},
+   "source": [
+    "The following sections will dive deeper into additional functionalities."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "602c9d5e",
+   "metadata": {},
+   "source": [
+    "<div style=\"text-align: center; background-color: #f0f0f0; padding: 10px;\">\n",
+    "    <h2 style=\"font-family: 'Arial', sans-serif; font-weight: bold; color: #555;\">Additional Constraints</h2>\n",
+    "</div>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "07bffb38-471b-4f7c-b48d-9954e0fe4e55",
+   "metadata": {},
+   "source": [
+    "<center><img src=\"images/electricity_showcase_2.png\" alt=\"Alternative text\" width=\"400\"/>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e277bb96",
+   "metadata": {},
+   "source": [
+    "Let's assess what happens if the \"_electricity production, nuclear, pressure water reactor | electricity, high voltage | DE_\" activity is indirectly constrained trough a restriction on \"_nuclear fuel element, for pressure water reactor, UO2 4.0% & MOX_\""
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "04b68998",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:58.910743Z",
+     "iopub.status.busy": "2023-09-27T11:36:58.909747Z",
+     "iopub.status.idle": "2023-09-27T11:36:59.553592Z",
+     "shell.execute_reply": "2023-09-27T11:36:59.553592Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:58.910743Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "activities = [\"market for nuclear fuel element, for pressure water reactor, UO2 4.0% & MOX\"]\n",
+    "reference_products = [\"nuclear fuel element, for pressure water reactor, UO2 4.0% & MOX\"]\n",
+    "locations = [\"GLO\"]\n",
+    "\n",
+    "nuclear_fuel = pulpo_worker.retrieve_activities(activities=activities, reference_products=reference_products, locations=locations)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "aebdc35c",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:59.554589Z",
+     "iopub.status.busy": "2023-09-27T11:36:59.554589Z",
+     "iopub.status.idle": "2023-09-27T11:36:59.569539Z",
+     "shell.execute_reply": "2023-09-27T11:36:59.569539Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:59.554589Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "nuclear_fuel"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "a89c6ee5",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:59.570536Z",
+     "iopub.status.busy": "2023-09-27T11:36:59.570536Z",
+     "iopub.status.idle": "2023-09-27T11:36:59.585486Z",
+     "shell.execute_reply": "2023-09-27T11:36:59.585486Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:59.570536Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "upper_limit = {nuclear_fuel[0]: 100000}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "da61b77d",
+   "metadata": {},
+   "source": [
+    "The rationale behind choosing this activity and this limit is based on inspection of the scaling vector of the previous results. This activity is limiting for the nuclear electricity activity but not for the others, so, to enforce a different result than before, this activity is constrained."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "87267aa9",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:36:59.586482Z",
+     "iopub.status.busy": "2023-09-27T11:36:59.586482Z",
+     "iopub.status.idle": "2023-09-27T11:37:09.201396Z",
+     "shell.execute_reply": "2023-09-27T11:37:09.201027Z",
+     "shell.execute_reply.started": "2023-09-27T11:36:59.586482Z"
+    },
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.instantiate(choices=choices, demand=demand, upper_limit=upper_limit)\n",
+    "results = pulpo_worker.solve()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "a03f1671",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:09.202395Z",
+     "iopub.status.busy": "2023-09-27T11:37:09.201396Z",
+     "iopub.status.idle": "2023-09-27T11:37:09.247752Z",
+     "shell.execute_reply": "2023-09-27T11:37:09.247752Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:09.202395Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.summarize_results(choices=choices, demand=demand, constraints=upper_limit)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5f409b26",
+   "metadata": {},
+   "source": [
+    "As can be seen from the summary above, part of the final electricity demand is supplied by the wind turbine processes, because the nuclear process is constrained by the nuclear fuel process. It is also evident that the impact is higher than the previous one, as the GWP minimizing process (nuclear) can no longer supply the full demand."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "48249a97",
+   "metadata": {},
+   "source": [
+    "<div style=\"text-align: center; background-color: #f0f0f0; padding: 10px;\">\n",
+    "    <h2 style=\"font-family: 'Arial', sans-serif; font-weight: bold; color: #555;\">Additional Methods</h2>\n",
+    "</div>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "3a455b7b",
+   "metadata": {},
+   "source": [
+    "Let's see how to evaluate different methods and set them as objectives, in this case evaluating the ReCiPe endpoints, and setting the human health one as objective:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "6ccd79d5",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:09.249742Z",
+     "iopub.status.busy": "2023-09-27T11:37:09.248745Z",
+     "iopub.status.idle": "2023-09-27T11:37:09.263716Z",
+     "shell.execute_reply": "2023-09-27T11:37:09.263716Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:09.249742Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "methods = {\"('IPCC 2013', 'climate change', 'GWP 100a')\": 0,\n",
+    "           \"('ReCiPe Endpoint (E,A)', 'resources', 'total')\": 0,\n",
+    "           \"('ReCiPe Endpoint (E,A)', 'human health', 'total')\": 1,\n",
+    "           \"('ReCiPe Endpoint (E,A)', 'ecosystem quality', 'total')\": 0}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "97f4df9e-1e6b-44d2-9f63-2551fd8f99d6",
+   "metadata": {},
+   "source": [
+    "With this, a new Pulpo worker must be created:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "8c71c0b9",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:09.264716Z",
+     "iopub.status.busy": "2023-09-27T11:37:09.264716Z",
+     "iopub.status.idle": "2023-09-27T11:37:09.279043Z",
+     "shell.execute_reply": "2023-09-27T11:37:09.279043Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:09.264716Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker = pulpo.PulpoOptimizer(project, database, methods, directory)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "60d4cad9",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:09.280015Z",
+     "iopub.status.busy": "2023-09-27T11:37:09.280015Z",
+     "iopub.status.idle": "2023-09-27T11:37:10.516342Z",
+     "shell.execute_reply": "2023-09-27T11:37:10.516342Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:09.280015Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.get_lci_data()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "cb241deb",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:10.517443Z",
+     "iopub.status.busy": "2023-09-27T11:37:10.517443Z",
+     "iopub.status.idle": "2023-09-27T11:37:24.972769Z",
+     "shell.execute_reply": "2023-09-27T11:37:24.971967Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:10.517443Z"
+    },
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.instantiate(choices=choices, demand=demand)\n",
+    "results = pulpo_worker.solve()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e81c9f8c",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:24.973769Z",
+     "iopub.status.busy": "2023-09-27T11:37:24.973769Z",
+     "iopub.status.idle": "2023-09-27T11:37:25.019696Z",
+     "shell.execute_reply": "2023-09-27T11:37:25.019696Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:24.973769Z"
+    },
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.summarize_results(choices=choices, demand=demand, zeroes=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "bfe4c83f-1585-405c-938d-c3c6e3fc34f7",
+   "metadata": {},
+   "source": [
+    "From the summary above, it can be seen that for the \"_human health_\" category, the nuclear process is not the most suitable anymore. With this objective, the wind turbine process is selected. \n",
+    "\n",
+    "As another category is minimized, the GWP has changed as well: previously, with nuclear electricity the total GWP was 1.599836e+10 while with wind electricity it is 1.739234e+10."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "7fb09ed7-e772-4248-8bc5-8409fb5ebfe0",
+   "metadata": {},
+   "source": [
+    "<div style=\"text-align: center; background-color: #f0f0f0; padding: 10px;\">\n",
+    "    <h2 style=\"font-family: 'Arial', sans-serif; font-weight: bold; color: #555;\">Lower Level Decisions</h2>\n",
+    "</div>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "26fec4c4",
+   "metadata": {},
+   "source": [
+    "<center><img src=\"images/electricity_showcase_3.png\" alt=\"Alternative text\" width=\"400\"/>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ee72103c",
+   "metadata": {},
+   "source": [
+    "In this case study, we would like to keep the current share of the electricity supplied by fossil sources the same. The choices that we consider on the electricity production level are between the coal and lignite activities:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "1e28df91-e6fc-4123-b3e6-3067ee6deca3",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:25.020697Z",
+     "iopub.status.busy": "2023-09-27T11:37:25.020697Z",
+     "iopub.status.idle": "2023-09-27T11:37:25.035670Z",
+     "shell.execute_reply": "2023-09-27T11:37:25.035670Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:25.020697Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "methods = {\"('IPCC 2013', 'climate change', 'GWP 100a')\": 1,\n",
+    "           \"('ReCiPe Endpoint (E,A)', 'resources', 'total')\": 0,\n",
+    "           \"('ReCiPe Endpoint (E,A)', 'human health', 'total')\": 0,\n",
+    "           \"('ReCiPe Endpoint (E,A)', 'ecosystem quality', 'total')\": 0}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "2b881759-2c15-4528-9780-39293f3747be",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:25.037666Z",
+     "iopub.status.busy": "2023-09-27T11:37:25.036669Z",
+     "iopub.status.idle": "2023-09-27T11:37:26.300813Z",
+     "shell.execute_reply": "2023-09-27T11:37:26.300813Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:25.037666Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker = pulpo.PulpoOptimizer(project, database, methods, directory)\n",
+    "pulpo_worker.get_lci_data()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e7cff49c-5163-4451-aa97-a1ef41bce88b",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:26.301812Z",
+     "iopub.status.busy": "2023-09-27T11:37:26.301812Z",
+     "iopub.status.idle": "2023-09-27T11:37:26.972087Z",
+     "shell.execute_reply": "2023-09-27T11:37:26.971391Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:26.301812Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "activities = [\"electricity production, lignite\", \n",
+    "             \"electricity production, hard coal\"]\n",
+    "reference_products = [\"electricity, high voltage\"]\n",
+    "locations = [\"DE\"]\n",
+    "\n",
+    "electricity_activities = pulpo_worker.retrieve_activities(activities=activities, reference_products=reference_products, locations=locations)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "a8f82b5b",
+   "metadata": {},
+   "source": [
+    "Instead of assessing only the **technology** choices, we are invetigating the best **regional** choice for the source of coal and lignite:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e68e44ef",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:26.972087Z",
+     "iopub.status.busy": "2023-09-27T11:37:26.972087Z",
+     "iopub.status.idle": "2023-09-27T11:37:29.110422Z",
+     "shell.execute_reply": "2023-09-27T11:37:29.110422Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:26.972087Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "coal_activities = [\"market for hard coal\"]\n",
+    "lignite_activities = [\"market for lignite\"]\n",
+    "\n",
+    "coal_activities = pulpo_worker.retrieve_activities(activities=coal_activities)\n",
+    "lignite_activities = pulpo_worker.retrieve_activities(activities=lignite_activities)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "ae575912",
+   "metadata": {},
+   "source": [
+    "The updated choice dictionary looks like this:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "bc665036",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:29.111473Z",
+     "iopub.status.busy": "2023-09-27T11:37:29.111473Z",
+     "iopub.status.idle": "2023-09-27T11:37:29.126440Z",
+     "shell.execute_reply": "2023-09-27T11:37:29.126440Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:29.111473Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "choices  = {'electricity': {elec: 1e16 for elec in electricity_activities},\n",
+    "            'coal': {coal: 1e16 for coal in coal_activities},\n",
+    "            'lignite': {lignite: 1e16 for lignite in lignite_activities}}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6db1e764",
+   "metadata": {},
+   "source": [
+    "Instantiating and solving the adapted problem:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "1143473f",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:29.128431Z",
+     "iopub.status.busy": "2023-09-27T11:37:29.127435Z",
+     "iopub.status.idle": "2023-09-27T11:37:38.220676Z",
+     "shell.execute_reply": "2023-09-27T11:37:38.219686Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:29.128431Z"
+    },
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.instantiate(choices=choices, demand=demand)\n",
+    "results = pulpo_worker.solve()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "73eadc47",
+   "metadata": {},
+   "source": [
+    "Visualizing the results"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "d9c7c140",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:38.221673Z",
+     "iopub.status.busy": "2023-09-27T11:37:38.220676Z",
+     "iopub.status.idle": "2023-09-27T11:37:38.281473Z",
+     "shell.execute_reply": "2023-09-27T11:37:38.280477Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:38.221673Z"
+    },
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.summarize_results(choices=choices, demand=demand, zeroes=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "18a60969",
+   "metadata": {},
+   "source": [
+    "I can be seen that out of the fossil alternatives, the electricity from coal minimizes GWP when the coal comes from RLA [Latin America and the Caribbean] (omitting transport emissions). Moreover, it can be seen that the market for lignite is somewhere used upstream of the coal production activity. In the place where it is used, the RER market for lignite is the preferred one. The lignite consumption is one order of magnitude lower than the coal consumption."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d71d8e1c",
+   "metadata": {},
+   "source": [
+    "<div style=\"text-align: center; background-color: #f0f0f0; padding: 10px;\">\n",
+    "    <h2 style=\"font-family: 'Arial', sans-serif; font-weight: bold; color: #555;\">Supply vs. Demand Problem</h2>\n",
+    "</div>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "66cb3f62",
+   "metadata": {},
+   "source": [
+    "Finally, let's test and assess the functionality of PULPO to specify supply values rather than demand values. This can be done by setting the lower_limit and the upper_limit of activities to the same value. This will enforce the corresponding scaling vector entry of that activity to the specified value, and activates the slack variable to relax the demand value. "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "4a3f2eac",
+   "metadata": {},
+   "source": [
+    "This can simply be done by specifying the upper and lower limits rather than the demand (note, we continue with the choices from the previous section):"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "89ecef38",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:38.282469Z",
+     "iopub.status.busy": "2023-09-27T11:37:38.282469Z",
+     "iopub.status.idle": "2023-09-27T11:37:38.295459Z",
+     "shell.execute_reply": "2023-09-27T11:37:38.295459Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:38.282469Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "upper_limit = {electricity_market[0]: 1.28819e+11}\n",
+    "lower_limit = {electricity_market[0]: 1.28819e+11}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "6f72394d",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:38.296459Z",
+     "iopub.status.busy": "2023-09-27T11:37:38.296459Z",
+     "iopub.status.idle": "2023-09-27T11:37:47.604058Z",
+     "shell.execute_reply": "2023-09-27T11:37:47.604058Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:38.296459Z"
+    },
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.instantiate(choices=choices, upper_limit=upper_limit, lower_limit=lower_limit)\n",
+    "results = pulpo_worker.solve()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "006b3b87",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:47.605055Z",
+     "iopub.status.busy": "2023-09-27T11:37:47.605055Z",
+     "iopub.status.idle": "2023-09-27T11:37:47.667844Z",
+     "shell.execute_reply": "2023-09-27T11:37:47.667844Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:47.605055Z"
+    },
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.summarize_results(choices=choices, demand=demand, constraints=upper_limit, zeroes=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6ecc80bf",
+   "metadata": {},
+   "source": [
+    "From the results it can be observed that the resulting GWP is **considerably** lower (6.191744e+10 vs. 5.920967e+10: ~4.4%) than in the previous section. Now, the production value (supply) of electricity is specified, so that electricity consumed in the background is accounted for in the specifications.\n",
+    "\n",
+    "Overall, when specifying supply values instead of demand values, the corresponding scaling vector entries are always smaller."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "976fd3a8-e3eb-4f8c-a875-856eb741aa3a",
+   "metadata": {},
+   "source": [
+    "<div style=\"text-align: center; background-color: #f0f0f0; padding: 10px;\">\n",
+    "    <h2 style=\"font-family: 'Arial', sans-serif; font-weight: bold; color: #555;\">Foreground vs. Background Modelling</h2>\n",
+    "</div>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d0b2e993-009a-4b7b-a7ee-ece90fe1eea0",
+   "metadata": {},
+   "source": [
+    "<center><img src=\"images/electricity_showcase_4.png\" alt=\"Alternative text\" width=\"700\"/>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "7610381f-c826-417d-8885-5a4e4b938199",
+   "metadata": {},
+   "source": [
+    "In this final part of the electricity showcase, the difference between foreground and background modelling and optimization is demonstrated. For that purpose a foreground system must be created ... this can be done either by hand or I can upload a folded foreground system for this case ... in essence, a new database must be created (e.g. \"_cutoff38_foreground_\" where copies from the market and electricity production activites are duplicated to from the \"_cutoff38_\". What this does is disconnecting this activities from their downstream.\n",
+    "\n",
+    "We are then replacing the original inputs in the electricity market with the duplicated processes from the foreground system, effectively disconnecting the choices made in the electricity market from the upstream of the electricity production technologies:"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "be25cefe-6ca4-42d2-bb8a-3096c93b8663",
+   "metadata": {},
+   "source": [
+    "With this in mind, we can set up the optimization as usual, importing the \"_cutoff38_foreground_\" database instead of the \"_cutoff38_\" database:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "83385f75-df84-4bb9-9248-aef2e78e3e3f",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:47.670835Z",
+     "iopub.status.busy": "2023-09-27T11:37:47.670835Z",
+     "iopub.status.idle": "2023-09-27T11:37:47.682571Z",
+     "shell.execute_reply": "2023-09-27T11:37:47.682571Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:47.670835Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "project = \"pulpo\"\n",
+    "database = \"cutoff38_foreground\""
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "651554db-128f-4b74-ab2d-0ab98ae5012b",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:47.683572Z",
+     "iopub.status.busy": "2023-09-27T11:37:47.683572Z",
+     "iopub.status.idle": "2023-09-27T11:37:47.697904Z",
+     "shell.execute_reply": "2023-09-27T11:37:47.697904Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:47.683572Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker = pulpo.PulpoOptimizer(project, database, methods, directory)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e9b26ce5-8055-4afe-b81d-e06f057d34f1",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:47.698901Z",
+     "iopub.status.busy": "2023-09-27T11:37:47.698901Z",
+     "iopub.status.idle": "2023-09-27T11:37:48.298695Z",
+     "shell.execute_reply": "2023-09-27T11:37:48.298695Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:47.698901Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.get_lci_data()"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "1840257e-f372-4a83-a519-cce3097bc68e",
+   "metadata": {},
+   "source": [
+    "Now, choose the foreground market for electricity:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "ff250df5-3646-4d2d-8d7f-2b7750c06bdb",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:48.299701Z",
+     "iopub.status.busy": "2023-09-27T11:37:48.299701Z",
+     "iopub.status.idle": "2023-09-27T11:37:48.314540Z",
+     "shell.execute_reply": "2023-09-27T11:37:48.314540Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:48.299701Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "activities = [\"market for electricity, high voltage, foreground\"]\n",
+    "reference_products = [\"electricity, high voltage\"]\n",
+    "locations = [\"DE\"]"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "63766777-83a7-4a3b-b49a-36601fe72638",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:48.315540Z",
+     "iopub.status.busy": "2023-09-27T11:37:48.315540Z",
+     "iopub.status.idle": "2023-09-27T11:37:48.344142Z",
+     "shell.execute_reply": "2023-09-27T11:37:48.344142Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:48.315540Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "foreground_market = pulpo_worker.retrieve_activities(activities=activities, reference_products=reference_products, locations=locations)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "37c0635f-cb0b-495d-bfd4-d72a2aabe414",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:48.346132Z",
+     "iopub.status.busy": "2023-09-27T11:37:48.345137Z",
+     "iopub.status.idle": "2023-09-27T11:37:48.359100Z",
+     "shell.execute_reply": "2023-09-27T11:37:48.359100Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:48.346132Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "demand = {foreground_market[0]: 1.28819e+11}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "7e358b4f-c25c-42d4-af5b-8fc3c2de4edb",
+   "metadata": {},
+   "source": [
+    "And the foreground electricity production technologies:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "32cbd648-bc8f-4ec0-af63-b7147803e805",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:48.360100Z",
+     "iopub.status.busy": "2023-09-27T11:37:48.360100Z",
+     "iopub.status.idle": "2023-09-27T11:37:48.390715Z",
+     "shell.execute_reply": "2023-09-27T11:37:48.390715Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:48.360100Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "activities = [\"electricity production, lignite, foreground\", \n",
+    "             \"electricity production, hard coal, foreground\",\n",
+    "             \"electricity production, nuclear, pressure water reactor, foreground\",\n",
+    "             \"electricity production, wind, 1-3MW turbine, onshore, foreground\"]\n",
+    "reference_products = [\"electricity, high voltage\"]\n",
+    "locations = [\"DE\"]\n",
+    "\n",
+    "electricity_activities = pulpo_worker.retrieve_activities(activities=activities, reference_products=reference_products, locations=locations)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "9af3f0cc-ca10-46b6-b6a6-818c7fba3579",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:48.391746Z",
+     "iopub.status.busy": "2023-09-27T11:37:48.391746Z",
+     "iopub.status.idle": "2023-09-27T11:37:48.407101Z",
+     "shell.execute_reply": "2023-09-27T11:37:48.406105Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:48.391746Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "choices  = {'electricity': {electricity_activities[0]: 1e16,\n",
+    "                         electricity_activities[1]: 1e16,\n",
+    "                         electricity_activities[2]: 1e16,\n",
+    "                         electricity_activities[3]: 1e16}}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "da8c0aec-7047-4e6c-8a2f-55777c160a78",
+   "metadata": {},
+   "source": [
+    "Create the instance and solve the problem:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "e2cd342f-cb54-49fb-8d11-078b16845fca",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:48.408097Z",
+     "iopub.status.busy": "2023-09-27T11:37:48.408097Z",
+     "iopub.status.idle": "2023-09-27T11:37:52.044276Z",
+     "shell.execute_reply": "2023-09-27T11:37:52.044276Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:48.408097Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "instance = pulpo_worker.instantiate(choices=choices, demand=demand)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "823f2a7d-053d-4ad3-8452-32d05d8aec9f",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:52.046268Z",
+     "iopub.status.busy": "2023-09-27T11:37:52.045272Z",
+     "iopub.status.idle": "2023-09-27T11:37:57.934691Z",
+     "shell.execute_reply": "2023-09-27T11:37:57.934691Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:52.046268Z"
+    },
+    "scrolled": true
+   },
+   "outputs": [],
+   "source": [
+    "results = pulpo_worker.solve()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "2e40a4aa-3c1c-4e45-b679-c6461e41fa1f",
+   "metadata": {
+    "execution": {
+     "iopub.execute_input": "2023-09-27T11:37:57.936688Z",
+     "iopub.status.busy": "2023-09-27T11:37:57.936688Z",
+     "iopub.status.idle": "2023-09-27T11:37:57.981332Z",
+     "shell.execute_reply": "2023-09-27T11:37:57.981332Z",
+     "shell.execute_reply.started": "2023-09-27T11:37:57.936688Z"
+    }
+   },
+   "outputs": [],
+   "source": [
+    "pulpo_worker.summarize_results(choices=choices, demand=demand, zeroes=True)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "5e8924a0-6f11-4891-9f12-c06eb2c699b2",
+   "metadata": {},
+   "source": [
+    "The result of this optimization is a minimum GWP of 1.764977e+10 kg CO2eq instead of 1.599836e+10 kg CO2eq from the initial calculations uising the full LCI. This is a difference of **8%**!"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "63afe2ba-d322-4a1e-a75b-fc36e0e354b6",
+   "metadata": {},
+   "source": [
+    "<div style=\"text-align: center; background-color: #f0f0f0; padding: 10px;\">\n",
+    "    <h2 style=\"font-family: 'Arial', sans-serif; font-weight: bold; color: #555;\">Integer Cuts</h2>\n",
+    "</div>"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "6485f0f7-0889-4a15-9abd-5b214bc7617a",
+   "metadata": {},
+   "source": [
+    "This functionality will be available in future versions of PULPO"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3 (ipykernel)",
+   "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.9.16"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/notebooks/hydrogen_showcase.ipynb b/notebooks/hydrogen_showcase.ipynb
index 6508cb4..e88c4f6 100644
--- a/notebooks/hydrogen_showcase.ipynb
+++ b/notebooks/hydrogen_showcase.ipynb
@@ -9,7 +9,7 @@
     "\n",
     "Written by Fabian Lechtenberg, 07.07.2023\n",
     "\n",
-    "Last Update: 29.08.2023"
+    "Last Update: 24.09.2023"
    ]
   },
   {
@@ -412,7 +412,9 @@
    },
    "outputs": [],
    "source": [
-    "results = pulpo_worker.solve(GAMS_PATH=GAMS_PATH)"
+    "results = pulpo_worker.solve()\n",
+    "# Alternatively using GAMS (cplex) solvers:\n",
+    "# results = pulpo_worker.solve(GAMS_PATH=GAMS_PATH)"
    ]
   },
   {
@@ -438,15 +440,7 @@
    "metadata": {},
    "outputs": [],
    "source": [
-    "pulpo_worker.save_results(choices=choices, demand=demand, name='showcase1_results.xlsx')"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "id": "20b00f17",
-   "metadata": {},
-   "source": [
-    "You can inspect the generated excel file to find that the technology with the lowest impact is \"**hydrogen cracking, APME**\" and the production of 100 kg of liquid hydrogen has an impact of 179.4 kg CO2 eq."
+    "pulpo_worker.save_results(choices=choices, demand=demand, name='hydrogen_showcase_results.xlsx')"
    ]
   },
   {
@@ -563,7 +557,7 @@
    "outputs": [],
    "source": [
     "pulpo_worker.instantiate(choices=choices, demand=demand, upper_limit=upper_limit)\n",
-    "results = pulpo_worker.solve(GAMS_PATH=GAMS_PATH)"
+    "results = pulpo_worker.solve()"
    ]
   },
   {
@@ -653,8 +647,8 @@
    },
    "outputs": [],
    "source": [
-    "pulpo_worker.instantiate(choices=choices, demand=demand, methods=methods)\n",
-    "results = pulpo_worker.solve(GAMS_PATH=GAMS_PATH)"
+    "pulpo_worker.instantiate(choices=choices, demand=demand)\n",
+    "results = pulpo_worker.solve()"
    ]
   },
   {
@@ -841,7 +835,7 @@
    "outputs": [],
    "source": [
     "pulpo_worker.instantiate(choices=choices, upper_limit=upper_limit, lower_limit=lower_limit)\n",
-    "results = pulpo_worker.solve(GAMS_PATH=GAMS_PATH)"
+    "results = pulpo_worker.solve()"
    ]
   },
   {
diff --git a/pulpo/pulpo.py b/pulpo/pulpo.py
index 1c5bda0..cb11370 100644
--- a/pulpo/pulpo.py
+++ b/pulpo/pulpo.py
@@ -2,23 +2,29 @@
 from typing import List, Union
 
 class PulpoOptimizer:
-    def __init__(self, project: str, database: str, method: Union[str, List[str]], directory: str):
+    def __init__(self, project: str, database: str, method: Union[str, List[str], dict], directory: str):
         self.project = project
         self.database = database
-        self.method = method
+        self.method = converter.convert_to_dict(method)
         self.directory = directory
         self.lci_data = None
         self.instance = None
 
     def get_lci_data(self):
-        self.lci_data = bw_parser.import_data(self.project, self.database, self.method, self.directory)
+        self.lci_data = bw_parser.import_data(self.project, self.database, self.method)
 
-    def instantiate(self, choices={}, demand={}, upper_limit={},lower_limit={}, methods={}):
+    def instantiate(self, choices={}, demand={}, upper_limit={},lower_limit={}):
+        # Instantiate only for those methods that are part of the objecitve
+        methods = {h: self.method[h] for h in self.method if self.method[h] !=0}
         data = converter.combine_inputs(self.lci_data, demand, choices, upper_limit, lower_limit, methods)
         self.instance = optimizer.instantiate(data)
 
     def solve(self, GAMS_PATH=False):
         results, self.instance = optimizer.solve_model(self.instance, GAMS_PATH)
+        # Post calculate additional methods, in case several methods have been specified and one of them is 0
+        if not isinstance(self.method, str):
+            if len(self.method) > 1 and 0 in [self.method[x] for x in self.method]:
+                self.instance = optimizer.calculate_methods(self.instance, self.lci_data, self.method)
         return results
 
     def retrieve_activities(self, keys=None, activities=None, reference_products=None, locations=None):
diff --git a/pulpo/utils/bw_parser.py b/pulpo/utils/bw_parser.py
index f5ee8b6..1c73e2b 100644
--- a/pulpo/utils/bw_parser.py
+++ b/pulpo/utils/bw_parser.py
@@ -4,7 +4,7 @@
 import brightway2 as bw
 from scipy import sparse
 
-def import_data(project: str, database: str, method: Union[str, List[str]], directory: str, force=False) -> Dict[str, Union[dict, Any]]:
+def import_data(project: str, database: str, method: Union[str, List[str], dict[str, int]]) -> Dict[str, Union[dict, Any]]:
     """ TODO can we find a faster way to import the data without having to save it locally? """
     """
     Main function to import LCI data for a project from a database.
@@ -12,93 +12,33 @@ def import_data(project: str, database: str, method: Union[str, List[str]], dire
     :param project: The name of the Ecoinvent project.
     :param database: The name of the Ecoinvent database.
     :param method: The method(s) to evaluate. Can be a single method name or a list of method names.
-    :param directory: The working directory where the matrices are saved to or read from.
-    :param force: If True, re-load the LCI data regardless of the existing data in the directory.
     :return: A dictionary with the relevant LCI data.
 
     :rtype: dict[str, Union[dict, Any]]
     """
+    # Set project and get database
     bw.projects.set_current(project)
     eidb = bw.Database(database)
-
+    # Prepare methods
     if isinstance(method, str):
         method = [method]  # Convert single string to list of strings
     method = sorted(method)
-
-    paths = get_paths(project, database, method, directory)
-
-    if should_reload_data(paths, force):
-        methods = retrieve_methods(project, method)
-        random_act = eidb.random()
-        myMultiLCA, lca = perform_generic_lca(random_act, methods)
-        save_lci_data(directory, project, method, database, myMultiLCA, lca, eidb)
-
-    lci_data = read_lci_data(directory, project, method, database)
-
-    return lci_data
-
-
-def get_paths(project: str, database: str, method: List[str], directory: str) -> List[str]:
-    """
-    Get a list of file paths for the LCI data files.
-    """
-    directory = directory.replace('\\', '/')
-
-    paths = [
-        f"{directory}/processed/{project}_{meth.replace(':',';').replace('/','!')}.npz" for meth in method
-    ]
-    paths.append(f"{directory}/processed/{project}_{database}_biosphere.npz")
-    paths.append(f"{directory}/processed/{project}_{database}_technosphere.npz")
-    paths.append(f"{directory}/processed/{project}_{database}_names.p")
-
-    return paths
-
-
-def should_reload_data(paths: List[str], force: bool) -> bool:
-    """
-    Check if the LCI data should be reloaded.
-    """
-    return any(not Path(path).exists() for path in paths) or force
-
-
-def perform_generic_lca(random_act: bw.Database, methods: List[str]) -> tuple:
-    """
-    Perform a generic LCA calculation.
-    """
-    bw.calculation_setups['multiLCA'] = {'inv': [{random_act: 1}], 'ia': methods}
-    myMultiLCA = bw.MultiLCA('multiLCA')
-    lca = myMultiLCA.lca
-
-    return myMultiLCA, lca
-
-
-def save_lci_data(directory: str, project: str, method: List[str], database: str,
-                  myMultiLCA: bw.MultiLCA, lca: bw.LCA, eidb: bw.Database):
-    """
-    Save the LCI data to files.
-    """
-    Path(f"{directory}/processed").mkdir(parents=True, exist_ok=True)
-
-    for i in range(0,len(myMultiLCA.methods)):
-        sparse.save_npz(f"{directory}/processed/{project}_{str(myMultiLCA.methods[i]).replace(':',';').replace('/','!')}", myMultiLCA.method_matrices[i])
-
-    sparse.save_npz(f"{directory}/processed/{project}_{database}_biosphere", lca.biosphere_matrix)
-    sparse.save_npz(f"{directory}/processed/{project}_{database}_technosphere", lca.technosphere_matrix)
-
+    methods = retrieve_methods(project, method)
+    # Get data
+    matrices = {}
+    rand_act = eidb.random()
+    for method in methods:
+        lca = bw.LCA({rand_act: 1}, method)
+        lca.load_lci_data()
+        lca.load_lcia_data()
+        matrices[str(method)] = lca.characterization_matrix
+    technosphere = lca.technosphere_matrix
+    biosphere = lca.biosphere_matrix
+    # Create activity map key --> ID | ID --> description
     activity_map = lca.product_dict
     for act in eidb:
-        activity_map[activity_map[act.key]] = str(act['name']) + ' | ' + str(act['reference product']) + ' | ' + str(act['location'])
-    pickle.dump(activity_map, open(f"{directory}/processed/{project}_{database}_names.p", 'wb'))
-
-
-def read_lci_data(directory: str, project: str, method: List[str], database: str) -> Dict[str, Union[dict, Any]]:
-    """
-    Read the LCI data from files.
-    """
-    matrices = {meth: sparse.load_npz(f"{directory}/processed/{project}_{meth.replace(':',';').replace('/','!')}.npz") for meth in method}
-    biosphere = sparse.load_npz(f"{directory}/processed/{project}_{database}_biosphere.npz")
-    technosphere = sparse.load_npz(f"{directory}/processed/{project}_{database}_technosphere.npz")
-    activity_map = pickle.load(open(f"{directory}/processed/{project}_{database}_names.p", 'rb'))
+        activity_map[activity_map[act.key]] = str(act['name']) + ' | ' + str(act['reference product']) + ' | ' + str(
+            act['location'])
 
     lci_data = {
         'matrices': matrices,
@@ -137,13 +77,13 @@ def retrieve_activities(project, database, keys=None, activities=None, reference
     else:
         return matching_activities
 
-def retrieve_envflows(project, keys=None, activities=None, categories=None):
+def retrieve_envflows(project, biosphere='biosphere3', keys=None, activities=None, categories=None):
     """
     Retrieve environmental flows from the biosphere database based on specified keys, activities, and categories.
     """
 
     bw.projects.set_current(project)
-    eidb = bw.Database('biosphere3')
+    eidb = bw.Database(biosphere)
 
     if keys is not None:
         if isinstance(keys, str):
@@ -164,35 +104,13 @@ def retrieve_envflows(project, keys=None, activities=None, categories=None):
         return matching_flows
 
 
-def retrieve_methods(project: str, sub_string: str):
+def retrieve_methods(project: str, sub_string: List[str]):
     ''' This function retrieves all the methods that contain the specified list of substrings'''
     bw.projects.set_current(project)
     return [method for method in bw.methods if any([x.lower() in str(method).lower() for x in sub_string])]
 
 def main():
-    import os
-    # Specify the project and database names as well as the impact to minimize
-    project = "PULPO Methanol Case"
-    scenario = "PkBudg500"
-    year = "2040"
-    database = "ecoinvent_cutoff_3.8_remind_SSP2-" + scenario + "_" + year
-    method = {"('IPCC 2013', 'climate change', 'GWP 100a, incl. H and bio CO2')": 1,
-              "('ReCiPe Endpoint (H,A)', 'total', 'total')": 0,
-              "('ReCiPe Endpoint (H,A)', 'ecosystem quality', 'total')": 0,
-              "('ReCiPe Endpoint (H,A)', 'resources', 'total')": 0,
-              "('ReCiPe Endpoint (H,A)', 'human health', 'total')": 0,
-              "('EF v3.0', 'acidification', 'accumulated exceedance (ae)')": 0,}
-
-    # Substitute with your working directory of choice
-    notebook_dir = os.path.dirname(os.getcwd())
-    directory = os.path.join(notebook_dir, 'data')
-
-    # Substitute with your GAMS path
-    GAMS_PATH = "C:/GAMS/37/gams.exe"
-
-    import_data(project, database, method, directory)
-
-
-# This conditional statement checks if the script is being run as the main program
+    1+1
+
 if __name__ == "__main__":
     main()
\ No newline at end of file
diff --git a/pulpo/utils/converter.py b/pulpo/utils/converter.py
index 5910190..fbd9efb 100644
--- a/pulpo/utils/converter.py
+++ b/pulpo/utils/converter.py
@@ -8,6 +8,9 @@ def combine_inputs(lci_data, demand, choices, upper_limit, lower_limit, methods)
     technosphere = lci_data['technosphere']
     activity_map = lci_data['activity_map']
 
+    ''' Remove matrices that are not part of the objective '''
+    matrices = {h: matrices[h] for h in matrices if str(h) in methods}
+
     ''' Prepare the environmental impact matrices Q[h]*B'''
     env_cost = {h: sparse.csr_matrix.dot(matrices[h], biosphere) for h in matrices}
 
@@ -93,3 +96,23 @@ def combine_inputs(lci_data, demand, choices, upper_limit, lower_limit, methods)
         }
     }
     return model_data
+
+def convert_to_dict(input_data):
+    ''' Checks if the passed method is in the correct format '''
+    # Check if the input is a string
+    if isinstance(input_data, str):
+        return {input_data: 1}
+
+    # Check if the input is a list of strings
+    elif isinstance(input_data, list) and all(isinstance(item, str) for item in input_data):
+        return {item: 1 for item in input_data}
+
+    # Check if the input is a dictionary with numerical values
+    elif isinstance(input_data, dict) and all(isinstance(value, (int, float)) for value in input_data.values()):
+        return input_data
+
+    # If the input is of any other type, raise an error
+    else:
+        raise ValueError(
+            "Input should be either a string, a list of strings, or a dictionary with string indices and numerical values")
+
diff --git a/pulpo/utils/optimizer.py b/pulpo/utils/optimizer.py
index c99b2f0..7130deb 100644
--- a/pulpo/utils/optimizer.py
+++ b/pulpo/utils/optimizer.py
@@ -91,6 +91,27 @@ def objective_function(model):
     """Objective is a sum over all indicators with weights. Typically, the indicator of study has weight 1, the rest 0"""
     return sum(model.impacts[h] * model.WEIGHTS[h] for h in model.INDICATOR)
 
+def calculate_methods(instance, lci_data, methods):
+    '''
+    This function calculates the impacts if a method with weight 0 has been specified
+    '''
+    import scipy.sparse as sparse
+    import numpy as np
+    matrices = lci_data['matrices']
+    biosphere = lci_data['biosphere']
+    matrices = {h: matrices[h] for h in matrices if str(h) in methods}
+    env_cost = {h: sparse.csr_matrix.dot(matrices[h], biosphere) for h in matrices}
+    try:
+        scaling_vector = np.array([instance.scaling_vector[x].value for x in instance.scaling_vector])
+    except:
+        scaling_vector = np.array([instance.scaling_vector[x] for x in instance.scaling_vector])
+    impacts = {}
+    for h in matrices:
+        impacts[h] = sum(env_cost[h].dot(scaling_vector))
+    instance.impacts_calculated = pyo.Var([h for h in impacts], initialize=impacts)
+    return instance
+
+
 
 def instantiate(model_data):
     """ This function builds an instance of the optimization model with specific data and objective function"""
@@ -132,15 +153,15 @@ def solve_model(model_instance, gams_path, options=None):
 
         if options is None:
             options = [
-                'option optcr = 0.000000001;',
+                'option optcr = 1e-15;',
                 'option reslim = 3600;',
                 'GAMS_MODEL.optfile = 1;',
                 '$onecho > cplex.opt',
                 'workmem=4096',
                 'scaind=1',
-                'numericalemphasis=1',
-                'epmrk=0.9',
-                'eprhs=1E-9',
+                #'numericalemphasis=1',
+                #'epmrk=0.99',
+                #'eprhs=1E-9',
                 '$offecho',
             ]
 
diff --git a/pulpo/utils/saver.py b/pulpo/utils/saver.py
index f741503..61a16fd 100644
--- a/pulpo/utils/saver.py
+++ b/pulpo/utils/saver.py
@@ -75,6 +75,17 @@ def summarize_results(instance, project, database, choices, constraints, demand,
     # Raw results
     for v in list_of_vars:
         if v.name == 'impacts':
+            try:
+                data = [(k, map[k], v) for k, v in v._data.items()]
+                df = pd.DataFrame(data, columns=['ID', 'Activity', 'Value'])
+            except:
+                data = [(k, v) for k, v in v._data.items()]
+                df = pd.DataFrame(data, columns=['Key', 'Value'])
+            df.sort_values(by=['Value'], inplace=True, ascending=False)
+            print('\nThese are the impacts contained in the objective:')
+            display(df)
+
+        if v.name == 'impacts_calculated':
             try:
                 data = [(k, map[k], v) for k, v in v._data.items()]
                 df = pd.DataFrame(data, columns=['ID', 'Activity', 'Value'])
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..215a067
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,204 @@
+alabaster==0.7.13
+anyio==3.7.0
+appdirs==1.4.4
+argon2-cffi==21.3.0
+argon2-cffi-bindings==21.2.0
+arrow==1.2.3
+asteval==0.9.30
+asttokens==2.2.1
+astunparse==1.6.3
+attrs==23.1.0
+Babel==2.12.1
+backcall==0.2.0
+beautifulsoup4==4.12.2
+bleach==6.0.0
+blinker==1.6.2
+boto3==1.27.1
+botocore==1.30.1
+Bottleneck==1.3.7
+brightway2==2.4.3
+bw-migrations==0.2
+bw2analyzer==0.10
+bw2calc==1.8.2
+bw2data==3.6.6
+bw2io==0.8.7
+bw2parameters==1.1.0
+cached-property==1.5.2
+certifi==2023.5.7
+cffi==1.15.1
+chardet==5.1.0
+charset-normalizer==3.1.0
+click==8.1.3
+click-plugins==1.1.1
+cligj==0.7.2
+colorama==0.4.6
+comm==0.1.3
+constructive-geometries==0.8.2
+contextlib2==21.6.0
+contourpy==1.1.0
+country-converter==1.0.0
+cryptography==41.0.3
+cycler==0.11.0
+datapackage==1.15.2
+debugpy==1.6.7
+decorator==5.1.1
+defusedxml==0.7.1
+docopt==0.6.2
+docutils==0.18.1
+eight==1.0.1
+et-xmlfile==1.1.0
+exceptiongroup==1.1.2
+executing==1.2.0
+fasteners==0.18
+fastjsonschema==2.17.1
+fernet==1.0.1
+Fiona==1.9.4.post1
+Flask==2.3.2
+fonttools==4.40.0
+fqdn==1.5.1
+future==0.18.3
+geopandas==0.13.2
+greenlet==2.0.2
+highspy==1.5.3
+idna==3.4
+ijson==3.2.2
+imagesize==1.4.1
+importlib-metadata==6.7.0
+importlib-resources==5.12.0
+ipykernel==6.24.0
+ipython==8.14.0
+ipython-genutils==0.2.0
+ipywidgets==8.0.6
+isodate==0.6.1
+isoduration==20.11.0
+itsdangerous==2.1.2
+jedi==0.18.2
+Jinja2==3.1.2
+jmespath==1.0.1
+jsonlines==3.1.0
+jsonpointer==2.4
+jsonschema==4.17.3
+jupyter==1.0.0
+jupyter-console==6.6.3
+jupyter-events==0.6.3
+jupyter_client==8.3.0
+jupyter_core==5.3.1
+jupyter_server==2.7.0
+jupyter_server_terminals==0.4.4
+jupyterlab-pygments==0.2.2
+jupyterlab-widgets==3.0.7
+kiwisolver==1.4.4
+linear-tsv==1.1.0
+llvmlite==0.40.1
+lxml==4.9.2
+MarkupSafe==2.1.3
+matplotlib==3.7.1
+matplotlib-inline==0.1.6
+mistune==3.0.1
+mrio-common-metadata==0.2.1
+nbclassic==1.0.0
+nbclient==0.8.0
+nbconvert==7.6.0
+nbformat==5.9.0
+nest-asyncio==1.5.6
+notebook==6.5.4
+notebook_shim==0.2.3
+numba==0.57.1
+numpy==1.24.4
+openpyxl==3.1.2
+overrides==7.3.1
+packaging==23.1
+pandas==1.5.3
+pandocfilters==1.5.0
+parso==0.8.3
+peewee==3.16.2
+pickleshare==0.7.5
+Pillow==10.0.0
+Pint==0.22
+platformdirs==3.8.0
+ply==3.11
+premise==1.6.7
+premise-gwp==0.9.6
+prettytable==3.8.0
+prometheus-client==0.17.0
+prompt-toolkit==3.0.38
+psutil==5.9.5
+pure-eval==0.2.2
+pyaes==1.6.1
+pyarrow==13.0.0
+pybind11==2.11.1
+pycountry==22.3.5
+pycparser==2.21
+Pygments==2.15.1
+Pyomo==6.6.1
+pyparsing==3.1.0
+PyPrind==2.11.3
+pyproj==3.6.0
+pyrsistent==0.19.3
+python-dateutil==2.8.2
+python-json-logger==2.0.7
+pytz==2023.3
+pywin32==306
+pywinpty==2.0.10
+pyxlsb==1.0.10
+PyYAML==6.0
+pyzmq==25.1.0
+qtconsole==5.4.3
+QtPy==2.3.1
+requests==2.31.0
+rfc3339-validator==0.1.4
+rfc3986==2.0.0
+rfc3986-validator==0.1.1
+s3transfer==0.6.1
+schema==0.7.5
+scipy==1.11.1
+Send2Trash==1.8.2
+shapely==2.0.1
+six==1.16.0
+sniffio==1.3.0
+snowballstemmer==2.2.0
+soupsieve==2.4.1
+sparse==0.14.0
+Sphinx==6.2.1
+sphinx-rtd-theme==1.2.2
+sphinxcontrib-applehelp==1.0.4
+sphinxcontrib-devhelp==1.0.2
+sphinxcontrib-htmlhelp==2.0.1
+sphinxcontrib-jquery==4.1
+sphinxcontrib-jsmath==1.0.1
+sphinxcontrib-qthelp==1.0.3
+sphinxcontrib-serializinghtml==1.1.5
+SQLAlchemy==2.0.18
+stack-data==0.6.2
+stats-arrays==0.6.5
+structlog==21.5.0
+tableschema==1.20.2
+tabulate==0.9.0
+tabulator==1.53.5
+terminado==0.17.1
+tinycss2==1.2.1
+toolz==0.12.0
+tornado==6.3.2
+tqdm==4.65.0
+traitlets==5.9.0
+typing_extensions==4.7.1
+tzdata==2023.3
+unfold==1.0.8
+unicodecsv==0.14.1
+Unidecode==1.3.6
+uri-template==1.3.0
+urllib3==1.26.16
+voluptuous==0.13.1
+wcwidth==0.2.6
+webcolors==1.13
+webencodings==0.5.1
+websocket-client==1.6.1
+Werkzeug==2.3.6
+Whoosh==2.7.4
+widgetsnbextension==4.0.7
+wrapt==1.15.0
+wurst==0.3.3
+xarray==2023.8.0
+xlrd==2.0.1
+XlsxWriter==3.1.2
+zipp==3.15.0


Test patch:
diff --git a/tests/sample_database.py b/tests/sample_database.py
new file mode 100644
index 0000000..0fd70b5
--- /dev/null
+++ b/tests/sample_database.py
@@ -0,0 +1,145 @@
+from brightway2 import Database, projects, Method, databases, methods
+import brightway2 as bw
+import copy
+
+def setup_test_db():
+    # Set the current project to "sample_project"
+    projects.set_current("sample_project")
+
+    # Keys
+    # Biosphere
+    co2_key = ('biosphere', 'CO2')
+    ch4_key = ('biosphere', 'CH4')
+    pm_key = ('biosphere', 'PM')
+    h2o_irrigation_key = ('biosphere', 'H2O_irrigation')
+    # Technosphere
+    wind_turbine_key = ('technosphere', 'wind turbine')
+    steam_cycle_key = ('technosphere', 'steam cycle')
+    lignite_extraction_key = ('technosphere', 'lignite extraction')
+    oil_extraction_key = ('technosphere', 'oil extraction')
+    e_car_key = ('technosphere', 'e-Car')
+
+    # Define biosphere flows and create the "biosphere" database if it doesn't exist
+    if "biosphere" not in databases:
+        biosphere_db = Database("biosphere")
+        biosphere_data = {
+            ("biosphere", "CO2"): {
+                "name": "Carbon dioxide, fossil",
+                "categories": ("climate change", "GWP 100a"),
+                "type": "emission",
+                "unit": "kg",
+            },
+            ("biosphere", "CH4"): {
+                "name": "Methane, agricultural",
+                "categories": ("climate change", "GWP 100a"),
+                "type": "emission",
+                "unit": "kg",
+            },
+            ("biosphere", "PM"): {
+                "name": "Particulate matter, industrial",
+                "categories": ("air quality", "particulate matter"),
+                "type": "emission",
+                "unit": "g",
+            },
+            ("biosphere", "H2O_irrigation"): {
+                "name": "Water, irrigation",
+                "categories": ("water use", "irrigation"),
+                "type": "resource",
+                "unit": "m3",
+            },
+        }
+        biosphere_db.write(biosphere_data)
+
+    # Create the "technosphere" database if it doesn't exist
+    if "technosphere" not in databases:
+        technosphere_db = Database("technosphere")
+        technosphere_db.write({})
+        process_data = [
+            ("oil extraction", "kg", "GLO", "oil"),
+            ("lignite extraction", "kg", "GLO", "lignite"),
+            ("steam cycle", "kWh", "GLO", "electricity"),
+            ("wind turbine", "kWh", "GLO", "electricity"),
+            ("e-Car", "tkm", "GLO", "transport"),
+        ]
+
+        for name, unit, location, ref_product in process_data:
+            act = technosphere_db.new_activity(name)
+            act["unit"] = unit
+            act["location"] = location
+            act["name"] = name
+            act["reference product"] = ref_product
+            act.new_exchange(amount=1.0, input=act.key, type="production").save()
+            act.save()
+
+        exchange_data = [
+            [e_car_key, oil_extraction_key, 0.03, 'technosphere'],
+            [e_car_key, lignite_extraction_key, 0.03, 'technosphere'],
+            [oil_extraction_key, steam_cycle_key, 0.5, 'technosphere'],
+            [lignite_extraction_key, steam_cycle_key, 0.5, 'technosphere'],
+            [steam_cycle_key, e_car_key, 0.5, 'technosphere'],
+            [wind_turbine_key, e_car_key, 0.5, 'technosphere'],
+            [e_car_key, wind_turbine_key, 0.03, 'technosphere'],
+            [co2_key, oil_extraction_key, 0.2, 'biosphere'],
+            [co2_key, lignite_extraction_key, 0.3, 'biosphere'],
+            [co2_key, steam_cycle_key, 1, 'biosphere'],
+            [co2_key, wind_turbine_key, 0.1, 'biosphere'],
+            [ch4_key, oil_extraction_key, 0.01, 'biosphere'],
+            [ch4_key, lignite_extraction_key, 0.02, 'biosphere'],
+            [pm_key, steam_cycle_key, 2.0, 'biosphere'],
+            [pm_key, wind_turbine_key, 1.5, 'biosphere'],
+            [h2o_irrigation_key, steam_cycle_key, 2.0, 'biosphere'],
+            [h2o_irrigation_key, wind_turbine_key, 5.0, 'biosphere'],
+            [pm_key, e_car_key, 0.1, 'biosphere'],
+            [h2o_irrigation_key, e_car_key, 0.1, 'biosphere'],
+        ]
+
+        for input, target, amount, type in exchange_data:
+            act = [act for act in technosphere_db if act.key==target][0]
+            act.new_exchange(amount=amount, input=input, type=type).save()
+            act.save()
+
+    # Loop through the list of methods and deregister each one
+    # Create a copy of the methods list
+    methods_copy = copy.deepcopy(methods)
+    # Loop through the copy of methods and deregister each one
+    for method in methods_copy:
+        Method(method).deregister()
+
+    # Define LCIA methods and CFs
+    methods_data = [
+        ("climate change", "kg CO2eq", 2, "cc", "climate change CFs", "climate_change", "CO2", [(co2_key, 1), (ch4_key, 29.7)]),
+        ("air quality", "ppm", 1, "aq", "air quality CFs", "air_quality", "PM", [(ch4_key, 29.7)]),
+        ("resources", "m3", 1, "rc", "resource CFs", "resources", "H2O_irrigation", [(h2o_irrigation_key,1)]),
+    ]
+
+    for method_name, unit, num_cfs, abbreviation, description, filename, flow_code, flow_list in methods_data:
+        method = Method(("my project", method_name))
+        method.register(**{
+            "unit": unit,
+            "num_cfs": num_cfs,
+            "abbreviation": abbreviation,
+            "description": description,
+            "filename": filename,
+        })
+        method.write(flow_list)
+
+def sample_lcia():
+    # Load the technosphere database
+    bw.projects.set_current('sample_project')
+    technosphere_db = bw.Database("technosphere")
+    # Perform LCA with FU 1 for all activities
+    act = {act.key: 1 for act in technosphere_db}
+    # Perform Multi-LCA
+    bw.calculation_setups['multiLCA'] = {'inv': [act], 'ia': list(methods)}
+    myMultiLCA = bw.MultiLCA('multiLCA')
+    results = [round(x, 5) for x in myMultiLCA.results[0]]
+    return results
+
+def main():
+    setup_test_db()
+    print(sample_lcia())
+
+if __name__ == "__main__":
+    main()
+
+
diff --git a/tests/test_functions.py b/tests/test_functions.py
index 633a4ae..1580223 100644
--- a/tests/test_functions.py
+++ b/tests/test_functions.py
@@ -1,48 +1,79 @@
+import pulpo as pulpo
+import unittest
+from sample_database import sample_lcia, setup_test_db
+from pulpo.utils.bw_parser import import_data, retrieve_methods, retrieve_envflows, retrieve_activities
 from pulpo import pulpo
 
-test_project = 'Gasification'
-test_database = 'cutoff38'
-#test_method1 = "('IPCC 2013', 'climate change', 'GWP 100a')"
-test_method2 = "('CML 2001 (superseded)', 'terrestrial ecotoxicity', 'TAETP infinite')"
-directory = r'C:\Users\Usuario\Documents\PhD Documents\Python\pulpo\data'
-GAMS_PATH = r'C:\GAMS\37\gams.exe'
-
-activities = ["market for hydrogen, liquid"]
-reference_products = ["hydrogen, liquid"]
-locations = ["RER"]
-
-pulpo_worker = pulpo.PulpoOptimizer(test_project, test_database, test_method2, directory)
-
-# demand
-hydrogen_market = pulpo_worker.retrieve_activities(activities=activities, reference_products=reference_products, locations=locations)
-demand = {hydrogen_market[0]: 100}
-
-# choices
-activities = ["chlor-alkali electrolysis, diaphragm cell",
-             "chlor-alkali electrolysis, membrane cell",
-             "chlor-alkali electrolysis, mercury cell",
-             "hydrogen cracking, APME"]
-reference_products = ["hydrogen, liquid"]
-locations = ["RER"]
-
-hydrogen_activities = pulpo_worker.retrieve_activities(activities=activities, reference_products=reference_products, locations=locations)
-choices = {'hydrogen': {hydrogen_activities[0]: 10000,
-                                hydrogen_activities[1]: 10000,
-                                hydrogen_activities[2]: 10000,
-                                hydrogen_activities[3]: 10000}}
-
-# supply
-supply = {}
-
-# constraints
-constraints = {}
-
-# objective weighting
-#weights = {test_method1: 1, test_method2: 0}
-
-# Get LCI data
-lci_data = pulpo_worker.get_lci_data()
-# Instantiate
-instance = pulpo_worker.instantiate(choices=choices, demand=demand)
-# Solve
-results = pulpo_worker.solve(GAMS_PATH)
+setup_test_db()
+
+class TestParser(unittest.TestCase):
+    def test_database_import(self):
+        result = sample_lcia()
+        self.assertEqual(result, [4.22308, 1.5937, 11.1567])  # Example assertion
+
+    def test_import_data(self):
+        # Test if the import works and has the expected structure
+        methods = {
+            "('my project', 'climate change')": 1,
+            "('my project', 'air quality')": 1,
+            "('my project', 'resources')": 1,
+        }
+
+        result = import_data('sample_project', 'technosphere', methods)
+
+        self.assertEqual([idx for idx in result], ['matrices', 'biosphere', 'technosphere', 'activity_map'])
+        self.assertEqual(result['technosphere'].shape, (5, 5))
+        self.assertEqual(result['biosphere'].shape, (4, 5))
+        self.assertEqual(result['activity_map'], {('technosphere', 'oil extraction'): 0, ('technosphere', 'lignite extraction'): 1, ('technosphere', 'steam cycle'): 2, ('technosphere', 'wind turbine'): 3, ('technosphere', 'e-Car'): 4, 2: 'steam cycle | electricity | GLO', 1: 'lignite extraction | lignite | GLO', 0: 'oil extraction | oil | GLO', 3: 'wind turbine | electricity | GLO', 4: 'e-Car | transport | GLO'})
+
+    def test_retrieve_activities(self):
+        key = retrieve_activities('sample_project', 'technosphere', keys=["('technosphere', 'wind turbine')"])
+        name = retrieve_activities('sample_project', 'technosphere', activities=['e-Car'])
+        location = retrieve_activities('sample_project', 'technosphere', locations=['GLO'])
+        self.assertEqual(key[0]['name'], "wind turbine")
+        self.assertEqual(name[0]['name'], "e-Car")
+        self.assertEqual(len(location), 5)
+
+    def test_retrieve_methods(self):
+        single_result = retrieve_methods('sample_project', ['climate'])
+        multi_result = retrieve_methods('sample_project', ['project'])
+        self.assertEqual(single_result, [('my project', 'climate change')])
+        self.assertEqual(multi_result, [('my project', 'climate change'), ('my project', 'air quality'), ('my project', 'resources')])
+
+    def test_retrieve_envflows(self):
+        result = retrieve_envflows('sample_project', biosphere='biosphere', keys="('biosphere', 'PM')")
+        self.assertEqual(result[0]['name'], 'Particulate matter, industrial')
+
+class TestPULPO(unittest.TestCase):
+
+    def test_pulpo(self):
+        project = 'sample_project'
+        database = 'technosphere'
+        methods = {"('my project', 'climate change')": 1,
+                   "('my project', 'air quality')": 1,
+                   "('my project', 'resources')": 0}
+
+        worker = pulpo.PulpoOptimizer(project, database, methods, '')
+        worker.get_lci_data()
+        eCar = worker.retrieve_activities(reference_products='transport')
+        demand = {eCar[0]: 1}
+        elec = worker.retrieve_activities(reference_products='electricity')
+        choices = {'electricity': {elec[0]: 100, elec[1]: 100}}
+        worker.instantiate(choices=choices, demand=demand)
+        worker.solve()
+        result_obj = round(worker.instance.OBJ(), 6)
+        result_aux = round(worker.instance.impacts_calculated["('my project', 'resources')"].value, 5)
+        self.assertEqual(result_obj, 0.103093)
+        self.assertEqual(result_aux, 5.25773)
+
+        upper_limit = {eCar[0]: 1}
+        lower_limit = {eCar[0]: 1}
+        worker.instantiate(choices=choices, upper_limit=upper_limit, lower_limit=lower_limit)
+        worker.solve()
+        result_obj = round(worker.instance.OBJ(), 6)
+        result_aux = round(worker.instance.impacts_calculated["('my project', 'resources')"].value, 5)
+        self.assertEqual(result_obj, 0.1)
+        self.assertEqual(result_aux, 5.1)
+
+
+