simple-pv-simulator/EnergySystem.py

191 lines
9.5 KiB
Python
Raw Permalink Normal View History

2024-05-03 14:07:57 +02:00
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
2024-05-06 19:24:11 +02:00
self.day_generated = []
self.generated = 0
self.stored = 0
self.hour_stored = []
self.hour_stored_2 = []
self.afford = True
2024-05-07 10:27:45 +02:00
self.cost = self.ess.get_cost() + self.pv.get_cost()
2024-05-06 19:24:11 +02:00
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 = []
2024-05-15 15:06:43 +02:00
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
2024-05-06 19:24:11 +02:00
self.granularity = 4
self.season_step = self.granularity * 24 * 7 * 12
2024-05-07 10:27:45 +02:00
self.season_start= self.granularity * 24 * 7 * 2
2024-05-06 19:24:11 +02:00
self.week_length = self.granularity * 24 * 7
2024-05-07 10:27:45 +02:00
self.unmet = []
2024-05-06 19:24:11 +02:00
2024-05-07 10:27:45 +02:00
def get_cost(self):
return self.ess.get_cost()+self.pv.get_cost()
2024-05-03 14:07:57 +02:00
2024-05-03 15:36:02 +02:00
def simulate(self, data, time_interval):
2024-05-17 10:48:07 +02:00
"""
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.
"""
2024-05-03 14:07:57 +02:00
total_benefit = 0
2024-05-10 23:57:58 +02:00
total_netto_benefit = 0
total_gen = 0
2024-05-15 15:06:43 +02:00
net_grid = 0.
2024-05-03 14:07:57 +02:00
for index, row in data.iterrows():
time = row['time']
2024-05-15 15:06:43 +02:00
self.time.append(time)
2024-05-13 16:48:16 +02:00
# sunlight_intensity = row['sunlight']
pv_yield = row['PV yield[kW/kWp]']
2024-05-03 14:07:57 +02:00
factory_demand = row['demand']
2024-05-10 17:29:08 +02:00
electricity_price = row['buy']
sell_price = row['sell']
2024-05-06 19:24:11 +02:00
# electricity_price = self.grid.get_price_for_time(time)
2024-05-17 10:48:07 +02:00
# 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
2024-05-15 15:06:43 +02:00
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)
2024-05-06 19:24:11 +02:00
self.generated += generated_pv_energy
2024-05-17 10:48:07 +02:00
# generated_pv_energy is larger than factory_demand energy
2024-05-03 14:07:57 +02:00
if generated_pv_energy >= factory_demand * time_interval:
2024-05-17 10:48:07 +02:00
"""
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.
"""
2024-05-03 14:07:57 +02:00
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)
2024-05-03 15:12:52 +02:00
self.ess.storage += charge_to_ess
2024-05-03 14:07:57 +02:00
surplus_after_ess = surplus_energy - charge_to_ess
2024-05-17 10:48:07 +02:00
"""
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.
"""
2024-05-03 14:07:57 +02:00
if surplus_after_ess > 0 and generated_pv_power > self.ess.charge_power + factory_demand:
sold_to_grid = surplus_after_ess
2024-05-10 17:29:08 +02:00
sell_income = sold_to_grid * sell_price
2024-05-03 14:07:57 +02:00
total_benefit += sell_income
2024-05-17 10:48:07 +02:00
"""
Saved energy is the energy that is saved by using the PV to power the factory.
"""
2024-05-06 19:24:11 +02:00
saved_energy = factory_demand * time_interval
2024-05-15 15:06:43 +02:00
self.grid_need_power_kW.append(0)
2024-05-03 14:07:57 +02:00
else:
2024-05-17 10:48:07 +02:00
"""
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
"""
2024-05-03 14:07:57 +02:00
needed_from_ess = factory_demand * time_interval - generated_pv_energy
2024-05-17 10:48:07 +02:00
"""
If the ESS has enough stored energy to power the factory, the energy will be taken from the ESS.
"""
2024-05-06 19:24:11 +02:00
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
2024-05-03 14:07:57 +02:00
self.ess.storage -= discharging_power
2024-05-17 10:48:07 +02:00
"""
In this case, the energy that is needed from the grid is 0.
"""
2024-05-06 19:24:11 +02:00
saved_energy = generated_pv_energy + discharging_power * self.ess.loss
2024-05-15 15:06:43 +02:00
self.grid_need_power_kW.append(0)
2024-05-03 14:07:57 +02:00
else:
2024-05-17 10:48:07 +02:00
"""
If the ESS does not have enough stored energy to power the factory, the energy will be taken from the grid.
"""
2024-05-06 19:24:11 +02:00
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
2024-05-07 10:27:45 +02:00
self.unmet.append((index,time,factory_demand,generated_pv_power))
2024-05-06 19:24:11 +02:00
saved_energy = generated_pv_energy + self.ess.storage * self.ess.loss
2024-05-03 14:07:57 +02:00
self.ess.storage = 0
2024-05-06 19:24:11 +02:00
needed_from_grid = factory_demand * time_interval - saved_energy
net_grid = min(self.grid.capacity * time_interval, needed_from_grid) * self.grid.loss
2024-05-15 15:06:43 +02:00
self.grid_need_power_kW.append(needed_from_grid * 4)
2024-05-10 23:57:58 +02:00
total_gen += saved_energy
2024-05-06 19:24:11 +02:00
benefit = (saved_energy) * electricity_price
cost = net_grid * electricity_price
2024-05-10 23:57:58 +02:00
total_netto_benefit += benefit
2024-05-06 19:24:11 +02:00
total_benefit += benefit - cost
2024-05-17 10:48:07 +02:00
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)
2024-05-03 14:07:57 +02:00
2024-05-10 23:57:58 +02:00
return (total_benefit, total_netto_benefit, total_gen)