Spaces:
Sleeping
Sleeping
Daniel Varga commited on
Commit ·
0c694ac
1
Parent(s): 5ef652c
before big rewrite
Browse files- v2/architecture.py +131 -3
v2/architecture.py
CHANGED
|
@@ -8,7 +8,7 @@ from supplier import Supplier
|
|
| 8 |
from data_processing import read_datasets, add_production_field, interpolate_and_join, Parameters
|
| 9 |
|
| 10 |
|
| 11 |
-
STEPS_PER_HOUR =
|
| 12 |
|
| 13 |
|
| 14 |
# mock model
|
|
@@ -68,7 +68,7 @@ class Decider:
|
|
| 68 |
def __init__(self):
|
| 69 |
self.parameters = None
|
| 70 |
self.input_window_size = STEPS_PER_HOUR * 24 # day long window.
|
| 71 |
-
self.output_window_size = STEPS_PER_HOUR # only output
|
| 72 |
|
| 73 |
# prod_cons_pred is a dataframe starting at now, containing
|
| 74 |
# fields Production and Consumption.
|
|
@@ -95,6 +95,134 @@ def simulator(battery_model, supplier, prod_cons, decider):
|
|
| 95 |
return None
|
| 96 |
|
| 97 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 98 |
def main():
|
| 99 |
battery_model = BatteryModel(capacity=200, efficiency=0.95)
|
| 100 |
|
|
@@ -109,7 +237,7 @@ def main():
|
|
| 109 |
|
| 110 |
decider = Decider()
|
| 111 |
|
| 112 |
-
results = simulator(battery_model, supplier, all_2021_data, decider)
|
| 113 |
|
| 114 |
|
| 115 |
if __name__ == '__main__':
|
|
|
|
| 8 |
from data_processing import read_datasets, add_production_field, interpolate_and_join, Parameters
|
| 9 |
|
| 10 |
|
| 11 |
+
STEPS_PER_HOUR = 12
|
| 12 |
|
| 13 |
|
| 14 |
# mock model
|
|
|
|
| 68 |
def __init__(self):
|
| 69 |
self.parameters = None
|
| 70 |
self.input_window_size = STEPS_PER_HOUR * 24 # day long window.
|
| 71 |
+
self.output_window_size = STEPS_PER_HOUR # only output decisions for the next hour
|
| 72 |
|
| 73 |
# prod_cons_pred is a dataframe starting at now, containing
|
| 74 |
# fields Production and Consumption.
|
|
|
|
| 95 |
return None
|
| 96 |
|
| 97 |
|
| 98 |
+
# TODO even in a first mockup version, parameters should come from a single
|
| 99 |
+
# place, not some from the parameters dataclass and some from the battery_model.
|
| 100 |
+
def simulator(battery_model, supplier, prod_cons, decider, parameters):
|
| 101 |
+
battery_model = copy.copy(battery_model)
|
| 102 |
+
|
| 103 |
+
demand_np = prod_cons['Consumption'].to_numpy()
|
| 104 |
+
production_np = prod_cons['Production'].to_numpy()
|
| 105 |
+
assert len(demand_np) == len(production_np)
|
| 106 |
+
step_in_minutes = prod_cons.index.freq.n
|
| 107 |
+
print(step_in_minutes)
|
| 108 |
+
assert step_in_minutes == 5
|
| 109 |
+
|
| 110 |
+
print("Simulating for", len(demand_np), "time steps. Each step is", step_in_minutes, "minutes.")
|
| 111 |
+
soc_series = []
|
| 112 |
+
# by convention, we only call end user demand, demand,
|
| 113 |
+
# and we only call end user consumption, consumption.
|
| 114 |
+
# in our simple model, demand is always satisfied, hence demand=consumption.
|
| 115 |
+
# BESS demand is called charge.
|
| 116 |
+
consumption_from_solar_series = [] # demand satisfied by solar production
|
| 117 |
+
consumption_from_network_series = [] # demand satisfied by network
|
| 118 |
+
consumption_from_bess_series = [] # demand satisfied by BESS
|
| 119 |
+
# the previous three must sum to demand_series.
|
| 120 |
+
|
| 121 |
+
# power taken from solar by BESS.
|
| 122 |
+
# note: in inital mock version power is never taken from network by BESS.
|
| 123 |
+
charge_of_bess_series = []
|
| 124 |
+
discarded_production_series = [] # solar power thrown away
|
| 125 |
+
|
| 126 |
+
# 1 is not nominal but targeted (healthy) maximum charge.
|
| 127 |
+
# we start with an empty battery, but not emptier than what's healthy for the batteries.
|
| 128 |
+
|
| 129 |
+
# For the sake of simplicity 0 <= soc <=1
|
| 130 |
+
# soc=0 means battery is emptied till it's 20% and soc=1 means battery is charged till 80% of its capacity
|
| 131 |
+
# soc = 1 - maximal_depth_of_discharge
|
| 132 |
+
# and will use only maximal_depth_of_discharge percent of the real battery capacity
|
| 133 |
+
|
| 134 |
+
max_cap_of_battery = parameters.bess_capacity * parameters.maximal_depth_of_discharge
|
| 135 |
+
cap_of_battery = soc * max_cap_of_battery
|
| 136 |
+
|
| 137 |
+
time_interval = step_in_minutes / 60 # amount of time step in hours
|
| 138 |
+
for i, (demand, production) in enumerate(zip(demand_np, production_np)):
|
| 139 |
+
|
| 140 |
+
# these five are modified on the appropriate codepaths:
|
| 141 |
+
consumption_from_solar = 0
|
| 142 |
+
consumption_from_bess = 0
|
| 143 |
+
consumption_from_network = 0
|
| 144 |
+
discarded_production = 0
|
| 145 |
+
|
| 146 |
+
unsatisfied_demand = demand
|
| 147 |
+
remaining_production = production
|
| 148 |
+
|
| 149 |
+
if parameters.bess_present:
|
| 150 |
+
is_battery_charged_enough = battery_model.soc > 0
|
| 151 |
+
is_battery_chargeable = battery_model.soc < 1.0
|
| 152 |
+
else:
|
| 153 |
+
is_battery_charged_enough = battery_model.soc <= 0
|
| 154 |
+
is_battery_chargeable = battery_model.soc >= 1.0
|
| 155 |
+
|
| 156 |
+
if unsatisfied_demand >= remaining_production:
|
| 157 |
+
# all goes to demand
|
| 158 |
+
consumption_from_solar = remaining_production
|
| 159 |
+
unsatisfied_demand -= consumption_from_solar
|
| 160 |
+
remaining_production = 0
|
| 161 |
+
# we try to cover the rest from BESS
|
| 162 |
+
if (unsatisfied_demand > 0 ) and parameters.bess_present:
|
| 163 |
+
if is_battery_charged_enough:
|
| 164 |
+
# battery capacity is limited!
|
| 165 |
+
if cap_of_battery >= unsatisfied_demand * time_interval :
|
| 166 |
+
consumption_from_bess = unsatisfied_demand
|
| 167 |
+
unsatisfied_demand = 0
|
| 168 |
+
cap_of_battery -= consumption_from_bess * time_interval
|
| 169 |
+
soc = cap_of_battery / max_cap_of_battery
|
| 170 |
+
else:
|
| 171 |
+
discharge_of_bess = cap_of_battery / time_interval
|
| 172 |
+
discharge = min(parameters.bess_discharge, discharge_of_bess)
|
| 173 |
+
consumption_from_bess = discharge
|
| 174 |
+
unsatisfied_demand -= consumption_from_bess
|
| 175 |
+
cap_of_battery -= consumption_from_bess * time_interval
|
| 176 |
+
soc = cap_of_battery / max_cap_of_battery
|
| 177 |
+
consumption_from_network = unsatisfied_demand
|
| 178 |
+
unsatisfied_demand = 0
|
| 179 |
+
else:
|
| 180 |
+
# we cover the rest from network
|
| 181 |
+
consumption_from_network = unsatisfied_demand
|
| 182 |
+
unsatisfied_demand = 0
|
| 183 |
+
else:
|
| 184 |
+
# demand fully satisfied by production
|
| 185 |
+
consumption_from_solar = unsatisfied_demand
|
| 186 |
+
remaining_production -= unsatisfied_demand
|
| 187 |
+
unsatisfied_demand = 0
|
| 188 |
+
if (remaining_production > 0) and parameters.bess_present:
|
| 189 |
+
# exploitable production still remains:
|
| 190 |
+
if is_battery_chargeable:
|
| 191 |
+
# we try to specify the BESS modell
|
| 192 |
+
if parameters.bess_charge <= remaining_production :
|
| 193 |
+
energy = parameters.bess_charge * time_interval
|
| 194 |
+
remaining_production = remaining_production - parameters.bess_charge
|
| 195 |
+
else :
|
| 196 |
+
energy = remaining_production * time_interval
|
| 197 |
+
remaining_production = 0
|
| 198 |
+
cap_of_battery += energy
|
| 199 |
+
soc = cap_of_battery / max_cap_of_battery
|
| 200 |
+
|
| 201 |
+
discarded_production = remaining_production
|
| 202 |
+
|
| 203 |
+
soc_series.append(battery_model.soc)
|
| 204 |
+
consumption_from_solar_series.append(consumption_from_solar)
|
| 205 |
+
consumption_from_network_series.append(consumption_from_network)
|
| 206 |
+
consumption_from_bess_series.append(consumption_from_bess)
|
| 207 |
+
discarded_production_series.append(discarded_production)
|
| 208 |
+
|
| 209 |
+
soc_series = np.array(soc_series)
|
| 210 |
+
consumption_from_solar_series = np.array(consumption_from_solar_series)
|
| 211 |
+
consumption_from_network_series = np.array(consumption_from_network_series)
|
| 212 |
+
consumption_from_bess_series = np.array(consumption_from_bess_series)
|
| 213 |
+
discarded_production_series = np.array(discarded_production_series)
|
| 214 |
+
|
| 215 |
+
results = pd.DataFrame({'soc_series': soc_series, 'consumption_from_solar': consumption_from_solar_series,
|
| 216 |
+
'consumption_from_network': consumption_from_network_series,
|
| 217 |
+
'consumption_from_bess': consumption_from_bess_series,
|
| 218 |
+
'discarded_production': discarded_production_series,
|
| 219 |
+
'Consumption': all_data['Consumption'],
|
| 220 |
+
'Production': all_data['Production']
|
| 221 |
+
})
|
| 222 |
+
results = results.set_index(all_data.index)
|
| 223 |
+
return results
|
| 224 |
+
|
| 225 |
+
|
| 226 |
def main():
|
| 227 |
battery_model = BatteryModel(capacity=200, efficiency=0.95)
|
| 228 |
|
|
|
|
| 237 |
|
| 238 |
decider = Decider()
|
| 239 |
|
| 240 |
+
results = simulator(battery_model, supplier, all_2021_data, decider, parameters)
|
| 241 |
|
| 242 |
|
| 243 |
if __name__ == '__main__':
|