Daniel Varga commited on
Commit
fcbe190
·
1 Parent(s): 07d9d06

reproducing previous version

Browse files
Files changed (2) hide show
  1. v2/app.py +82 -37
  2. v2/data_processing.py +5 -2
v2/app.py CHANGED
@@ -4,6 +4,7 @@
4
  import numpy as np
5
  import pandas as pd
6
  import gradio as gr
 
7
 
8
  # from simulation import *
9
  from data_processing import *
@@ -24,26 +25,14 @@ met_2021_data, cons_2021_data = read_datasets()
24
  # TODO move out everything that should not be recalculated.
25
  # TODO actually use uiParameters. (base_price and peak_price were just to mock up the Accordion gui.)
26
  # TODO ui_refresh spawns its own Supplier, which is just dumb.
27
- def recalculate(**uiParameters):
28
- fixed_consumption = uiParameters['fixed_consumption']
29
- del uiParameters['fixed_consumption']
30
-
31
- parameters = SolarParameters()
32
- for k, v in uiParameters.items():
33
- setattr(parameters, k, v)
34
-
35
  np.random.seed(1)
36
 
37
- supplier = Supplier(price=100) # Ft/kWh
38
- # nine-to-five increased price.
39
- supplier.set_price_for_daily_interval(9, 17, 150)
40
- # midnight-to-three decreased price, to test network charge.
41
- supplier.set_price_for_daily_interval(0, 3, 20)
42
- # peak_demand dimension is kWh, but it's interpreted as the full consumption
43
- # during a 15 minute timestep.
44
- supplier.set_demand_charge(peak_demand=2.5, surcharge_per_kwh=500) # kWh in a 15 minutes interval, Ft/kWh
45
 
46
- solar_parameters = SolarParameters()
47
 
48
  add_production_field(met_2021_data, solar_parameters)
49
  all_data = interpolate_and_join(met_2021_data, cons_2021_data)
@@ -51,14 +40,16 @@ def recalculate(**uiParameters):
51
  time_interval_min = all_data.index.freq.n
52
  time_interval_h = time_interval_min / 60
53
 
 
 
54
  # for faster testing:
55
  DATASET_TRUNCATED_SIZE = None
56
  if DATASET_TRUNCATED_SIZE is not None:
57
  print("Truncating dataset to", DATASET_TRUNCATED_SIZE, "datapoints, that is", DATASET_TRUNCATED_SIZE * time_interval_h / 24, "days")
58
  all_data = all_data.iloc[:DATASET_TRUNCATED_SIZE]
59
 
60
- if fixed_consumption:
61
- all_data['Consumption'] = 10
62
 
63
  print("Working with", solar_parameters.solar_cell_num, "solar cells, that's a maximum production of", all_data['Production'].max(), "kW.")
64
 
@@ -69,22 +60,63 @@ def recalculate(**uiParameters):
69
  # we delete the supplier to avoid accidentally calling it instead of precalculated_supplier
70
  supplier = None
71
 
72
- all_data_with_predictions['Consumption_fees'] = precalculated_supplier.consumption_fees # [HUF / kWh]
73
-
74
- battery_model = BatteryModel(capacity_Ah=600, time_interval_h=time_interval_h)
75
-
76
  # param_1 is prob of choosing PASSIVE
77
  # param_2 is prob of choosing NETWORK_CHARGE
 
 
78
  decider = RandomDecider(np.array([0.0, 0.0]), precalculated_supplier)
79
 
 
 
80
  results, total_network_fee = simulator(battery_model, all_data_with_predictions, decider)
81
  print(f"{total_network_fee=}")
82
  return results
83
 
84
 
85
- def ui_refresh(solar_cell_num, bess_nominal_capacity, fixed_consumption, base_price, peak_price):
86
- results = recalculate(solar_cell_num=solar_cell_num, bess_nominal_capacity=bess_nominal_capacity, fixed_consumption=fixed_consumption)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
 
88
  fig1 = plotly_visualize_simulation(results, date_range=("2021-02-01", "2021-02-07"))
89
  fig2 = plotly_visualize_simulation(results, date_range=("2021-08-02", "2021-08-08"))
