Daniel Varga commited on
Commit
83ff599
1 Parent(s): 3705ebb
v2/architecture.py CHANGED
@@ -95,6 +95,8 @@ def simulator(battery_model, prod_cons, decider):
95
 
96
  discarded_production_series = [] # solar power thrown away
97
 
 
 
98
  # 1 is not nominal but targeted (healthy) maximum charge.
99
  # we start with an empty battery, but not emptier than what's healthy for the batteries.
100
 
@@ -123,6 +125,7 @@ def simulator(battery_model, prod_cons, decider):
123
  cons_prediction = demand_prediction_np[i: i + decider.input_window_size]
124
  consumption_fees = consumption_fees_np[i: i + decider.input_window_size]
125
  decision = decider.decide(prod_prediction, cons_prediction, consumption_fees, battery_model)
 
126
 
127
  production_used_to_charge = 0
128
  if unsatisfied_demand >= remaining_production:
@@ -169,6 +172,8 @@ def simulator(battery_model, prod_cons, decider):
169
  consumption_from_network_series = np.array(consumption_from_network_series)
170
  consumption_from_bess_series = np.array(consumption_from_bess_series)
171
  discarded_production_series = np.array(discarded_production_series)
 
 
172
 
173
  consumption_from_network_pandas_series = pd.Series(consumption_from_network_series, index=prod_cons.index)
174
 
@@ -197,10 +202,10 @@ def simulator(battery_model, prod_cons, decider):
197
  'consumption_from_network_to_bess_series': consumption_from_network_to_bess_series,
198
  'discarded_production': discarded_production_series,
199
  'Consumption': prod_cons['Consumption'],
200
- 'Production': prod_cons['Production']
 
201
  })
202
  results = results.set_index(prod_cons.index)
203
- # results['soc_series'].plot() ; plt.show() ; exit()
204
  return results, total_network_fee
205
 
206
 
 
95
 
96
  discarded_production_series = [] # solar power thrown away
97
 
98
+ decisions = []
99
+
100
  # 1 is not nominal but targeted (healthy) maximum charge.
101
  # we start with an empty battery, but not emptier than what's healthy for the batteries.
102
 
 
125
  cons_prediction = demand_prediction_np[i: i + decider.input_window_size]
126
  consumption_fees = consumption_fees_np[i: i + decider.input_window_size]
127
  decision = decider.decide(prod_prediction, cons_prediction, consumption_fees, battery_model)
128
+ decisions.append(decision)
129
 
130
  production_used_to_charge = 0
131
  if unsatisfied_demand >= remaining_production:
 
172
  consumption_from_network_series = np.array(consumption_from_network_series)
173
  consumption_from_bess_series = np.array(consumption_from_bess_series)
174
  discarded_production_series = np.array(discarded_production_series)
175
+ decisions = np.array(decisions)
176
+
177
 
178
  consumption_from_network_pandas_series = pd.Series(consumption_from_network_series, index=prod_cons.index)
179
 
 
202
  'consumption_from_network_to_bess_series': consumption_from_network_to_bess_series,
203
  'discarded_production': discarded_production_series,
204
  'Consumption': prod_cons['Consumption'],
205
+ 'Production': prod_cons['Production'],
206
+ 'decisions': decisions
207
  })
208
  results = results.set_index(prod_cons.index)
 
209
  return results, total_network_fee
210
 
211
 
v2/decider.py CHANGED
@@ -53,12 +53,13 @@ class Decider:
53
  mean_surdemand_kw = (cons_pred[:surdemand_window] - prod_pred[:surdemand_window]).mean()
54
 
55
  current_fee = fees[0]
56
- # TODO this should not be a hard threashold, and more importantly,
57
  # it should not be hardwired.
58
  HARDWIRED_THRESHOLD_FOR_CHEAP_POWER_HUF_PER_KWH = 20 # [HUF/kWh]
59
  if mean_surdemand_kw > self.lookahead_surdemand_kw and current_fee <= HARDWIRED_THRESHOLD_FOR_CHEAP_POWER_HUF_PER_KWH:
60
  return Decision.NETWORK_CHARGE
61
 
 
62
  if deficit_kwh > self.precalculated_supplier.peak_demand:
