Spaces:
Sleeping
Sleeping
| import pandas as pd | |
| import numpy as np | |
| from data_processing import add_production_field, interpolate_and_join, monthly_analysis | |
| def simulator_with_solar(all_data, parameters): | |
| demand_np = all_data['Consumption'].to_numpy() | |
| production_np = all_data['Production'].to_numpy() | |
| assert len(demand_np) == len(production_np) | |
| step_in_minutes = all_data.index.freq.n | |
| # print("Simulating for", len(demand_np), "time steps. Each step is", step_in_minutes, "minutes.") | |
| soc_series = [] # soc = state_of_charge. | |
| # by convention, we only call end user demand, demand, | |
| # and we only call end user consumption, consumption. | |
| # in our simple model, demand is always satisfied, hence demand=consumption. | |
| # BESS demand is called charge. | |
| consumption_from_solar_series = [] # demand satisfied by solar production | |
| consumption_from_network_series = [] # demand satisfied by network | |
| consumption_from_bess_series = [] # demand satisfied by BESS | |
| # the previous three must sum to demand_series. | |
| charge_of_bess_series = [] # power taken from solar by BESS. note: power never taken from network by BESS. | |
| discarded_production_series = [] # solar power thrown away | |
| # 1 is not nominal but targeted (healthy) maximum charge. | |
| # we start with an empty battery, but not emptier than what's healthy for the batteries. | |
| # For the sake of simplicity 0 <= soc <=1 | |
| # soc=0 means battery is emptied till it's 20% and soc=1 means battery is charged till 80% of its capacity | |
| # soc = 1 - maximal_depth_of_discharge | |
| # and will use only maximal_depth_of_discharge percent of the real battery capacity | |
| soc = 0 | |
| max_cap_of_battery = parameters.bess_capacity * parameters.maximal_depth_of_discharge | |
| cap_of_battery = soc * max_cap_of_battery | |
| time_interval = step_in_minutes / 60 # amount of time step in hours | |
| for i, (demand, production) in enumerate(zip(demand_np, production_np)): | |
| # these five are modified on the appropriate codepaths: | |
| consumption_from_solar = 0 | |
| consumption_from_bess = 0 | |
| consumption_from_network = 0 | |
| discarded_production = 0 | |
| unsatisfied_demand = demand | |
| remaining_production = production | |
| # crucially, we never charge the BESS from the network. | |
| # if demand >= production: | |
| # all goes to demand | |
| # we try to cover the rest from BESS | |
| # we cover the rest from network | |
| # else: | |
| # demand fully satisfied by production | |
| # if exploitable production still remains: | |
| # if is_battery_chargeable: | |
| # charge_battery | |
| # else: | |
| # log discarded production | |
| #battery_charged_enough = (soc > 1- maximal_depth_of_discharge) | |
| if parameters.bess_present: | |
| is_battery_charged_enough = soc > 0 | |
| is_battery_chargeable = soc < 1.0 | |
| else: | |
| is_battery_charged_enough = soc <= 0 | |
| is_battery_chargeable = soc >= 1.0 | |
| if unsatisfied_demand >= remaining_production: | |
| # all goes to demand | |
| consumption_from_solar = remaining_production | |
| unsatisfied_demand -= consumption_from_solar | |
| remaining_production = 0 | |
| # we try to cover the rest from BESS | |
| if (unsatisfied_demand > 0 ) and parameters.bess_present: | |
| if is_battery_charged_enough: | |
| # battery capacity is limited! | |
| if cap_of_battery >= unsatisfied_demand * time_interval : | |
| consumption_from_bess = unsatisfied_demand | |
| unsatisfied_demand = 0 | |
| cap_of_battery -= consumption_from_bess * time_interval | |
| soc = cap_of_battery / max_cap_of_battery | |
| else: | |
| discharge_of_bess = cap_of_battery / time_interval | |
| discharge = min(parameters.bess_discharge, discharge_of_bess) | |
| consumption_from_bess = discharge | |
| unsatisfied_demand -= consumption_from_bess | |
| cap_of_battery -= consumption_from_bess * time_interval | |
| soc = cap_of_battery / max_cap_of_battery | |
| consumption_from_network = unsatisfied_demand | |
| unsatisfied_demand = 0 | |
| else: | |
| # we cover the rest from network | |
| consumption_from_network = unsatisfied_demand | |
| unsatisfied_demand = 0 | |
| else: | |
| # demand fully satisfied by production | |
| consumption_from_solar = unsatisfied_demand | |
| remaining_production -= unsatisfied_demand | |
| unsatisfied_demand = 0 | |
| if (remaining_production > 0) and parameters.bess_present: | |
| # exploitable production still remains: | |
| if is_battery_chargeable: | |
| # we try to specify the BESS modell | |
| if parameters.bess_charge <= remaining_production : | |
| energy = parameters.bess_charge * time_interval | |
| remaining_production = remaining_production - parameters.bess_charge | |
| else : | |
| energy = remaining_production * time_interval | |
| remaining_production = 0 | |
| cap_of_battery += energy | |
| soc = cap_of_battery / max_cap_of_battery | |
| discarded_production = remaining_production | |
| soc_series.append(soc) | |
| consumption_from_solar_series.append(consumption_from_solar) | |
| consumption_from_network_series.append(consumption_from_network) | |
| consumption_from_bess_series.append(consumption_from_bess) | |
| charge_of_bess_series.append(soc) | |
| discarded_production_series.append(discarded_production) | |
| soc_series = np.array(soc_series) | |
| consumption_from_solar_series = np.array(consumption_from_solar_series) | |
| consumption_from_network_series = np.array(consumption_from_network_series) | |
| consumption_from_bess_series = np.array(consumption_from_bess_series) | |
| charge_of_bess_series = np.array(charge_of_bess_series) | |
| discarded_production_series = np.array(discarded_production_series) | |
| results = pd.DataFrame({'soc_series': soc_series, 'consumption_from_solar': consumption_from_solar_series, | |
| 'consumption_from_network': consumption_from_network_series, | |
| 'consumption_from_bess': consumption_from_bess_series, | |
| 'charge_of_bess': charge_of_bess_series, | |
| 'discarded_production': discarded_production_series, | |
| 'Consumption': all_data['Consumption'], | |
| 'Production': all_data['Production'] | |
| }) | |
| results = results.set_index(all_data.index) | |
| return results | |
| def evaluate_parameters(parameters, met_2021_data, cons_2021_data): | |
| add_production_field(met_2021_data, parameters) | |
| all_2021_data = interpolate_and_join(met_2021_data, cons_2021_data) | |
| results = simulator_with_solar(all_2021_data, parameters) | |
| consumptions_in_mwh = monthly_analysis(results) | |
| return consumptions_in_mwh.sum(axis=0) | |