Commit aff607cc authored by Sebastian Schwarz's avatar Sebastian Schwarz
Browse files

Preparations for new examples. The flexibility quantification example is already final.

parent cafa3e97
Pipeline #313954 passed with stages
in 1 minute and 28 seconds
# ToDo: Show the fundamentals of pyCity_scheduling. Adapt from pyCity_base.
# ToDo: Perform a very simple scheduling using the central optimization algorithm.
# ToDo: Perform a very simple scheduling using the local optimization algorithm.
# ToDo: Perform a very simple scheduling using the stand-alone optimization algorithm.
......@@ -50,8 +50,8 @@ bes.addDevice(chp)
ap = Apartment(e)
bd2.addEntity(ap)
load = np.array([20, 20])
dl = DeferrableLoad(e, 3.0, 1.5)
ap.addEntity(dl)
#dl = DeferrableLoad(e, 3.0, 1.5)
#ap.addEntity(dl)
cl = CurtailableLoad(e, 1.5, 0.8)
ap.addEntity(cl)
sh = SpaceHeating(e, method=0, loadcurve=load)
......
# ToDo: Perform a very simple scheduling using the peak-shaving objective (on system level).
# ToDo: Perform a very simple scheduling using the price objective (on system level).
# ToDo: Perform a very simple scheduling using the max-consumption objective (on system level).
# ToDo: Perform a very simple scheduling using the co2 objective (on system level).
# ToDo: Perform a very simple scheduling using the valley-filling objective (on system level).
# ToDo: Perform a very simple scheduling demonstrating the impact from local and system level objectives.
import pycity_scheduling.util.factory as factory
import pycity_scheduling.util.debug as debug
env = factory.generate_standard_environment()
env = factory.generate_standard_environment(initial_date=(2018, 12, 6), step_size=900, op_horizon=96)
# 20 single-family houses
num_sfh = 20
......
......@@ -4,11 +4,10 @@ from matplotlib import gridspec
import numpy as np
import pycity_scheduling.util.factory as factory
import pycity_scheduling.algorithms as algs
from pycity_scheduling.util.metric import absolute_flexibility_gain
from pycity_scheduling.util import calculate_flexibility_potential
# This example is built upon the same district setup as defined in example 'district_generator_01.py'
env = factory.generate_standard_environment()
# This example is built upon the same district setup as defined in example 'example_12_district_generator.py'
env = factory.generate_standard_environment(initial_date=(2018, 12, 6), step_size=900, op_horizon=96)
# 20 single-family houses
num_sfh = 20
......@@ -70,33 +69,9 @@ district = factory.generate_tabula_district(env, num_sfh, num_mfh,
building_objective='none'
)
# Perform the scheduling (in this example desired to quantify the city district's maximum flexibility potential):
algs.local_optimization(district)
district.save_ref_schedule()
district.objective = 'flexibility-quantification'
algs.central_optimization(district)
# Quantify the city district's maximum operational flexibility potential:
flex = calculate_flexibility_potential(city_district=district, algorithm="central", reference_algorithm="stand-alone")
# Print the flexibility metric:
np.set_printoptions(formatter={'float': '{: >8.3f}'.format})
print('Absolute flex. gain (kWh): {: >8.3f}'.format(absolute_flexibility_gain(district, "Ref")))
# Plot
plot_time = list(range(env.timer.timestepsUsedHorizon))
figure = plt.figure(figsize=(6, 6))
gs = gridspec.GridSpec(2, 1)
ax0 = plt.subplot(gs[0])
ax0.plot(plot_time, district.P_El_Ref_Schedule)
plt.grid()
plt.ylabel("City District Reference [kW]")
plt.xlim((0, env.timer.timestepsUsedHorizon - 1))
plt.title("Schedules")
ax1 = plt.subplot(gs[1], sharex=ax0)
ax1.plot(plot_time, district.P_El_Schedule)
plt.grid()
plt.ylabel("City District [kW]")
plt.show()
# ToDo: Wait for stand-alone algorithm fix.
# ToDo: Implement this flexibility quantification method as a separate util function?!
print('Absolute flex. gain (kWh): {: >8.3f}'.format(flex))
......@@ -7,9 +7,9 @@ import pycity_scheduling.util.factory as factory
import pycity_scheduling.algorithms as algs
from pycity_scheduling.classes import *
# This example is built upon the district setup as defined in example 'district_generator_01.py'.
# This example is built upon the district setup as defined in example 'example_12_district_generator.py'.
# However, the city district scenario contains more than 100 buildings and is hence considered complex.
env = factory.generate_standard_environment()
env = factory.generate_standard_environment(initial_date=(2018, 12, 6), step_size=900, op_horizon=96)
# 75 single-family houses
num_sfh = 75
......@@ -95,5 +95,3 @@ plt.ylabel("City District Power [kW]")
plt.xlim((0, env.timer.timestepsUsedHorizon - 1))
plt.title("Complex City District Scenario - Schedule")
plt.show()
# ToDo: Why WEC is not working?!
......@@ -6,29 +6,28 @@ from pycity_scheduling.util import factory
from pycity_scheduling.util.metric import calculate_costs, peak_to_average_ratio, peak_reduction_ratio, \
self_consumption, autarky, absolute_flexibility_gain
env = factory.generate_standard_environment(step_size=900, op_horizon=96)
# Make it attractive for the consumers to shift demand into the second half of the
# scheduling period
env.prices.tou_prices = np.array([20]*48 + [10]*48)
# Use a standard environment of 24 hours with hourly resolution (=60min=3600sec):
env = factory.generate_standard_environment(step_size=3600, op_horizon=24)
# Make it attractive for the consumers to shift demand into the second half of the scheduling period:
env.prices.tou_prices = np.array([20]*12 + [10]*12)
# City district / aggregator objective is peak-shaving
# City district / aggregator objective is set to peak-shaving:
cd = classes.CityDistrict(env, objective='peak-shaving')
# Fixed load of constant 10 kW, space heating with constant 10 kW load, thermal
# storage with 20 kWh and electric heater with 20 kW
# The building gets assigned a price objective automatically
# The sample building in this example comes with a constant electrical and space heating load of 10kW,
# thermal storage of capacity 20kWh and an electric heater of thermal nominal power 20kW:
bd = factory.generate_simple_building(env, fl=10, sh=10, tes=20, eh=20)
cd.addEntity(bd, (0, 0))
# Pseudo scheduling where each device is scheduled on its own
# Perform a 'pseudo' stand-alone scheduling, where each device is scheduled on its own (=no coordination):
algs.stand_alone_optimization(cd)
# Results are now in the _Ref schedules
bd.save_ref_schedule()
cd.save_ref_schedule()
# Normal scheduling with aggregator and customer objectives
# Results are now in the _Ref schedules:
bd.copy_schedule("Ref")
cd.copy_schedule("Ref")
# Now perform a coordinated scheduling with aggregator and customer objectives:
algs.central_optimization(cd)
# Evaluate and print different metrics:
np.set_printoptions(formatter={'float': '{: >8.3f}'.format})
print('Comparing stand-alone with optimized case:')
print('Building P_El:')
......@@ -48,3 +47,8 @@ print('PRR: {: >8.3f}'.format(peak_reduction_ratio(bd, "Ref")
print('Self-consumption ratio: {: >8.3f}'.format(self_consumption(bd)))
print('Autarky ratio: {: >8.3f}'.format(autarky(bd)))
print('Absolute flex. gain: {: >8.3f}'.format(absolute_flexibility_gain(cd, "Ref")))
# Conclusion:
# In contrast to the unoptimized case (stand-alone) both the price as well as
# the peaks in the schedule are reduced in the optimized case. This is due to the
# price and peak-shaving objectives of the building and the city district.
......@@ -5,32 +5,30 @@ import pycity_scheduling.util.factory as factory
from pycity_scheduling.classes import *
from pycity_scheduling.algorithms import *
# ToDo: Fix from Gitlab required. Then adjust and fix example.
# ToDo: Why do electro-thermal heating units not follow the SH Profile without TES?! Fixed!!! :)
# Scheduling will be performed for a typical winter day within the annual heating period:
env = factory.generate_standard_environment(step_size=900, op_horizon=96, mpc_horizon=None, mpc_step_width=None,
env = factory.generate_standard_environment(step_size=3600, op_horizon=24, mpc_horizon=None, mpc_step_width=None,
initial_date=(2010, 2, 10), initial_time=(0, 0, 0))
# City district / aggregator objective is peak-shaving:
cd = CityDistrict(environment=env, objective='peak-shaving')
# Building equipped with space heating, heat pump, photovoltaic unit and battery storage.
# Building equipped with space heating, electrical heater, thermal energy storage, and photovoltaic unit.
# Objective is peak-shaving:
bd1 = Building(environment=env, objective='peak-shaving')
cd.addEntity(entity=bd1, position=[0, 0])
ap1 = Apartment(environment=env)
bd1.addEntity(ap1)
sh1 = SpaceHeating(environment=env, method=1, livingArea=140, specificDemand=85.9, profile_type='HEF')
sh1 = SpaceHeating(environment=env, method=1, livingArea=120.0, specificDemand=85.9, profile_type='HEF')
ap1.addEntity(sh1)
bes1 = BuildingEnergySystem(environment=env)
bd1.addEntity(bes1)
hp1 = HeatPump(environment=env, P_Th_nom=16.0, cop=3.0)
bes1.addDevice(hp1)
pv1 = Photovoltaic(environment=env, area=60.0, eta=0.18)
eh1 = ElectricalHeater(environment=env, P_Th_nom=12.0, lower_activation_limit=0.5)
bes1.addDevice(eh1)
tes1 = ThermalEnergyStorage(environment=env, E_Th_max=24.0)
bes1.addDevice(tes1)
pv1 = Photovoltaic(environment=env, area=100.0, eta=0.18)
bes1.addDevice(pv1)
bat1 = Battery(environment=env, E_El_max=13.5, P_El_max_charge=4.6)
bes1.addDevice(bat1)
# First, perform the scheduling using convex models for the electrical appliances:
......@@ -45,7 +43,7 @@ gs = gridspec.GridSpec(5, 1)
ax0 = plt.subplot(gs[0])
ax0.plot(plot_time, cd.P_El_Schedule)
plt.xlim((0, env.timer.timestepsUsedHorizon - 1))
plt.ylim([-3, 3])
plt.ylim([-15, 15])
plt.title("Convex Schedules")
plt.ylabel("District [kW]")
plt.grid()
......@@ -57,21 +55,21 @@ plt.ylabel("PV [kW]")
plt.grid()
ax2 = plt.subplot(gs[2], sharex=ax0)
ax2.plot(plot_time, hp1.P_Th_Schedule)
ax2.plot(plot_time, sh1.P_Th_Schedule)
plt.xlim((0, env.timer.timestepsUsedHorizon - 1))
plt.ylabel("El. Heater [kW]")
plt.ylabel("Space Heating Demand [kW]")
plt.grid()
ax3 = plt.subplot(gs[3], sharex=ax0)
ax3.plot(plot_time, bat1.P_El_Schedule)
ax3.plot(plot_time, eh1.P_El_Schedule)
plt.xlim((0, env.timer.timestepsUsedHorizon - 1))
plt.ylabel("Bat. Power [kW]")
plt.ylabel("El. Heater [kW]")
plt.grid()
ax4 = plt.subplot(gs[4], sharex=ax0)
ax4.plot(plot_time, bat1.E_El_Schedule)
ax4.plot(plot_time, tes1.E_Th_Schedule)
plt.xlim((0, env.timer.timestepsUsedHorizon - 1))
plt.ylabel("Bat. SoC [kWh]")
plt.ylabel("TES SoC [kWh]")
plt.grid()
plt.xlabel("Time", fontsize=12)
......@@ -89,7 +87,7 @@ gs = gridspec.GridSpec(5, 1)
ax0 = plt.subplot(gs[0])
ax0.plot(plot_time, cd.P_El_Schedule)
plt.xlim((0, env.timer.timestepsUsedHorizon - 1))
plt.ylim([-3, 3])
plt.ylim([-15, 15])
plt.title("Integer Schedules")
plt.ylabel("District [kW]")
plt.grid()
......@@ -101,21 +99,21 @@ plt.ylabel("PV [kW]")
plt.grid()
ax2 = plt.subplot(gs[2], sharex=ax0)
ax2.plot(plot_time, hp1.P_Th_Schedule)
ax2.plot(plot_time, sh1.P_Th_Schedule)
plt.xlim((0, env.timer.timestepsUsedHorizon - 1))
plt.ylabel("El. Heater [kW]")
plt.ylabel("Space Heating Demand [kW]")
plt.grid()
ax3 = plt.subplot(gs[3], sharex=ax0)
ax3.plot(plot_time, bat1.P_El_Schedule)
ax3.plot(plot_time, eh1.P_El_Schedule)
plt.xlim((0, env.timer.timestepsUsedHorizon - 1))
plt.ylabel("Bat. Power [kW]")
plt.ylabel("El. Heater [kW]")
plt.grid()
ax4 = plt.subplot(gs[4], sharex=ax0)
ax4.plot(plot_time, bat1.E_El_Schedule)
ax4.plot(plot_time, tes1.E_Th_Schedule)
plt.xlim((0, env.timer.timestepsUsedHorizon - 1))
plt.ylabel("Bat. SoC [kWh]")
plt.ylabel("TES SoC [kWh]")
plt.grid()
plt.xlabel("Time", fontsize=12)
......
# ToDo: Add a scheduling example of a (big) PV+Battery system and a load (desired as annual simulation).
# ToDo: Add a scheduling example for a building having both heating and cooling loads (desired as annual simulation).
......@@ -8,25 +8,27 @@ from pycity_scheduling.util.metric import calculate_costs
# Use a simple environment of 6 hours with quarter-hourly resolution (=15min=900sec):
env = factory.generate_standard_environment(step_size=900, op_horizon=6)
# Make it attractive to shift demand into the first half of the scheduling period (compare metric_example_01.py):
# Make it attractive for the customer to shift demand into the first half of the scheduling period
# (compare example_15_scheduling_metrics_evaluation.py):
env.prices.tou_prices = np.array([5]*3 + [10]*3)
district = classes.CityDistrict(env)
# The sample building in this example comes with a constant space heating load of 10kW, thermal storage of capacity
# 20 kWh and an electric heater of thermal nominal power 20 kW:
# 20kWh and an electric heater of thermal nominal power 20kW:
bd = factory.generate_simple_building(env, sh=10, tes=20, eh=20)
district.addEntity(bd, (0, 0))
# Perform the Scheduling without RO:
# Perform the scheduling without RO:
central_optimization(district)
bd.save_ref_schedule()
# Protect 6 time steps and assume a maximum deviation of 50% in each time step
# Such a high deviation is unrealistic but makes it a good example.
# Protect 6 time steps and assume a maximum deviation of 50% in each time step.
# Such a high deviation is usually unrealistic, but makes it a nice example here.
central_optimization(district, robustness=(6, 0.5))
# Print schedules/results:
# **Note:** We compare the schedules from the two performed schedulings (with/without RO) with each other.
np.set_printoptions(formatter={'float': '{: >8.3f}'.format})
print('Building P_El:')
print(bd.P_El_Ref_Schedule)
......@@ -48,3 +50,12 @@ bd.load_schedule("Ref")
print('{:.2f}'.format(calculate_costs(bd)))
bd.load_schedule("default")
print('{:.2f}'.format(calculate_costs(bd)))
# Conclusions:
# If robust optimization (RO) is used, the flexibility of the thermal energy storage is not fully used.
# This is best seen in the first four time steps, where the SoC is lower than without the robust scheduling.
# Instead, the 'energy difference' is used to cater uncertainties that may stem from an uncertain thermal demand of the
# building.
# As a trade-off, the RO schedule becomes always less optimal than without RO.
# This is shown by the higher costs for the robust scheduling compared to the non-robust case.
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment