diff --git a/.gitignore b/.gitignore index e2fb05d..ace5617 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,5 @@ src/* examples/files/DE.inc __pycache__ output -config \ No newline at end of file +config +build \ No newline at end of file diff --git a/docs/get_started/examples.md b/docs/examples.md similarity index 73% rename from docs/get_started/examples.md rename to docs/examples.md index bc30fc9..d717069 100644 --- a/docs/get_started/examples.md +++ b/docs/examples.md @@ -1,6 +1,13 @@ - -## Examples +# Examples The following notebooks provide examples on how to use pybalmorel for pre-processing, post-processing and for executing Balmorel scenarios: - [Pre-Processing](https://github.com/Mathias157/pybalmorel/blob/master/examples/PreProcessing.ipynb) - [Execution](https://github.com/Mathias157/pybalmorel/blob/master/examples/Execution.ipynb) - [Post-Processing](https://github.com/Mathias157/pybalmorel/blob/master/examples/PostProcessing.ipynb) + +The next pages also include some code snippets on how to use various functions. + +```{toctree} +:maxdepth: 1 + +examples/geofilemaker.md +``` \ No newline at end of file diff --git a/docs/examples/geofilemaker.md b/docs/examples/geofilemaker.md new file mode 100644 index 0000000..28729a6 --- /dev/null +++ b/docs/examples/geofilemaker.md @@ -0,0 +1,16 @@ +# Defining Geography + +pybalmorel includes a GUI to interactively define nodes in Balmorel's hierarchical geographic structure comprising countries, regions and areas. +```python +from pybalmorel import GUI +GUI.geofilemaker() +``` + +The video below illustrates how it works. +:::{figure} ../img/geoset_generator_example.gif +:name: geofilemaker +:alt: How to use the 'geofilemaker' GUI. +:width: 100% +:align: center +How to use the 'geofilemaker' GUI. +::: \ No newline at end of file diff --git a/docs/get_started.md b/docs/get_started.md index 63e7a09..9d15135 100644 --- a/docs/get_started.md +++ b/docs/get_started.md @@ -1,8 +1,9 @@ # Get Started +An installation instruction. + ```{toctree} :maxdepth: 1 get_started/installation.md -get_started/examples.md ``` \ No newline at end of file diff --git a/docs/get_started/installation.md b/docs/get_started/installation.md index 9cc5832..376e56a 100644 --- a/docs/get_started/installation.md +++ b/docs/get_started/installation.md @@ -13,13 +13,13 @@ channels: - conda-forge dependencies: - python >= 3.9 - - pandas >= 2.1.4 - - matplotlib >= 3.9.0 - - geopandas >= 0.14.4 - - ipywidgets >= 8.1.3 - - ipykernel + - pandas>=2.1.4 + - matplotlib>=3.9.0 + - geopandas>=1.0.1 + - ipywidgets>=8.1.3 - pip - pip: - - gamsapi[transfer] >= 45.0.0 - - pybalmorel + - gamsapi[transfer]>=45.0.0 + - eel>=0.17.0 + - pybalmorel==0.3.6 ``` diff --git a/docs/img/geoset_generator_example.gif b/docs/img/geoset_generator_example.gif new file mode 100644 index 0000000..9c1db38 Binary files /dev/null and b/docs/img/geoset_generator_example.gif differ diff --git a/docs/index.md b/docs/index.md index f2379ea..1686de6 100644 --- a/docs/index.md +++ b/docs/index.md @@ -10,6 +10,7 @@ Get started [here](get_started.md). :hidden: get_started +examples ``` ```{toctree} diff --git a/environment.yaml b/environment.yaml new file mode 100644 index 0000000..5015d32 --- /dev/null +++ b/environment.yaml @@ -0,0 +1,14 @@ +name: pybalmorel +channels: + - conda-forge +dependencies: + - python >= 3.9 + - pandas>=2.1.4 + - matplotlib>=3.9.0 + - geopandas>=1.0.1 + - ipywidgets>=8.1.3 + - pip + - pip: + - gamsapi[transfer]>=45.0.0 + - eel>=0.17.0 + - pybalmorel==0.3.6 \ No newline at end of file diff --git a/environment.yml b/environment.yml deleted file mode 100644 index 3ec21f9..0000000 --- a/environment.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: pybalmorel -channels: - - conda-forge -dependencies: - - python >= 3.9 - - pandas >= 2.1.4 - - matplotlib >= 3.9.0 - - geopandas >= 0.14.4 - - ipywidgets >= 8.1.3 - - ipykernel - - pip - - pip: - - gamsapi[transfer] >= 45.0.0 - - pybalmorel \ No newline at end of file diff --git a/examples/PreProcessing.ipynb b/examples/PreProcessing.ipynb index f27eae9..31d28a7 100644 --- a/examples/PreProcessing.ipynb +++ b/examples/PreProcessing.ipynb @@ -18,7 +18,7 @@ "outputs": [], "source": [ "### 0.1 Use development scripts or the package installed from pip\n", - "use_development = True\n", + "use_development = False\n", "if use_development:\n", " import sys\n", " import os\n", @@ -26,7 +26,7 @@ " project_root = os.path.abspath(os.path.join(os.path.dirname(\"__file__\"), '..'))\n", " if project_root not in sys.path:\n", " sys.path.insert(0, project_root)\n", - " from src.pybalmorel import IncFile\n", + " from src.pybalmorel import IncFile, GUI\n", "else:\n", " from pybalmorel import IncFile" ] @@ -40,7 +40,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -65,6 +65,30 @@ "# Save .inc file to path (will save as ./Balmorel/sc1/data/DE.inc)\n", "DE.save()" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## GUI for Generating Geographic .inc Files\n", + "\n", + "Will generate the necessary geographic files:\n", + "- CCCRRRAAA.inc\n", + "- CCC.inc\n", + "- RRR.inc\n", + "- AAA.inc\n", + "- CCCRRR.inc\n", + "- RRRAAA.inc" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "GUI.geofilemaker()" + ] } ], "metadata": { @@ -83,7 +107,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.11" + "version": "3.12.6" } }, "nbformat": 4, diff --git a/pyproject.toml b/pyproject.toml index 576526e..852afcf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pybalmorel" -version = "0.3.5" +version = "0.3.6" maintainers = [ { name="Mathias Berg Rosendal", email="mathiasros@gmail.com"}, { name="Théodore Le Nalinec"}, @@ -17,7 +17,7 @@ classifiers = [ "Operating System :: OS Independent", ] dependencies = ['pandas>=2.1.4', 'matplotlib>=3.9.0', 'geopandas>=0.14.4', - 'gamsapi[transfer]>=45.0.0', 'ipywidgets>=8.1.3'] + 'gamsapi[transfer]>=45.0.0', 'ipywidgets>=8.1.3', 'eel>=0.17.0'] [project.urls] Repository = "https://github.com/Mathias157/pybalmorel" diff --git a/src/pybalmorel/__init__.py b/src/pybalmorel/__init__.py index d501d45..8022d51 100644 --- a/src/pybalmorel/__init__.py +++ b/src/pybalmorel/__init__.py @@ -1,4 +1,4 @@ from . import formatting, utils -from .classes import IncFile, MainResults, Balmorel +from .classes import IncFile, MainResults, Balmorel, GUI -__all__ = [IncFile, MainResults, Balmorel] +__all__ = [IncFile, MainResults, Balmorel, GUI] diff --git a/src/pybalmorel/classes.py b/src/pybalmorel/classes.py index 6e31ed2..c635cc7 100644 --- a/src/pybalmorel/classes.py +++ b/src/pybalmorel/classes.py @@ -19,6 +19,7 @@ from matplotlib.axes import Axes from .utils import symbol_to_df from .interactive.interactive_functions import interactive_bar_chart +from .interactive.dashboard.eel_dashboard import interactive_geofilemaker from .plotting.production_profile import plot_profile from .plotting.maps_balmorel import plot_map @@ -391,4 +392,29 @@ def load_incfiles(self, model_db.run() # Store the database (will take some minutes) - self.input_data[scenario] = model_db.get_out_db() \ No newline at end of file + self.input_data[scenario] = model_db.get_out_db() + + +class GUI: + def __init__(self) -> None: + pass + + # Interactive bar chart plotting + def bar_chart(self, MainResults_instance): + """Interactive GUI to plot bar charts from MainResults + + Args: + MainResults_instance (class): Loaded MainResults + + Returns: + None: An interactive GUI is opened to plot bar charts + """ + return interactive_bar_chart(MainResults_instance) + + def geofilemaker(): + """Opens a GUI to interactively generate necessary .inc files for Balmorel geography + + Returns: + None: An interactive GUI to generate geographic .inc files + """ + return interactive_geofilemaker() \ No newline at end of file diff --git a/src/pybalmorel/interactive/dashboard/eel_dashboard.py b/src/pybalmorel/interactive/dashboard/eel_dashboard.py new file mode 100644 index 0000000..a2d5ae0 --- /dev/null +++ b/src/pybalmorel/interactive/dashboard/eel_dashboard.py @@ -0,0 +1,87 @@ +#%% +import eel +import ast +from pybalmorel import IncFile +import pkg_resources +import os + +# 1.0 Other functions +def get_wkdir(): + return os.path.abspath('.') + +# 1.1 Create .inc Files +def create_incfile(unique_processing): + """The general wrapper for creating and saving .inc files, + because the creating of the IncFile class and saving it is the same everytime. + The unique processing of the .body content differs, however. + + Args: + unique_processing (func): The unique processing per case + **incfile_kwargs: Keyword arguments to pass to IncFile + """ + def wrapper(**kwargs): + inc_file = IncFile(name=kwargs['name'], prefix=kwargs['prefix'], + suffix=kwargs['suffix'], path=kwargs['path']) + unique_processing(inc_file, kwargs['geo_nodes']) + inc_file.save() + return wrapper + +## 1.1.1 CCC, RRR or AAA +@create_incfile +def create_sets(inc_file: IncFile, geo_nodes_layer2: dict): + inc_file.body += '\n'.join(list(geo_nodes_layer2.keys())) + +## 1.1.2 CCCRRRAAA +@create_incfile +def create_CCCRRRAAA(inc_file: IncFile, geo_nodes: dict): + for key in geo_nodes.keys(): + inc_file.body += '\n* %s:\n' % key.capitalize() + inc_file.body += '\n'.join(geo_nodes[key].keys()) + +## 1.1.3 CCCRRR or RRRAAA +@create_incfile +def create_setconnection(inc_file: IncFile, geo_nodes_layer1: dict): + for node in geo_nodes_layer1.keys(): + if len(geo_nodes_layer1[node]) != 0: + inc_file.body += f'\n{node} . ' + inc_file.body += f'\n{node} . '.join(geo_nodes_layer1[node]) + + +# 1.2 Create .inc Files +def create_incfiles(output: str, path: str): + geo_nodes = ast.literal_eval(output) # Convert output to dict + prefix = """SET CCC(CCCRRRAAA) 'All countries' +/\n""" + create_sets(geo_nodes=geo_nodes['countries'], name='CCC', prefix=prefix, suffix="\n/;", path=path) + + prefix = """SET RRR(CCCRRRAAA) 'All regions' +/\n""" + create_sets(geo_nodes=geo_nodes['regions'], name='RRR', prefix=prefix, suffix="\n/;", path=path) + + prefix = """SET AAA(CCCRRRAAA) 'All areas' +/\n""" + create_sets(geo_nodes=geo_nodes['areas'], name='AAA', prefix=prefix, suffix="\n/;", path=path) + + prefix = """* All sets that are related to Geographical resolution +SET CCCRRRAAA 'All geographical entities (CCC + RRR + AAA)' +/""" + create_CCCRRRAAA(geo_nodes=geo_nodes, name='CCCRRRAAA', prefix=prefix, suffix="\n/;", path=path) + + prefix="""SET CCCRRR(CCC,RRR) "Regions in countries" +/""" + create_setconnection(geo_nodes=geo_nodes['countries'], name='CCCRRR', prefix=prefix, suffix="\n/;", path=path) + + prefix="""SET RRRAAA(RRR,AAA) "Areas in regions" +/""" + create_setconnection(geo_nodes=geo_nodes['regions'], name='RRRAAA', prefix=prefix, suffix="\n/;", path=path) + +def interactive_geofilemaker(): + + # Get working directory and package directory + static_path = pkg_resources.resource_filename(__name__, 'static') + index_path = pkg_resources.resource_filename(__name__, 'static/index.html') + + eel.init(static_path) + eel.expose(get_wkdir) + eel.expose(create_incfiles) + eel.start(index_path) \ No newline at end of file diff --git a/src/pybalmorel/interactive/dashboard/static/index.html b/src/pybalmorel/interactive/dashboard/static/index.html new file mode 100644 index 0000000..26a2b9e --- /dev/null +++ b/src/pybalmorel/interactive/dashboard/static/index.html @@ -0,0 +1,97 @@ + + +
+ + +Countries
+Regions
+Areas
+