Last active
January 18, 2024 21:57
-
-
Save marians/b3ae2cb82b784bdef5cbde2e17d38265 to your computer and use it in GitHub Desktop.
Modellierung eines Balkonkraftwerks mit pvlib-python und PVGIS
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Modellierung eines Balkonkraftwerks | |
# mit Hoymiles 600 Watt Mikrowechselrichter | |
# und 2 Modulen Typ Canadian Solar CS3L-370MS. | |
# Ausrichtung: 155 (Süd-Süd-Ost), Neigung 12 Grad. | |
# Vollständige Verschattung von Westen bis Norden (270° bis 0°) | |
# durch Gebäude. | |
# | |
# Es wird das Wetter des typischen meteorologischen Jahrs (TMY) | |
# auf Basis der Daten von 2005 bis 2020 verwendet. Mehr dazu | |
# unter https://joint-research-centre.ec.europa.eu/pvgis-online-tool/pvgis-tools/pvgis-typical-meteorological-year-tmy-generator_en | |
# | |
# Für den durchschnittlichen Tagesertrag im Monat werden für | |
# den jeweiligen Monat des TMY die Stundenwerte aller Tage | |
# gemittelt. Die Fehlerbalken in den Balkendiagrammen zeigen | |
# die Standardabweichung innerhalb des Monats. | |
from pvlib import pvsystem, modelchain, location | |
from pvlib.iotools import get_pvgis_tmy | |
import matplotlib.pyplot as plt | |
from numpy import mean, std | |
# Breitengrad des Standorts | |
LATITUDE = 50.9 | |
# Längengrad des Standorts | |
LONGITUDE = 7.2 | |
# Neigungswinkel (0 entspricht horizontal, 90 entspricht senkrecht) | |
TILT = 12 | |
# Himmelsrichtung. 0 = Nord, 90 = Ost, 180 = Süd, 270 = West | |
AZIMUTH = 155 | |
# Horizont-Linie. Jede Zahl steht für einen Azimuth-Bereich von 30°, | |
# angefangen bei 0° (Nord). Die Werte wiederum sind eine Grad-Angabe von | |
# 0 bis 90, wobei 0 für den tiefstmöglichen Hoorizont (keine Verschattung) | |
# steht. 90 Grad steht für vollständige Verschattung, zum Beispiel durch | |
# eine Wand. | |
USER_HORIZON = [90, 10, 10, 10, 10, 10, 10, 10, 10, 90, 90, 90] | |
# Ende Konfiguration | |
# Calculate average day from a series containing multiple days. | |
# Returns tuple of lists for average power and standard deviation. | |
def average_month_day(series): | |
hours = list() | |
for _ in range(0, 24): | |
hours.append([]) | |
for index, value in series.items(): | |
hour = int(index.hour) | |
if value < 0: | |
hours[hour].append(0) | |
else: | |
hours[hour].append(value) | |
averages = [] | |
for n in range(0, 24): | |
averages.append(mean(hours[n])) | |
stdev = [] | |
for n in range(0, 24): | |
stdev.append(std(hours[n])) | |
return (averages, stdev) | |
# Count the hours above a certain wattage. | |
# Returns tuple. | |
def hours_above_nwatts(hours_average): | |
above_50 = 0 | |
above_100 = 0 | |
above_200 = 0 | |
above_300 = 0 | |
above_400 = 0 | |
above_500 = 0 | |
for val in hours_average: | |
if val >= 50: | |
above_50 += 1 | |
if val >= 100: | |
above_100 += 1 | |
if val >= 200: | |
above_200 += 1 | |
if val >= 300: | |
above_300 += 1 | |
if val >= 400: | |
above_400 += 1 | |
if val >= 500: | |
above_500 += 1 | |
return (above_50, above_100, above_200, above_300, above_400, above_500) | |
loc = location.Location(LATITUDE, LONGITUDE) | |
data, months_selected, inputs, metadata = get_pvgis_tmy(latitude=LATITUDE, | |
longitude=LONGITUDE, | |
usehorizon=True, | |
userhorizon=USER_HORIZON, | |
map_variables=True, | |
url='https://re.jrc.ec.europa.eu/api/v5_2/') | |
# To get the data for e. g. 21st of march, we | |
# have to find the right year number to use first. | |
march_begin = f"{months_selected[2]['year']}-3-1" | |
march_end = f"{months_selected[2]['year']}-3-31" | |
weather_march = data.loc[march_begin:march_end] | |
december_begin = f"{months_selected[11]['year']}-12-1" | |
december_end = f"{months_selected[11]['year']}-12-31" | |
weather_december = data.loc[december_begin:december_end] | |
july_begin = f"{months_selected[6]['year']}-7-1" | |
july_end = f"{months_selected[6]['year']}-7-31" | |
weather_july = data.loc[july_begin:july_end] | |
modules = pvsystem.retrieve_sam(path='https://raw.githubusercontent.com/NREL/SAM/patch/deploy/libraries/CEC%20Modules.csv') | |
module = modules['CSI_Solar_Co__Ltd__CS3L_370MS'] | |
inverters = pvsystem.retrieve_sam(path='https://raw.githubusercontent.com/NREL/SAM/patch/deploy/libraries/CEC%20Inverters.csv') | |
inverter = inverters['Hoymiles_Power_Electronics_Inc___HM_600N__240V_'] | |
array_kwargs = dict( | |
module_parameters=module, | |
temperature_model_parameters=dict(a=-3.56, b=-0.075, deltaT=3) | |
) | |
# 2 Strings mit identischer Ausrichtung. | |
# Für Ost-West-Ausrichtung kann man hier auch verschiedene Werte angeben. | |
arrays = [ | |
pvsystem.Array(pvsystem.FixedMount(TILT, AZIMUTH), name='Modules', modules_per_string=1, **array_kwargs), | |
pvsystem.Array(pvsystem.FixedMount(TILT, AZIMUTH), name='Modules', modules_per_string=1, **array_kwargs), | |
] | |
system = pvsystem.PVSystem(arrays=arrays, inverter_parameters=inverter) | |
plt.rcdefaults() | |
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, sharey=True) | |
fig.suptitle(f'Durchschnittliche PV-Leistung im typischen Monat') | |
fig.set_size_inches(18, 5) | |
x_pos = range(0, 24) | |
months = [ | |
("März", weather_march, ax1), | |
("Juli", weather_july, ax2), | |
("Dezember", weather_december, ax3), | |
] | |
for name, weather, ax in months: | |
print(f"\n{name}\n") | |
ax.set_title(name) | |
mc = modelchain.ModelChain(system, loc, aoi_model='physical', spectral_model='no_loss') | |
mc.run_model(weather) | |
hours_average, hours_stdev = average_month_day(mc.results.ac) | |
above_50, above_100, above_200, above_300, above_400, above_500 = hours_above_nwatts(hours_average) | |
print(f'Tägliche Stunden über 50 Watt: {above_50}') | |
print(f'Tägliche Stunden über 100 Watt: {above_100}') | |
print(f'Tägliche Stunden über 200 Watt: {above_200}') | |
print(f'Tägliche Stunden über 300 Watt: {above_300}') | |
print(f'Tägliche Stunden über 400 Watt: {above_400}') | |
print(f'Tägliche Stunden über 500 Watt: {above_500}') | |
print(f'Gesamt im Monat: {(mc.results.ac.sum() / 1000.0):.0f} kWh') | |
ax.bar(x=x_pos, height=hours_average, yerr=hours_stdev) | |
ax.set_ylabel('Leistung (W)') | |
ax.set_xlabel('Tageszeit') | |
ax.set_title(name) | |
fig.savefig(f'simulation-tmy.png', dpi=100) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment