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
@@ -0,0 +1,49 @@
1
+ horizon_azimuth,horizon_elevation
2
+ 0,9.9
3
+ 7.5,13
4
+ 15,14.5
5
+ 22.5,15.7
6
+ 30,14.9
7
+ 37.5,15.3
8
+ 45,15.7
9
+ 52.5,15.7
10
+ 60,13
11
+ 67.5,11.5
12
+ 75,11.1
13
+ 82.5,11.5
14
+ 90,10.3
15
+ 97.5,11.5
16
+ 105,10.3
17
+ 112.5,9.5
18
+ 120,10.7
19
+ 127.5,11.8
20
+ 135,11.8
21
+ 142.5,8.8
22
+ 150,8.4
23
+ 157.5,7.3
24
+ 165,5.7
25
+ 172.5,5.7
26
+ 180,4.6
27
+ 187.5,3.4
28
+ 195,0.8
29
+ 202.5,0
30
+ 210,0
31
+ 217.5,0
32
+ 225,0
33
+ 232.5,0
34
+ 240,0
35
+ 247.5,0
36
+ 255,0
37
+ 262.5,0
38
+ 270,0
39
+ 277.5,0
40
+ 285,0
41
+ 292.5,0
42
+ 300,0
43
+ 307.5,0
44
+ 315,1.1
45
+ 322.5,1.9
46
+ 330,3.8
47
+ 337.5,5
48
+ 345,6.5
49
+ 352.5,9.2
@@ -7,6 +7,7 @@ dni_extra;direct normal irradiance at top of atmosphere (extraterrestrial)
7
7
  dhi;diffuse horizontal irradiance
8
8
  bhi;beam/direct horizontal irradiance
9
9
  ghi;global horizontal irradiance
10
+ ghi_extra;horizontal irradiance at top of atmosphere (extraterrestrial)
10
11
  gri;ground-reflected irradiance
11
12
  aoi;angle of incidence between :math:`90\deg` and :math:`90\deg`
12
13
  aoi_projection;cos(aoi)
@@ -32,6 +33,8 @@ relative_humidity;relative humidity
32
33
  wind_speed;wind speed
33
34
  wind_direction;wind direction
34
35
  pressure;atmospheric pressure
36
+ albedo;ratio of reflected solar irradiance to global horizontal irradiance, unitless
37
+ precipitable_water;total precipitable water contained in a column of unit cross section from earth to top of atmosphere
35
38
  v_mp, i_mp, p_mp;module voltage, current, power at the maximum power point
36
39
  v_oc;open circuit module voltage
37
40
  i_sc;short circuit module current
pvlib/iam.py CHANGED
@@ -175,8 +175,12 @@ def physical(aoi, n=1.526, K=4.0, L=0.002, *, n_ar=None):
175
175
  n2costheta2 = n2 * costheta
176
176
 
177
177
  # reflectance of s-, p-polarized, and normal light by the first interface
178
- rho12_s = ((n1costheta1 - n2costheta2) / (n1costheta1 + n2costheta2)) ** 2
179
- rho12_p = ((n1costheta2 - n2costheta1) / (n1costheta2 + n2costheta1)) ** 2
178
+ with np.errstate(divide='ignore', invalid='ignore'):
179
+ rho12_s = \
180
+ ((n1costheta1 - n2costheta2) / (n1costheta1 + n2costheta2)) ** 2
181
+ rho12_p = \
182
+ ((n1costheta2 - n2costheta1) / (n1costheta2 + n2costheta1)) ** 2
183
+
180
184
  rho12_0 = ((n1 - n2) / (n1 + n2)) ** 2
181
185
 
182
186
  # transmittance through the first interface
@@ -208,13 +212,22 @@ def physical(aoi, n=1.526, K=4.0, L=0.002, *, n_ar=None):
208
212
  tau_0 *= (1 - rho23_0) / (1 - rho23_0 * rho12_0)
209
213
 
210
214
  # transmittance after absorption in the glass
