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
@@ -1,312 +0,0 @@
1
- """
2
- Read data from ECMWF MACC Reanalysis.
3
- """
4
-
5
- import threading
6
- import pandas as pd
7
-
8
- try:
9
- import netCDF4
10
- except ImportError:
11
- class netCDF4:
12
- @staticmethod
13
- def Dataset(*a, **kw):
14
- raise ImportError(
15
- 'Reading ECMWF data requires netCDF4 to be installed.')
16
-
17
- try:
18
- from ecmwfapi import ECMWFDataServer
19
- except ImportError:
20
- def ECMWFDataServer(*a, **kw):
21
- raise ImportError(
22
- 'To download data from ECMWF requires the API client.\nSee https:/'
23
- '/confluence.ecmwf.int/display/WEBAPI/Access+ECMWF+Public+Datasets'
24
- )
25
-
26
- #: map of ECMWF MACC parameter keynames and codes used in API
27
- PARAMS = {
28
- "tcwv": "137.128",
29
- "aod550": "207.210",
30
- 'aod469': '213.210',
31
- 'aod670': '214.210',
32
- 'aod865': '215.210',
33
- "aod1240": "216.210",
34
- }
35
-
36
-
37
- def _ecmwf(server, startdate, enddate, params, targetname):
38
- # see http://apps.ecmwf.int/datasets/data/macc-reanalysis/levtype=sfc/
39
- server.retrieve({
40
- "class": "mc",
41
- "dataset": "macc",
42
- "date": "%s/to/%s" % (startdate, enddate),
43
- "expver": "rean",
44
- "grid": "0.75/0.75",
45
- "levtype": "sfc",
46
- "param": params,
47
- "step": "3/6/9/12/15/18/21/24",
48
- "stream": "oper",
49
- "format": "netcdf",
50
- "time": "00:00:00",
51
- "type": "fc",
52
- "target": targetname,
53
- })
54
-
55
-
56
- def get_ecmwf_macc(filename, params, start, end, lookup_params=True,
57
- server=None, target=_ecmwf):
58
- """
59
- Download data from ECMWF MACC Reanalysis API.
60
-
61
- Parameters
62
- ----------
63
- filename : str
64
- full path of file where to save data, ``.nc`` appended if not given
65
- params : str or sequence of str
66
- keynames of parameter[s] to download
67
- start : datetime.datetime or datetime.date
68
- UTC date
69
- end : datetime.datetime or datetime.date
70
- UTC date
71
- lookup_params : bool, default True
72
- optional flag, if ``False``, then codes are already formatted
73
- server : ecmwfapi.api.ECMWFDataServer
74
- optionally provide a server object, default is ``None``
75
- target : callable
76
- optional function that calls ``server.retrieve`` to pass to thread
77
-
78
- Returns
79
- -------
80
- t : thread
81
- a thread object, use it to check status by calling `t.is_alive()`
82
-
83
- Notes
84
- -----
85
- To download data from ECMWF requires the API client and a registration
86
- key. Please read the documentation in `Access ECMWF Public Datasets
87
- <https://confluence.ecmwf.int/display/WEBAPI/Access+ECMWF+Public+Datasets>`_.
88
- Follow the instructions in step 4 and save the ECMWF registration key
89
- as `$HOME/.ecmwfapirc` or set `ECMWF_API_KEY` as the path to the key.
90
-
91
- This function returns a daemon thread that runs in the background. Exiting
92
- Python will kill this thread, however this thread will not block the main
93
- thread or other threads. This thread will terminate when the file is
94
- downloaded or if the thread raises an unhandled exception. You may submit
95
- multiple requests simultaneously to break up large downloads. You can also
96
- check the status and retrieve downloads online at
97
- http://apps.ecmwf.int/webmars/joblist/. This is useful if you kill the
98
- thread. Downloads expire after 24 hours.
99
-
100
- .. warning:: Your request may be queued online for an hour or more before
101
- it begins to download
102
-
103
- Precipitable water :math:`P_{wat}` is equivalent to the total column of
104
- water vapor (TCWV), but the units given by ECMWF MACC Reanalysis are kg/m^2
105
- at STP (1-atm, 25-C). Divide by ten to convert to centimeters of
106
- precipitable water:
107
-
108
- .. math::
109
- P_{wat} \\left( \\text{cm} \\right) \
110
- = TCWV \\left( \\frac{\\text{kg}}{\\text{m}^2} \\right) \
111
- \\frac{100 \\frac{\\text{cm}}{\\text{m}}} \
112
- {1000 \\frac{\\text{kg}}{\\text{m}^3}}
113
-
114
- The keynames available for the ``params`` argument are given by
115
- :const:`pvlib.iotools.ecmwf_macc.PARAMS` which maps the keys to codes used
116
- in the API. The following keynames are available:
117
-
118
- ======= =========================================
119
- keyname description
120
- ======= =========================================
121
- tcwv total column water vapor in kg/m^2 at STP
122
- aod550 aerosol optical depth measured at 550-nm
123
- aod469 aerosol optical depth measured at 469-nm
124
- aod670 aerosol optical depth measured at 670-nm
125
- aod865 aerosol optical depth measured at 865-nm
126
- aod1240 aerosol optical depth measured at 1240-nm
127
- ======= =========================================
128
-
129
- If ``lookup_params`` is ``False`` then ``params`` must contain the codes
130
- preformatted according to the ECMWF MACC Reanalysis API. This is useful if
131
- you want to retrieve codes that are not mapped in
132
- :const:`pvlib.iotools.ecmwf_macc.PARAMS`.
133
-
134
- Specify a custom ``target`` function to modify how the ECMWF API function
135
- ``server.retrieve`` is called. The ``target`` function must have the
136
- following signature in which the parameter definitions are similar to
137
- :func:`pvlib.iotools.get_ecmwf_macc`. ::
138
-
139
-
140
- target(server, startdate, enddate, params, filename) -> None
141
-
142
- Examples
143
- --------
144
- Retrieve the AOD measured at 550-nm and the total column of water vapor for
145
- November 1, 2012.
146
-
147
- >>> from datetime import date
148
- >>> from pvlib.iotools import get_ecmwf_macc
149
- >>> filename = 'aod_tcwv_20121101.nc' # .nc extension added if missing
150
- >>> params = ('aod550', 'tcwv')
151
- >>> start = end = date(2012, 11, 1)
152
- >>> t = get_ecmwf_macc(filename, params, start, end)
153
- >>> t.is_alive()
154
- True
155
-
156
- """
157
- if not filename.endswith('nc'):
158
- filename += '.nc'
159
- if lookup_params:
160
- try:
161
- params = '/'.join(PARAMS.get(p) for p in params)
162
- except TypeError:
163
- params = PARAMS.get(params)
164
- startdate = start.strftime('%Y-%m-%d')
165
- enddate = end.strftime('%Y-%m-%d')
166
- if not server:
167
- server = ECMWFDataServer()
168
- t = threading.Thread(target=target, daemon=True,
169
- args=(server, startdate, enddate, params, filename))
170
- t.start()
171
- return t
172
-
173
-
174
- class ECMWF_MACC(object):
175
- """container for ECMWF MACC reanalysis data"""
176
-
177
- TCWV = 'tcwv' # total column water vapor in kg/m^2 at (1-atm,25-degC)
178
-
179
- def __init__(self, filename):
180
- self.data = netCDF4.Dataset(filename)
181
- # data variables and dimensions
182
- variables = set(self.data.variables.keys())
183
- dimensions = set(self.data.dimensions.keys())
184
- self.keys = tuple(variables - dimensions)
185
- # size of lat/lon dimensions
186
- self.lat_size = self.data.dimensions['latitude'].size
187
- self.lon_size = self.data.dimensions['longitude'].size
188
- # spatial resolution in degrees
189
- self.delta_lat = -180.0 / (self.lat_size - 1) # from north to south
190
- self.delta_lon = 360.0 / self.lon_size # from west to east
191
- # time resolution in hours
192
- self.time_size = self.data.dimensions['time'].size
193
- self.start_time = self.data['time'][0]
194
- self.end_time = self.data['time'][-1]
195
- self.time_range = self.end_time - self.start_time
196
- self.delta_time = self.time_range / (self.time_size - 1)
197
-
198
- def get_nearest_indices(self, latitude, longitude):
199
- """
200
- Get nearest indices to (latitude, longitude).
201
-
202
- Parmaeters
203
- ----------
204
- latitude : float
205
- Latitude in degrees
206
- longitude : float
207
- Longitude in degrees
208
-
209
- Returns
210
- -------
211
- idx_lat : int
212
- index of nearest latitude
213
- idx_lon : int
214
- index of nearest longitude
215
- """
216
- # index of nearest latitude
217
- idx_lat = int(round((latitude - 90.0) / self.delta_lat))
218
- # avoid out of bounds latitudes
219
- if idx_lat < 0:
220
- idx_lat = 0 # if latitude == 90, north pole
221
- elif idx_lat > self.lat_size:
222
- idx_lat = self.lat_size # if latitude == -90, south pole
223
- # adjust longitude from -180/180 to 0/360
224
- longitude = longitude % 360.0
225
- # index of nearest longitude
226
- idx_lon = int(round(longitude / self.delta_lon)) % self.lon_size
227
- return idx_lat, idx_lon
228
-
229
- def interp_data(self, latitude, longitude, utc_time, param):
230
- """
231
- Interpolate ``param`` values to ``utc_time`` using indices nearest to
232
- (``latitude, longitude``).
233
-
234
- Parmaeters
235
- ----------
236
- latitude : float
237
- Latitude in degrees
238
- longitude : float
239
- Longitude in degrees
240
- utc_time : datetime.datetime or datetime.date
241
- Naive or UTC date or datetime to interpolate
242
- param : str
243
- Name of the parameter to interpolate from the data
244
-
245
- Returns
246
- -------
247
- Interpolated ``param`` value at (``utc_time, latitude, longitude``)
248
-
249
- Examples
250
- --------
251
- Use this to get a single value of a parameter in the data at a specific
252
- time and set of (latitude, longitude) coordinates.
253
-
254
- >>> from datetime import datetime
255
- >>> from pvlib.iotools import ecmwf_macc
256
- >>> data = ecmwf_macc.ECMWF_MACC('aod_tcwv_20121101.nc')
257
- >>> dt = datetime(2012, 11, 1, 11, 33, 1)
258
- >>> data.interp_data(38.2, -122.1, dt, 'aod550')
259
- """
260
- nctime = self.data['time'] # time
261
- ilat, ilon = self.get_nearest_indices(latitude, longitude)
262
- # time index before
263
- before = netCDF4.date2index(utc_time, nctime, select='before')
264
- fbefore = self.data[param][before, ilat, ilon]
265
- fafter = self.data[param][before + 1, ilat, ilon]
266
- dt_num = netCDF4.date2num(utc_time, nctime.units)
267
- time_ratio = (dt_num - nctime[before]) / self.delta_time
268
- return fbefore + (fafter - fbefore) * time_ratio
269
-
270
-
271
- def read_ecmwf_macc(filename, latitude, longitude, utc_time_range=None):
272
- """
273
- Read data from ECMWF MACC reanalysis netCDF4 file.
274
-
275
- Parameters
276
- ----------
277
- filename : string
278
- full path to netCDF4 data file.
279
- latitude : float
280
- latitude in degrees
281
- longitude : float
282
- longitude in degrees
283
- utc_time_range : sequence of datetime.datetime
284
- pair of start and end naive or UTC date-times
285
-
286
- Returns
287
- -------
288
- data : pandas.DataFrame
289
- dataframe for specified range of UTC date-times
290
- """
291
- ecmwf_macc = ECMWF_MACC(filename)
292
- try:
293
- ilat, ilon = ecmwf_macc.get_nearest_indices(latitude, longitude)
294
- nctime = ecmwf_macc.data['time']
295
- if utc_time_range:
296
- start_idx = netCDF4.date2index(
297
- utc_time_range[0], nctime, select='nearest')
298
- end_idx = netCDF4.date2index(
299
- utc_time_range[-1], nctime, select='nearest')
300
- time_slice = slice(start_idx, end_idx + 1)
301
- else:
302
- time_slice = slice(0, ecmwf_macc.time_size)
303
- times = netCDF4.num2date(nctime[time_slice], nctime.units)
304
- df = {k: ecmwf_macc.data[k][time_slice, ilat, ilon]
305
- for k in ecmwf_macc.keys}
306
- if ECMWF_MACC.TCWV in df:
307
- # convert total column water vapor in kg/m^2 at (1-atm, 25-degC) to
308
- # precipitable water in cm
309
- df['precipitable_water'] = df[ECMWF_MACC.TCWV] / 10.0
310
- finally:
311
- ecmwf_macc.data.close()
312
- return pd.DataFrame(df, index=times.astype('datetime64[s]'))
@@ -1,162 +0,0 @@
1
- """
2
- tests for :mod:`pvlib.iotools.ecmwf_macc`
3
- """
4
-
5
- import os
6
- import datetime
7
- import numpy as np
8
- import pytest
9
- from ..conftest import requires_netCDF4, DATA_DIR
10
- from pvlib.iotools import ecmwf_macc
11
-
12
- TESTDATA = 'aod550_tcwv_20121101_test.nc'
13
-
14
- # for creating test data
15
- START = END = datetime.date(2012, 11, 1)
16
- DATAFILE = 'aod550_tcwv_20121101.nc'
17
- RESIZE = 4
18
- LON_BND = (0, 360.0)
19
- LAT_BND = (90, -90)
20
-
21
-
22
- @pytest.fixture
23
- def expected_test_data():
24
- return DATA_DIR / TESTDATA
25
-
26
-
27
- @requires_netCDF4
28
- def test_get_nearest_indices(expected_test_data):
29
- """Test getting indices given latitude, longitude from ECMWF_MACC data."""
30
- data = ecmwf_macc.ECMWF_MACC(expected_test_data)
31
- ilat, ilon = data.get_nearest_indices(38, -122)
32
- assert ilat == 17
33
- assert ilon == 79
34
-
35
-
36
- @requires_netCDF4
37
- def test_interp_data(expected_test_data):
38
- """Test interpolating UTC time from ECMWF_MACC data."""
39
- data = ecmwf_macc.ECMWF_MACC(expected_test_data)
40
- test9am = data.interp_data(
41
- 38, -122, datetime.datetime(2012, 11, 1, 9, 0, 0), 'aod550')
42
- assert np.isclose(test9am, data.data.variables['aod550'][2, 17, 79])
43
- test12pm = data.interp_data(
44
- 38, -122, datetime.datetime(2012, 11, 1, 12, 0, 0), 'aod550')
45
- assert np.isclose(test12pm, data.data.variables['aod550'][3, 17, 79])
46
- test113301 = data.interp_data(
47
- 38, -122, datetime.datetime(2012, 11, 1, 11, 33, 1), 'aod550')
48
- expected = test9am + (2 + (33 + 1 / 60) / 60) / 3 * (test12pm - test9am)
49
- assert np.isclose(test113301, expected) # 0.15515305836696536
50
-
51
-
52
- @requires_netCDF4
53
- def test_read_ecmwf_macc(expected_test_data):
54
- """Test reading ECMWF_MACC data from netCDF4 file."""
55
- data = ecmwf_macc.read_ecmwf_macc(
56
- expected_test_data, 38, -122)
57
- expected_times = [
58
- 1351738800, 1351749600, 1351760400, 1351771200, 1351782000, 1351792800,
59
- 1351803600, 1351814400]
60
- assert np.allclose(data.index.view(np.int64) // 1000000000, expected_times)
61
- expected_aod = np.array([
62
- 0.39531226, 0.22371339, 0.18373083, 0.15010143, 0.130809, 0.11172834,
63
- 0.09741255, 0.0921606])
64
- expected_tcwv = np.array([
65
- 26.56172238, 22.75563109, 19.37884304, 16.19186269, 13.31990346,
66
- 11.65635338, 10.94879802, 10.55725756])
67
- assert np.allclose(data.aod550.values, expected_aod)
68
- assert np.allclose(data.tcwv.values, expected_tcwv)
69
- assert np.allclose(data.precipitable_water.values, expected_tcwv / 10.0)
70
- datetimes = (datetime.datetime(2012, 11, 1, 9, 0, 0),
71
- datetime.datetime(2012, 11, 1, 12, 0, 0))
72
- data_9am_12pm = ecmwf_macc.read_ecmwf_macc(
73
- expected_test_data, 38, -122, datetimes)
74
- assert np.allclose(data_9am_12pm.aod550.values, expected_aod[2:4])
75
- assert np.allclose(data_9am_12pm.tcwv.values, expected_tcwv[2:4])
76
-
77
-
78
- def _create_test_data(datafile=DATAFILE, testfile=TESTDATA, start=START,
79
- end=END, resize=RESIZE): # pragma: no cover
80
- """
81
- Create test data from downloaded data.
82
-
83
- Downloaded data from ECMWF for a single day is 3MB. This creates a subset
84
- of the downloaded data that is only 100kb.
85
- """
86
-
87
- import netCDF4
88
-
89
- if not os.path.exists(datafile):
90
- ecmwf_macc.get_ecmwf_macc(datafile, ("aod550", "tcwv"), start, end)
91
-
92
- data = netCDF4.Dataset(datafile)
93
- testdata = netCDF4.Dataset(testfile, 'w', format="NETCDF3_64BIT_OFFSET")
94
-
95
- # attributes
96
- testdata.Conventions = data.Conventions
97
- testdata.history = "intentionally blank"
98
-
99
- # longitude
100
- lon_name = 'longitude'
101
- lon_test = data.variables[lon_name][::resize]
102
- lon_size = lon_test.size
103
- lon = testdata.createDimension(lon_name, lon_size)
104
- assert not lon.isunlimited()
105
- assert lon_test[0] == LON_BND[0]
106
- assert (LON_BND[-1] - lon_test[-1]) == (LON_BND[-1] / lon_size)
107
- longitudes = testdata.createVariable(lon_name, "f4", (lon_name,))
108
- longitudes.units = data.variables[lon_name].units
109
- longitudes.long_name = lon_name
110
- longitudes[:] = lon_test
111
-
112
- # latitude
113
- lat_name = 'latitude'
114
- lat_test = data.variables[lat_name][::resize]
115
- lat = testdata.createDimension(lat_name, lat_test.size)
116
- assert not lat.isunlimited()
117
- assert lat_test[0] == LAT_BND[0]
118
- assert lat_test[-1] == LAT_BND[-1]
119
- latitudes = testdata.createVariable(lat_name, "f4", (lat_name,))
120
- latitudes.units = data.variables[lat_name].units
121
- latitudes.long_name = lat_name
122
- latitudes[:] = lat_test
123
-
124
- # time
125
- time_name = 'time'
126
- time_test = data.variables[time_name][:]
127
- time = testdata.createDimension(time_name, None)
128
- assert time.isunlimited()
129
- times = testdata.createVariable(time_name, 'i4', (time_name,))
130
- times.units = data.variables[time_name].units
131
- times.long_name = time_name
132
- times.calendar = data.variables[time_name].calendar
133
- times[:] = time_test
134
-
135
- # aod
136
- aod_name = 'aod550'
137
- aod_vars = data.variables[aod_name]
138
- aod_dims = (time_name, lat_name, lon_name)
139
- aod_fill_value = getattr(aod_vars, '_FillValue')
140
- aods = testdata.createVariable(
141
- aod_name, 'i2', aod_dims, fill_value=aod_fill_value)
142
- for attr in aod_vars.ncattrs():
143
- if attr.startswith('_'):
144
- continue
145
- setattr(aods, attr, getattr(aod_vars, attr))
146
- aods[:] = aod_vars[:, ::resize, ::resize]
147
-
148
- # tcwv
149
- tcwv_name = 'tcwv'
150
- tcwv_vars = data.variables[tcwv_name]
151
- tcwv_dims = (time_name, lat_name, lon_name)
152
- tcwv_fill_value = getattr(tcwv_vars, '_FillValue')
153
- tcwvs = testdata.createVariable(
154
- tcwv_name, 'i2', tcwv_dims, fill_value=tcwv_fill_value)
155
- for attr in tcwv_vars.ncattrs():
156
- if attr.startswith('_'):
157
- continue
158
- setattr(tcwvs, attr, getattr(tcwv_vars, attr))
159
- tcwvs[:] = tcwv_vars[:, ::resize, ::resize]
160
-
161
- data.close()
162
- testdata.close()
@@ -1,228 +0,0 @@
1
- from datetime import datetime, timedelta, timezone
2
- import warnings
3
-
4
- import pandas as pd
5
-
6
- import pytest
7
- from numpy.testing import assert_allclose
8
-
9
- from .conftest import (
10
- requires_siphon,
11
- has_siphon,
12
- skip_windows,
13
- requires_recent_cftime
14
- )
15
- from .conftest import RERUNS, RERUNS_DELAY
16
-
17
- from pvlib._deprecation import pvlibDeprecationWarning
18
-
19
- pytestmark = pytest.mark.skipif(not has_siphon, reason='requires siphon')
20
-
21
-
22
- if has_siphon:
23
- with warnings.catch_warnings():
24
- # don't emit import warning
25
- warnings.simplefilter("ignore")
26
- from pvlib.forecast import GFS, HRRR_ESRL, HRRR, NAM, NDFD, RAP
27
-
28
- # setup times and location to be tested. Tucson, AZ
29
- _latitude = 32.2
30
- _longitude = -110.9
31
- _tz = 'US/Arizona'
32
- _start = pd.Timestamp.now(tz=_tz)
33
- _end = _start + pd.Timedelta(days=1)
34
- _modelclasses = [
35
- GFS, NAM, HRRR, NDFD, RAP,
36
- pytest.param(
37
- HRRR_ESRL, marks=[
38
- skip_windows,
39
- pytest.mark.xfail(reason="HRRR_ESRL is unreliable"),
40
- pytest.mark.timeout(timeout=60),
41
- pytest.mark.filterwarnings('ignore:.*experimental')])]
42
- _working_models = []
43
- _variables = ['temp_air', 'wind_speed', 'total_clouds', 'low_clouds',
44
- 'mid_clouds', 'high_clouds', 'dni', 'dhi', 'ghi']
45
- _nonnan_variables = ['temp_air', 'wind_speed', 'total_clouds', 'dni',
46
- 'dhi', 'ghi']
47
- else:
48
- _modelclasses = []
49
-
50
-
51
- # make a model object for each model class
52
- # get the data for that model and store it in an
53
- # attribute for further testing
54
- @requires_siphon
55
- @pytest.fixture(scope='module', params=_modelclasses)
56
- def model(request):
57
- with pytest.warns(pvlibDeprecationWarning):
58
- amodel = request.param()
59
- try:
60
- raw_data = amodel.get_data(_latitude, _longitude, _start, _end)
61
- except Exception as e:
62
- warnings.warn('Exception getting data for {}.\n'
63
- 'latitude, longitude, start, end = {} {} {} {}\n{}'
64
- .format(amodel, _latitude, _longitude, _start, _end, e))
65
- raw_data = pd.DataFrame() # raw_data.empty will be used later
66
- amodel.raw_data = raw_data
67
- return amodel
68
-
69
-
70
- @requires_siphon
71
- @requires_recent_cftime
72
- @pytest.mark.remote_data
73
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
74
- def test_process_data(model):
75
- for how in ['campbell_norman', 'clearsky_scaling']:
76
- if model.raw_data.empty:
77
- warnings.warn('Could not test {} process_data with how={} '
78
- 'because raw_data was empty'.format(model, how))
79
- continue
80
- data = model.process_data(model.raw_data, how=how)
81
- for variable in _nonnan_variables:
82
- try:
83
- assert not data[variable].isnull().values.any()
84
- except AssertionError:
85
- warnings.warn('{}, {}, data contained null values'
86
- .format(model, variable))
87
-
88
-
89
- @requires_siphon
90
- @requires_recent_cftime
91
- @pytest.mark.remote_data
92
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
93
- def test_bad_kwarg_get_data():
94
- # For more information on why you would want to pass an unknown keyword
95
- # argument, see Github issue #745.
96
- with pytest.warns(pvlibDeprecationWarning):
97
- amodel = NAM()
98
- data = amodel.get_data(_latitude, _longitude, _start, _end,
99
- bad_kwarg=False)
100
- assert not data.empty
101
-
102
-
103
- @requires_siphon
104
- @requires_recent_cftime
105
- @pytest.mark.remote_data
106
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
107
- def test_bad_kwarg_get_processed_data():
108
- # For more information on why you would want to pass an unknown keyword
109
- # argument, see Github issue #745.
110
- with pytest.warns(pvlibDeprecationWarning):
111
- amodel = NAM()
112
- data = amodel.get_processed_data(_latitude, _longitude, _start, _end,
113
- bad_kwarg=False)
114
- assert not data.empty
115
-
116
-
117
- @requires_siphon
118
- @requires_recent_cftime
119
- @pytest.mark.remote_data
120
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
121
- def test_how_kwarg_get_processed_data():
122
- with pytest.warns(pvlibDeprecationWarning):
123
- amodel = NAM()
124
- data = amodel.get_processed_data(_latitude, _longitude, _start, _end,
125
- how='clearsky_scaling')
126
- assert not data.empty
127
-
128
-
129
- @requires_siphon
130
- @requires_recent_cftime
131
- @pytest.mark.remote_data
132
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
133
- def test_vert_level():
134
- with pytest.warns(pvlibDeprecationWarning):
135
- amodel = NAM()
136
- vert_level = 5000
137
- amodel.get_processed_data(_latitude, _longitude, _start, _end,
138
- vert_level=vert_level)
139
-
140
-
141
- @requires_siphon
142
- @requires_recent_cftime
143
- @pytest.mark.remote_data
144
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
145
- def test_datetime():
146
- with pytest.warns(pvlibDeprecationWarning):
147
- amodel = NAM()
148
- start = datetime.now(tz=timezone.utc)
149
- end = start + timedelta(days=1)
150
- amodel.get_processed_data(_latitude, _longitude, start, end)
151
-
152
-
153
- @requires_siphon
154
- @requires_recent_cftime
155
- @pytest.mark.remote_data
156
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
157
- def test_queryvariables():
158
- with pytest.warns(pvlibDeprecationWarning):
159
- amodel = GFS()
160
- new_variables = ['u-component_of_wind_height_above_ground']
161
- data = amodel.get_data(_latitude, _longitude, _start, _end,
162
- query_variables=new_variables)
163
- data['u-component_of_wind_height_above_ground']
164
-
165
-
166
- @requires_siphon
167
- def test_latest():
168
- with pytest.warns(pvlibDeprecationWarning):
169
- GFS(set_type='latest')
170
-
171
-
172
- @requires_siphon
173
- def test_full():
174
- with pytest.warns(pvlibDeprecationWarning):
175
- GFS(set_type='full')
176
-
177
-
178
- def test_temp_convert():
179
- with pytest.warns(pvlibDeprecationWarning):
180
- amodel = GFS()
181
- data = pd.DataFrame({'temp_air': [273.15]})
182
- data['temp_air'] = amodel.kelvin_to_celsius(data['temp_air'])
183
-
184
- assert_allclose(data['temp_air'].values, 0.0)
185
-
186
-
187
- # @requires_siphon
188
- # def test_bounding_box():
189
- # amodel = GFS()
190
- # latitude = [31.2,32.2]
191
- # longitude = [-111.9,-110.9]
192
- # new_variables = {'temperature':'Temperature_surface'}
193
- # data = amodel.get_query_data(latitude, longitude, _start, _end,
194
- # variables=new_variables)
195
-
196
-
197
- def test_set_location():
198
- with pytest.warns(pvlibDeprecationWarning):
199
- amodel = GFS()
200
- latitude, longitude = 32.2, -110.9
201
- time = 'UTC'
202
- amodel.set_location(time, latitude, longitude)
203
-
204
-
205
- def test_set_query_time_range_tzfail():
206
- with pytest.warns(pvlibDeprecationWarning):
207
- amodel = GFS()
208
- with pytest.raises(TypeError):
209
- amodel.set_query_time_range(datetime.now(), datetime.now())
210
-
211
-
212
- def test_cloud_cover_to_transmittance_linear():
213
- with pytest.warns(pvlibDeprecationWarning):
214
- amodel = GFS()
215
- assert_allclose(amodel.cloud_cover_to_transmittance_linear(0), 0.75)
216
- assert_allclose(amodel.cloud_cover_to_transmittance_linear(100), 0.0)
217
- assert_allclose(amodel.cloud_cover_to_transmittance_linear(0, 0.5), 0.5)
218
-
219
-
220
- def test_cloud_cover_to_ghi_linear():
221
- with pytest.warns(pvlibDeprecationWarning):
222
- amodel = GFS()
223
- ghi_clear = 1000
224
- offset = 25
225
- out = amodel.cloud_cover_to_ghi_linear(0, ghi_clear, offset=offset)
226
- assert_allclose(out, 1000)
227
- out = amodel.cloud_cover_to_ghi_linear(100, ghi_clear, offset=offset)
228
- assert_allclose(out, 250)