Daniel Varga commited on
Commit
7f927c0
1 Parent(s): 1fee7d7

random decider for debugging

Browse files
Files changed (4) hide show
  1. v2/architecture.py +66 -15
  2. v2/decider.py +39 -1
  3. v2/evolution_strategies.py +4 -16
  4. v2/supplier.py +1 -1
v2/architecture.py CHANGED
@@ -5,7 +5,7 @@ import time
5
  import matplotlib.pyplot as plt
6
 
7
  # it's really just a network pricing model
8
- from decider import Decider, Decision
9
  from supplier import Supplier, precalculate_supplier
10
  from data_processing import read_datasets, add_production_field, interpolate_and_join, SolarParameters
11
  from bess import BatteryModel
@@ -27,8 +27,8 @@ def add_dummy_predictions(all_data_with_predictions):
27
  all_data_with_predictions['Consumption_prediction'] = all_data_with_predictions['Consumption'].shift(periods=cons_shift)
28
  all_data_with_predictions['Production_prediction'] = all_data_with_predictions['Production'].shift(periods=prod_shift)
29
  # we predict zero before we have data, no big deal:
30
- all_data_with_predictions['Consumption_prediction'][:cons_shift] = 0
31
- all_data_with_predictions['Production_prediction'][:prod_shift] = 0
32
 
33
 
34
  # even mock-er class than usual.
@@ -185,7 +185,7 @@ def simulator(battery_model, prod_cons, decider):
185
  '''
186
  consumption_charge_series = consumption_fees_np * consumption_from_network_pandas_series.to_numpy()
187
  step_in_hour = consumption_from_network_pandas_series.index.freq.n / 60 # [hour], the length of a time step.
188
- fifteen_minute_demands_in_kwh = consumption_from_network_pandas_series.resample('15T').sum() * step_in_hour
189
  fifteen_minute_surdemands_in_kwh = (fifteen_minute_demands_in_kwh - decider.precalculated_supplier.peak_demand).clip(lower=0)
190
  demand_charges = fifteen_minute_surdemands_in_kwh * decider.precalculated_supplier.surcharge_per_kwh
191
  total_network_fee = consumption_charge_series.sum() + demand_charges.sum()
@@ -210,23 +210,62 @@ def simulator(battery_model, prod_cons, decider):
210
 
211
 
212
 
213
- def optimizer(battery_model, all_data_with_predictions, precalculated_supplier):
 
 
 
214
  def objective_function(params):
215
  print("Simulating with parameters", params)
216
- # TODO params completely ignored right now.
217
- decider = Decider(params, precalculated_supplier)
218
  t = time.perf_counter()
219
  results, total_network_fee = simulator(battery_model, all_data_with_predictions, decider)
 
220
  return total_network_fee
221
 
222
  def clipper_function(params):
223
- return Decider.clip_params(params)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
224
 
225
- init_mean, init_scale = Decider.initial_params()
226
- best_params = evolution_strategies_optimizer(objective_function, clipper_function, init_mean=init_mean, init_scale=init_scale)
227
 
228
 
229
  def main():
 
 
230
  supplier = Supplier(price=100) # Ft/kWh
231
  # nine-to-five increased price.
232
  supplier.set_price_for_daily_interval(9, 17, 150)
@@ -242,11 +281,17 @@ def main():
242
  add_production_field(met_2021_data, solar_parameters)
243
  all_data = interpolate_and_join(met_2021_data, cons_2021_data)
244
 
245
- print("Working with", solar_parameters.solar_cell_num, "solar cells, that's a maximum production of", all_data['Production'].max(), "kW.")
246
-
247
  time_interval_min = all_data.index.freq.n
248
  time_interval_h = time_interval_min / 60
249
 
 
 
 
 
 
 
 
 
250
  all_data_with_predictions = all_data.copy()
251
  add_dummy_predictions(all_data_with_predictions)
252
 
@@ -258,11 +303,13 @@ def main():
258
 
259
  battery_model = BatteryModel(capacity_Ah=600, time_interval_h=time_interval_h)
260
 
 
 
261
  # TODO this is super unfortunate:
262
  # Consumption_fees travels via all_data_with_predictions,
263
  # peak_demand and surcharge_per_kwh travels via precalculated_supplier of decider.
264
- decider_init_mean, decider_init_scale = Decider.initial_params()
265
- decider = Decider(decider_init_mean, precalculated_supplier)
266
 
267
  t = time.perf_counter()
268
  results, total_network_fee = simulator(battery_model, all_data_with_predictions, decider)
@@ -273,7 +320,11 @@ def main():
273
  plt.title('soc_series')
274
  plt.show()
275
 
276
- optimizer(battery_model, all_data_with_predictions, precalculated_supplier)
 
 
 
 
277
 
278
  if __name__ == '__main__':
279
  main()
 
5
  import matplotlib.pyplot as plt
6
 
7
  # it's really just a network pricing model
8
+ from decider import Decision, Decider, RandomDecider
9
  from supplier import Supplier, precalculate_supplier
10
  from data_processing import read_datasets, add_production_field, interpolate_and_join, SolarParameters
11
  from bess import BatteryModel
 
27
  all_data_with_predictions['Consumption_prediction'] = all_data_with_predictions['Consumption'].shift(periods=cons_shift)
28
  all_data_with_predictions['Production_prediction'] = all_data_with_predictions['Production'].shift(periods=prod_shift)
29
  # we predict zero before we have data, no big deal:
30
+ all_data_with_predictions.loc[all_data_with_predictions.index[:cons_shift], 'Consumption_prediction'] = 0
31
+ all_data_with_predictions.loc[all_data_with_predictions.index[:prod_shift], 'Production_prediction'] = 0
32
 
33
 
34
  # even mock-er class than usual.
 
185
  '''