211
- tau_s *= np.exp(-K * L / costheta)
212
- tau_p *= np.exp(-K * L / costheta)
215
+ with np.errstate(divide='ignore', invalid='ignore'):
216
+ tau_s *= np.exp(-K * L / costheta)
217
+ tau_p *= np.exp(-K * L / costheta)
218
+
213
219
  tau_0 *= np.exp(-K * L)
214
220
 
215
221
  # incidence angle modifier
216
222
  iam = (tau_s + tau_p) / 2 / tau_0
217
223
 
224
+ # for light coming from behind the plane, none can enter the module
225
+ # when n2 > 1, this is already the case
226
+ if np.isclose(n2, 1).any():
227
+ iam = np.where(aoi >= 90, 0, iam)
228
+ if isinstance(aoi, pd.Series):
229
+ iam = pd.Series(iam, index=aoi.index)
230
+
218
231
  return iam
219
232
 
220
233
 
pvlib/inverter.py CHANGED
@@ -335,7 +335,7 @@ def pvwatts(pdc, pdc0, eta_inv_nom=0.96, eta_inv_ref=0.9637):
335
335
  NREL's PVWatts inverter model.
336
336
 
337
337
  The PVWatts inverter model [1]_ calculates inverter efficiency :math:`\eta`
338
- as a function of input DC power
338
+ as a function of input DC power :math:`P_{dc}`
339
339
 
340
340
  .. math::
341
341
 
@@ -369,6 +369,10 @@ def pvwatts(pdc, pdc0, eta_inv_nom=0.96, eta_inv_ref=0.9637):
369
369
 
370
370
  Notes
371
371
  -----
372
+ When sourcing ``pdc`` from pvlib functions
373
+ (e.g. :py:func:`pvlib.pvsystem.pvwatts_dc`) their DC power output is in W,
374
+ and ``pdc0`` should have the same unit (W).
375
+
372
376
  Note that ``pdc0`` is also used as a symbol in
373
377
  :py:func:`pvlib.pvsystem.pvwatts_dc`. ``pdc0`` in this function refers to
374
378
  the DC power input limit of the inverter. ``pdc0`` in
@@ -393,6 +397,7 @@ def pvwatts(pdc, pdc0, eta_inv_nom=0.96, eta_inv_ref=0.9637):
393
397
  pdc_neq_0 = ~np.equal(pdc, 0)
394
398
 
395
399
  # eta < 0 if zeta < 0.006. power_ac is forced to be >= 0 below. GH 541
400
+ # In some published versions of [1] the parentheses are missing
396
401
  eta = eta_inv_nom / eta_inv_ref * (
397
402
  -0.0162 * zeta - np.divide(0.0059, zeta, out=eta, where=pdc_neq_0)
398
403
  + 0.9858) # noQA: W503
pvlib/iotools/__init__.py CHANGED
@@ -2,11 +2,10 @@ from pvlib.iotools.tmy import read_tmy2, read_tmy3 # noqa: F401
2
2
  from pvlib.iotools.epw import read_epw, parse_epw # noqa: F401
3
3
  from pvlib.iotools.srml import read_srml # noqa: F401
4
4
  from pvlib.iotools.srml import read_srml_month_from_solardat # noqa: F401
5
+ from pvlib.iotools.srml import get_srml # noqa: F401
5
6
  from pvlib.iotools.surfrad import read_surfrad # noqa: F401
6
7
  from pvlib.iotools.midc import read_midc # noqa: F401
7
8
  from pvlib.iotools.midc import read_midc_raw_data_from_nrel # noqa: F401
8
- from pvlib.iotools.ecmwf_macc import read_ecmwf_macc # noqa: F401
9
- from pvlib.iotools.ecmwf_macc import get_ecmwf_macc # noqa: F401
10
9
  from pvlib.iotools.crn import read_crn # noqa: F401
11
10
  from pvlib.iotools.solrad import read_solrad # noqa: F401
