Skip to content

Commit

Permalink
Optimization solved but unfeasible results with sequence of power
Browse files Browse the repository at this point in the history
  • Loading branch information
davidusb-geek committed Jun 1, 2024
1 parent 1a50e45 commit 7795aaa
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 117 deletions.
6 changes: 3 additions & 3 deletions scripts/script_debug_optim.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@
# Set special debug cases
optim_conf.update({'treat_def_as_semi_cont': [True, True]})
optim_conf.update({'set_def_constant': [False, False]})
# optim_conf.update({'P_deferrable_nom': [[500.0, 1000.0, 1000.0, 500.0], 750.0]})
optim_conf.update({'P_deferrable_nom': [[500.0, 1000.0, 1000.0, 500.0], 750.0]})

optim_conf.update({'set_use_battery': True})
optim_conf.update({'set_use_battery': False})
optim_conf.update({'set_nocharge_from_grid': False})
optim_conf.update({'set_battery_dynamic': True})
optim_conf.update({'set_nodischarge_to_grid': True})

optim_conf.update({'inverter_is_hybrid': True})
optim_conf.update({'inverter_is_hybrid': False})

df_input_data.loc[df_input_data.index[25:30],'unit_prod_price'] = -0.07
df_input_data['P_PV_forecast'] = df_input_data['P_PV_forecast']*2
Expand Down
196 changes: 82 additions & 114 deletions src/emhass/optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,6 @@ def perform_optimization(self, data_opt: pd.DataFrame, P_PV: np.array, P_load: n
P_PV_curtailment = {(i):plp.LpVariable(cat='Continuous', lowBound=0,
name="P_PV_curtailment{}".format(i)) for i in set_I}

# Special variables to define a sequence:
# Seq_start = {(i):plp.LpVariable(cat='Binary',
# name="Seq_start_{}".format(i)) for i in set_I}

## Define objective
P_def_sum= []
for i in set_I:
Expand Down Expand Up @@ -256,24 +252,13 @@ def perform_optimization(self, data_opt: pd.DataFrame, P_PV: np.array, P_load: n
self.optim_conf['weight_battery_charge']*P_sto_neg[i]) for i in set_I)

# Add term penalizing each startup where configured
if (
"def_start_penalty" in self.optim_conf
and self.optim_conf["def_start_penalty"]
):
if ("def_start_penalty" in self.optim_conf and self.optim_conf["def_start_penalty"]):
for k in range(self.optim_conf["num_def_loads"]):
if (
len(self.optim_conf["def_start_penalty"]) > k
and self.optim_conf["def_start_penalty"][k]
):
if (len(self.optim_conf["def_start_penalty"]) > k and self.optim_conf["def_start_penalty"][k]):
objective = objective + plp.lpSum(
-0.001
* self.timeStep
* self.optim_conf["def_start_penalty"][k]
* P_def_start[k][i]
* unit_load_cost[i]
* self.optim_conf['P_deferrable_nom'][k]
for i in set_I
)
-0.001 * self.timeStep * self.optim_conf["def_start_penalty"][k] * P_def_start[k][i] *\
unit_load_cost[i] * self.optim_conf['P_deferrable_nom'][k]
for i in set_I)

opt_model.setObjective(objective)

Expand All @@ -295,16 +280,24 @@ def perform_optimization(self, data_opt: pd.DataFrame, P_PV: np.array, P_load: n
for i in set_I}

