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) | |