Source code for pvlib.iotools.psm4

"""
Functions for reading and retrieving data from NSRDB PSM4. See:
https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-GOES-aggregated-v4-0-0-download/
https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-GOES-tmy-v4-0-0-download/
https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-GOES-conus-v4-0-0-download/
https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-GOES-full-disc-v4-0-0-download/
https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-polar-v4-0-0-download/
https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-polar-tmy-v4-0-0-download/
"""

import csv
import io
from urllib.parse import urljoin
import requests
import pandas as pd
from json import JSONDecodeError
from pvlib import tools

NSRDB_API_BASE = "https://developer.nlr.gov/api/nsrdb/v2/solar/"
PSM4_AGG_ENDPOINT = "nsrdb-GOES-aggregated-v4-0-0-download.csv"
PSM4_TMY_ENDPOINT = "nsrdb-GOES-tmy-v4-0-0-download.csv"
PSM4_CON_ENDPOINT = "nsrdb-GOES-conus-v4-0-0-download.csv"
PSM4_FUL_ENDPOINT = "nsrdb-GOES-full-disc-v4-0-0-download.csv"
PSM4_POLAR_ENDPOINT = "nsrdb-polar-v4-0-0-download.csv"
PSM4_POLAR_TMY_ENDPOINT = "nsrdb-polar-tmy-v4-0-0-download.csv"
PSM4_AGG_URL = urljoin(NSRDB_API_BASE, PSM4_AGG_ENDPOINT)
PSM4_TMY_URL = urljoin(NSRDB_API_BASE, PSM4_TMY_ENDPOINT)
PSM4_CON_URL = urljoin(NSRDB_API_BASE, PSM4_CON_ENDPOINT)
PSM4_FUL_URL = urljoin(NSRDB_API_BASE, PSM4_FUL_ENDPOINT)
PSM4_POLAR_URL = urljoin(NSRDB_API_BASE, PSM4_POLAR_ENDPOINT)
PSM4_POLAR_TMY_URL = urljoin(NSRDB_API_BASE, PSM4_POLAR_TMY_ENDPOINT)

PARAMETERS = (
    'air_temperature', 'dew_point', 'dhi', 'dni', 'ghi', 'surface_albedo',
    'surface_pressure', 'wind_direction', 'wind_speed')
PVLIB_PYTHON = 'pvlib python'

# Dictionary mapping PSM4 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',
    'AOD': 'aod',
}

# Dictionary mapping pvlib names to PSM4 request names
# Note, PSM4 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',
    'aod': 'aod',
}


