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):
|