186
  consumption_charge_series = consumption_fees_np * consumption_from_network_pandas_series.to_numpy()
187
  step_in_hour = consumption_from_network_pandas_series.index.freq.n / 60 # [hour], the length of a time step.
188
+ fifteen_minute_demands_in_kwh = consumption_from_network_pandas_series.resample('15min').sum() * step_in_hour
189
  fifteen_minute_surdemands_in_kwh = (fifteen_minute_demands_in_kwh - decider.precalculated_supplier.peak_demand).clip(lower=0)
190
  demand_charges = fifteen_minute_surdemands_in_kwh * decider.precalculated_supplier.surcharge_per_kwh
191
  total_network_fee = consumption_charge_series.sum() + demand_charges.sum()
 
210
 
211
 
212
 
213
+ def optimizer(decider_class, battery_model, all_data_with_predictions, precalculated_supplier):
214
+ number_of_generations = 3
215
+ population_size = 100
216
+ collected_loss_values = []
217
  def objective_function(params):
218
  print("Simulating with parameters", params)
219
+ decider = decider_class(params, precalculated_supplier)
 
220
  t = time.perf_counter()
221
  results, total_network_fee = simulator(battery_model, all_data_with_predictions, decider)
222
+ collected_loss_values.append((params, total_network_fee))
223
  return total_network_fee
224
 
225
  def clipper_function(params):
226
+ return decider_class.clip_params(params)
227
+
228
+ init_mean, init_scale = decider_class.initial_params()
229
+ best_params = evolution_strategies_optimizer(objective_function, clipper_function,
230
+ init_mean=init_mean, init_scale=init_scale,
231
+ number_of_generations=number_of_generations,
232
+ population_size=population_size)
233
+ return best_params, collected_loss_values
234
+
235
+
236
+ def visualize_collected_loss_values(collected_loss_values):
237
+ all_params = []
238
+ losses = []
239
+ for row in collected_loss_values:
240
+ params, loss = row
241
+ all_params.append(params)
242
+ losses.append(loss)
243
+
244
+ all_params = np.array(all_params)
245
+ losses = np.array(losses)
246
+
247
+ # losses -= losses.min() ; losses /= losses.max()
248
+
249
+ plt.scatter(all_params[:, 0], all_params[:, 1], c=range(len(all_params)))
250
+ plt.show()
251
+
252
+
253
+ from mpl_toolkits.mplot3d import Axes3D
254
+
255
+ fig = plt.figure()
256
+
257
+ # Add a 3D subplot
258
+ ax = fig.add_subplot(111, projection='3d')
259
+
260
+ # Scatter plot
261
+ ax.scatter(all_params[:, 0], all_params[:, 1], losses)
262
+ plt.show()
263
 
 
 
264
 
265
 
266
  def main():
