Skip to content

Commit

Permalink
Merge pull request #118 from ojdo/split-urbs-into-subfiles
Browse files Browse the repository at this point in the history
Split urbs.py into 10 files. Kind of closes #2.
  • Loading branch information
ojdo authored Jan 12, 2017
2 parents 78bb0d1 + 91dd7c6 commit aec3631
Show file tree
Hide file tree
Showing 12 changed files with 2,618 additions and 2,605 deletions.
2,605 changes: 0 additions & 2,605 deletions urbs.py

This file was deleted.

20 changes: 20 additions & 0 deletions urbs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"""urbs: A linear optimisation model for distributed energy systems
urbs minimises total cost for providing energy in form of desired commodities
(usually electricity) to satisfy a given demand in form of timeseries. The
model contains commodities (electricity, fossil fuels, renewable energy
sources, greenhouse gases), processes that convert one commodity to another
(while emitting greenhouse gases as a secondary output), transmission for
transporting commodities between sites and storage for saving/retrieving
commodities.
"""

from .data import COLORS
from .model import create_model
from .input import read_excel, get_input
from .output import get_constants, get_timeseries
from .plot import plot, result_figures, to_color
from .pyomoio import get_entity, get_entities, list_entities
from .report import report
from .saveload import load, save
30 changes: 30 additions & 0 deletions urbs/data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
COLORS = {
# process types
'Biomass plant': (0, 122, 55),
'Coal plant': (100, 100, 100),
'Gas plant': (237, 227, 0),
'Gud plant': (153, 153, 0),
'Hydro plant': (198, 188, 240),
'Lignite plant': (116, 66, 65),
'Photovoltaics': (243, 174, 0),
'Slack powerplant': (163, 74, 130),
'Wind park': (122, 179, 225),
# hard-coded strings
'Decoration': (128, 128, 128), # plot labels
'Unshifted': (130, 130, 130), # unshifted demand line
'Shifted': (25, 25, 25), # shifted demand line
'Delta': (130, 130, 130), # demand delta
'Grid': (128, 128, 128), # background grid
'Overproduction': (190, 0, 99), # excess power (diagnostic)
'Storage': (60, 36, 154), # storage area
'Stock': (222, 222, 222), # stock commodity creation
# cost types
'Environmental': (180, 50, 15),
'Feed-in': (255, 204, 153),
'Fixed': (128, 128, 128),
'Fuel': (218, 215, 203),
'Invest': (0, 101, 189),
'Revenue': (62, 173, 0),
'Purchase': (0, 51, 89),
'Startup': (105, 8, 90),
'Variable': (128, 153, 172)}
128 changes: 128 additions & 0 deletions urbs/input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import pandas as pd
from xlrd import XLRDError


def read_excel(filename):
"""Read Excel input file and prepare URBS input dict.
Reads an Excel spreadsheet that adheres to the structure shown in
mimo-example.xlsx. Two preprocessing steps happen here:
1. Column titles in 'Demand' and 'SupIm' are split, so that
'Site.Commodity' becomes the MultiIndex column ('Site', 'Commodity').
2. The attribute 'annuity-factor' is derived here from the columns 'wacc'
and 'depreciation' for 'Process', 'Transmission' and 'Storage'.
Args:
filename: filename to an Excel spreadsheet with the required sheets
'Commodity', 'Process', 'Transmission', 'Storage', 'Demand' and
'SupIm'.
Returns:
a dict of 6 DataFrames
Example:
>>> data = read_excel('mimo-example.xlsx')
>>> data['hacks'].loc['Global CO2 limit', 'Value']
150000000
"""
with pd.ExcelFile(filename) as xls:
site = xls.parse('Site').set_index(['Name'])
commodity = (
xls.parse('Commodity').set_index(['Site', 'Commodity', 'Type']))
process = xls.parse('Process').set_index(['Site', 'Process'])
process_commodity = (
xls.parse('Process-Commodity')
.set_index(['Process', 'Commodity', 'Direction']))
transmission = (
xls.parse('Transmission')
.set_index(['Site In', 'Site Out',
'Transmission', 'Commodity']))
storage = (
xls.parse('Storage').set_index(['Site', 'Storage', 'Commodity']))
demand = xls.parse('Demand').set_index(['t'])
supim = xls.parse('SupIm').set_index(['t'])
buy_sell_price = xls.parse('Buy-Sell-Price').set_index(['t'])
dsm = xls.parse('DSM').set_index(['Site', 'Commodity'])
try:
hacks = xls.parse('Hacks').set_index(['Name'])
except XLRDError:
hacks = None

# prepare input data
# split columns by dots '.', so that 'DE.Elec' becomes the two-level
# column index ('DE', 'Elec')
demand.columns = split_columns(demand.columns, '.')
supim.columns = split_columns(supim.columns, '.')
buy_sell_price.columns = split_columns(buy_sell_price.columns, '.')

data = {
'site': site,
'commodity': commodity,
'process': process,
'process_commodity': process_commodity,
'transmission': transmission,
'storage': storage,
'demand': demand,
'supim': supim,
'buy_sell_price': buy_sell_price,
'dsm': dsm}
if hacks is not None:
data['hacks'] = hacks

# sort nested indexes to make direct assignments work
for key in data:
if isinstance(data[key].index, pd.core.index.MultiIndex):
data[key].sortlevel(inplace=True)
return data


def split_columns(columns, sep='.'):
"""Split columns by separator into MultiIndex.
Given a list of column labels containing a separator string (default: '.'),
derive a MulitIndex that is split at the separator string.
Args:
columns: list of column labels, containing the separator string
sep: the separator string (default: '.')
Returns:
a MultiIndex corresponding to input, with levels split at separator
Example:
>>> split_columns(['DE.Elec', 'MA.Elec', 'NO.Wind'])
MultiIndex(levels=[['DE', 'MA', 'NO'], ['Elec', 'Wind']],
labels=[[0, 1, 2], [0, 0, 1]])
"""
if len(columns) == 0:
return columns
column_tuples = [tuple(col.split('.')) for col in columns]
return pd.MultiIndex.from_tuples(column_tuples)


def get_input(prob, name):
"""Return input DataFrame of given name from urbs instance.
These are identical to the key names returned by function `read_excel`.
That means they are lower-case names and use underscores for word
separation, e.g. 'process_commodity'.
Args:
prob: a urbs model instance
name: an input DataFrame name ('commodity', 'process', ...)
Returns:
the corresponding input DataFrame
"""
if hasattr(prob, name):
# classic case: input data DataFrames are accessible via named
# attributes, e.g. `prob.process`.
return getattr(prob, name)
elif hasattr(prob, '_data') and name in prob._data:
# load case: input data is accessible via the input data cache dict
return prob._data[name]
else:
# unknown
raise ValueError("Unknown input DataFrame name!")
Loading

0 comments on commit aec3631

Please sign in to comment.