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.”


The 40 stations in the network are:

Station name Latitude Longitude Altitude
Loading... (need help?)

And a nice map, just to get a sense of the distribution of the stations:

Make this Notebook Trusted to load map: File -> Trust Notebook

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.

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]_.

    station: int
        Station number (integer). All stations can be requested by specifying
    start: datetime-like
        First day of the requested period
    end: datetime-like
        Last day of the requested period

    data: DataFrame
        timeseries data from the PV-LIVE measurement network.
    metadata: dict
        metadata (not currently available).

        If one or more requested files are missing a UserWarning is returned.

    Data is returned for the entire months between and including start and end.

    >>> # 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

    .. [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.
    .. [2] `Zenodo
    """  # noqa: E501

    months = pd.date_range(start, end.replace(day=1)+pd.DateOffset(months=1), freq='1M')

    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"
            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')
        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_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(

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
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()