Skip to content

Commit

Permalink
Add Winners Code and Documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
pjbull committed Jun 12, 2018
1 parent b2fe069 commit b529407
Show file tree
Hide file tree
Showing 13 changed files with 3,271 additions and 0 deletions.
Binary file added 1st Place/Report-ORLab.pdf
Binary file not shown.
139 changes: 139 additions & 0 deletions 1st Place/battery_controller.py
@@ -0,0 +1,139 @@

from ortools.linear_solver import pywraplp

class BatteryContoller(object):
step = 960
def propose_state_of_charge(self,
site_id,
timestamp,
battery,
actual_previous_load,
actual_previous_pv_production,
price_buy,
price_sell,
load_forecast,
pv_forecast):


self.step -= 1
if (self.step == 1): return 0
if (self.step > 1): number_step = min(96, self.step)
#
price_buy = price_buy.tolist()
price_sell = price_sell.tolist()
load_forecast = load_forecast.tolist()
pv_forecast = pv_forecast.tolist()
#
energy = [None] * number_step

for i in range(number_step):
if (pv_forecast[i] >=50): energy[i] = load_forecast[i] - pv_forecast[i]
else: energy[i] = load_forecast[i]
#battery
capacity = battery.capacity
charging_efficiency = battery.charging_efficiency
discharging_efficiency = 1. / battery.discharging_efficiency
current = capacity * battery.current_charge
limit = battery.charging_power_limit
dis_limit = battery.discharging_power_limit
limit /= 4.
dis_limit /= 4.

# Ortools
solver = pywraplp.Solver("B", pywraplp.Solver.GLOP_LINEAR_PROGRAMMING)

#Variables: all are continous
charge = [solver.NumVar(0.0, limit, "c"+str(i)) for i in range(number_step)]
dis_charge = [solver.NumVar( dis_limit, 0.0, "d"+str(i)) for i in range(number_step)]
battery_power = [solver.NumVar(0.0, capacity, "b"+str(i)) for i in range(number_step+1)]
grid = [solver.NumVar(0.0, solver.infinity(), "g"+str(i)) for i in range(number_step)]

#Objective function
objective = solver.Objective()
for i in range(number_step):
objective.SetCoefficient(grid[i], price_buy[i] - price_sell[i])
objective.SetCoefficient(charge[i], price_sell[i] + price_buy[i] / 1000.)
objective.SetCoefficient(dis_charge[i], price_sell[i])
objective.SetMinimization()

# 3 Constraints
c_grid = [None] * number_step
c_power = [None] * (number_step+1)

# first constraint
c_power[0] = solver.Constraint(current, current)
c_power[0].SetCoefficient(battery_power[0], 1)

for i in range(0, number_step):
# second constraint
c_grid[i] = solver.Constraint(energy[i], solver.infinity())
c_grid[i].SetCoefficient(grid[i], 1)
c_grid[i].SetCoefficient(charge[i], -1)
c_grid[i].SetCoefficient(dis_charge[i], -1)
# third constraint
c_power[i+1] = solver.Constraint( 0, 0)
c_power[i+1].SetCoefficient(charge[i], charging_efficiency)
c_power[i+1].SetCoefficient(dis_charge[i], discharging_efficiency)
c_power[i+1].SetCoefficient(battery_power[i], 1)
c_power[i+1].SetCoefficient(battery_power[i+1], -1)

#solve the model
solver.Solve()

if ((energy[0] < 0) & (dis_charge[0].solution_value() >= 0)):
n = 0
first = -limit
mid = 0

sum_charge = charge[0].solution_value()
last = energy[0]
for n in range(1, number_step):
if((energy[n] > 0) | (dis_charge[n].solution_value() < 0) | (price_sell[n] != price_sell[n-1])):
break
last = min(last, energy[n])
sum_charge += charge[n].solution_value()
if (sum_charge <= 0.):
return battery_power[1].solution_value() / capacity
def tinh(X):
res = 0
for i in range(n):
res += min(limit, max(-X - energy[i], 0.))
if (res >= sum_charge): return True
return False
last = 2 - last
# binary search
while (last - first > 1):
mid = (first + last) / 2
if (tinh(mid)): first = mid
else: last = mid
return (current + min(limit, max(-first - energy[0] , 0)) * charging_efficiency) / capacity

