Spaces:
Sleeping
Sleeping
Daniel Varga
commited on
Commit
•
83ff599
1
Parent(s):
3705ebb
ES vis
Browse files- v2/architecture.py +7 -2
- v2/decider.py +4 -3
- v2/evolution_strategies.py +20 -3
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
|
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([
|
84 |
-
init_scale = np.array([
|
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.
|
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.
|
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):
|