Commit 370280b0 authored by Sebastian Schwarz's avatar Sebastian Schwarz
Browse files

Implemented objective function "self-consumption".

parent 4f32bb3e
Pipeline #472100 canceled with stages
in 9 seconds
"""
The pycity_scheduling framework
Copyright (C) 2020,
Institute for Automation of Complex Power Systems (ACS),
E.ON Energy Research Center (E.ON ERC),
RWTH Aachen University
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
"""
import numpy as np
import matplotlib.pyplot as plt
from pycity_scheduling.classes import *
from pycity_scheduling.algorithms import *
# This is a very simple power scheduling example using the central optimization algorithm to demonstrate the impact
# of system level objective "self-consumption".
def main(do_plot=False):
print("\n\n------ Example 09: Objective Self-Consumption ------\n\n")
# Define timer, price, weather, and environment objects:
t = Timer(op_horizon=96, step_size=900, initial_date=(2015, 4, 1))
p = Prices(timer=t)
w = Weather(timer=t)
e = Environment(timer=t, weather=w, prices=p)
# City district with district operator objective "self-consumption":
cd = CityDistrict(environment=e, objective='self-consumption')
# Schedule some sample buildings. The buildings' objectives are defined as "none".
n = 10
for i in range(n):
bd = Building(environment=e, objective='none')
cd.addEntity(entity=bd, position=[0, i])
bes = BuildingEnergySystem(environment=e)
bd.addEntity(bes)
ths = ThermalHeatingStorage(environment=e, e_th_max=40, soc_init=0.5)
bes.addDevice(ths)
eh = ElectricalHeater(environment=e, p_th_nom=10)
bes.addDevice(eh)
ap = Apartment(environment=e)
bd.addEntity(ap)
fi = FixedLoad(e, method=1, annual_demand=3000.0, profile_type='H0')
ap.addEntity(fi)
sh = SpaceHeating(environment=e, method=1, living_area=120, specific_demand=90, profile_type='HEF')
ap.addEntity(sh)
pv = Photovoltaic(environment=e, method=1, peak_power=8.2)
bes.addDevice(pv)
bat = Battery(environment=e, e_el_max=12.0, p_el_max_charge=4.6, p_el_max_discharge=4.6)
bes.addDevice(bat)
# Perform the scheduling:
opt = CentralOptimization(city_district=cd)
results = opt.solve()
cd.copy_schedule("self-consumption")
# Print and show the city district's schedule:
print("Schedule of the city district:")
print(list(cd.p_el_schedule))
plt.plot(cd.p_el_schedule)
#plt.ylim([-2.0, 5.0])
plt.xlabel('Time in hours')
plt.ylabel('Electrical power in kW')
plt.title('City district scheduling result')
plt.grid()
if do_plot:
plt.show()
return
# Conclusions:
# Using "self-consumption" as the system level objective results in a power profile with zero net power export for the
# considered city district over time. In other words, this means that the local power generation (e.g. from the
# buildings' PV units) is fully self-consumed inside the city district. However, this may result in an increased power
# import from the distribution grid and therefore cause power peaks.
if __name__ == '__main__':
# Run example:
main(do_plot=True)
......@@ -35,7 +35,7 @@ from pycity_scheduling.algorithms import *
def main(do_plot=False):
print("\n\n------ Example 09: Objective Price ------\n\n")
print("\n\n------ Example 10: Objective Price ------\n\n")
# Define timer, price, weather, and environment objects:
t = Timer(op_horizon=96, step_size=900, initial_date=(2015, 4, 1))
......
......@@ -35,7 +35,7 @@ from pycity_scheduling.algorithms import *
def main(do_plot=False):
print("\n\n------ Example 10: Objective CO2------\n\n")
print("\n\n------ Example 11: Objective CO2------\n\n")
# Define timer, price, weather, and environment objects:
t = Timer(op_horizon=96, step_size=900, initial_date=(2015, 4, 1))
......
......@@ -35,7 +35,7 @@ from pycity_scheduling.algorithms import *
def main(do_plot=False):
print("\n\n------ Example 11: Objective Valley-Filling ------\n\n")
print("\n\n------ Example 12: Objective Valley-Filling ------\n\n")
# Define timer, price, weather, and environment objects:
t = Timer(op_horizon=96, step_size=900, initial_date=(2015, 4, 1))
......
......@@ -35,7 +35,7 @@ import pycity_scheduling.util.debug as debug
def main(do_plot=False):
print("\n\n------ Example 12: District Generator ------\n\n")
print("\n\n------ Example 13: District Generator ------\n\n")
# First, create an environment using the factory's "generate_standard_environment" method. The environment
# automatically encapsulates time, weather, and price data/information.
......
......@@ -33,7 +33,7 @@ from pycity_scheduling.util import calculate_flexibility_potential
def main(do_plot=False):
print("\n\n------ Example 13: District Flexibility Quantification ------\n\n")
print("\n\n------ Example 14: District Flexibility Quantification ------\n\n")
# First, create an environment using the factory's "generate_standard_environment" method. The environment
# automatically encapsulates time, weather, and price data/information.
......
......@@ -36,7 +36,7 @@ from pycity_scheduling.classes import *
# is hence considered more complex.
def main(do_plot=False):
print("\n\n------ Example 14: Scheduling Complex City District ------\n\n")
print("\n\n------ Example 15: Scheduling Complex City District ------\n\n")
# First, create an environment using the factory's "generate_standard_environment" method. The environment
# automatically encapsulates time, weather, and price data/information.
......
......@@ -36,7 +36,7 @@ from pycity_scheduling.algorithms import *
def main(do_plot=False):
print("\n\n------ Example 15: Scheduling Convex vs. Integer Mode ------\n\n")
print("\n\n------ Example 16: Scheduling Convex vs. Integer Mode ------\n\n")
# Scheduling will be performed for a typical winter day within the annual heating period:
env = factory.generate_standard_environment(step_size=3600, op_horizon=24, mpc_horizon=None, mpc_step_width=None,
......
......@@ -36,7 +36,7 @@ from pycity_scheduling.algorithms import *
def main(do_plot=False):
print("\n\n------ Example 16: Scheduling PV+Battery System ------\n\n")
print("\n\n------ Example 17: Scheduling PV+Battery System ------\n\n")
# Scheduling will be performed for one month:
env = factory.generate_standard_environment(step_size=3600, op_horizon=24*31, mpc_horizon=None,
......
......@@ -36,7 +36,7 @@ from pycity_scheduling.algorithms import *
def main(do_plot=False):
print("\n\n------ Example 17: Scheduling Heating and Cooling Loads ------\n\n")
print("\n\n------ Example 18: Scheduling Heating and Cooling Loads ------\n\n")
# Scheduling will be performed for a full year:
env = factory.generate_standard_environment(step_size=3600, op_horizon=24*365, mpc_horizon=None,
......
......@@ -34,7 +34,7 @@ from pycity_scheduling.util.metric import calculate_costs
def main(do_plot=False):
print("\n\n------ Example 18: Scheduling Robust Optimization ------\n\n")
print("\n\n------ Example 19: Scheduling Robust Optimization ------\n\n")
# Use a simple environment of 6 hours with quarter-hourly resolution (=15min=900sec):
env = factory.generate_standard_environment(step_size=900, op_horizon=6)
......
......@@ -35,7 +35,7 @@ from pycity_scheduling.util.write_schedules import schedule_to_json, schedule_to
def main(do_plot=False):
print("\n\n------ Example 19: Post-Processing Schedule Evaluation ------\n\n")
print("\n\n------ Example 20: Post-Processing Schedule Evaluation ------\n\n")
# Define timer, price, weather, and environment objects:
t = Timer()
......
......@@ -37,7 +37,7 @@ from pycity_scheduling.util.metric import calculate_costs, peak_to_average_ratio
def main(do_plot=False):
print("\n\n------ Example 20: Post-Processing Metrics Evaluation ------\n\n")
print("\n\n------ Example 21: Post-Processing Metrics Evaluation ------\n\n")
# Use a standard environment of 24 hours with hourly resolution (=60min=3600sec):
env = factory.generate_standard_environment(step_size=3600, op_horizon=24)
......
......@@ -45,6 +45,7 @@ class Building(EntityContainer, bd.Building):
- 'co2' : Optimize for the CO2 emissions given by `prices.co2_prices`.
- 'peak-shaving' : Try to flatten the schedule as much as possible.
- 'max-consumption' : Try to reduce the maximum of the absolute values of the schedule as much as possible.
- 'self-consumption' : Try to maximize the self-consumption of the local power generation.
- 'none' : No objective (leave all flexibility to other participants).
name : str, optional
Name for the building.
......
......@@ -41,13 +41,14 @@ class CityDistrict(ElectricalEntity, cd.CityDistrict):
- 'price' : Optimize for the minimum total cost given by `prices.da_prices`.
- 'peak-shaving' : Try to 'flatten' the schedule as much as possible.
- 'max-consumption' : Try to minimize the maximum power subscription.
- 'co2' : Optimize for the minimum total co2 emissions given by `prices.co2_prices`.
- valley-filling : Try to fill the 'valleys' given by a reference power profile.
- flexibility-quantification: To be used to quantify the flexibility potential of the city district only.
- 'valley-filling' : Try to fill the 'valleys' given by a reference power profile.
- 'max-consumption' : Try to minimize the maximum power subscription.
- 'self-consumption' : Try to maximize the self-consumption of the local power generation.
- 'flexibility-quantification' : To be used to quantify the flexibility potential of the city district only.
- 'none' : No objective.
valley_profile : numpy.ndarray, optional
Profile to be filled by applying valley filling.
Profile to be filled by applying the valley filling objective.
Notes
-----
......
......@@ -71,11 +71,19 @@ class ElectricalEntity(OptimizationEntity):
def p_consumption_rule(model, t):
return model.max_consumption_var >= m.p_el_vars[t]
m.P_cons_constr = pyomo.Constraint(m.t, rule=p_consumption_rule)
m.p_cons_constr = pyomo.Constraint(m.t, rule=p_consumption_rule)
def p_generation_rule(model, t):
return model.max_consumption_var >= -m.p_el_vars[t]
m.P_gen_constr = pyomo.Constraint(m.t, rule=p_generation_rule)
m.p_gen_constr = pyomo.Constraint(m.t, rule=p_generation_rule)
if self.objective == "self-consumption":
m.p_export_var = pyomo.Var(m.t, domain=pyomo.Reals, bounds=(0, None), initialize=0)
m.p_import_var = pyomo.Var(m.t, domain=pyomo.Reals, bounds=(0, None), initialize=0)
def p_self_consumption_rule(model, t):
return model.p_import_var[t] - model.p_export_var[t] == m.p_el_vars[t]
m.p_self_cons_constr = pyomo.Constraint(m.t, rule=p_self_consumption_rule)
if self.objective == "flexibility-quantification":
m.max_p_flex_var = pyomo.Var(m.t, domain=pyomo.Reals, bounds=(0, None), initialize=0)
......@@ -94,8 +102,6 @@ class ElectricalEntity(OptimizationEntity):
def get_objective(self, coeff=1):
if self.objective == 'peak-shaving':
return coeff * pyomo.sum_product(self.model.p_el_vars, self.model.p_el_vars)
if self.objective == 'flexibility-quantification':
return coeff * pyomo.sum_product(self.model.max_p_flex_var, self.model.max_p_flex_var)
if self.objective in ['price', 'co2']:
if self.objective == 'price':
prices = self.environment.prices.tou_prices
......@@ -110,8 +116,20 @@ class ElectricalEntity(OptimizationEntity):
return 0
if self.objective == "max-consumption":
if not hasattr(self.model, "max_consumption_var"):
raise ValueError("'max-consumption' needs to be selected during populate_model call.")
raise ValueError("Objective 'max-consumption' needs to be selected during populate_model call.")
if coeff < 0:
raise ValueError("Setting a coefficient below zero is not supported for the max-consumption objective.")
return coeff * self.model.max_consumption_var
if self.objective == "self-consumption":
if not hasattr(self.model, "p_export_var"):
raise ValueError("'Objective self-consumption' needs to be selected during populate_model call.")
if coeff < 0:
raise ValueError("Setting a coefficient below zero is not supported for the self-consumption "
"objective.")
return coeff * pyomo.sum_product(self.model.p_export_var, self.model.p_export_var)
if self.objective == 'flexibility-quantification':
if not hasattr(self.model, "max_p_flex_var"):
raise ValueError("Objective 'flexibility-quantification' needs to be selected during populate_model "
"call.")
return coeff * pyomo.sum_product(self.model.max_p_flex_var, self.model.max_p_flex_var)
return super().get_objective(coeff)
Markdown is supported
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