if ((energy[0] > 0) & (charge[0].solution_value() <=0)):
n = 0
first = dis_limit
mid = 0
sum_discharge = dis_charge[0].solution_value()
last = energy[0]
for n in range(1, number_step):
if ((energy[n] < 0) | (charge[n].solution_value() > 0) | (price_sell[n] != price_sell[n-1]) | (price_buy[n] != price_buy[n-1])):
break
last = max(last, energy[n])
sum_discharge += dis_charge[n].solution_value()
if (sum_discharge >= 0.):
return battery_power[1].solution_value() / capacity

def tinh2(X):
res = 0
for i in range(n):
res += max(dis_limit, min(X - energy[i], 0))
if (res <= sum_discharge): return True
return False
last += 2

# binary search
while (last - first > 1):
mid = (first + last) / 2
if (tinh2(mid)): first = mid
else: last = mid
return (current + max(dis_limit, min(first - energy[0], 0)) * discharging_efficiency) / capacity
return battery_power[1].solution_value() / capacity
30 changes: 30 additions & 0 deletions 1st Place/readme
@@ -0,0 +1,30 @@
1. To run the code, please install Ortools - version 6.7.4957

2. The linear programming model is as follows:

Variables:
charge[i]: charged energy of battery at step i (charge[i] >=0)
dis_charge[i]: discharged energy of battery at step i (dis_charge[i] <=0)
battery_power[i]: power of battery at step i (0 <= battery_power[i] <= capacity)
grid[i]: energy bought from grid at step i (grid[i] > 0)

Objective
Minimize Z =∑_(i=0)^95 (grid[i]*(price_buy[i] - price_sell[i])+ charge[i]*(price_sell[i] + price_buy[i]/1000.)+dis_charge[i] * price_sell[i])

Constraints
(1) Initialize the power of battery
battery_power[0] = capacity * battery.current_charge

(2) The demand of building must be satisfied
grid[i] – (charge[i] + dis_charge[i]) >= load_forecast[i] - pv_forecast[i]

(3) Relationship of battery power between two consecutive steps
battery_power[i] + charge[i] * charging_efficiency + dis_charge[i]/discharging_efficiency = battery_power[i+1]

(4) Charged energy must less than charging power limit
charge[i] <= charging_power_limit * (1/4.)

(5) Discharged energy must be less than discharging power limit
dis_charge[i] >= discharging_power_limit* (1/4.)


Binary file not shown.
13 changes: 13 additions & 0 deletions 2nd Place/README.md
@@ -0,0 +1,13 @@
# Final solution for Power Laws: Optimizing Demand-side Strategies
https://www.drivendata.org/competitions/53/optimize-photovoltaic-battery/

