Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 24 additions & 15 deletions src/optimizer/optimizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,14 @@ def _setup_variables(self):
self.variables['p_demand_pen'] = [[None for t in self.time_steps] for i in range(len(self.batteries))]
# binary variable to allow one out of two alternative constraints
self.variables['z_p_demand'] = [[None for t in self.time_steps] for i in range(len(self.batteries))]
# binary variable to capture when s_max is reached
self.variables['z_s_max_reached'] = [[None for t in self.time_steps] for i in range(len(self.batteries))]
for i, bat in enumerate(self.batteries):
if bat.p_demand is not None:
for t in self.time_steps:
self.variables['p_demand_pen'][i][t] = pulp.LpVariable(f"p_demand_pen_{i}_{t}", lowBound=0)
self.variables['z_p_demand'][i][t] = pulp.LpVariable(f"z_p_demand_{i}_{t}", cat='Binary')
self.variables['z_s_max_reached'][i][t] = pulp.LpVariable(f"z_s_max_reached_{i}_{t}", cat='Binary')

# penalty variable for staying above max SOC and below min SOC
self.variables['s_max_pen'] = [[pulp.LpVariable(f"s_max_pen_{i}_{t}", lowBound=0) for t in self.time_steps] for i in range(len(self.batteries))]
Expand Down Expand Up @@ -448,25 +451,31 @@ def _add_battery_constraints(self):
if bat.p_demand is not None:
for t in self.time_steps:
if bat.p_demand[t] > 0:
# clip required charge to max charging power if needed
# and leave some air to breathe for the optimizer
# clip requested charging power to max charging power if needed
p_demand = min(bat.c_max * self.time_series.dt[t] / 3600., bat.p_demand[t])
# two alternative constraints, only one is active:
# constraint option 1: charge energy tries to reach min charge energy parameter
self.problem += (self.variables['c'][i][t] + self.variables['p_demand_pen'][i][t]
+ self.M * self.variables['z_p_demand'][i][t] >= p_demand)
# constraint option 2: charge energy tries to reach energy to fill the battery to s_max

# introduce z_p_demand to become only 1 if s_max is almost reached and the
# charging with c_min would stop before the end of the time slot
self.problem += ((bat.s_max - self.variables['s'][i][t])
<= (1 - self.variables['z_p_demand'][i][t]) * self.M
+ bat.c_min * self.time_series.dt[t] / 3600.)

# introduce z_s_max_reached to become only 1 if s_max is fully reached
self.problem += ((bat.s_max - self.variables['s'][i][t])
<= (1 - self.variables['z_s_max_reached'][i][t]) * self.M)

# set a "soft" constraint to reach the requested charging rate if possible.
# deactivate it if s_max is already reached
self.problem += (self.variables['c'][i][t] + self.variables['p_demand_pen'][i][t]
+ self.M * (1 - self.variables['z_p_demand'][i][t])
- (self.batteries[i].s_max - self.variables['s'][i][t]) >= 0.)
elif bat.c_min > 0:
# in time steps without given charging demand, apply normal lower bound:
# Lower bound: either 0 or at least c_min
self.problem += (self.variables['c'][i][t] >= bat.c_min * self.time_series.dt[t] / 3600.
* self.variables['z_c'][i][t])
>= (1 - self.variables['z_s_max_reached'][i][t]) * p_demand)

if bat.c_min > 0:
# set constraints to exclude charging between 0 and c_min if z_p_demand is not 1.
self.problem += (self.variables['c'][i][t] + self.variables['z_p_demand'][i][t] * self.M
>= bat.c_min * self.time_series.dt[t] / 3600. * self.variables['z_c'][i][t])
self.problem += (self.variables['c'][i][t] <= self.M * self.variables['z_c'][i][t])

# Constraint (7): Minimum charge power limits if there is not charge demand
# Constraint (7): Minimum charge power limits if there is no charge demand
elif bat.c_min > 0:
for t in self.time_steps:
# Lower bound: either 0 or at least c_min
Expand Down
28 changes: 14 additions & 14 deletions test_cases/011-infeasible-charge-demand.json
Original file line number Diff line number Diff line change
Expand Up @@ -336,14 +336,14 @@
{
"charging_power": [
2700.3723,
2726.2801,
3775.7398,
4087.0091,
3515.8202,
2656.4201,
1606.9604,
1803.9868,
824.5558,
-7.48207e-12,
-2.4940233e-12,
0.0,
0.0,
0.0,
0.0,
0.0,
Expand Down Expand Up @@ -434,9 +434,9 @@
],
"state_of_charge": [
3847.3351,
6300.9872,
9979.2954,
13143.534,
7245.5009,
10923.809,
14088.047,
15534.312,
17157.9,
17900.0,
Expand Down Expand Up @@ -485,10 +485,10 @@
{
"charging_power": [
0.0,
1049.4597,
0.0,
0.0,
0.0,
1049.4597,
0.0,
0.0,
0.0,
Expand Down Expand Up @@ -544,8 +544,8 @@
552.01445,
689.2983,
326.2485,
7.1183464e-11,
-7.1183464e-11,
0.0,
0.0,
0.0,
0.0,
0.0,
Expand Down Expand Up @@ -583,9 +583,9 @@
],
"state_of_charge": [
1267.2,
2211.7137,
2211.7137,
2211.7137,
1267.2,
1267.2,
1267.2,
2211.7137,
2211.7137,
2211.7137,
Expand Down Expand Up @@ -732,10 +732,10 @@
],
"flow_direction": [
0,
1,
0,
0,
0,
1,
0,
0,
0,
Expand Down
Loading
Loading