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.
Files changed (71) hide show
  1. pvlib/__init__.py +3 -2
  2. pvlib/atmosphere.py +6 -171
  3. pvlib/bifacial/infinite_sheds.py +30 -267
  4. pvlib/bifacial/utils.py +225 -5
  5. pvlib/data/test_psm3_2017.csv +17521 -17521
  6. pvlib/data/test_read_psm3.csv +17522 -17522
  7. pvlib/data/test_read_pvgis_horizon.csv +49 -0
  8. pvlib/data/variables_style_rules.csv +3 -0
  9. pvlib/iam.py +17 -4
  10. pvlib/inverter.py +6 -1
  11. pvlib/iotools/__init__.py +7 -2
  12. pvlib/iotools/acis.py +516 -0
  13. pvlib/iotools/midc.py +4 -4
  14. pvlib/iotools/psm3.py +32 -31
  15. pvlib/iotools/pvgis.py +84 -28
  16. pvlib/iotools/sodapro.py +8 -6
  17. pvlib/iotools/srml.py +121 -18
  18. pvlib/iotools/surfrad.py +2 -2
  19. pvlib/iotools/tmy.py +146 -102
  20. pvlib/irradiance.py +151 -0
  21. pvlib/ivtools/sde.py +11 -7
  22. pvlib/ivtools/sdm.py +16 -10
  23. pvlib/ivtools/utils.py +6 -6
  24. pvlib/location.py +3 -2
  25. pvlib/modelchain.py +67 -70
  26. pvlib/pvsystem.py +160 -532
  27. pvlib/shading.py +41 -0
  28. pvlib/singlediode.py +215 -65
  29. pvlib/soiling.py +3 -3
  30. pvlib/spa.py +327 -368
  31. pvlib/spectrum/__init__.py +8 -2
  32. pvlib/spectrum/mismatch.py +335 -0
  33. pvlib/temperature.py +1 -8
  34. pvlib/tests/bifacial/test_infinite_sheds.py +0 -111
  35. pvlib/tests/bifacial/test_utils.py +101 -4
  36. pvlib/tests/conftest.py +0 -31
  37. pvlib/tests/iotools/test_acis.py +213 -0
  38. pvlib/tests/iotools/test_midc.py +6 -6
  39. pvlib/tests/iotools/test_psm3.py +3 -3
  40. pvlib/tests/iotools/test_pvgis.py +21 -14
  41. pvlib/tests/iotools/test_sodapro.py +1 -1
  42. pvlib/tests/iotools/test_srml.py +71 -6
  43. pvlib/tests/iotools/test_tmy.py +43 -8
  44. pvlib/tests/ivtools/test_sde.py +19 -17
  45. pvlib/tests/ivtools/test_sdm.py +9 -4
  46. pvlib/tests/test_atmosphere.py +6 -62
  47. pvlib/tests/test_iam.py +12 -0
  48. pvlib/tests/test_irradiance.py +40 -2
  49. pvlib/tests/test_location.py +1 -1
  50. pvlib/tests/test_modelchain.py +33 -76
  51. pvlib/tests/test_pvsystem.py +366 -201
  52. pvlib/tests/test_shading.py +28 -0
  53. pvlib/tests/test_singlediode.py +166 -30
  54. pvlib/tests/test_soiling.py +8 -7
  55. pvlib/tests/test_spa.py +6 -7
  56. pvlib/tests/test_spectrum.py +145 -1
  57. pvlib/tests/test_temperature.py +0 -7
  58. pvlib/tests/test_tools.py +25 -0
  59. pvlib/tests/test_tracking.py +0 -149
  60. pvlib/tools.py +26 -1
  61. pvlib/tracking.py +1 -269
  62. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/METADATA +1 -9
  63. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/RECORD +67 -68
  64. pvlib/forecast.py +0 -1211
  65. pvlib/iotools/ecmwf_macc.py +0 -312
  66. pvlib/tests/iotools/test_ecmwf_macc.py +0 -162
  67. pvlib/tests/test_forecast.py +0 -228
  68. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/AUTHORS.md +0 -0
  69. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/LICENSE +0 -0
  70. {pvlib-0.9.5.dist-info → pvlib-0.10.0.dist-info}/WHEEL +0 -0
  71. {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]