Compare commits

...

36 Commits

Author SHA1 Message Date
Hanzhang ma
f1b2959143 add English comments 2024-05-17 10:48:07 +02:00
Hanzhang Ma
df2f953678 update the csv file output code and add a progress bar in the code 2024-05-16 21:12:13 +02:00
Hanzhang Ma
3740136d7c update the format 2024-05-15 15:06:43 +02:00
Hanzhang ma
e04e01e943 edit read_data.py to accept changeable data 2024-05-13 22:22:03 +02:00
Hanzhang Ma
9f472b4bf4 add city data 2024-05-13 17:00:59 +02:00
Hanzhang Ma
127f005dcd add city data 2024-05-13 16:59:12 +02:00
Hanzhang Ma
c5edf456c5 move some data to the folder 2024-05-13 16:54:20 +02:00
Hanzhang Ma
d8ece46e14 add city data 2024-05-13 16:52:43 +02:00
Hanzhang Ma
566ebca6cd make factory demand to csv file 2024-05-13 16:49:22 +02:00
Hanzhang Ma
c8c37b756c update pv yield code 2024-05-13 16:48:16 +02:00
Hanzhang Ma
4f1a47d505 update generate data code 2024-05-13 16:47:56 +02:00
Hanzhang Ma
ad9b5e6a19 update generate data code 2024-05-13 16:26:24 +02:00
Hanzhang Ma
33871fba77 done with convert data 2024-05-13 16:09:28 +02:00
Hanzhang Ma
9d143399ed get new intensity file 2024-05-13 15:24:44 +02:00
Hanzhang Ma
72d4ce811e data 2024-05-11 00:03:14 +02:00
Hanzhang Ma
060fa5bff1 v0.0.5 code 2024-05-10 23:57:58 +02:00
Hanzhang Ma
ebebd2d481 add read sell data 2024-05-10 17:40:54 +02:00
Hanzhang Ma
a330946f71 add monthly plots code 2024-05-10 17:29:08 +02:00
Hanzhang Ma
32e8e59c82 read electricity data locally 2024-05-10 17:18:41 +02:00
Hanzhang Ma
c4ec4590c2 Merge branch 'main' of http://ff.mhrooz.xyz:3980/iicd/simple-pv-simulator into main 2024-05-10 13:28:02 +02:00
Hanzhang ma
cb0bd2c3e0 delete the debug code 2024-05-10 10:15:44 +02:00
Hanzhang Ma
4364411485 update draw code 2024-05-10 10:08:08 +02:00
Hanzhang Ma
54dc8b744c for merge 2024-05-10 01:11:17 +02:00
Hanzhang Ma
d4fde202d0 update draw.py 2024-05-10 01:10:55 +02:00
Hanzhang Ma
58a7662a8b fix all draw code 2024-05-10 00:11:34 +02:00
Hanzhang ma
d791ac481a add draw.py 2024-05-09 23:49:48 +02:00
Hanzhang ma
4b72bc6fa3 add some contour code 2024-05-09 13:19:49 +02:00
Hanzhang ma
eb24361ea3 add some contour code 2024-05-09 13:15:53 +02:00
Hanzhang ma
fd3bbbf212 merge 2024-05-09 03:15:24 +02:00
Hanzhang ma
760cdc9c1f wait for merging 2024-05-09 03:11:16 +02:00
Hanzhang ma
23826aed75 wait for merging 2024-05-09 03:09:37 +02:00
Hanzhang Ma
8cf3d6472c add contour code but need to update
update electricity data
2024-05-08 16:07:14 +02:00
Hanzhang ma
c217b6309c add version number and remove some draw code 2024-05-08 09:39:56 +02:00
Hanzhang ma
f85fcd58f2 add save data code 2024-05-07 21:06:03 +02:00
Hanzhang ma
cf7a66276f Merge branch 'main' of http://ff.mhrooz.xyz:3980/iicd/simple-pv-simulator 2024-05-07 20:21:24 +02:00
Hanzhang ma
bf74e87ba0 for merge 2024-05-07 20:21:20 +02:00
20 changed files with 317330 additions and 71892 deletions

View File

