Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/p2h'
Browse files Browse the repository at this point in the history
  • Loading branch information
squoilin committed Feb 4, 2020
2 parents 4b6fc93 + 54dcc45 commit a83cd2b
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 42 deletions.
40 changes: 28 additions & 12 deletions dispaset/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,44 @@
# DispaSET fuels:
commons['Fuels'] = ['BIO', 'GAS', 'HRD', 'LIG', 'NUC', 'OIL', 'PEA', 'SUN', 'WAT', 'WIN', 'WST', 'OTH', 'GEO']
# Ordered list of fuels for plotting (the first ones are negative):
commons['MeritOrder'] = ['Storage','FlowOut','GEO','NUC', 'LIG', 'HRD', 'BIO', 'GAS', 'OIL', 'PEA', 'WST', 'OTH', 'SUN', 'WIN', 'FlowIn', 'WAT']
# commons['MeritOrder'] = ['Storage','FlowOut','GEO','NUC', 'LIG', 'HRD', 'BIO', 'GAS', 'OIL', 'PEA', 'WST', 'OTH', 'SUN', 'WIN', 'FlowIn', 'WAT']
commons['MeritOrder'] = ['THMS','BATS','BEVS','HDAM','HPHS','FlowOut','GEO','NUC', 'LIG', 'HRD', 'BIO', 'GAS', 'OIL', 'PEA', 'WST', 'OTH', 'SUN', 'WIN', 'FlowIn', 'WAT']

# Colors associated with each fuel:
commons['colors'] = {'LIG': '#af4b9180', 'PEA': '#af4b9199', 'HRD': '#af4b91b2', 'OIL': '#af4b91ff',
# commons['colors'] = {'LIG': '#af4b9180', 'PEA': '#af4b9199', 'HRD': '#af4b91b2', 'OIL': '#af4b91ff',
# # 'GAS': '#d7642dff',
# # 'NUC': '#466eb4ff',
# # 'SUN': '#e6a532ff',
# # 'WIN': '#41afaaff',
# # 'WAT': '#00a0e1ff',
# # 'BIO': '#7daf4bff', 'GEO': '#7daf4bbf',
# # 'Storage': '#b93c46ff', 'FlowIn': '#b93c46b2', 'FlowOut': '#b93c4666',
# # 'OTH': '#b9c33799', 'WST': '#b9c337ff',
# # 'HDAM': '#00a0e1ff',
# # 'HPHS': '#3090C7ff',
# # 'THMS': '#C04000ff',
# # 'BATS': '#41A317ff',
# # 'BEVS': '#CC80FFff'}
commons['colors'] = {'LIG': '#af4b9180', 'PEA': '#af4b9199', 'HRD': 'darkviolet', 'OIL': 'magenta',
'GAS': '#d7642dff',
'NUC': '#466eb4ff',
'SUN': '#e6a532ff',
'WIN': '#41afaaff',
'WAT': '#00a0e1ff',
'BIO': '#7daf4bff', 'GEO': '#7daf4bbf',
'Storage': '#b93c46ff', 'FlowIn': '#b93c46b2', 'FlowOut': '#b93c4666',
'OTH': '#b9c33799', 'WST': '#b9c337ff'}
'OTH': '#b9c33799', 'WST': '#b9c337ff',
'HDAM': '#00a0e1ff',
'HPHS': 'blue',
'THMS': '#C04000ff',
'BATS': '#41A317ff',
'BEVS': '#b9c33799'}

