Skip to content

Remove pvgis_tmy outputformat='basic' #2416

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions docs/sphinx/source/whatsnew/v0.13.0.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
.. _whatsnew_01300:


v0.13.0 (June XX, 2025)
------------------------

Breaking Changes
~~~~~~~~~~~~~~~~
* Remove the ``outputformat='basic'`` option in :py:func:`~pvlib.iotools.get_pvgis_tmy`
and :py:func:`~pvlib.iotools.read_pvgis_tmy`. (:pull:`2416`)

Bug fixes
~~~~~~~~~


Enhancements
~~~~~~~~~~~~


Documentation
~~~~~~~~~~~~~


Testing
~~~~~~~


Maintenance
~~~~~~~~~~~


Contributors
~~~~~~~~~~~~
* Adam R. Jensen (:ghuser:`AdamRJensen`)
66 changes: 25 additions & 41 deletions pvlib/iotools/pvgis.py
Original file line number Diff line number Diff line change
Expand Up @@ -429,7 +429,7 @@ def get_pvgis_tmy(latitude, longitude, outputformat='json', usehorizon=True,
longitude : float
Longitude in degrees east
outputformat : str, default 'json'
Must be in ``['csv', 'basic', 'epw', 'json']``. See PVGIS TMY tool
Must be in ``['csv', 'epw', 'json']``. See PVGIS TMY tool
documentation [2]_ for more info.
usehorizon : bool, default True
include effects of horizon
Expand Down Expand Up @@ -461,11 +461,11 @@ def get_pvgis_tmy(latitude, longitude, outputformat='json', usehorizon=True,
data : pandas.DataFrame
the weather data
months_selected : list
TMY year for each month, ``None`` for basic and EPW
TMY year for each month, ``None`` for EPW
inputs : dict
the inputs, ``None`` for basic and EPW
the inputs, ``None`` for EPW
metadata : list or dict
file metadata, ``None`` for basic
file metadata

Raises
------
Expand Down Expand Up @@ -516,17 +516,16 @@ def get_pvgis_tmy(latitude, longitude, outputformat='json', usehorizon=True,
elif outputformat == 'csv':
with io.BytesIO(res.content) as src:
data, months_selected, inputs, meta = _parse_pvgis_tmy_csv(src)
elif outputformat == 'basic':
with io.BytesIO(res.content) as src:
data, months_selected, inputs, meta = _parse_pvgis_tmy_basic(src)
elif outputformat == 'epw':
with io.StringIO(res.content.decode('utf-8')) as src:
data, meta = parse_epw(src)
months_selected, inputs = None, None
# raise exception if pvgis format isn't in ['csv', 'json', 'epw']
else:
# this line is never reached because if outputformat is not valid then
# the response is HTTP/1.1 400 BAD REQUEST which is handled earlier
pass
err_msg = (
"pvgis format '{:s}' was unknown, must be either 'json', 'csv', or"
" 'epw'.").format(outputformat)
raise ValueError(err_msg)

if map_variables:
data = data.rename(columns=VARIABLE_MAP)
Expand Down Expand Up @@ -590,14 +589,6 @@ def _parse_pvgis_tmy_csv(src):
return data, months_selected, inputs, meta


def _parse_pvgis_tmy_basic(src):
data = pd.read_csv(src)
data.index = pd.to_datetime(
data['time(UTC)'], format='%Y%m%d:%H%M', utc=True)
data = data.drop('time(UTC)', axis=1)
return data, None, None, None


def read_pvgis_tmy(filename, pvgis_format=None, map_variables=True):
"""
Read a file downloaded from PVGIS.
Expand All @@ -610,11 +601,9 @@ def read_pvgis_tmy(filename, pvgis_format=None, map_variables=True):
Format of PVGIS file or buffer. Equivalent to the ``outputformat``
parameter in the PVGIS TMY API. If ``filename`` is a file and
``pvgis_format`` is not specified then the file extension will be used
to determine the PVGIS format to parse. For PVGIS files from the API
with ``outputformat='basic'``, please set ``pvgis_format`` to
``'basic'``.
to determine the PVGIS format to parse.
If ``filename`` is a buffer, then ``pvgis_format`` is required and must
be in ``['csv', 'epw', 'json', 'basic']``.
be in ``['csv', 'epw', 'json']``.
map_variables: bool, default True
When true, renames columns of the Dataframe to pvlib variable names
where applicable. See variable :const:`VARIABLE_MAP`.
Expand All @@ -625,18 +614,18 @@ def read_pvgis_tmy(filename, pvgis_format=None, map_variables=True):
data : pandas.DataFrame
the weather data
months_selected : list
TMY year for each month, ``None`` for basic and EPW
TMY year for each month, ``None`` for EPW
inputs : dict
the inputs, ``None`` for basic and EPW
the inputs, ``None`` for EPW
metadata : list or dict
file metadata, ``None`` for basic
file metadata

Raises
------
ValueError
if ``pvgis_format`` is not specified and the file extension is neither
``.csv``, ``.json``, nor ``.epw``, or if ``pvgis_format`` is provided
as input but isn't in ``['csv', 'epw', 'json', 'basic']``
as input but isn't in ``['csv', 'epw', 'json']``
TypeError
if ``pvgis_format`` is not specified and ``filename`` is a buffer

Expand All @@ -652,8 +641,7 @@ def read_pvgis_tmy(filename, pvgis_format=None, map_variables=True):
outputformat = Path(filename).suffix[1:].lower()
else:
outputformat = pvgis_format
# parse the pvgis file based on the output format, either 'epw', 'json',
# 'csv', or 'basic'
# parse pvgis file based on outputformat, either 'epw', 'json', or 'csv'

# EPW: use the EPW parser from the pvlib.iotools epw.py module
if outputformat == 'epw':
Expand All @@ -663,7 +651,7 @@ def read_pvgis_tmy(filename, pvgis_format=None, map_variables=True):
data, meta = read_epw(filename)
months_selected, inputs = None, None

# NOTE: json, csv, and basic output formats have parsers defined as private
# NOTE: json and csv output formats have parsers defined as private
# functions in this module

# JSON: use Python built-in json module to convert file contents to a
Expand All @@ -677,24 +665,20 @@ def read_pvgis_tmy(filename, pvgis_format=None, map_variables=True):
src = json.load(fbuf)
data, months_selected, inputs, meta = _parse_pvgis_tmy_json(src)

# CSV or basic: use the correct parser from this module
# eg: _parse_pvgis_tmy_csv() or _parse_pvgist_tmy_basic()
elif outputformat in ['csv', 'basic']:
# get the correct parser function for this output format from globals()
pvgis_parser = globals()['_parse_pvgis_tmy_{:s}'.format(outputformat)]
# NOTE: pvgis_parse() is a pvgis parser function from this module,
# either _parse_pvgis_tmy_csv() or _parse_pvgist_tmy_basic()
elif outputformat == 'csv':
try:
data, months_selected, inputs, meta = pvgis_parser(filename)
data, months_selected, inputs, meta = \
_parse_pvgis_tmy_csv(filename)
except AttributeError: # str/path has no .read() attribute
with open(str(filename), 'rb') as fbuf:
data, months_selected, inputs, meta = pvgis_parser(fbuf)
data, months_selected, inputs, meta = \
_parse_pvgis_tmy_csv(fbuf)

else:
# raise exception if pvgis format isn't in ['csv','basic','epw','json']
# raise exception if pvgis format isn't in ['csv','epw','json']
err_msg = (
"pvgis format '{:s}' was unknown, must be either 'epw', 'json', "
"'csv', or 'basic'").format(outputformat)
"pvgis format '{:s}' was unknown, must be either 'json', 'csv',"
"or 'epw'").format(outputformat)
raise ValueError(err_msg)

if map_variables:
Expand Down
39 changes: 7 additions & 32 deletions tests/iotools/test_pvgis.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,13 @@ def test_get_pvgis_hourly_bad_outputformat(requests_mock):
get_pvgis_hourly(latitude=45, longitude=8, outputformat='basic')


def test_get_pvgis_tmy_basic_outputformat():
# Test if a ValueError is raised if an unsupported outputformat is used
# E.g. 'basic' is a valid PVGIS format, but is not supported by pvlib
with pytest.raises(ValueError):
get_pvgis_tmy(latitude=45, longitude=8, outputformat='basic')


url_additional_inputs = 'https://re.jrc.ec.europa.eu/api/seriescalc?lat=55.6814&lon=12.5758&outputformat=csv&angle=0&aspect=0&pvcalculation=1&pvtechchoice=crystSi&mountingplace=free&trackingtype=0&components=1&usehorizon=1&optimalangles=1&optimalinclination=0&loss=2&userhorizon=10%2C15%2C20%2C10&peakpower=5' # noqa: E501


Expand Down Expand Up @@ -427,21 +434,6 @@ def test_get_pvgis_tmy_kwargs(userhorizon_expected):
assert inputs['meteo_data']['year_max'] == 2016


@pytest.mark.remote_data
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
def test_get_pvgis_tmy_basic(expected, meta_expected):
pvgis_data = get_pvgis_tmy(45, 8, outputformat='basic',
map_variables=False)
_compare_pvgis_tmy_basic(expected, meta_expected, pvgis_data)


def _compare_pvgis_tmy_basic(expected, meta_expected, pvgis_data):
data, _, _, _ = pvgis_data
# check each column of output separately
for outvar in meta_expected['outputs']['tmy_hourly']['variables'].keys():
assert np.allclose(data[outvar], expected[outvar])


@pytest.mark.remote_data
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
def test_get_pvgis_tmy_coerce_year():
Expand Down Expand Up @@ -630,23 +622,6 @@ def test_read_pvgis_tmy_csv(expected, month_year_expected, inputs_expected,
meta_expected, csv_meta, pvgis_data)


def test_read_pvgis_tmy_basic(expected, meta_expected):
fn = TESTS_DATA_DIR / 'tmy_45.000_8.000_2005_2023.txt'
# XXX: can't infer outputformat from file extensions for basic
with pytest.raises(ValueError, match="pvgis format 'txt' was unknown"):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there are not tests now for raising an exception if either the file extension or pvgis_format was not 'json' or 'csv'. Don't need the basic test file, can create a named temp file instead.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is now a dedicated test that checks if an error is raised when a filename with a non-recognized file format is passed.

read_pvgis_tmy(fn, map_variables=False)
# explicit pvgis outputformat
pvgis_data = read_pvgis_tmy(fn, pvgis_format='basic', map_variables=False)
_compare_pvgis_tmy_basic(expected, meta_expected, pvgis_data)
with fn.open('rb') as fbuf:
pvgis_data = read_pvgis_tmy(fbuf, pvgis_format='basic',
map_variables=False)
_compare_pvgis_tmy_basic(expected, meta_expected, pvgis_data)
# file buffer raises TypeError if passed to pathlib.Path()
with pytest.raises(TypeError):
read_pvgis_tmy(fbuf, map_variables=False)


def test_read_pvgis_tmy_exception():
bad_outputformat = 'bad'
err_msg = f"pvgis format '{bad_outputformat:s}' was unknown"
Expand Down