[docs] def get_nsrdb_psm4_aggregated(latitude, longitude, api_key, email, year, time_step=60, parameters=PARAMETERS, leap_day=True, full_name=PVLIB_PYTHON, affiliation=PVLIB_PYTHON, utc=False, map_variables=True, url=None, timeout=30): """ Retrieve NSRDB PSM4 timeseries weather data from the PSM4 NSRDB GOES Aggregated v4 API. The NSRDB is described in [1]_ and the PSM4 NSRDB GOES Aggregated v4 API is described in [2]_. 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 NLR Developer Network API key email : str NLR API uses this to automatically communicate messages back to the user only if necessary year : int or str PSM4 API parameter specifing year (e.g. ``2023``) to download. The allowed values update periodically, so consult the NSRDB reference below for the current set of options. Called ``names`` in NSRDB API. time_step : int, {60, 30} time step in minutes, must be 60 or 30 for PSM4 Aggregated. Called ``interval`` in NSRDB API. parameters : list of str, optional meteorological fields to fetch. If not specified, defaults to ``pvlib.iotools.psm4.PARAMETERS``. See reference [2]_ for a list 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 ``parameters=[]``. leap_day : bool, default : True include leap day in the results full_name : str, default 'pvlib python' optional affiliation : str, default 'pvlib python' optional utc: bool, default : False retrieve data with timestamps converted to UTC. False returns timestamps in local standard time of the selected location 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 Full API endpoint URL. If not specified, the PSM4 GOES Aggregated v4 URL is used. timeout : int, default 30 time in seconds to wait for server response before timeout Returns ------- data : pandas.DataFrame timeseries data from NLR PSM4 metadata : dict metadata from NLR PSM4 about the record, see :func:`pvlib.iotools.read_nsrdb_psm4` 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 NLR developer key, `api_key`, is available for free by registering at the `NLR Developer Network <https://developer.nlr.gov/>`_. .. warning:: The "DEMO_KEY" `api_key` is severely rate limited and may result in rejected requests. .. warning:: PSM4 is limited to data found in the NSRDB, please consult the references below for locations with available data. See Also -------- pvlib.iotools.get_nsrdb_psm4_tmy, pvlib.iotools.get_nsrdb_psm4_conus, pvlib.iotools.get_nsrdb_psm4_full_disc, pvlib.iotools.get_nsrdb_psm4_polar, pvlib.iotools.get_nsrdb_psm4_polar_tmy, pvlib.iotools.read_nsrdb_psm4 References ---------- .. [1] `NLR National Solar Radiation Database (NSRDB) <https://developer.nlr.gov/docs/solar/nsrdb/>`_ .. [2] `NSRDB GOES Aggregated V4.0.0 <https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-GOES-aggregated-v4-0-0-download/>`_ """ # 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 pvlib names in parameters to PSM4 convention parameters = [REQUEST_VARIABLE_MAP.get(a, a) for a in parameters] # required query-string parameters for request to PSM4 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': year, 'attributes': ','.join(parameters), 'leap_day': str(leap_day).lower(), 'utc': str(utc).lower(), 'interval': time_step } # request CSV download from NLR PSM4 if url is None: url = PSM4_AGG_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_nsrdb_psm4(fbuf, map_variables)
[docs] def get_nsrdb_psm4_tmy(latitude, longitude, api_key, email, year='tmy', time_step=60, parameters=PARAMETERS, leap_day=False, full_name=PVLIB_PYTHON, affiliation=PVLIB_PYTHON, utc=False, map_variables=True, url=None, timeout=30): """ Retrieve NSRDB PSM4 timeseries weather data from the PSM4 NSRDB GOES TMY v4 API. The NSRDB is described in [1]_ and the PSM4 NSRDB GOES TMY v4 API is described in [2]_. 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 NLR Developer Network API key email : str NLR API uses this to automatically communicate messages back to the user only if necessary year : str, default 'tmy' PSM4 API parameter specifing TMY variant to download (e.g. ``'tmy'`` or ``'tgy-2022'``). The allowed values update periodically, so consult the NSRDB references below for the current set of options. Called ``names`` in NSRDB API. time_step : int, {60} time step in minutes. Must be 60 for typical year requests. Called ``interval`` in NSRDB API. parameters : list of str, optional meteorological fields to fetch. If not specified, defaults to ``pvlib.iotools.psm4.PARAMETERS``. See reference [2]_ for a list 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 ``parameters=[]``. leap_day : bool, default : False Include leap day in the results. Ignored for tmy/tgy/tdy requests. full_name : str, default 'pvlib python' optional affiliation : str, default 'pvlib python' optional utc: bool, default : False retrieve data with timestamps converted to UTC. False returns timestamps in local standard time of the selected location 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 Full API endpoint URL. If not specified, the PSM4 GOES TMY v4 URL is used. timeout : int, default 30 time in seconds to wait for server response before timeout Returns ------- data : pandas.DataFrame timeseries data from NLR PSM4 metadata : dict metadata from NLR PSM4 about the record, see :func:`pvlib.iotools.read_nsrdb_psm4` 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 NLR developer key, `api_key`, is available for free by registering at the `NLR Developer Network <https://developer.nlr.gov/>`_. .. warning:: The "DEMO_KEY" `api_key` is severely rate limited and may result in rejected requests. .. warning:: PSM4 is limited to data found in the NSRDB, please consult the references below for locations with available data. See Also -------- pvlib.iotools.get_nsrdb_psm4_polar_tmy, pvlib.iotools.get_nsrdb_psm4_aggregated, pvlib.iotools.get_nsrdb_psm4_conus, pvlib.iotools.get_nsrdb_psm4_full_disc, pvlib.iotools.get_nsrdb_psm4_polar, pvlib.iotools.read_nsrdb_psm4 References ---------- .. [1] `NLR National Solar Radiation Database (NSRDB) <https://developer.nlr.gov/docs/solar/nsrdb/>`_ .. [2] `NSRDB GOES Tmy V4.0.0 <https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-GOES-tmy-v4-0-0-download/>`_ """ # 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 pvlib names in parameters to PSM4 convention parameters = [REQUEST_VARIABLE_MAP.get(a, a) for a in parameters] # required query-string parameters for request to PSM4 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': year, 'attributes': ','.join(parameters), 'leap_day': str(leap_day).lower(), 'utc': str(utc).lower(), 'interval': time_step } # request CSV download from NLR PSM4 if url is None: url = PSM4_TMY_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_nsrdb_psm4(fbuf, map_variables)
[docs] def get_nsrdb_psm4_conus(latitude, longitude, api_key, email, year, time_step=60, parameters=PARAMETERS, leap_day=True, full_name=PVLIB_PYTHON, affiliation=PVLIB_PYTHON, utc=False, map_variables=True, url=None, timeout=30): """ Retrieve NSRDB PSM4 timeseries weather data from the PSM4 NSRDB GOES CONUS v4 API. The NSRDB is described in [1]_ and the PSM4 NSRDB GOES CONUS v4 API is described in [2]_. 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 NLR Developer Network API key email : str NLR API uses this to automatically communicate messages back to the user only if necessary year : int or str PSM4 API parameter specifing year (e.g. ``2023``) to download. The allowed values update periodically, so consult the NSRDB reference below for the current set of options. Called ``names`` in NSRDB API. time_step : int, {60, 5, 15, 30} time step in minutes. Called ``interval`` in NSRDB API. parameters : list of str, optional meteorological fields to fetch. If not specified, defaults to ``pvlib.iotools.psm4.PARAMETERS``. See reference [2]_ for a list 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 ``parameters=[]``. leap_day : bool, default : True include leap day in the results full_name : str, default 'pvlib python' optional affiliation : str, default 'pvlib python' optional utc: bool, default : False retrieve data with timestamps converted to UTC. False returns timestamps in local standard time of the selected location 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 Full API endpoint URL. If not specified, the PSM4 GOES CONUS v4 URL is used. timeout : int, default 30 time in seconds to wait for server response before timeout Returns ------- data : pandas.DataFrame timeseries data from NLR PSM4 metadata : dict metadata from NLR PSM4 about the record, see :func:`pvlib.iotools.read_nsrdb_psm4` 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 NLR developer key, `api_key`, is available for free by registering at the `NLR Developer Network <https://developer.nlr.gov/>`_. .. warning:: The "DEMO_KEY" `api_key` is severely rate limited and may result in rejected requests. .. warning:: PSM4 is limited to data found in the NSRDB, please consult the references below for locations with available data. See Also -------- pvlib.iotools.get_nsrdb_psm4_aggregated, pvlib.iotools.get_nsrdb_psm4_tmy, pvlib.iotools.get_nsrdb_psm4_full_disc, pvlib.iotools.get_nsrdb_psm4_polar, pvlib.iotools.get_nsrdb_psm4_polar_tmy, pvlib.iotools.read_nsrdb_psm4 References ---------- .. [1] `NLR National Solar Radiation Database (NSRDB) <https://developer.nlr.gov/docs/solar/nsrdb/>`_ .. [2] `NSRDB GOES Conus V4.0.0 <https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-GOES-conus-v4-0-0-download/>`_ """ # 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 pvlib names in parameters to PSM4 convention parameters = [REQUEST_VARIABLE_MAP.get(a, a) for a in parameters] # required query-string parameters for request to PSM4 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': year, 'attributes': ','.join(parameters), 'leap_day': str(leap_day).lower(), 'utc': str(utc).lower(), 'interval': time_step } # request CSV download from NLR PSM4 if url is None: url = PSM4_CON_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_nsrdb_psm4(fbuf, map_variables)
[docs] def get_nsrdb_psm4_full_disc(latitude, longitude, api_key, email, year, time_step=60, parameters=PARAMETERS, leap_day=True, full_name=PVLIB_PYTHON, affiliation=PVLIB_PYTHON, utc=False, map_variables=True, url=None, timeout=30): """ Retrieve NSRDB PSM4 timeseries weather data from the PSM4 NSRDB GOES Full Disc v4 API. The NSRDB is described in [1]_ and the PSM4 NSRDB GOES Full Disc v4 API is described in [2]_. 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 NLR Developer Network API key email : str NLR API uses this to automatically communicate messages back to the user only if necessary year : int or str PSM4 API parameter specifing year (e.g. ``2023``) to download. The allowed values update periodically, so consult the NSRDB reference below for the current set of options. Called ``names`` in NSRDB API. time_step : int, {60, 10, 30} time step in minutes, must be 10, 30 or 60. Called ``interval`` in NSRDB API. parameters : list of str, optional meteorological fields to fetch. If not specified, defaults to ``pvlib.iotools.psm4.PARAMETERS``. See reference [2]_ for a list 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 ``parameters=[]``. leap_day : bool, default : True include leap day in the results full_name : str, default 'pvlib python' optional affiliation : str, default 'pvlib python' optional utc: bool, default : False retrieve data with timestamps converted to UTC. False returns timestamps in local standard time of the selected location 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 Full API endpoint URL. If not specified, the PSM4 GOES Full Disc v4 URL is used. timeout : int, default 30 time in seconds to wait for server response before timeout Returns ------- data : pandas.DataFrame timeseries data from NLR PSM4 metadata : dict metadata from NLR PSM4 about the record, see :func:`pvlib.iotools.read_nsrdb_psm4` 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 NLR developer key, `api_key`, is available for free by registering at the `NLR Developer Network <https://developer.nlr.gov/>`_. .. warning:: The "DEMO_KEY" `api_key` is severely rate limited and may result in rejected requests. .. warning:: PSM4 is limited to data found in the NSRDB, please consult the references below for locations with available data. See Also -------- pvlib.iotools.get_nsrdb_psm4_aggregated, pvlib.iotools.get_nsrdb_psm4_tmy, pvlib.iotools.get_nsrdb_psm4_conus, pvlib.iotools.get_nsrdb_psm4_polar, pvlib.iotools.get_nsrdb_psm4_polar_tmy, pvlib.iotools.read_nsrdb_psm4 References ---------- .. [1] `NLR National Solar Radiation Database (NSRDB) <https://developer.nlr.gov/docs/solar/nsrdb/>`_ .. [2] `NSRDB GOES Full Disc V4.0.0 <https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-GOES-full-disc-v4-0-0-download/>`_ """ # 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 pvlib names in parameters to PSM4 convention parameters = [REQUEST_VARIABLE_MAP.get(a, a) for a in parameters] # required query-string parameters for request to PSM4 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': year, 'attributes': ','.join(parameters), 'leap_day': str(leap_day).lower(), 'utc': str(utc).lower(), 'interval': time_step } # request CSV download from NLR PSM4 if url is None: url = PSM4_FUL_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_nsrdb_psm4(fbuf, map_variables)
[docs] def get_nsrdb_psm4_polar(latitude, longitude, api_key, email, year, *, time_step=60, parameters=PARAMETERS, leap_day=True, full_name=PVLIB_PYTHON, affiliation=PVLIB_PYTHON, utc=False, map_variables=True, url=None, timeout=30): """ Retrieve timeseries weather data from the PSM4 NSRDB Polar API. The NSRDB is described in [1]_ and the NSRDB PSM4 NSRDB Polar v4 dataset is described in [2]_, [3]_. The polar dataset extends NSRDB coverage to high latitude regions, that are outside the field of view of the geostationary satellites. The dataset provides data from 2013 onwards for the exclusive economic zones above 60 degrees north. The data have a spatial resolution of 4 km and a temporal resolution of 1 hour. Parameters ---------- latitude : float or int in decimal degrees, between 60 and 90 degrees north. Location must be within an exclusive economic zone. longitude : float or int in decimal degrees, between -180 and 180, east is positive. Location must be within an exclusive economic zone. api_key : str NLR Developer Network API key email : str NLR API uses this to automatically communicate messages back to the user only if necessary year : int or str PSM4 API parameter specifing year (e.g. ``2023``) to download. The allowed values update periodically, so consult the NSRDB reference below for the current set of options. Called ``names`` in NSRDB API. time_step : int, default 60 time step in minutes. Called ``interval`` in NSRDB API. Only 60 is supported for the Polar dataset. parameters : list of str, optional meteorological fields to fetch. If not specified, defaults to ``pvlib.iotools.psm4.PARAMETERS``. See reference [2]_ for a list 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 ``parameters=[]``. leap_day : bool, default : True include leap day in the results full_name : str, default 'pvlib python' optional affiliation : str, default 'pvlib python' optional utc: bool, default : False retrieve data with timestamps converted to UTC. False returns timestamps in local standard time of the selected location 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 Full API endpoint URL. If not specified, the PSM4 Polar v4 URL is used. timeout : int, default 30 time in seconds to wait for server response before timeout Returns ------- data : pandas.DataFrame timeseries data from NLR PSM4 Polar metadata : dict metadata from NLR PSM4 about the record, see :func:`pvlib.iotools.read_nsrdb_psm4` 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 NLR developer key, `api_key`, is available for free by registering at the `NLR Developer Network <https://developer.nlr.gov/>`_. .. warning:: The "DEMO_KEY" `api_key` is severely rate limited and may result in rejected requests. .. warning:: PSM4 is limited to data found in the NSRDB, please consult the references below for locations with available data. See Also -------- pvlib.iotools.get_nsrdb_psm4_aggregated, pvlib.iotools.get_nsrdb_psm4_tmy, pvlib.iotools.get_nsrdb_psm4_conus, pvlib.iotools.get_nsrdb_psm4_full_disc, pvlib.iotools.get_nsrdb_psm4_polar, pvlib.iotools.get_nsrdb_psm4_polar_tmy, pvlib.iotools.read_nsrdb_psm4 References ---------- .. [1] `NLR National Solar Radiation Database (NSRDB) <https://developer.nlr.gov/docs/solar/nsrdb/>`_ .. [2] `NLR NSRDB Polar Data <https://nsrdb.nlr.gov/data-sets/polar-data/>`_ .. [3] `NSRDB Polar V4.0.0 <https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-polar-v4-0-0-download/>`_ """ # 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 pvlib names in parameters to PSM4 convention parameters = [REQUEST_VARIABLE_MAP.get(a, a) for a in parameters] # required query-string parameters for request to PSM4 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': year, 'attributes': ','.join(parameters), 'leap_day': str(leap_day).lower(), 'utc': str(utc).lower(), 'interval': time_step } # request CSV download from NLR PSM4 if url is None: url = PSM4_POLAR_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_nsrdb_psm4(fbuf, map_variables)
[docs] def get_nsrdb_psm4_polar_tmy(latitude, longitude, api_key, email, year='tmy', time_step=60, parameters=PARAMETERS, leap_day=False, full_name=PVLIB_PYTHON, affiliation=PVLIB_PYTHON, utc=False, map_variables=True, url=None, timeout=30): """ Retrieve timeseries weather data from the PSM4 NSRDB Polar TMY v4 API. The NSRDB is described in [1]_ and the NSRDB PSM4 Polar TMY v4 dataset is described in [2]_, [3]_. The polar dataset extends NSRDB coverage to high latitude polar regions, that are outside the field of view of the geostationary satellites. The dataset provides typical meteorological year (TMY) data for the exclusive economic zones above 60 degrees north. The data have a spatial resolution of 4 km and a temporal resolution of 1 hour. Parameters ---------- latitude : float or int in decimal degrees, between 60 and 90 degrees north. Location must be within an exclusive economic zone. longitude : float or int in decimal degrees, between -180 and 180, east is positive. Location must be within an exclusive economic zone. api_key : str NLR Developer Network API key email : str NLR API uses this to automatically communicate messages back to the user only if necessary year : str, default 'tmy' PSM4 API parameter specifing TMY variant to download (e.g. ``'tmy'`` or ``'tgy-2023'``). The allowed values update periodically, so consult the NSRDB references below for the current set of options. Called ``names`` in NSRDB API. time_step : int, {60} time step in minutes. Must be 60 for typical year requests. Called ``interval`` in NSRDB API. parameters : list of str, optional meteorological fields to fetch. If not specified, defaults to ``pvlib.iotools.psm4.PARAMETERS``. See reference [2]_ for a list 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 ``parameters=[]``. leap_day : bool, default : False Include leap day in the results. Ignored for tmy/tgy/tdy requests. full_name : str, default 'pvlib python' optional affiliation : str, default 'pvlib python' optional utc: bool, default : False retrieve data with timestamps converted to UTC. False returns timestamps in local standard time of the selected location 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 Full API endpoint URL. If not specified, the PSM4 Polar TMY v4 URL is used. timeout : int, default 30 time in seconds to wait for server response before timeout Returns ------- data : pandas.DataFrame timeseries data from NLR PSM4 Polar TMY metadata : dict metadata from NLR PSM4, see :func:`pvlib.iotools.read_nsrdb_psm4` 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 NLR developer key, `api_key`, is available for free by registering at the `NLR Developer Network <https://developer.nlr.gov/>`_. .. warning:: The "DEMO_KEY" `api_key` is severely rate limited and may result in rejected requests. .. warning:: PSM4 is limited to data found in the NSRDB, please consult the references below for locations with available data. See Also -------- pvlib.iotools.get_nsrdb_psm4_polar, pvlib.iotools.get_nsrdb_psm4_tmy, pvlib.iotools.get_nsrdb_psm4_aggregated, pvlib.iotools.get_nsrdb_psm4_conus, pvlib.iotools.get_nsrdb_psm4_full_disc, pvlib.iotools.read_nsrdb_psm4 References ---------- .. [1] `NLR National Solar Radiation Database (NSRDB) <https://developer.nlr.gov/docs/solar/nsrdb/>`_ .. [2] `NLR NSRDB Polar Data <https://nsrdb.nlr.gov/data-sets/polar-data/>`_ .. [3] `NSRDB Polar Tmy V4.0.0 <https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-polar-tmy-v4-0-0-download/>`_ """ # 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 pvlib names in parameters to PSM4 convention parameters = [REQUEST_VARIABLE_MAP.get(a, a) for a in parameters] # required query-string parameters for request to PSM4 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': year, 'attributes': ','.join(parameters), 'leap_day': str(leap_day).lower(), 'utc': str(utc).lower(), 'interval': time_step } # request CSV download from NLR PSM4 if url is None: url = PSM4_POLAR_TMY_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_nsrdb_psm4(fbuf, map_variables)
[docs] def read_nsrdb_psm4(filename, map_variables=True): """ Read an NSRDB PSM4 weather file (formatted as SAM CSV). The NSRDB is described in [1]_ and the SAM CSV format is described in [2]_. 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 NLR PSM4 metadata : dict metadata from NLR PSM4 about the record, see notes for fields Notes ----- The return is a tuple with two items. The first item is a dataframe with the PSM4 timeseries data. The second item is a dictionary with metadata from NLR PSM4 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 PSM4 file: >>> df, metadata = iotools.read_nsrdb_psm4("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_nsrdb_psm4(f) # doctest: +SKIP See Also -------- pvlib.iotools.get_nsrdb_psm4_aggregated pvlib.iotools.get_nsrdb_psm4_tmy pvlib.iotools.get_nsrdb_psm4_conus pvlib.iotools.get_nsrdb_psm4_full_disc pvlib.iotools.get_nsrdb_psm4_polar pvlib.iotools.get_nsrdb_psm4_polar_tmy References ---------- .. [1] `NLR National Solar Radiation Database (NSRDB) <https://developer.nlr.gov/docs/solar/nsrdb/>`_ .. [2] `Standard Time Series Data File Format <https://web.archive.org/web/20170207203107/https://sam.nrel.gov/sites/default/files/content/documents/pdf/wfcsv.pdf>`_ """ with tools._file_context_manager(filename) as fbuf: # The first 3 header lines are parsed with the csv module rather than a # naive str.split(',') so that quoted fields containing commas are kept # intact. Spectral-on-demand files, for instance, have column names # like '"GaAs (Bauhuis et al., 2009)"' whose embedded commas would # otherwise be split into spurious columns (see GH #2736). # The first 2 lines of the response are headers with metadata metadata_fields = next(csv.reader([fbuf.readline()])) metadata_values = next(csv.reader([fbuf.readline()])) # get the column names so we can set the dtypes columns = next(csv.reader([fbuf.readline()])) 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) 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