90
 
@@ -98,11 +130,7 @@ def ui_refresh(solar_cell_num, bess_nominal_capacity, fixed_consumption, base_pr
98
  for column, column_name in zip((network, solar, bess), ("Network", "Solar directly", "Solar via BESS")):
99
  html += f"<tr><td>Yearly consumption served by {column_name}:&nbsp;&nbsp;&nbsp;</td><td>{column:0.2f} MWh</td></tr>\n"
100
 
101
- supplier = Supplier(price=70) # HUF/kWh
102
- supplier.set_price_for_daily_interval_on_workdays(start=6, end=22, price=100)
103
-
104
- # not realistic, just for testing the effect
105
- # supplier.set_demand_charge(peak_demand=100, surcharge_per_kw=1000)
106
 
107
  fee = supplier.fee(results["consumption_from_network"])
108
  html += f"<tr><td>{fee/1e6:.3f} million HUF billed by energy supplier</td></tr>\n"
@@ -116,13 +144,22 @@ with gr.Blocks() as ui:
116
  with gr.Row():
117
  # LEFT: Input controls
118
  with gr.Column(scale=1): # narrower column
119
- solar_slider = gr.Slider(0, 2000, 114, label="Solar cell number")
120
- bess_slider = gr.Slider(0, 2000, 330, label="BESS nominal capacity in [Ah]")
121
- fixed_consumption = gr.Checkbox(value=False, label="Use fixed consumption (10 kW)")
 
 
 
 
122
 
123
- with gr.Accordion("Advanced Pricing Settings", open=False):
124
- base_price = gr.Number(value=0.1, label="Base energy price [€/kWh]")
125
- peak_price = gr.Number(value=0.3, label="Peak energy price [€/kWh]")
 
 
 
 
 
126
 
127
  run_btn = gr.Button("Run Simulation")
128
 
@@ -133,9 +170,17 @@ with gr.Blocks() as ui:
133
  plot2 = gr.Plot()
134
  plot3 = gr.Plot()
135
 
 
 
 
 
 
 
 
136
  run_btn.click(
137
  ui_refresh,
138
- inputs=[solar_slider, bess_slider, fixed_consumption, base_price, peak_price],
 
139
  outputs=[html_out, plot1, plot2, plot3],
140
  )
141
 
 
4
  import numpy as np
5
  import pandas as pd
6
  import gradio as gr
7
+ from types import SimpleNamespace
8
 
9
  # from simulation import *
10
  from data_processing import *
 
25
  # TODO move out everything that should not be recalculated.
26
  # TODO actually use uiParameters. (base_price and peak_price were just to mock up the Accordion gui.)
27
  # TODO ui_refresh spawns its own Supplier, which is just dumb.
28
+ # TODO some gui to upload consumption data.
29
+ # and maybe solar data? but we don't have a solar data -> production model built in. just scale and switch years from a dropdown?
30
+ def recalculate(ui):
 
 
 
 
 
31
  np.random.seed(1)
32
 
33
+ supplier = create_supplier(ui)
 
 
 
 
 
 
 
34
 
35
+ solar_parameters = SolarParameters(solar_cell_num=ui.solar_cell_num, panel_power_at_NOCT=ui.solar_cell_nominal_capacity)
36
 
37
  add_production_field(met_2021_data, solar_parameters)
38
  all_data = interpolate_and_join(met_2021_data, cons_2021_data)
 
40
  time_interval_min = all_data.index.freq.n
41
  time_interval_h = time_interval_min / 60
42
 
43
+ battery_model = BatteryModel(capacity_Ah=600, time_interval_h=time_interval_h)
44
+
45
  # for faster testing:
46
  DATASET_TRUNCATED_SIZE = None
47
  if DATASET_TRUNCATED_SIZE is not None:
48
  print("Truncating dataset to", DATASET_TRUNCATED_SIZE, "datapoints, that is", DATASET_TRUNCATED_SIZE * time_interval_h / 24, "days")
49
  all_data = all_data.iloc[:DATASET_TRUNCATED_SIZE]
50
 
51
+ if ui.fixed_consumption_kW is not None:
52
+ all_data['Consumption'] = ui.fixed_consumption_kW
53
 
54
  print("Working with", solar_parameters.solar_cell_num, "solar cells, that's a maximum production of", all_data['Production'].max(), "kW.")
55
 
 
60
  # we delete the supplier to avoid accidentally calling it instead of precalculated_supplier
61
  supplier = None
62
 
 
 
 
 
63
  # param_1 is prob of choosing PASSIVE
64
  # param_2 is prob of choosing NETWORK_CHARGE
65
+ # so this corresponds to constant DISCHARGE mode, where the priorities are:
66
+ # 1. try solar 2. try bess 3. try network
67
  decider = RandomDecider(np.array([0.0, 0.0]), precalculated_supplier)
68
 
69
+ all_data_with_predictions['Consumption_fees'] = precalculated_supplier.consumption_fees # [HUF / kWh]
70
+
71
  results, total_network_fee = simulator(battery_model, all_data_with_predictions, decider)
72
  print(f"{total_network_fee=}")
73
  return results
74
 
75
 
76
+ # that's very error-prone, sorry. must be kept in sync with the gradio button click call.
77
+ # secondarily, with recalculate().
78
+ def list_to_namespace(
79
+ solar_cell_num, solar_cell_nominal_capacity,
80
+ bess_capacity_Ah, bess_voltage_V, bess_charge_kW, bess_discharge_kW,
81
+ fixed_consumption_kW,
82
+ base_price_HUFpkWh, peak_price_HUFpkWh):
83
+ return SimpleNamespace(
84
+ solar_cell_num=solar_cell_num,
85
+ solar_cell_nominal_capacity=solar_cell_nominal_capacity,
86
+ bess_capacity_Ah=bess_capacity_Ah,
87
+ bess_voltage_V=bess_voltage_V,
88
+ bess_charge_kW=bess_charge_kW,
89
+ bess_discharge_kW=bess_discharge_kW,
90
+ fixed_consumption_checkbox=fixed_consumption_checkbox,
91
+ fixed_consumption_kW=fixed_consumption_kW,
92
+ base_price_HUFpkWh=base_price_HUFpkWh,
93
+ peak_price_HUFpkWh=peak_price_HUFpkWh,
94
+ )
95
+
96
+
97
+ def create_supplier(ui):
98
+ supplier = Supplier(price=ui.base_price_HUFpkWh)
99
+ supplier.set_price_for_daily_interval_on_workdays(start=6, end=22, price=ui.peak_price_HUFpkWh)
100
+ return supplier
101
+
102
+ def ui_refresh(
103
+ solar_cell_num, solar_cell_nominal_capacity,
104
+ bess_capacity_Ah, bess_voltage_V, bess_charge_kW, bess_discharge_kW,
105
+ fixed_consumption_checkbox, fixed_consumption_kW,
106
+ base_price_HUFpkWh, peak_price_HUFpkWh):
107
+
108
+ if not fixed_consumption_checkbox:
109
+ fixed_consumption_kW = None # None means use dataframe.
110
+
111
+ ui = list_to_namespace(
112
+ solar_cell_num, solar_cell_nominal_capacity,
113
+ bess_capacity_Ah, bess_voltage_V, bess_charge_kW, bess_discharge_kW,
114
+ fixed_consumption_kW,
115
+ base_price_HUFpkWh, peak_price_HUFpkWh)
116
+
117
+ results = recalculate(ui)
118
 
119
+ # TODO generalize to other input ranges
120
  fig1 = plotly_visualize_simulation(results, date_range=("2021-02-01", "2021-02-07"))
121
  fig2 = plotly_visualize_simulation(results, date_range=("2021-08-02", "2021-08-08"))
122
 
 
130
  for column, column_name in zip((network, solar, bess), ("Network", "Solar directly", "Solar via BESS")):
131
  html += f"<tr><td>Yearly consumption served by {column_name}:&nbsp;&nbsp;&nbsp;</td><td>{column:0.2f} MWh</td></tr>\n"
132
 
133
+ supplier = create_supplier(ui)
 
 
 
 
134
 
135
  fee = supplier.fee(results["consumption_from_network"])
136
  html += f"<tr><td>{fee/1e6:.3f} million HUF billed by energy supplier</td></tr>\n"
 
144
  with gr.Row():
145
  # LEFT: Input controls
146
  with gr.Column(scale=1): # narrower column
147
+ solar_cell_num = gr.Slider(0, 3000, 1140, label="number of installed solar cells")
148
+ solar_cell_nominal_capacity = gr.Slider(0, 1000, 280, label="solar cell nominal capacity at NOCT [W]")
149
+
150
+ bess_capacity_Ah = gr.Slider(0, 2000, 330, label="BESS nominal capacity [Ah]")
151
+
152
+ fixed_consumption_checkbox = gr.Checkbox(value=False, label="Use fixed consumption")
153
+ fixed_consumption_kW = gr.Slider(0, 100, 10, label="Amount of fixed consumption [kW]")
154
 
155
+ with gr.Accordion("Extra BESS settings", open=False):
156
+ bess_voltage_V = gr.Number(value=600, label="BESS voltage [V]")
157
+ bess_charge_kW = gr.Number(value=50, label="BESS charge [kW]")
158
+ bess_discharge_kW = gr.Number(value=60, label="BESS discharge [kW]")
159
+
160
+ with gr.Accordion("Pricing settings", open=False):
161
+ peak_price_HUFpkWh = gr.Number(value=60, label="Peak energy price [HUF/kWh]")
162
+ base_price_HUFpkWh = gr.Number(value=60, label="Base energy price [HUF/kWh]")
163
 
164
  run_btn = gr.Button("Run Simulation")
165
 
 
170
  plot2 = gr.Plot()
171
  plot3 = gr.Plot()
172
 
173
+ inputs = [
174
+ solar_cell_num, solar_cell_nominal_capacity,
175
+ bess_capacity_Ah, bess_voltage_V, bess_charge_kW, bess_discharge_kW,
176
+ fixed_consumption_checkbox, fixed_consumption_kW,
177
+ base_price_HUFpkWh, peak_price_HUFpkWh
178
+ ]
179
+
180
  run_btn.click(
181
  ui_refresh,
182
+ inputs=inputs,
183
+ # [solar_cell_num_slider, bess_slider, fixed_consumption_slider, base_price, peak_price],
184
  outputs=[html_out, plot1, plot2, plot3],
185
  )
186
 
v2/data_processing.py CHANGED
@@ -46,15 +46,18 @@ def read_datasets(mini=False):
46
  class SolarParameters:
47
  solar_cell_num: float = 1140 # units
48
  solar_efficiency: float = 0.93 * 0.96 # [dimensionless]
49
- NOCT: float = 280 # [W]
 
50
  NOCT_irradiation: float = 800 # [W/m^2]
51
 
52
 
53
  # mutates met_2021_data
54
  def add_production_field(met_2021_data, parameters):
 
55
  sr = met_2021_data['sr']
56
 
57
- nop_total = sr * parameters.solar_cell_num * parameters.solar_efficiency * parameters.NOCT / parameters.NOCT_irradiation / 1e3
 
58
  nop_total = nop_total.clip(0)
59
  met_2021_data['Production'] = nop_total
60
 
 
46
  class SolarParameters:
47
  solar_cell_num: float = 1140 # units
48
  solar_efficiency: float = 0.93 * 0.96 # [dimensionless]
49
+ panel_power_at_NOCT: float = 280 # [W]
50
+ # this is the SR (solar radiation) level where panel_power_at_NOCT is produced:
51
  NOCT_irradiation: float = 800 # [W/m^2]
52
 
53
 
54
  # mutates met_2021_data
55
  def add_production_field(met_2021_data, parameters):
56
+ # sr has dimension W/m^2.
57
  sr = met_2021_data['sr']
58
 
59
+ # TODO use something a bit more fancy nonlinear if we have the temperature anyway.
60
+ nop_total = sr * parameters.solar_cell_num * parameters.solar_efficiency * parameters.panel_power_at_NOCT / parameters.NOCT_irradiation / 1e3
61
  nop_total = nop_total.clip(0)
62
  met_2021_data['Production'] = nop_total
63