use pvfactors for bifacial modelling

This commit is contained in:
Lucas Tan 2025-04-05 22:07:30 +08:00
parent cc7a1eb993
commit e9d426e6ec
2 changed files with 37 additions and 55 deletions

View File

@ -27,7 +27,11 @@ def optimise_vertical_panel_pitch(c):
# perform minimization # perform minimization
initial_pitch = c["array"]["spacing"] initial_pitch = c["array"]["spacing"]
result = minimize( result = minimize(
objective, initial_pitch, bounds=[(0, 20)], tol=1e-8, options={"eps": 1} objective,
initial_pitch,
bounds=[(c["panel"]["dimensions"]["length"], 20)],
tol=1e-8,
options={"eps": 1},
) )
optimal_pitch = result.x[0] optimal_pitch = result.x[0]
c["array"]["spacing"] = optimal_pitch c["array"]["spacing"] = optimal_pitch

View File

@ -2,6 +2,7 @@ import numpy as np
import pandas as pd import pandas as pd
import logging import logging
import math import math
import matplotlib.pyplot as pl
import pvlib import pvlib
from pvlib.bifacial.pvfactors import pvfactors_timeseries from pvlib.bifacial.pvfactors import pvfactors_timeseries
@ -154,22 +155,15 @@ def calculate_energy_production_vertical(c):
shading_row_rotation = 90 shading_row_rotation = 90
surface_azimuth = 90 # east facing surface_azimuth = 90 # east facing
axis_tilt = 0 axis_tilt = 0
axis_azimuth = 0 axis_azimuth = 180
ground_coverage = ( gcr = np.divide(c["panel"]["dimensions"]["length"], pitch)
no_of_panels gcr = min(1, gcr)
* c["panel"]["dimensions"]["width"]
* c["panel"]["dimensions"]["thickness"]
)
land_area = (
c["environment"]["roof"]["dimensions"]["width"]
* c["environment"]["roof"]["dimensions"]["length"]
)
gcr = ground_coverage / land_area
logger.info(f"Ground coverage ratio: {gcr}") logger.info(f"Ground coverage ratio: {gcr}")
# use pvfactors bifacial modelling package # use pvfactors bifacial modelling package
for row in range(no_of_rows): POA_data = pd.Series(dtype=object)
for row in range(0, 3):
result = pvfactors_timeseries( result = pvfactors_timeseries(
solar_zenith=solar_positions["apparent_zenith"], solar_zenith=solar_positions["apparent_zenith"],
solar_azimuth=solar_positions["azimuth"], solar_azimuth=solar_positions["azimuth"],
@ -183,55 +177,39 @@ def calculate_energy_production_vertical(c):
pvrow_height=c["panel"]["dimensions"]["length"], pvrow_height=c["panel"]["dimensions"]["length"],
pvrow_width=c["panel"]["dimensions"]["width"] * no_of_panels_in_row, pvrow_width=c["panel"]["dimensions"]["width"] * no_of_panels_in_row,
albedo=0.2, albedo=0.2,
n_pvrows=no_of_rows, n_pvrows=3,
index_observed_pvrow=row, index_observed_pvrow=row,
) )
# set negative values to 0
poa_front = result[2].clip(lower=0)
poa_rear = result[3].clip(lower=0)
poa_global = poa_front + poa_rear * c["panel"]["bifaciality"]
POA_data.at[row] = poa_global
# calculate irradiance on plane of array total_hourly_irradiance = POA_data.at[0] + POA_data.at[2] + (POA_data.at[1] * 20)
poa_front = pvlib.irradiance.get_total_irradiance(
surface_tilt=90,
surface_azimuth=90,
solar_zenith=morning_projected_solar_zenith,
solar_azimuth=solar_positions["azimuth"],
dni=clearsky_data["dni"],
ghi=clearsky_data["ghi"],
dhi=clearsky_data["dhi"],
surface_type="urban",
)
# drop rows with poa_global NaN values
poa_front = poa_front.dropna(subset=["poa_global"])
poa_rear = pvlib.irradiance.get_total_irradiance(
surface_tilt=180 - 90,
surface_azimuth=90 + 180,
solar_zenith=afternoon_projected_solar_zenith,
solar_azimuth=solar_positions["azimuth"],
dni=clearsky_data["dni"],
ghi=clearsky_data["ghi"],
dhi=clearsky_data["dhi"],
surface_type="urban",
)
# drop rows with poa_global NaN values
poa_rear = poa_rear.dropna(subset=["poa_global"])
effective_front = poa_front["poa_global"] * (1 - morning_shaded_fraction)
effective_rear = (
poa_rear["poa_global"]
* (1 - afternoon_shaded_fraction)
* c["panel"]["bifaciality"]
)
total_hourly_irradiance = effective_front + effective_rear
system_size = c["panel"]["peak_power"] * no_of_panels
pdc0 = system_size
gamma_pdc = c["panel"]["temperature_coefficient"] gamma_pdc = c["panel"]["temperature_coefficient"]
temp_cell = c["panel"]["nominal_operating_cell_temperature"] temp_cell = c["panel"]["nominal_operating_cell_temperature"]
pdc = pvlib.pvsystem.pvwatts_dc( p_row = no_of_panels_in_row * c["panel"]["peak_power"]
pdc0=pdc0, p_middle_rows = (no_of_panels - 2 * no_of_panels_in_row) * c["panel"]["peak_power"]
pdc_first_row = pvlib.pvsystem.pvwatts_dc(
pdc0=p_row,
gamma_pdc=gamma_pdc, gamma_pdc=gamma_pdc,
temp_cell=temp_cell, temp_cell=temp_cell,
g_poa_effective=total_hourly_irradiance, g_poa_effective=POA_data.at[0],
) )
pdc_last_row = pvlib.pvsystem.pvwatts_dc(
pdc0=p_row,
gamma_pdc=gamma_pdc,
temp_cell=temp_cell,
g_poa_effective=POA_data.at[2],
)
pdc_middle_rows = pvlib.pvsystem.pvwatts_dc(
pdc0=p_middle_rows,
gamma_pdc=gamma_pdc,
temp_cell=temp_cell,
g_poa_effective=POA_data.at[1],
)
pdc = pdc_first_row + pdc_last_row + pdc_middle_rows
total_hourly_energy = pdc * 15 / 60 / 1e3 # convert to kWh total_hourly_energy = pdc * 15 / 60 / 1e3 # convert to kWh
@ -259,7 +237,7 @@ def calculate_energy_production_horizontal(c):
shaded_row_rotation = 0 shaded_row_rotation = 0
shading_row_rotation = 0 shading_row_rotation = 0
axis_tilt = 0 axis_tilt = 0
axis_azimuth = 90 # south facing axis_azimuth = 270 # south facing surface
projected_solar_zenith = pvlib.shading.projected_solar_zenith_angle( projected_solar_zenith = pvlib.shading.projected_solar_zenith_angle(
solar_zenith=solar_positions["apparent_zenith"], solar_zenith=solar_positions["apparent_zenith"],