12
11
  from pvlib.iotools.psm3 import get_psm3 # noqa: F401
@@ -15,9 +14,15 @@ from pvlib.iotools.psm3 import parse_psm3 # noqa: F401
15
14
  from pvlib.iotools.pvgis import get_pvgis_tmy, read_pvgis_tmy # noqa: F401
16
15
  from pvlib.iotools.pvgis import read_pvgis_hourly # noqa: F401
17
16
  from pvlib.iotools.pvgis import get_pvgis_hourly # noqa: F401
17
+ from pvlib.iotools.pvgis import get_pvgis_horizon # noqa: F401
18
18
  from pvlib.iotools.bsrn import get_bsrn # noqa: F401
19
19
  from pvlib.iotools.bsrn import read_bsrn # noqa: F401
20
20
  from pvlib.iotools.bsrn import parse_bsrn # noqa: F401
21
21
  from pvlib.iotools.sodapro import get_cams # noqa: F401
22
22
  from pvlib.iotools.sodapro import read_cams # noqa: F401
23
23
  from pvlib.iotools.sodapro import parse_cams # noqa: F401
24
+ from pvlib.iotools.acis import get_acis_prism # noqa: F401
25
+ from pvlib.iotools.acis import get_acis_nrcc # noqa: F401
26
+ from pvlib.iotools.acis import get_acis_mpe # noqa: F401
27
+ from pvlib.iotools.acis import get_acis_station_data # noqa: F401
28
+ from pvlib.iotools.acis import get_acis_available_stations # noqa: F401
pvlib/iotools/acis.py ADDED
@@ -0,0 +1,516 @@
1
+ import requests
2
+ import pandas as pd
3
+ import numpy as np
4
+
5
+
6
+ VARIABLE_MAP = {
7
+ # time series names
8
+ 'pcpn': 'precipitation',
9
+ 'maxt': 'temp_air_max',
10
+ 'avgt': 'temp_air_average',
11
+ 'obst': 'temp_air_observation',
12
+ 'mint': 'temp_air_min',
13
+ 'cdd': 'cooling_degree_days',
14
+ 'hdd': 'heating_degree_days',
15
+ 'gdd': 'growing_degree_days',
16
+ 'snow': 'snowfall',
17
+ 'snwd': 'snowdepth',
18
+
19
+ # metadata names
20
+ 'lat': 'latitude',
21
+ 'lon': 'longitude',
22
+ 'elev': 'altitude',
23
+ }
24
+
25
+
26
+ def _get_acis(start, end, params, map_variables, url, **kwargs):
27
+ """
28
+ generic helper for the public get_acis_X functions
29
+ """
30
+ params = {
31
+ # use pd.to_datetime so that strings (e.g. '2021-01-01') are accepted
32
+ 'sdate': pd.to_datetime(start).strftime('%Y-%m-%d'),
33
+ 'edate': pd.to_datetime(end).strftime('%Y-%m-%d'),
34
+ 'output': 'json',
35
+ **params, # endpoint-specific parameters
36
+ }
37
+ response = requests.post(url,
38
+ json=params,
39
+ headers={"Content-Type": "application/json"},
40
+ **kwargs)
41
+ response.raise_for_status()
42
+ payload = response.json()
43
+
44
+ # somewhat inconveniently, the ACIS API tends to return errors as "valid"
45
+ # responses instead of using proper HTTP error codes:
46
+ if "error" in payload:
47
+ raise requests.HTTPError(payload['error'], response=response)
48
+
49
+ columns = ['date'] + [e['name'] for e in params['elems']]
50
+ df = pd.DataFrame(payload['data'], columns=columns)
51
+ df = df.set_index('date')
52
+ df.index = pd.to_datetime(df.index)
53
+ df.index.name = None
54
+
55
+ metadata = payload['meta']
56
+
57
+ try:
58
+ # for StnData endpoint, unpack combination "ll" into lat, lon
59
+ metadata['lon'], metadata['lat'] = metadata.pop('ll')
60
+ except KeyError:
61
+ pass
62
+
63
+ try:
64
+ metadata['elev'] = metadata['elev'] * 0.3048 # feet to meters
65
+ except KeyError:
66
+ # some queries don't return elevation
67
+ pass
68
+
69
+ if map_variables:
70
+ df = df.rename(columns=VARIABLE_MAP)
71
+
72
+ for key in list(metadata.keys()):
73
+ if key in VARIABLE_MAP:
74
+ metadata[VARIABLE_MAP[key]] = metadata.pop(key)
75
+
76
+ return df, metadata
77
+
78
+
79
+ def get_acis_prism(latitude, longitude, start, end, map_variables=True,
80
+ url="https://data.rcc-acis.org/GridData", **kwargs):
81
+ """
82
+ Retrieve estimated daily precipitation and temperature data from PRISM
83
+ via the Applied Climate Information System (ACIS).
84
+
85
+ ACIS [2]_, [3]_ aggregates and provides access to climate data
86
+ from many underlying sources. This function retrieves daily data from
87
+ the Parameter-elevation Regressions on Independent Slopes Model
88
+ (PRISM) [1]_, a gridded precipitation and temperature model
89
+ from Oregon State University.
90
+
91
+ Geographical coverage: US, Central America, and part of South America.
92
+ Approximately 0° to 50° in latitude and -130° to -65° in longitude.
93
+
94
+ Parameters
95
+ ----------
96
+ latitude : float
97
+ in decimal degrees, between -90 and 90, north is positive
98
+ longitude : float
99
+ in decimal degrees, between -180 and 180, east is positive
100
+ start : datetime-like
101
+ First day of the requested period
102
+ end : datetime-like
103
+ Last day of the requested period
104
+ map_variables : bool, default True
105
+ When True, rename data columns and metadata keys to pvlib variable
106
+ names where applicable. See variable :const:`VARIABLE_MAP`.
107
+ url : str, default: 'https://data.rcc-acis.org/GridData'
108
+ API endpoint URL
109
+ kwargs:
110
+ Optional parameters passed to ``requests.post``.
111
+
112
+ Returns
113
+ -------
114
+ data : pandas.DataFrame
115
+ Daily precipitation [mm], temperature [Celsius], and degree day
116
+ [Celsius-days] data
117
+ metadata : dict
118
+ Metadata of the selected grid cell
119
+
120
+ Raises
121
+ ------
122
+ requests.HTTPError
123
+ A message from the ACIS server if the request is rejected
124
+
125
+ Notes
126
+ -----
127
+ PRISM data is aggregated from 12:00 to 12:00 UTC, meaning data labeled
128
+ May 26 reflects to the 24 hours ending at 7:00am Eastern Standard Time
129
+ on May 26.
130
+
131
+ References
132
+ ----------
133
+ .. [1] `PRISM <https://prism.oregonstate.edu/>`_
134
+ .. [2] `ACIS Gridded Data <http://www.rcc-acis.org/docs_gridded.html>`_
135
+ .. [3] `ACIS Web Services <http://www.rcc-acis.org/docs_webservices.html>`_
136
+
137
+ Examples
138
+ --------
139
+ >>> from pvlib.iotools import get_acis_prism
140
+ >>> df, meta = get_acis_prism(40, 80, '2020-01-01', '2020-12-31')
141
+ """
142
+ elems = [
143
+ {"name": "pcpn", "interval": "dly", "units": "mm"},
144
+ {"name": "maxt", "interval": "dly", "units": "degreeC"},
145
+ {"name": "mint", "interval": "dly", "units": "degreeC"},
146
+ {"name": "avgt", "interval": "dly", "units": "degreeC"},
147
+ {"name": "cdd", "interval": "dly", "units": "degreeC"},
148
+ {"name": "hdd", "interval": "dly", "units": "degreeC"},
149
+ {"name": "gdd", "interval": "dly", "units": "degreeC"},
150
+ ]
151
+ params = {
152
+ 'loc': f"{longitude},{latitude}",
153
+ 'grid': "21",
154
+ 'elems': elems,
155
+ 'meta': ["ll", "elev"],
156
+ }
157
+ df, meta = _get_acis(start, end, params, map_variables, url, **kwargs)
158
+ df = df.replace(-999, np.nan)
159
+ return df, meta
160
+
161
+
162
+ def get_acis_nrcc(latitude, longitude, start, end, grid, map_variables=True,
163
+ url="https://data.rcc-acis.org/GridData", **kwargs):
164
+ """
165
+ Retrieve estimated daily precipitation and temperature data from the
166
+ Northeast Regional Climate Center via the Applied Climate
167
+ Information System (ACIS).
168
+
169
+ ACIS [2]_, [3]_ aggregates and provides access to climate data
170
+ from many underlying sources. This function retrieves daily data from
171
+ Cornell's Northeast Regional Climate Center (NRCC) [1]_.
172
+
173
+ Geographical coverage: US, Central America, and part of South America.
174
+ Approximately 0° to 50° in latitude and -130° to -65° in longitude.
175
+
176
+ Parameters
177
+ ----------
178
+ latitude : float
179
+ in decimal degrees, between -90 and 90, north is positive
180
+ longitude : float
181
+ in decimal degrees, between -180 and 180, east is positive
182
+ start : datetime-like
183
+ First day of the requested period
184
+ end : datetime-like
185
+ Last day of the requested period
186
+ grid : int
187
+ Options are either 1 (for "NRCC Interpolated") or 3
188
+ (for "NRCC Hi-Resolution"). See [2]_ for details.
189
+ map_variables : bool, default True
190
+ When True, rename data columns and metadata keys to pvlib variable
191
+ names where applicable. See variable :const:`VARIABLE_MAP`.
192
+ url : str, default: 'https://data.rcc-acis.org/GridData'
193
+ API endpoint URL
194
+ kwargs:
195
+ Optional parameters passed to ``requests.post``.
196
+
197
+ Returns
198
+ -------
199
+ data : pandas.DataFrame
200
+ Daily precipitation [mm], temperature [Celsius], and degree day
201
+ [Celsius-days] data
202
+ metadata : dict
203
+ Metadata of the selected grid cell
204
+
205
+ Raises
206
+ ------
207
+ requests.HTTPError
208
+ A message from the ACIS server if the request is rejected
209
+
210
+ Notes
211
+ -----
212
+ The returned values are 24-hour aggregates, but
213
+ the aggregation period may not be midnight to midnight in local time.
214
+ Check the ACIS and NRCC documentation for details.
215
+
216
+ References
217
+ ----------
218
+ .. [1] `NRCC <http://www.nrcc.cornell.edu/>`_
219
+ .. [2] `ACIS Gridded Data <http://www.rcc-acis.org/docs_gridded.html>`_
220
+ .. [3] `ACIS Web Services <http://www.rcc-acis.org/docs_webservices.html>`_
221
+
222
+ Examples
223
+ --------
224
+ >>> from pvlib.iotools import get_acis_nrcc
225
+ >>> df, meta = get_acis_nrcc(40, -80, '2020-01-01', '2020-12-31', grid=1)
226
+ """
227
+ elems = [
228
+ {"name": "pcpn", "interval": "dly", "units": "mm"},
229
+ {"name": "maxt", "interval": "dly", "units": "degreeC"},
230
+ {"name": "mint", "interval": "dly", "units": "degreeC"},
231
+ {"name": "avgt", "interval": "dly", "units": "degreeC"},
232
+ {"name": "cdd", "interval": "dly", "units": "degreeC"},
233
+ {"name": "hdd", "interval": "dly", "units": "degreeC"},
234
+ {"name": "gdd", "interval": "dly", "units": "degreeC"},
235
+ ]
236
+ params = {
237
+ 'loc': f"{longitude},{latitude}",
238
+ 'grid': grid,
239
+ 'elems': elems,
240
+ 'meta': ["ll", "elev"],
241
+ }
242
+ df, meta = _get_acis(start, end, params, map_variables, url, **kwargs)
243
+ df = df.replace(-999, np.nan)
244
+ return df, meta
245
+
246
+
247
+
248
+ def get_acis_mpe(latitude, longitude, start, end, map_variables=True,
249
+ url="https://data.rcc-acis.org/GridData", **kwargs):
250
+ """
251
+ Retrieve estimated daily Multi-sensor Precipitation Estimates
252
+ via the Applied Climate Information System (ACIS).
253
+
254
+ ACIS [2]_, [3]_ aggregates and provides access to climate data
255
+ from many underlying sources. This function retrieves daily data from
256
+ the National Weather Service's Multi-sensor Precipitation Estimates
257
+ (MPE) [1]_, a gridded precipitation model.
258
+
259
+ This dataset covers the contiguous United States, Mexico, and parts of
260
+ Central America.
261
+
262
+ Parameters
263
+ ----------
264
+ latitude : float
265
+ in decimal degrees, between -90 and 90, north is positive
266
+ longitude : float
267
+ in decimal degrees, between -180 and 180, east is positive
268
+ start : datetime-like
269
+ First day of the requested period
270
+ end : datetime-like
271
+ Last day of the requested period
272
+ map_variables : bool, default True
273
+ When True, rename data columns and metadata keys to pvlib variable
274
+ names where applicable. See variable :const:`VARIABLE_MAP`.
275
+ url : str, default: 'https://data.rcc-acis.org/GridData'
276
+ API endpoint URL
277
+ kwargs:
278
+ Optional parameters passed to ``requests.post``.
279
+
280
+ Returns
281
+ -------
282
+ data : pandas.DataFrame
283
+ Daily precipitation [mm] data
284
+ metadata : dict
285
+ Coordinates of the selected grid cell
286
+
287
+ Raises
288
+ ------
289
+ requests.HTTPError
290
+ A message from the ACIS server if the request is rejected
291
+
292
+ Notes
293
+ -----
294
+ The returned values are 24-hour aggregates, but
295
+ the aggregation period may not be midnight to midnight in local time.
296
+ Check the ACIS and MPE documentation for details.
297
+
298
+ References
299
+ ----------
300
+ .. [1] `Multisensor Precipitation Estimates
301
+ <https://www.weather.gov/marfc/Multisensor_Precipitation>`_
302
+ .. [2] `ACIS Gridded Data <http://www.rcc-acis.org/docs_gridded.html>`_
303
+ .. [3] `ACIS Web Services <http://www.rcc-acis.org/docs_webservices.html>`_
304
+
305
+ Examples
306
+ --------
307
+ >>> from pvlib.iotools import get_acis_mpe
308
+ >>> df, meta = get_acis_mpe(40, -80, '2020-01-01', '2020-12-31')
309
+ """
310
+ elems = [
311
+ # only precipitation is supported in this dataset
312
+ {"name": "pcpn", "interval": "dly", "units": "mm"},
313
+ ]
314
+ params = {
315
+ 'loc': f"{longitude},{latitude}",
316
+ 'grid': "2",
317
+ 'elems': elems,
318
+ 'meta': ["ll"], # "elev" is not supported for this dataset
319
+ }
320
+ df, meta = _get_acis(start, end, params, map_variables, url, **kwargs)
321
+ df = df.replace(-999, np.nan)
322
+ return df, meta
323
+
324
+
325
+ def get_acis_station_data(station, start, end, trace_val=0.001,
326
+ map_variables=True,
327
+ url="https://data.rcc-acis.org/StnData", **kwargs):
328
+ """
329
+ Retrieve weather station climate records via the Applied Climate
330
+ Information System (ACIS).
331
+
332
+ ACIS [1]_, [2]_ aggregates and provides access to climate data
333
+ from many underlying sources. This function retrieves measurements
334
+ from ground stations belonging to various global networks.
335
+
336
+ This function can query data from stations all over the world.
337
+ The stations available in a given area can be listed using
338
+ :py:func:`get_acis_available_stations`.
339
+
340
+ Parameters
341
+ ----------
342
+ station : str
343
+ Identifier code for the station to query. Identifiers from many
344
+ station networks are accepted, including WBAN, COOP, FAA, WMO, GHCN,
345
+ and others. See [1]_ and [2]_ for details.
346
+ start : datetime-like
347
+ First day of the requested period
348
+ end : datetime-like
349
+ Last day of the requested period
350
+ map_variables : bool, default True
351
+ When True, rename data columns and metadata keys to pvlib variable
352
+ names where applicable. See variable :const:`VARIABLE_MAP`.
353
+ trace_val : float, default 0.001
354
+ Value to replace "trace" values in the precipitation data
355
+ url : str, default: 'https://data.rcc-acis.org/GridData'
356
+ API endpoint URL
357
+ kwargs:
358
+ Optional parameters passed to ``requests.post``.
359
+
360
+ Returns
361
+ -------
362
+ data : pandas.DataFrame
363
+ Daily precipitation [mm], temperature [Celsius], snow [mm], and
364
+ degree day [Celsius-days] data
365
+ metadata : dict
366
+ station metadata
367
+
368
+ Raises
369
+ ------
370
+ requests.HTTPError
371
+ A message from the ACIS server if the request is rejected
372
+
373
+ See Also
374
+ --------
375
+ get_acis_available_stations
376
+
377
+ References
378
+ ----------
379
+ .. [1] `ACIS Web Services <http://www.rcc-acis.org/docs_webservices.html>`_
380
+ .. [2] `ACIS Metadata <http://www.rcc-acis.org/docs_metadata.html>`_
381
+
382
+ Examples
383
+ --------
384
+ >>> # Using an FAA code (Chicago O'Hare airport)
385
+ >>> from pvlib.iotools import get_acis_station_data
386
+ >>> df, meta = get_acis_station_data('ORD', '2020-01-01', '2020-12-31')
387
+ >>>
388
+ >>> # Look up available stations in a lat/lon rectangle, with data
389
+ >>> # available in the specified date range:
390
+ >>> from pvlib.iotools import get_acis_available_stations
391
+ >>> stations = get_acis_available_stations([39.5, 40.5], [-80.5, -79.5],
392
+ ... '2020-01-01', '2020-01-03')
393
+ >>> stations['sids'][0]
394
+ ['369367 2', 'USC00369367 6', 'WYNP1 7']
395
+ >>> df, meta = get_acis_station_data('369367', '2020-01-01', '2020-01-03')
396
+ """
397
+ elems = [
398
+ {"name": "maxt", "interval": "dly", "units": "degreeC"},
399
+ {"name": "mint", "interval": "dly", "units": "degreeC"},
400
+ {"name": "avgt", "interval": "dly", "units": "degreeC"},
401
+ {"name": "obst", "interval": "dly", "units": "degreeC"},
402
+ {"name": "pcpn", "interval": "dly", "units": "mm"},
403
+ {"name": "snow", "interval": "dly", "units": "cm"},
404
+ {"name": "snwd", "interval": "dly", "units": "cm"},
405
+ {"name": "cdd", "interval": "dly", "units": "degreeC"},
406
+ {"name": "hdd", "interval": "dly", "units": "degreeC"},
407
+ {"name": "gdd", "interval": "dly", "units": "degreeC"},
408
+ ]
409
+ params = {
410
+ 'sid': str(station),
411
+ 'elems': elems,
412
+ 'meta': ('name,state,sids,sid_dates,ll,elev,uid,county,'
413
+ 'climdiv,valid_daterange,tzo,network')
414
+ }
415
+ df, metadata = _get_acis(start, end, params, map_variables, url, **kwargs)
416
+ df = df.replace("M", np.nan)
417
+ df = df.replace("T", trace_val)
418
+ df = df.astype(float)
419
+ return df, metadata
420
+
421
+
422
+ def get_acis_available_stations(latitude_range, longitude_range,
423
+ start=None, end=None,
424
+ url="https://data.rcc-acis.org/StnMeta",
425
+ **kwargs):
426
+ """
427
+ List weather stations in a given area available from the
428
+ Applied Climate Information System (ACIS).
429
+
430
+ The ``sids`` returned by this function can be used with
431
+ :py:func:`get_acis_station_data` to retrieve weather measurements
432
+ from the station.
433
+
434
+ Parameters
435
+ ----------
436
+ latitude_range : list
437
+ A 2-element list of [southern bound, northern bound]
438
+ in decimal degrees, between -90 and 90, north is positive
439
+ longitude_range : list
440
+ A 2-element list of [western bound, eastern bound]
441
+ in decimal degrees, between -180 and 180, east is positive
442
+ start : datetime-like, optional
443
+ If specified, return only stations that have data between ``start`` and
444
+ ``end``. If not specified, all stations in the region are returned.
445
+ end : datetime-like, optional
446
+ See ``start``
447
+ url : str, default: 'https://data.rcc-acis.org/StnMeta'
448
+ API endpoint URL
449
+ kwargs:
450
+ Optional parameters passed to ``requests.post``.
451
+
452
+ Returns
453
+ -------
454
+ stations : pandas.DataFrame
455
+ A dataframe of station metadata, one row per station.
456
+ The ``sids`` column contains IDs that can be used with
457
+ :py:func:`get_acis_station_data`.
458
+
459
+ Raises
460
+ ------
461
+ requests.HTTPError
462
+ A message from the ACIS server if the request is rejected
463
+
464
+ See Also
465
+ --------
466
+ get_acis_station_data
467
+
468
+ References
469
+ ----------
470
+ .. [1] `ACIS Web Services <http://www.rcc-acis.org/docs_webservices.html>`_
471
+ .. [2] `ACIS Metadata <http://www.rcc-acis.org/docs_metadata.html>`_
472
+
473
+ Examples
474
+ --------
475
+ >>> # Look up available stations in a lat/lon rectangle, with data
476
+ >>> # available in the specified date range:
477
+ >>> from pvlib.iotools import get_acis_available_stations
478
+ >>> stations = get_acis_available_stations([39.5, 40.5], [-80.5, -79.5],
479
+ ... '2020-01-01', '2020-01-03')
480
+ >>> stations['sids'][0]
481
+ ['369367 2', 'USC00369367 6', 'WYNP1 7']
482
+ """
483
+ bbox = "{},{},{},{}".format(
484
+ longitude_range[0],
485
+ latitude_range[0],
486
+ longitude_range[1],
487
+ latitude_range[1],
488
+ )
489
+ params = {
490
+ "bbox": bbox,
491
+ "meta": ("name,state,sids,sid_dates,ll,elev,"
492
+ "uid,county,climdiv,tzo,network"),
493
+ }
494
+ if start is not None and end is not None:
495
+ params['elems'] = ['maxt', 'mint', 'avgt', 'obst',
496
+ 'pcpn', 'snow', 'snwd']
497
+ params['sdate'] = pd.to_datetime(start).strftime('%Y-%m-%d')
498
+ params['edate'] = pd.to_datetime(end).strftime('%Y-%m-%d')
499
+
500
+ response = requests.post(url,
501
+ json=params,
502
+ headers={"Content-Type": "application/json"},
503
+ **kwargs)
504
+ response.raise_for_status()
505
+ payload = response.json()
506
+ if "error" in payload:
507
+ raise requests.HTTPError(payload['error'], response=response)
508
+
509
+ metadata = payload['meta']
510
+ for station_record in metadata:
511
+ station_record['altitude'] = station_record.pop('elev')
512
+ station_record['longitude'], station_record['latitude'] = \
513
+ station_record.pop('ll')
514
+
515
+ df = pd.DataFrame(metadata)
516
+ return df