to solve an optimal solution for a time period
Answered-
To solve this problem using Gurobi in Python, you need to formulate it as a linear optimization problem. Here's a general outline of how you might structure your model:
-
Decision Variables:
-
x[t]
: Amount of electricity stored at hourt
. -
buy[t]
: Amount of electricity bought at hourt
. -
sell[t]
: Amount of electricity sold at hourt
. -
delta_x[t]
: Change in storage from hourt-1
tot
.
-
-
Objective Function:
- Maximize the total profit over 5 hours. Profit at each hour is calculated as (sell price * amount sold) - (buy price * amount bought).
-
Constraints:
- Storage limits:
0 <= x[t] <= 8
. - Change in storage limits:
-5 <= delta_x[t] <= 5
. - Storage update:
x[t] = x[t-1] + delta_x[t]
(fort > 0
). - Transaction constraints:
delta_x[t] = buy[t] - sell[t]
. - Non-negativity:
buy[t] >= 0
,sell[t] >= 0
.
- Storage limits:
Here's a basic example of how you might write this in Python using Gurobi:
from gurobipy import Model, GRB
# Define the data
buy_prices = [2, 2, 5, 5, 2]
sell_prices = [1, 1, 3, 3, 1]
hours = range(5)
# Create a new model
m = Model("electricity_storage")
# Create variables
x = m.addVars(hours, vtype=GRB.CONTINUOUS, name="storage")
buy = m.addVars(hours, vtype=GRB.CONTINUOUS, name="buy")
sell = m.addVars(hours, vtype=GRB.CONTINUOUS, name="sell")
delta_x = m.addVars(hours, vtype=GRB.CONTINUOUS, name="delta_x")
# Set the objective
m.setObjective(sum((sell_prices[t] * sell[t] - buy_prices[t] * buy[t]) for t in hours), GRB.MAXIMIZE)
# Add constraints
m.addConstrs((0 <= x[t] <= 8 for t in hours), "StorageLimits")
m.addConstrs((-5 <= delta_x[t] <= 5 for t in hours), "DeltaLimits")
m.addConstrs((x[t] == (x[t-1] + delta_x[t]) for t in hours if t > 0), "StorageUpdate")
m.addConstrs((delta_x[t] == buy[t] - sell[t] for t in hours), "Transaction")
# Solve the model
m.optimize()
# Print the results
if m.status == GRB.OPTIMAL:
for t in hours:
print(f"Hour {t}: Δx = {delta_x[t].X}, Spend/Gain = {sell[t].X * sell_prices[t] - buy[t].X * buy_prices[t]}, Total Storage = {x[t].X}")
print("Total Gain over 5 hours:", m.objVal)This code sets up and solves the problem as described. After running this model, you'll get the optimal
delta_x
at each moment, the spend or gain at each moment, and the total gain over 5 hours. Note that this is a simplified model and might need adjustments based on additional real-world considerations.0 -
-
thank you for your help. But the code met one error in constraints 2.“ gurobipy.GurobiError: Constraint has no bool value (are you trying "lb <= expr <= ub"?) ”
0 -
The error in the Python code arises from the use of the
m.addConstrs
method with a generator expression that includes a double inequality. In Gurobi, you cannot directly use the syntax0 <= x[t] <= 8
withinm.addConstrs
as it does not translate to a valid linear constraint format recognized by Gurobi.To fix this, you should split the double inequality into two separate constraints for each variable
t
. Here's the corrected part of the code:# Add constraints
m.addConstrs((x[t] >= 0 for t in hours), "StorageLowerLimits")
m.addConstrs((x[t] <= 8 for t in hours), "StorageUpperLimits")
m.addConstrs((delta_x[t] >= -5 for t in hours), "DeltaLowerLimits")
m.addConstrs((delta_x[t] <= 5 for t in hours), "DeltaUpperLimits")The code ran successfully, and the model was solved optimally by Gurobi. However, the optimal objective value is 0, which might be unexpected. This could be due to the specific data and constraints in the model, which might not allow for any profitable transactions under the given conditions.
Here are the results of the optimization:
results = [
{'Hour': 0, 'Delta_x': 0.0, 'Spend_Gain': 0.0, 'Total_Storage': 0.0},
{'Hour': 1, 'Delta_x': 0.0, 'Spend_Gain': 0.0, 'Total_Storage': 0.0},
{'Hour': 2, 'Delta_x': 0.0, 'Spend_Gain': 0.0, 'Total_Storage': 0.0},
{'Hour': 3, 'Delta_x': 0.0, 'Spend_Gain': 0.0, 'Total_Storage': 0.0},
{'Hour': 4, 'Delta_x': 0.0, 'Spend_Gain': 0.0, 'Total_Storage': 0.0}
]
total_gain = 0.It seems that the model determined the best strategy was not to engage in any buying or selling transactions, as indicated by the zero values for
Delta_x
andSpend_Gain
for each hour.You may want to review the data or the constraints to ensure they reflect the scenario you're trying to model. If you have specific goals or conditions you're trying to achieve, adjusting the model accordingly might yield more interesting results.
0 -
Why would the result be 0? If I buy 5 at moment 0 and sell 5 at moment 2, then I can earn 3 * 5-2 * 5=5. Is it the result of a logical error in the code that caused the error
0 -
Hi 汉涛 田 ,
I think Gurobot has had a big week. Gurobi 11.0.1 was just released and Gurobot was up late celebrating and has given some suboptimal advice.
Note that variables have default lower bounds of zero, unless explicitly changed. And indeed if the delta[x].lb values are not changed from the default then the result is an optimal solution where all variables are zero.
Instead of trying to set variable bounds with constraints, it is better to do it at variable creation, with lb and ub properties. So instead of this:
# Add constraints
m.addConstrs((x[t] >= 0 for t in hours), "StorageLowerLimits")
m.addConstrs((x[t] <= 8 for t in hours), "StorageUpperLimits")
m.addConstrs((delta_x[t] >= -5 for t in hours), "DeltaLowerLimits")
m.addConstrs((delta_x[t] <= 5 for t in hours), "DeltaUpperLimits")do this:
x = m.addVars(hours, ub=8, vtype=GRB.CONTINUOUS, name="storage") # default lb of 0
delta_x = m.addVars(hours, lb=-5, ub=5, vtype=GRB.CONTINUOUS, name="delta_x")It's cleaner, and we don't end up with a default lower bound of 0 on the delta variables.
Running the code with these changes gives you the following output:
Optimal objective 2.900000000e+01
Hour 0: Δx = -5.0, Spend/Gain = 5.0, Total Storage = 8.0
Hour 1: Δx = 0.0, Spend/Gain = 0.0, Total Storage = 8.0
Hour 2: Δx = -5.0, Spend/Gain = 15.0, Total Storage = 3.0
Hour 3: Δx = -3.0, Spend/Gain = 9.0, Total Storage = 0.0
Hour 4: Δx = 0.0, Spend/Gain = 0.0, Total Storage = 0.0
Total Gain over 5 hours: 29.0- Riley
0 -
Thank you for your reply, but I still feel some confusion.
"X=m. addVars (hours, ub=8, vtype=GRB. CONTANUOUS, name=" storage ")" The upper limit of storage has been set to 8 in the formula,
So how to explain "Hour 0: Δ In the result of "x=-5.0, Spend/Main=5.0, Total Storage=8.0", after the operation of -5, the storage is still 8?
0 -
Ah well spotted. I was celebrating with Gurobot too. Let's look at the following constraint:
m.addConstrs((x[t] == (x[t-1] + delta_x[t]) for t in hours if t > 0), "StorageUpdate")
What about when t = 0? The above constraint won't work because we would have x[-1] but we do need something for t = 0. We can either add the following:
m.addConstr(x[0] == delta_x[0], "StorageUpdate_0")
or we replace the first set of constraints above with:
m.addConstrs((x[t] == x.get(t-1,0) + delta_x[t] for t in hours), "StorageUpdate")
where we use dict.get to return a default value of 0 if the key t-1 doesn't exist, and don't worry about restricting t > 0.
- Riley
0 -
thank you for your help. But I still met a problem.
⬇ ⬇ ⬇
def gurobi(time, ESS):
buy_prices = [0.27, 0.27, 0.27, 0.27, 0.27, 0.27, 0.27, 0.49, 0.49, 0.49, 0.83, 0.83, 0.83, 0.49, 0.49, 0.49, 0.49,
0.49, 0.83, 0.83, 0.83, 0.49, 0.49, 0.27]
sell_prices = [0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.13, 0.38, 0.38, 0.38, 0.65, 0.65, 0.65, 0.38, 0.38, 0.38, 0.38,
0.38, 0.65, 0.65, 0.65, 0.38, 0.38, 0.13]
hours = range(time, 24)
# Create a new model
m = Model("electricity_storage")
# Create variables
x = m.addVars(hours, ub=500, vtype=GRB.CONTINUOUS, name="storage") # 储能容量
buy = m.addVars(hours, vtype=GRB.CONTINUOUS, name="buy")
sell = m.addVars(hours, vtype=GRB.CONTINUOUS, name="sell")
delta_x = m.addVars(hours, lb=-50, ub=50, vtype=GRB.CONTINUOUS, name="delta_x")
# Set the objective
m.setObjective(sum((sell_prices[t] * sell[t] - buy_prices[t] * buy[t]) for t in hours), GRB.MAXIMIZE)
# Add constraints
m.addConstrs((delta_x[t] == buy[t] - sell[t] for t in hours), "Transaction")
⬇ ⬇ ⬇
m.addConstrs((x[t] == x.get(t - 1, ESS) + delta_x[t] for t in hours), "StorageUpdate")
# Solve the model
m.optimize()
# Print the results
if m.status == GRB.OPTIMAL:
print("Total Gain over 24 hours:", m.objVal)
objective_value = m.objVal
return objective_valuefor i in range(rounds):
ESS = 0
v_c = 0
⬇ ⬇ ⬇
for j in range(24):
s = np.zeros(state_dim)
s[0] = j
s[1] = ESS
s[2] = buy_prices[j]
s[3] = sell_prices[j]
a=ddpg.choose_action(np.reshape(s, (1, state_dim)))
P_ESS_delta = a * 50
if ESS + P_ESS_delta < 0 or ESS + P_ESS_delta > 500:
v_c = 1
else:
v_c = 0
# 计算reward
if v_c == 0:
if j != 23:
⬇ ⬇ ⬇
r_n = gurobi(j + 1, ESS+ P_ESS_delta[0][0] )
else:
if ESS + P_ESS_delta == 0:
r_n = P_ESS_delta * sell_prices[j]
else:
r_n = 0
if v_c == 1:
r_n = 0
#更新状态
x_c[i][j][0] = ESS + P_ESS_delta
x_c[i][j][1] = P_ESS_delta
x_c[i][j][2] = r_n
ESS = + P_ESS_delta
s_=np.zeros(state_dim)
s_[0]=j + 1
s_[1]= ESS
if j == 23:
s_[2] = buy_prices[0]
s_[3] = sell_prices[0]
else:
s_[2] = buy_prices[j + 1]
s_[3] = sell_prices[j + 1]
#存储
memory=ddpg.store_transition(s, a, r_n, s_)
if ddpg.pointer > MEMORY_CAPACITY:
var *= 0.99995
ddpg.learn()
s=s_WARNING:tensorflow:Calling GradientTape.gradient on a persistent tape inside its context is significantly less efficient than calling it outside the context (it causes the gradient ops to be recorded on the tape, leading to increased CPU and memory usage). Only call GradientTape.gradient inside the context if you actually want to trace the gradient in order to compute higher order derivatives.
D:\Pycharm\EHGmaddpg\venv\lib\site-packages\keras\engine\training.py:2470: UserWarning: `Model.state_updates` will be removed in a future version. This property should not be used in TensorFlow 2.0, as `updates` are applied automatically.
warnings.warn('`Model.state_updates` will be removed in a future version. '
Using license file C:\Users\TianHanTao\gurobi.lic
Gurobi Optimizer version 9.1.2 build v9.1.2rc0 (win64)
Thread count: 10 physical cores, 16 logical processors, using up to 16 threads
Optimize a model with 46 rows, 92 columns and 137 nonzeros
Model fingerprint: 0x788dadeb
Coefficient statistics:
Matrix range [1e+00, 1e+00]
Objective range [1e-01, 8e-01]
Bounds range [5e+01, 5e+02]
RHS range [3e+00, 3e+00]
Presolve removed 1 rows and 1 columns
Presolve time: 0.00s
Presolved: 45 rows, 91 columns, 135 nonzerosIteration Objective Primal Inf. Dual Inf. Time
0 6.8880000e+31 4.600000e+31 6.888000e+01 0s
63 1.1497383e+02 0.000000e+00 0.000000e+00 0sSolved in 63 iterations and 0.00 seconds
Optimal objective 1.149738281e+02
Total Gain over 24 hours: 114.97382809638977
Traceback (most recent call last):
File "D:\Pycharm\EHGmaddpg\ECD-DDPG-6G.py", line 202, in <module>
r_n = gurobi(j + 1, ESS+ P_ESS_delta[0][0] )
File "D:\Pycharm\EHGmaddpg\ECD-DDPG-6G.py", line 42, in gurobi
m.addConstrs((x[t] == x.get(t - 1, ESS) + delta_x[t] for t in hours), "StorageUpdate")
File "src\gurobipy\model.pxi", line 3473, in gurobipy.Model.addConstrs
File "src\gurobipy\model.pxi", line 3359, in gurobipy.Model.addConstr
File "src\gurobipy\linexpr.pxi", line 471, in gurobipy.LinExpr.__sub__
File "src\gurobipy\linexpr.pxi", line 450, in gurobipy.LinExpr.__add__
File "src\gurobipy\linexpr.pxi", line 183, in gurobipy.LinExpr.add
gurobipy.GurobiError: Unsupported type (<class 'numpy.ndarray'>) for LinExpr addition argument0 -
What is the value of ESS when you receive this error? Use a try/except block to find out. My guess from the error message is that it is a numpy array.
- Riley
0
Please sign in to leave a comment.
Comments
9 comments