commons['colors']['curtailment'] = 'red'
# Hatches associated with each fuel:
commons['hatches'] = {'LIG': '', 'PEA': '', 'HRD': '', 'OIL': '',
'GAS': '',
'NUC': '',
'SUN': '',
'WIN': '',
'WAT': '',
'BIO': '', 'GEO': '',
'Storage': '', 'FlowIn': '/', 'FlowOut': '\\',
'WST': '', 'OTH': ''
commons['hatches'] = {'LIG': '', 'PEA': '', 'HRD': '', 'OIL': '', 'GAS': '', 'NUC': '', 'SUN': '', 'WIN': '', 'WAT': '',
'BIO': '', 'GEO': '', 'Storage': '', 'WST': '', 'OTH': '',
'FlowIn': '/', 'FlowOut': '\\', 'HDAM': '/','HPHS': '/','THMS': '','BATS': '/','BEVS': '/'
}

commons['logfile'] = str(datetime.datetime.now()).replace(':','-').replace(' ','_') + '.dispa.log'
118 changes: 89 additions & 29 deletions dispaset/postprocessing/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,19 @@
from ..misc.str_handler import clean_strings
from ..common import commons

from .postprocessing import get_imports, get_plot_data, filter_by_zone
from .postprocessing import get_imports, get_plot_data, filter_by_zone, filter_by_tech, filter_by_storage


def plot_dispatch(demand, plotdata, level=None, curtailment=None, rng=None,
def plot_dispatch(demand, plotdata, level=None, curtailment=None, shedload=None, rng=None,
alpha=None, figsize=(13, 6)):
"""
Function that plots the dispatch data and the reservoir level as a cumulative sum
:param demand: Pandas Series with the demand curve
:param plotdata: Pandas Dataframe with the data to be plotted. Negative columns should be at the beginning. Output of the function GetPlotData
:param level: Optional pandas series with an aggregated reservoir level for the considered zone.
:param level: Optional pandas series/dataframe with an (dis)aggregated reservoir level for the considered zone.
:param curtailment: Optional pandas series with the value of the curtailment
:param shedload: Optional pandas series with the value of the shed load
:param rng: Indexes of the values to be plotted. If undefined, the first week is plotted
"""
import matplotlib.patches as mpatches
Expand All @@ -34,7 +35,7 @@ def plot_dispatch(demand, plotdata, level=None, curtailment=None, rng=None,
raise ValueError()
elif rng[0] < plotdata.index[0] or rng[0] > plotdata.index[-1] or rng[-1] < plotdata.index[0] or rng[-1] > \
plotdata.index[-1]:
logging.warn('Plotting range is not properly defined, considering the first simulated week')
logging.warning('Plotting range is not properly defined, considering the first simulated week')
pdrng = plotdata.index[:min(len(plotdata) - 1, 7 * 24)]
else:
pdrng = rng
Expand Down Expand Up @@ -74,22 +75,50 @@ def plot_dispatch(demand, plotdata, level=None, curtailment=None, rng=None,

fig.suptitle('Power dispatch for zone ' + demand.name[1])

# Define labels, patches and colors
labels = []
patches = []
colorlist = []

# # Plot negative values:
# Plot reservoir levels (either separated or as one value)
if level is not None:
if isinstance(level, pd.DataFrame):
cols_lvl = level.columns.tolist()
sumplot_lev = level[cols_lvl[0:]].cumsum(axis=1)
sumplot_lev['zero'] = 0
sumplot_lev = sumplot_lev[['zero'] + sumplot_lev.columns[:-1].tolist()]
print('Its dataframe')
for j in range(len(sumplot_lev.columns) - 1):
col3 = sumplot_lev.columns[j]
col4 = sumplot_lev.columns[j + 1]
rez_color = commons['colors'][col4]
rez_hatch = commons['hatches'][col4]
axes[1].fill_between(pdrng, sumplot_lev.loc[pdrng, col3], sumplot_lev.loc[pdrng, col4],
facecolor=rez_color, alpha=alpha, hatch=rez_hatch)
labels.append(col4)
patches.append(mpatches.Patch(facecolor=rez_color, alpha=alpha, hatch=rez_hatch, label=col4))
colorlist.append(rez_color)
elif isinstance(level, pd.Series):
# Create right axis:
axes[1].plot(pdrng, level[pdrng], color='k', alpha=alpha, linestyle=':')
axes[1].fill_between(pdrng, 0, level[pdrng],
facecolor=commons['colors']['WAT'], alpha=.3)
axes[1].set_ylabel('Level [TWh]')
axes[1].yaxis.label.set_fontsize(12)
line_SOC = mlines.Line2D([], [], color='black', alpha=alpha, label='Reservoir', linestyle=':')

# Plot negative values:
for j in range(idx_zero):
col1 = sumplot_neg.columns[j]
col2 = sumplot_neg.columns[j + 1]
color = commons['colors'][col2]
hatch = commons['hatches'][col2]
axes[0].fill_between(pdrng, sumplot_neg.loc[pdrng, col1], sumplot_neg.loc[pdrng, col2], facecolor=color,
alpha=alpha,
hatch=hatch)
labels.append(col1)
patches.append(mpatches.Patch(facecolor=color, alpha=alpha, hatch=hatch, label=col2))
colorlist.append(color)
alpha=alpha, hatch=hatch)
if col2 not in labels:
labels.append(col1)
patches.append(mpatches.Patch(facecolor=color, alpha=alpha, hatch=hatch, label=col2))
colorlist.append(color)

# Plot Positive values:
for j in range(len(sumplot_pos.columns) - 1):
Expand Down Expand Up @@ -117,22 +146,32 @@ def plot_dispatch(demand, plotdata, level=None, curtailment=None, rng=None,
axes[0].set_ylabel('Power [GW]')
axes[0].yaxis.label.set_fontsize(12)

if level is not None:
# Create right axis:
axes[1].plot(pdrng, level[pdrng], color='k', alpha=alpha, linestyle=':')
axes[1].fill_between(pdrng, 0, level[pdrng],
facecolor=commons['colors']['WAT'], alpha=.3)

axes[1].set_ylabel('Level [TWh]')
axes[1].yaxis.label.set_fontsize(12)
line_SOC = mlines.Line2D([], [], color='black', alpha=alpha, label='Reservoir', linestyle=':')
if isinstance(shedload, pd.Series):
if not shedload.index.equals(demand.index):
logging.error('The shedload time series must have the same index as the demand')
sys.exit(1)
reduced_demand = demand - shedload
axes[0].plot(pdrng, reduced_demand[pdrng], color='k', alpha=alpha, linestyle='dashed')
line_shedload = mlines.Line2D([], [], color='black', alpha=alpha, label='Shed Load', linestyle='dashed')

line_demand = mlines.Line2D([], [], color='black', label='Load')
plt.legend(handles=[line_demand] + patches[::-1], loc=4)
if level is None:
plt.legend(handles=[line_demand] + patches[::-1], loc=4)
# plt.legend(handles=[line_demand] + patches[::-1], loc=4)

if shedload is None and level is None:
plt.legend(handles=[line_demand] + patches[::-1], loc=4, bbox_to_anchor=(1.2, 0.5))
if shedload is None:
plt.legend(handles=[line_demand] + [line_SOC] + patches[::-1], loc=4, bbox_to_anchor=(1.2, 0.5))
elif level is None:
plt.legend(handles=[line_demand] + [line_shedload] + patches[::-1], loc=4, bbox_to_anchor=(1.2, 0.5))
axes[0].fill_between(demand.index, demand, reduced_demand, facecolor="none", hatch="X", edgecolor="k",
linestyle='dashed')
else:
plt.legend(title='Dispatch for ' + demand.name[1], handles=[line_demand] + [line_SOC] + patches[::-1], loc=4)
plt.legend(title='Dispatch for ' + demand.name[1], handles=[line_demand] + [line_shedload] + [line_SOC] +
patches[::-1], loc=4, bbox_to_anchor=(1.2, 0.5))
axes[0].fill_between(demand.index, demand, reduced_demand, facecolor="none", hatch="X", edgecolor="k",
linestyle='dashed')

plt.subplots_adjust(right=0.8)
plt.show()


Expand Down Expand Up @@ -249,6 +288,7 @@ def plot_energy_zone_fuel(inputs, results, PPindicators):
demand = inputs['param_df']['Demand']['DA'].sum() / 1E6
ax.barh(demand, left=ax.get_xticks() - 0.4, width=[0.8] * len(demand), height=ax.get_ylim()[1] * 0.005, linewidth=2,
color='k')
plt.show()
return ax


Expand All @@ -274,7 +314,7 @@ def plot_zone_capacities(inputs, plot=True):
PowerCapacity = PowerCapacity[cols]
if plot:
colors = [commons['colors'][tech] for tech in PowerCapacity.columns]
ax = PowerCapacity.plot(kind="bar", figsize=(12, 8), stacked=True, color=colors, alpha=1.0, legend='reverse',
ax = PowerCapacity.plot(kind="bar", figsize=(12, 8), stacked=True, color=colors, alpha=0.8, legend='reverse',
title='Installed capacity per zone (the horizontal lines indicate the peak demand)')
ax.set_ylabel('Capacity [MW]')
demand = inputs['param_df']['Demand']['DA'].max()
Expand Down Expand Up @@ -304,9 +344,22 @@ def plot_zone(inputs, results, z='', rng=None, rug_plot=True):

plotdata = get_plot_data(inputs, results, z) / 1000 # GW

aggregation = False
if 'OutputStorageLevel' in results:
level = filter_by_zone(results['OutputStorageLevel'], inputs, z) / 1E6 # TWh
level = level.sum(axis=1)
lev = filter_by_zone(results['OutputStorageLevel'], inputs, z) / 1E6 # TWh
level = filter_by_storage(lev, inputs, StorageSubset='s')
levels = pd.DataFrame(index=results['OutputStorageLevel'].index,columns=inputs['sets']['t'])
for t in ['HDAM','HPHS','BEVS','BATS']:
temp = filter_by_tech(level,inputs,t)
levels[t] = temp.sum(axis=1)
levels.dropna(axis=1,inplace=True)
for col in levels.columns:
if levels[col].max() == 0 and levels[col].min() == 0:
del levels[col]
if aggregation is True:
level = level.sum(axis=1)
else:
level = levels
else:
level = pd.Series(0, index=results['OutputPower'].index)

Expand All @@ -324,18 +377,25 @@ def plot_zone(inputs, results, z='', rng=None, rug_plot=True):
# if 'OutputShedLoad' in results:
if 'OutputShedLoad' in results and z in results['OutputShedLoad']:
shed_load = results['OutputShedLoad'][z] / 1000 # GW
shed_load = pd.Series(shed_load, index=demand.index).fillna(0)
else:
shed_load = pd.Series(0, index=demand.index) / 1000 # GW
diff = (sum_generation - demand + shed_load).abs()
if diff.max() > 0.01 * demand.max():
logging.critical('There is up to ' + str(
diff.max() / demand.max() * 100) + '% difference in the instantaneous energy balance of zone ' + z)

if 'OutputCurtailedPower' in results and z in results['OutputCurtailedPower']:
if 'OutputCurtailedPower' in results and z in results['OutputCurtailedPower'] \
and 'OutputShedLoad' in results and z in results['OutputShedLoad']:
curtailment = results['OutputCurtailedPower'][z] / 1000 # GW
plot_dispatch(demand, plotdata, level, curtailment=curtailment, shedload = shed_load, rng=rng, alpha=0.8)
elif 'OutputCurtailedPower' in results and z in results['OutputCurtailedPower']:
curtailment = results['OutputCurtailedPower'][z] / 1000 # GW
plot_dispatch(demand, plotdata, level, curtailment=curtailment, rng=rng)
plot_dispatch(demand, plotdata, level, curtailment=curtailment, rng=rng, alpha=0.8)
elif 'OutputShedLoad' in results and z in results['OutputShedLoad']:
plot_dispatch(demand, plotdata, level, shedload = shed_load, rng=rng, alpha=0.8)
else:
plot_dispatch(demand, plotdata, level, rng=rng)
plot_dispatch(demand, plotdata, level, rng=rng, alpha=0.8)

# Generation plot:
if rug_plot:
Expand Down
38 changes: 37 additions & 1 deletion dispaset/postprocessing/postprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,34 @@ def filter_by_zone(PowerOutput, inputs, z):
return Power


def filter_by_tech(PowerOutput, inputs, t):
"""
This function filters the dispaset power output dataframe by technology
:param PowerOutput: Dataframe of power generation with units as columns and time as index
:param inputs: Dispaset inputs version 2.1.1
:param t: Selected tech (e.g. 'HDAM')
:returns Power:
"""
loc = inputs['units']['Technology']
Power = PowerOutput.loc[:, [u for u in PowerOutput.columns if loc[u] == t]]
return Power


def filter_by_storage(PowerOutput, Inputs, StorageSubset=None):
"""
This function filters the power generation curves of the different storage units by storage type
:param PowerOutput: Dataframe of power generationwith units as columns and time as index
:param Inputs: Dispaset inputs version 2.1.1
:param SpecifySubset: If not all EES storages should be considered, list containing the relevant ones
:returns PowerByFuel: Dataframe with power generation by fuel
"""
storages = Inputs['sets'][StorageSubset]
Power = PowerOutput.loc[:, PowerOutput.columns.isin(storages)]
return Power


def get_plot_data(inputs, results, z):
"""
Function that reads the results dataframe of a DispaSET simulation and extract the dispatch data spedific to one zone
Expand All @@ -112,7 +140,15 @@ def get_plot_data(inputs, results, z):
#onnly take the columns that correspond to storage units (StorageInput is also used for CHP plants):
cols = [col for col in results['OutputStorageInput'] if inputs['units'].loc[col,'Technology'] in commons['tech_storage']]
tmp = filter_by_zone(results['OutputStorageInput'][cols], inputs, z)
plotdata['Storage'] = -tmp.sum(axis=1)
bb = pd.DataFrame()
for tech in commons['tech_storage']:
aa = filter_by_tech(tmp, inputs, tech)
aa = aa.sum(axis=1)
aa = pd.DataFrame(aa,columns=[tech])
bb = pd.concat([bb,aa],axis=1)
bb = -bb
plotdata = pd.concat([plotdata,bb], axis=1)
# plotdata['Storage'] = -tmp.sum(axis=1)
else:
plotdata['Storage'] = 0
plotdata.fillna(value=0, inplace=True)
Expand Down

0 comments on commit a83cd2b

Please sign in to comment.