use PVWatts to simulate panel output
This commit is contained in:
parent
ebae0aecfe
commit
05186bd77f
@ -18,16 +18,18 @@ def optimise_vertical_panel_pitch(c):
|
|||||||
"""
|
"""
|
||||||
c["array"]["spacing"] = pitch
|
c["array"]["spacing"] = pitch
|
||||||
logging.info(f"Optimizing with pitch: {pitch}m")
|
logging.info(f"Optimizing with pitch: {pitch}m")
|
||||||
vertical_energy = calculate_energy_production_vertical(c)
|
vertical_energy, _ = calculate_energy_production_vertical(c)
|
||||||
total_energy_yield = vertical_energy.sum()
|
total_energy_yield = vertical_energy.sum()
|
||||||
logger.info(f"Total energy yield for pitch {pitch}m: {total_energy_yield}kWh")
|
logger.info(f"Total energy yield for pitch {pitch}m: {total_energy_yield}kWh")
|
||||||
return -total_energy_yield
|
return -total_energy_yield
|
||||||
|
|
||||||
# perform minimization
|
# perform minimization
|
||||||
initial_pitch = c["array"]["spacing"]
|
initial_pitch = c["array"]["spacing"]
|
||||||
result = minimize(objective, initial_pitch, bounds=[(0, 90)])
|
result = minimize(
|
||||||
|
objective, initial_pitch, bounds=[(0, 5)], tol=1e-8, options={"eps": 0.5}
|
||||||
|
)
|
||||||
optimal_pitch = result.x[0]
|
optimal_pitch = result.x[0]
|
||||||
c["array"]["spacing"] = optimal_pitch
|
c["array"]["spacing"] = optimal_pitch
|
||||||
logger.info(f"Optimal pitch found: {optimal_pitch}m")
|
logger.info(f"Optimal pitch found: {optimal_pitch}m")
|
||||||
vetical_energy = calculate_energy_production_vertical(c)
|
vetical_energy, no_of_panels = calculate_energy_production_vertical(c)
|
||||||
return (optimal_pitch, vetical_energy)
|
return (optimal_pitch, vetical_energy, no_of_panels)
|
||||||
|
@ -132,11 +132,6 @@ def calculate_energy_production_vertical(c):
|
|||||||
panel_coordinates, no_of_panels = define_grid_layout(c, panel_tilt=90)
|
panel_coordinates, no_of_panels = define_grid_layout(c, panel_tilt=90)
|
||||||
solar_positions, clearsky_data = get_solar_data(c)
|
solar_positions, clearsky_data = get_solar_data(c)
|
||||||
|
|
||||||
# split the solar positions data into morning and afternoon, using solar azimuth of
|
|
||||||
# 180 degrees as the threshold
|
|
||||||
morning_solar_positions = solar_positions[solar_positions["azimuth"] <= 180]
|
|
||||||
afternoon_solar_positions = solar_positions[solar_positions["azimuth"] > 180]
|
|
||||||
|
|
||||||
# the first row is always not shaded so exclude
|
# the first row is always not shaded so exclude
|
||||||
no_of_rows = np.unique(panel_coordinates["y"]).shape[0]
|
no_of_rows = np.unique(panel_coordinates["y"]).shape[0]
|
||||||
no_of_shaded_rows = no_of_rows - 1
|
no_of_shaded_rows = no_of_rows - 1
|
||||||
@ -207,31 +202,31 @@ def calculate_energy_production_vertical(c):
|
|||||||
# drop rows with poa_global NaN values
|
# drop rows with poa_global NaN values
|
||||||
poa_rear = poa_rear.dropna(subset=["poa_global"])
|
poa_rear = poa_rear.dropna(subset=["poa_global"])
|
||||||
|
|
||||||
effective_front = (
|
effective_front = poa_front["poa_global"] * (1 - morning_shaded_fraction)
|
||||||
poa_front["poa_global"]
|
|
||||||
* (1 - morning_shaded_fraction)
|
|
||||||
* c["panel"]["efficiency"]
|
|
||||||
)
|
|
||||||
effective_rear = (
|
effective_rear = (
|
||||||
poa_rear["poa_global"]
|
poa_rear["poa_global"]
|
||||||
* (1 - afternoon_shaded_fraction)
|
* (1 - afternoon_shaded_fraction)
|
||||||
* c["panel"]["bifaciality"]
|
* c["panel"]["bifaciality"]
|
||||||
* c["panel"]["efficiency"]
|
|
||||||
)
|
)
|
||||||
|
|
||||||
energy_front = effective_front * 15 / 60 / 1e3
|
total_hourly_irradiance = effective_front + effective_rear
|
||||||
energy_rear = effective_rear * 15 / 60 / 1e3
|
system_size = c["panel"]["peak_power"] * no_of_panels
|
||||||
total_hourly_energy_m2 = energy_front + energy_rear
|
pdc0 = system_size
|
||||||
energy_total = total_hourly_energy_m2.sum()
|
gamma_pdc = c["panel"]["temperature_coefficient"]
|
||||||
logger.info(f"Energy yield calculated: {energy_total} kWh/m2")
|
temp_cell = c["panel"]["nominal_operating_cell_temperature"]
|
||||||
|
pdc = pvlib.pvsystem.pvwatts_dc(
|
||||||
|
pdc0=pdc0,
|
||||||
|
gamma_pdc=gamma_pdc,
|
||||||
|
temp_cell=temp_cell,
|
||||||
|
g_poa_effective=total_hourly_irradiance,
|
||||||
|
)
|
||||||
|
|
||||||
|
total_hourly_energy = pdc * 15 / 60 / 1e3 # convert to kWh
|
||||||
|
|
||||||
panel_area = c["panel"]["dimensions"]["length"] * c["panel"]["dimensions"]["width"]
|
|
||||||
total_area = panel_area * no_of_panels
|
|
||||||
total_hourly_energy = total_hourly_energy_m2 * total_area
|
|
||||||
total_energy = total_hourly_energy.sum()
|
total_energy = total_hourly_energy.sum()
|
||||||
logger.info(f"Total energy yield calculated: {total_energy} kWh")
|
logger.info(f"Total energy yield calculated: {total_energy} kWh")
|
||||||
|
|
||||||
return total_hourly_energy
|
return total_hourly_energy, no_of_panels
|
||||||
|
|
||||||
|
|
||||||
def calculate_energy_production_horizontal(c):
|
def calculate_energy_production_horizontal(c):
|
||||||
@ -263,7 +258,6 @@ def calculate_energy_production_horizontal(c):
|
|||||||
axis_tilt=axis_tilt,
|
axis_tilt=axis_tilt,
|
||||||
)
|
)
|
||||||
shaded_fraction = shaded_fraction * no_of_shaded_rows / no_of_rows
|
shaded_fraction = shaded_fraction * no_of_shaded_rows / no_of_rows
|
||||||
logger.info(f"Shaded fraction calculated for solar positions: {shaded_fraction}")
|
|
||||||
|
|
||||||
poa = pvlib.irradiance.get_total_irradiance(
|
poa = pvlib.irradiance.get_total_irradiance(
|
||||||
surface_tilt=0,
|
surface_tilt=0,
|
||||||
@ -277,18 +271,22 @@ def calculate_energy_production_horizontal(c):
|
|||||||
)
|
)
|
||||||
poa = poa.dropna(subset=["poa_global"])
|
poa = poa.dropna(subset=["poa_global"])
|
||||||
|
|
||||||
effective_front = (
|
effective_front = poa["poa_global"] * (1 - shaded_fraction)
|
||||||
poa["poa_global"] * (1 - shaded_fraction) * c["panel"]["efficiency"]
|
|
||||||
|
system_size = c["panel"]["peak_power"] * no_of_panels
|
||||||
|
|
||||||
|
pdc0 = system_size
|
||||||
|
gamma_pdc = c["panel"]["temperature_coefficient"]
|
||||||
|
temp_cell = c["panel"]["nominal_operating_cell_temperature"]
|
||||||
|
pdc = pvlib.pvsystem.pvwatts_dc(
|
||||||
|
pdc0=pdc0,
|
||||||
|
gamma_pdc=gamma_pdc,
|
||||||
|
temp_cell=temp_cell,
|
||||||
|
g_poa_effective=effective_front,
|
||||||
)
|
)
|
||||||
|
|
||||||
total_hourly_energy_m2 = effective_front * 15 / 60 / 1e3
|
total_hourly_energy = pdc * 15 / 60 / 1e3 # convert to kWh
|
||||||
energy_total = total_hourly_energy_m2.sum()
|
|
||||||
logger.info(f"Energy yield calculated: {energy_total} kWh/m2")
|
|
||||||
|
|
||||||
panel_area = c["panel"]["dimensions"]["length"] * c["panel"]["dimensions"]["width"]
|
|
||||||
total_area = panel_area * no_of_panels
|
|
||||||
total_hourly_energy = total_hourly_energy_m2 * total_area
|
|
||||||
total_energy = total_hourly_energy.sum()
|
total_energy = total_hourly_energy.sum()
|
||||||
logger.info(f"Total energy yield calculated: {total_energy} kWh")
|
logger.info(f"Total energy yield calculated: {total_energy} kWh")
|
||||||
|
|
||||||
return total_hourly_energy
|
return total_hourly_energy, no_of_panels
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
array:
|
array:
|
||||||
system_size: 900 # in kWp
|
system_size: 900 # in kWp
|
||||||
spacing: 1 # spacing between adjacent panel rows in m
|
spacing: 21.8 # spacing between adjacent panel rows in m
|
||||||
edge_setback: 1.8 # distance from the edge of the roof to the array
|
edge_setback: 1.8 # distance from the edge of the roof to the array
|
||||||
roof_slope: 0
|
roof_slope: 0
|
||||||
slope: 0 # degrees from horizontal (+ve means shaded row is higher than the row in front)
|
slope: 0 # degrees from horizontal (+ve means shaded row is higher than the row in front)
|
||||||
@ -30,3 +30,6 @@ panel:
|
|||||||
length: 2.384
|
length: 2.384
|
||||||
width: 1.303
|
width: 1.303
|
||||||
thickness: 0.033
|
thickness: 0.033
|
||||||
|
temperature_coefficient: -0.0029 # /°C
|
||||||
|
nominal_operating_cell_temperature: 43 # °C
|
||||||
|
|
||||||
|
13
main.py
13
main.py
@ -3,7 +3,10 @@ import yaml
|
|||||||
import logging
|
import logging
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import matplotlib.pyplot as pl
|
import matplotlib.pyplot as pl
|
||||||
from Utilities.Shading import calculate_energy_production_horizontal
|
from Utilities.Shading import (
|
||||||
|
calculate_energy_production_horizontal,
|
||||||
|
calculate_energy_production_vertical,
|
||||||
|
)
|
||||||
from Utilities.Optimisation import optimise_vertical_panel_pitch
|
from Utilities.Optimisation import optimise_vertical_panel_pitch
|
||||||
|
|
||||||
logging.basicConfig(
|
logging.basicConfig(
|
||||||
@ -27,15 +30,17 @@ with open(config_path, "r") as file:
|
|||||||
logger.info("Configuration loaded successfully.")
|
logger.info("Configuration loaded successfully.")
|
||||||
logger.debug(f"Configuration: {c}")
|
logger.debug(f"Configuration: {c}")
|
||||||
|
|
||||||
|
|
||||||
# calculate energy production for horizontal and vertical panels
|
# calculate energy production for horizontal and vertical panels
|
||||||
optimal_pitch, vertical_energy = optimise_vertical_panel_pitch(c)
|
optimal_pitch, vertical_energy, no_of_panels_vertical = optimise_vertical_panel_pitch(c)
|
||||||
logger.info("Energy production for vertical panels calculated successfully.")
|
logger.info("Energy production for vertical panels calculated successfully.")
|
||||||
logger.debug(f"Vertical Energy Production: {vertical_energy.sum()}")
|
logger.debug(f"Vertical Energy Production: {vertical_energy.sum()}")
|
||||||
|
logger.debug("Number of panels: %d", no_of_panels_vertical)
|
||||||
|
|
||||||
horizontal_energy = calculate_energy_production_horizontal(c)
|
horizontal_energy, no_of_panels_horizontal = calculate_energy_production_horizontal(c)
|
||||||
logger.info("Energy production for horizontal panels calculated successfully.")
|
logger.info("Energy production for horizontal panels calculated successfully.")
|
||||||
logger.debug(f"Horizontal Energy Production: {horizontal_energy.sum()}")
|
logger.debug(f"Horizontal Energy Production: {horizontal_energy.sum()}")
|
||||||
|
logger.debug("Number of panels: %d", no_of_panels_horizontal)
|
||||||
|
|
||||||
NOVA_scaledown = 0.75
|
NOVA_scaledown = 0.75
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user