PV-Live#
Description of the station network from Lorenz, E. (2021):
“The irradiance measurement network set up in the project PV-Live consists of 40 measurement stations in the control area of the transmission system operator TransnetBW. With their rather even distribution a good representation of the mean irradiance conditions in the grid area is achieved. The distance between neighbouring measurement stations ranges between 15 km and 35 km. Details of the station locations, including coordinates and heights, are given in Table 1. Most stations are installed close to PV power plants. A few of them are located at outposts and buildings of TransnetBW, public utilities and other station partners.
A PV-Live measurement station … consists of a SMP10 thermopile pyranometer (Kipp and Zonen, 2020), which measures GHI, and three Mencke & Tegtmeyer Si sensors (silicon photovoltaic reference cells; Ingenieurbüro Mencke & Tegtmeyer GmbH, 2020), which measure GTI. The three Si sensors are oriented towards east (90°), south (180°) and west (270°) and have tilt angles of 25°. Besides irradiance also temperature is measured by the pyranometer as well as the silicon sensors.
The SMP10 pyranometers are ISO-9060 (International Organization for Standardization, 2018) spectrally flat Class A sensors. They are not equipped with ventilation units, because these would have caused considerable additional costs and an increase in electricity consumption.”
References:
The 40 stations in the network are:
Station name | Latitude | Longitude | Altitude |
---|---|---|---|
Loading ITables v2.2.2 from the init_notebook_mode cell...
(need help?) |
And a nice map, just to get a sense of the distribution of the stations:
Data availability#
“The PV-Live irradiance and temperature measurements … are made publicly available on the open-access repository Zenodo (European Organization For Nuclear Research and OpenAIRE, 2013) through the DOI: 10.5281/zenodo.4036728 (Dittmann et al., 2021), starting with data of September 2020. The data set is extended by adding data of the previous month on a monthly basis. All measurement values are provided with quality flags as described in Section 6.
Near real-time availability is an important aspect of the PV-Live network. Measurements are taken once per second and stored as oneminute mean values. Near real-time data transfer of these minute values is achieved by using the mobile phone network to establish connections of the measurement stations with Fraunhofer ISE. In addition, the measurements are stored in an internal memory. In case a measurement station is temporarily unavailable, the data is completed automatically, when the connection is re-established.”
Below is an example of how to retrieve time series irradiance data from the PV-Live network.
Show code cell source
import urllib.request
import io
import zipfile
import os
import pandas as pd
import warnings
PVLIVE_BASE_URL ='https://zenodo.org/record/7311989/files/'
def get_pvlive(station, start, end):
"""
Retrieve ground measured irradiance data from the PV-Live network.
The PV-Live network consists of 40 solar irradiance measurement stations
across the German state of Baden-Württemberg. All stations measure global
horizontal irradiance and temperature with a pyranometer, and global tilted
irradiance in east, south, and west direction with tilt angles of 25° with
three photovoltaic reference cells in minute resolution [1]_.
Data is available from Zenodo [2]_.
Parameters
----------
station: int
Station number (integer). All stations can be requested by specifying
station='all'.
start: datetime-like
First day of the requested period
end: datetime-like
Last day of the requested period
Returns
-------
data: DataFrame
timeseries data from the PV-LIVE measurement network.
metadata: dict
metadata (not currently available).
Warns
-----
UserWarning
If one or more requested files are missing a UserWarning is returned.
Notes
-----
Data is returned for the entire months between and including start and end.
Examples
--------
>>> # Retrieve two months irradiance data PV-Live station 1
>>> data, metadata = get_pvlive(station=1, # doctest: +SKIP
>>> start=pd.Timestamp(2021,1,1), end=pd.Timestamp(2021,2,28), # doctest: +SKIP
References
----------
.. [1] `High resolution measurement network of global horizontal and tilted solar
irradiance in southern Germany with a new quality control scheme. Elke Lorenz
et al. 2022. Solar Energy.
<https://doi.org/10.1016/j.solener.2021.11.023/>`_
.. [2] `Zenodo
<https://doi.org/10.5281/zenodo.4036728>`_
""" # noqa: E501
months = pd.date_range(start, end.replace(day=1)+pd.DateOffset(months=1), freq='1ME')
dfs_inter_months = [] # Initialize list for holding dataframes
for m in months:
# Generate URL to the monthly ZIP archive
url = f"{PVLIVE_BASE_URL}pvlive_{m.year}-{m.month:02}.zip?download=1"
try:
remotezip = urllib.request.urlopen(url)
except urllib.error.HTTPError as e:
if 'not found' in e.msg.lower():
warnings.warn('File was not found. The selected time period is probably '
'outside the available time period')
continue
else:
raise
zipinmemory = io.BytesIO(remotezip.read())
zip = zipfile.ZipFile(zipinmemory)
dfs_intra_months = []
for filename in zip.namelist():
basename = os.path.basename(filename) # Extract file basename
# Parse file if extension is file starts wtih 'tng' and ends with '.tsv'
if basename.startswith('tng') & basename.endswith('.tsv'):
# Extract station number from file name
station_number = int(basename[6:8])
if (station=='all') | (station==station_number):
# Read data into pandas dataframe
dfi = pd.read_csv(io.StringIO(zip.read(filename).decode("utf-8")),
sep='\t', index_col=[0], parse_dates=[0])
if station == 'all':
# Add station number to column names (MultiIndex)
dfi.columns = [[station_number]*dfi.shape[1], dfi.columns]
# Add dataframe to list
dfs_intra_months.append(dfi)
dfs_inter_months.append(pd.concat(dfs_intra_months, axis='columns'))
data = pd.concat(dfs_inter_months, axis='rows')
meta = {}
return data, meta
df, _ = get_pvlive(
station=1,
start=pd.Timestamp(2021,1,1),
end=pd.Timestamp(2021,1,31))
df.loc['2021-01-02 09:00': '2021-01-02 09:05'] # Print six minutes of measurements
Gg_pyr | flag_Gg_pyr | Gg_si_south | flag_Gg_si_south | Gg_si_east | flag_Gg_si_east | Gg_si_west | flag_Gg_si_west | T_pyr | flag_T_pyr | flag_shading | |
---|---|---|---|---|---|---|---|---|---|---|---|
datetime | |||||||||||
2021-01-02 09:00:00 | 42.5 | 0 | 41.6 | 0 | 42.7 | 0 | 35.9 | 0 | 0.3 | 0 | 0 |
2021-01-02 09:01:00 | 45.5 | 0 | 45.0 | 0 | 45.6 | 0 | 38.9 | 0 | 0.3 | 0 | 0 |
2021-01-02 09:02:00 | 48.7 | 0 | 48.0 | 0 | 48.5 | 0 | 41.9 | 0 | 0.3 | 0 | 0 |
2021-01-02 09:03:00 | 51.3 | 0 | 50.6 | 0 | 51.0 | 0 | 44.2 | 0 | 0.3 | 0 | 0 |
2021-01-02 09:04:00 | 53.7 | 0 | 52.9 | 0 | 53.6 | 0 | 46.1 | 0 | 0.3 | 0 | 0 |
2021-01-02 09:05:00 | 56.5 | 0 | 56.2 | 0 | 56.5 | 0 | 48.6 | 0 | 0.3 | 0 | 0 |
Visualize measurement data#
_ = df[['Gg_pyr', 'Gg_si_south', 'Gg_si_east', 'Gg_si_west']].plot(
subplots=True, sharex=True, figsize=(6,6))
Visualize one day of measurement data#
_ = df[['Gg_pyr', 'Gg_si_south', 'Gg_si_east', 'Gg_si_west']].loc['2021-01-16 05':'2021-01-16 18'].plot()
Weekly incident irradiation#
df[['Gg_pyr', 'Gg_si_south', 'Gg_si_east', 'Gg_si_west']].resample('1h').mean().resample('1w').sum().plot.bar()
/tmp/ipykernel_1909/383750121.py:1: FutureWarning: 'w' is deprecated and will be removed in a future version, please use 'W' instead.
df[['Gg_pyr', 'Gg_si_south', 'Gg_si_east', 'Gg_si_west']].resample('1h').mean().resample('1w').sum().plot.bar()
<Axes: xlabel='datetime'>