# Constraint for hybrid inverter and curtailment cases
cec_inverters = bz2.BZ2File(pathlib.Path(__file__).parent / 'data/cec_inverters.pbz2', "rb")
cec_inverters = cPickle.load(cec_inverters)
if type(self.plant_conf['module_model']) == list:
P_nom_inverter = 0.0
for i in range(len(self.plant_conf['inverter_model'])):
inverter = cec_inverters[self.plant_conf['inverter_model'][i]]
P_nom_inverter += inverter.Paco
if type(self.plant_conf['inverter_model'][i]) == str:
cec_inverters = bz2.BZ2File(pathlib.Path(__file__).parent / 'data/cec_inverters.pbz2', "rb")
cec_inverters = cPickle.load(cec_inverters)
inverter = cec_inverters[self.plant_conf['inverter_model'][i]]
P_nom_inverter += inverter.Paco
else:
P_nom_inverter += self.plant_conf['inverter_model'][i]
else:
inverter = cec_inverters[self.plant_conf['inverter_model']]
P_nom_inverter = inverter.Paco
if type(self.plant_conf['inverter_model'][i]) == str:
cec_inverters = bz2.BZ2File(pathlib.Path(__file__).parent / 'data/cec_inverters.pbz2', "rb")
cec_inverters = cPickle.load(cec_inverters)
inverter = cec_inverters[self.plant_conf['inverter_model']]
P_nom_inverter = inverter.Paco
else:
P_nom_inverter = self.plant_conf['inverter_model']
if self.optim_conf['inverter_is_hybrid']:
constraints.update({"constraint_hybrid_inverter1_{}".format(i) :
plp.LpConstraint(
Expand All @@ -327,6 +320,8 @@ def perform_optimization(self, data_opt: pd.DataFrame, P_PV: np.array, P_load: n
for i in set_I})

# Constraint for sequence of deferrable
# WARNING: This is experimental, formulation seems correct but feasibility problems.
# Probably uncomptabile with other constraints
for k in range(self.optim_conf['num_def_loads']):
if type(self.optim_conf['P_deferrable_nom'][k]) == list:
power_sequence = self.optim_conf['P_deferrable_nom'][k]
Expand All @@ -338,18 +333,18 @@ def create_matrix(input_list, n):
matrix.append(row[:n*2])
return matrix
matrix = create_matrix(power_sequence, n-sequence_length)
y = plp.LpVariable.dicts("y", (i for i in set_I), cat='Binary')
y = plp.LpVariable.dicts("y", (i for i in range(len(matrix))), cat='Binary')
constraints.update({f"SingleValueConstraint_{i}" :
plp.LpConstraint(
e = plp.lpSum(y[i] for i in set_I),
e = plp.lpSum(y[i] for i in range(len(matrix))) - 1,
sense = plp.LpConstraintEQ,
rhs = 1)
rhs = 0)
})
constraints.update({f"DefSumConstraint_{i}" :
plp.LpConstraint(
e = plp.lpSum(P_deferrable[k][i] for i in set_I),
e = plp.lpSum(P_deferrable[k][i] for i in set_I) - np.sum(power_sequence),
sense = plp.LpConstraintEQ,
rhs = np.sum(power_sequence))
rhs = 0)
})
constraints.update({f"DefPositiveConstraint_{i}" :
plp.LpConstraint(
Expand All @@ -358,11 +353,11 @@ def create_matrix(input_list, n):
rhs = 0)
for i in set_I})
for num, mat in enumerate(matrix):
constraints.update({f"ValueConstraint_{num}" :
constraints.update({f"ValueConstraint_{num}_{i}" :
plp.LpConstraint(
e = P_deferrable[k][i],
e = P_deferrable[k][i] - mat[i]*y[num],
sense = plp.LpConstraintEQ,
rhs = mat[i]*y[i])
rhs = 0)
for i in set_I})

# Two special constraints just for a self-consumption cost function
Expand Down Expand Up @@ -464,102 +459,75 @@ def create_matrix(input_list, n):
sense = plp.LpConstraintEQ,
rhs = 1)
})
<<<<<<< HEAD

