# port of # https://colab.research.google.com/drive/1PJgcJ4ly7x5GuZy344eJeYSODo8trbM4#scrollTo=39F2u-4hvwLU import numpy as np import pandas as pd import gradio as gr from types import SimpleNamespace import datetime # from simulation import * from data_processing import * from visualization import * from supplier import Supplier, precalculate_supplier from architecture import simulator, add_dummy_predictions from decider import Decider, RandomDecider from bess import BatteryParameters, BatteryModel #@title ### Downloading the data # !wget "https://static.renyi.hu/ai-shared/daniel/pq/PL_44527.19-21.csv.gz" # !wget "https://static.renyi.hu/ai-shared/daniel/pq/pq_terheles_2021_adatok.tsv" OLD_DATASET = False MINI = False if MINI: met_default_filename = 'PL_44527.2101.csv.gz' cons_default_filename = 'pq_terheles_202101_adatok.tsv' else: met_default_filename = 'PL_44527.19-21.csv.gz' cons_default_filename = 'pq_terheles_2021_adatok.tsv' met_2021_data, cons_2021_data = read_datasets(met_default_filename, cons_default_filename, old_dataset=OLD_DATASET) # TODO some gui to upload consumption data. # and maybe solar data? but we don't have a solar data -> production model built in. just scale and switch years from a dropdown? def recalculate(ui): np.random.seed(1) supplier = create_supplier(ui) solar_parameters = SolarParameters(solar_cell_num=ui.solar_cell_num, panel_power_at_NOCT=ui.solar_cell_nominal_capacity) met_data = met_2021_data cons_data = cons_2021_data changed_datasource = False if ui.meteorology_csv is not None: met_filename = ui.meteorology_csv.name changed_datasource = True else: met_filename = met_default_filename if ui.consumption_csv is not None: print("Hm, we should do something with this CSV.", ui.consumption_csv.name) cons_filename = ui.consumption_csv.name changed_datasource = True else: cons_filename = cons_default_filename if changed_datasource: met_data, cons_data = read_datasets(met_filename, cons_filename, old_dataset=OLD_DATASET) else: met_data, cons_data = met_2021_data, cons_2021_data add_production_field(met_data, solar_parameters) if OLD_DATASET: # i've obsoleted this, but it's not a 100% replacement, # it treats daylight savings changes differently. # new version drops repeated hour and interpolates skipped hour. all_data = interpolate_and_join(met_data, cons_data) else: all_data = join_consumption_meteorology( met_data, cons_data, target_freq="5min", ) time_interval_min = all_data.index.freq.n time_interval_h = time_interval_min / 60 battery_parameters = BatteryParameters( capacity_Ah = ui.bess_capacity_Ah, voltage_V = ui.bess_voltage_V, charge_kW = ui.bess_charge_kW, discharge_kW = ui.bess_discharge_kW ) battery_model = BatteryModel(battery_parameters, time_interval_h=time_interval_h) # for faster testing: DATASET_TRUNCATED_SIZE = None if DATASET_TRUNCATED_SIZE is not None: print("Truncating dataset to", DATASET_TRUNCATED_SIZE, "datapoints, that is", DATASET_TRUNCATED_SIZE * time_interval_h / 24, "days") all_data = all_data.iloc[:DATASET_TRUNCATED_SIZE] if ui.fixed_consumption_kW is not None: all_data['Consumption'] = ui.fixed_consumption_kW all_data_with_predictions = all_data.copy() add_dummy_predictions(all_data_with_predictions) precalculated_supplier = precalculate_supplier(supplier, all_data.index) # we delete the supplier to avoid accidentally calling it instead of precalculated_supplier supplier = None # param_1 is prob of choosing PASSIVE # param_2 is prob of choosing NETWORK_CHARGE # so this corresponds to constant DISCHARGE mode, where the priorities are: # 1. try solar 2. try bess 3. try network decider = RandomDecider(np.array([0.0, 0.0]), precalculated_supplier) all_data_with_predictions['Consumption_fees'] = precalculated_supplier.consumption_fees # [HUF / kWh] results, total_network_fee = simulator(battery_model, all_data_with_predictions, decider) print(f"{total_network_fee=}") return results # that's very error-prone, sorry. must be kept in sync with the gradio button click call, # the caller ui_refresh and recalculate(). def list_to_namespace( solar_cell_num, solar_cell_nominal_capacity, bess_capacity_Ah, bess_voltage_V, bess_charge_kW, bess_discharge_kW, fixed_consumption_kW, base_price_HUFpkWh, peak_price_HUFpkWh, consumption_csv, meteorology_csv ): return SimpleNamespace( solar_cell_num=solar_cell_num, solar_cell_nominal_capacity=solar_cell_nominal_capacity, bess_capacity_Ah=bess_capacity_Ah, bess_voltage_V=bess_voltage_V, bess_charge_kW=bess_charge_kW, bess_discharge_kW=bess_discharge_kW, # not passed to recalculate(ui): # fixed_consumption_checkbox=fixed_consumption_checkbox, fixed_consumption_kW=fixed_consumption_kW, base_price_HUFpkWh=base_price_HUFpkWh, peak_price_HUFpkWh=peak_price_HUFpkWh, # not passed to recalculate(ui): # week_a=week_a, week_b=week_b, consumption_csv=consumption_csv, meteorology_csv=meteorology_csv ) def create_supplier(ui): supplier = Supplier(price=ui.base_price_HUFpkWh) supplier.set_price_for_daily_interval_on_workdays(start=6, end=18, price=ui.peak_price_HUFpkWh) return supplier def ui_refresh( solar_cell_num, solar_cell_nominal_capacity, bess_capacity_Ah, bess_voltage_V, bess_charge_kW, bess_discharge_kW, fixed_consumption_checkbox, fixed_consumption_kW, base_price_HUFpkWh, peak_price_HUFpkWh, week_a, week_b, consumption_csv, meteorology_csv): if not fixed_consumption_checkbox: fixed_consumption_kW = None # None means use dataframe. ui = list_to_namespace( solar_cell_num, solar_cell_nominal_capacity, bess_capacity_Ah, bess_voltage_V, bess_charge_kW, bess_discharge_kW, fixed_consumption_kW, base_price_HUFpkWh, peak_price_HUFpkWh, consumption_csv, meteorology_csv ) results = recalculate(ui) # TODO this is a bit too vibey start_a = (week_a if isinstance(week_a, datetime.datetime) else datetime.datetime.fromisoformat(week_a)) end_a = start_a + datetime.timedelta(days=6) start_b = (week_b if isinstance(week_b, datetime.datetime) else datetime.datetime.fromisoformat(week_b)) end_b = start_b + datetime.timedelta(days=6) date_range_a = (start_a.strftime("%Y-%m-%d"), end_a.strftime("%Y-%m-%d")) date_range_b = (start_b.strftime("%Y-%m-%d"), end_b.strftime("%Y-%m-%d")) fig1 = plotly_visualize_simulation(results, date_range=date_range_a) fig2 = plotly_visualize_simulation(results, date_range=date_range_b) # (12, 3), the 3 indexed with (network, solar, bess): consumptions_in_mwh = monthly_analysis(results) fig_monthly = plotly_visualize_monthly(consumptions_in_mwh) network, solar, bess = consumptions_in_mwh.sum(axis=0) html = "\n" for column, column_name in zip((network, solar, bess), ("Network", "Solar directly", "Solar via BESS")): html += f"\n" supplier = create_supplier(ui) fee = supplier.fee(results["consumption_from_network"]) html += f"\n" html += "
Yearly consumption served by {column_name}:   {column:0.2f} MWh
Network energy usage charge:{fee/1e6:.3f} million HUF
" return (html, fig_monthly, fig1, fig2) with gr.Blocks() as ui: with gr.Row(): # LEFT: Input controls with gr.Column(scale=1): # narrower column solar_cell_num = gr.Slider(0, 3000, 1140, label="Number of installed solar cells") solar_cell_nominal_capacity = gr.Slider(0, 1000, 280, label="Solar cell nominal capacity at NOCT [W]") bess_capacity_Ah = gr.Slider(0, 2000, 330, label="BESS nominal capacity [Ah]") fixed_consumption_checkbox = gr.Checkbox(value=False, label="Use fixed consumption") fixed_consumption_kW = gr.Slider(0, 100, 10, label="Amount of fixed consumption [kW]") with gr.Accordion("Extra BESS settings", open=False): bess_voltage_V = gr.Number(value=600, label="BESS voltage [V]") bess_charge_kW = gr.Number(value=50, label="BESS charge [kW]") bess_discharge_kW = gr.Number(value=60, label="BESS discharge [kW]") with gr.Accordion("Pricing settings", open=False): peak_price_HUFpkWh = gr.Number(value=60, label="Peak energy price [HUF/kWh]") base_price_HUFpkWh = gr.Number(value=60, label="Base energy price [HUF/kWh]") with gr.Accordion("Custom consumption/meteorology data", open=False): consumption_csv = gr.File(label="Upload consumption CSV", file_types=[".csv", ".gz", ".tsv"]) meteorology_csv = gr.File(label="Upload meteorology CSV", file_types=[".csv", ".gz", ".tsv"]) run_btn = gr.Button("Run Simulation") week_a_default = "2021-02-01" ; week_b_default = "2021-08-02" # week_b_default = datetime.date(2021, 2, 1) ; week_b_default = datetime.date(2021, 8, 2) # RIGHT: Output display with gr.Column(scale=2): # wider column html_out = gr.HTML() plot1 = gr.Plot(label="Monthly energy use") # date selector + Plot 2 week_a = gr.DateTime( value=week_a_default, include_time=False, label="Start date for Week A", type="string" ) plot2 = gr.Plot(label="Week A") # date selector + Plot 3 week_b = gr.DateTime( value=week_b_default, include_time=False, label="Start date for Week B", type="string" ) plot3 = gr.Plot(label="Week B") inputs = [ solar_cell_num, solar_cell_nominal_capacity, bess_capacity_Ah, bess_voltage_V, bess_charge_kW, bess_discharge_kW, fixed_consumption_checkbox, fixed_consumption_kW, base_price_HUFpkWh, peak_price_HUFpkWh, week_a, week_b, consumption_csv, meteorology_csv ] run_btn.click( ui_refresh, inputs=inputs, # [solar_cell_num_slider, bess_slider, fixed_consumption_slider, base_price, peak_price], outputs=[html_out, plot1, plot2, plot3], ) ui.launch()