267
+ np.random.seed(1)
268
+
269
  supplier = Supplier(price=100) # Ft/kWh
270
  # nine-to-five increased price.
271
  supplier.set_price_for_daily_interval(9, 17, 150)
 
281
  add_production_field(met_2021_data, solar_parameters)
282
  all_data = interpolate_and_join(met_2021_data, cons_2021_data)
283
 
 
 
284
  time_interval_min = all_data.index.freq.n
285
  time_interval_h = time_interval_min / 60
286
 
287
+ # for faster testing:
288
+ DATASET_TRUNCATED_SIZE = 10000
289
+ if DATASET_TRUNCATED_SIZE is not None:
290
+ print("Truncating dataset to", DATASET_TRUNCATED_SIZE, "datapoints, that is", DATASET_TRUNCATED_SIZE * time_interval_h / 24, "days")
291
+ all_data = all_data.iloc[:DATASET_TRUNCATED_SIZE]
292
+
293
+ print("Working with", solar_parameters.solar_cell_num, "solar cells, that's a maximum production of", all_data['Production'].max(), "kW.")
294
+
295
  all_data_with_predictions = all_data.copy()
296
  add_dummy_predictions(all_data_with_predictions)
297
 
 
303
 
304
  battery_model = BatteryModel(capacity_Ah=600, time_interval_h=time_interval_h)
305
 
306
+ decider_class = RandomDecider
307
+
308
  # TODO this is super unfortunate:
309
  # Consumption_fees travels via all_data_with_predictions,
310
  # peak_demand and surcharge_per_kwh travels via precalculated_supplier of decider.
311
+ decider_init_mean, decider_init_scale = decider_class.initial_params()
312
+ decider = decider_class(decider_init_mean, precalculated_supplier)
313
 
314
  t = time.perf_counter()
315
  results, total_network_fee = simulator(battery_model, all_data_with_predictions, decider)
 
320
  plt.title('soc_series')
321
  plt.show()
322
 
323
+ best_params, collected_loss_values = optimizer(decider_class, battery_model, all_data_with_predictions, precalculated_supplier)
324
+ visualize_collected_loss_values(collected_loss_values)
325
+
326
+ decider = decider_class(best_params, precalculated_supplier)
327
+ results, total_network_fee = simulator(battery_model, all_data_with_predictions, decider)
328
 
329
  if __name__ == '__main__':
330
  main()
v2/decider.py CHANGED
@@ -25,12 +25,50 @@ class Decision(IntEnum):
25
  NETWORK_CHARGE = 2
26
 
27
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  # mock class as usual
29
  # output_window_size is not yet used, always decides one timestep.
30
  class Decider:
31
  def __init__(self, params, precalculated_supplier):
32
  self.input_window_size = STEPS_PER_HOUR * 24 # day long window.
33
- self.random_seed = 0
34
  self.precalculated_supplier = precalculated_supplier
35
  assert params.shape == (2, )
36
  # param_1 is how many minutes do we look ahead to decide if there's
 
25
  NETWORK_CHARGE = 2
26
 
27
 
28
+ class RandomDecider:
29
+ def __init__(self, params, precalculated_supplier):
30
+ self.input_window_size = STEPS_PER_HOUR * 24 # day long window.
31
+ self.precalculated_supplier = precalculated_supplier
32
+ assert params.shape == (2, )
33
+ # param_1 is prob of choosing PASSIVE
34
+ # param_2 is prob of choosing NETWORK_CHARGE
35
+ self.passive_prob, self.network_prob = params
36
+
37
+ def decide(self, prod_pred, cons_pred, fees, battery_model):
38
+ r = np.random.rand()
39
+ if r < self.passive_prob:
40
+ return Decision.PASSIVE
41
+ elif r < self.passive_prob + self.network_prob:
42
+ return Decision.NETWORK_CHARGE
43
+ else:
44
+ return Decision.DISCHARGE
45
+
46
+ @staticmethod
47
+ def clip_params(params):
48
+ assert params.shape == (2, )
49
+ p1, p2 = params
50
+ p1 = max((0, p1))
51
+ p2 = max((0, p2))
52
+ s = p1 + p2
53
+ if s > 1:
54
+ p1 /= s
55
+ p2 /= s
56
+ return np.array([p1, p2])
57
+
58
+
59
+ @staticmethod
60
+ def initial_params():
61
+ init_mean = np.array([1/3, 1/3])
62
+ init_scale = np.array([1.0, 1.0])
63
+ return init_mean, init_scale
64
+
65
+
66
+
67
  # mock class as usual
