191 lines
9.5 KiB
Python
191 lines
9.5 KiB
Python
from config import pv_config, ess_config, grid_config
|
|
import pandas as pd
|
|
class EnergySystem:
|
|
def __init__(self, pv_type: pv_config, ess_type: ess_config, grid_type: grid_config):
|
|
self.pv = pv_type
|
|
self.ess = ess_type
|
|
self.grid = grid_type
|
|
self.day_generated = []
|
|
self.generated = 0
|
|
self.stored = 0
|
|
self.hour_stored = []
|
|
self.hour_stored_2 = []
|
|
self.afford = True
|
|
self.cost = self.ess.get_cost() + self.pv.get_cost()
|
|
self.overload_cnt = 0
|
|
self.spring_week_gen = []
|
|
self.summer_week_gen = []
|
|
self.autumn_week_gen = []
|
|
self.winter_week_gen = []
|
|
self.spring_week_soc = []
|
|
self.summer_week_soc = []
|
|
self.autumn_week_soc = []
|
|
self.winter_week_soc = []
|
|
self.factory_demand = []
|
|
self.buy_price_kWh = []
|
|
self.sell_price_kWh = []
|
|
self.pv_generated_kWh = []
|
|
self.grid_need_power_kW = []
|
|
self.time = []
|
|
self.ess_rest = 0
|
|
self.granularity = 4
|
|
self.season_step = self.granularity * 24 * 7 * 12
|
|
self.season_start= self.granularity * 24 * 7 * 2
|
|
self.week_length = self.granularity * 24 * 7
|
|
self.unmet = []
|
|
|
|
def get_cost(self):
|
|
return self.ess.get_cost()+self.pv.get_cost()
|
|
|
|
def simulate(self, data, time_interval):
|
|
"""
|
|
The program will use the PV to supply the factory first. If the PV output can meet the factory's demand, it will be directly powered, and the excess electrical energy will be used to charge the ESS. Program will use the PV to supply the Ess.
|
|
|
|
When the PV is insufficient, the ESS is used to supplement. If the PV output is not enough to meet the factory's demand, the required power is first obtained from the ESS.
|
|
|
|
If the ESS is also insufficient to meet the demand, it will be obtained from the grid. When the stored power in the ESS is also insufficient to supplement, the remaining required power will be purchased from the grid.
|
|
|
|
Args:
|
|
data: pandas.DataFrame
|
|
The data that contains the factory's demand, PV output, and electricity price.
|
|
time_interval: float
|
|
The time interval of the data in hours.
|
|
|
|
Returns:
|
|
tuple
|
|
The total benefit, total netto benefit, and total generated energy.
|
|
|
|
"""
|
|
total_benefit = 0
|
|
total_netto_benefit = 0
|
|
total_gen = 0
|
|
net_grid = 0.
|
|
for index, row in data.iterrows():
|
|
time = row['time']
|
|
self.time.append(time)
|
|
# sunlight_intensity = row['sunlight']
|
|
pv_yield = row['PV yield[kW/kWp]']
|
|
factory_demand = row['demand']
|
|
electricity_price = row['buy']
|
|
sell_price = row['sell']
|
|
# electricity_price = self.grid.get_price_for_time(time)
|
|
|
|
# if time == '00:00':
|
|
# self.day_generated.append(self.generated)
|
|
# self.generated = 0
|
|
# if time.endswith('14:00'):
|
|
# soc = self.ess.storage / self.ess.capacity
|
|
# self.hour_stored.append(soc)
|
|
# if time.endswith('08:00'):
|
|
# soc = self.ess.storage / self.ess.capacity
|
|
# self.hour_stored_2.append(soc)
|
|
|
|
# `generated_pv_power`: the power generated by the PV in kW
|
|
# `generated_pv_energy`: the energy generated by the PV in kWh
|
|
generated_pv_power = self.pv.capacity * pv_yield
|
|
generated_pv_energy = generated_pv_power * time_interval * self.pv.loss
|
|
|
|
self.pv_generated_kWh.append(generated_pv_energy)
|
|
self.factory_demand.append(factory_demand)
|
|
self.buy_price_kWh.append(electricity_price)
|
|
self.sell_price_kWh.append(sell_price)
|
|
|
|
self.generated += generated_pv_energy
|
|
# generated_pv_energy is larger than factory_demand energy
|
|
if generated_pv_energy >= factory_demand * time_interval:
|
|
"""
|
|
That means the generated energy is enough to power the factory.
|
|
The surplus energy will be used to charge the ESS.
|
|
|
|
surplus_energy: The energy that is left after powering the factory.
|
|
formula: generated_pv_energy - factory_demand * time_interval
|
|
|
|
charge_to_ess: The energy that will be charged to the ESS.
|
|
formula: min(surplus_energy, ess.charge_power * time_interval, ess.capacity - ess.storage)
|
|
|
|
surplus_after_ess: The energy that is left after charging the ESS.
|
|
"""
|
|
surplus_energy = generated_pv_energy - factory_demand * time_interval
|
|
charge_to_ess = min(surplus_energy, self.ess.charge_power * time_interval, self.ess.capacity - self.ess.storage)
|
|
self.ess.storage += charge_to_ess
|
|
surplus_after_ess = surplus_energy - charge_to_ess
|
|
"""
|
|
If there is still surplus energy after charging the ESS, and the generated PV power is greater than the sum of the ESS's charge power and the factory's demand power, the surplus energy will be sold to the grid.
|
|
"""
|
|
if surplus_after_ess > 0 and generated_pv_power > self.ess.charge_power + factory_demand:
|
|
sold_to_grid = surplus_after_ess
|
|
sell_income = sold_to_grid * sell_price
|
|
total_benefit += sell_income
|
|
"""
|
|
Saved energy is the energy that is saved by using the PV to power the factory.
|
|
"""
|
|
saved_energy = factory_demand * time_interval
|
|
self.grid_need_power_kW.append(0)
|
|
else:
|
|
"""
|
|
If the generated energy is not enough to power the factory, the ESS will be used to supplement the energy.
|
|
|
|
needed_from_ess: The energy that is needed from the ESS to power the factory.
|
|
formula: factory_demand * time_interval - generated_pv_energy
|
|
"""
|
|
needed_from_ess = factory_demand * time_interval - generated_pv_energy
|
|
"""
|
|
If the ESS has enough stored energy to power the factory, the energy will be taken from the ESS.
|
|
"""
|
|
if self.ess.storage * self.ess.loss >= needed_from_ess:
|
|
if self.ess.discharge_power * time_interval * self.ess.loss < needed_from_ess:
|
|
discharging_power = self.ess.discharge_power * time_interval
|
|
else:
|
|
discharging_power = needed_from_ess / self.ess.loss
|
|
|
|
self.ess.storage -= discharging_power
|
|
"""
|
|
In this case, the energy that is needed from the grid is 0.
|
|
"""
|
|
saved_energy = generated_pv_energy + discharging_power * self.ess.loss
|
|
self.grid_need_power_kW.append(0)
|
|
else:
|
|
"""
|
|
If the ESS does not have enough stored energy to power the factory, the energy will be taken from the grid.
|
|
"""
|
|
if self.grid.capacity * time_interval + generated_pv_energy + self.ess.storage * self.ess.loss < factory_demand * time_interval:
|
|
self.afford = False
|
|
self.overload_cnt+=1
|
|
self.unmet.append((index,time,factory_demand,generated_pv_power))
|
|
saved_energy = generated_pv_energy + self.ess.storage * self.ess.loss
|
|
self.ess.storage = 0
|
|
needed_from_grid = factory_demand * time_interval - saved_energy
|
|
net_grid = min(self.grid.capacity * time_interval, needed_from_grid) * self.grid.loss
|
|
self.grid_need_power_kW.append(needed_from_grid * 4)
|
|
total_gen += saved_energy
|
|
benefit = (saved_energy) * electricity_price
|
|
cost = net_grid * electricity_price
|
|
total_netto_benefit += benefit
|
|
total_benefit += benefit - cost
|
|
print_season_flag = False
|
|
if print_season_flag == True:
|
|
week_start = self.season_start
|
|
week_end = self.week_length + week_start
|
|
if index in range(week_start, week_end):
|
|
self.spring_week_gen.append(generated_pv_power)
|
|
self.spring_week_soc.append(self.ess.storage / self.ess.capacity)
|
|
self.ess_rest = self.ess.storage
|
|
# summer
|
|
week_start += self.season_step
|
|
week_end += self.season_step
|
|
if index in range(week_start, week_end):
|
|
self.summer_week_gen.append(generated_pv_power)
|
|
self.summer_week_soc.append(self.ess.storage / self.ess.capacity)
|
|
# # autumn
|
|
week_start += self.season_step
|
|
week_end += self.season_step
|
|
if index in range(week_start, week_end):
|
|
self.autumn_week_gen.append(generated_pv_power)
|
|
self.autumn_week_soc.append(self.ess.storage / self.ess.capacity)
|
|
week_start += self.season_step
|
|
week_end += self.season_step
|
|
if index in range(week_start, week_end):
|
|
self.winter_week_gen.append(generated_pv_power)
|
|
self.winter_week_soc.append(self.ess.storage / self.ess.capacity)
|
|
|
|
return (total_benefit, total_netto_benefit, total_gen) |