Skip to content

Commit

Permalink
Improving symbol_to_df and added a fast .locate_results() function (#23)
Browse files Browse the repository at this point in the history
* Made symbol_to_df accept variables or equations too

* Made function for locating results, if you just want an overview instead of the more heavy loading

* Improving docs

* Making the plot maps function work for any cases in the commodity input

* Release 0.4.3
  • Loading branch information
Mathias157 authored Oct 20, 2024
1 parent 2df71c1 commit 39c7936
Show file tree
Hide file tree
Showing 11 changed files with 108 additions and 20 deletions.
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
project = "pybalmorel"
copyright = "2024, Mathias Berg Rosendal, Théodore Le Nalinec"
author = "Mathias Berg Rosendal, Théodore Le Nalinec"
release = "0.4.2"
release = "0.4.3"

exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ".testenv", ".testenv/**"]

Expand Down
2 changes: 1 addition & 1 deletion docs/examples/execution.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Execution

The 'Balmorel' class can also be used to run Balmorel scenarios.
The 'Balmorel' class can also be used to run Balmorel scenarios. Check out [this notebook](https://github.com/Mathias157/pybalmorel/blob/master/examples/Execution.ipynb) or the examples below.

```python
from pybalmorel import Balmorel
Expand Down
43 changes: 42 additions & 1 deletion docs/examples/postprocessing.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,44 @@
# Post-Processing

There are several functions to plot energy balances, transmission capacity maps and a ipywidget to interactively plot bar charts, check out [this notebook](https://github.com/Mathias157/pybalmorel/blob/master/examples/PostProcessing.ipynb).
There are several functions to plot energy balances, transmission capacity maps and a ipywidget to interactively plot bar charts, check out [this notebook](https://github.com/Mathias157/pybalmorel/blob/master/examples/PostProcessing.ipynb).

The example below uses the Balmorel class to load results.

## Interactively Plotting All Results
```python
from pybalmorel import Balmorel

# Load Balmorel
model = Balmorel('path/to/Balmorel')

# Collect results
model.collect_results()

# Plot bar charts with an interactive GUI
model.results.interactive_bar_chart()
```

## Transmission Maps and Energy Balances

Using the collected results below, the examples below illustrate how to plot figures of transmission capacities and save them.

```python
# Plot electricity transmission capacities in the first scenario, year 2050
model.results.plot_map(model.results.sc[0], 'electricity', 2050)

# Plot total heat energy balance in the second scenario, year 2050
model.results.plot_profile('heat', 2050, model.results.sc[1])
```

## Getting Specific Results
The `model.results` is actually a `MainResults` class. The example below illustrates how to get specific results using that class.

```python
from pybalmorel import MainResults

mainresults_files = ['MainResults_SC1.gdx', 'MainResults_SC2.gdx']
paths = ['path/to/folder/with/SC1', 'path/to/folder/with/SC2']
results = MainResults(mainresults_files, paths=paths)
```

If the files are in the same folder, you just need to input a single path to the paths argument. The class will name the scenarios 'SC1' and 'SC2' by default and store them in a list in `results.sc`, but you can also provide your own names with the scenario_names argument.
2 changes: 1 addition & 1 deletion docs/examples/preprocessing.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Pre-Processing

The examples below show how to create single .inc-files, loading all .inc files using the Balmorel class, and defining geography using an interactive GUI.
The examples below show how to create single .inc-files, loading all .inc files using the Balmorel class, and defining geography using an interactive GUI. A [notebook](https://github.com/Mathias157/pybalmorel/blob/master/examples/PreProcessing.ipynb) can also be downloaded from the GitHub repository.

## Create an .inc File
The 'IncFile' class is a handy class for creating .inc files that are the input for Balmorel.
Expand Down
2 changes: 1 addition & 1 deletion docs/get_started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ dependencies:
- ipykernel>=6.29.5
- pip
- pip:
- pybalmorel==0.4.2
- pybalmorel==0.4.3
```
2 changes: 1 addition & 1 deletion environment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@ dependencies:
- pytest=8.3.3
- pip
- pip:
- pybalmorel==0.4.2
- pybalmorel==0.4.3
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pybalmorel"
version = "0.4.2"
version = "0.4.3"
maintainers = [
{ name="Mathias Berg Rosendal", email="[email protected]"},
{ name="Théodore Le Nalinec"},
Expand Down
43 changes: 33 additions & 10 deletions src/pybalmorel/classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -307,23 +307,46 @@ def __init__(self, model_folder: str, gams_system_directory: str = None):
else:
print('Folder %s not added to scenario as the necessary %s/model/Balmorel.gms and/or %s/model/cplex.op4 did not exist'%tuple([SC]*3))

def collect_results(self):

files = []
paths = []
scenario_names = []
def locate_results(self):
"""
Locates results, which is faster than collecting them if you just want an overview
"""

self.files = []
self.paths = []
self.scenario_names = []
self.scfolder_to_scname = {}
self.scname_to_scfolder = {}
for SC in self.scenarios:
path = os.path.join(self.path, '%s/model'%SC)
mainresults_files = pd.Series(os.listdir(path))
mainresults_files = mainresults_files[(mainresults_files.str.find('MainResults') != -1) & (mainresults_files.str.find('.gdx') != -1)]
files += list(mainresults_files)
paths += [path]*len(mainresults_files)
self.files += list(mainresults_files)
self.paths += [path]*len(mainresults_files)
if len(mainresults_files) == 1:
scenario_names += [SC]
self.scenario_names += [SC]
self.scfolder_to_scname[SC] = [SC]
self.scname_to_scfolder[SC] = SC
else:
scenario_names += list(mainresults_files)
mainresults_files = (
mainresults_files
.str.replace('MainResults_', '')
.str.replace('.gdx', '')
)
self.scenario_names += list(mainresults_files)
self.scfolder_to_scname[SC] = list(mainresults_files)

for scenario_name in mainresults_files:
self.scname_to_scfolder[scenario_name] = SC

def collect_results(self):
"""
Collects results
"""

self.locate_results()

self.results = MainResults(files=files, paths=paths, scenario_names=scenario_names, system_directory=self._gams_system_directory)
self.results = MainResults(files=self.files, paths=self.paths, scenario_names=self.scenario_names, system_directory=self._gams_system_directory)

def run(self, scenario: str, cmd_line_options: dict = {}):

Expand Down
1 change: 1 addition & 0 deletions src/pybalmorel/plotting/maps_balmorel.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ def plot_map(path_to_result: str,
font_hub = 12 #Font size of hub labels
font_region = 10 #Font size of region labels
line_decimals = 1 #Number of decimals shown for line capacities
COMMODITY = COMMODITY.capitalize()
# For Elec
if COMMODITY == 'Electricity':
line_width_constant = .2 #Constant related to thickness of lines: the higher the number, the narrower the lines will be
Expand Down
25 changes: 24 additions & 1 deletion src/pybalmorel/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,25 @@ def create_parameter_columns(df: pd.DataFrame,

return df

def create_variable_columns(df: pd.DataFrame,
db: gams.GamsDatabase,
symbol: str,
mainresult_symbol_columns: list,
cols: Tuple[list, None]):
if cols == None:
try:
df.columns = mainresult_symbol_columns[symbol] + ['Unit', 'Value', 'Marginal', 'Lower', 'Upper', 'Scale']
except (ValueError, KeyError):
try:
df.columns = mainresult_symbol_columns[symbol] + ['Value', 'Marginal', 'Lower', 'Upper', 'Scale']
except KeyError:
# If no standard format exists, just use columns from GAMS
df.columns = db[symbol].domains_as_strings + ['Value', 'Marginal', 'Lower', 'Upper', 'Scale']
else:
df.columns = cols

return df

def create_set_columns(df: pd.DataFrame,
db: gams.GamsDatabase,
symbol: str,
Expand Down Expand Up @@ -77,8 +96,12 @@ def symbol_to_df(db: gams.GamsDatabase, symbol: str,
elif type(db[symbol]) == gams.GamsSet:
df = pd.DataFrame([tuple(rec.keys) for rec in db[symbol] ])
df = create_set_columns(df, db, symbol, preformatted_columns[result_type.lower()], cols)
elif type(db[symbol]) == gams.GamsVariable or type(db[symbol]) == gams.GamsEquation:
df = dict( (tuple(rec.keys), rec.level) for rec in db[symbol] )
df = pd.DataFrame(df, index=['Value', 'Marginal', 'Lower', 'Upper', 'Scale']).T.reset_index() # Convert to dataframe
df = create_variable_columns(df, db, symbol, preformatted_columns[result_type.lower()], cols)
else:
print('Variables or equations not supported yet')
raise TypeError('%s is not supported by symbol_to_df'%(str(type(db[symbol]))))

if print_explanatory_text:
print(db[symbol].text)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_postprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ def test_MainResults():
assert 'electricity_profile.png' in os.listdir('tests/output') and 'heat_profile.png' in os.listdir('tests/output') and 'hydrogen_profile.png' in os.listdir('tests/output')

# Test map
fig, ax = res.plot_map('SC2', 'Electricity', 2050)
fig, ax = res.plot_map('SC2', 'elecTriciTY', 2050)
fig.savefig('tests/output/electricity_map.png')
fig, ax = res.plot_map('SC2', 'Hydrogen', 2050)
fig, ax = res.plot_map('SC2', 'HYDROGEN', 2050)
fig.savefig('tests/output/hydrogen_map.png')
assert 'electricity_map.png' in os.listdir('tests/output') and 'hydrogen_map.png' in os.listdir('tests/output')

0 comments on commit 39c7936

Please sign in to comment.