## Files
battery_controller.py
This is the script used on the simulation.
assets/coefs.json
This coeficients are used by the battery_controller to improve the accuracy
of the forecasts.
forecast_accuracy_optimization.ipynb
This notebook shows how the coeficients are computed using keras and the training data.
requirements.txt
A list with the libraries needed to run the scripts.
1 change: 1 addition & 0 deletions 2nd Place/assets/coefs.json
@@ -0,0 +1 @@
{"1": {"2014-08-28 18:15:00": [0.9381, 0.0214, 0.0407], "2015-05-02 18:15:00": [0.9065, 0.0885, 0.0051], "2015-08-25 18:15:00": [0.9283, 0.054, 0.018], "2015-10-24 18:15:00": [0.9346, 0.0466, 0.019], "2016-02-18 18:15:00": [0.8491, 0.2042, -0.0533], "2016-04-18 18:15:00": [0.9359, 0.0538, 0.01], "2017-01-31 18:15:00": [0.8547, 0.1986, -0.0536], "2017-04-01 18:15:00": [0.8857, 0.1422, -0.0276], "2017-05-31 18:15:00": [0.8517, 0.2163, -0.068]}, "10": {"2013-02-21 00:45:00": [0.8904, 0.1622, -0.0529], "2013-04-22 00:45:00": [0.8763, 0.183, -0.0594], "2013-06-21 00:45:00": [0.8848, 0.1923, -0.0773], "2013-08-20 00:45:00": [0.8938, 0.1603, -0.054], "2013-10-19 00:45:00": [0.8906, 0.1602, -0.051], "2013-12-18 00:45:00": [0.8906, 0.1578, -0.0484], "2014-02-16 00:45:00": [0.8735, 0.188, -0.0615], "2014-04-17 00:45:00": [0.8679, 0.1936, -0.0611], "2014-06-16 00:45:00": [0.8963, 0.1503, -0.0467]}, "11": {"2015-07-22 01:45:00": [0.783, 0.2983, -0.0732], "2015-09-24 01:45:00": [0.8083, 0.274, -0.0795], "2016-01-20 01:45:00": [0.7746, 0.3171, -0.0925], "2016-03-20 01:45:00": [0.7362, 0.3798, -0.1221], "2016-05-19 01:45:00": [0.764, 0.3311, -0.1014], "2016-09-25 01:45:00": [0.6083, 0.5763, -0.1913], "2016-11-24 01:45:00": [0.7374, 0.3597, -0.1045]}, "12": {"2014-08-21 13:00:00": [0.675, 0.4098, -0.0897], "2014-10-20 13:00:00": [0.7597, 0.3138, -0.0765], "2014-12-19 13:00:00": [0.6879, 0.3834, -0.0806], "2015-02-17 13:00:00": [0.6935, 0.3718, -0.0745], "2015-04-18 13:00:00": [0.7009, 0.3527, -0.0627], "2015-06-17 13:00:00": [0.7205, 0.3374, -0.0677], "2015-08-19 13:00:00": [0.6952, 0.3752, -0.0803], "2015-10-18 13:00:00": [0.6895, 0.3785, -0.0782], "2016-01-17 13:00:00": [0.7134, 0.35, -0.0733], "2016-03-17 13:00:00": [0.7141, 0.3504, -0.0743], "2016-05-16 13:00:00": [0.729, 0.3321, -0.0713], "2016-07-15 13:00:00": [0.6097, 0.4807, -0.1016], "2016-09-13 13:00:00": [0.718, 0.3427, -0.0714], "2017-01-03 13:00:00": [0.9443, -0.0313, 0.069]}, "2": {"2014-12-17 16:30:00": [0.7644, 0.2905, -0.0548], "2015-02-15 16:30:00": [0.7429, 0.3615, -0.1047], "2015-07-19 16:30:00": [0.7733, 0.3272, -0.102], "2015-10-30 16:30:00": [0.8375, 0.2293, -0.0675], "2016-01-10 16:30:00": [0.8681, 0.1689, -0.038]}, "29": {"2013-02-20 23:45:00": [0.88, 0.1842, -0.0639], "2013-04-21 23:45:00": [0.8695, 0.189, -0.0569], "2013-06-20 23:45:00": [0.9052, 0.0647, 0.0338], "2013-08-19 23:45:00": [0.8606, 0.1644, -0.0235], "2013-10-28 23:45:00": [0.8489, 0.1597, -0.0053], "2014-02-23 23:45:00": [0.8466, 0.1455, 0.0095], "2014-04-24 23:45:00": [0.9173, 0.0533, 0.0327], "2014-06-23 23:45:00": [0.8612, 0.1221, 0.0187], "2014-08-22 23:45:00": [0.8708, 0.1086, 0.0235], "2014-12-17 23:45:00": [0.8809, 0.1026, 0.0207], "2015-02-15 23:45:00": [0.9139, 0.0586, 0.0325], "2015-06-13 23:45:00": [0.888, 0.098, 0.0182], "2015-08-12 23:45:00": [0.8961, 0.0858, 0.0227], "2015-10-11 23:45:00": [0.9055, 0.0662, 0.0323], "2015-12-10 23:45:00": [0.8622, 0.1334, 0.0077], "2016-04-29 23:45:00": [0.8706, 0.0949, 0.0384], "2016-11-13 23:45:00": [0.8678, 0.125, 0.0119], "2017-01-15 23:45:00": [0.8028, 0.2112, -0.0082]}, "3": {"2015-03-12 15:15:00": [0.8655, 0.1445, -0.0117], "2015-06-09 15:15:00": [0.9118, 0.1005, -0.0121], "2015-08-08 15:15:00": [0.8902, 0.129, -0.0209], "2015-10-07 15:15:00": [0.8874, 0.1368, -0.0261], "2016-01-10 15:15:00": [0.8996, 0.1126, -0.0144], "2016-03-10 15:15:00": [0.8828, 0.1456, -0.0308], "2016-05-09 15:15:00": [0.8962, 0.1199, -0.0174], "2016-07-08 15:15:00": [0.9023, 0.1175, -0.0223], "2017-01-31 15:15:00": [0.8877, 0.1393, -0.0291], "2017-04-01 15:15:00": [0.894, 0.1268, -0.0226], "2017-05-31 15:15:00": [0.8778, 0.1561, -0.0365], "2017-07-30 15:15:00": [0.8661, 0.1799, -0.0473], "2017-09-28 15:15:00": [0.8193, 0.175, 0.0037]}, "31": {"2015-05-03 00:45:00": [0.7651, 0.2928, -0.0606], "2015-08-26 00:45:00": [0.7591, 0.2843, -0.0461], "2015-10-25 00:45:00": [0.798, 0.2327, -0.0336], "2016-02-19 00:45:00": [0.7852, 0.2639, -0.0519], "2016-04-19 00:45:00": [0.798, 0.2511, -0.0512], "2017-02-01 00:45:00": [0.7925, 0.2645, -0.0595], "2017-04-02 00:45:00": [0.7898, 0.2659, -0.0566], "2017-06-01 00:45:00": [0.8257, 0.2047, -0.033], "2017-07-31 00:45:00": [0.8051, 0.2422, -0.0503]}, "32": {"2015-02-11 14:15:00": [0.9355, 0.0984, -0.0287], "2015-05-27 14:15:00": [0.875, 0.1634, -0.0324], "2015-07-26 14:15:00": [0.859, 0.1677, -0.0218], "2015-10-31 14:15:00": [0.9429, 0.0592, 0.0035]}, "4": {"2015-03-06 12:15:00": [0.8722, 0.0546, 0.0725], "2015-05-05 12:15:00": [0.9083, 0.0428, 0.0492], "2015-07-04 12:15:00": [0.8915, 0.0724, 0.0352], "2015-09-02 12:15:00": [0.8907, 0.0776, 0.0297], "2015-11-01 12:15:00": [0.8888, 0.0767, 0.0328], "2016-01-10 12:15:00": [0.8847, 0.0705, 0.0422], "2016-03-10 12:15:00": [0.8869, 0.0658, 0.0445], "2016-07-05 12:15:00": [0.8914, 0.0739, 0.0318], "2017-01-31 12:15:00": [0.8865, 0.0758, 0.0355], "2017-04-16 12:15:00": [0.8921, 0.0657, 0.0422], "2017-06-15 12:15:00": [0.8889, 0.0453, 0.0662], "2017-08-14 12:15:00": [0.89, 0.0751, 0.0389]}, "7": {"2015-06-20 16:15:00": [0.7893, 0.2725, -0.0613], "2015-08-19 16:15:00": [0.7939, 0.2637, -0.0573], "2015-10-18 16:15:00": [0.789, 0.2689, -0.0577], "2015-12-17 16:15:00": [0.8158, 0.2297, -0.0432], "2016-02-15 16:15:00": [0.8156, 0.2277, -0.0426], "2016-04-15 16:15:00": [0.7751, 0.2884, -0.0616], "2016-06-14 16:15:00": [0.8339, 0.2103, -0.0411], "2016-08-13 16:15:00": [0.8117, 0.2424, -0.0518], "2016-10-12 16:15:00": [0.8781, 0.1451, -0.0204]}}

0 comments on commit b529407

Please sign in to comment.