Skip to content

Commit

Permalink
Merge pull request #181 from lodersky/optional-name-plot-report
Browse files Browse the repository at this point in the history
Functionality for optional name for plot or report added (fix #180)
  • Loading branch information
KSchoenleber authored Mar 7, 2018
2 parents f85ea2f + 0b3a23e commit 609b24d
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 60 deletions.
73 changes: 54 additions & 19 deletions doc/tutorial.rst
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,23 @@ The :func:`urbs.report` function creates a spreadsheet from the main results.
Summaries of costs, emissions, capacities (process, transmissions, storage) are
saved to one sheet each. For timeseries, each combination of the given
``sites`` and ``commodities`` are summarised both in sum (in sheet "Energy
sums") and as individual timeseries (in sheet "... timeseries"). See also
:ref:`report-function` for a detailed explanation of this function's implementation.
sums") and as individual timeseries (in sheet "... timeseries").
::
# detailed reporting commodity/sites
report_tuples = [
('North', 'Elec'), ('Mid', 'Elec'), ('South', 'Elec'),
('North', 'CO2'), ('Mid', 'CO2'), ('South', 'CO2')]
# optional: define names for sites in report_tuples
report_sites_name = {
('North') : 'Greenland',
('North', 'South') : 'Not Mid'}

Optional it is possible to define ``report_tuples`` to control what shell be reported. And
with ``report_sites_name`` you can define, if the sites inside the report tuples should
be named differently. If they are empty, the default value will be taken.
See also :ref:`report-function` for a detailed explanation of this function's implementation.

Plotting
^^^^^^^^
Expand All @@ -297,30 +312,50 @@ colors is demonstrated. All plot colors are user-defineable by adding color
:func:`tuple` ``(r, g, b)`` or modifying existing tuples for commodities and
plot decoration elements. Here, new colors for displaying import/export are
added. Without these, pseudo-random colors are generated in
:func:`to_color`.::
:func:`to_color`.
::

# create timeseries plot for each demand (site, commodity) timeseries
for sit, com in prob.demand.columns:
# create figure
fig = urbs.plot(prob, com, sit)
# change the figure title
ax0 = fig.get_axes()[0]
nice_sce_name = sce.replace('_', ' ').title()
new_figure_title = ax0.get_title().replace(
'Energy balance of ', '{}: '.format(nice_sce_name))
ax0.set_title(new_figure_title)
# save plot to files
for ext in ['png', 'pdf']:
fig_filename = os.path.join(
result_dir, '{}-{}-{}-{}.{}').format(sce, com, sit, now, ext)
fig.savefig(fig_filename, bbox_inches='tight')

Finally, for each demand commodity (only ``Elec`` in this case), a plot over
the whole optimisation period is created. If ``timesteps`` were longer, a
shorter plotting period could be defined and given as an optional argument to
:func:`plot`.
::

# plotting commodities/sites
plot_tuples = [
('North', 'Elec'),
('Mid', 'Elec'),
('South', 'Elec'),
(['North', 'Mid', 'South'], 'Elec')]

# optional: define names for plot_tuples
plot_sites_name = {
('North', 'Mid', 'South') : 'NMS plot'}
As in the reporting section mentioned, also for plotting the output of plots
can optionally changed with ``plot_tuples``. The sites in title and name of
each plot can be adapted with ``plot_sites_name``.
::

# change the figure title
# if no custom title prefix is specified, use the figure_basename
ax0 = fig.get_axes()[0]
if not plot_title_prefix:
plot_title_prefix = os.path.basename(figure_basename)
new_figure_title = '{}: {} in {}'.format(
plot_title_prefix, com, plot_sites_name[sit])
ax0.set_title(new_figure_title)

# save plot to files
for ext in extensions:
fig_filename = '{}-{}-{}-{}.{}'.format(
figure_basename, com, ''.join(
plot_sites_name[sit]), period, ext)
fig.savefig(fig_filename, bbox_inches='tight')
plt.close(fig)

The paragraph "change figure title" shows exemplarily how to use matplotlib
manually to modify some aspects of a plot without having to recreate the
Expand Down
21 changes: 17 additions & 4 deletions runme.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,8 @@ def setup_solver(optim, logfile='solver.log'):


def run_scenario(input_file, timesteps, scenario, result_dir,
plot_tuples=None, plot_periods=None, report_tuples=None):
plot_tuples=None, plot_sites_name=None, plot_periods=None,
report_tuples=None, report_sites_name=None):
""" run an urbs model for given input, time steps and scenario
Args:
Expand All @@ -100,8 +101,10 @@ def run_scenario(input_file, timesteps, scenario, result_dir,
scenario: a scenario function that modifies the input data dict
result_dir: directory name for result spreadsheet and plots
plot_tuples: (optional) list of plot tuples (c.f. urbs.result_figures)
plot_periods: (optional) dict of plot periods (c.f. urbs.result_figures)
plot_sites_name: (optional) dict of names for sites in plot_tuples
plot_periods: (optional) dict of plot periods(c.f. urbs.result_figures)
report_tuples: (optional) list of (sit, com) tuples (c.f. urbs.report)
report_sites_name: (optional) dict of names for sites in report_tuples
Returns:
the urbs model instance
Expand Down Expand Up @@ -132,14 +135,16 @@ def run_scenario(input_file, timesteps, scenario, result_dir,
urbs.report(
prob,
os.path.join(result_dir, '{}.xlsx').format(sce),
report_tuples=report_tuples)
report_tuples=report_tuples,
report_sites_name=report_sites_name)

# result plots
urbs.result_figures(
prob,
os.path.join(result_dir, '{}'.format(sce)),
plot_title_prefix=sce.replace('_', ' '),
plot_tuples=plot_tuples,
plot_sites_name=plot_sites_name,
periods=plot_periods,
figure_size=(24, 9))
return prob
Expand All @@ -165,11 +170,17 @@ def run_scenario(input_file, timesteps, scenario, result_dir,
('South', 'Elec'),
(['North', 'Mid', 'South'], 'Elec')]

# optional: define names for sites in plot_tuples
plot_sites_name = {('North', 'Mid', 'South'):'All'}

# detailed reporting commodity/sites
report_tuples = [
('North', 'Elec'), ('Mid', 'Elec'), ('South', 'Elec'),
('North', 'CO2'), ('Mid', 'CO2'), ('South', 'CO2')]

# optional: define names for sites in report_tuples
report_sites_name = {'North': 'Greenland'}

# plotting timesteps
plot_periods = {
'all': timesteps[1:]
Expand All @@ -196,5 +207,7 @@ def run_scenario(input_file, timesteps, scenario, result_dir,
for scenario in scenarios:
prob = run_scenario(input_file, timesteps, scenario, result_dir,
plot_tuples=plot_tuples,
plot_sites_name=plot_sites_name,
plot_periods=plot_periods,
report_tuples=report_tuples)
report_tuples=report_tuples,
report_sites_name=report_sites_name)
26 changes: 19 additions & 7 deletions urbs/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,8 @@ def plot(prob, com, sit, timesteps=None,


def result_figures(prob, figure_basename, plot_title_prefix=None,
plot_tuples=None, periods=None, extensions=None, **kwds):
plot_tuples=None, plot_sites_name=None,
periods=None, extensions=None, **kwds):
"""Create plots for multiple periods and sites and save them to files.
Args:
Expand All @@ -305,6 +306,7 @@ def result_figures(prob, figure_basename, plot_title_prefix=None,
plot_tuples: (optional) list of (sit, com) tuples to plot
sit may be individual site names or lists of sites
default: all demand (sit, com) tuples are plotted
plot_sites_name: (optional) dict of names for created plots
periods: (optional) dict of 'period name': timesteps_list items
default: one period 'all' with all timesteps is assumed
extensions: (optional) list of file extensions for plot images
Expand All @@ -327,25 +329,35 @@ def result_figures(prob, figure_basename, plot_title_prefix=None,
for sit, com in plot_tuples:
# wrap single site name in 1-element list for consistent behaviour
if is_string(sit):
sit = [sit]
help_sit = [sit]
else:
help_sit = sit
sit = tuple(sit)

try:
plot_sites_name[sit]
except:
plot_sites_name[sit] = str(sit)

for period, timesteps in periods.items():
# do the plotting
fig = plot(prob, com, sit, timesteps=timesteps, **kwds)
fig = plot(prob, com, help_sit, timesteps=timesteps, **kwds)

# change the figure title
ax0 = fig.get_axes()[0]
# if no custom title prefix is specified, use the figure basenmae
# if no custom title prefix is specified, use the figure_basename
if not plot_title_prefix:
plot_title_prefix = os.path.basename(figure_basename)
new_figure_title = ax0.get_title().replace(
'Commodity balance of ', '{}: '.format(plot_title_prefix))

new_figure_title = '{}: {} in {}'.format(
plot_title_prefix, com, plot_sites_name[sit])
ax0.set_title(new_figure_title)

# save plot to files
for ext in extensions:
fig_filename = '{}-{}-{}-{}.{}'.format(
figure_basename, com, '-'.join(sit), period, ext)
figure_basename, com, ''.join(
plot_sites_name[sit]), period, ext)
fig.savefig(fig_filename, bbox_inches='tight')
plt.close(fig)

Expand Down
94 changes: 64 additions & 30 deletions urbs/report.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import pandas as pd
from .input import get_input
from .output import get_constants, get_timeseries
from .util import is_string


def report(instance, filename, report_tuples=None):
def report(instance, filename, report_tuples=None, report_sites_name=None):
"""Write result summary to a spreadsheet file
Args:
instance: a urbs model instance
filename: Excel spreadsheet filename, will be overwritten if exists
report_tuples: (optional) list of (sit, com) tuples for which to
create detailed timeseries sheets
report_sites_name: (optional) dict of names for created timeseries
sheets
Returns:
Nothing
"""
Expand All @@ -33,35 +35,63 @@ def report(instance, filename, report_tuples=None):
# initialize timeseries tableaus
energies = []
timeseries = {}
help_ts = {}

# collect timeseries data
for sit, com in report_tuples:
(created, consumed, stored, imported, exported,
dsm) = get_timeseries(instance, com, sit)

overprod = pd.DataFrame(
columns=['Overproduction'],
data=created.sum(axis=1) - consumed.sum(axis=1) +
imported.sum(axis=1) - exported.sum(axis=1) +
stored['Retrieved'] - stored['Stored'])

tableau = pd.concat(
[created, consumed, stored, imported, exported, overprod,
dsm],
axis=1,
keys=['Created', 'Consumed', 'Storage', 'Import from',
'Export to', 'Balance', 'DSM'])
timeseries[(sit, com)] = tableau.copy()

# timeseries sums
sums = pd.concat([created.sum(), consumed.sum(),
stored.sum().drop('Level'),
imported.sum(), exported.sum(), overprod.sum(),
dsm.sum()],
axis=0,
keys=['Created', 'Consumed', 'Storage', 'Import',
'Export', 'Balance', 'DSM'])
energies.append(sums.to_frame("{}.{}".format(sit, com)))

# wrap single site name in 1-element list for consistent behavior
if is_string(sit):
help_sit = [sit]
else:
help_sit = sit
sit = tuple(sit)

# check existence of predefined names, else define them
try:
report_sites_name[sit]
except:
report_sites_name[sit] = str(sit)

for lv in help_sit:
(created, consumed, stored, imported, exported,
dsm) = get_timeseries(instance, com, lv)

overprod = pd.DataFrame(
columns=['Overproduction'],
data=created.sum(axis=1) - consumed.sum(axis=1) +
imported.sum(axis=1) - exported.sum(axis=1) +
stored['Retrieved'] - stored['Stored'])

tableau = pd.concat(
[created, consumed, stored, imported, exported, overprod,
dsm],
axis=1,
keys=['Created', 'Consumed', 'Storage', 'Import from',
'Export to', 'Balance', 'DSM'])
help_ts[(lv, com)] = tableau.copy()

# timeseries sums
help_sums = pd.concat([created.sum(), consumed.sum(),
stored.sum().drop('Level'),
imported.sum(), exported.sum(),
overprod.sum(), dsm.sum()],
axis=0,
keys=['Created', 'Consumed', 'Storage',
'Import', 'Export', 'Balance',
'DSM'])
try:
timeseries[(report_sites_name[sit], com)] = timeseries[
(report_sites_name[sit], com)].add(
help_ts[(lv, com)], axis=1, fill_value=0)
sums = sums.add(help_sums, fill_value=0)
except:
timeseries[(report_sites_name[sit], com)] = help_ts[
(lv, com)]
sums = help_sums

energies.append(sums.to_frame("{}.{}".format(
report_sites_name[sit], com)))

# write timeseries data (if any)
if timeseries:
Expand All @@ -71,6 +101,10 @@ def report(instance, filename, report_tuples=None):

# write timeseries to individual sheets
for sit, com in report_tuples:
if isinstance(sit, list):
sit = tuple(sit)
# sheet names cannot be longer than 31 characters...
sheet_name = "{}.{} timeseries".format(sit, com)[:31]
timeseries[(sit, com)].to_excel(writer, sheet_name)
sheet_name = "{}.{} timeseries".format(
report_sites_name[sit], com)[:31]
timeseries[(report_sites_name[sit], com)].to_excel(
writer, sheet_name)

0 comments on commit 609b24d

Please sign in to comment.