Daniel Varga commited on
Commit
0c694ac
1 Parent(s): 5ef652c

before big rewrite

Browse files
Files changed (1) hide show
  1. 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 = 4
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 4 decisions for the next hour
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__':