# Treat deferrable load as a semi-continuous variable
if self.optim_conf['treat_def_as_semi_cont'][k]:
constraints.update({"constraint_pdef{}_semicont1_{}".format(k, i) :
# Treat deferrable load as a semi-continuous variable
if self.optim_conf['treat_def_as_semi_cont'][k]:
constraints.update({"constraint_pdef{}_semicont1_{}".format(k, i) :
plp.LpConstraint(
e=P_deferrable[k][i] - self.optim_conf['P_deferrable_nom'][k]*P_def_bin1[k][i],
sense=plp.LpConstraintGE,
rhs=0)
for i in set_I})
constraints.update({"constraint_pdef{}_semicont2_{}".format(k, i) :
plp.LpConstraint(
e=P_deferrable[k][i] - self.optim_conf['P_deferrable_nom'][k]*P_def_bin1[k][i],
sense=plp.LpConstraintLE,
rhs=0)
for i in set_I})
# Treat the number of starts for a deferrable load
current_state = 0
if ("def_current_state" in self.optim_conf and len(self.optim_conf["def_current_state"]) > k):
current_state = 1 if self.optim_conf["def_current_state"][k] else 0
# P_deferrable < P_def_bin2 * 1 million
# P_deferrable must be zero if P_def_bin2 is zero
constraints.update({"constraint_pdef{}_start1_{}".format(k, i):
plp.LpConstraint(
e=P_deferrable[k][i] - self.optim_conf['P_deferrable_nom'][k]*P_def_bin1[k][i],
sense=plp.LpConstraintGE,
e=P_deferrable[k][i] - P_def_bin2[k][i] * M,
sense=plp.LpConstraintLE,
rhs=0)
for i in set_I})
constraints.update({"constraint_pdef{}_semicont2_{}".format(k, i) :
# P_deferrable - P_def_bin2 <= 0
# P_def_bin2 must be zero if P_deferrable is zero
constraints.update({"constraint_pdef{}_start1a_{}".format(k, i):
plp.LpConstraint(
e=P_deferrable[k][i] - self.optim_conf['P_deferrable_nom'][k]*P_def_bin1[k][i],
e=P_def_bin2[k][i] - P_deferrable[k][i],
sense=plp.LpConstraintLE,
rhs=0)
for i in set_I})

# Treat the number of starts for a deferrable load
current_state = 0
if (
"def_current_state" in self.optim_conf
and len(self.optim_conf["def_current_state"]) > k
):
current_state = 1 if self.optim_conf["def_current_state"][k] else 0
# P_deferrable < P_def_bin2 * 1 million
# P_deferrable must be zero if P_def_bin2 is zero
constraints.update(
{
"constraint_pdef{}_start1_{}".format(k, i): plp.LpConstraint(
e=P_deferrable[k][i] - P_def_bin2[k][i] * M,
sense=plp.LpConstraintLE,
rhs=0,
)
for i in set_I
}
)
# P_deferrable - P_def_bin2 <= 0
# P_def_bin2 must be zero if P_deferrable is zero
constraints.update(
{
"constraint_pdef{}_start1a_{}".format(k, i): plp.LpConstraint(
e=P_def_bin2[k][i] - P_deferrable[k][i],
sense=plp.LpConstraintLE,
rhs=0,
)
for i in set_I
}
)
# P_def_start + P_def_bin2[i-1] >= P_def_bin2[i]
# If load is on this cycle (P_def_bin2[i] is 1) then P_def_start must be 1 OR P_def_bin2[i-1] must be 1
# For first timestep, use current state if provided by caller.
constraints.update(
{
"constraint_pdef{}_start2_{}".format(k, i): plp.LpConstraint(
# P_def_start + P_def_bin2[i-1] >= P_def_bin2[i]
# If load is on this cycle (P_def_bin2[i] is 1) then P_def_start must be 1 OR P_def_bin2[i-1] must be 1
# For first timestep, use current state if provided by caller.
constraints.update({"constraint_pdef{}_start2_{}".format(k, i):
plp.LpConstraint(
e=P_def_start[k][i]
- P_def_bin2[k][i]
+ (P_def_bin2[k][i - 1] if i - 1 >= 0 else current_state),
sense=plp.LpConstraintGE,
rhs=0,
)
for i in set_I
}
)
# P_def_bin2[i-1] + P_def_start <= 1
# If load started this cycle (P_def_start[i] is 1) then P_def_bin2[i-1] must be 0
constraints.update({"constraint_pdef{}_start3_{}".format(k, i):
plp.LpConstraint(
e=(P_def_bin2[k][i-1] if i-1 >= 0 else 0) + P_def_start[k][i],
sense=plp.LpConstraintLE,
rhs=1)
for i in set_I})
if self.optim_conf['set_def_constant'][k]:
# P_def_start[i] must be 1 for exactly 1 value of i
constraints.update({"constraint_pdef{}_start4".format(k) :
plp.LpConstraint(
e = plp.lpSum(P_def_start[k][i] for i in set_I),
sense = plp.LpConstraintEQ,
rhs = 1)
})
# P_def_bin2 must be 1 for exactly the correct number of timesteps.
constraints.update({"constraint_pdef{}_start5".format(k) :
plp.LpConstraint(
e = plp.lpSum(P_def_bin2[k][i] for i in set_I),
sense = plp.LpConstraintEQ,
rhs = def_total_hours[k]/self.timeStep)
})

=======
rhs=0)
for i in set_I})
# P_def_bin2[i-1] + P_def_start <= 1
# If load started this cycle (P_def_start[i] is 1) then P_def_bin2[i-1] must be 0
constraints.update({"constraint_pdef{}_start3_{}".format(k, i):
plp.LpConstraint(
e=(P_def_bin2[k][i-1] if i-1 >= 0 else 0) + P_def_start[k][i],
sense=plp.LpConstraintLE,
rhs=1)
for i in set_I})
if self.optim_conf['set_def_constant'][k]:
# P_def_start[i] must be 1 for exactly 1 value of i
constraints.update({"constraint_pdef{}_start4".format(k) :
plp.LpConstraint(
e = plp.lpSum(P_def_start[k][i] for i in set_I),
sense = plp.LpConstraintEQ,
rhs = 1)
})
# P_def_bin2 must be 1 for exactly the correct number of timesteps.
constraints.update({"constraint_pdef{}_start5".format(k) :
plp.LpConstraint(
e = plp.lpSum(P_def_bin2[k][i] for i in set_I),
sense = plp.LpConstraintEQ,
rhs = self.optim_conf['def_total_hours'][k]/self.timeStep)
rhs = def_total_hours[k]/self.timeStep)
})

>>>>>>> f5e0df4a7de51cec11810c9ac936e5c6af742a54

# The battery constraints
if self.optim_conf['set_use_battery']:
# Optional constraints to avoid charging the battery from the grid
Expand Down

0 comments on commit 7795aaa

Please sign in to comment.