Skip to content

Commit c152699

Browse files
committed
Adding functions for retrieving NSRDB PSM4 Polar datasets
Added two new functions to psm4.py: get_nsrdb_psm4_polar and get_nsrdb_psm4_polar_tmy using the methodology created for other psm4 datasets.
1 parent 770bcd1 commit c152699

File tree

1 file changed

+282
-0
lines changed

1 file changed

+282
-0
lines changed

pvlib/iotools/psm4.py

Lines changed: 282 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
https://developer.nrel.gov/docs/solar/nsrdb/nsrdb-GOES-tmy-v4-0-0-download/
55
https://developer.nrel.gov/docs/solar/nsrdb/nsrdb-GOES-conus-v4-0-0-download/
66
https://developer.nrel.gov/docs/solar/nsrdb/nsrdb-GOES-full-disc-v4-0-0-download/
7+
https://developer.nrel.gov/docs/solar/nsrdb/nsrdb-polar-v4-0-0-download/
8+
https://developer.nrel.gov/docs/solar/nsrdb/nsrdb-polar-tmy-v4-0-0-download/
79
"""
810

911
import io
@@ -18,10 +20,14 @@
1820
PSM4_TMY_ENDPOINT = "nsrdb-GOES-tmy-v4-0-0-download.csv"
1921
PSM4_CON_ENDPOINT = "nsrdb-GOES-conus-v4-0-0-download.csv"
2022
PSM4_FUL_ENDPOINT = "nsrdb-GOES-full-disc-v4-0-0-download.csv"
23+
PSM4_POL_ENDPOINT = "nsrdb-polar-v4-0-0-download.csv"
24+
PSM4_PTMY_ENDPOINT = "nsrdb-polar-tmy-v4-0-0-download.csv"
2125
PSM4_AGG_URL = urljoin(NSRDB_API_BASE, PSM4_AGG_ENDPOINT)
2226
PSM4_TMY_URL = urljoin(NSRDB_API_BASE, PSM4_TMY_ENDPOINT)
2327
PSM4_CON_URL = urljoin(NSRDB_API_BASE, PSM4_CON_ENDPOINT)
2428
PSM4_FUL_URL = urljoin(NSRDB_API_BASE, PSM4_FUL_ENDPOINT)
29+
PSM4_POL_URL = urljoin(NSRDB_API_BASE, PSM4_POL_ENDPOINT)
30+
PSM4_PTMY_URL = urljoin(NSRDB_API_BASE, PSM4_PTMY_ENDPOINT)
2531

2632
PARAMETERS = (
2733
'air_temperature', 'dew_point', 'dhi', 'dni', 'ghi', 'surface_albedo',
@@ -623,6 +629,282 @@ def get_nsrdb_psm4_full_disc(latitude, longitude, api_key, email,
623629
return read_nsrdb_psm4(fbuf, map_variables)
624630

625631

632+
def get_nsrdb_psm4_polar(latitude, longitude, api_key, email,
633+
year, time_step=60,
634+
parameters=PARAMETERS, leap_day=True,
635+
full_name=PVLIB_PYTHON,
636+
affiliation=PVLIB_PYTHON,
637+
utc=False, map_variables=True, url=None,
638+
timeout=30):
639+
"""
640+
Retrieve NSRDB PSM4 Polar timeseries weather data from the PSM4 NSRDB Polar v4 API.
641+
642+
The NSRDB is described in [1]_ and the PSM4 NSRDB Polar v4 API is
643+
described in [2]_.
644+
645+
Parameters
646+
----------
647+
latitude : float or int
648+
in decimal degrees, between -90 and 90, north is positive
649+
longitude : float or int
650+
in decimal degrees, between -180 and 180, east is positive
651+
api_key : str
652+
NREL Developer Network API key
653+
email : str
654+
NREL API uses this to automatically communicate messages back
655+
to the user only if necessary
656+
year : int or str
657+
PSM4 API parameter specifing year (e.g. ``2023``) to download. The
658+
allowed values update periodically, so consult the NSRDB reference
659+
below for the current set of options. Called ``names`` in NSRDB API.
660+
time_step : int, {60}
661+
time step in minutes, must be 60 for PSM4 Polar. Called
662+
``interval`` in NSRDB API.
663+
parameters : list of str, optional
664+
meteorological fields to fetch. If not specified, defaults to
665+
``pvlib.iotools.psm4.PARAMETERS``. See reference [2]_ for a list of
666+
available fields. Alternatively, pvlib names may also be used (e.g.
667+
'ghi' rather than 'GHI'); see :const:`REQUEST_VARIABLE_MAP`. To
668+
retrieve all available fields, set ``parameters=[]``.
669+
leap_day : bool, default : True
670+
include leap day in the results
671+
full_name : str, default 'pvlib python'
672+
optional
673+
affiliation : str, default 'pvlib python'
674+
optional
675+
utc: bool, default : False
676+
retrieve data with timestamps converted to UTC. False returns
677+
timestamps in local standard time of the selected location
678+
map_variables : bool, default True
679+
When true, renames columns of the Dataframe to pvlib variable names
680+
where applicable. See variable :const:`VARIABLE_MAP`.
681+
url : str, optional
682+
Full API endpoint URL. If not specified, the PSM4 Polar v4
683+
URL is used.
684+
timeout : int, default 30
685+
time in seconds to wait for server response before timeout
686+
687+
Returns
688+
-------
689+
data : pandas.DataFrame
690+
timeseries data from NREL PSM4
691+
metadata : dict
692+
metadata from NREL PSM4 about the record, see
693+
:func:`pvlib.iotools.read_nsrdb_psm4` for fields
694+
695+
Raises
696+
------
697+
requests.HTTPError
698+
if the request response status is not ok, then the ``'errors'`` field
699+
from the JSON response or any error message in the content will be
700+
raised as an exception, for example if the `api_key` was rejected or if
701+
the coordinates were not found in the NSRDB
702+
703+
Notes
704+
-----
705+
The required NREL developer key, `api_key`, is available for free by
706+
registering at the `NREL Developer Network <https://developer.nrel.gov/>`_.
707+
708+
.. warning:: The "DEMO_KEY" `api_key` is severely rate limited and may
709+
result in rejected requests.
710+
711+
.. warning:: PSM4 is limited to data found in the NSRDB, please consult
712+
the references below for locations with available data.
713+
714+
See Also
715+
--------
716+
pvlib.iotools.get_nsrdb_psm4_aggregated, pvlib.iotools.get_nsrdb_psm4_tmy, pvlib.iotools.get_nsrdb_psm4_conus,
717+
pvlib.iotools.get_nsrdb_psm4_full_disc, pvlib.iotools.get_nsrdb_psm4_polar_tmy, pvlib.iotools.read_nsrdb_psm4
718+
719+
References
720+
----------
721+
.. [1] `NREL National Solar Radiation Database (NSRDB)
722+
<https://nsrdb.nrel.gov/>`_
723+
.. [2] `NSRDB Polar V4.0.0
724+
<https://developer.nrel.gov/docs/solar/nsrdb/nsrdb-polar-v4-0-0-download/>`_
725+
"""
726+
# The well know text (WKT) representation of geometry notation is strict.
727+
# A POINT object is a string with longitude first, then the latitude, with
728+
# four decimals each, and exactly one space between them.
729+
longitude = ('%9.4f' % longitude).strip()
730+
latitude = ('%8.4f' % latitude).strip()
731+
# TODO: make format_WKT(object_type, *args) in tools.py
732+
733+
# convert pvlib names in parameters to PSM4 convention
734+
parameters = [REQUEST_VARIABLE_MAP.get(a, a) for a in parameters]
735+
736+
# required query-string parameters for request to PSM4 API
737+
params = {
738+
'api_key': api_key,
739+
'full_name': full_name,
740+
'email': email,
741+
'affiliation': affiliation,
742+
'reason': PVLIB_PYTHON,
743+
'mailing_list': 'false',
744+
'wkt': 'POINT(%s %s)' % (longitude, latitude),
745+
'names': year,
746+
'attributes': ','.join(parameters),
747+
'leap_day': str(leap_day).lower(),
748+
'utc': str(utc).lower(),
749+
'interval': time_step
750+
}
751+
# request CSV download from NREL PSM4
752+
if url is None:
753+
url = PSM4_POL_URL
754+
755+
response = requests.get(url, params=params, timeout=timeout)
756+
if not response.ok:
757+
# if the API key is rejected, then the response status will be 403
758+
# Forbidden, and then the error is in the content and there is no JSON
759+
try:
760+
errors = response.json()['errors']
761+
except JSONDecodeError:
762+
errors = response.content.decode('utf-8')
763+
raise requests.HTTPError(errors, response=response)
764+
# the CSV is in the response content as a UTF-8 bytestring
765+
# to use pandas we need to create a file buffer from the response
766+
fbuf = io.StringIO(response.content.decode('utf-8'))
767+
return read_nsrdb_psm4(fbuf, map_variables)
768+
769+
770+
def get_nsrdb_psm4_polar_tmy(latitude, longitude, api_key, email,
771+
year='tmy', time_step=60,
772+
parameters=PARAMETERS, leap_day=False,
773+
full_name=PVLIB_PYTHON,
774+
affiliation=PVLIB_PYTHON,
775+
utc=False, map_variables=True, url=None,
776+
timeout=30):
777+
"""
778+
Retrieve NSRDB PSM4 Polar TMY timeseries weather data from the PSM4 NSRDB Polar TMY v4 API.
779+
780+
The NSRDB is described in [1]_ and the PSM4 NSRDB Polar TMY v4 API is
781+
described in [2]_.
782+
783+
Parameters
784+
----------
785+
latitude : float or int
786+
in decimal degrees, between -90 and 90, north is positive
787+
longitude : float or int
788+
in decimal degrees, between -180 and 180, east is positive
789+
api_key : str
790+
NREL Developer Network API key
791+
email : str
792+
NREL API uses this to automatically communicate messages back
793+
to the user only if necessary
794+
year : str, default 'tmy'
795+
PSM4 API parameter specifing TMY variant to download (e.g. ``'tmy'``
796+
or ``'tgy-2022'``). The allowed values update periodically, so
797+
consult the NSRDB references below for the current set of options.
798+
Called ``names`` in NSRDB API.
799+
time_step : int, {60}
800+
time step in minutes. Must be 60 for typical year requests. Called
801+
``interval`` in NSRDB API.
802+
parameters : list of str, optional
803+
meteorological fields to fetch. If not specified, defaults to
804+
``pvlib.iotools.psm4.PARAMETERS``. See reference [2]_ for a list of
805+
available fields. Alternatively, pvlib names may also be used (e.g.
806+
'ghi' rather than 'GHI'); see :const:`REQUEST_VARIABLE_MAP`. To
807+
retrieve all available fields, set ``parameters=[]``.
808+
leap_day : bool, default : False
809+
Include leap day in the results. Ignored for tmy/tgy/tdy requests.
810+
full_name : str, default 'pvlib python'
811+
optional
812+
affiliation : str, default 'pvlib python'
813+
optional
814+
utc: bool, default : False
815+
retrieve data with timestamps converted to UTC. False returns
816+
timestamps in local standard time of the selected location
817+
map_variables : bool, default True
818+
When true, renames columns of the Dataframe to pvlib variable names
819+
where applicable. See variable :const:`VARIABLE_MAP`.
820+
url : str, optional
821+
Full API endpoint URL. If not specified, the PSM4 Polar TMY v4
822+
URL is used.
823+
timeout : int, default 30
824+
time in seconds to wait for server response before timeout
825+
826+
Returns
827+
-------
828+
data : pandas.DataFrame
829+
timeseries data from NREL PSM4 Polar
830+
metadata : dict
831+
metadata from NREL PSM4 about the record, see
832+
:func:`pvlib.iotools.read_nsrdb_psm4` for fields
833+
834+
Raises
835+
------
836+
requests.HTTPError
837+
if the request response status is not ok, then the ``'errors'`` field
838+
from the JSON response or any error message in the content will be
839+
raised as an exception, for example if the `api_key` was rejected or if
840+
the coordinates were not found in the NSRDB
841+
842+
Notes
843+
-----
844+
The required NREL developer key, `api_key`, is available for free by
845+
registering at the `NREL Developer Network <https://developer.nrel.gov/>`_.
846+
847+
.. warning:: The "DEMO_KEY" `api_key` is severely rate limited and may
848+
result in rejected requests.
849+
850+
.. warning:: PSM4 is limited to data found in the NSRDB, please consult
851+
the references below for locations with available data.
852+
853+
See Also
854+
--------
855+
pvlib.iotools.get_nsrdb_psm4_aggregated, pvlib.iotools.get_nsrdb_psm4_tmy, pvlib.iotools.get_nsrdb_psm4_conus,
856+
pvlib.iotools.get_nsrdb_psm4_full_disc, pvlib.iotools.get_nsrdb_psm4_polar, pvlib.iotools.read_nsrdb_psm4
857+
858+
References
859+
----------
860+
.. [1] `NREL National Solar Radiation Database (NSRDB)
861+
<https://nsrdb.nrel.gov/>`_
862+
.. [2] `NSRDB Polar V4.0.0
863+
<https://developer.nrel.gov/docs/solar/nsrdb/nsrdb-polar-tmy-v4-0-0-download/>`_
864+
"""
865+
# The well know text (WKT) representation of geometry notation is strict.
866+
# A POINT object is a string with longitude first, then the latitude, with
867+
# four decimals each, and exactly one space between them.
868+
longitude = ('%9.4f' % longitude).strip()
869+
latitude = ('%8.4f' % latitude).strip()
870+
# TODO: make format_WKT(object_type, *args) in tools.py
871+
872+
# convert pvlib names in parameters to PSM4 convention
873+
parameters = [REQUEST_VARIABLE_MAP.get(a, a) for a in parameters]
874+
875+
# required query-string parameters for request to PSM4 API
876+
params = {
877+
'api_key': api_key,
878+
'full_name': full_name,
879+
'email': email,
880+
'affiliation': affiliation,
881+
'reason': PVLIB_PYTHON,
882+
'mailing_list': 'false',
883+
'wkt': 'POINT(%s %s)' % (longitude, latitude),
884+
'names': year,
885+
'attributes': ','.join(parameters),
886+
'leap_day': str(leap_day).lower(),
887+
'utc': str(utc).lower(),
888+
'interval': time_step
889+
}
890+
# request CSV download from NREL PSM4
891+
if url is None:
892+
url = PSM4_PTMY_URL
893+
894+
response = requests.get(url, params=params, timeout=timeout)
895+
if not response.ok:
896+
# if the API key is rejected, then the response status will be 403
897+
# Forbidden, and then the error is in the content and there is no JSON
898+
try:
899+
errors = response.json()['errors']
900+
except JSONDecodeError:
901+
errors = response.content.decode('utf-8')
902+
raise requests.HTTPError(errors, response=response)
903+
# the CSV is in the response content as a UTF-8 bytestring
904+
# to use pandas we need to create a file buffer from the response
905+
fbuf = io.StringIO(response.content.decode('utf-8'))
906+
return read_nsrdb_psm4(fbuf, map_variables)
907+
626908
def read_nsrdb_psm4(filename, map_variables=True):
627909
"""
628910
Read an NSRDB PSM4 weather file (formatted as SAM CSV).

0 commit comments

Comments
 (0)