Code
import numpy as np
import matplotlib.pyplot as plt
import pyomo.environ as pyo
Sie haben einen zeitabhängingen Energiepreis \(c_j\) und wollen den dazu kostenoptimalen Startzeitpunkt für Ihre Waschmaschine finden, sodass der Waschvorgang in den kommenden acht Stunden beendet ist. Der Lastgang des Waschvorgangs und die Preise sind durch die folgenden Daten in Viertelstundenschritten gegeben:
# time:
T = 8.0 # h
dt = 0.25 # h
n = int(T/dt) # number of time periods
times = np.arange(start=0, stop=T + dt, step=dt)
periods = np.arange(start=0, stop=T, step=dt)
time_indices = range(n + 1) # 0, 1, ..., n - 1, n
period_indices = range(n) # 0, 1, ..., n - 1
# load profile:
load_profile = np.array([0.5, 1.0, 1.0, 0.2, 0.2, 1.0]) # kW
# duration of the load profile in number of time periods:
m = len(load_profile)
# example demand for a given start time:
load_start = 11 # index of starting time of load profile
demand = np.zeros_like(periods)
demand[load_start:load_start + m] = load_profile
# prices:
np.random.seed(0)
noise = np.random.normal(loc=0, scale=0.05, size=n)
price = .15 + 0.2*noise**2 + noise
# plot:
plt.figure(figsize=(5, 4))
plt.subplot(2, 1, 1)
plt.step(periods, demand, where='post', color='black')
plt.xlabel('time (h)')
plt.ylabel('demand (kW)')
plt.grid(True)
plt.subplot(2, 1, 2)
plt.step(periods, price, where='post', color='red')
plt.xlabel('time (h)')
plt.ylabel('price (EUR/kWh)')
plt.grid(True)
plt.tight_layout()
Daten:
Entscheidungsvariablen:
Zielfunktion: \(\min \sum_{j=0}^{n-1} c_j p_j \Delta t\)
Nebenbedingungen:
model = pyo.ConcreteModel()
model.J = pyo.Set(initialize=period_indices)
# set of time points where the load profile can be started:
model.S = pyo.Set(initialize=time_indices[:-m])
model.p = pyo.Var(model.J, domain=pyo.NonNegativeReals) # power in kW
model.s = pyo.Var(model.S, domain=pyo.Binary) # start load at time/period
model.cost = pyo.Objective(expr=sum(price[j]*model.p[j]*dt
for j in model.J), sense=pyo.minimize)
model.one_start = pyo.Constraint(expr=sum(model.s[j] for j in model.S) == 1)
@model.Constraint(model.J)
def demand_constraint(model, j):
return model.p[j] == sum(load_profile[k]*model.s[j - k]
for k in range(m)
if j - k in model.S)
status = ok
minimal cost = 0.10 EUR
p_sol_dict = model.p.extract_values()
p_sol = np.array([p_sol_dict[j] for j in period_indices])
s_sol_dict = model.s.extract_values()
s_sol = np.array([s_sol_dict[j] for j in time_indices[:-m]])
plt.figure(figsize=(5, 5))
plt.subplot(3, 1, 1)
plt.stem(times[:-m], s_sol,basefmt=' ',
markerfmt='.', linefmt='g:')
plt.xlim(0, T)
plt.xlabel('time (h)')
plt.ylabel('start load')
plt.grid(True)
plt.subplot(3, 1, 2)
plt.step(periods, p_sol, where='post', color='black')
plt.xlim(0, T)
plt.xlabel('time (h)')
plt.ylabel('demand (kW)')
plt.grid(True)
plt.subplot(3, 1, 3)
plt.step(periods, price, where='post', color='red')
plt.xlim(0, T)
plt.xlabel('time (h)')
plt.ylabel('price (EUR/kWh)')
plt.grid(True)
plt.tight_layout()
Gegeben ist die Ertragskurve einer PV-Anlage mit 5 kWp über einen Tag. Sie wollen 20 Gabelstapler-Austauschbatterien so laden, dass die Gesamtladekurve möglichst der PV-Ertragskurve folgt. Die Ladekurve einer Batterie und der PV-Ertrag sind durch die Daten unten angegeben.
Das Folgen einer vorgegebenen Leistungskurve wird auch als Power Tracking bezeichnet. Dabei wird der Unterschied zwischen der vorgegebenen Kurve und der nachfahrenden Kurve minimiert. Wir quantifizieren den Unterschied in diesem Beispiel durch die Summe der Beträge der Differenzen zwischen den beiden Kurven: \[ \sum_{j=0}^{n-1} \left| p_{\text{pv},j} - p_{\text{load},j} \right| \] Andere Möglichkeiten, den Unterschied zu quantifizieren sind die Summe der Quadrate der Differenzen oder die maximale absolute Differenz, siehe den Abschnitt Tracking.
# sampling interval:
dt = 0.25 # h
# load profile for charging a forklift
load_profile = np.array([0.5, 1, 1, 1, 1, 1, 1, 0.5]) # kW
m = len(load_profile)
# PV power:
T = 24 # h
n = int(T/dt) # number of time periods
times = np.arange(start=0, stop=T + dt, step=dt) # timestamps: 0, 0.25, 0.5, ..., 23.75, 24
periods = np.arange(start=0, stop=T, step=dt) # start times of periods: 0, 0.25, 0.5, ..., 23.75
pv_power = np.zeros_like(periods)
pv_power[6*4:6*4 + 12*4] = 5*np.sin(2*np.pi/(6*4) * periods[:12*4] ) # PV power in kW
plt.figure(figsize=(5, 5))
plt.subplot(2, 1, 1)
load_profile_ = np.hstack((load_profile, load_profile[-1]))
plt.step(np.arange(m + 1)*dt, load_profile_, where='post',
marker='.', label='load profile')
plt.ylim(0, 1.1*load_profile.max())
plt.xlabel('time (h)')
plt.ylabel('power (kW)')
plt.legend()
plt.grid()
plt.subplot(2, 1, 2)
plt.step(periods, pv_power, where='post', label='PV')
plt.xlabel('time (h)')
plt.ylabel('power (kW)')
plt.legend()
plt.grid()
plt.tight_layout()