63
  return Decision.DISCHARGE
64
  else:
@@ -80,6 +81,6 @@ class Decider:
80
  # surdemand_lookahead_window_min, lookahead_surdemand_kw
81
  # param1 [minutes]. one day mean, half day scale for lookahead horizon.
82
  # param2 [kWh], averaged over the horizon. negative values are meaningful.
83
- init_mean = np.array([1440.0, 0.0])
84
- init_scale = np.array([720.0, 100.0])
85
  return init_mean, init_scale
 
53
  mean_surdemand_kw = (cons_pred[:surdemand_window] - prod_pred[:surdemand_window]).mean()
54
 
55
  current_fee = fees[0]
56
+ # TODO this should not be a hard threshold, and more importantly,
57
  # it should not be hardwired.
58
  HARDWIRED_THRESHOLD_FOR_CHEAP_POWER_HUF_PER_KWH = 20 # [HUF/kWh]
59
  if mean_surdemand_kw > self.lookahead_surdemand_kw and current_fee <= HARDWIRED_THRESHOLD_FOR_CHEAP_POWER_HUF_PER_KWH:
60
  return Decision.NETWORK_CHARGE
61
 
62
+ # peak shaving
63
  if deficit_kwh > self.precalculated_supplier.peak_demand:
64
  return Decision.DISCHARGE
65
  else:
 
81
  # surdemand_lookahead_window_min, lookahead_surdemand_kw
82
  # param1 [minutes]. one day mean, half day scale for lookahead horizon.
83
  # param2 [kWh], averaged over the horizon. negative values are meaningful.
84
+ init_mean = np.array([60 * 24.0, 0.0])
85
+ init_scale = np.array([60 * 12.0, 100.0])
86
  return init_mean, init_scale
v2/evolution_strategies.py CHANGED
@@ -1,4 +1,8 @@
1
  import numpy as np
 
 
 
 
2
 
3
 
4
  # in-place
@@ -11,7 +15,7 @@ def evolution_strategies_optimizer(objective_function, clipper_function, init_me
11
  # Initialize parameters
12
  population_size = 100
13
  number_of_generations = 30
14
- mutation_scale = 0.1
15
  selection_ratio = 0.5
16
  selected_size = int(population_size * selection_ratio)
17
 
@@ -28,7 +32,7 @@ def evolution_strategies_optimizer(objective_function, clipper_function, init_me
28
  selected = population[selected_indices]
29
 
30
  # Reproduce (mutate)
31
- offspring = selected + np.random.randn(selected_size, 2) * mutation_scale
32
  clip_params(offspring, clipper_function) # in-place
33
 
34
  # Replacement: Here we simply generate new candidates around the selected ones
@@ -40,6 +44,19 @@ def evolution_strategies_optimizer(objective_function, clipper_function, init_me
40
  best_index = np.argmin(fitness)
41
  best_solution = population[best_index]
42
  print(f"Generation {generation + 1}: Best Fitness = {best_fitness}", f"Best solution so far: {best_solution}")
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
 
45
  # Best solution
@@ -50,7 +67,7 @@ def evolution_strategies_optimizer(objective_function, clipper_function, init_me
50
 
51
 
52
  def toy_objective_function(x):
53
- return (x[0] - 3)**2 + (x[1] + 2)**2
54
 
55
 
56
  def toy_clipper_function(x):
 
1
  import numpy as np
2
+ import matplotlib.pyplot as plt
3
+
4
+
5
+ DO_VIS = True
6
 
7
 
8
  # in-place
 
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)
21
 
 
32
  selected = population[selected_indices]
33
 
34
  # Reproduce (mutate)
35
+ offspring = selected + np.random.normal(loc=0, scale=init_scale * mutation_scale, size=(selected_size, len(init_mean)))
36
  clip_params(offspring, clipper_function) # in-place
37
 
38
  # Replacement: Here we simply generate new candidates around the selected ones
 
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
 
67
 
68
 
69
  def toy_objective_function(x):
70
+ return (x[..., 0] - 3)**2 + (x[..., 1] + 2)**2
71
 
72
 
73
  def toy_clipper_function(x):