@@ -164,7 +164,8 @@ def get_nsrdb_psm4_aggregated(latitude, longitude, api_key, email,
164164 See Also
165165 --------
166166 pvlib.iotools.get_nsrdb_psm4_tmy, pvlib.iotools.get_nsrdb_psm4_conus,
167- pvlib.iotools.get_nsrdb_psm4_full_disc, pvlib.iotools.read_nsrdb_psm4
167+ pvlib.iotools.get_nsrdb_psm4_full_disc, pvlib.iotools.get_nsrdb_psm4_polar,
168+ pvlib.iotools.get_nsrdb_psm4_polar_tmy, pvlib.iotools.read_nsrdb_psm4
168169
169170 References
170171 ----------
@@ -300,8 +301,10 @@ def get_nsrdb_psm4_tmy(latitude, longitude, api_key, email, year='tmy',
300301
301302 See Also
302303 --------
304+ pvlib.iotools.get_nsrdb_psm4_polar_tmy,
303305 pvlib.iotools.get_nsrdb_psm4_aggregated,
304306 pvlib.iotools.get_nsrdb_psm4_conus, pvlib.iotools.get_nsrdb_psm4_full_disc,
307+ pvlib.iotools.get_nsrdb_psm4_polar,
305308 pvlib.iotools.read_nsrdb_psm4
306309
307310 References
@@ -436,9 +439,9 @@ def get_nsrdb_psm4_conus(latitude, longitude, api_key, email, year,
436439
437440 See Also
438441 --------
439- pvlib.iotools.get_nsrdb_psm4_aggregated,
440- pvlib.iotools.get_nsrdb_psm4_tmy , pvlib.iotools.get_nsrdb_psm4_full_disc ,
441- pvlib.iotools.read_nsrdb_psm4
442+ pvlib.iotools.get_nsrdb_psm4_aggregated, pvlib.iotools.get_nsrdb_psm4_tmy,
443+ pvlib.iotools.get_nsrdb_psm4_full_disc , pvlib.iotools.get_nsrdb_psm4_polar ,
444+ pvlib.iotools.get_nsrdb_psm4_polar_tmy, pvlib.iotools. read_nsrdb_psm4
442445
443446 References
444447 ----------
@@ -575,9 +578,9 @@ def get_nsrdb_psm4_full_disc(latitude, longitude, api_key, email,
575578
576579 See Also
577580 --------
578- pvlib.iotools.get_nsrdb_psm4_aggregated,
579- pvlib.iotools.get_nsrdb_psm4_tmy , pvlib.iotools.get_nsrdb_psm4_conus ,
580- pvlib.iotools.read_nsrdb_psm4
581+ pvlib.iotools.get_nsrdb_psm4_aggregated, pvlib.iotools.get_nsrdb_psm4_tmy,
582+ pvlib.iotools.get_nsrdb_psm4_conus , pvlib.iotools.get_nsrdb_psm4_polar ,
583+ pvlib.iotools.get_nsrdb_psm4_polar_tmy, pvlib.iotools. read_nsrdb_psm4
581584
582585 References
583586 ----------
@@ -640,7 +643,7 @@ def get_nsrdb_psm4_polar(latitude, longitude, api_key, email,
640643 Retrieve timeseries weather data from the PSM4 NSRDB Polar API.
641644
642645 The NSRDB is described in [1]_ and the NSRDB PSM4 NSRDB Polar v4 dataset
643- is described in [2]_, [3]_. The Polar dataset extends NSRDB coverage to
646+ is described in [2]_, [3]_. The polar dataset extends NSRDB coverage to
644647 high latitude regions, that are outside the field of view of the
645648 geostationary satellites.
646649
@@ -721,6 +724,7 @@ def get_nsrdb_psm4_polar(latitude, longitude, api_key, email,
721724 --------
722725 pvlib.iotools.get_nsrdb_psm4_aggregated, pvlib.iotools.get_nsrdb_psm4_tmy,
723726 pvlib.iotools.get_nsrdb_psm4_conus, pvlib.iotools.get_nsrdb_psm4_full_disc,
727+ pvlib.iotools.get_nsrdb_psm4_polar, pvlib.iotools.get_nsrdb_psm4_polar_tmy,
724728 pvlib.iotools.read_nsrdb_psm4
725729
726730 References
@@ -776,6 +780,152 @@ def get_nsrdb_psm4_polar(latitude, longitude, api_key, email,
776780 return read_nsrdb_psm4 (fbuf , map_variables )
777781
778782
783+ def get_nsrdb_psm4_polar_tmy (latitude , longitude , api_key , email , year = 'tmy' ,
784+ time_step = 60 , parameters = PARAMETERS ,
785+ leap_day = False , full_name = PVLIB_PYTHON ,
786+ affiliation = PVLIB_PYTHON , utc = False ,
787+ map_variables = True , url = None , timeout = 30 ):
788+ """
789+ Retrieve timeseries weather data from the PSM4 NSRDB Polar TMY v4 API.
790+
791+ The NSRDB is described in [1]_ and the NSRDB PSM4 Polar TMY v4 dataset is
792+ described in [2]_, [3]_. The polar dataset extends NSRDB coverage to high
793+ latitude polar regions, that are outside the field of view of the
794+ geostationary satellites.
795+
796+ The dataset provides typical year data over the exclusive economic zone
797+ above 60 degrees north. The data have a spatial resolution of 4 km and a
798+ temporal resolution of 1 hour.
799+
800+ Parameters
801+ ----------
802+ latitude : float or int
803+ in decimal degrees, between 60 and 90 degrees north
804+ longitude : float or int
805+ in decimal degrees, between -180 and 180, east is positive
806+ api_key : str
807+ NLR Developer Network API key
808+ email : str
809+ NLR API uses this to automatically communicate messages back
810+ to the user only if necessary
811+ year : str, default 'tmy'
812+ PSM4 API parameter specifing TMY variant to download (e.g. ``'tmy'``
813+ or ``'tgy-2023'``). The allowed values update periodically, so
814+ consult the NSRDB references below for the current set of options.
815+ Called ``names`` in NSRDB API.
816+ time_step : int, {60}
817+ time step in minutes. Must be 60 for typical year requests. Called
818+ ``interval`` in NSRDB API.
819+ parameters : list of str, optional
820+ meteorological fields to fetch. If not specified, defaults to
821+ ``pvlib.iotools.psm4.PARAMETERS``. See reference [2]_ for a list of
822+ available fields. Alternatively, pvlib names may also be used (e.g.
823+ 'ghi' rather than 'GHI'); see :const:`REQUEST_VARIABLE_MAP`. To
824+ retrieve all available fields, set ``parameters=[]``.
825+ leap_day : bool, default : False
826+ Include leap day in the results. Ignored for tmy/tgy/tdy requests.
827+ full_name : str, default 'pvlib python'
828+ optional
829+ affiliation : str, default 'pvlib python'
830+ optional
831+ utc: bool, default : False
832+ retrieve data with timestamps converted to UTC. False returns
833+ timestamps in local standard time of the selected location
834+ map_variables : bool, default True
835+ When true, renames columns of the Dataframe to pvlib variable names
836+ where applicable. See variable :const:`VARIABLE_MAP`.
837+ url : str, optional
838+ Full API endpoint URL. If not specified, the PSM4 Polar TMY v4 URL
839+ is used.
840+ timeout : int, default 30
841+ time in seconds to wait for server response before timeout
842+
843+ Returns
844+ -------
845+ data : pandas.DataFrame
846+ timeseries data from NLR PSM4
847+ metadata : dict
848+ metadata from NLR PSM4 about the record, see
849+ :func:`pvlib.iotools.read_nsrdb_psm4` for fields
850+
851+ Raises
852+ ------
853+ requests.HTTPError
854+ if the request response status is not ok, then the ``'errors'`` field
855+ from the JSON response or any error message in the content will be
856+ raised as an exception, for example if the `api_key` was rejected or if
857+ the coordinates were not found in the NSRDB
858+
859+ Notes
860+ -----
861+ The required NLR developer key, `api_key`, is available for free by
862+ registering at the `NLR Developer Network <https://developer.nlr.gov/>`_.
863+
864+ .. warning:: The "DEMO_KEY" `api_key` is severely rate limited and may
865+ result in rejected requests.
866+
867+ .. warning:: PSM4 is limited to data found in the NSRDB, please consult
868+ the references below for locations with available data.
869+
870+ See Also
871+ --------
872+ pvlib.iotools.get_nsrdb_psm4_polar, pvlib.iotools.get_nsrdb_psm4_tmy,
873+ pvlib.iotools.get_nsrdb_psm4_aggregated, pvlib.iotools.get_nsrdb_psm4_conus,
874+ pvlib.iotools.get_nsrdb_psm4_full_disc, pvlib.iotools.read_nsrdb_psm4
875+
876+ References
877+ ----------
878+ .. [1] `NLR National Solar Radiation Database (NSRDB)
879+ <https://developer.nlr.gov/docs/solar/nsrdb/>`_
880+ .. [2] `NLR NSRDB Polar Data
881+ <https://nsrdb.nlr.gov/data-sets/polar-data/>`_
882+ .. [3] `NSRDB Polar Tmy V4.0.0
883+ <https://developer.nlr.gov/docs/solar/nsrdb/nsrdb-polar-tmy-v4-0-0-download/>`_
884+ """
885+ # The well know text (WKT) representation of geometry notation is strict.
886+ # A POINT object is a string with longitude first, then the latitude, with
887+ # four decimals each, and exactly one space between them.
888+ longitude = ('%9.4f' % longitude ).strip ()
889+ latitude = ('%8.4f' % latitude ).strip ()
890+ # TODO: make format_WKT(object_type, *args) in tools.py
891+
892+ # convert pvlib names in parameters to PSM4 convention
893+ parameters = [REQUEST_VARIABLE_MAP .get (a , a ) for a in parameters ]
894+
895+ # required query-string parameters for request to PSM4 API
896+ params = {
897+ 'api_key' : api_key ,
898+ 'full_name' : full_name ,
899+ 'email' : email ,
900+ 'affiliation' : affiliation ,
901+ 'reason' : PVLIB_PYTHON ,
902+ 'mailing_list' : 'false' ,
903+ 'wkt' : 'POINT(%s %s)' % (longitude , latitude ),
904+ 'names' : year ,
905+ 'attributes' : ',' .join (parameters ),
906+ 'leap_day' : str (leap_day ).lower (),
907+ 'utc' : str (utc ).lower (),
908+ 'interval' : time_step
909+ }
910+ # request CSV download from NLR PSM4
911+ if url is None :
912+ url = PSM4_POLAR_TMY_URL
913+
914+ response = requests .get (url , params = params , timeout = timeout )
915+ if not response .ok :
916+ # if the API key is rejected, then the response status will be 403
917+ # Forbidden, and then the error is in the content and there is no JSON
918+ try :
919+ errors = response .json ()['errors' ]
920+ except JSONDecodeError :
921+ errors = response .content .decode ('utf-8' )
922+ raise requests .HTTPError (errors , response = response )
923+ # the CSV is in the response content as a UTF-8 bytestring
924+ # to use pandas we need to create a file buffer from the response
925+ fbuf = io .StringIO (response .content .decode ('utf-8' ))
926+ return read_nsrdb_psm4 (fbuf , map_variables )
927+
928+
779929def read_nsrdb_psm4 (filename , map_variables = True ):
780930 """
781931 Read an NSRDB PSM4 weather file (formatted as SAM CSV).
@@ -867,6 +1017,8 @@ def read_nsrdb_psm4(filename, map_variables=True):
8671017 pvlib.iotools.get_nsrdb_psm4_tmy
8681018 pvlib.iotools.get_nsrdb_psm4_conus
8691019 pvlib.iotools.get_nsrdb_psm4_full_disc
1020+ pvlib.iotools.get_nsrdb_psm4_polar
1021+ pvlib.iotools.get_nsrdb_psm4_polar_tmy
8701022
8711023 References
8721024 ----------
0 commit comments