68
  # output_window_size is not yet used, always decides one timestep.
69
  class Decider:
70
  def __init__(self, params, precalculated_supplier):
71
  self.input_window_size = STEPS_PER_HOUR * 24 # day long window.
 
72
  self.precalculated_supplier = precalculated_supplier
73
  assert params.shape == (2, )
74
  # param_1 is how many minutes do we look ahead to decide if there's
v2/evolution_strategies.py CHANGED
@@ -11,10 +11,11 @@ def clip_params(population, clipper_function):
11
  population[i] = clipper_function(individual)
12
 
13
 
14
- def evolution_strategies_optimizer(objective_function, clipper_function, init_mean, init_scale):
 
 
 
15
  # Initialize parameters
16
- population_size = 100
17
- number_of_generations = 30
18
  mutation_scale = 0.05
19
  selection_ratio = 0.5
20
  selected_size = int(population_size * selection_ratio)
@@ -44,19 +45,6 @@ def evolution_strategies_optimizer(objective_function, clipper_function, init_me
44
  best_index = np.argmin(fitness)
45
  best_solution = population[best_index]
46
  print(f"Generation {generation + 1}: Best Fitness = {best_fitness}", f"Best solution so far: {best_solution}")
47
- if DO_VIS:
48
- plt.scatter(population[:, 0], population[:, 1])
49
- plt.xlim(-5, 5)
50
- plt.ylim(-5, 5)
51
- x = np.linspace(-5, 5, 100)
52
- y = np.linspace(-5, 5, 100)
53
- XY = np.stack(np.meshgrid(x, y), axis=-1)
54
- print(XY.shape)
55
-
56
- # Defining the function (x - 3)**2 + (y + 2)**2
57
- Z = toy_objective_function(XY)
58
- contour = plt.contour(XY[..., 0], XY[..., 1], Z, levels=50)
59
- plt.show()
60
 
61
 
62
  # Best solution
 
11
  population[i] = clipper_function(individual)
12
 
13
 
14
+ def evolution_strategies_optimizer(objective_function, clipper_function,
15
+ init_mean, init_scale,
16
+ number_of_generations,
17
+ population_size):
18
  # Initialize parameters
 
 
19
  mutation_scale = 0.05
20
  selection_ratio = 0.5
21
  selected_size = int(population_size * selection_ratio)
 
45
  best_index = np.argmin(fitness)
46
  best_solution = population[best_index]
47
  print(f"Generation {generation + 1}: Best Fitness = {best_fitness}", f"Best solution so far: {best_solution}")
 
 
 
 
 
 
 
 
 
 
 
 
 
48
 
49
 
50
  # Best solution
v2/supplier.py CHANGED
@@ -71,7 +71,7 @@ class Supplier:
71
  assert 15 % demand_series.index.freq.n == 0
72
  time_steps_per_demand_charge_evaluation = 15 // demand_series.index.freq.n
73
  # fifteen_minute_peaks [kW] tells the maximum demand in a 15 minutes timeframe:
74
- fifteen_minute_demands_in_kwh = demand_series.resample('15T').sum() * step_in_hour
75
  demand_charges = pd.Series([self.demand_charge(demand_in_kwh) for demand_in_kwh in fifteen_minute_demands_in_kwh], index=fifteen_minute_demands_in_kwh.index)
76
  total_demand_charge = sum(demand_charges)
77
  total_charge = consumption_charge + total_demand_charge
 
71
  assert 15 % demand_series.index.freq.n == 0
72
  time_steps_per_demand_charge_evaluation = 15 // demand_series.index.freq.n
73
  # fifteen_minute_peaks [kW] tells the maximum demand in a 15 minutes timeframe:
74
+ fifteen_minute_demands_in_kwh = demand_series.resample('15min').sum() * step_in_hour
75
  demand_charges = pd.Series([self.demand_charge(demand_in_kwh) for demand_in_kwh in fifteen_minute_demands_in_kwh], index=fifteen_minute_demands_in_kwh.index)
76
  total_demand_charge = sum(demand_charges)
77
  total_charge = consumption_charge + total_demand_charge