@@ -21,6 +21,13 @@ class EnergySystem:
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
@@ -30,110 +37,155 @@ class EnergySystem:
def get_cost(self):
return self.ess.get_cost()+self.pv.get_cost()
# 优先使用PV供电给工厂 - 如果PV输出能满足工厂的需求则直接供电多余的电能用来给ESS充电。
# PV不足时使用ESS补充 - 如果PV输出不足以满足工厂需求首先从ESS获取所需电量。
# 如果ESS也不足以满足需求再从电网获取 - 当ESS中的存储电量也不足以补充时再从电网购买剩余所需电量。
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']
sunlight_intensity = row['sunlight']
self.time.append(time)
# sunlight_intensity = row['sunlight']
pv_yield = row['PV yield[kW/kWp]']
factory_demand = row['demand']
electricity_price = row['price']
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)
# 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)
generated_pv_power = self.pv.capacity * sunlight_intensity # 生成的功率,单位 kW
generated_pv_energy = generated_pv_power * time_interval * self.pv.loss # 生成的能量,单位 kWh
self.generated += generated_pv_energy
# pv生成的能量如果比工厂的需求要大
# generated_pv_energy is larger than factory_demand energy
if generated_pv_energy >= factory_demand * time_interval:
# 剩余的能量(kwh) = pv生成的能量 - 工厂需求的功率 * 时间间隔
"""
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
# 要充到ess中的能量 = min(剩余的能量,ess的充电功率*时间间隔(ess在时间间隔内能充进的电量),ess的容量-ess储存的能量(ess中能冲进去的电量))
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
# 如果还有电量盈余,且pv功率大于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 * self.grid.sell_price
sell_income = sold_to_grid * sell_price
total_benefit += sell_income
# 节省的能量 = 工厂需求的能量 * 时间段
# total_energy = factory_demand * time_interval
"""
Saved energy is the energy that is saved by using the PV to power the factory.
"""
saved_energy = factory_demand * time_interval
# pv比工厂的需求小
self.grid_need_power_kW.append(0)
else:
# 从ess中需要的电量 = 工厂需要的电量 - pv中的电量
"""
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
# 如果ess中存的电量比需要的多
"""
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
# 节省下来的能量 = pv的能量 + 放出来的能量
"""
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:
# 如果存的电量不够
# 需要把ess中的所有电量释放出来
"""
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
log = f"index: {index}, time: {time}, SoC:{self.ess.storage / self.ess.capacity}%, storage: {self.ess.storage}, pv_gen:{generated_pv_power}, power_demand: {factory_demand}, overload_cnt:{self.overload_cnt}, day:{int(index/96) + 1}"
self.unmet.append((index,time,factory_demand,generated_pv_power))
# with open(f'plots/summary/ess-{self.ess.capacity}-pv-{self.pv.capacity}', 'a') as f:
# f.write(log)
# print(log)
# self.unmet.append(log)
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
# grid_energy += net_grid
# total_energy += net_grid
# print(total_energy)
# 工厂需求量-总能量
# unmet_demand = max(0, factory_demand * time_interval - total_energy)
# benefit = (total_energy - unmet_demand) * electricity_price
self.grid_need_power_kW.append(needed_from_grid * 4)
total_gen += saved_energy
benefit = (saved_energy) * electricity_price
cost = net_grid * electricity_price
# print(f"time:{time} benefit: {benefit}, cost: {cost}")
total_netto_benefit += benefit
total_benefit += benefit - cost
# # spring
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)
# 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)
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
return (total_benefit, total_netto_benefit, total_gen)

File diff suppressed because it is too large Load Diff

View File

@@ -15,31 +15,39 @@
"capacity": 5000
},
"pv_capacities":{
"begin": 5000,
"begin": 0,
"end": 50000,
"groups": 3
"groups": 3
},
"ess_capacities":{
"begin": 5000,
"end": 50000,
"groups": 3
"begin": 0,
"end": 100000,
"groups": 3
},
"time_interval":{
"numerator": 15,
"denominator": 60
},
"annotated": {
"unmet_prob": true,
"unmet_prob": false,
"benefit": false,
"cost": true
"cost": false,
"roi": false
},
"figure_size":{
"height": 18,
"length": 20
"height": 9,
"length": 10
},
"plot_title":{
"unmet_prob": "Probability of unmet electricity demands",
"cost": "Costs of the PV System/million Eur",
"benefit": "Benefit Heatmap Based on PV and ESS Capacities (thousand EUR/year)"
"unmet_prob": "Coverage Rate of Factory Electrical Demands",
"cost": "Costs of Microgrid system [m-EUR]",
"benefit": "Financial Profit Based on Py & Ess Configuration (k-EUR / year)",
"roi": "ROI"
},
"data_path": {
"pv_yield": "read_data/Serbia.csv",
"demand": "read_data/factory_power1.csv",
"sell": "read_data/electricity_price_data_sell.csv",
"buy": "read_data/electricity_price_data.csv"
}
}

View File

@@ -10,12 +10,12 @@ class pv_config:
def get_cost_per_year(self):
return self.capacity * self.cost_per_kW / self.lifetime
class ess_config:
def __init__(self, capacity, cost_per_kW, lifetime, loss, charge_power, discharge_power):
def __init__(self, capacity, cost_per_kW, lifetime, loss, charge_power, discharge_power, storage=0):
self.capacity = capacity
self.cost_per_kW = cost_per_kW
self.lifetime = lifetime
self.loss = loss
self.storage = 100
self.storage = storage
self.charge_power = charge_power
self.discharge_power = discharge_power
def get_cost(self):

209
draw.py Normal file
View File

@@ -0,0 +1,209 @@
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from matplotlib.ticker import FuncFormatter
import numpy as np
import pandas as pd
import os
import seaborn as sns
import json
from matplotlib.colors import LinearSegmentedColormap
def read_data(file_name: str):
with open(file_name, 'r') as f:
data = json.load(f)
for key, value in data.items():
for subkey, subvalue in value.items():
data[key][subkey] = float(subvalue)
df = pd.DataFrame.from_dict(data, orient='index')
df = df.T
df.index = pd.to_numeric(df.index)
df.columns = pd.to_numeric(df.columns)
return df
def draw_results(results, filename, title_benefit, annot_benefit=False, figure_size=(10, 10)):
df=results
df = df.astype(float)
df.index = df.index / 1000
df.index = df.index.map(int)
df.columns = df.columns / 1000
df.columns = df.columns.map(int)
min_value = df.min().min()
max_value = df.max().max()
max_scale = max(abs(min_value/1000), abs(max_value/1000))
df[df.columns[-1] + 1] = df.iloc[:, -1]
new_Data = pd.DataFrame(index=[df.index[-1] + 1], columns=df.columns)
for i in df.columns:
new_Data[i] = df[i].iloc[-1]
df = pd.concat([df, new_Data])
X, Y = np.meshgrid(np.arange(df.shape[1]), np.arange(df.shape[0]))
def fmt(x,pos):
return '{:.0f}'.format(x/1000)
cmap = sns.color_palette("coolwarm", as_cmap=True)
plt.figure(figsize=figure_size)
ax = sns.heatmap(df/1000, fmt=".1f", cmap=cmap, vmin=-max_scale, vmax=max_scale, annot=annot_benefit)
CS = ax.contour(X, Y, df, colors='black', alpha=0.5)
ax.clabel(CS, inline=True, fontsize=10, fmt=FuncFormatter(fmt))
plt.title(title_benefit)
plt.gca().invert_yaxis()
plt.xlim(0, df.shape[1] - 1)
plt.ylim(0, df.shape[0] - 1)
plt.xlabel('ESS Capacity (MWh)')
plt.ylabel('PV Capacity (MW)')
plt.savefig(filename)
def draw_cost(costs, filename, title_cost, annot_cost=False, figure_size=(10, 10)):
df = costs
df = df.astype(int)
df.index = df.index / 1000
df.index = df.index.map(int)
df.columns = df.columns / 1000
df.columns = df.columns.map(int)
df[df.columns[-1] + 1] = df.iloc[:, -1]
new_Data = pd.DataFrame(index=[df.index[-1] + 1], columns=df.columns)
for i in df.columns:
new_Data[i] = df[i].iloc[-1]
df = pd.concat([df, new_Data])
X, Y = np.meshgrid(np.arange(df.shape[1]), np.arange(df.shape[0]))
def fmt(x, pos):
return '{:.0f}'.format(x / 1000000)
plt.figure(figsize=figure_size)
ax = sns.heatmap(df/1000000, fmt=".1f", cmap='viridis', annot=annot_cost)
CS = ax.contour(X, Y, df, colors='black', alpha=0.5)
ax.clabel(CS, inline=True, fontsize=10, fmt=FuncFormatter(fmt))
plt.title(title_cost)
plt.gca().invert_yaxis()
plt.xlim(0, df.shape[1] - 1)
plt.ylim(0, df.shape[0] - 1)
plt.xlabel('ESS Capacity (MWh)')
plt.ylabel('PV Capacity (MW)')
plt.savefig(filename)
def draw_overload(overload_cnt, filename, title_unmet, annot_unmet=False, figure_size=(10, 10)):
df = overload_cnt
df = (4 * 24 * 365 - df) / (4 * 24 * 365)
df = df.astype(float)
df.index = df.index / 1000
df.index = df.index.map(int)
df.columns = df.columns / 1000
df.columns = df.columns.map(int)
min_value = df.min().min()
max_value = df.max().max()
df[df.columns[-1] + 1] = df.iloc[:, -1]
new_Data = pd.DataFrame(index=[df.index[-1] + 1], columns=df.columns)
for i in df.columns:
new_Data[i] = df[i].iloc[-1]
# print(new_Data)
df = pd.concat([df, new_Data])
plt.figure(figsize=figure_size)
cmap = LinearSegmentedColormap.from_list("", ["white", "blue"])
ax = sns.heatmap(df, fmt=".00%", cmap=cmap, vmin=0, vmax=1, annot=annot_unmet)
cbar = ax.collections[0].colorbar
cbar.set_ticks([0, 0.25, 0.5, 0.75, 1])
cbar.set_ticklabels(['0%', '25%', '50%', '75%', '100%'])
cbar.ax.yaxis.set_major_formatter(ticker.FuncFormatter(lambda x, pos: f'{x:.0%}'))
X, Y = np.meshgrid(np.arange(df.shape[1]), np.arange(df.shape[0]))
def fmt(x, pos):
return '{:.0f}%'.format(x * 100)
CS = ax.contour(X, Y, df, colors='black', alpha=0.5)
ax.clabel(CS, inline=True, fontsize=10, fmt=FuncFormatter(fmt))
plt.xlim(0, df.shape[1] - 1)
plt.ylim(0, df.shape[0] - 1)
plt.title(title_unmet)
plt.xlabel('ESS Capacity (MWh)')
plt.ylabel('PV Capacity (MW)')
plt.savefig(filename)
with open('config.json', 'r') as f:
js_data = json.load(f)
data = pd.read_csv('combined_data.csv')
time_interval = js_data["time_interval"]["numerator"] / js_data["time_interval"]["denominator"]
pv_loss = js_data["pv"]["loss"]
pv_cost_per_kW = js_data["pv"]["cost_per_kW"]
pv_lifetime = js_data["pv"]["lifetime"]
ess_loss = js_data["ess"]["loss"]
ess_cost_per_kW = js_data["ess"]["cost_per_kW"]
ess_lifetime = js_data["ess"]["lifetime"]
grid_loss = js_data["grid"]["loss"]
sell_price = js_data["grid"]["sell_price"]
grid_capacity = js_data["grid"]["capacity"]
pv_begin = js_data["pv_capacities"]["begin"]
pv_end = js_data["pv_capacities"]["end"]
pv_groups = js_data["pv_capacities"]["groups"]
ess_begin = js_data["ess_capacities"]["begin"]
ess_end = js_data["ess_capacities"]["end"]
ess_groups = js_data["ess_capacities"]["groups"]
annot_unmet = js_data["annotated"]["unmet_prob"]
annot_benefit = js_data["annotated"]["benefit"]
annot_cost = js_data["annotated"]["cost"]
title_unmet = js_data["plot_title"]["unmet_prob"]
title_cost = js_data["plot_title"]["cost"]
title_benefit = js_data["plot_title"]["benefit"]
figure_size = (js_data["figure_size"]["length"], js_data["figure_size"]["height"])
directory = 'data/'
file_list = [f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))]
split_files = [f.split('-') for f in file_list]
costs_files = [f for f in split_files if f[-1].endswith('costs.json')]
print(f'find costs files: {costs_files}')
overload_files = [f for f in split_files if f[-1].endswith('overload_cnt.json')]
print(f'find coverage/unmet files: {overload_files}')
results_files = [f for f in split_files if f[-1].endswith('results.json')]
print(f'find profit/benefit files: {results_files}')
costs_dfs = [read_data(directory + '-'.join(f)) for f in costs_files]
overload_dfs = [read_data(directory + '-'.join(f)) for f in overload_files]
results_dfs = [read_data(directory + '-'.join(f)) for f in results_files]
for costs_df, overload_df, results_df in zip(costs_dfs, overload_dfs, results_dfs):
draw_cost(costs_df,
f'plots/costs-ess-{int(costs_df.columns[0])}-{int(costs_df.columns[-1])}-pv-{int(costs_df.index[0])}-{int(costs_df.index[-1])}.png',
title_cost=title_cost,
annot_cost=annot_cost)
draw_overload(overload_df,
f'plots/overload-ess-{overload_df.columns[0]}-{overload_df.columns[-1]}-pv-{overload_df.index[0]}-{overload_df.index[-1]}.png',
title_unmet=title_unmet,
annot_unmet=False)
draw_results(results_df,
f'plots/results-ess-{results_df.columns[0]}-{results_df.columns[-1]}-pv-{results_df.index[0]}-{results_df.index[-1]}.png',
title_benefit=title_benefit,
annot_benefit=False)

File diff suppressed because it is too large Load Diff

1969
main.ipynb

File diff suppressed because one or more lines are too long

845
main.py
View File

@@ -1,199 +1,25 @@
#!/usr/bin/env python
# coding: utf-8
# In[40]:
# In[83]:
import pandas as pd
class pv_config:
def __init__(self, capacity, cost_per_kW, lifetime, loss):
self.capacity = capacity
self.cost_per_kW = cost_per_kW
self.lifetime = lifetime
self.loss = loss
def get_cost(self):
return self.capacity * self.cost_per_kW
def get_cost_per_year(self):
return self.capacity * self.cost_per_kW / self.lifetime
class ess_config:
def __init__(self, capacity, cost_per_kW, lifetime, loss, charge_power, discharge_power):
self.capacity = capacity
self.cost_per_kW = cost_per_kW
self.lifetime = lifetime
self.loss = loss
self.storage = 100
self.charge_power = charge_power
self.discharge_power = discharge_power
def get_cost(self):
return self.capacity * self.cost_per_kW
def get_cost_per_year(self):
return self.capacity * self.cost_per_kW / self.lifetime
class grid_config:
def __init__(self, capacity, grid_loss, sell_price):
# self.price_schedule = price_schedule
self.loss = grid_loss
self.sell_price = sell_price
self.capacity = capacity
def get_price_for_time(self, time):
hour, minute = map(int, time.split(':'))
total_minutes = hour * 60 + minute
for _, row in self.price_schedule.iterrows():
start_hour, start_minute = map(int, row['time_start'].split(':'))
end_hour, end_minute = map(int, row['time_end'].split(':'))
start_total_minutes = start_hour * 60 + start_minute
end_total_minutes = end_hour * 60 + end_minute
if start_total_minutes <= total_minutes < end_total_minutes:
return row['price']
return 0.1 # 默认电价,以防万一没有匹配的时间段
# In[41]:
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.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()
# 优先使用PV供电给工厂 - 如果PV输出能满足工厂的需求则直接供电多余的电能用来给ESS充电。
# PV不足时使用ESS补充 - 如果PV输出不足以满足工厂需求首先从ESS获取所需电量。
# 如果ESS也不足以满足需求再从电网获取 - 当ESS中的存储电量也不足以补充时再从电网购买剩余所需电量。
def simulate(self, data, time_interval):
total_benefit = 0
for index, row in data.iterrows():
time = row['time']
sunlight_intensity = row['sunlight']
factory_demand = row['demand']
electricity_price = row['price']
# 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 = self.pv.capacity * sunlight_intensity # 生成的功率,单位 kW
generated_pv_energy = generated_pv_power * time_interval * self.pv.loss # 生成的能量,单位 kWh
self.generated += generated_pv_energy
# pv生成的能量如果比工厂的需求要大
if generated_pv_energy >= factory_demand * time_interval:
# 剩余的能量(kwh) = pv生成的能量 - 工厂需求的功率 * 时间间隔
surplus_energy = generated_pv_energy - factory_demand * time_interval
# 要充到ess中的能量 = min(剩余的能量,ess的充电功率*时间间隔(ess在时间间隔内能充进的电量),ess的容量-ess储存的能量(ess中能冲进去的电量))
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
# 如果还有电量盈余,且pv功率大于ess的充电功率+工厂的需求功率则准备卖电
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 * self.grid.sell_price
total_benefit += sell_income
# 节省的能量 = 工厂需求的能量 * 时间段
# total_energy = factory_demand * time_interval
saved_energy = factory_demand * time_interval
# pv比工厂的需求小
else:
# 从ess中需要的电量 = 工厂需要的电量 - pv中的电量
needed_from_ess = factory_demand * time_interval - generated_pv_energy
# 如果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
# 节省下来的能量 = pv的能量 + 放出来的能量
saved_energy = generated_pv_energy + discharging_power * self.ess.loss
else:
# 如果存的电量不够
# 需要把ess中的所有电量释放出来
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
log = f"index: {index}, time: {time}, SoC:{self.ess.storage / self.ess.capacity}%, storage: {self.ess.storage}, pv_gen:{generated_pv_power}, power_demand: {factory_demand}, overload_cnt:{self.overload_cnt}, day:{int(index/96) + 1}"
self.unmet.append((index,time,factory_demand,generated_pv_power))
# with open(f'plots/summary/ess-{self.ess.capacity}-pv-{self.pv.capacity}', 'a') as f:
# f.write(log)
print(log)
# self.unmet.append(log)
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
# grid_energy += net_grid
# total_energy += net_grid
# print(total_energy)
# 工厂需求量-总能量
# unmet_demand = max(0, factory_demand * time_interval - total_energy)
# benefit = (total_energy - unmet_demand) * electricity_price
benefit = (saved_energy) * electricity_price
cost = net_grid * electricity_price
# print(f"time:{time} benefit: {benefit}, cost: {cost}")
total_benefit += benefit - cost
# # spring
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)
# 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
import os
import glob
import shutil
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
from matplotlib.ticker import FuncFormatter
import numpy as np
import pandas as pd
import os
import seaborn as sns
import json
from matplotlib.colors import LinearSegmentedColormap
def clear_folder_make_ess_pv(folder_path):
shutil.rmtree(folder_path)
if os.path.isdir(folder_path):
shutil.rmtree(folder_path)
os.makedirs(folder_path)
os.makedirs(os.path.join(folder_path,'ess'))
os.makedirs(os.path.join(folder_path,'pv'))
@@ -202,27 +28,32 @@ folder_path = 'plots'
clear_folder_make_ess_pv(folder_path)
# In[42]:
# In[84]:
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
import pandas as pd
from EnergySystem import EnergySystem
from config import pv_config, grid_config, ess_config
figure_size = (10,8)
# In[43]:
# In[85]:
import json
print("Version 0.0.7\n")
with open('config.json', 'r') as f:
js_data = json.load(f)
data = pd.read_csv('combined_data.csv')
time_interval = js_data["time_interval"]["numerator"] / js_data["time_interval"]["denominator"]
# print(time_interval)
pv_loss = js_data["pv"]["loss"]
pv_cost_per_kW = js_data["pv"]["cost_per_kW"]
@@ -243,15 +74,49 @@ pv_groups = js_data["pv_capacities"]["groups"]
ess_begin = js_data["ess_capacities"]["begin"]
ess_end = js_data["ess_capacities"]["end"]
ess_groups = js_data["ess_capacities"]["groups"]
annot_unmet = js_data["annotated"]["unmet_prob"]
annot_benefit = js_data["annotated"]["benefit"]
annot_cost = js_data["annotated"]["cost"]
annot_roi = js_data["annotated"]["roi"]
title_unmet = js_data["plot_title"]["unmet_prob"]
title_cost = js_data["plot_title"]["cost"]
title_benefit = js_data["plot_title"]["benefit"]
title_roi = js_data["plot_title"]["roi"]
figure_size = (js_data["figure_size"]["length"], js_data["figure_size"]["height"])
data = pd.read_csv('combined_data.csv')
granularity = js_data["time_interval"]["numerator"]
months_days = [31,28,31,30,31,30,31,31,30,31,30,31]
def get_month_coe(num, granularity):
return 60 / granularity * 24 * months_days[num]
months_index = [get_month_coe(num, granularity) for num in range(12)]
months_data = []
for i in range(1,12):
months_index[i] += months_index[i-1]
for i in range(12):
start = 0 if i == 0 else months_index[i-1]
end = months_index[i]
months_data.append(data.iloc[int(start):int(end)])
pv_capacities = np.linspace(pv_begin, pv_end, pv_groups)
ess_capacities = np.linspace(ess_begin, ess_end, ess_groups)
results = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
affords = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
costs = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
overload_cnt = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
# results = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
# affords = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
# costs = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
# overload_cnt = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
# In[44]:
# In[86]:
hour_demand = []
@@ -267,238 +132,440 @@ plt.savefig('plots/demand.png')
plt.close()
# In[45]:
# In[87]:
def cal_profit(es: EnergySystem, saved_money):
profit = saved_money - es.ess.get_cost_per_year() - es.pv.get_cost_per_year()
return profit
def draw_results(results, filename, title_benefit, annot_benefit=False, figure_size=(10, 10)):
df=results
df = df.astype(float)
df.index = df.index / 1000
df.index = df.index.map(int)
df.columns = df.columns / 1000
df.columns = df.columns.map(int)
min_value = df.min().min()
max_value = df.max().max()
max_scale = max(abs(min_value/1000), abs(max_value/1000))
df[df.columns[-1] + 1] = df.iloc[:, -1]
new_Data = pd.DataFrame(index=[df.index[-1] + 1], columns=df.columns)
for i in df.columns:
new_Data[i] = df[i].iloc[-1]
df = pd.concat([df, new_Data])
X, Y = np.meshgrid(np.arange(df.shape[1]), np.arange(df.shape[0]))
def fmt(x,pos):
return '{:.0f}'.format(x/1000)
cmap = sns.color_palette("coolwarm", as_cmap=True)
plt.figure(figsize=figure_size)
ax = sns.heatmap(df/1000, fmt=".1f", cmap=cmap, vmin=-max_scale, vmax=max_scale, annot=annot_benefit)
CS = ax.contour(X, Y, df, colors='black', alpha=0.5)
ax.clabel(CS, inline=True, fontsize=10, fmt=FuncFormatter(fmt))
plt.title(title_benefit)
plt.gca().invert_yaxis()
plt.xlim(0, df.shape[1] - 1)
plt.ylim(0, df.shape[0] - 1)
plt.xlabel('ESS Capacity (MWh)')
plt.ylabel('PV Capacity (MW)')
plt.savefig(filename)
# In[46]:
# In[88]:
for pv_capacity in pv_capacities:
print(f"pv_capacity:{pv_capacity}")
for ess_capacity in ess_capacities:
print(f"ess_capacity:{ess_capacity}")
pv = pv_config(capacity=pv_capacity,
cost_per_kW=pv_cost_per_kW,
lifetime=pv_lifetime,
loss=pv_loss)
ess = ess_config(capacity=ess_capacity,
cost_per_kW=ess_cost_per_kW,
lifetime=ess_lifetime,
loss=ess_loss,
charge_power=ess_capacity,
discharge_power=ess_capacity)
grid = grid_config(capacity=grid_capacity,
grid_loss=grid_loss,
sell_price= sell_price)
energySystem = EnergySystem(pv_type=pv,
ess_type=ess,
grid_type= grid)
benefit = energySystem.simulate(data, time_interval)
results.loc[pv_capacity,ess_capacity] = cal_profit(energySystem, benefit)
affords.loc[pv_capacity,ess_capacity] = energySystem.afford
overload_cnt.loc[pv_capacity,ess_capacity] = energySystem.overload_cnt
costs.loc[pv_capacity,ess_capacity] = energySystem.ess.capacity * energySystem.ess.cost_per_kW + energySystem.pv.capacity * energySystem.pv.cost_per_kW
pv_generated = energySystem.day_generated
ess_generated = energySystem.hour_stored
ess_generated_2 = energySystem.hour_stored_2
plt.figure(figsize=(10,8));
plt.plot(ess_generated)
plt.xlabel('day #')
plt.ylabel('SoC %')
plt.title(f'14:00 ESS SoC \n PV cap:{pv_capacity}, ESS cap:{ess_capacity}')
plt.savefig(f'plots/ess/1400-{pv_capacity}-{ess_capacity}.png')
plt.close()
plt.figure(figsize=(10,8));
plt.plot(ess_generated_2)
plt.xlabel('day #')
plt.ylabel('SoC%')
plt.title(f'08:00 ESS SoC \n PV cap:{pv_capacity}, ESS cap:{ess_capacity}')
plt.savefig(f'plots/ess/0800-{pv_capacity}-{ess_capacity}.png')
plt.close()
print(energySystem.unmet)
spring_week_start = energySystem.season_start
spring_week_end = spring_week_start + energySystem.week_length
# summer_week_start = energySystem.season_start + 1 * energySystem.season_step
# summer_week_end = summer_week_start + energySystem.week_length
# autumn_week_start = energySystem.season_start + 2 * energySystem.season_step
# autumn_week_end = autumn_week_start + energySystem.week_length
# winter_week_start = energySystem.season_start + 3 * energySystem.season_step
# winter_week_end = winter_week_start+ energySystem.week_length
def draw_roi(costs, results, filename, title_roi, days=365, annot_roi=False, figure_size=(10, 10)):
costs = costs.astype(float)
costs = costs / 365
costs = costs * days
spring_consume_data = []
# summer_consume_data = []
# autumn_consume_data = []
# winter_consume_data = []
for index, row in data.iterrows():
if index in range(spring_week_start, spring_week_end):
spring_consume_data.append(row['demand'])
# elif index in range(summer_week_start, summer_week_end):
# summer_consume_data.append(row['demand'])
# elif index in range(autumn_week_start, autumn_week_end):
# autumn_consume_data.append(row['demand'])
# elif index in range(winter_week_start, winter_week_end):
# winter_consume_data.append(row['demand'])
df = results
df = costs / df
if 0 in df.index and 0 in df.columns:
df.loc[0,0] = 100
df[df > 80] = 100
# print(df)
spring_week_time = list(range(spring_week_start, spring_week_end))
# summer_week_time = list(range(summer_week_start, summer_week_end))
# autumn_week_time = list(range(autumn_week_start, autumn_week_end))
# winter_week_time = list(range(winter_week_start, winter_week_end))
df = df.astype(float)
df.index = df.index / 1000
df.index = df.index.map(int)
df.columns = df.columns / 1000
df.columns = df.columns.map(int)
min_value = df.min().min()
max_value = df.max().max()
# print(max_value)
max_scale = max(abs(min_value), abs(max_value))
spring_pv_generated = energySystem.spring_week_gen
# summer_pv_generated = energySystem.summer_week_gen
# autumn_pv_generated = energySystem.autumn_week_gen
# winter_pv_generated = energySystem.winter_week_gen
df[df.columns[-1] + 1] = df.iloc[:, -1]
new_Data = pd.DataFrame(index=[df.index[-1] + 1], columns=df.columns)
for i in df.columns:
new_Data[i] = df[i].iloc[-1]
df = pd.concat([df, new_Data])
# spring_soc = energySystem.spring_week_soc
# summer_soc = energySystem.summer_week_soc
# autumn_soc = energySystem.autumn_week_soc
# winter_soc = energySystem.winter_week_soc
X, Y = np.meshgrid(np.arange(df.shape[1]), np.arange(df.shape[0]))
def fmt(x,pos):
return '{:.0f}'.format(x)
# fig, ax1 = plt.subplots()
plt.plot(spring_week_time, spring_pv_generated, label = 'pv generation')
plt.plot(spring_week_time, spring_consume_data, label = 'factory consume')
plt.ylabel('Power / kW')
plt.xlabel('15 min #')
plt.title(f'ess: {energySystem.ess.capacity/1000 } MWh pv: {energySystem.pv.capacity/1000 } MW spring week generate condition')
plt.legend()
plt.savefig(f'plots/{energySystem.ess.capacity}-{energySystem.pv.capacity}-spring.png')
plt.close()
# plt.plot(summer_week_time, summer_pv_generated, label = 'pv generation')
# plt.plot(summer_week_time, summer_consume_data, label = 'factory consume')
# plt.ylabel('Power / kW')
# plt.xlabel('15 min #')
# plt.title(f'ess: {energySystem.ess.capacity/1000 } MWh pv: {energySystem.pv.capacity/1000 } MW summer week generate condition')
# plt.legend()
# plt.savefig(f'plots/{energySystem.ess.capacity}-{energySystem.pv.capacity}-summer.png')
# plt.close()
# plt.plot(autumn_week_time, autumn_pv_generated, label = 'pv generation')
# plt.plot(autumn_week_time, autumn_consume_data, label = 'factory consume')
# plt.ylabel('Power / kW')
# plt.xlabel('15 min #')
# plt.title(f'ess: {energySystem.ess.capacity/1000 } MWh pv: {energySystem.pv.capacity/1000 } MW autumn week generate condition')
# plt.legend()
# plt.savefig(f'plots/{energySystem.ess.capacity}-{energySystem.pv.capacity}-autumn.png')
# plt.close()
# plt.plot(winter_week_time, winter_pv_generated, label = 'pv generation')
# plt.plot(winter_week_time, winter_consume_data, label = 'factory consume')
# plt.ylabel('Power / kW')
# plt.xlabel('15 min #')
# plt.title(f'ess: {energySystem.ess.capacity/1000 } MWh pv: {energySystem.pv.capacity/1000 } MW winter week generate condition')
# plt.legend()
# plt.savefig(f'plots/{energySystem.ess.capacity}-{energySystem.pv.capacity}-winter.png')
# plt.close()
plt.figure();
plt.plot(pv_generated)
plt.xlabel('day #')
plt.ylabel('Electricity kWh')
plt.title(f'PV generated pv cap:{pv_capacity}, ess cap:{ess_capacity}')
plt.savefig(f'plots/pv/{pv_capacity}-{ess_capacity}.png')
cmap = sns.color_palette("Greys", as_cmap=True)
plt.figure(figsize=figure_size)
ax = sns.heatmap(df, fmt=".1f", cmap=cmap, vmin=0, vmax=100, annot=annot_benefit)
CS = ax.contour(X, Y, df, colors='black', alpha=0.5)
ax.clabel(CS, inline=True, fontsize=10, fmt=FuncFormatter(fmt))
plt.title(title_roi)
plt.gca().invert_yaxis()
plt.xlim(0, df.shape[1] - 1)
plt.ylim(0, df.shape[0] - 1)
plt.xlabel('ESS Capacity (MWh)')
plt.ylabel('PV Capacity (MW)')
plt.savefig(filename)
plt.close()
# plt.show()
# In[89]:
def draw_cost(costs, filename, title_cost, annot_cost=False, figure_size=(10, 10)):
df = costs
df = df.astype(int)
df.index = df.index / 1000
df.index = df.index.map(int)
df.columns = df.columns / 1000
df.columns = df.columns.map(int)
df[df.columns[-1] + 1] = df.iloc[:, -1]
new_Data = pd.DataFrame(index=[df.index[-1] + 1], columns=df.columns)
for i in df.columns:
new_Data[i] = df[i].iloc[-1]
df = pd.concat([df, new_Data])
X, Y = np.meshgrid(np.arange(df.shape[1]), np.arange(df.shape[0]))
def fmt(x, pos):
return '{:.0f}'.format(x / 1000000)
plt.figure(figsize=figure_size)
ax = sns.heatmap(df/1000000, fmt=".1f", cmap='viridis', annot=annot_cost)
CS = ax.contour(X, Y, df, colors='black', alpha=0.5)
ax.clabel(CS, inline=True, fontsize=10, fmt=FuncFormatter(fmt))
plt.title(title_cost)
plt.gca().invert_yaxis()
plt.xlim(0, df.shape[1] - 1)
plt.ylim(0, df.shape[0] - 1)
plt.xlabel('ESS Capacity (MWh)')
plt.ylabel('PV Capacity (MW)')
plt.savefig(filename)
plt.close()
# In[90]:
def draw_overload(overload_cnt, filename, title_unmet, annot_unmet=False, figure_size=(10, 10), days=365, granularity=15):
df = overload_cnt
# print(days, granularity)
coef = 60 / granularity * days * 24
# print(coef)
# print(df)
df = ( coef - df) / coef
# print(df)
df = df.astype(float)
df.index = df.index / 1000
df.index = df.index.map(int)
df.columns = df.columns / 1000
df.columns = df.columns.map(int)
df[df.columns[-1] + 1] = df.iloc[:, -1]
new_Data = pd.DataFrame(index=[df.index[-1] + 1], columns=df.columns)
for i in df.columns:
new_Data[i] = df[i].iloc[-1]
# print(new_Data)
df = pd.concat([df, new_Data])
plt.figure(figsize=figure_size)
cmap = LinearSegmentedColormap.from_list("", ["white", "blue"])
ax = sns.heatmap(df, fmt=".00%", cmap=cmap, vmin=0, vmax=1, annot=annot_unmet)
cbar = ax.collections[0].colorbar
cbar.set_ticks([0, 0.10, 0.20, 0.30, 0.40, 0.50, 0.60, 0.70, 0.80, 0.90, 1])
cbar.set_ticklabels(['0%', '10%', '20%', '30%', '40%', '50%', '60%', '70%', '80%', '90%', '100%'])
cbar.ax.yaxis.set_major_formatter(ticker.FuncFormatter(lambda x, pos: f'{x:.0%}'))
X, Y = np.meshgrid(np.arange(df.shape[1]), np.arange(df.shape[0]))
def fmt(x, pos):
return '{:.0f}%'.format(x * 100)
CS = ax.contour(X, Y, df, colors='black', alpha=0.5)
ax.clabel(CS, inline=True, fontsize=10, fmt=FuncFormatter(fmt))
plt.xlim(0, df.shape[1] - 1)
plt.ylim(0, df.shape[0] - 1)
plt.title(title_unmet)
plt.xlabel('ESS Capacity (MWh)')
plt.ylabel('PV Capacity (MW)')
plt.savefig(filename)
plt.close()
# In[91]:
def cal_profit(es: EnergySystem, saved_money, days):
profit = saved_money - es.ess.get_cost_per_year() / 365 * days - es.pv.get_cost_per_year() / 365 * days
return profit
# In[92]:
def generate_data(pv_capacity, pv_cost_per_kW, pv_lifetime, pv_loss, ess_capacity, ess_cost_per_kW, ess_lifetime, ess_loss, grid_capacity, grid_loss, sell_price, time_interval, data, days, storage=0):
pv = pv_config(capacity=pv_capacity,
cost_per_kW=pv_cost_per_kW,
lifetime=pv_lifetime,
loss=pv_loss)
ess = ess_config(capacity=ess_capacity,
cost_per_kW=ess_cost_per_kW,
lifetime=ess_lifetime,
loss=ess_loss,
charge_power=ess_capacity,
discharge_power=ess_capacity,
storage=storage)
grid = grid_config(capacity=grid_capacity,
grid_loss=grid_loss,
sell_price= sell_price)
energySystem = EnergySystem(pv_type=pv,
ess_type=ess,
grid_type= grid)
(benefit, netto_benefit, gen_energy) = energySystem.simulate(data, time_interval)
results = cal_profit(energySystem, benefit, days)
overload_cnt = energySystem.overload_cnt
costs = energySystem.ess.capacity * energySystem.ess.cost_per_kW + energySystem.pv.capacity * energySystem.pv.cost_per_kW
return (results,
overload_cnt,
costs,
netto_benefit,
gen_energy,
energySystem.generated,
energySystem.ess_rest,
energySystem.factory_demand,
energySystem.buy_price_kWh,
energySystem.sell_price_kWh,
energySystem.pv_generated_kWh,
energySystem.grid_need_power_kW,
energySystem.time)
# results = results.astype(float)
# In[93]:
# pv = pv_config(capacity=100000,cost_per_kW=200,lifetime=25,loss=0.95)
# ess = ess_config(capacity=100000,cost_per_kW=300,lifetime=25,loss=0.95,charge_power=100000,discharge_power=100000)
# grid = grid_config(price_schedule=price_schedule, capacity=5000, grid_loss=0.95, sell_price=0.4)
# grid = grid_config(capacity=50000, grid_loss=0.95, sell_price=0.4)
from tqdm import tqdm
months_results = []
months_costs = []
months_overload = []
months_nettos = []
months_gen_energy = []
months_gen_energy2 = []
months_ess_rest = pd.DataFrame(30, index=pv_capacities, columns= ess_capacities)
months_csv_data = {}
for index, month_data in tqdm(enumerate(months_data), total=len(months_data), position=0, leave= True):
results = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
costs = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
overload_cnt = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
nettos = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
gen_energies = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
gen_energies2 = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
factory_demands = {}
buy_prices= {}
sell_prices = {}
pv_generates = {}
grid_need_powers = {}
times = {}
for pv_capacity in tqdm(pv_capacities, total=len(pv_capacities), desc=f'generating pv for month {index + 1}',position=1, leave=False):
factory_demands[pv_capacity] = {}
buy_prices[pv_capacity] = {}
sell_prices[pv_capacity] = {}
pv_generates[pv_capacity] = {}
grid_need_powers[pv_capacity] = {}
times[pv_capacity] = {}
for ess_capacity in ess_capacities:
(result,
overload,
cost,
netto,
gen_energy,
gen_energy2,
ess_rest,
factory_demand,
buy_price,
sell_price,
pv_generate,
grid_need_power,
time) = generate_data(pv_capacity=pv_capacity,
pv_cost_per_kW=pv_cost_per_kW,
pv_lifetime=pv_lifetime,
pv_loss=pv_loss,
ess_capacity=ess_capacity,
ess_cost_per_kW=ess_cost_per_kW,
ess_lifetime=ess_lifetime,
ess_loss=ess_loss,
grid_capacity=grid_capacity,
grid_loss=grid_loss,
sell_price=sell_price,
time_interval=time_interval,
data=month_data,
days=months_days[index],
storage=months_ess_rest.loc[pv_capacity, ess_capacity])
results.loc[pv_capacity,ess_capacity] = result
overload_cnt.loc[pv_capacity,ess_capacity] = overload
costs.loc[pv_capacity,ess_capacity] = cost
nettos.loc[pv_capacity,ess_capacity] = netto
gen_energies.loc[pv_capacity, ess_capacity] = gen_energy
gen_energies2.loc[pv_capacity, ess_capacity] = gen_energy2
months_ess_rest.loc[pv_capacity, ess_capacity] = ess_rest
factory_demands[pv_capacity][ess_capacity] = factory_demand
buy_prices[pv_capacity][ess_capacity] = buy_price
sell_prices[pv_capacity][ess_capacity] = sell_price
pv_generates[pv_capacity][ess_capacity] = pv_generate
grid_need_powers[pv_capacity][ess_capacity] = grid_need_power
times[pv_capacity][ess_capacity] = time
months_csv_data[index] = {"factory_demand": factory_demands, "buy_price": buy_prices, "sell_price": sell_prices, "pv_generate": pv_generates, "grid_need_power": grid_need_powers, "time": times}
months_results.append(results)
months_costs.append(costs)
months_overload.append(overload_cnt)
months_nettos.append(nettos)
months_gen_energy.append(gen_energies)
months_gen_energy2.append(gen_energies2)
draw_results(results=results,
filename=f'plots/pv-{pv_capacity}-ess-{ess_capacity}-month-{index+1}-benefit.png',
title_benefit=title_benefit,
annot_benefit=annot_benefit,
figure_size=figure_size)
draw_overload(overload_cnt=overload_cnt,
filename=f'plots/pv-{pv_capacity}-ess-{ess_capacity}-month-{index+1}-unmet.png',
title_unmet=title_unmet,
annot_unmet=annot_unmet,
figure_size=figure_size,
days=months_days[index],
granularity=granularity)
annual_result = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
annual_costs = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
annual_overload = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
annual_nettos = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
annual_gen = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
annual_gen2 = pd.DataFrame(index=pv_capacities, columns= ess_capacities)
# print(benefit)
# get the yearly results
for pv_capacity in pv_capacities:
for ess_capacity in ess_capacities:
results = 0
costs = 0
overload_cnt = 0
nettos = 0
gen = 0
gen2 = 0
for index, month_data in enumerate(months_data):
results += months_results[index].loc[pv_capacity,ess_capacity]
costs += months_costs[index].loc[pv_capacity,ess_capacity]
overload_cnt += months_overload[index].loc[pv_capacity, ess_capacity]
nettos += months_nettos[index].loc[pv_capacity, ess_capacity]
gen += months_gen_energy[index].loc[pv_capacity, ess_capacity]
gen2 += months_gen_energy[index].loc[pv_capacity, ess_capacity]
annual_result.loc[pv_capacity, ess_capacity] = results
annual_costs.loc[pv_capacity, ess_capacity] = costs
annual_overload.loc[pv_capacity, ess_capacity] = overload_cnt
annual_nettos.loc[pv_capacity, ess_capacity] = nettos
annual_gen.loc[pv_capacity, ess_capacity] = gen
annual_gen2.loc[pv_capacity, ess_capacity] = gen2
draw_cost(costs=annual_costs,
filename='plots/annual_cost.png',
title_cost=title_cost,
annot_cost=annot_cost,
figure_size=figure_size)
draw_results(results=annual_result,
filename='plots/annual_benefit.png',
title_benefit=title_benefit,
annot_benefit=annot_benefit,
figure_size=figure_size)
draw_overload(overload_cnt=annual_overload,
filename='plots/annual_unmet.png',
title_unmet=title_unmet,
annot_unmet=annot_unmet,
figure_size=figure_size)
# In[47]:
# In[94]:
energySystem.unmet
def collapse_months_csv_data(months_csv_data, column_name,pv_capacies, ess_capacities):
data = {}
for pv_capacity in pv_capacities:
data[pv_capacity] = {}
for ess_capacity in ess_capacities:
annual_data = []
for index, month_data in enumerate(months_data):
annual_data.extend(months_csv_data[index][column_name][pv_capacity][ess_capacity])
# months_csv_data[index][column_name][pv_capacity][ess_capacity] = months_csv_data[index][column_name][pv_capacity][ess_capacity].tolist()
data[pv_capacity][ess_capacity] = annual_data
return data
# In[48]:
# In[102]:
df=results
df = df.astype(float)
df.index = df.index / 1000
df.columns = df.columns / 1000
min_value = df.min().min()
max_value = df.max().max()
max_scale = max(abs(min_value/1000), abs(max_value/1000))
plt.figure(figsize=figure_size)
cmap = sns.color_palette("coolwarm", as_cmap=True)
sns.heatmap(df/1000, annot=True, fmt=".1f", cmap=cmap, vmin=-max_scale, vmax=max_scale)
plt.title('Benefit Heatmap Based on PV and ESS Capacities (kEUR/year)')
plt.gca().invert_yaxis()
plt.xlabel('ESS Capacity (MWh)')
plt.ylabel('PV Capacity (MW)')
plt.savefig('plots/benefit.png')
annual_pv_gen = collapse_months_csv_data(months_csv_data, "pv_generate", pv_capacities, ess_capacities)
annual_time = collapse_months_csv_data(months_csv_data, "time", pv_capacities, ess_capacities)
annual_buy_price = collapse_months_csv_data(months_csv_data, "buy_price",pv_capacities, ess_capacities)
annual_sell_price = collapse_months_csv_data(months_csv_data, "sell_price", pv_capacities, ess_capacities)
annual_factory_demand = collapse_months_csv_data(months_csv_data, "factory_demand", pv_capacities, ess_capacities)
annual_grid_need_power = collapse_months_csv_data(months_csv_data, "grid_need_power", pv_capacities, ess_capacities)
from datetime import datetime, timedelta
# In[49]:
df = costs
df = df.astype(int)
df.index = df.index / 1000
df.columns = df.columns / 1000
plt.figure(figsize=figure_size)
sns.heatmap(df/1000000, annot=True, fmt=".1f", cmap='viridis')
plt.title('Costs of the PV System/million Eur')
plt.gca().invert_yaxis()
plt.xlabel('ESS Capacity (MWh)')
plt.ylabel('PV Capacity (MW)')
plt.savefig('plots/costs.png')
# pv = pv_config(capacity=100000,cost_per_kW=200,lifetime=25,loss=0.95)
# ess = ess_config(capacity=100000,cost_per_kW=300,lifetime=25,loss=0.95,charge_power=100000,discharge_power=100000)
# grid = grid_config(price_schedule=price_schedule, capacity=5000, grid_loss=0.95, sell_price=0.4)
# grid = grid_config(capacity=50000, grid_loss=0.95, sell_price=0.4)
# print(benefit)
# In[ ]:
for pv_capacity in pv_capacities:
for ess_capacity in ess_capacities:
with open(f'data/annual_data-pv-{pv_capacity}-ess-{ess_capacity}.csv', 'w') as f:
f.write("date, time,pv_generate (kW),factory_demand (kW),buy_price (USD/MWh),sell_price (USD/MWh),grid_need_power (kW)\n")
start_date = datetime(2023, 1, 1, 0, 0, 0)
for i in range(len(annual_time[pv_capacity][ess_capacity])):
current_date = start_date + timedelta(hours=i)
formate_date = current_date.strftime("%Y-%m-%d")
f.write(f"{formate_date},{annual_time[pv_capacity][ess_capacity][i]},{int(annual_pv_gen[pv_capacity][ess_capacity][i])},{int(annual_factory_demand[pv_capacity][ess_capacity][i])},{int(annual_buy_price[pv_capacity][ess_capacity][i]*1000)},{int(annual_sell_price[pv_capacity][ess_capacity][i]*1000)},{int(annual_grid_need_power[pv_capacity][ess_capacity][i])} \n")
# In[96]:
# In[50]:
def save_data(data, filename):
data.to_csv(filename+'.csv')
data.to_json(filename + '.json')
from matplotlib.colors import LinearSegmentedColormap
df = overload_cnt
df = df.astype(int)
df.index = df.index / 1000
df.columns = df.columns / 1000
min_value = df.min().min()
max_value = df.max().max()
max_scale = max(abs(min_value/1000), abs(max_value/1000))
# In[97]:
plt.figure(figsize=figure_size)
cmap = LinearSegmentedColormap.from_list("", ["white", "blue"])
sns.heatmap(df/(4*24*365), annot=True, fmt=".1f", cmap=cmap, vmin=0, vmax=1)
plt.title('Probability of unmet electricity demands')
plt.gca().invert_yaxis()
plt.xlabel('ESS Capacity (MWh)')
plt.ylabel('PV Capacity (MW)')
plt.savefig('plots/unmet.png')
if not os.path.isdir('data'):
os.makedirs('data')
save_data(annual_result, f'data/{pv_begin}-{pv_end}-{pv_groups}-{ess_begin}-{ess_end}-{ess_groups}-results')
save_data(annual_costs, f'data/{pv_begin}-{pv_end}-{pv_groups}-{ess_begin}-{ess_end}-{ess_groups}-costs')
save_data(annual_overload, f'data/{pv_begin}-{pv_end}-{pv_groups}-{ess_begin}-{ess_end}-{ess_groups}-overload_cnt')
# In[98]:
draw_results(annual_result, 'plots/test.png', 'test', False)
# In[99]:
draw_roi(annual_costs, annual_nettos, 'plots/annual_roi.png', title_roi, 365, annot_benefit, figure_size)

View File

@@ -1,57 +1,47 @@
import pandas as pd
import numpy as np
import csv
import json
sunlight_file_name = 'lightintensity.xlsx'
factory_demand_file_name = 'factory_power1.xlsx'
with open('config.json', 'r') as f:
js_data = json.load(f)
df_sunlight = pd.read_excel(sunlight_file_name, header=None, names=['SunlightIntensity'])
pv_yield_file_name = js_data["data_path"]["pv_yield"]
print(pv_yield_file_name)
# factory_demand_file_name = 'factory_power1.xlsx'
factory_demand_file_name = js_data["data_path"]["demand"]
print(factory_demand_file_name)
electricity_price_data = js_data["data_path"]["buy"]
print(electricity_price_data)
electricity_price_data_sell = js_data["data_path"]["sell"]
print(electricity_price_data_sell)
start_date = '2023-01-01 00:00:00' # 根据数据的实际开始日期调整
hours = pd.date_range(start=start_date, periods=len(df_sunlight), freq='h')
df_sunlight['Time'] = hours
df_sunlight.set_index('Time', inplace=True)
pv_df = pd.read_csv(pv_yield_file_name, index_col='Time', usecols=['Time', 'PV yield[kW/kWp]'])
pv_df.index = pd.to_datetime(pv_df.index)
df_sunlight_resampled = df_sunlight.resample('15min').interpolate()
df_power = pd.read_excel(factory_demand_file_name,
header=None,
names=['FactoryPower'],
dtype={'FactoryPower': float})
times = pd.date_range(start=start_date, periods=len(df_power), freq='15min')
df_power['Time'] = times
df_power.set_index('Time',inplace=True)
print(df_power.head())
df_combined = df_sunlight_resampled.join(df_power)
df_combined.to_csv('combined_data.csv', index=True, index_label='Time')
price_data = np.random.uniform(0.3, 0.3, len(times))
# 创建DataFrame
price_df = pd.DataFrame(data={'Time': times, 'ElectricityPrice': price_data})
price_df.set_index('Time', inplace=True)
# 保存到CSV文件
price_df.to_csv('electricity_price_data.csv', index=True)
print(price_df.head())
print("Electricity price data generated and saved.")
df_power = pd.read_csv(factory_demand_file_name, index_col='Time', usecols=['Time', 'FactoryPower'])
df_power.index = pd.to_datetime(df_power.index)
df_combined = pv_df.join(df_power)
price_df = pd.read_csv(electricity_price_data, index_col='Time', usecols=['Time', 'ElectricityBuy'])
price_df.index = pd.to_datetime(price_df.index)
price_df = price_df.reindex(df_combined.index)
df_combined2 = df_combined.join(price_df)
print(df_combined2.head())
# 保存结果
sell_df = pd.read_csv(electricity_price_data_sell, index_col='Time', usecols=['Time', 'ElectricitySell'])
sell_df.index = pd.to_datetime(sell_df.index)
sell_df = sell_df.reindex(df_combined.index)
df_combined3 = df_combined2.join(sell_df)
with open('combined_data.csv', 'w', newline='') as file:
writer = csv.writer(file)
writer.writerow(['time', 'sunlight', 'demand','price'])
writer.writerow(['time', 'PV yield[kW/kWp]', 'demand','buy', 'sell'])
cnt = 0
for index, row in df_combined2.iterrows():
for index, row in df_combined3.iterrows():
time_formatted = index.strftime('%H:%M')
writer.writerow([time_formatted, row['SunlightIntensity'], row['FactoryPower'],row['ElectricityPrice']])
writer.writerow([time_formatted, row['PV yield[kW/kWp]'], row['FactoryPower'],row['ElectricityBuy'], row['ElectricitySell']])
print('The file is written to combined_data.csv')
# combined_data.to_csv('updated_simulation_with_prices.csv', index=False)
print("Simulation data with electricity prices has been updated and saved.")

35041
read_data/Berlin.csv Normal file

File diff suppressed because it is too large Load Diff

35041
read_data/Cambodge.csv Normal file

File diff suppressed because it is too large Load Diff

35041
read_data/Marcedonia.csv Normal file

File diff suppressed because it is too large Load Diff

35041
read_data/Riyahd.csv Normal file

File diff suppressed because it is too large Load Diff

35041
read_data/Serbia.csv Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,372 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 85,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n",
"import pandas as pd\n",
"import numpy as np\n",
"import os\n",
"import csv"
]
},
{
"cell_type": "code",
"execution_count": 86,
"metadata": {},
"outputs": [],
"source": [
"def read_csv(filename):\n",
" skip_rows = list(range(1, 17))\n",
" data = pd.read_csv(filename, sep=';', skiprows=skip_rows)\n",
" return data"
]
},
{
"cell_type": "code",
"execution_count": 87,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/tmp/ipykernel_3075037/3659192646.py:3: DtypeWarning: Columns (32,33,35) have mixed types. Specify dtype option on import or set low_memory=False.\n",
" data = pd.read_csv(filename, sep=';', skiprows=skip_rows)\n"
]
},
{
"data": {
"text/plain": [
"Index(['Time', 'Irradiance onto horizontal plane ',\n",
" 'Diffuse Irradiation onto Horizontal Plane ', 'Outside Temperature ',\n",
" 'Module Area 1: Height of Sun ',\n",
" 'Module Area 1: Irradiance onto tilted surface ',\n",
" 'Module Area 1: Module Temperature ', 'Grid Export ',\n",
" 'Energy from Grid ', 'Global radiation - horizontal ',\n",
" 'Deviation from standard spectrum ', 'Ground Reflection (Albedo) ',\n",
" 'Orientation and inclination of the module surface ', 'Shading ',\n",
" 'Reflection on the Module Surface ',\n",
" 'Irradiance on the rear side of the module ',\n",
" 'Global Radiation at the Module ',\n",
" 'Module Area 1: Reflection on the Module Surface ',\n",
" 'Module Area 1: Global Radiation at the Module ',\n",
" 'Global PV Radiation ', 'Bifaciality ', 'Soiling ',\n",
" 'STC Conversion (Rated Efficiency of Module) ', 'Rated PV Energy ',\n",
" 'Low-light performance ', 'Module-specific Partial Shading ',\n",
" 'Deviation from the nominal module temperature ', 'Diodes ',\n",
" 'Mismatch (Manufacturer Information) ',\n",
" 'Mismatch (Configuration/Shading) ',\n",
" 'Power optimizer (DC conversion/clipping) ',\n",
" 'PV Energy (DC) without inverter clipping ',\n",
" 'Failing to reach the DC start output ',\n",
" 'Clipping on account of the MPP Voltage Range ',\n",
" 'Clipping on account of the max. DC Current ',\n",
" 'Clipping on account of the max. DC Power ',\n",
" 'Clipping on account of the max. AC Power/cos phi ', 'MPP Matching ',\n",
" 'PV energy (DC) ',\n",
" 'Inverter 1 - MPP 1 - to Module Area 1: PV energy (DC) ',\n",
" 'Inverter 1 - MPP 2 - to Module Area 1: PV energy (DC) ',\n",
" 'Inverter 1 - MPP 3 - to Module Area 1: PV energy (DC) ',\n",
" 'Inverter 1 - MPP 4 - to Module Area 1: PV energy (DC) ',\n",
" 'Inverter 1 - MPP 5 - to Module Area 1: PV energy (DC) ',\n",
" 'Inverter 1 - MPP 6 - to Module Area 1: PV energy (DC) ',\n",
" 'Inverter 2 - MPP 1 - to Module Area 1: PV energy (DC) ',\n",
" 'Inverter 2 - MPP 2 - to Module Area 1: PV energy (DC) ',\n",
" 'Energy at the Inverter Input ',\n",
" 'Input voltage deviates from rated voltage ', 'DC/AC Conversion ',\n",
" 'Own Consumption (Standby or Night) ', 'Total Cable Losses ',\n",
" 'PV energy (AC) minus standby use ', 'Feed-in energy ',\n",
" 'Inverter 1 to Module Area 1: Own Consumption (Standby or Night) ',\n",
" 'Inverter 1 to Module Area 1: PV energy (AC) minus standby use ',\n",
" 'Inverter 2 to Module Area 1: Own Consumption (Standby or Night) ',\n",
" 'Inverter 2 to Module Area 1: PV energy (AC) minus standby use ',\n",
" 'Unnamed: 58'],\n",
" dtype='object')"
]
},
"execution_count": 87,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"\n",
"file_name = 'Riyahd_raw.csv'\n",
"df = read_csv(file_name)\n",
"df.columns"
]
},
{
"cell_type": "code",
"execution_count": 88,
"metadata": {},
"outputs": [],
"source": [
"remain_column = ['Time','PV energy (AC) minus standby use ']\n",
"energy_row_name = remain_column[1]\n",
"\n",
"df = df[remain_column]\n",
"df[energy_row_name] = df[energy_row_name].str.replace(',','.').astype(float)\n"
]
},
{
"cell_type": "code",
"execution_count": 89,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"770594.226863267"
]
},
"execution_count": 89,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sum_energy = df[energy_row_name].sum()\n",
"sum_energy"
]
},
{
"cell_type": "code",
"execution_count": 90,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"1975.882632982736"
]
},
"execution_count": 90,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sum_energy / 390"
]
},
{
"cell_type": "code",
"execution_count": 91,
"metadata": {},
"outputs": [],
"source": [
"group_size = 15\n",
"df['group_id'] = df.index // group_size\n",
"\n",
"sums = df.groupby('group_id')[energy_row_name].sum()\n",
"sums_df = sums.reset_index(drop=True).to_frame(name = 'Energy')"
]
},
{
"cell_type": "code",
"execution_count": 92,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<bound method NDFrame.head of Energy\n",
"0 0.0\n",
"1 0.0\n",
"2 0.0\n",
"3 0.0\n",
"4 0.0\n",
"... ...\n",
"35035 0.0\n",
"35036 0.0\n",
"35037 0.0\n",
"35038 0.0\n",
"35039 0.0\n",
"\n",
"[35040 rows x 1 columns]>"
]
},
"execution_count": 92,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"sums_df.head"
]
},
{
"cell_type": "code",
"execution_count": 93,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" Time\n",
"0 2023-01-01 00:00:00\n",
"1 2023-01-01 00:15:00\n",
"2 2023-01-01 00:30:00\n",
"3 2023-01-01 00:45:00\n",
"4 2023-01-01 01:00:00\n",
" Time\n",
"35035 2023-12-31 22:45:00\n",
"35036 2023-12-31 23:00:00\n",
"35037 2023-12-31 23:15:00\n",
"35038 2023-12-31 23:30:00\n",
"35039 2023-12-31 23:45:00\n"
]
}
],
"source": [
"\n",
"start_date = '2023-01-01'\n",
"end_date = '2023-12-31'\n",
"\n",
"# 生成每天的15分钟间隔时间\n",
"all_dates = pd.date_range(start=start_date, end=end_date, freq='D')\n",
"all_times = pd.timedelta_range(start='0 min', end='1435 min', freq='15 min')\n",
"\n",
"# 生成完整的时间标签\n",
"date_times = [pd.Timestamp(date) + time for date in all_dates for time in all_times]\n",
"\n",
"# 创建DataFrame\n",
"time_frame = pd.DataFrame({\n",
" 'Time': date_times\n",
"})\n",
"\n",
"# 查看生成的DataFrame\n",
"print(time_frame.head())\n",
"print(time_frame.tail())\n"
]
},
{
"cell_type": "code",
"execution_count": 94,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"(35040, 1)\n",
"(35040, 1)\n"
]
}
],
"source": [
"print(sums_df.shape)\n",
"print(time_frame.shape)"
]
},
{
"cell_type": "code",
"execution_count": 95,
"metadata": {},
"outputs": [],
"source": [
"# sums_df['Time'] = time_frame['Time']\n",
"sums_df = pd.concat([time_frame, sums_df], axis=1)"
]
},
{
"cell_type": "code",
"execution_count": 96,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
" Energy\n",
"Time \n",
"2023-01-01 00:00:00 0.0\n",
"2023-01-01 00:15:00 0.0\n",
"2023-01-01 00:30:00 0.0\n",
"2023-01-01 00:45:00 0.0\n",
"2023-01-01 01:00:00 0.0\n"
]
}
],
"source": [
"sums_df.set_index('Time', inplace=True)\n",
"print(sums_df.head())"
]
},
{
"cell_type": "code",
"execution_count": 97,
"metadata": {},
"outputs": [],
"source": [
"max_value = sums_df['Energy'].max()\n",
"sums_df['Energy'] = sums_df['Energy'] / max_value\n"
]
},
{
"cell_type": "code",
"execution_count": 98,
"metadata": {},
"outputs": [],
"source": [
"def save_csv(df, filename, columns):\n",
" tmp_df = df.copy()\n",
" tmp_df[columns[1]] = tmp_df[columns[1]].round(4)\n",
" with open(filename, 'w', newline='') as file:\n",
" writer = csv.writer(file)\n",
" writer.writerow(columns)\n",
" for index, row in tmp_df.iterrows():\n",
" time_formatted = index.strftime('%H:%M')\n",
" writer.writerow([time_formatted, row[columns[1]]])\n",
" \n",
" print(f'The file is written to {filename}')\n",
" \n",
"\n"
]
},
{
"cell_type": "code",
"execution_count": 99,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"The file is written to Riyahd.csv\n"
]
}
],
"source": [
"save_csv(sums_df, 'Riyahd.csv', ['Time', 'Energy'])"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "pv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.9"
}
},
"nbformat": 4,
"nbformat_minor": 2
}

79
read_data/convert_data.py Normal file
View File

@@ -0,0 +1,79 @@
#!/usr/bin/env python
# coding: utf-8
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import os
import csv
def generate_min_df(mins = 15):
end = 60/mins * 24
start_date = '2023-01-01'
end_date = '2023-12-31'
all_dates = pd.date_range(start=start_date, end=end_date, freq='D')
all_times = pd.timedelta_range(start='0 min', end=f'1435 min', freq=f'{mins} min')
date_times = [pd.Timestamp(date) + time for date in all_dates for time in all_times]
time_frame = pd.DataFrame({
'Time': date_times
})
return time_frame
def save_csv(df, filename, columns):
with open(filename, 'w', newline='') as file:
writer = csv.writer(file)
writer.writerow(['Time', 'PV yield[kW/kWp]'])
for index, row in df.iterrows():
time_formatted = index.strftime('%H:%M')
writer.writerow([time_formatted, row[columns[1]]])
print(f'The file is written to {filename}')
def read_csv(filename):
skip_rows = list(range(1, 17))
data = pd.read_csv(filename, sep=';', skiprows=skip_rows)
return data
def process(file_name):
df = read_csv(file_name)
city = file_name.split('_')[0]
remain_column = ['Time','PV energy (AC) minus standby use ']
energy_row_name = remain_column[1]
df = df[remain_column]
df[energy_row_name] = df[energy_row_name].str.replace(',','.').astype(float)
sum_energy = df[energy_row_name].sum()
group_size = 15
df['group_id'] = df.index // group_size
sums = df.groupby('group_id')[energy_row_name].sum()
sums_df = sums.reset_index(drop=True).to_frame(name = 'Energy')
pv_energy_column_name = 'PV yield[kW/kWp]'
sums_df = sums_df.rename(columns={'Energy': pv_energy_column_name})
time_frame = generate_min_df(15)
sums_df = pd.concat([time_frame, sums_df], axis=1)
# sums_df.set_index('Time', inplace=True)
# max_value = sums_df[pv_energy_column_name].max()
sums_df[pv_energy_column_name] = sums_df[pv_energy_column_name] / 390.
sums_df[pv_energy_column_name] = sums_df[pv_energy_column_name].round(4)
sums_df[pv_energy_column_name].replace(0.0, -0.0)
sums_df.to_csv(f'{city}.csv')
# save_csv(sums_df, f'{city}.csv', ['Time', 'Energy'])
if __name__ == '__main__':
city_list = ['Riyahd', 'Cambodge', 'Berlin', 'Serbia']
for city in city_list:
print(f'Processing {city}')
file_name = f'{city}_raw.csv'
process(file_name)
print(f'Processing {city} is done\n')

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

35041
read_data/factory_power1.csv Normal file

File diff suppressed because it is too large Load Diff

16
xlsx2csv.py Normal file
View File

@@ -0,0 +1,16 @@
import pandas as pd
excel_file = 'factory_power1.xlsx'
sheet_name = 'Sheet1'
df = pd.read_excel(excel_file, sheet_name=sheet_name)
start_date = '2023-01-01'
df_power = pd.read_excel(excel_file,
header=None,
names=['FactoryPower'],
dtype={'FactoryPower': float})
times = pd.date_range(start=start_date, periods=len(df_power), freq='15min')
df_power['Time'] = times
df_power = df_power[['Time', 'FactoryPower']]
df_power.to_csv('factory_power1.csv', index=True)