From fe050fecaee9fd2301c674991789d752ce1793ea Mon Sep 17 00:00:00 2001 From: Kevin Anderson Date: Tue, 28 Oct 2025 10:19:28 -0400 Subject: [PATCH 1/3] delete psm3.py and test_psm3.py --- pvlib/iotools/psm3.py | 365 ------------------------------------- tests/iotools/test_psm3.py | 191 ------------------- 2 files changed, 556 deletions(-) delete mode 100644 pvlib/iotools/psm3.py delete mode 100644 tests/iotools/test_psm3.py diff --git a/pvlib/iotools/psm3.py b/pvlib/iotools/psm3.py deleted file mode 100644 index 184a4a7028..0000000000 --- a/pvlib/iotools/psm3.py +++ /dev/null @@ -1,365 +0,0 @@ -""" -Get PSM3 TMY -see https://developer.nrel.gov/docs/solar/nsrdb/psm3_data_download/ -""" - -import io -import requests -import pandas as pd -from json import JSONDecodeError -from pvlib._deprecation import deprecated -from pvlib import tools - -NSRDB_API_BASE = "https://developer.nrel.gov" -PSM_URL = NSRDB_API_BASE + "/api/nsrdb/v2/solar/psm3-2-2-download.csv" -TMY_URL = NSRDB_API_BASE + "/api/nsrdb/v2/solar/psm3-tmy-download.csv" -PSM5MIN_URL = NSRDB_API_BASE + "/api/nsrdb/v2/solar/psm3-5min-download.csv" - -ATTRIBUTES = ( - 'air_temperature', 'dew_point', 'dhi', 'dni', 'ghi', 'surface_albedo', - 'surface_pressure', 'wind_direction', 'wind_speed') -PVLIB_PYTHON = 'pvlib python' - -# Dictionary mapping PSM3 response names to pvlib names -VARIABLE_MAP = { - 'GHI': 'ghi', - 'DHI': 'dhi', - 'DNI': 'dni', - 'Clearsky GHI': 'ghi_clear', - 'Clearsky DHI': 'dhi_clear', - 'Clearsky DNI': 'dni_clear', - 'Solar Zenith Angle': 'solar_zenith', - 'Temperature': 'temp_air', - 'Dew Point': 'temp_dew', - 'Relative Humidity': 'relative_humidity', - 'Pressure': 'pressure', - 'Wind Speed': 'wind_speed', - 'Wind Direction': 'wind_direction', - 'Surface Albedo': 'albedo', - 'Precipitable Water': 'precipitable_water', -} - -# Dictionary mapping pvlib names to PSM3 request names -# Note, PSM3 uses different names for the same variables in the -# response and the request -REQUEST_VARIABLE_MAP = { - 'ghi': 'ghi', - 'dhi': 'dhi', - 'dni': 'dni', - 'ghi_clear': 'clearsky_ghi', - 'dhi_clear': 'clearsky_dhi', - 'dni_clear': 'clearsky_dni', - 'solar_zenith': 'solar_zenith_angle', - 'temp_air': 'air_temperature', - 'temp_dew': 'dew_point', - 'relative_humidity': 'relative_humidity', - 'pressure': 'surface_pressure', - 'wind_speed': 'wind_speed', - 'wind_direction': 'wind_direction', - 'albedo': 'surface_albedo', - 'precipitable_water': 'total_precipitable_water', -} - - -def get_psm3(latitude, longitude, api_key, email, names='tmy', interval=60, - attributes=ATTRIBUTES, leap_day=True, full_name=PVLIB_PYTHON, - affiliation=PVLIB_PYTHON, map_variables=True, url=None, - timeout=30): - """ - Retrieve NSRDB PSM3 timeseries weather data from the PSM3 API. The NSRDB - is described in [1]_ and the PSM3 API is described in [2]_, [3]_, and [4]_. - - .. versionchanged:: 0.9.0 - The function now returns a tuple where the first element is a dataframe - and the second element is a dictionary containing metadata. Previous - versions of this function had the return values switched. - - .. versionchanged:: 0.10.0 - The default endpoint for hourly single-year datasets is now v3.2.2. - The previous datasets can still be accessed (for now) by setting - the ``url`` parameter to the original API endpoint - (``"https://developer.nrel.gov/api/nsrdb/v2/solar/psm3-download.csv"``). - - Parameters - ---------- - latitude : float or int - in decimal degrees, between -90 and 90, north is positive - longitude : float or int - in decimal degrees, between -180 and 180, east is positive - api_key : str - NREL Developer Network API key - email : str - NREL API uses this to automatically communicate messages back - to the user only if necessary - names : str, default 'tmy' - PSM3 API parameter specifing year (e.g. ``2020``) or TMY variant - to download (e.g. ``'tmy'`` or ``'tgy-2019'``). The allowed values - update periodically, so consult the NSRDB references below for the - current set of options. - interval : int, {60, 5, 15, 30} - interval size in minutes, must be 5, 15, 30 or 60. Must be 60 for - typical year requests (i.e., tmy/tgy/tdy). - attributes : list of str, optional - meteorological fields to fetch. If not specified, defaults to - ``pvlib.iotools.psm3.ATTRIBUTES``. See references [2]_, [3]_, and [4]_ - for lists of available fields. Alternatively, pvlib names may also be - used (e.g. 'ghi' rather than 'GHI'); see :const:`REQUEST_VARIABLE_MAP`. - To retrieve all available fields, set ``attributes=[]``. - leap_day : bool, default : True - include leap day in the results. Only used for single-year requests - (i.e., it is ignored for tmy/tgy/tdy requests). - full_name : str, default 'pvlib python' - optional - affiliation : str, default 'pvlib python' - optional - map_variables : bool, default True - When true, renames columns of the Dataframe to pvlib variable names - where applicable. See variable :const:`VARIABLE_MAP`. - url : str, optional - API endpoint URL. If not specified, the endpoint is determined from - the ``names`` and ``interval`` parameters. - timeout : int, default 30 - time in seconds to wait for server response before timeout - - Returns - ------- - data : pandas.DataFrame - timeseries data from NREL PSM3 - metadata : dict - metadata from NREL PSM3 about the record, see - :func:`pvlib.iotools.read_psm3` for fields - - Raises - ------ - requests.HTTPError - if the request response status is not ok, then the ``'errors'`` field - from the JSON response or any error message in the content will be - raised as an exception, for example if the `api_key` was rejected or if - the coordinates were not found in the NSRDB - - Notes - ----- - The required NREL developer key, `api_key`, is available for free by - registering at the `NREL Developer Network `_. - - .. warning:: The "DEMO_KEY" `api_key` is severely rate limited and may - result in rejected requests. - - .. warning:: PSM3 is limited to data found in the NSRDB, please consult the - references below for locations with available data. Additionally, - querying data with < 30-minute resolution uses a different API endpoint - with fewer available fields (see [4]_). - - See Also - -------- - pvlib.iotools.read_psm3 - - References - ---------- - - .. [1] `NREL National Solar Radiation Database (NSRDB) - `_ - .. [2] `Physical Solar Model (PSM) v3.2.2 - `_ - .. [3] `Physical Solar Model (PSM) v3 TMY - `_ - .. [4] `Physical Solar Model (PSM) v3 - Five Minute Temporal Resolution - `_ - """ - # The well know text (WKT) representation of geometry notation is strict. - # A POINT object is a string with longitude first, then the latitude, with - # four decimals each, and exactly one space between them. - longitude = ('%9.4f' % longitude).strip() - latitude = ('%8.4f' % latitude).strip() - # TODO: make format_WKT(object_type, *args) in tools.py - - # convert to string to accomodate integer years being passed in - names = str(names) - - # convert pvlib names in attributes to psm3 convention - attributes = [REQUEST_VARIABLE_MAP.get(a, a) for a in attributes] - - # required query-string parameters for request to PSM3 API - params = { - 'api_key': api_key, - 'full_name': full_name, - 'email': email, - 'affiliation': affiliation, - 'reason': PVLIB_PYTHON, - 'mailing_list': 'false', - 'wkt': 'POINT(%s %s)' % (longitude, latitude), - 'names': names, - 'attributes': ','.join(attributes), - 'leap_day': str(leap_day).lower(), - 'utc': 'false', - 'interval': interval - } - # request CSV download from NREL PSM3 - if url is None: - # determine the endpoint that suits the user inputs - if any(prefix in names for prefix in ('tmy', 'tgy', 'tdy')): - url = TMY_URL - elif interval in (5, 15): - url = PSM5MIN_URL - else: - url = PSM_URL - - response = requests.get(url, params=params, timeout=timeout) - if not response.ok: - # if the API key is rejected, then the response status will be 403 - # Forbidden, and then the error is in the content and there is no JSON - try: - errors = response.json()['errors'] - except JSONDecodeError: - errors = response.content.decode('utf-8') - raise requests.HTTPError(errors, response=response) - # the CSV is in the response content as a UTF-8 bytestring - # to use pandas we need to create a file buffer from the response - fbuf = io.StringIO(response.content.decode('utf-8')) - return read_psm3(fbuf, map_variables) - - -def read_psm3(filename, map_variables=True): - """ - Read an NSRDB PSM3 weather file (formatted as SAM CSV). The NSRDB - is described in [1]_ and the SAM CSV format is described in [2]_. - - .. versionchanged:: 0.9.0 - The function now returns a tuple where the first element is a dataframe - and the second element is a dictionary containing metadata. Previous - versions of this function had the return values switched. - - Parameters - ---------- - filename: str, path-like, or buffer - Filename or in-memory buffer of a file containing data to read. - map_variables: bool, default True - When true, renames columns of the Dataframe to pvlib variable names - where applicable. See variable :const:`VARIABLE_MAP`. - - Returns - ------- - data : pandas.DataFrame - timeseries data from NREL PSM3 - metadata : dict - metadata from NREL PSM3 about the record, see notes for fields - - Notes - ----- - The return is a tuple with two items. The first item is a dataframe with - the PSM3 timeseries data. - - The second item is a dictionary with metadata from NREL PSM3 about the - record containing the following fields: - - * Source - * Location ID - * City - * State - * Country - * Latitude - * Longitude - * Time Zone - * Elevation - * Local Time Zone - * Clearsky DHI Units - * Clearsky DNI Units - * Clearsky GHI Units - * Dew Point Units - * DHI Units - * DNI Units - * GHI Units - * Solar Zenith Angle Units - * Temperature Units - * Pressure Units - * Relative Humidity Units - * Precipitable Water Units - * Wind Direction Units - * Wind Speed Units - * Cloud Type -15 - * Cloud Type 0 - * Cloud Type 1 - * Cloud Type 2 - * Cloud Type 3 - * Cloud Type 4 - * Cloud Type 5 - * Cloud Type 6 - * Cloud Type 7 - * Cloud Type 8 - * Cloud Type 9 - * Cloud Type 10 - * Cloud Type 11 - * Cloud Type 12 - * Fill Flag 0 - * Fill Flag 1 - * Fill Flag 2 - * Fill Flag 3 - * Fill Flag 4 - * Fill Flag 5 - * Surface Albedo Units - * Version - - Examples - -------- - >>> # Read a local PSM3 file: - >>> df, metadata = iotools.read_psm3("data.csv") # doctest: +SKIP - - >>> # Read a file object or an in-memory buffer: - >>> with open(filename, 'r') as f: # doctest: +SKIP - ... df, metadata = iotools.read_psm3(f) # doctest: +SKIP - - See Also - -------- - pvlib.iotools.get_psm3 - - References - ---------- - .. [1] `NREL National Solar Radiation Database (NSRDB) - `_ - .. [2] `Standard Time Series Data File Format - `_ - """ - with tools._file_context_manager(filename) as fbuf: - # The first 2 lines of the response are headers with metadata - metadata_fields = fbuf.readline().split(',') - metadata_values = fbuf.readline().split(',') - # get the column names so we can set the dtypes - columns = fbuf.readline().split(',') - columns[-1] = columns[-1].strip() # strip trailing newline - # Since the header has so many columns, excel saves blank cols in the - # data below the header lines. - columns = [col for col in columns if col != ''] - dtypes = dict.fromkeys(columns, float) # all floats except datevec - dtypes.update({'Year': int, 'Month': int, 'Day': int, 'Hour': int, - 'Minute': int, 'Cloud Type': int, 'Fill Flag': int}) - data = pd.read_csv( - fbuf, header=None, names=columns, usecols=columns, dtype=dtypes, - delimiter=',', lineterminator='\n') # skip carriage returns \r - - metadata_fields[-1] = metadata_fields[-1].strip() # trailing newline - metadata_values[-1] = metadata_values[-1].strip() # trailing newline - metadata = dict(zip(metadata_fields, metadata_values)) - # the response is all strings, so set some metadata types to numbers - metadata['Local Time Zone'] = int(metadata['Local Time Zone']) - metadata['Time Zone'] = int(metadata['Time Zone']) - metadata['Latitude'] = float(metadata['Latitude']) - metadata['Longitude'] = float(metadata['Longitude']) - metadata['Elevation'] = int(metadata['Elevation']) - - # the response 1st 5 columns are a date vector, convert to datetime - dtidx = pd.to_datetime(data[['Year', 'Month', 'Day', 'Hour', 'Minute']]) - # in USA all timezones are integers - tz = 'Etc/GMT%+d' % -metadata['Time Zone'] - data.index = pd.DatetimeIndex(dtidx).tz_localize(tz) - - if map_variables: - data = data.rename(columns=VARIABLE_MAP) - metadata['latitude'] = metadata.pop('Latitude') - metadata['longitude'] = metadata.pop('Longitude') - metadata['altitude'] = metadata.pop('Elevation') - - return data, metadata - - -parse_psm3 = deprecated(since="0.13.0", name="parse_psm3", - alternative="read_psm3")(read_psm3) diff --git a/tests/iotools/test_psm3.py b/tests/iotools/test_psm3.py deleted file mode 100644 index 39de06d234..0000000000 --- a/tests/iotools/test_psm3.py +++ /dev/null @@ -1,191 +0,0 @@ -""" -test iotools for PSM3 -""" - -from pvlib.iotools import psm3 -from tests.conftest import ( - TESTS_DATA_DIR, - RERUNS, - RERUNS_DELAY, - assert_index_equal, - nrel_api_key, -) -import numpy as np -import pandas as pd -import pytest -from requests import HTTPError -from io import StringIO - -from pvlib._deprecation import pvlibDeprecationWarning - - -TMY_TEST_DATA = TESTS_DATA_DIR / 'test_psm3_tmy-2017.csv' -YEAR_TEST_DATA = TESTS_DATA_DIR / 'test_psm3_2017.csv' -YEAR_TEST_DATA_5MIN = TESTS_DATA_DIR / 'test_psm3_2019_5min.csv' -MANUAL_TEST_DATA = TESTS_DATA_DIR / 'test_read_psm3.csv' -LATITUDE, LONGITUDE = 40.5137, -108.5449 -METADATA_FIELDS = [ - 'Source', 'Location ID', 'City', 'State', 'Country', 'Latitude', - 'Longitude', 'Time Zone', 'Elevation', 'Local Time Zone', - 'Dew Point Units', 'DHI Units', 'DNI Units', 'GHI Units', - 'Temperature Units', 'Pressure Units', 'Wind Direction Units', - 'Wind Speed Units', 'Surface Albedo Units', 'Version'] -PVLIB_EMAIL = 'pvlib-admin@googlegroups.com' - - -def assert_psm3_equal(data, metadata, expected): - """check consistency of PSM3 data""" - # check datevec columns - assert np.allclose(data.Year, expected.Year) - assert np.allclose(data.Month, expected.Month) - assert np.allclose(data.Day, expected.Day) - assert np.allclose(data.Hour, expected.Hour) - assert np.allclose(data.Minute, expected.Minute) - # check data columns - assert np.allclose(data.GHI, expected.GHI) - assert np.allclose(data.DNI, expected.DNI) - assert np.allclose(data.DHI, expected.DHI) - assert np.allclose(data.Temperature, expected.Temperature) - assert np.allclose(data.Pressure, expected.Pressure) - assert np.allclose(data['Dew Point'], expected['Dew Point']) - assert np.allclose(data['Surface Albedo'], expected['Surface Albedo']) - assert np.allclose(data['Wind Speed'], expected['Wind Speed']) - assert np.allclose(data['Wind Direction'], expected['Wind Direction']) - # check header - for mf in METADATA_FIELDS: - assert mf in metadata - # check timezone - assert (data.index.tzinfo.zone == 'Etc/GMT%+d' % -metadata['Time Zone']) - - -@pytest.mark.remote_data -@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) -def test_get_psm3_tmy(nrel_api_key): - """test get_psm3 with a TMY""" - data, metadata = psm3.get_psm3(LATITUDE, LONGITUDE, nrel_api_key, - PVLIB_EMAIL, names='tmy-2017', - leap_day=False, map_variables=False) - expected = pd.read_csv(TMY_TEST_DATA) - assert_psm3_equal(data, metadata, expected) - - -@pytest.mark.remote_data -@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) -def test_get_psm3_singleyear(nrel_api_key): - """test get_psm3 with a single year""" - data, metadata = psm3.get_psm3(LATITUDE, LONGITUDE, nrel_api_key, - PVLIB_EMAIL, names='2017', - leap_day=False, map_variables=False, - interval=30) - expected = pd.read_csv(YEAR_TEST_DATA) - assert_psm3_equal(data, metadata, expected) - - -@pytest.mark.remote_data -@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) -def test_get_psm3_5min(nrel_api_key): - """test get_psm3 for 5-minute data""" - data, metadata = psm3.get_psm3(LATITUDE, LONGITUDE, nrel_api_key, - PVLIB_EMAIL, names='2019', interval=5, - leap_day=False, map_variables=False) - assert len(data) == 525600/5 - first_day = data.loc['2019-01-01'] - expected = pd.read_csv(YEAR_TEST_DATA_5MIN) - assert_psm3_equal(first_day, metadata, expected) - - -@pytest.mark.remote_data -@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) -def test_get_psm3_check_leap_day(nrel_api_key): - data_2012, _ = psm3.get_psm3(LATITUDE, LONGITUDE, nrel_api_key, - PVLIB_EMAIL, names="2012", interval=60, - leap_day=True, map_variables=False) - assert len(data_2012) == (8760 + 24) - - -@pytest.mark.parametrize('latitude, longitude, api_key, names, interval', - [(LATITUDE, LONGITUDE, 'BAD', 'tmy-2017', 60), - (51, -5, nrel_api_key, 'tmy-2017', 60), - (LATITUDE, LONGITUDE, nrel_api_key, 'bad', 60), - (LATITUDE, LONGITUDE, nrel_api_key, '2017', 15), - ]) -@pytest.mark.remote_data -@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) -def test_get_psm3_tmy_errors( - latitude, longitude, api_key, names, interval -): - """Test get_psm3() for multiple erroneous input scenarios. - - These scenarios include: - * Bad API key -> HTTP 403 forbidden because api_key is rejected - * Bad latitude/longitude -> Coordinates were not found in the NSRDB. - * Bad name -> Name is not one of the available options. - * Bad interval, single year -> Intervals can only be 30 or 60 minutes. - """ - with pytest.raises(HTTPError) as excinfo: - psm3.get_psm3(latitude, longitude, api_key, PVLIB_EMAIL, - names=names, interval=interval, leap_day=False, - map_variables=False) - # ensure the HTTPError caught isn't due to overuse of the API key - assert "OVER_RATE_LIMIT" not in str(excinfo.value) - - -@pytest.fixture -def io_input(request): - """file-like object for read_psm3""" - with MANUAL_TEST_DATA.open() as f: - data = f.read() - obj = StringIO(data) - return obj - - -def test_parse_psm3(io_input): - """test parse_psm3""" - with pytest.warns(pvlibDeprecationWarning, match='Use read_psm3 instead'): - data, metadata = psm3.parse_psm3(io_input, map_variables=False) - expected = pd.read_csv(YEAR_TEST_DATA) - assert_psm3_equal(data, metadata, expected) - - -def test_read_psm3(): - """test read_psm3""" - data, metadata = psm3.read_psm3(MANUAL_TEST_DATA, map_variables=False) - expected = pd.read_csv(YEAR_TEST_DATA) - assert_psm3_equal(data, metadata, expected) - - -def test_read_psm3_buffer(io_input): - data, metadata = psm3.read_psm3(io_input, map_variables=False) - expected = pd.read_csv(YEAR_TEST_DATA) - assert_psm3_equal(data, metadata, expected) - - -def test_read_psm3_map_variables(): - """test read_psm3 map_variables=True""" - data, metadata = psm3.read_psm3(MANUAL_TEST_DATA, map_variables=True) - columns_mapped = ['Year', 'Month', 'Day', 'Hour', 'Minute', 'dhi', 'ghi', - 'dni', 'ghi_clear', 'dhi_clear', 'dni_clear', - 'Cloud Type', 'temp_dew', 'solar_zenith', - 'Fill Flag', 'albedo', 'wind_speed', - 'wind_direction', 'precipitable_water', - 'relative_humidity', 'temp_air', 'pressure'] - data, metadata = psm3.read_psm3(MANUAL_TEST_DATA, map_variables=True) - assert_index_equal(data.columns, pd.Index(columns_mapped)) - - -@pytest.mark.remote_data -@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) -def test_get_psm3_attribute_mapping(nrel_api_key): - """Test that pvlib names can be passed in as attributes and get correctly - reverse mapped to PSM3 names""" - data, meta = psm3.get_psm3(LATITUDE, LONGITUDE, nrel_api_key, PVLIB_EMAIL, - names=2019, interval=60, - attributes=['ghi', 'wind_speed'], - leap_day=False, map_variables=True) - # Check that columns are in the correct order (GH1647) - expected_columns = [ - 'Year', 'Month', 'Day', 'Hour', 'Minute', 'ghi', 'wind_speed'] - pd.testing.assert_index_equal(pd.Index(expected_columns), data.columns) - assert 'latitude' in meta.keys() - assert 'longitude' in meta.keys() - assert 'altitude' in meta.keys() From f5a6b3274f89b2a83153f260f6744ea7bd7f6262 Mon Sep 17 00:00:00 2001 From: Kevin Anderson Date: Tue, 28 Oct 2025 10:19:48 -0400 Subject: [PATCH 2/3] update references across the docs --- docs/examples/system-models/plot_oedi_9068.py | 10 ++++++---- docs/sphinx/source/reference/iotools.rst | 3 --- docs/sphinx/source/user_guide/extras/faq.rst | 10 +--------- .../source/user_guide/modeling_topics/weather_data.rst | 5 +++-- docs/sphinx/source/whatsnew/v0.10.0.rst | 6 +++--- docs/sphinx/source/whatsnew/v0.10.2.rst | 2 +- docs/sphinx/source/whatsnew/v0.10.3.rst | 2 +- docs/sphinx/source/whatsnew/v0.11.0.rst | 6 +++--- docs/sphinx/source/whatsnew/v0.13.0.rst | 2 +- docs/sphinx/source/whatsnew/v0.7.0.rst | 2 +- docs/sphinx/source/whatsnew/v0.7.1.rst | 4 ++-- docs/sphinx/source/whatsnew/v0.8.0.rst | 2 +- docs/sphinx/source/whatsnew/v0.8.1.rst | 2 +- docs/sphinx/source/whatsnew/v0.9.0.rst | 4 ++-- docs/sphinx/source/whatsnew/v0.9.1.rst | 4 ++-- docs/sphinx/source/whatsnew/v0.9.2.rst | 2 +- docs/sphinx/source/whatsnew/v0.9.5.rst | 2 +- pvlib/iotools/__init__.py | 3 --- pvlib/iotools/psm4.py | 1 - 19 files changed, 30 insertions(+), 42 deletions(-) diff --git a/docs/examples/system-models/plot_oedi_9068.py b/docs/examples/system-models/plot_oedi_9068.py index 8544f34c9a..9a73bfa9ce 100644 --- a/docs/examples/system-models/plot_oedi_9068.py +++ b/docs/examples/system-models/plot_oedi_9068.py @@ -143,10 +143,12 @@ keys = ['ghi', 'dni', 'dhi', 'temp_air', 'wind_speed', 'albedo', 'precipitable_water'] -psm3, psm3_metadata = pvlib.iotools.get_psm3(latitude, longitude, api_key, - email, interval=5, names=2019, - map_variables=True, leap_day=True, - attributes=keys) +psm3, psm3_metadata = pvlib.iotools.get_nsrdb_psm4_conus(latitude, longitude, + api_key, email, + year=2019, interval=5, + parameters=keys, + map_variables=True, + leap_day=True) # %% # Pre-generate some model inputs diff --git a/docs/sphinx/source/reference/iotools.rst b/docs/sphinx/source/reference/iotools.rst index 12db7d6818..70a727b7bb 100644 --- a/docs/sphinx/source/reference/iotools.rst +++ b/docs/sphinx/source/reference/iotools.rst @@ -81,9 +81,6 @@ Satellite-derived irradiance and weather data for the Americas. iotools.get_nsrdb_psm4_conus iotools.get_nsrdb_psm4_full_disc iotools.read_nsrdb_psm4 - iotools.get_psm3 - iotools.read_psm3 - iotools.parse_psm3 Commercial datasets diff --git a/docs/sphinx/source/user_guide/extras/faq.rst b/docs/sphinx/source/user_guide/extras/faq.rst index f87fa101b5..29eeef7c2b 100644 --- a/docs/sphinx/source/user_guide/extras/faq.rst +++ b/docs/sphinx/source/user_guide/extras/faq.rst @@ -52,15 +52,7 @@ Where can I get irradiance data for my simulation? pvlib has a module called iotools which has several functions for retrieving irradiance data as well as reading standard file formats -such as EPW, TMY2, and TMY3. For free irradiance data, you may -consider NREL's NSRDB which can be accessed using the -:py:func:`pvlib.iotools.get_psm3` function and is available for -North America. For Europe and Africa, you may consider looking into -CAMS (:py:func:`pvlib.iotools.get_cams`). -PVGIS (:py:func:`pvlib.iotools.get_pvgis_hourly`) is another option, which -provides irradiance from several different databases with near global coverage. -pvlib also has functions for accessing a plethora of ground-measured -irradiance datasets, including the BSRN, SURFRAD, SRML, and NREL's MIDC. +such as EPW, TMY2, and TMY3. See :ref:`weatherdata`. Can I use PVsyst (PAN/OND) files with pvlib? diff --git a/docs/sphinx/source/user_guide/modeling_topics/weather_data.rst b/docs/sphinx/source/user_guide/modeling_topics/weather_data.rst index 8199044a5c..8e99895ab5 100644 --- a/docs/sphinx/source/user_guide/modeling_topics/weather_data.rst +++ b/docs/sphinx/source/user_guide/modeling_topics/weather_data.rst @@ -112,7 +112,8 @@ online web APIs. For example, :py:func:`~pvlib.iotools.get_pvgis_hourly` downloads data from PVGIS's webservers and returns it as a python variable. Functions that retrieve data from the internet are named ``get_``, followed by the name of the data source: :py:func:`~pvlib.iotools.get_bsrn`, -:py:func:`~pvlib.iotools.get_psm3`, :py:func:`~pvlib.iotools.get_pvgis_tmy`, +:py:func:`~pvlib.iotools.get_nsrdb_psm4_conus`, +:py:func:`~pvlib.iotools.get_pvgis_tmy`, and so on. For satellite/reanalysis datasets, the location is specified by latitude and @@ -121,7 +122,7 @@ longitude in decimal degrees: .. code-block:: python latitude, longitude = 33.75, -84.39 # Atlanta, Georgia, United States - df, metadata = pvlib.iotools.get_psm3(latitude, longitude, map_variables=True, ...) + df, metadata = pvlib.iotools.get_pvgis_tmy(latitude, longitude, map_variables=True, ...) For ground station networks, the location identifier is the station ID: diff --git a/docs/sphinx/source/whatsnew/v0.10.0.rst b/docs/sphinx/source/whatsnew/v0.10.0.rst index 6b6ae1abe9..eaefe12672 100644 --- a/docs/sphinx/source/whatsnew/v0.10.0.rst +++ b/docs/sphinx/source/whatsnew/v0.10.0.rst @@ -109,9 +109,9 @@ Enhancements :py:func:`pvlib.iotools.get_pvgis_horizon`. (:issue:`1290`, :pull:`1395`) * Update the URL used in the :py:func:`pvlib.iotools.get_cams` function. The new URL supports load-balancing and redirects to the fastest server. (:issue:`1688`, :pull:`1740`) -* :py:func:`pvlib.iotools.get_psm3` now has a ``url`` parameter to give the user +* :py:func:`!pvlib.iotools.get_psm3` now has a ``url`` parameter to give the user the option of controlling what NSRDB endpoint is used. (:pull:`1736`) -* :py:func:`pvlib.iotools.get_psm3` now uses the new NSRDB 3.2.2 endpoint for +* :py:func:`!pvlib.iotools.get_psm3` now uses the new NSRDB 3.2.2 endpoint for hourly and half-hourly single-year datasets. (:issue:`1591`, :pull:`1736`) * The default solar position algorithm (NREL SPA) is now 50-100% faster. (:pull:`1748`) * Added functions to retrieve daily precipitation, temperature, and snowfall data @@ -146,7 +146,7 @@ Testing Documentation ~~~~~~~~~~~~~ * Updated the description of the interval parameter in - :py:func:`pvlib.iotools.get_psm3`. (:issue:`1702`, :pull:`1712`) + :py:func:`!pvlib.iotools.get_psm3`. (:issue:`1702`, :pull:`1712`) * Fixed outdated nbviewer links. (:issue:`1721`, :pull:`1726`) diff --git a/docs/sphinx/source/whatsnew/v0.10.2.rst b/docs/sphinx/source/whatsnew/v0.10.2.rst index 3b82d98613..0bba4c69d1 100644 --- a/docs/sphinx/source/whatsnew/v0.10.2.rst +++ b/docs/sphinx/source/whatsnew/v0.10.2.rst @@ -28,7 +28,7 @@ Enhancements Bug fixes ~~~~~~~~~ -* :py:func:`~pvlib.iotools.get_psm3` no longer incorrectly returns clear-sky +* :py:func:`!pvlib.iotools.get_psm3` no longer incorrectly returns clear-sky DHI instead of clear-sky GHI when requesting ``ghi_clear``. (:pull:`1819`) * :py:func:`pvlib.singlediode.bishop88` with ``method='newton'`` no longer crashes when passed ``pandas.Series`` of length one. diff --git a/docs/sphinx/source/whatsnew/v0.10.3.rst b/docs/sphinx/source/whatsnew/v0.10.3.rst index 4d222fca06..6e4ac8e0ca 100644 --- a/docs/sphinx/source/whatsnew/v0.10.3.rst +++ b/docs/sphinx/source/whatsnew/v0.10.3.rst @@ -29,7 +29,7 @@ Bug fixes * Fixed CAMS error message handler in :py:func:`pvlib.iotools.get_cams`. (:issue:`1799`, :pull:`1905`) * Fix mapping of the dew point column to ``temp_dew`` when ``map_variables`` - is True in :py:func:`pvlib.iotools.get_psm3`. (:pull:`1920`) + is True in :py:func:`!pvlib.iotools.get_psm3`. (:pull:`1920`) * Fix :py:class:`pvlib.modelchain.ModelChain` to use attribute `clearsky_model`. (:pull:`1924`) diff --git a/docs/sphinx/source/whatsnew/v0.11.0.rst b/docs/sphinx/source/whatsnew/v0.11.0.rst index 219b57a059..d71fdcd9d9 100644 --- a/docs/sphinx/source/whatsnew/v0.11.0.rst +++ b/docs/sphinx/source/whatsnew/v0.11.0.rst @@ -14,10 +14,10 @@ Breaking changes * ``pvlib.iotools.read_srml_month_from_solardat`` was deprecated in v0.10.0 and has now been completely removed. The function is replaced by :py:func:`~pvlib.iotools.get_srml()`. (:pull:`1779`, :pull:`1989`) -* The ``leap_day`` parameter in :py:func:`~pvlib.iotools.get_psm3` +* The ``leap_day`` parameter in :py:func:`!pvlib.iotools.get_psm3` now defaults to True instead of False. (:issue:`1481`, :pull:`1991`) -* :py:func:`~pvlib.iotools.get_psm3`, :py:func:`~pvlib.iotools.read_psm3`, and - :py:func:`~pvlib.iotools.parse_psm3` all now have ``map_variables=True`` by +* :py:func:`!pvlib.iotools.get_psm3`, :py:func:`!pvlib.iotools.read_psm3`, and + :py:func:`!pvlib.iotools.parse_psm3` all now have ``map_variables=True`` by default. (:issue:`1425`, :pull:`2094`) * The deprecated ``ivcurve_pnts`` parameter of :py:func:`pvlib.pvsystem.singlediode` is removed. Use :py:func:`pvlib.pvsystem.v_from_i` and diff --git a/docs/sphinx/source/whatsnew/v0.13.0.rst b/docs/sphinx/source/whatsnew/v0.13.0.rst index 754d2a539b..94b7aac356 100644 --- a/docs/sphinx/source/whatsnew/v0.13.0.rst +++ b/docs/sphinx/source/whatsnew/v0.13.0.rst @@ -28,7 +28,7 @@ Deprecations :pull:`2467`, :pull:`2466`) - :py:func:`~pvlib.iotools.parse_epw` - - :py:func:`~pvlib.iotools.parse_psm3` + - :py:func:`!pvlib.iotools.parse_psm3` - :py:func:`~pvlib.iotools.parse_cams` - :py:func:`~pvlib.iotools.parse_bsrn` diff --git a/docs/sphinx/source/whatsnew/v0.7.0.rst b/docs/sphinx/source/whatsnew/v0.7.0.rst index 011852e513..41df67ac85 100644 --- a/docs/sphinx/source/whatsnew/v0.7.0.rst +++ b/docs/sphinx/source/whatsnew/v0.7.0.rst @@ -148,7 +148,7 @@ Enhancements diode model fitting function '6parsolve' from NREL's System Advisor Model. * Add :py:func:`~pvlib.ivtools.fit_sdm_desoto`, a method to fit the De Soto single diode model to the typical specifications given in manufacturers datasheets. -* Add `timeout` to :py:func:`pvlib.iotools.get_psm3`. +* Add `timeout` to :py:func:`!pvlib.iotools.get_psm3`. * Add :py:func:`~pvlib.scaling.wvm`, a port of the wavelet variability model for computing reductions in variability due to a spatially distributed plant. * Add :py:meth:`~pvlib.location.Location.from_epw`, a method to create a Location diff --git a/docs/sphinx/source/whatsnew/v0.7.1.rst b/docs/sphinx/source/whatsnew/v0.7.1.rst index af0368821d..630da2cff1 100644 --- a/docs/sphinx/source/whatsnew/v0.7.1.rst +++ b/docs/sphinx/source/whatsnew/v0.7.1.rst @@ -5,8 +5,8 @@ v0.7.1 (January 17, 2020) Enhancements ~~~~~~~~~~~~ -* Added :py:func:`~pvlib.iotools.read_psm3` to read local NSRDB PSM3 files and - :py:func:`~pvlib.iotools.parse_psm3` to parse local NSRDB PSM3 file-like +* Added :py:func:`!pvlib.iotools.read_psm3` to read local NSRDB PSM3 files and + :py:func:`!pvlib.iotools.parse_psm3` to parse local NSRDB PSM3 file-like objects. (:issue:`841`) * Added `leap_day` parameter to `iotools.get_psm3` instead of hardcoding it as False. diff --git a/docs/sphinx/source/whatsnew/v0.8.0.rst b/docs/sphinx/source/whatsnew/v0.8.0.rst index 86fb81574f..e322b9fb0c 100644 --- a/docs/sphinx/source/whatsnew/v0.8.0.rst +++ b/docs/sphinx/source/whatsnew/v0.8.0.rst @@ -19,7 +19,7 @@ Breaking changes * :py:func:`pvlib.iotools.read_tmy3` can now only read local data files because the NREL RREDC server hosting the TMY3 dataset has been retired. For - fetching TMY data from NREL servers, :py:func:`pvlib.iotools.get_psm3` is + fetching TMY data from NREL servers, :py:func:`!pvlib.iotools.get_psm3` is now recommended to retrieve newer PSM3 data over the older TMY3 data. (:issue:`996`) (:pull:`1004`) diff --git a/docs/sphinx/source/whatsnew/v0.8.1.rst b/docs/sphinx/source/whatsnew/v0.8.1.rst index 885973f786..f3ce66c5ff 100644 --- a/docs/sphinx/source/whatsnew/v0.8.1.rst +++ b/docs/sphinx/source/whatsnew/v0.8.1.rst @@ -24,7 +24,7 @@ Enhancements 10 minutes. (:pull:`1074`) * Added :py:func:`pvlib.inverter.sandia_multi` and :py:func:`pvlib.inverter.pvwatts_multi` for modeling inverters with multiple MPPTs (:issue:`457`, :pull:`1085`, :pull:`1106`) -* Added optional ``attributes`` parameter to :py:func:`pvlib.iotools.get_psm3` +* Added optional ``attributes`` parameter to :py:func:`!pvlib.iotools.get_psm3` and added the option of fetching 5- and 15-minute PSM3 data. (:pull:`1086`) * Added :py:func:`pvlib.irradiance.campbell_norman` for estimating DNI, DHI and GHI from extraterrestrial irradiance. This function replaces ``pvlib.irradiance.liujordan``; diff --git a/docs/sphinx/source/whatsnew/v0.9.0.rst b/docs/sphinx/source/whatsnew/v0.9.0.rst index d508f8871a..f574852126 100644 --- a/docs/sphinx/source/whatsnew/v0.9.0.rst +++ b/docs/sphinx/source/whatsnew/v0.9.0.rst @@ -50,7 +50,7 @@ Breaking changes :py:meth:`~pvlib.pvsystem.PVSystem.calcparams_cec` (:issue:`1118`, :pull:`1222`) * Switched the order of the outputs from the PSM3 iotools, notably - :py:func:`~pvlib.iotools.get_psm3` and :py:func:`~pvlib.iotools.read_psm3` + :py:func:`!pvlib.iotools.get_psm3` and :py:func:`!pvlib.iotools.read_psm3` (:issue:`1245`, :pull:`1268`) * Changed the naming of the inputs ``startdate``/``enddate`` to ``start``/``end`` in @@ -223,7 +223,7 @@ Documentation and to make the procedural and OO results match exactly. (:issue:`1116`, :pull:`1144`) * Add a gallery example showing how to appropriately use interval-averaged weather data for modeling. (:pull:`1152`) -* Update documentation links in :py:func:`pvlib.iotools.get_psm3` (:pull:`1169`) +* Update documentation links in :py:func:`!pvlib.iotools.get_psm3` (:pull:`1169`) * Use ``Mount`` classes in ``introtutorial`` and ``pvsystem`` docs pages (:pull:`1267`) * Clarified how statistics are calculated for :py:func:`pvlib.clearsky.detect_clearsky` (:issue:`1070`, :pull:`1243`) diff --git a/docs/sphinx/source/whatsnew/v0.9.1.rst b/docs/sphinx/source/whatsnew/v0.9.1.rst index 0a6a0d70f9..1734398c47 100644 --- a/docs/sphinx/source/whatsnew/v0.9.1.rst +++ b/docs/sphinx/source/whatsnew/v0.9.1.rst @@ -18,8 +18,8 @@ Deprecations Enhancements ~~~~~~~~~~~~ -* Added ``map_variables`` option to :py:func:`pvlib.iotools.get_psm3` and - :py:func:`pvlib.iotools.read_psm3` (:pull:`1374`) +* Added ``map_variables`` option to :py:func:`!pvlib.iotools.get_psm3` and + :py:func:`!pvlib.iotools.read_psm3` (:pull:`1374`) * Added ``pvlib.bifacial.infinite_sheds``, containing a model for irradiance on front and back surfaces of bifacial arrays. (:pull:`717`) * Added ``map_variables`` option to :func:`~pvlib.iotools.read_crn` (:pull:`1368`) diff --git a/docs/sphinx/source/whatsnew/v0.9.2.rst b/docs/sphinx/source/whatsnew/v0.9.2.rst index 2616734036..01612b2fad 100644 --- a/docs/sphinx/source/whatsnew/v0.9.2.rst +++ b/docs/sphinx/source/whatsnew/v0.9.2.rst @@ -35,7 +35,7 @@ Bug fixes timestamps as either 24:00 (which is the standard) as well as 00:00. Previously 00:00 timestamps would incorrectly be moved one day forward. (:pull:`1494`) -* :py:func:`pvlib.iotools.get_psm3` now raises a deprecation warning if +* :py:func:`!pvlib.iotools.get_psm3` now raises a deprecation warning if the ``leap_day`` parameter is not specified in a single-year request. Starting in pvlib 0.11.0 ``leap_day`` will default to True instead of False. (:issue:`1481`, :pull:`1511`) diff --git a/docs/sphinx/source/whatsnew/v0.9.5.rst b/docs/sphinx/source/whatsnew/v0.9.5.rst index 23766d566e..8d9c1b0aec 100644 --- a/docs/sphinx/source/whatsnew/v0.9.5.rst +++ b/docs/sphinx/source/whatsnew/v0.9.5.rst @@ -44,7 +44,7 @@ Bug fixes incorrect loss results for systems that are near the ground. (:issue:`1636`, :pull:`1653`) * Fixed incorrect mapping of requested parameters names when using - :py:func:`pvlib.iotools.get_psm3`. + :py:func:`!pvlib.iotools.get_psm3`. Also fixed the random reordering of the dataframe columns. (:issue:`1629`, :issue:`1647`, :pull:`1648`) * When using ``utc_time_range`` with :py:func:`pvlib.iotools.read_ecmwf_macc`, diff --git a/pvlib/iotools/__init__.py b/pvlib/iotools/__init__.py index 75663507f3..56bbe2cffb 100644 --- a/pvlib/iotools/__init__.py +++ b/pvlib/iotools/__init__.py @@ -8,9 +8,6 @@ from pvlib.iotools.crn import read_crn # noqa: F401 from pvlib.iotools.solrad import read_solrad # noqa: F401 from pvlib.iotools.solrad import get_solrad # noqa: F401 -from pvlib.iotools.psm3 import get_psm3 # noqa: F401 -from pvlib.iotools.psm3 import read_psm3 # noqa: F401 -from pvlib.iotools.psm3 import parse_psm3 # noqa: F401 from pvlib.iotools.psm4 import get_nsrdb_psm4_aggregated # noqa: F401 from pvlib.iotools.psm4 import get_nsrdb_psm4_tmy # noqa: F401 from pvlib.iotools.psm4 import get_nsrdb_psm4_conus # noqa: F401 diff --git a/pvlib/iotools/psm4.py b/pvlib/iotools/psm4.py index 44325364c7..ecab84fd21 100644 --- a/pvlib/iotools/psm4.py +++ b/pvlib/iotools/psm4.py @@ -714,7 +714,6 @@ def read_nsrdb_psm4(filename, map_variables=True): pvlib.iotools.get_nsrdb_psm4_tmy pvlib.iotools.get_nsrdb_psm4_conus pvlib.iotools.get_nsrdb_psm4_full_disc - pvlib.iotools.read_psm3 References ---------- From 5f62e9cfb4a4f4ea9c2b2a4631c2506ab959c456 Mon Sep 17 00:00:00 2001 From: Kevin Anderson Date: Tue, 28 Oct 2025 10:21:07 -0400 Subject: [PATCH 3/3] whatsnew --- docs/sphinx/source/whatsnew/v0.13.2.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/sphinx/source/whatsnew/v0.13.2.rst b/docs/sphinx/source/whatsnew/v0.13.2.rst index e12f7277e5..0956c74a44 100644 --- a/docs/sphinx/source/whatsnew/v0.13.2.rst +++ b/docs/sphinx/source/whatsnew/v0.13.2.rst @@ -6,6 +6,9 @@ v0.13.2 (Anticipated December, 2025) Breaking Changes ~~~~~~~~~~~~~~~~ +* Following the removal of the NSRDB PSM3 API, the :func:`!pvlib.iotools.get_psm3`, + :func:`!pvlib.iotools.read_psm3`, and :func:`!pvlib.iotools.parse_psm3` + functions are removed. (:issue:`2581`, :pull:`2582`) Deprecations