pvlib 0.9.5__py3-none-any.whl → 0.10.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pvlib/__init__.py +3 -2
- pvlib/atmosphere.py +6 -171
- pvlib/bifacial/infinite_sheds.py +30 -267
- pvlib/bifacial/utils.py +225 -5
- pvlib/data/test_psm3_2017.csv +17521 -17521
- pvlib/data/test_read_psm3.csv +17522 -17522
- pvlib/data/test_read_pvgis_horizon.csv +49 -0
- pvlib/data/variables_style_rules.csv +3 -0
- pvlib/iam.py +17 -4
- pvlib/inverter.py +6 -1
- pvlib/iotools/__init__.py +7 -2
- pvlib/iotools/acis.py +516 -0
- pvlib/iotools/midc.py +4 -4
- pvlib/iotools/psm3.py +32 -31
- pvlib/iotools/pvgis.py +84 -28
- pvlib/iotools/sodapro.py +8 -6
- pvlib/iotools/srml.py +121 -18
- pvlib/iotools/surfrad.py +2 -2
- pvlib/iotools/tmy.py +146 -102
- pvlib/irradiance.py +151 -0
- pvlib/ivtools/sde.py +11 -7
- pvlib/ivtools/sdm.py +16 -10
- pvlib/ivtools/utils.py +6 -6
- pvlib/location.py +3 -2
- pvlib/modelchain.py +67 -70
- pvlib/pvsystem.py +160 -532
- pvlib/shading.py +41 -0
- pvlib/singlediode.py +215 -65
- pvlib/soiling.py +3 -3
- pvlib/spa.py +327 -368
- pvlib/spectrum/__init__.py +8 -2
- pvlib/spectrum/mismatch.py +335 -0
- pvlib/temperature.py +1 -8
- pvlib/tests/bifacial/test_infinite_sheds.py +0 -111
- pvlib/tests/bifacial/test_utils.py +101 -4
- pvlib/tests/conftest.py +0 -31
- pvlib/tests/iotools/test_acis.py +213 -0
- pvlib/tests/iotools/test_midc.py +6 -6
- pvlib/tests/iotools/test_psm3.py +3 -3
- pvlib/tests/iotools/test_pvgis.py +21 -14
- pvlib/tests/iotools/test_sodapro.py +1 -1
- pvlib/tests/iotools/test_srml.py +71 -6
- pvlib/tests/iotools/test_tmy.py +43 -8
- pvlib/tests/ivtools/test_sde.py +19 -17
- pvlib/tests/ivtools/test_sdm.py +9 -4
- pvlib/tests/test_atmosphere.py +6 -62
- pvlib/tests/test_iam.py +12 -0
- pvlib/tests/test_irradiance.py +40 -2
- pvlib/tests/test_location.py +1 -1
- pvlib/tests/test_modelchain.py +33 -76
- pvlib/tests/test_pvsystem.py +366 -201
- pvlib/tests/test_shading.py +28 -0
- pvlib/tests/test_singlediode.py +166 -30
- pvlib/tests/test_soiling.py +8 -7
- pvlib/tests/test_spa.py +6 -7
- pvlib/tests/test_spectrum.py +145 -1
- pvlib/tests/test_temperature.py +0 -7
- pvlib/tests/test_tools.py +25 -0
- pvlib/tests/test_tracking.py +0 -149
- pvlib/tools.py +26 -1
- pvlib/tracking.py +1 -269
- {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/METADATA +1 -9
- {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/RECORD +67 -68
- pvlib/forecast.py +0 -1211
- pvlib/iotools/ecmwf_macc.py +0 -312
- pvlib/tests/iotools/test_ecmwf_macc.py +0 -162
- pvlib/tests/test_forecast.py +0 -228
- {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/AUTHORS.md +0 -0
- {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/LICENSE +0 -0
- {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/WHEEL +0 -0
- {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/top_level.txt +0 -0
pvlib/forecast.py
DELETED
|
@@ -1,1211 +0,0 @@
|
|
|
1
|
-
'''
|
|
2
|
-
The 'forecast' module contains class definitions for
|
|
3
|
-
retreiving forecasted data from UNIDATA Thredd servers.
|
|
4
|
-
'''
|
|
5
|
-
from netCDF4 import num2date
|
|
6
|
-
import numpy as np
|
|
7
|
-
import pandas as pd
|
|
8
|
-
from requests.exceptions import HTTPError
|
|
9
|
-
from xml.etree.ElementTree import ParseError
|
|
10
|
-
|
|
11
|
-
from pvlib.location import Location
|
|
12
|
-
from pvlib.irradiance import campbell_norman, get_extra_radiation, disc
|
|
13
|
-
from siphon.catalog import TDSCatalog
|
|
14
|
-
from siphon.ncss import NCSS
|
|
15
|
-
|
|
16
|
-
import warnings
|
|
17
|
-
from pvlib._deprecation import deprecated
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
warnings.warn(
|
|
21
|
-
'The forecast module algorithms and features are highly experimental. '
|
|
22
|
-
'The API may change, the functionality may be consolidated into an io '
|
|
23
|
-
'module, or the module may be separated into its own package.')
|
|
24
|
-
|
|
25
|
-
_forecast_deprecated = deprecated(
|
|
26
|
-
since='0.9.1',
|
|
27
|
-
removal='a future release',
|
|
28
|
-
addendum='For details, see https://pvlib-python.readthedocs.io/en/stable/user_guide/forecasts.html' # noqa: E501
|
|
29
|
-
)
|
|
30
|
-
|
|
31
|
-
# don't decorate the base class to prevent the subclasses from showing
|
|
32
|
-
# duplicate warnings:
|
|
33
|
-
# @_forecast_deprecated
|
|
34
|
-
class ForecastModel:
|
|
35
|
-
"""
|
|
36
|
-
An object for querying and holding forecast model information for
|
|
37
|
-
use within the pvlib library.
|
|
38
|
-
|
|
39
|
-
Simplifies use of siphon library on a THREDDS server.
|
|
40
|
-
|
|
41
|
-
Parameters
|
|
42
|
-
----------
|
|
43
|
-
model_type: string
|
|
44
|
-
UNIDATA category in which the model is located.
|
|
45
|
-
model_name: string
|
|
46
|
-
Name of the UNIDATA forecast model.
|
|
47
|
-
set_type: string
|
|
48
|
-
Model dataset type.
|
|
49
|
-
|
|
50
|
-
Attributes
|
|
51
|
-
----------
|
|
52
|
-
access_url: string
|
|
53
|
-
URL specifying the dataset from data will be retrieved.
|
|
54
|
-
base_tds_url : string
|
|
55
|
-
The top level server address
|
|
56
|
-
catalog_url : string
|
|
57
|
-
The url path of the catalog to parse.
|
|
58
|
-
data: pd.DataFrame
|
|
59
|
-
Data returned from the query.
|
|
60
|
-
data_format: string
|
|
61
|
-
Format of the forecast data being requested from UNIDATA.
|
|
62
|
-
dataset: Dataset
|
|
63
|
-
Object containing information used to access forecast data.
|
|
64
|
-
dataframe_variables: list
|
|
65
|
-
Model variables that are present in the data.
|
|
66
|
-
datasets_list: list
|
|
67
|
-
List of all available datasets.
|
|
68
|
-
fm_models: Dataset
|
|
69
|
-
TDSCatalog object containing all available
|
|
70
|
-
forecast models from UNIDATA.
|
|
71
|
-
fm_models_list: list
|
|
72
|
-
List of all available forecast models from UNIDATA.
|
|
73
|
-
latitude: list
|
|
74
|
-
A list of floats containing latitude values.
|
|
75
|
-
location: Location
|
|
76
|
-
A pvlib Location object containing geographic quantities.
|
|
77
|
-
longitude: list
|
|
78
|
-
A list of floats containing longitude values.
|
|
79
|
-
lbox: boolean
|
|
80
|
-
Indicates the use of a location bounding box.
|
|
81
|
-
ncss: NCSS object
|
|
82
|
-
NCSS
|
|
83
|
-
model_name: string
|
|
84
|
-
Name of the UNIDATA forecast model.
|
|
85
|
-
model: Dataset
|
|
86
|
-
A dictionary of Dataset object, whose keys are the name of the
|
|
87
|
-
dataset's name.
|
|
88
|
-
model_url: string
|
|
89
|
-
The url path of the dataset to parse.
|
|
90
|
-
modelvariables: list
|
|
91
|
-
Common variable names that correspond to queryvariables.
|
|
92
|
-
query: NCSS query object
|
|
93
|
-
NCSS object used to complete the forecast data retrival.
|
|
94
|
-
queryvariables: list
|
|
95
|
-
Variables that are used to query the THREDDS Data Server.
|
|
96
|
-
time: DatetimeIndex
|
|
97
|
-
Time range.
|
|
98
|
-
variables: dict
|
|
99
|
-
Defines the variables to obtain from the weather
|
|
100
|
-
model and how they should be renamed to common variable names.
|
|
101
|
-
units: dict
|
|
102
|
-
Dictionary containing the units of the standard variables
|
|
103
|
-
and the model specific variables.
|
|
104
|
-
vert_level: float or integer
|
|
105
|
-
Vertical altitude for query data.
|
|
106
|
-
"""
|
|
107
|
-
|
|
108
|
-
access_url_key = 'NetcdfSubset'
|
|
109
|
-
catalog_url = 'https://thredds.ucar.edu/thredds/catalog.xml'
|
|
110
|
-
base_tds_url = catalog_url.split('/thredds/')[0]
|
|
111
|
-
data_format = 'netcdf'
|
|
112
|
-
|
|
113
|
-
units = {
|
|
114
|
-
'temp_air': 'C',
|
|
115
|
-
'wind_speed': 'm/s',
|
|
116
|
-
'ghi': 'W/m^2',
|
|
117
|
-
'ghi_raw': 'W/m^2',
|
|
118
|
-
'dni': 'W/m^2',
|
|
119
|
-
'dhi': 'W/m^2',
|
|
120
|
-
'total_clouds': '%',
|
|
121
|
-
'low_clouds': '%',
|
|
122
|
-
'mid_clouds': '%',
|
|
123
|
-
'high_clouds': '%'}
|
|
124
|
-
|
|
125
|
-
def __init__(self, model_type, model_name, set_type, vert_level=None):
|
|
126
|
-
self.model_type = model_type
|
|
127
|
-
self.model_name = model_name
|
|
128
|
-
self.set_type = set_type
|
|
129
|
-
self.connected = False
|
|
130
|
-
self.vert_level = vert_level
|
|
131
|
-
|
|
132
|
-
def connect_to_catalog(self):
|
|
133
|
-
self.catalog = TDSCatalog(self.catalog_url)
|
|
134
|
-
self.fm_models = TDSCatalog(
|
|
135
|
-
self.catalog.catalog_refs[self.model_type].href)
|
|
136
|
-
self.fm_models_list = sorted(list(self.fm_models.catalog_refs.keys()))
|
|
137
|
-
|
|
138
|
-
try:
|
|
139
|
-
model_url = self.fm_models.catalog_refs[self.model_name].href
|
|
140
|
-
except ParseError:
|
|
141
|
-
raise ParseError(self.model_name + ' model may be unavailable.')
|
|
142
|
-
|
|
143
|
-
try:
|
|
144
|
-
self.model = TDSCatalog(model_url)
|
|
145
|
-
except HTTPError:
|
|
146
|
-
try:
|
|
147
|
-
self.model = TDSCatalog(model_url)
|
|
148
|
-
except HTTPError:
|
|
149
|
-
raise HTTPError(self.model_name + ' model may be unavailable.')
|
|
150
|
-
|
|
151
|
-
self.datasets_list = list(self.model.datasets.keys())
|
|
152
|
-
self.set_dataset()
|
|
153
|
-
self.connected = True
|
|
154
|
-
|
|
155
|
-
def __repr__(self):
|
|
156
|
-
return f'{self.model_name}, {self.set_type}'
|
|
157
|
-
|
|
158
|
-
def set_dataset(self):
|
|
159
|
-
'''
|
|
160
|
-
Retrieves the designated dataset, creates NCSS object, and
|
|
161
|
-
creates a NCSS query object.
|
|
162
|
-
'''
|
|
163
|
-
|
|
164
|
-
keys = list(self.model.datasets.keys())
|
|
165
|
-
labels = [item.split()[0].lower() for item in keys]
|
|
166
|
-
if self.set_type == 'best':
|
|
167
|
-
self.dataset = self.model.datasets[keys[labels.index('best')]]
|
|
168
|
-
elif self.set_type == 'latest':
|
|
169
|
-
self.dataset = self.model.datasets[keys[labels.index('latest')]]
|
|
170
|
-
elif self.set_type == 'full':
|
|
171
|
-
self.dataset = self.model.datasets[keys[labels.index('full')]]
|
|
172
|
-
|
|
173
|
-
self.access_url = self.dataset.access_urls[self.access_url_key]
|
|
174
|
-
self.ncss = NCSS(self.access_url)
|
|
175
|
-
self.query = self.ncss.query()
|
|
176
|
-
|
|
177
|
-
def set_query_time_range(self, start, end):
|
|
178
|
-
"""
|
|
179
|
-
Parameters
|
|
180
|
-
----------
|
|
181
|
-
start : datetime.datetime, pandas.Timestamp
|
|
182
|
-
Must be tz-localized.
|
|
183
|
-
end : datetime.datetime, pandas.Timestamp
|
|
184
|
-
Must be tz-localized.
|
|
185
|
-
|
|
186
|
-
Notes
|
|
187
|
-
-----
|
|
188
|
-
Assigns ``self.start``, ``self.end``. Modifies ``self.query``
|
|
189
|
-
"""
|
|
190
|
-
self.start = pd.Timestamp(start)
|
|
191
|
-
self.end = pd.Timestamp(end)
|
|
192
|
-
if self.start.tz is None or self.end.tz is None:
|
|
193
|
-
raise TypeError('start and end must be tz-localized')
|
|
194
|
-
# don't assume that siphon or the server can handle anything other
|
|
195
|
-
# than UTC
|
|
196
|
-
self.query.time_range(
|
|
197
|
-
self.start.tz_convert('UTC'),
|
|
198
|
-
self.end.tz_convert('UTC')
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
def set_query_latlon(self):
|
|
202
|
-
'''
|
|
203
|
-
Sets the NCSS query location latitude and longitude.
|
|
204
|
-
'''
|
|
205
|
-
|
|
206
|
-
if (isinstance(self.longitude, list) and
|
|
207
|
-
isinstance(self.latitude, list)):
|
|
208
|
-
self.lbox = True
|
|
209
|
-
# west, east, south, north
|
|
210
|
-
self.query.lonlat_box(self.longitude[0], self.longitude[1],
|
|
211
|
-
self.latitude[0], self.latitude[1])
|
|
212
|
-
else:
|
|
213
|
-
self.lbox = False
|
|
214
|
-
self.query.lonlat_point(self.longitude, self.latitude)
|
|
215
|
-
|
|
216
|
-
def set_location(self, tz, latitude, longitude):
|
|
217
|
-
'''
|
|
218
|
-
Sets the location for the query.
|
|
219
|
-
|
|
220
|
-
Parameters
|
|
221
|
-
----------
|
|
222
|
-
tz: tzinfo
|
|
223
|
-
Timezone of the query
|
|
224
|
-
latitude: float
|
|
225
|
-
Latitude of the query
|
|
226
|
-
longitude: float
|
|
227
|
-
Longitude of the query
|
|
228
|
-
|
|
229
|
-
Notes
|
|
230
|
-
-----
|
|
231
|
-
Assigns ``self.location``.
|
|
232
|
-
'''
|
|
233
|
-
self.location = Location(latitude, longitude, tz=tz)
|
|
234
|
-
|
|
235
|
-
def get_data(self, latitude, longitude, start, end,
|
|
236
|
-
vert_level=None, query_variables=None,
|
|
237
|
-
close_netcdf_data=True, **kwargs):
|
|
238
|
-
"""
|
|
239
|
-
Submits a query to the UNIDATA servers using Siphon NCSS and
|
|
240
|
-
converts the netcdf data to a pandas DataFrame.
|
|
241
|
-
|
|
242
|
-
Parameters
|
|
243
|
-
----------
|
|
244
|
-
latitude: float
|
|
245
|
-
The latitude value.
|
|
246
|
-
longitude: float
|
|
247
|
-
The longitude value.
|
|
248
|
-
start: datetime or timestamp
|
|
249
|
-
The start time.
|
|
250
|
-
end: datetime or timestamp
|
|
251
|
-
The end time.
|
|
252
|
-
vert_level: None, float or integer, default None
|
|
253
|
-
Vertical altitude of interest.
|
|
254
|
-
query_variables: None or list, default None
|
|
255
|
-
If None, uses self.variables.
|
|
256
|
-
close_netcdf_data: bool, default True
|
|
257
|
-
Controls if the temporary netcdf data file should be closed.
|
|
258
|
-
Set to False to access the raw data.
|
|
259
|
-
**kwargs:
|
|
260
|
-
Additional keyword arguments are silently ignored.
|
|
261
|
-
|
|
262
|
-
Returns
|
|
263
|
-
-------
|
|
264
|
-
forecast_data : DataFrame
|
|
265
|
-
column names are the weather model's variable names.
|
|
266
|
-
"""
|
|
267
|
-
|
|
268
|
-
if not self.connected:
|
|
269
|
-
self.connect_to_catalog()
|
|
270
|
-
|
|
271
|
-
if vert_level is not None:
|
|
272
|
-
self.vert_level = vert_level
|
|
273
|
-
|
|
274
|
-
if query_variables is None:
|
|
275
|
-
self.query_variables = list(self.variables.values())
|
|
276
|
-
else:
|
|
277
|
-
self.query_variables = query_variables
|
|
278
|
-
|
|
279
|
-
self.set_query_time_range(start, end)
|
|
280
|
-
|
|
281
|
-
self.latitude = latitude
|
|
282
|
-
self.longitude = longitude
|
|
283
|
-
self.set_query_latlon() # modifies self.query
|
|
284
|
-
self.set_location(self.start.tz, latitude, longitude)
|
|
285
|
-
|
|
286
|
-
if self.vert_level is not None:
|
|
287
|
-
self.query.vertical_level(self.vert_level)
|
|
288
|
-
|
|
289
|
-
self.query.variables(*self.query_variables)
|
|
290
|
-
self.query.accept(self.data_format)
|
|
291
|
-
|
|
292
|
-
self.netcdf_data = self.ncss.get_data(self.query)
|
|
293
|
-
|
|
294
|
-
# might be better to go to xarray here so that we can handle
|
|
295
|
-
# higher dimensional data for more advanced applications
|
|
296
|
-
self.data = self._netcdf2pandas(self.netcdf_data, self.query_variables,
|
|
297
|
-
self.start, self.end)
|
|
298
|
-
|
|
299
|
-
if close_netcdf_data:
|
|
300
|
-
self.netcdf_data.close()
|
|
301
|
-
|
|
302
|
-
return self.data
|
|
303
|
-
|
|
304
|
-
def process_data(self, data, **kwargs):
|
|
305
|
-
"""
|
|
306
|
-
Defines the steps needed to convert raw forecast data
|
|
307
|
-
into processed forecast data. Most forecast models implement
|
|
308
|
-
their own version of this method which also call this one.
|
|
309
|
-
|
|
310
|
-
Parameters
|
|
311
|
-
----------
|
|
312
|
-
data: DataFrame
|
|
313
|
-
Raw forecast data
|
|
314
|
-
|
|
315
|
-
Returns
|
|
316
|
-
-------
|
|
317
|
-
data: DataFrame
|
|
318
|
-
Processed forecast data.
|
|
319
|
-
"""
|
|
320
|
-
data = self.rename(data)
|
|
321
|
-
return data
|
|
322
|
-
|
|
323
|
-
def get_processed_data(self, *args, **kwargs):
|
|
324
|
-
"""
|
|
325
|
-
Get and process forecast data.
|
|
326
|
-
|
|
327
|
-
Parameters
|
|
328
|
-
----------
|
|
329
|
-
*args: positional arguments
|
|
330
|
-
Passed to get_data
|
|
331
|
-
**kwargs: keyword arguments
|
|
332
|
-
Passed to get_data and process_data
|
|
333
|
-
|
|
334
|
-
Returns
|
|
335
|
-
-------
|
|
336
|
-
data: DataFrame
|
|
337
|
-
Processed forecast data
|
|
338
|
-
"""
|
|
339
|
-
return self.process_data(self.get_data(*args, **kwargs), **kwargs)
|
|
340
|
-
|
|
341
|
-
def rename(self, data, variables=None):
|
|
342
|
-
"""
|
|
343
|
-
Renames the columns according the variable mapping.
|
|
344
|
-
|
|
345
|
-
Parameters
|
|
346
|
-
----------
|
|
347
|
-
data: DataFrame
|
|
348
|
-
variables: None or dict, default None
|
|
349
|
-
If None, uses self.variables
|
|
350
|
-
|
|
351
|
-
Returns
|
|
352
|
-
-------
|
|
353
|
-
data: DataFrame
|
|
354
|
-
Renamed data.
|
|
355
|
-
"""
|
|
356
|
-
if variables is None:
|
|
357
|
-
variables = self.variables
|
|
358
|
-
return data.rename(columns={y: x for x, y in variables.items()})
|
|
359
|
-
|
|
360
|
-
def _netcdf2pandas(self, netcdf_data, query_variables, start, end):
|
|
361
|
-
"""
|
|
362
|
-
Transforms data from netcdf to pandas DataFrame.
|
|
363
|
-
|
|
364
|
-
Parameters
|
|
365
|
-
----------
|
|
366
|
-
data: netcdf
|
|
367
|
-
Data returned from UNIDATA NCSS query.
|
|
368
|
-
query_variables: list
|
|
369
|
-
The variables requested.
|
|
370
|
-
start: Timestamp
|
|
371
|
-
The start time
|
|
372
|
-
end: Timestamp
|
|
373
|
-
The end time
|
|
374
|
-
|
|
375
|
-
Returns
|
|
376
|
-
-------
|
|
377
|
-
pd.DataFrame
|
|
378
|
-
"""
|
|
379
|
-
# set self.time
|
|
380
|
-
try:
|
|
381
|
-
time_var = 'time'
|
|
382
|
-
self.set_time(netcdf_data.variables[time_var])
|
|
383
|
-
except KeyError:
|
|
384
|
-
# which model does this dumb thing?
|
|
385
|
-
time_var = 'time1'
|
|
386
|
-
self.set_time(netcdf_data.variables[time_var])
|
|
387
|
-
|
|
388
|
-
data_dict = {}
|
|
389
|
-
for key, data in netcdf_data.variables.items():
|
|
390
|
-
# if accounts for possibility of extra variable returned
|
|
391
|
-
if key not in query_variables:
|
|
392
|
-
continue
|
|
393
|
-
squeezed = data[:].squeeze()
|
|
394
|
-
|
|
395
|
-
# If the data is big endian, swap the byte order to make it
|
|
396
|
-
# little endian
|
|
397
|
-
if squeezed.dtype.byteorder == '>':
|
|
398
|
-
squeezed = squeezed.byteswap().newbyteorder()
|
|
399
|
-
if squeezed.ndim == 1:
|
|
400
|
-
data_dict[key] = squeezed
|
|
401
|
-
elif squeezed.ndim == 2:
|
|
402
|
-
for num, data_level in enumerate(squeezed.T):
|
|
403
|
-
data_dict[key + '_' + str(num)] = data_level
|
|
404
|
-
else:
|
|
405
|
-
raise ValueError('cannot parse ndim > 2')
|
|
406
|
-
|
|
407
|
-
data = pd.DataFrame(data_dict, index=self.time)
|
|
408
|
-
# sometimes data is returned as hours since T0
|
|
409
|
-
# where T0 is before start. Then the hours between
|
|
410
|
-
# T0 and start are added *after* end. So sort and slice
|
|
411
|
-
# to remove the garbage
|
|
412
|
-
data = data.sort_index().loc[start:end]
|
|
413
|
-
return data
|
|
414
|
-
|
|
415
|
-
def set_time(self, time):
|
|
416
|
-
'''
|
|
417
|
-
Converts time data into a pandas date object.
|
|
418
|
-
|
|
419
|
-
Parameters
|
|
420
|
-
----------
|
|
421
|
-
time: netcdf
|
|
422
|
-
Contains time information.
|
|
423
|
-
|
|
424
|
-
Returns
|
|
425
|
-
-------
|
|
426
|
-
pandas.DatetimeIndex
|
|
427
|
-
'''
|
|
428
|
-
# np.masked_array with elements like real_datetime(2021, 8, 17, 16, 0)
|
|
429
|
-
# and dtype=object
|
|
430
|
-
times = num2date(time[:].squeeze(), time.units,
|
|
431
|
-
only_use_cftime_datetimes=False,
|
|
432
|
-
only_use_python_datetimes=True)
|
|
433
|
-
# convert to pandas, localize to UTC, convert to desired timezone
|
|
434
|
-
self.time = pd.DatetimeIndex(
|
|
435
|
-
times, tz='UTC').tz_convert(self.location.tz)
|
|
436
|
-
|
|
437
|
-
def cloud_cover_to_ghi_linear(self, cloud_cover, ghi_clear, offset=35,
|
|
438
|
-
**kwargs):
|
|
439
|
-
"""
|
|
440
|
-
Convert cloud cover to GHI using a linear relationship.
|
|
441
|
-
|
|
442
|
-
0% cloud cover returns ghi_clear.
|
|
443
|
-
|
|
444
|
-
100% cloud cover returns offset*ghi_clear.
|
|
445
|
-
|
|
446
|
-
Parameters
|
|
447
|
-
----------
|
|
448
|
-
cloud_cover: numeric
|
|
449
|
-
Cloud cover in %.
|
|
450
|
-
ghi_clear: numeric
|
|
451
|
-
GHI under clear sky conditions.
|
|
452
|
-
offset: numeric, default 35
|
|
453
|
-
Determines the minimum GHI.
|
|
454
|
-
kwargs
|
|
455
|
-
Not used.
|
|
456
|
-
|
|
457
|
-
Returns
|
|
458
|
-
-------
|
|
459
|
-
ghi: numeric
|
|
460
|
-
Estimated GHI.
|
|
461
|
-
|
|
462
|
-
References
|
|
463
|
-
----------
|
|
464
|
-
Larson et. al. "Day-ahead forecasting of solar power output from
|
|
465
|
-
photovoltaic plants in the American Southwest" Renewable Energy
|
|
466
|
-
91, 11-20 (2016).
|
|
467
|
-
"""
|
|
468
|
-
|
|
469
|
-
offset = offset / 100.
|
|
470
|
-
cloud_cover = cloud_cover / 100.
|
|
471
|
-
ghi = (offset + (1 - offset) * (1 - cloud_cover)) * ghi_clear
|
|
472
|
-
return ghi
|
|
473
|
-
|
|
474
|
-
def cloud_cover_to_irradiance_clearsky_scaling(self, cloud_cover,
|
|
475
|
-
method='linear',
|
|
476
|
-
**kwargs):
|
|
477
|
-
"""
|
|
478
|
-
Estimates irradiance from cloud cover in the following steps:
|
|
479
|
-
|
|
480
|
-
1. Determine clear sky GHI using Ineichen model and
|
|
481
|
-
climatological turbidity.
|
|
482
|
-
2. Estimate cloudy sky GHI using a function of
|
|
483
|
-
cloud_cover e.g.
|
|
484
|
-
:py:meth:`~ForecastModel.cloud_cover_to_ghi_linear`
|
|
485
|
-
3. Estimate cloudy sky DNI using the DISC model.
|
|
486
|
-
4. Calculate DHI from DNI and GHI.
|
|
487
|
-
|
|
488
|
-
Parameters
|
|
489
|
-
----------
|
|
490
|
-
cloud_cover : Series
|
|
491
|
-
Cloud cover in %.
|
|
492
|
-
method : str, default 'linear'
|
|
493
|
-
Method for converting cloud cover to GHI.
|
|
494
|
-
'linear' is currently the only option.
|
|
495
|
-
**kwargs
|
|
496
|
-
Passed to the method that does the conversion
|
|
497
|
-
|
|
498
|
-
Returns
|
|
499
|
-
-------
|
|
500
|
-
irrads : DataFrame
|
|
501
|
-
Estimated GHI, DNI, and DHI.
|
|
502
|
-
"""
|
|
503
|
-
solpos = self.location.get_solarposition(cloud_cover.index)
|
|
504
|
-
cs = self.location.get_clearsky(cloud_cover.index, model='ineichen',
|
|
505
|
-
solar_position=solpos)
|
|
506
|
-
|
|
507
|
-
method = method.lower()
|
|
508
|
-
if method == 'linear':
|
|
509
|
-
ghi = self.cloud_cover_to_ghi_linear(cloud_cover, cs['ghi'],
|
|
510
|
-
**kwargs)
|
|
511
|
-
else:
|
|
512
|
-
raise ValueError('invalid method argument')
|
|
513
|
-
|
|
514
|
-
dni = disc(ghi, solpos['zenith'], cloud_cover.index)['dni']
|
|
515
|
-
dhi = ghi - dni * np.cos(np.radians(solpos['zenith']))
|
|
516
|
-
|
|
517
|
-
irrads = pd.DataFrame({'ghi': ghi, 'dni': dni, 'dhi': dhi}).fillna(0)
|
|
518
|
-
return irrads
|
|
519
|
-
|
|
520
|
-
def cloud_cover_to_transmittance_linear(self, cloud_cover, offset=0.75,
|
|
521
|
-
**kwargs):
|
|
522
|
-
"""
|
|
523
|
-
Convert cloud cover (percentage) to atmospheric transmittance
|
|
524
|
-
using a linear model.
|
|
525
|
-
|
|
526
|
-
0% cloud cover returns "offset".
|
|
527
|
-
|
|
528
|
-
100% cloud cover returns 0.
|
|
529
|
-
|
|
530
|
-
Parameters
|
|
531
|
-
----------
|
|
532
|
-
cloud_cover : numeric
|
|
533
|
-
Cloud cover in %.
|
|
534
|
-
offset : numeric, default 0.75
|
|
535
|
-
Determines the maximum transmittance. [unitless]
|
|
536
|
-
kwargs
|
|
537
|
-
Not used.
|
|
538
|
-
|
|
539
|
-
Returns
|
|
540
|
-
-------
|
|
541
|
-
transmittance : numeric
|
|
542
|
-
The fraction of extraterrestrial irradiance that reaches
|
|
543
|
-
the ground. [unitless]
|
|
544
|
-
"""
|
|
545
|
-
transmittance = ((100.0 - cloud_cover) / 100.0) * offset
|
|
546
|
-
|
|
547
|
-
return transmittance
|
|
548
|
-
|
|
549
|
-
def cloud_cover_to_irradiance_campbell_norman(self, cloud_cover, **kwargs):
|
|
550
|
-
"""
|
|
551
|
-
Estimates irradiance from cloud cover in the following steps:
|
|
552
|
-
|
|
553
|
-
1. Determine transmittance using a function of cloud cover e.g.
|
|
554
|
-
:py:meth:`~ForecastModel.cloud_cover_to_transmittance_linear`
|
|
555
|
-
2. Calculate GHI, DNI, DHI using the
|
|
556
|
-
:py:func:`pvlib.irradiance.campbell_norman` model
|
|
557
|
-
|
|
558
|
-
Parameters
|
|
559
|
-
----------
|
|
560
|
-
cloud_cover : Series
|
|
561
|
-
|
|
562
|
-
Returns
|
|
563
|
-
-------
|
|
564
|
-
irradiance : DataFrame
|
|
565
|
-
Columns include ghi, dni, dhi
|
|
566
|
-
"""
|
|
567
|
-
# in principle, get_solarposition could use the forecast
|
|
568
|
-
# pressure, temp, etc., but the cloud cover forecast is not
|
|
569
|
-
# accurate enough to justify using these minor corrections
|
|
570
|
-
solar_position = self.location.get_solarposition(cloud_cover.index)
|
|
571
|
-
dni_extra = get_extra_radiation(cloud_cover.index)
|
|
572
|
-
|
|
573
|
-
transmittance = self.cloud_cover_to_transmittance_linear(cloud_cover,
|
|
574
|
-
**kwargs)
|
|
575
|
-
|
|
576
|
-
irrads = campbell_norman(solar_position['apparent_zenith'],
|
|
577
|
-
transmittance, dni_extra=dni_extra)
|
|
578
|
-
irrads = irrads.fillna(0)
|
|
579
|
-
|
|
580
|
-
return irrads
|
|
581
|
-
|
|
582
|
-
def cloud_cover_to_irradiance(self, cloud_cover, how='clearsky_scaling',
|
|
583
|
-
**kwargs):
|
|
584
|
-
"""
|
|
585
|
-
Convert cloud cover to irradiance. A wrapper method.
|
|
586
|
-
|
|
587
|
-
Parameters
|
|
588
|
-
----------
|
|
589
|
-
cloud_cover : Series
|
|
590
|
-
how : str, default 'clearsky_scaling'
|
|
591
|
-
Selects the method for conversion. Can be one of
|
|
592
|
-
clearsky_scaling or campbell_norman. Method liujordan is
|
|
593
|
-
deprecated.
|
|
594
|
-
**kwargs
|
|
595
|
-
Passed to the selected method.
|
|
596
|
-
|
|
597
|
-
Returns
|
|
598
|
-
-------
|
|
599
|
-
irradiance : DataFrame
|
|
600
|
-
Columns include ghi, dni, dhi
|
|
601
|
-
"""
|
|
602
|
-
|
|
603
|
-
how = how.lower()
|
|
604
|
-
if how == 'clearsky_scaling':
|
|
605
|
-
irrads = self.cloud_cover_to_irradiance_clearsky_scaling(
|
|
606
|
-
cloud_cover, **kwargs)
|
|
607
|
-
elif how == 'campbell_norman':
|
|
608
|
-
irrads = self.cloud_cover_to_irradiance_campbell_norman(
|
|
609
|
-
cloud_cover, **kwargs)
|
|
610
|
-
else:
|
|
611
|
-
raise ValueError('invalid how argument')
|
|
612
|
-
|
|
613
|
-
return irrads
|
|
614
|
-
|
|
615
|
-
def kelvin_to_celsius(self, temperature):
|
|
616
|
-
"""
|
|
617
|
-
Converts Kelvin to celsius.
|
|
618
|
-
|
|
619
|
-
Parameters
|
|
620
|
-
----------
|
|
621
|
-
temperature: numeric
|
|
622
|
-
|
|
623
|
-
Returns
|
|
624
|
-
-------
|
|
625
|
-
temperature: numeric
|
|
626
|
-
"""
|
|
627
|
-
return temperature - 273.15
|
|
628
|
-
|
|
629
|
-
def isobaric_to_ambient_temperature(self, data):
|
|
630
|
-
"""
|
|
631
|
-
Calculates temperature from isobaric temperature.
|
|
632
|
-
|
|
633
|
-
Parameters
|
|
634
|
-
----------
|
|
635
|
-
data: DataFrame
|
|
636
|
-
Must contain columns pressure, temperature_iso,
|
|
637
|
-
temperature_dew_iso. Input temperature in K.
|
|
638
|
-
|
|
639
|
-
Returns
|
|
640
|
-
-------
|
|
641
|
-
temperature : Series
|
|
642
|
-
Temperature in K
|
|
643
|
-
"""
|
|
644
|
-
|
|
645
|
-
P = data['pressure'] / 100.0 # noqa: N806
|
|
646
|
-
Tiso = data['temperature_iso'] # noqa: N806
|
|
647
|
-
Td = data['temperature_dew_iso'] - 273.15 # noqa: N806
|
|
648
|
-
|
|
649
|
-
# saturation water vapor pressure
|
|
650
|
-
e = 6.11 * 10**((7.5 * Td) / (Td + 273.3))
|
|
651
|
-
|
|
652
|
-
# saturation water vapor mixing ratio
|
|
653
|
-
w = 0.622 * (e / (P - e))
|
|
654
|
-
|
|
655
|
-
temperature = Tiso - ((2.501 * 10.**6) / 1005.7) * w
|
|
656
|
-
|
|
657
|
-
return temperature
|
|
658
|
-
|
|
659
|
-
def uv_to_speed(self, data):
|
|
660
|
-
"""
|
|
661
|
-
Computes wind speed from wind components.
|
|
662
|
-
|
|
663
|
-
Parameters
|
|
664
|
-
----------
|
|
665
|
-
data : DataFrame
|
|
666
|
-
Must contain the columns 'wind_speed_u' and 'wind_speed_v'.
|
|
667
|
-
|
|
668
|
-
Returns
|
|
669
|
-
-------
|
|
670
|
-
wind_speed : Series
|
|
671
|
-
"""
|
|
672
|
-
wind_speed = np.sqrt(data['wind_speed_u']**2 + data['wind_speed_v']**2)
|
|
673
|
-
|
|
674
|
-
return wind_speed
|
|
675
|
-
|
|
676
|
-
def gust_to_speed(self, data, scaling=1/1.4):
|
|
677
|
-
"""
|
|
678
|
-
Computes standard wind speed from gust.
|
|
679
|
-
Very approximate and location dependent.
|
|
680
|
-
|
|
681
|
-
Parameters
|
|
682
|
-
----------
|
|
683
|
-
data : DataFrame
|
|
684
|
-
Must contain the column 'wind_speed_gust'.
|
|
685
|
-
|
|
686
|
-
Returns
|
|
687
|
-
-------
|
|
688
|
-
wind_speed : Series
|
|
689
|
-
"""
|
|
690
|
-
wind_speed = data['wind_speed_gust'] * scaling
|
|
691
|
-
|
|
692
|
-
return wind_speed
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
@_forecast_deprecated
|
|
696
|
-
class GFS(ForecastModel):
|
|
697
|
-
"""
|
|
698
|
-
Subclass of the ForecastModel class representing GFS
|
|
699
|
-
forecast model.
|
|
700
|
-
|
|
701
|
-
Model data corresponds to 0.25 degree resolution forecasts.
|
|
702
|
-
|
|
703
|
-
Parameters
|
|
704
|
-
----------
|
|
705
|
-
resolution: string, default 'half'
|
|
706
|
-
Resolution of the model, either 'half' or 'quarter' degree.
|
|
707
|
-
set_type: string, default 'best'
|
|
708
|
-
Type of model to pull data from.
|
|
709
|
-
|
|
710
|
-
Attributes
|
|
711
|
-
----------
|
|
712
|
-
dataframe_variables: list
|
|
713
|
-
Common variables present in the final set of data.
|
|
714
|
-
model: string
|
|
715
|
-
Name of the UNIDATA forecast model.
|
|
716
|
-
model_type: string
|
|
717
|
-
UNIDATA category in which the model is located.
|
|
718
|
-
variables: dict
|
|
719
|
-
Defines the variables to obtain from the weather
|
|
720
|
-
model and how they should be renamed to common variable names.
|
|
721
|
-
units: dict
|
|
722
|
-
Dictionary containing the units of the standard variables
|
|
723
|
-
and the model specific variables.
|
|
724
|
-
"""
|
|
725
|
-
|
|
726
|
-
_resolutions = ['Half', 'Quarter']
|
|
727
|
-
|
|
728
|
-
def __init__(self, resolution='half', set_type='best'):
|
|
729
|
-
model_type = 'Forecast Model Data'
|
|
730
|
-
|
|
731
|
-
resolution = resolution.title()
|
|
732
|
-
if resolution not in self._resolutions:
|
|
733
|
-
raise ValueError(f'resolution must in {self._resolutions}')
|
|
734
|
-
|
|
735
|
-
model = f'GFS {resolution} Degree Forecast'
|
|
736
|
-
|
|
737
|
-
# isobaric variables will require a vert_level to prevent
|
|
738
|
-
# excessive data downloads
|
|
739
|
-
self.variables = {
|
|
740
|
-
'temp_air': 'Temperature_surface',
|
|
741
|
-
'wind_speed_gust': 'Wind_speed_gust_surface',
|
|
742
|
-
'wind_speed_u': 'u-component_of_wind_isobaric',
|
|
743
|
-
'wind_speed_v': 'v-component_of_wind_isobaric',
|
|
744
|
-
'total_clouds':
|
|
745
|
-
'Total_cloud_cover_entire_atmosphere_Mixed_intervals_Average',
|
|
746
|
-
'low_clouds':
|
|
747
|
-
'Low_cloud_cover_low_cloud_Mixed_intervals_Average',
|
|
748
|
-
'mid_clouds':
|
|
749
|
-
'Medium_cloud_cover_middle_cloud_Mixed_intervals_Average',
|
|
750
|
-
'high_clouds':
|
|
751
|
-
'High_cloud_cover_high_cloud_Mixed_intervals_Average',
|
|
752
|
-
'boundary_clouds': ('Total_cloud_cover_boundary_layer_cloud_'
|
|
753
|
-
'Mixed_intervals_Average'),
|
|
754
|
-
'convect_clouds': 'Total_cloud_cover_convective_cloud',
|
|
755
|
-
'ghi_raw': ('Downward_Short-Wave_Radiation_Flux_'
|
|
756
|
-
'surface_Mixed_intervals_Average')}
|
|
757
|
-
|
|
758
|
-
self.output_variables = [
|
|
759
|
-
'temp_air',
|
|
760
|
-
'wind_speed',
|
|
761
|
-
'ghi',
|
|
762
|
-
'dni',
|
|
763
|
-
'dhi',
|
|
764
|
-
'total_clouds',
|
|
765
|
-
'low_clouds',
|
|
766
|
-
'mid_clouds',
|
|
767
|
-
'high_clouds']
|
|
768
|
-
|
|
769
|
-
super().__init__(model_type, model, set_type,
|
|
770
|
-
vert_level=100000)
|
|
771
|
-
|
|
772
|
-
def process_data(self, data, cloud_cover='total_clouds', **kwargs):
|
|
773
|
-
"""
|
|
774
|
-
Defines the steps needed to convert raw forecast data
|
|
775
|
-
into processed forecast data.
|
|
776
|
-
|
|
777
|
-
Parameters
|
|
778
|
-
----------
|
|
779
|
-
data: DataFrame
|
|
780
|
-
Raw forecast data
|
|
781
|
-
cloud_cover: str, default 'total_clouds'
|
|
782
|
-
The type of cloud cover used to infer the irradiance.
|
|
783
|
-
|
|
784
|
-
Returns
|
|
785
|
-
-------
|
|
786
|
-
data: DataFrame
|
|
787
|
-
Processed forecast data.
|
|
788
|
-
"""
|
|
789
|
-
data = super().process_data(data, **kwargs)
|
|
790
|
-
data['temp_air'] = self.kelvin_to_celsius(data['temp_air'])
|
|
791
|
-
data['wind_speed'] = self.uv_to_speed(data)
|
|
792
|
-
irrads = self.cloud_cover_to_irradiance(data[cloud_cover], **kwargs)
|
|
793
|
-
data = data.join(irrads, how='outer')
|
|
794
|
-
return data[self.output_variables]
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
@_forecast_deprecated
|
|
798
|
-
class HRRR_ESRL(ForecastModel): # noqa: N801
|
|
799
|
-
"""
|
|
800
|
-
Subclass of the ForecastModel class representing
|
|
801
|
-
NOAA/GSD/ESRL's HRRR forecast model.
|
|
802
|
-
This is not an operational product.
|
|
803
|
-
|
|
804
|
-
Model data corresponds to NOAA/GSD/ESRL HRRR CONUS 3km resolution
|
|
805
|
-
surface forecasts.
|
|
806
|
-
|
|
807
|
-
Parameters
|
|
808
|
-
----------
|
|
809
|
-
set_type: string, default 'best'
|
|
810
|
-
Type of model to pull data from.
|
|
811
|
-
|
|
812
|
-
Attributes
|
|
813
|
-
----------
|
|
814
|
-
dataframe_variables: list
|
|
815
|
-
Common variables present in the final set of data.
|
|
816
|
-
model: string
|
|
817
|
-
Name of the UNIDATA forecast model.
|
|
818
|
-
model_type: string
|
|
819
|
-
UNIDATA category in which the model is located.
|
|
820
|
-
variables: dict
|
|
821
|
-
Defines the variables to obtain from the weather
|
|
822
|
-
model and how they should be renamed to common variable names.
|
|
823
|
-
units: dict
|
|
824
|
-
Dictionary containing the units of the standard variables
|
|
825
|
-
and the model specific variables.
|
|
826
|
-
"""
|
|
827
|
-
|
|
828
|
-
def __init__(self, set_type='best'):
|
|
829
|
-
warnings.warn('HRRR_ESRL is an experimental model and is not '
|
|
830
|
-
'always available.')
|
|
831
|
-
|
|
832
|
-
model_type = 'Forecast Model Data'
|
|
833
|
-
model = 'GSD HRRR CONUS 3km surface'
|
|
834
|
-
|
|
835
|
-
self.variables = {
|
|
836
|
-
'temp_air': 'Temperature_surface',
|
|
837
|
-
'wind_speed_gust': 'Wind_speed_gust_surface',
|
|
838
|
-
# 'temp_air': 'Temperature_height_above_ground', # GH 702
|
|
839
|
-
# 'wind_speed_u': 'u-component_of_wind_height_above_ground',
|
|
840
|
-
# 'wind_speed_v': 'v-component_of_wind_height_above_ground',
|
|
841
|
-
'total_clouds': 'Total_cloud_cover_entire_atmosphere',
|
|
842
|
-
'low_clouds': 'Low_cloud_cover_UnknownLevelType-214',
|
|
843
|
-
'mid_clouds': 'Medium_cloud_cover_UnknownLevelType-224',
|
|
844
|
-
'high_clouds': 'High_cloud_cover_UnknownLevelType-234',
|
|
845
|
-
'ghi_raw': 'Downward_short-wave_radiation_flux_surface', }
|
|
846
|
-
|
|
847
|
-
self.output_variables = [
|
|
848
|
-
'temp_air',
|
|
849
|
-
'wind_speed',
|
|
850
|
-
'ghi_raw',
|
|
851
|
-
'ghi',
|
|
852
|
-
'dni',
|
|
853
|
-
'dhi',
|
|
854
|
-
'total_clouds',
|
|
855
|
-
'low_clouds',
|
|
856
|
-
'mid_clouds',
|
|
857
|
-
'high_clouds']
|
|
858
|
-
|
|
859
|
-
super().__init__(model_type, model, set_type)
|
|
860
|
-
|
|
861
|
-
def process_data(self, data, cloud_cover='total_clouds', **kwargs):
|
|
862
|
-
"""
|
|
863
|
-
Defines the steps needed to convert raw forecast data
|
|
864
|
-
into processed forecast data.
|
|
865
|
-
|
|
866
|
-
Parameters
|
|
867
|
-
----------
|
|
868
|
-
data: DataFrame
|
|
869
|
-
Raw forecast data
|
|
870
|
-
cloud_cover: str, default 'total_clouds'
|
|
871
|
-
The type of cloud cover used to infer the irradiance.
|
|
872
|
-
|
|
873
|
-
Returns
|
|
874
|
-
-------
|
|
875
|
-
data: DataFrame
|
|
876
|
-
Processed forecast data.
|
|
877
|
-
"""
|
|
878
|
-
|
|
879
|
-
data = super().process_data(data, **kwargs)
|
|
880
|
-
data['temp_air'] = self.kelvin_to_celsius(data['temp_air'])
|
|
881
|
-
data['wind_speed'] = self.gust_to_speed(data)
|
|
882
|
-
# data['wind_speed'] = self.uv_to_speed(data) # GH 702
|
|
883
|
-
irrads = self.cloud_cover_to_irradiance(data[cloud_cover], **kwargs)
|
|
884
|
-
data = data.join(irrads, how='outer')
|
|
885
|
-
return data[self.output_variables]
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
@_forecast_deprecated
|
|
889
|
-
class NAM(ForecastModel):
|
|
890
|
-
"""
|
|
891
|
-
Subclass of the ForecastModel class representing NAM
|
|
892
|
-
forecast model.
|
|
893
|
-
|
|
894
|
-
Model data corresponds to NAM CONUS 12km resolution forecasts
|
|
895
|
-
from CONDUIT.
|
|
896
|
-
|
|
897
|
-
Parameters
|
|
898
|
-
----------
|
|
899
|
-
set_type: string, default 'best'
|
|
900
|
-
Type of model to pull data from.
|
|
901
|
-
|
|
902
|
-
Attributes
|
|
903
|
-
----------
|
|
904
|
-
dataframe_variables: list
|
|
905
|
-
Common variables present in the final set of data.
|
|
906
|
-
model: string
|
|
907
|
-
Name of the UNIDATA forecast model.
|
|
908
|
-
model_type: string
|
|
909
|
-
UNIDATA category in which the model is located.
|
|
910
|
-
variables: dict
|
|
911
|
-
Defines the variables to obtain from the weather
|
|
912
|
-
model and how they should be renamed to common variable names.
|
|
913
|
-
units: dict
|
|
914
|
-
Dictionary containing the units of the standard variables
|
|
915
|
-
and the model specific variables.
|
|
916
|
-
"""
|
|
917
|
-
|
|
918
|
-
def __init__(self, set_type='best'):
|
|
919
|
-
model_type = 'Forecast Model Data'
|
|
920
|
-
model = 'NAM CONUS 12km from CONDUIT'
|
|
921
|
-
|
|
922
|
-
self.variables = {
|
|
923
|
-
'temp_air': 'Temperature_surface',
|
|
924
|
-
'wind_speed_gust': 'Wind_speed_gust_surface',
|
|
925
|
-
'total_clouds': 'Total_cloud_cover_entire_atmosphere_single_layer',
|
|
926
|
-
'low_clouds': 'Low_cloud_cover_low_cloud',
|
|
927
|
-
'mid_clouds': 'Medium_cloud_cover_middle_cloud',
|
|
928
|
-
'high_clouds': 'High_cloud_cover_high_cloud',
|
|
929
|
-
'ghi_raw': 'Downward_Short-Wave_Radiation_Flux_surface', }
|
|
930
|
-
|
|
931
|
-
self.output_variables = [
|
|
932
|
-
'temp_air',
|
|
933
|
-
'wind_speed',
|
|
934
|
-
'ghi',
|
|
935
|
-
'dni',
|
|
936
|
-
'dhi',
|
|
937
|
-
'total_clouds',
|
|
938
|
-
'low_clouds',
|
|
939
|
-
'mid_clouds',
|
|
940
|
-
'high_clouds']
|
|
941
|
-
|
|
942
|
-
super().__init__(model_type, model, set_type)
|
|
943
|
-
|
|
944
|
-
def process_data(self, data, cloud_cover='total_clouds', **kwargs):
|
|
945
|
-
"""
|
|
946
|
-
Defines the steps needed to convert raw forecast data
|
|
947
|
-
into processed forecast data.
|
|
948
|
-
|
|
949
|
-
Parameters
|
|
950
|
-
----------
|
|
951
|
-
data: DataFrame
|
|
952
|
-
Raw forecast data
|
|
953
|
-
cloud_cover: str, default 'total_clouds'
|
|
954
|
-
The type of cloud cover used to infer the irradiance.
|
|
955
|
-
|
|
956
|
-
Returns
|
|
957
|
-
-------
|
|
958
|
-
data: DataFrame
|
|
959
|
-
Processed forecast data.
|
|
960
|
-
"""
|
|
961
|
-
|
|
962
|
-
data = super().process_data(data, **kwargs)
|
|
963
|
-
data['temp_air'] = self.kelvin_to_celsius(data['temp_air'])
|
|
964
|
-
data['wind_speed'] = self.gust_to_speed(data)
|
|
965
|
-
irrads = self.cloud_cover_to_irradiance(data[cloud_cover], **kwargs)
|
|
966
|
-
data = data.join(irrads, how='outer')
|
|
967
|
-
return data[self.output_variables]
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
@_forecast_deprecated
|
|
971
|
-
class HRRR(ForecastModel):
|
|
972
|
-
"""
|
|
973
|
-
Subclass of the ForecastModel class representing HRRR
|
|
974
|
-
forecast model.
|
|
975
|
-
|
|
976
|
-
Model data corresponds to NCEP HRRR CONUS 2.5km resolution
|
|
977
|
-
forecasts.
|
|
978
|
-
|
|
979
|
-
Parameters
|
|
980
|
-
----------
|
|
981
|
-
set_type: string, default 'best'
|
|
982
|
-
Type of model to pull data from.
|
|
983
|
-
|
|
984
|
-
Attributes
|
|
985
|
-
----------
|
|
986
|
-
dataframe_variables: list
|
|
987
|
-
Common variables present in the final set of data.
|
|
988
|
-
model: string
|
|
989
|
-
Name of the UNIDATA forecast model.
|
|
990
|
-
model_type: string
|
|
991
|
-
UNIDATA category in which the model is located.
|
|
992
|
-
variables: dict
|
|
993
|
-
Defines the variables to obtain from the weather
|
|
994
|
-
model and how they should be renamed to common variable names.
|
|
995
|
-
units: dict
|
|
996
|
-
Dictionary containing the units of the standard variables
|
|
997
|
-
and the model specific variables.
|
|
998
|
-
"""
|
|
999
|
-
|
|
1000
|
-
def __init__(self, set_type='best'):
|
|
1001
|
-
model_type = 'Forecast Model Data'
|
|
1002
|
-
model = 'HRRR CONUS 2.5km Forecasts'
|
|
1003
|
-
|
|
1004
|
-
self.variables = {
|
|
1005
|
-
'temp_air': 'Temperature_height_above_ground',
|
|
1006
|
-
'pressure': 'Pressure_surface',
|
|
1007
|
-
'wind_speed_gust': 'Wind_speed_gust_surface',
|
|
1008
|
-
'wind_speed_u': 'u-component_of_wind_height_above_ground',
|
|
1009
|
-
'wind_speed_v': 'v-component_of_wind_height_above_ground',
|
|
1010
|
-
'total_clouds': 'Total_cloud_cover_entire_atmosphere',
|
|
1011
|
-
'low_clouds': 'Low_cloud_cover_low_cloud',
|
|
1012
|
-
'mid_clouds': 'Medium_cloud_cover_middle_cloud',
|
|
1013
|
-
'high_clouds': 'High_cloud_cover_high_cloud'}
|
|
1014
|
-
|
|
1015
|
-
self.output_variables = [
|
|
1016
|
-
'temp_air',
|
|
1017
|
-
'wind_speed',
|
|
1018
|
-
'ghi',
|
|
1019
|
-
'dni',
|
|
1020
|
-
'dhi',
|
|
1021
|
-
'total_clouds',
|
|
1022
|
-
'low_clouds',
|
|
1023
|
-
'mid_clouds',
|
|
1024
|
-
'high_clouds', ]
|
|
1025
|
-
|
|
1026
|
-
super().__init__(model_type, model, set_type)
|
|
1027
|
-
|
|
1028
|
-
def process_data(self, data, cloud_cover='total_clouds', **kwargs):
|
|
1029
|
-
"""
|
|
1030
|
-
Defines the steps needed to convert raw forecast data
|
|
1031
|
-
into processed forecast data.
|
|
1032
|
-
|
|
1033
|
-
Parameters
|
|
1034
|
-
----------
|
|
1035
|
-
data: DataFrame
|
|
1036
|
-
Raw forecast data
|
|
1037
|
-
cloud_cover: str, default 'total_clouds'
|
|
1038
|
-
The type of cloud cover used to infer the irradiance.
|
|
1039
|
-
|
|
1040
|
-
Returns
|
|
1041
|
-
-------
|
|
1042
|
-
data: DataFrame
|
|
1043
|
-
Processed forecast data.
|
|
1044
|
-
"""
|
|
1045
|
-
data = super().process_data(data, **kwargs)
|
|
1046
|
-
wind_mapping = {
|
|
1047
|
-
'wind_speed_u': 'u-component_of_wind_height_above_ground_0',
|
|
1048
|
-
'wind_speed_v': 'v-component_of_wind_height_above_ground_0',
|
|
1049
|
-
}
|
|
1050
|
-
data = self.rename(data, variables=wind_mapping)
|
|
1051
|
-
data['temp_air'] = self.kelvin_to_celsius(data['temp_air'])
|
|
1052
|
-
data['wind_speed'] = self.uv_to_speed(data)
|
|
1053
|
-
irrads = self.cloud_cover_to_irradiance(data[cloud_cover], **kwargs)
|
|
1054
|
-
data = data.join(irrads, how='outer')
|
|
1055
|
-
data = data.iloc[:-1, :] # issue with last point
|
|
1056
|
-
return data[self.output_variables]
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
@_forecast_deprecated
|
|
1060
|
-
class NDFD(ForecastModel):
|
|
1061
|
-
"""
|
|
1062
|
-
Subclass of the ForecastModel class representing NDFD forecast
|
|
1063
|
-
model.
|
|
1064
|
-
|
|
1065
|
-
Model data corresponds to NWS CONUS CONDUIT forecasts.
|
|
1066
|
-
|
|
1067
|
-
Parameters
|
|
1068
|
-
----------
|
|
1069
|
-
set_type: string, default 'best'
|
|
1070
|
-
Type of model to pull data from.
|
|
1071
|
-
|
|
1072
|
-
Attributes
|
|
1073
|
-
----------
|
|
1074
|
-
dataframe_variables: list
|
|
1075
|
-
Common variables present in the final set of data.
|
|
1076
|
-
model: string
|
|
1077
|
-
Name of the UNIDATA forecast model.
|
|
1078
|
-
model_type: string
|
|
1079
|
-
UNIDATA category in which the model is located.
|
|
1080
|
-
variables: dict
|
|
1081
|
-
Defines the variables to obtain from the weather
|
|
1082
|
-
model and how they should be renamed to common variable names.
|
|
1083
|
-
units: dict
|
|
1084
|
-
Dictionary containing the units of the standard variables
|
|
1085
|
-
and the model specific variables.
|
|
1086
|
-
"""
|
|
1087
|
-
|
|
1088
|
-
def __init__(self, set_type='best'):
|
|
1089
|
-
model_type = 'Forecast Products and Analyses'
|
|
1090
|
-
model = 'National Weather Service CONUS Forecast Grids (CONDUIT)'
|
|
1091
|
-
self.variables = {
|
|
1092
|
-
'temp_air': 'Temperature_height_above_ground',
|
|
1093
|
-
'wind_speed': 'Wind_speed_height_above_ground',
|
|
1094
|
-
'total_clouds': 'Total_cloud_cover_surface', }
|
|
1095
|
-
self.output_variables = [
|
|
1096
|
-
'temp_air',
|
|
1097
|
-
'wind_speed',
|
|
1098
|
-
'ghi',
|
|
1099
|
-
'dni',
|
|
1100
|
-
'dhi',
|
|
1101
|
-
'total_clouds', ]
|
|
1102
|
-
super().__init__(model_type, model, set_type)
|
|
1103
|
-
|
|
1104
|
-
def process_data(self, data, **kwargs):
|
|
1105
|
-
"""
|
|
1106
|
-
Defines the steps needed to convert raw forecast data
|
|
1107
|
-
into processed forecast data.
|
|
1108
|
-
|
|
1109
|
-
Parameters
|
|
1110
|
-
----------
|
|
1111
|
-
data: DataFrame
|
|
1112
|
-
Raw forecast data
|
|
1113
|
-
|
|
1114
|
-
Returns
|
|
1115
|
-
-------
|
|
1116
|
-
data: DataFrame
|
|
1117
|
-
Processed forecast data.
|
|
1118
|
-
"""
|
|
1119
|
-
|
|
1120
|
-
cloud_cover = 'total_clouds'
|
|
1121
|
-
data = super().process_data(data, **kwargs)
|
|
1122
|
-
data['temp_air'] = self.kelvin_to_celsius(data['temp_air'])
|
|
1123
|
-
irrads = self.cloud_cover_to_irradiance(data[cloud_cover], **kwargs)
|
|
1124
|
-
data = data.join(irrads, how='outer')
|
|
1125
|
-
return data[self.output_variables]
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
@_forecast_deprecated
|
|
1129
|
-
class RAP(ForecastModel):
|
|
1130
|
-
"""
|
|
1131
|
-
Subclass of the ForecastModel class representing RAP forecast model.
|
|
1132
|
-
|
|
1133
|
-
Model data corresponds to Rapid Refresh CONUS 20km resolution
|
|
1134
|
-
forecasts.
|
|
1135
|
-
|
|
1136
|
-
Parameters
|
|
1137
|
-
----------
|
|
1138
|
-
resolution: string or int, default '20'
|
|
1139
|
-
The model resolution, either '20' or '40' (km)
|
|
1140
|
-
set_type: string, default 'best'
|
|
1141
|
-
Type of model to pull data from.
|
|
1142
|
-
|
|
1143
|
-
Attributes
|
|
1144
|
-
----------
|
|
1145
|
-
dataframe_variables: list
|
|
1146
|
-
Common variables present in the final set of data.
|
|
1147
|
-
model: string
|
|
1148
|
-
Name of the UNIDATA forecast model.
|
|
1149
|
-
model_type: string
|
|
1150
|
-
UNIDATA category in which the model is located.
|
|
1151
|
-
variables: dict
|
|
1152
|
-
Defines the variables to obtain from the weather
|
|
1153
|
-
model and how they should be renamed to common variable names.
|
|
1154
|
-
units: dict
|
|
1155
|
-
Dictionary containing the units of the standard variables
|
|
1156
|
-
and the model specific variables.
|
|
1157
|
-
"""
|
|
1158
|
-
|
|
1159
|
-
_resolutions = ['20', '40']
|
|
1160
|
-
|
|
1161
|
-
def __init__(self, resolution='20', set_type='best'):
|
|
1162
|
-
|
|
1163
|
-
resolution = str(resolution)
|
|
1164
|
-
if resolution not in self._resolutions:
|
|
1165
|
-
raise ValueError(f'resolution must in {self._resolutions}')
|
|
1166
|
-
|
|
1167
|
-
model_type = 'Forecast Model Data'
|
|
1168
|
-
model = f'Rapid Refresh CONUS {resolution}km'
|
|
1169
|
-
self.variables = {
|
|
1170
|
-
'temp_air': 'Temperature_surface',
|
|
1171
|
-
'wind_speed_gust': 'Wind_speed_gust_surface',
|
|
1172
|
-
'total_clouds': 'Total_cloud_cover_entire_atmosphere',
|
|
1173
|
-
'low_clouds': 'Low_cloud_cover_low_cloud',
|
|
1174
|
-
'mid_clouds': 'Medium_cloud_cover_middle_cloud',
|
|
1175
|
-
'high_clouds': 'High_cloud_cover_high_cloud', }
|
|
1176
|
-
self.output_variables = [
|
|
1177
|
-
'temp_air',
|
|
1178
|
-
'wind_speed',
|
|
1179
|
-
'ghi',
|
|
1180
|
-
'dni',
|
|
1181
|
-
'dhi',
|
|
1182
|
-
'total_clouds',
|
|
1183
|
-
'low_clouds',
|
|
1184
|
-
'mid_clouds',
|
|
1185
|
-
'high_clouds', ]
|
|
1186
|
-
super().__init__(model_type, model, set_type)
|
|
1187
|
-
|
|
1188
|
-
def process_data(self, data, cloud_cover='total_clouds', **kwargs):
|
|
1189
|
-
"""
|
|
1190
|
-
Defines the steps needed to convert raw forecast data
|
|
1191
|
-
into processed forecast data.
|
|
1192
|
-
|
|
1193
|
-
Parameters
|
|
1194
|
-
----------
|
|
1195
|
-
data: DataFrame
|
|
1196
|
-
Raw forecast data
|
|
1197
|
-
cloud_cover: str, default 'total_clouds'
|
|
1198
|
-
The type of cloud cover used to infer the irradiance.
|
|
1199
|
-
|
|
1200
|
-
Returns
|
|
1201
|
-
-------
|
|
1202
|
-
data: DataFrame
|
|
1203
|
-
Processed forecast data.
|
|
1204
|
-
"""
|
|
1205
|
-
|
|
1206
|
-
data = super().process_data(data, **kwargs)
|
|
1207
|
-
data['temp_air'] = self.kelvin_to_celsius(data['temp_air'])
|
|
1208
|
-
data['wind_speed'] = self.gust_to_speed(data)
|
|
1209
|
-
irrads = self.cloud_cover_to_irradiance(data[cloud_cover], **kwargs)
|
|
1210
|
-
data = data.join(irrads, how='outer')
|
|
1211
|
-
return data[self.output_variables]
|