1-
21"""
32Get PSM3 TMY
43see https://developer.nrel.gov/docs/solar/nsrdb/psm3_data_download/
87import requests
98import pandas as pd
109from json import JSONDecodeError
10+ import warnings
11+ from pvlib ._deprecation import pvlibDeprecationWarning
1112
1213NSRDB_API_BASE = "https://developer.nrel.gov"
1314PSM_URL = NSRDB_API_BASE + "/api/nsrdb/v2/solar/psm3-download.csv"
2021 'surface_pressure' , 'wind_direction' , 'wind_speed' )
2122PVLIB_PYTHON = 'pvlib python'
2223
24+ # Dictionary mapping PSM3 names to pvlib names
25+ VARIABLE_MAP = {
26+ 'GHI' : 'ghi' ,
27+ 'DHI' : 'dhi' ,
28+ 'DNI' : 'dni' ,
29+ 'Clearsky GHI' : 'ghi_clear' ,
30+ 'Clearsky DHI' : 'dhi_clear' ,
31+ 'Clearsky DNI' : 'dni_clear' ,
32+ 'Solar Zenith Angle' : 'solar_zenith' ,
33+ 'Temperature' : 'temp_air' ,
34+ 'Relative Humidity' : 'relative_humidity' ,
35+ 'Dew point' : 'temp_dew' ,
36+ 'Pressure' : 'pressure' ,
37+ 'Wind Direction' : 'wind_direction' ,
38+ 'Wind Speed' : 'wind_speed' ,
39+ 'Surface Albedo' : 'albedo' ,
40+ 'Precipitable Water' : 'precipitable_water' ,
41+ }
42+
2343
2444def get_psm3 (latitude , longitude , api_key , email , names = 'tmy' , interval = 60 ,
2545 attributes = ATTRIBUTES , leap_day = False , full_name = PVLIB_PYTHON ,
26- affiliation = PVLIB_PYTHON , timeout = 30 ):
46+ affiliation = PVLIB_PYTHON , map_variables = None , timeout = 30 ):
2747 """
28- Retrieve NSRDB PSM3 timeseries weather data from the PSM3 API. The NSRDB
48+ Retrieve NSRDB PSM3 timeseries weather data from the PSM3 API. The NSRDB
2949 is described in [1]_ and the PSM3 API is described in [2]_, [3]_, and [4]_.
3050
3151 .. versionchanged:: 0.9.0
@@ -48,19 +68,23 @@ def get_psm3(latitude, longitude, api_key, email, names='tmy', interval=60,
4868 PSM3 API parameter specifing year or TMY variant to download, see notes
4969 below for options
5070 interval : int, {60, 5, 15, 30}
51- interval size in minutes, must be 5, 15, 30 or 60. Only used for
71+ interval size in minutes, must be 5, 15, 30 or 60. Only used for
5272 single-year requests (i.e., it is ignored for tmy/tgy/tdy requests).
5373 attributes : list of str, optional
5474 meteorological fields to fetch. If not specified, defaults to
5575 ``pvlib.iotools.psm3.ATTRIBUTES``. See references [2]_, [3]_, and [4]_
56- for lists of available fields.
76+ for lists of available fields. Alternatively, pvlib names may also be
77+ used (e.g. 'ghi' rather than 'GHI'); see :const:`VARIABLE_MAP`.
5778 leap_day : boolean, default False
58- include leap day in the results. Only used for single-year requests
79+ include leap day in the results. Only used for single-year requests
5980 (i.e., it is ignored for tmy/tgy/tdy requests).
6081 full_name : str, default 'pvlib python'
6182 optional
6283 affiliation : str, default 'pvlib python'
6384 optional
85+ map_variables: boolean, optional
86+ When true, renames columns of the Dataframe to pvlib variable names
87+ where applicable. See variable :const:`VARIABLE_MAP`.
6488 timeout : int, default 30
6589 time in seconds to wait for server response before timeout
6690
@@ -96,14 +120,15 @@ def get_psm3(latitude, longitude, api_key, email, names='tmy', interval=60,
96120 +===========+=============================================================+
97121 | Year | 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, |
98122 | | 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, |
99- | | 2018, 2019 |
123+ | | 2018, 2019, 2020 |
100124 +-----------+-------------------------------------------------------------+
101125 | TMY | tmy, tmy-2016, tmy-2017, tdy-2017, tgy-2017, |
102126 | | tmy-2018, tdy-2018, tgy-2018, tmy-2019, tdy-2019, tgy-2019 |
127+ | | tmy-2020, tdy-2020, tgy-2020 |
103128 +-----------+-------------------------------------------------------------+
104129
105130 .. warning:: PSM3 is limited to data found in the NSRDB, please consult the
106- references below for locations with available data. Additionally,
131+ references below for locations with available data. Additionally,
107132 querying data with < 30-minute resolution uses a different API endpoint
108133 with fewer available fields (see [4]_).
109134
@@ -133,6 +158,13 @@ def get_psm3(latitude, longitude, api_key, email, names='tmy', interval=60,
133158 # convert to string to accomodate integer years being passed in
134159 names = str (names )
135160
161+ # convert pvlib names in attributes to psm3 convention (reverse mapping)
162+ # unlike psm3 columns, attributes are lower case and with underscores
163+ amap = {value : key .lower ().replace (' ' , '_' ) for (key , value ) in
164+ VARIABLE_MAP .items ()}
165+ attributes = [amap .get (a , a ) for a in attributes ]
166+ attributes = list (set (attributes )) # remove duplicate values
167+
136168 # required query-string parameters for request to PSM3 API
137169 params = {
138170 'api_key' : api_key ,
@@ -167,12 +199,12 @@ def get_psm3(latitude, longitude, api_key, email, names='tmy', interval=60,
167199 # the CSV is in the response content as a UTF-8 bytestring
168200 # to use pandas we need to create a file buffer from the response
169201 fbuf = io .StringIO (response .content .decode ('utf-8' ))
170- return parse_psm3 (fbuf )
202+ return parse_psm3 (fbuf , map_variables )
171203
172204
173- def parse_psm3 (fbuf ):
205+ def parse_psm3 (fbuf , map_variables = None ):
174206 """
175- Parse an NSRDB PSM3 weather file (formatted as SAM CSV). The NSRDB
207+ Parse an NSRDB PSM3 weather file (formatted as SAM CSV). The NSRDB
176208 is described in [1]_ and the SAM CSV format is described in [2]_.
177209
178210 .. versionchanged:: 0.9.0
@@ -184,6 +216,9 @@ def parse_psm3(fbuf):
184216 ----------
185217 fbuf: file-like object
186218 File-like object containing data to read.
219+ map_variables: bool
220+ When true, renames columns of the Dataframe to pvlib variable names
221+ where applicable. See variable VARIABLE_MAP.
187222
188223 Returns
189224 -------
@@ -296,12 +331,25 @@ def parse_psm3(fbuf):
296331 tz = 'Etc/GMT%+d' % - metadata ['Time Zone' ]
297332 data .index = pd .DatetimeIndex (dtidx ).tz_localize (tz )
298333
334+ if map_variables is None :
335+ warnings .warn (
336+ 'PSM3 variable names will be renamed to pvlib conventions by '
337+ 'default starting in pvlib 0.11.0. Specify map_variables=True '
338+ 'to enable that behavior now, or specify map_variables=False '
339+ 'to hide this warning.' , pvlibDeprecationWarning )
340+ map_variables = False
341+ if map_variables :
342+ data = data .rename (columns = VARIABLE_MAP )
343+ metadata ['latitude' ] = metadata .pop ('Latitude' )
344+ metadata ['longitude' ] = metadata .pop ('Longitude' )
345+ metadata ['altitude' ] = metadata .pop ('Elevation' )
346+
299347 return data , metadata
300348
301349
302- def read_psm3 (filename ):
350+ def read_psm3 (filename , map_variables = None ):
303351 """
304- Read an NSRDB PSM3 weather file (formatted as SAM CSV). The NSRDB
352+ Read an NSRDB PSM3 weather file (formatted as SAM CSV). The NSRDB
305353 is described in [1]_ and the SAM CSV format is described in [2]_.
306354
307355 .. versionchanged:: 0.9.0
@@ -313,6 +361,9 @@ def read_psm3(filename):
313361 ----------
314362 filename: str
315363 Filename of a file containing data to read.
364+ map_variables: bool
365+ When true, renames columns of the Dataframe to pvlib variable names
366+ where applicable. See variable VARIABLE_MAP.
316367
317368 Returns
318369 -------
@@ -334,5 +385,5 @@ def read_psm3(filename):
334385 <https://web.archive.org/web/20170207203107/https://sam.nrel.gov/sites/default/files/content/documents/pdf/wfcsv.pdf>`_
335386 """
336387 with open (str (filename ), 'r' ) as fbuf :
337- content = parse_psm3 (fbuf )
388+ content = parse_psm3 (fbuf , map_variables )
338389 return content
0 commit comments