GreenTransPowerCalculate/PV/PV_total2.py

322 lines
14 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import requests
import pandas as pd
import math
from scipy.optimize import fsolve
# 默认文件路径
PV_EXCEL_PATH = r".\pv_product.xlsx" # 光伏组件数值
def calculate_pv_metrics(component_name, electricity_price, pv_number, q, longitude, latitude,
is_fixed=True, optimize=True, peak_load_hour=16, cost_per_kw=3.4, E_S=1.0, K=0.8):
"""
计算光伏项目的各项指标包括装机容量、年发电量、等效小时数、环境收益和内部收益率IRR
参数:
component_name (str): 光伏组件名称,例如 "TWMHF-66HD715"
electricity_price (float): 电价(元/kWh
pv_number (int): 光伏组件数量
q (float): 运维成本占初始投资成本的比例(例如 0.02 表示 2%
longitude (float): 经度
latitude (float): 纬度
is_fixed (bool): 是否为固定式支架True 为固定式False 为跟踪式
optimize (bool): 是否优化倾角和方位角
peak_load_hour (int): 峰值负荷小时,默认 16
cost_per_kw (float): 每 kW 投资成本(元/kW默认 3.4 元/kW
E_S (float): 标准辐射量,默认 1.0
K (float): 系统效率,默认 0.8
返回:
dict: 包含以下结果的字典:
- component_name: 组件名称
- tilt: 倾角 (度)
- azimuth: 方位角 (度)
- array_distance: 阵列间距 (米)
- max_power: 单组件最大功率 (Wp)
- capacity: 装机容量 (kW)
- peak_sunshine_hours: 峰值日照小时数 (小时/天)
- single_daily_energy: 一天单个组件发电量 (kWh)
- annual_energy: 年发电量 (kWh)
- equivalent_hours: 等效小时数 (小时)
- coal_reduction: 标准煤减排量 (kg)
- CO2_reduction: CO₂ 减排量 (kg)
- SO2_reduction: SO₂ 减排量 (kg)
- NOX_reduction: NOx artic_reduction量 (kg)
- IRR: 内部收益率 (%)
"""
# 1. 计算 PSH峰值日照小时数
def calculate_psh_average(lat, lon, start_year=2010, end_year=2023):
url = "https://power.larc.nasa.gov/api/temporal/monthly/point"
params = {
"parameters": "ALLSKY_SFC_SW_DWN", # 水平面全球辐照GHI
"community": "RE",
"longitude": lon,
"latitude": lat,
"format": "JSON",
"start": str(start_year),
"end": str(end_year)
}
try:
response = requests.get(url, params=params)
response.raise_for_status()
data = response.json()
ghi_data = data["properties"]["parameter"]["ALLSKY_SFC_SW_DWN"]
# 过滤掉年度平均值(例如 YYYY13
ghi_data = {k: v for k, v in ghi_data.items() if not k.endswith("13")}
# 创建 DataFrame
df = pd.DataFrame.from_dict(ghi_data, orient="index", columns=["GHI (kWh/m²/day)"])
df.index = [f"{k[:4]}-{k[-2:]:02}" for k in df.index]
# 计算 PSHPSH = GHI / 1 kW/m²
df["PSH (hours/day)"] = df["GHI (kWh/m²/day)"]
# 计算每年的平均 PSH
df['Year'] = df.index.str[:4]
annual_avg = df.groupby('Year')['PSH (hours/day)'].mean()
# 计算总平均 PSH
return annual_avg.mean()
except requests.exceptions.RequestException as e:
print(f"NASA POWER API Error: {e}")
return None
except ValueError as e:
print(f"Data Error: {e}")
return None
# 2. 计算最佳倾角
def calculate_optimal_tilt(lat):
try:
# 将纬度从角度转换为弧度
lat_radians = math.radians(lat)
# 计算最佳倾角:倾角 = 纬度(弧度) × 0.86 + 24
optimal_tilt = lat_radians * 0.86 + 24
return optimal_tilt
except ValueError as e:
raise Exception(f"Tilt Calculation Error: {e}")
# 3. 计算倾角和方位角
def get_tilt_and_azimuth(is_fixed=True, optimize=True, longitude=116, latitude=None, peak_load_hour=16):
if optimize and latitude is None:
raise ValueError("优化模式下需提供纬度")
if is_fixed:
if optimize:
# 使用公式计算倾角
tilt = calculate_optimal_tilt(latitude)
if not (0 <= tilt <= 90):
raise ValueError(f"计算得到的倾角 {tilt:.2f}° 超出合理范围0°-90°")
azimuth = (peak_load_hour - 12) * 15 + (longitude - 116) # 方位角公式
azimuth = azimuth % 360 if azimuth >= 0 else azimuth + 360
else:
print("倾角0°(水平)-90°(垂直) | 方位角0°(正北)-180°(正南),顺时针")
tilt = float(input("请输入倾角(度)"))
azimuth = float(input("请输入方位角(度)"))
if not (0 <= tilt <= 90) or not (0 <= azimuth <= 360):
raise ValueError("倾角需在0-90°方位角需在0-360°")
else: # 跟踪式
azimuth = 180 # 固定朝南
if optimize:
tilt = calculate_optimal_tilt(latitude)
if not (0 <= tilt <= 90):
raise ValueError(f"计算得到的倾角 {tilt:.2f}° 超出合理范围0°-90°")
else:
print("倾角0°(水平)-90°(垂直)")
tilt = float(input("请输入倾角(度)"))
if not (0 <= tilt <= 90):
raise ValueError("倾角需在0-90°")
return tilt, azimuth
# 4. 获取光伏组件信息
def get_pv_product_info(component_name, excel_path=PV_EXCEL_PATH):
try:
df = pd.read_excel(excel_path)
if len(df.columns) < 10:
raise ValueError("Excel文件需包含至少10列组件名称、尺寸、功率等")
row = df[df.iloc[:, 1] == component_name]
if row.empty:
raise ValueError(f"未找到组件:{component_name}")
return {
"component_name": component_name,
"max_power": row.iloc[0, 5],
"efficiency": row.iloc[0, 9],
"pv_size": row.iloc[0, 3]
}
except FileNotFoundError:
raise FileNotFoundError(f"未找到Excel文件{excel_path}")
except Exception as e:
raise Exception(f"读取Excel出错{e}")
# 5. 计算光伏阵列间距
def calculate_array_distance(L, tilt, latitude):
beta_rad = math.radians(tilt)
phi_rad = math.radians(latitude)
return (L * math.cos(beta_rad) +
L * math.sin(beta_rad) * 0.707 * math.tan(phi_rad) +
0.4338 * math.tan(phi_rad))
# 6. 计算等效小时数
def calculate_equivalent_hours(P, P_r):
if P_r == 0:
raise ValueError("额定功率不能为 0")
h = P / P_r # 单位换算
return h
# 7. 计算装机容量
def calculate_installed_capacity(max_power, num_components):
if max_power < 0 or num_components < 0 or not isinstance(num_components, int):
raise ValueError("功率和数量需为非负数,数量需为整数")
return (max_power * num_components) / 1000 # 单位kW
# 8. 计算年发电量
def calculate_annual_energy(peak_hours, capacity, E_S=E_S, K=K):
if any(x < 0 for x in [peak_hours, capacity]) or E_S <= 0 or not 0 <= K <= 1:
raise ValueError("输入参数需满足辐射量、容量≥0E_S>0K∈[0,1]")
return peak_hours * 365 * (capacity / E_S) * K # 单位kWh
# 9. 计算环境收益
def calculate_environmental_benefits(E_p_million_kwh):
if E_p_million_kwh < 0:
raise ValueError("年发电量需≥0")
return {
"coal_reduction": E_p_million_kwh * 0.404 * 10,
"CO2_reduction": E_p_million_kwh * 0.977 * 10,
"SO2_reduction": E_p_million_kwh * 0.03 * 10,
"NOX_reduction": E_p_million_kwh * 0.015 * 10
}
# 10. 计算净现值和内部收益率
def calculate_reference_yield(E_p, electricity_price, IC, q, n=25):
if E_p < 0 or electricity_price < 0 or IC <= 0 or not 0 <= q <= 1:
raise ValueError("发电量、电价≥0投资成本>0回收比例∈[0,1]")
def npv_equation(irr, p, w, ic, q_val, n=n):
term1 = (1 + irr) ** (-1)
term2 = irr * (1 + irr) ** (-1) if irr != 0 else float('inf')
pv_revenue = p * w * (term1 / term2) * (1 - (1 + irr) ** (-n))
pv_salvage = q_val * ic * (term1 / term2) * (1 - (1 + irr) ** (-n))
return pv_revenue - ic + pv_salvage
irr_guess = 0.1
irr = float(fsolve(npv_equation, irr_guess, args=(E_p, electricity_price, IC, q))[0])
if not 0 <= irr <= 1:
raise ValueError(f"IRR计算结果{irr:.4f}不合理,请检查输入")
npv = npv_equation(irr, E_p, electricity_price, IC, q)
return {"NPV": npv, "IRR": irr * 100}
# 主计算流程
try:
# 获取倾角和方位角
tilt, azimuth = get_tilt_and_azimuth(
is_fixed=is_fixed,
optimize=optimize,
longitude=longitude,
latitude=latitude,
peak_load_hour=peak_load_hour
)
# 获取组件信息
pv_info = get_pv_product_info(component_name)
width_mm = float(pv_info["pv_size"].split("×")[1])
L = (width_mm / 1000) * 4
array_distance = calculate_array_distance(L, tilt, latitude)
# 计算装机容量
max_power = pv_info["max_power"]
capacity = calculate_installed_capacity(max_power, pv_number) # 单位kW
# 获取峰值日照小时数(使用 NASA POWER API
peak_hours = calculate_psh_average(latitude, longitude)
# 计算一天单个组件发电量
single_daily_energy = peak_hours * (capacity / pv_number) * K # 单位kWh
# 计算年发电量
E_p = calculate_annual_energy(peak_hours, capacity, E_S, K) # 单位kWh
# 计算等效小时数
h = calculate_equivalent_hours(E_p, capacity) # P_r 单位为 kWE_p 单位为 kWh
# 计算环境收益(转换为百万 kWh
E_p_million_kwh = E_p / 1000000 # 转换为百万 kWh
env_benefits = calculate_environmental_benefits(E_p_million_kwh)
# 计算初始投资成本
IC = capacity * cost_per_kw * 1000 # 单位:元
# 计算 IRR
ref_yield = calculate_reference_yield(E_p, electricity_price, IC, q)
# 返回结果
return {
"component_name": component_name,
"tilt": tilt,
"azimuth": azimuth,
"array_distance": array_distance,
"max_power": max_power,
"capacity": capacity,
"peak_sunshine_hours": peak_hours,
"single_daily_energy": single_daily_energy,
"annual_energy": E_p,
"equivalent_hours": h,
"coal_reduction": env_benefits["coal_reduction"],
"CO2_reduction": env_benefits["CO2_reduction"],
"SO2_reduction": env_benefits["SO2_reduction"],
"NOX_reduction": env_benefits["NOX_reduction"],
"IRR": ref_yield["IRR"]
}
except Exception as e:
raise Exception(f"计算过程中发生错误: {str(e)}")
# 示例用法
if __name__ == "__main__":
try:
# 输入
# 输入并验证经纬度
latitude = float(input("请输入纬度(-90 到 90例如 39.9042"))
if not (-90 <= latitude <= 90):
raise ValueError("纬度必须在 -90 到 90 之间")
longitude = float(input("请输入经度(-180 到 180例如 116.4074"))
if not (-180 <= longitude <= 180):
raise ValueError("经度必须在 -180 到 180 之间")
component_name = input("请输入组件名称:")
electricity_price = float(input("请输入电价(元/kWh"))
pv_number = 1200 # 固定组件数量
q = 0.02 # 运维成本占初始投资成本的比例
choice = input("选择光伏支架(固定式/跟踪式):")
optimize = input("是否优化倾角和方位角(是/否):")
# 调用主函数
result = calculate_pv_metrics(
component_name=component_name,
electricity_price=electricity_price,
pv_number=pv_number,
q=q,
is_fixed=(choice == "固定式"),
optimize=(optimize.lower() == ""),
longitude=longitude,
latitude=latitude
)
# 打印结果
print("\n")
print(f"组件名称:{result['component_name']}")
print(f"倾角:{result['tilt']:.2f}° | 方位角:{result['azimuth']:.2f}°")
print(f"阵列间距:{result['array_distance']:.2f}")
print(f"单个组件最大功率Wp{result['max_power']}")
print(f"装机容量:{result['capacity']:.2f} kW")
print(f"峰值日照小时数:{result['peak_sunshine_hours']:.2f} 小时/天")
print(f"一天单个组件发电量:{result['single_daily_energy']:.0f} kWh")
print(f"年发电量:{result['annual_energy']:,.0f} kWh")
print(f"等效小时数:{result['equivalent_hours']:.0f} 小时")
print("环境收益:")
print(f"标准煤减排量:{result['coal_reduction']:,.0f} kg")
print(f"CO₂减排量{result['CO2_reduction']:,.0f} kg")
print(f"SO₂减排量{result['SO2_reduction']:,.0f} kg")
print(f"NOx减排量{result['NOX_reduction']:,.0f} kg")
print(f"内部收益率 IRR{result['IRR']:.2f}%")
except Exception as e:
print(f"错误:{e}")