consumption.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. import pandas as pd
  2. import numpy as np
  3. import cvxpy as cp
  4. from statsmodels.formula.api import ols
  5. from typing import Union
  6. class ConsumptionModel:
  7. def __init__(self):
  8. pass
  9. def get(self):
  10. pass
  11. class ThermoModel(ConsumptionModel):
  12. def __init__(self):
  13. pass
  14. def get(temperatures: np.ndarray):
  15. pass
  16. class FittedConsumptionModel(ConsumptionModel):
  17. def __init__(self, yearly_total: float):
  18. self.yearly_total = yearly_total
  19. self.fit()
  20. def get(self, times: Union[pd.Series, np.ndarray]):
  21. # compute the load for the desired timestamps
  22. times = pd.DataFrame({'time': times}).set_index("time")
  23. times.index = pd.to_datetime(times.index, utc=True)
  24. times['h'] = ((times.index - self.time_reference
  25. ).total_seconds()/3600).astype(int)
  26. times['Y'] = 1
  27. for i, f in enumerate(self.frequencies):
  28. times[f'c_{i+1}'] = (times['h']*f*2*np.pi).apply(np.cos)
  29. times[f's_{i+1}'] = (times['h']*f*2*np.pi).apply(np.sin)
  30. curve = self.fit_results.predict(times).values
  31. return curve
  32. def fit(self):
  33. consumption = pd.read_csv("data/consommation-quotidienne-brute.csv", sep=";")
  34. consumption['time'] = pd.to_datetime(
  35. consumption["Date - Heure"].str.replace(":", ""), format="%Y-%m-%dT%H%M%S%z", utc=True)
  36. consumption.set_index("time", inplace=True)
  37. hourly = consumption.resample('1H').mean()
  38. self.time_reference = hourly.index.min()
  39. hourly['h'] = ((hourly.index - self.time_reference
  40. ).total_seconds()/3600).astype(int)
  41. hourly['Y'] = hourly.index.year-hourly.index.year.min()
  42. # generate fourier components
  43. self.frequencies = list((1/(365.25*24))*np.arange(1, 13)) + \
  44. list((1/(24*7)) * np.arange(1, 7)) + \
  45. list((1/24) * np.arange(1, 12))
  46. components = []
  47. for i, f in enumerate(self.frequencies):
  48. hourly[f'c_{i+1}'] = (hourly['h']*f*2*np.pi).apply(np.cos)
  49. hourly[f's_{i+1}'] = (hourly['h']*f*2*np.pi).apply(np.sin)
  50. components += [f'c_{i+1}', f's_{i+1}']
  51. hourly.rename(
  52. columns={'Consommation brute électricité (MW) - RTE': 'conso'}, inplace=True)
  53. hourly["conso"] /= 1000
  54. # fit load curve to fourier components
  55. model = ols("conso ~ " + " + ".join(components)+" + C(Y)", data=hourly)
  56. self.fit_results = model.fit()
  57. # normalize according to the desired total yearly consumption
  58. intercept = self.fit_results.params[0]+self.fit_results.params[1]
  59. self.fit_results.params *= self.yearly_total/(intercept*365.25*24)
  60. class ConsumptionFlexibilityModel:
  61. def __init__(self, flexibility_power: float, flexibility_time: int):
  62. self.flexibility_power = flexibility_power
  63. self.flexibility_time = flexibility_time
  64. def run(self, load: np.ndarray, supply: np.ndarray):
  65. from functools import reduce
  66. T = len(supply)
  67. tau = self.flexibility_time
  68. h = cp.Variable((T, tau+1))
  69. constraints = [
  70. h >= 0,
  71. h <= 1,
  72. h[:, 0] >= 1-flexibility_power/load, # bound on the fraction of the demand at any time that can be postponed
  73. cp.multiply(load, cp.sum(h, axis=1))-load <= flexibility_power, # total demand conservation
  74. ] + [
  75. reduce(lambda x, y: x+y, [h[t-l, l] for l in range(tau)]) == 1
  76. for t in np.arange(flexibility_time, T)
  77. ]
  78. prob = cp.Problem(
  79. cp.Minimize(
  80. cp.sum(cp.pos(cp.multiply(load, cp.sum(h, axis=1))-supply))
  81. ),
  82. constraints
  83. )
  84. prob.solve(verbose=True, solver=cp.ECOS, max_iters=300)
  85. hb = np.array(h.value)
  86. return load*np.sum(hb, axis=1)