I am trying to do an Optimization for the Energy Supply of an domestic House. The energy demand should be satisfied by a Heat Pump, PV-Modules, Electric Water Heater and the public electricity Grid. Also the Energy System consists of an Battery Storage an a Heat Storage. The only (binary) integer Variable in my Program is the Heat Pump. My Goal is to Optimize the System over the Timeframe of 1 Year (8760 timesteps). When I run the Code with 1800 timesteps I get results in about 500 seconds. For 4500 timestamps it already takes about 9 Hours. For the full 8760 timesteps the code is still running (since about 24 Hours) without any solution. In earlier iterations of the code it ran for more than a Week without generating results. I already tried a few things I read here to speed up the optimization. Is there anyway I can get the Program to find a solution faster? Since I am a beginner at Python it is very much possible that my Code is inefficient. I would very much appreciate it, if someone has an Idea that can get me faster Results or estimate the time the program takes to find a solution. Thank you very much in advance.
Here is my Code, I have shortened the Arrays for the Energy-Demand to 100 Values to shorten my Code:
from gekko import GEKKO
import numpy as np
import matplotlib.pyplot as plt
timesteps= 100
m = GEKKO(remote=False)
t = np.linspace(0, timesteps-1, timesteps) #Zeit
m.time = t
m.options.SOLVER = 1
m.options.IMODE = 6
m.options.NODES = 2
m.options.REDUCE=3
m.solver_options = ['minlp_maximum_iterations 1000',\
'minlp_max_iter_with_int_sol 1000',\
'minlp_integer_tol 1.0e-1',\
'minlp_branch_method 1',\
'objective_convergence_tolerance 1.0e-4',\
'minlp_gap_tol 9e-1']
# Energy Demand
#1. electricity
EL_Demand_Arr1=np.array([1.9200000,1.4890000,1.4920000,1.1300000,0.64099997,0.58600003,0.58399999,0.61000001,0.54900002,0.59500003,0.92699999,0.95599997,0.91000003,1.1450000,1.1090000,1.6360000,1.4740000,1.4680001,2.6150000,2.1810000,1.2320000,1.3700000,0.96899998,1.3220000,1.1880000,0.64399999,0.53899997,0.55299997,0.52899998,0.56099999,0.54600000,0.80000001,1.1350000,0.70700002,1.1680000,1.0440000,2.3160000,1.6420000,2.2370000,2.8870001,1.8550000,1.4030000,0.70599997,1.4980000,3.4879999,1.5130000,1.4349999,1.3520000,1.0530000,0.51700002,0.55000001,0.52800000,0.52999997,0.56199998,0.53700000,0.58999997,0.53500003,0.92500001,1.3490000,0.66299999,4.3810000,1.0200000,0.79799998,0.77899998,1.0840000,2.1530001,3.7449999,5.3490000,1.8710001,2.3610001,0.78799999,0.47099999,0.56800002,0.51700002,0.54799998,0.55699998,0.51400000,0.56500000,3.2790000,2.2750001,1.2300000,0.97899997,0.78200001,1.0140001,0.77800000,0.58099997,0.52999997,0.55900002,1.1770000,1.5400000,1.4349999,2.0400000,2.2790000,1.6520000,1.6450000,1.2830000,0.55800003,0.52499998,0.51899999,0.53799999])
EL_Demand_Arr2=EL_Demand_Arr1.round(decimals=3)
EL_Demand_Arr=EL_Demand_Arr2[0:timesteps]
EL_Demand=m.Param(EL_Demand_Arr,name='EL_Demand')
#2. heat
H_Demand_Arr1=np.array([1.0960000,1.0790000,1.1590000,1.1760000,1.6940000,2.2639999,2.1450000,2.0769999,2.0720000,2.0300000,1.9069999,1.8810000,1.7880000,1.8180000,1.8049999,2.0430000,2.1489999,2.1700001,2.1830001,2.1910000,1.9920000,1.5290000,1.1810000,1.0400000,1.4310000,1.4110000,1.4700000,1.4900000,1.8880000,2.4530001,2.2809999,2.3199999,2.2960000,2.3299999,2.1630001,2.1289999,2.0599999,2.1090000,2.0940001,2.3450000,2.4380000,2.4679999,2.4630001,2.4480000,2.2219999,1.8480000,1.5779999,1.4310000,1.5000000,1.4790000,1.5410000,1.5620000,1.9790000,2.5720000,2.3910000,2.4319999,2.4070001,2.4430001,2.2679999,2.2309999,2.1589999,2.2110000,2.1949999,2.4579999,2.5560000,2.5869999,2.5820000,2.5660000,2.3290000,1.9380000,1.6540000,1.5000000,1.7160000,1.6930000,1.7630000,1.7869999,2.2650001,2.9430001,2.7360001,2.7839999,2.7539999,2.7950001,2.5950000,2.5539999,2.4710000,2.5300000,2.5120001,2.8130000,2.9250000,2.9600000,2.9549999,2.9370000,2.6659999,2.2170000,1.8930000,1.7160000,1.7980000,1.7670000,1.8789999,1.9160000])
H_Demand_Arr2=H_Demand_Arr1.round(decimals=3)
H_Demand_Arr=H_Demand_Arr2[0:timesteps]
H_Demand=m.Param(H_Demand_Arr,name='H_Demand')
#3. Domestic Hot Water
DHW_Demand_Arr1=np.array([1.7420000,0,0,2.0320001,0,0,3.7739999,2.4960001,3.3670001,0,2.4380000,1.1030000,0,0,0,3.1350000,2.2060001,0,4.4120002,0,0,0,0.87099999,1.5089999,0,0,0,0,0,0.87099999,0.81300002,1.1610000,2.5539999,1.6260000,0,0,0.63900000,0,3.4830000,2.8450000,2.4960001,7.1409998,5.7480001,2.3800001,3.1930001,0,1.1610000,0,0,0,0,0,0,0,2.6129999,1.9160000,4.2379999,0.34799999,5.4569998,0,0,2.8450000,0,0,0,0,0,2.4960001,1.6260000,0,2.5539999,0,0,0,0,0,1.6260000,0,3.0190001,0,2.8450000,1.1030000,2.9030001,0,0,0,0.98699999,0,1.1610000,0.34799999,1.3930000,1.2770000,4.4120002,0,0,0,0,1.8580000,0,0.98699999])
DHW_Demand_Arr2=DHW_Demand_Arr1.round(decimals=3)
DHW_Demand_Arr=DHW_Demand_Arr2[0:timesteps]
DHW_Demand=m.Param(DHW_Demand_Arr,name='TWW_BED')
#4. electricity production from PV
PV_P_Arr1=np.array([0,0,0,0,0,0,0,0,0,0.057000000,0.14399999,0.30500001,0.13600001,0.28900000,0.22000000,0.0040000002,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.061999999,0.78899997,0.56300002,0.13600001,0.052999999,0.017000001,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.037000000,0.098999999,0.15000001,0.11200000,0,0.12600000,0.032000002,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0.0040000002,0.73600000,1.8250000,2.4020000,3.1870000,0.66500002,0.045000002,0,0,0,0,0,0,0,0,0,0,0,0,0])
PV_P_Arr2=PV_P_Arr1.round(decimals=3)
PV_P_Arr=PV_P_Arr2[0:timesteps]
PV_P=m.Param(PV_P_Arr,name='PV_P')
# Heat Pump "Bit" ist '1' during the Heating Season and '0' outside the heating Season to tell the Promgram that the Heat Pump may only be used during heating Season
HP_Bit_Arr1=np.array([1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1])
HP_Bit_Arr=HP_Bit_Arr1[0:timesteps]
HP_Bit=m.Param(HP_Bit_Arr,name='HP_Bit')
# Battery Storage
B_S = m.SV(0,lb=0)
B_S.FSTATUS=1
m.fix_initial(B_S,0)
B_S_Load = m.SV(0,lb=0) #Loading Battery
B_S_Recover = m.SV(0,lb=0) #Recover Energy from Batterie
eff_B_S = 0.95 #efficiency Battery
# Heat Storage
H_S = m.SV(0,lb=0)
H_S.FSTATUS=1
m.fix_initial(H_S,0)
H_S_Load = m.SV(lb=0) #Loading Heat Storage
H_S_Recover = m.SV(lb=0) #Recover Energy from Heat Storage
eff_H_S = 0.9 #efficiency Heat Storage
#Heat Pump
# Binary Variable for Heat Pump (it can either be turned on '1' or off '0')
P_HP_Binary=HP_Bit*m.sos1([0,1])
P_HP_Binary.STATUS = 1
#Electrical sizing of the Heat Pump
P_el_HP1= m.SV(0,lb=0)
P_el_HP1.FSTATUS=1
#Electrical Water Heater
EH=m.SV(0,lb=0)
EH.FSTATUS=1
# The Power of the Heat Pump multiplied with the Binary Variable gives the actual Output of the Heat Pump
P_el_HP=m.Intermediate(P_HP_Binary*P_el_HP1)
COP_HP=3.5 #COP of the Heat Pump
Q_HP=m.Intermediate(COP_HP*P_el_HP) # thermal Energy Output of the Heat Pump
# the objective of this Optimization ist to minimize the Cost for the Energy-System, scince you only Pay for the maximal Value of the Heat Pump, Energy Storages and the electrical Water Heater and not for the value at each timestep, I define a FV that describes the maximal Value of the Components
P_el_HP_max=m.FV(lb=0) #Heat Pump
P_el_HP_max.STATUS=1
H_S_max=m.FV(lb=0) #Heat Storage
H_S_max.STATUS=1
B_S_max=m.FV(lb=0) #Battery
B_S_max.STATUS=1
EH_max=m.FV(lb=0) #Electrical Water Heater
EH_max.STATUS=1
# We have energy Production from PV, there ist a possibility to give Energy thats not needed to the public Grid
I_Excess=m.Var(0,lb=0)
# In Case we have more Demand for Electrical Enery than Production from PV we have the possibility to get Energy from the public Grid
I_feed_out=m.SV(0,lb=0)
I_feed_out.FSTATUS=1
# Volume of the Heat Storage in m^3
Vol_HS=m.Intermediate((H_S_max*3600)/(1000*4.18*(35-10)))
# boundary conditions
m.Equations([PV_P +I_feed_out + B_S_Recover - P_el_HP - B_S_Load - I_Excess - EH == EL_Demand, #Energy Balance needs to satisfy the Demand
B_S.dt() == B_S_Load - B_S_Recover/eff_B_S, #Loading and Recovery of the Battery
B_S_Load * B_S_Recover == 0, #It is not allowed to Load and Recover at the same Time, at least one of both needs to be equal to '0' at each Timestep
P_el_HP*COP_HP + H_S_Recover - H_S_Load + EH == H_Demand + DHW_Demand, #The Demand of Heat and DHW needs to be satisfied at each timestep
H_S.dt() == H_S_Load - H_S_Recover/eff_H_S, #Loading and recovery of the Heat Storage
H_S_Load * H_S_Recover == 0, #It is not allowed to Load and Recover at the same Time, at least one of both needs to be equal to '0' at each Timestep
# The maximal Value of the Enery System Components is the Upper Bound for the Value at each time Step
P_el_HP1 <= P_el_HP_max,
P_el_HP1 >= 0.4*P_el_HP_max, # the Heat Pump is a variable speed heat Pump and has a minimal output of 40% of the nominal Power
H_S <= H_S_max,
B_S <= B_S_max,
EH <= EH_max,])
#Objective is to minimize the cost of the Energy System (the Cost of Components that only need to be bought once get divided by the number of timesteps)
Objective=(((P_el_HP_max*1918.4)*P_el_HP_max+(EH_max*50)+B_S_max*1664.9*B_S_max+(Vol_HS*2499.3*Vol_HS))/(20*timesteps)-0.05*I_Excess+0.35*(I_feed_out))
m.Minimize(Objective)
m.solve(disp=True)
#Print Results
print("Nominal Power of the Heat Pump=",max(P_el_HP),"kW")
print("maximum Capacity of the Heat Storage=",max(H_S),"kW")
print("Volume of the Heat Storage=", max(Vol_HS),"m^3")
print("maximum Capacity of the Battery", max(B_S),"kW")
print("Electricity from the Public Grid",sum(I_feed_out[0:timesteps-1]))
# Plot results
fig, axes = plt.subplots(6, 1, figsize=(5, 5.1), sharex=True)
axes = axes.ravel()
ax = axes[0]
ax.plot(t, EL_Demand, 'r-', label='Electrical Demand',lw=1)
ax.plot(t, PV_P,'b:', label='PV Production',lw=1) #z.B. Generator (haben wir aber in unserem Energiesystem nicht)
ax = axes[1]
ax.plot(t, EL_Demand, 'r-', label='Electrical Demand',lw=1)
ax.plot(t,I_feed_out, 'k--', label='Electricity from the public Grid',lw=1)
ax = axes[2]
ax.plot(t,B_S.value, 'k-', label='Battery Storage',lw=1)
ax.plot(t,B_S_Load,'g--',label='Battery Storage Loading',lw=1)
ax.plot(t,B_S_Recover,'b:',label='Battery Storage Recovery',lw=1) #lw=2 --> linewidth
ax = axes[3]
ax.plot(t,H_Demand, 'r-', label='Heat Demand',lw=1)
ax.plot(t, Q_HP.value,'b:',\
label='Thermal Production Heat Pump',lw=1)
ax = axes[4]
ax.plot(t,H_S, 'k-', label='Heat Storage',lw=1)
ax.plot(t,H_S_Load,'g--',label='Heat Storage Loading',lw=1)
ax.plot(t,H_S_Recover.value,'b:',\
label='Heat Storage Recovered Energy',lw=1)
ax = axes[5]
ax.plot(t,DHW_Demand, 'r-', label='Domestic Hot Water Demand',lw=1)
ax.plot(t, EH,'b:',\
label='Electrical Water Heater',lw=1)
for ax in axes:
ax.legend(loc='center left',\
bbox_to_anchor=(1,0.5),frameon=False)
ax.grid()
ax.set_xlim(0,len(t)-1)
plt.savefig('Results.png', dpi=600,\
bbox_inches = 'tight')
plt.show()