pvlib 0.11.1__py3-none-any.whl → 0.12.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 (149) hide show
  1. pvlib/__init__.py +1 -0
  2. pvlib/_deprecation.py +73 -0
  3. pvlib/atmosphere.py +77 -7
  4. pvlib/bifacial/infinite_sheds.py +4 -3
  5. pvlib/bifacial/utils.py +2 -1
  6. pvlib/clearsky.py +35 -22
  7. pvlib/iam.py +4 -4
  8. pvlib/iotools/midc.py +1 -1
  9. pvlib/iotools/psm3.py +1 -1
  10. pvlib/iotools/pvgis.py +10 -12
  11. pvlib/iotools/tmy.py +3 -69
  12. pvlib/irradiance.py +112 -55
  13. pvlib/ivtools/sdm.py +75 -52
  14. pvlib/location.py +73 -33
  15. pvlib/modelchain.py +18 -35
  16. pvlib/pvsystem.py +139 -94
  17. pvlib/snow.py +64 -28
  18. pvlib/solarposition.py +46 -30
  19. pvlib/spa.py +4 -2
  20. pvlib/spectrum/__init__.py +0 -1
  21. pvlib/spectrum/irradiance.py +2 -64
  22. pvlib/spectrum/mismatch.py +3 -3
  23. pvlib/spectrum/spectrl2.py +2 -1
  24. pvlib/temperature.py +49 -3
  25. pvlib/tools.py +6 -5
  26. {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info}/METADATA +14 -11
  27. pvlib-0.12.0.dist-info/RECORD +75 -0
  28. {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info}/WHEEL +1 -1
  29. pvlib/data/BIRD_08_16_2012.csv +0 -8761
  30. pvlib/data/BIRD_08_16_2012_patm.csv +0 -8761
  31. pvlib/data/Burlington, United States SolarAnywhere Time Series 2021 Lat_44_465 Lon_-73_205 TMY3 format.csv +0 -8762
  32. pvlib/data/Burlington, United States SolarAnywhere Time Series 20210101 to 20210103 Lat_44_4675 Lon_-73_2075 SA format.csv +0 -578
  33. pvlib/data/Burlington, United States SolarAnywhere Typical GHI Year Lat_44_465 Lon_-73_205 SA format.csv +0 -74
  34. pvlib/data/CPS SCH275KTL-DO-US-800-250kW_275kVA_1.OND +0 -146
  35. pvlib/data/CRNS0101-05-2019-AZ_Tucson_11_W.txt +0 -4
  36. pvlib/data/CRN_with_problems.txt +0 -3
  37. pvlib/data/ET-M772BH550GL.PAN +0 -75
  38. pvlib/data/NLD_Amsterdam062400_IWEC.epw +0 -8768
  39. pvlib/data/PVsyst_demo.csv +0 -10757
  40. pvlib/data/PVsyst_demo_model.csv +0 -3588
  41. pvlib/data/SRML-day-EUPO1801.txt +0 -1441
  42. pvlib/data/abq19056.dat +0 -6
  43. pvlib/data/aod550_tcwv_20121101_test.nc +0 -0
  44. pvlib/data/bishop88_numerical_precision.csv +0 -101
  45. pvlib/data/bsrn-lr0100-pay0616.dat +0 -86901
  46. pvlib/data/bsrn-pay0616.dat.gz +0 -0
  47. pvlib/data/cams_mcclear_1min_verbose.csv +0 -60
  48. pvlib/data/cams_mcclear_monthly.csv +0 -42
  49. pvlib/data/cams_radiation_1min_verbose.csv +0 -72
  50. pvlib/data/cams_radiation_monthly.csv +0 -47
  51. pvlib/data/detect_clearsky_data.csv +0 -35
  52. pvlib/data/detect_clearsky_threshold_data.csv +0 -126
  53. pvlib/data/greensboro_kimber_soil_manwash.dat +0 -8761
  54. pvlib/data/greensboro_kimber_soil_nowash.dat +0 -8761
  55. pvlib/data/inverter_fit_snl_meas.csv +0 -127
  56. pvlib/data/inverter_fit_snl_sim.csv +0 -19
  57. pvlib/data/ivtools_numdiff.csv +0 -52
  58. pvlib/data/midc_20181014.txt +0 -1441
  59. pvlib/data/midc_raw_20181018.txt +0 -1441
  60. pvlib/data/midc_raw_short_header_20191115.txt +0 -1441
  61. pvlib/data/msn19056.dat +0 -6
  62. pvlib/data/precise_iv_curves1.json +0 -10251
  63. pvlib/data/precise_iv_curves2.json +0 -10251
  64. pvlib/data/precise_iv_curves_parameter_sets1.csv +0 -33
  65. pvlib/data/precise_iv_curves_parameter_sets2.csv +0 -33
  66. pvlib/data/pvgis_hourly_Timeseries_45.000_8.000_SA2_10kWp_CIS_5_2a_2013_2014.json +0 -1
  67. pvlib/data/pvgis_hourly_Timeseries_45.000_8.000_SA_30deg_0deg_2016_2016.csv +0 -35
  68. pvlib/data/pvgis_tmy_meta.json +0 -32
  69. pvlib/data/pvgis_tmy_test.dat +0 -8761
  70. pvlib/data/pvwatts_8760_rackmount.csv +0 -8779
  71. pvlib/data/pvwatts_8760_roofmount.csv +0 -8779
  72. pvlib/data/singleaxis_tracker_wslope.csv +0 -8761
  73. pvlib/data/spectrl2_example_spectra.csv +0 -123
  74. pvlib/data/surfrad-slv16001.dat +0 -1442
  75. pvlib/data/test_psm3_2017.csv +0 -17521
  76. pvlib/data/test_psm3_2019_5min.csv +0 -289
  77. pvlib/data/test_psm3_tmy-2017.csv +0 -8761
  78. pvlib/data/test_read_psm3.csv +0 -17523
  79. pvlib/data/test_read_pvgis_horizon.csv +0 -49
  80. pvlib/data/tmy_45.000_8.000_2005_2020.csv +0 -8789
  81. pvlib/data/tmy_45.000_8.000_2005_2020.epw +0 -8768
  82. pvlib/data/tmy_45.000_8.000_2005_2020.json +0 -1
  83. pvlib/data/tmy_45.000_8.000_2005_2020.txt +0 -8761
  84. pvlib/data/tmy_45.000_8.000_userhorizon.json +0 -1
  85. pvlib/data/variables_style_rules.csv +0 -56
  86. pvlib/spa_c_files/README.md +0 -81
  87. pvlib/spa_c_files/cspa_py.pxd +0 -43
  88. pvlib/spa_c_files/spa_py.pyx +0 -30
  89. pvlib/tests/__init__.py +0 -0
  90. pvlib/tests/bifacial/__init__.py +0 -0
  91. pvlib/tests/bifacial/test_infinite_sheds.py +0 -317
  92. pvlib/tests/bifacial/test_losses_models.py +0 -54
  93. pvlib/tests/bifacial/test_pvfactors.py +0 -82
  94. pvlib/tests/bifacial/test_utils.py +0 -192
  95. pvlib/tests/conftest.py +0 -476
  96. pvlib/tests/iotools/__init__.py +0 -0
  97. pvlib/tests/iotools/test_acis.py +0 -213
  98. pvlib/tests/iotools/test_bsrn.py +0 -131
  99. pvlib/tests/iotools/test_crn.py +0 -95
  100. pvlib/tests/iotools/test_epw.py +0 -23
  101. pvlib/tests/iotools/test_midc.py +0 -89
  102. pvlib/tests/iotools/test_panond.py +0 -32
  103. pvlib/tests/iotools/test_psm3.py +0 -198
  104. pvlib/tests/iotools/test_pvgis.py +0 -644
  105. pvlib/tests/iotools/test_sodapro.py +0 -298
  106. pvlib/tests/iotools/test_solaranywhere.py +0 -287
  107. pvlib/tests/iotools/test_solargis.py +0 -68
  108. pvlib/tests/iotools/test_solcast.py +0 -324
  109. pvlib/tests/iotools/test_solrad.py +0 -152
  110. pvlib/tests/iotools/test_srml.py +0 -124
  111. pvlib/tests/iotools/test_surfrad.py +0 -75
  112. pvlib/tests/iotools/test_tmy.py +0 -133
  113. pvlib/tests/ivtools/__init__.py +0 -0
  114. pvlib/tests/ivtools/test_sde.py +0 -230
  115. pvlib/tests/ivtools/test_sdm.py +0 -407
  116. pvlib/tests/ivtools/test_utils.py +0 -173
  117. pvlib/tests/spectrum/__init__.py +0 -0
  118. pvlib/tests/spectrum/conftest.py +0 -40
  119. pvlib/tests/spectrum/test_irradiance.py +0 -138
  120. pvlib/tests/spectrum/test_mismatch.py +0 -304
  121. pvlib/tests/spectrum/test_response.py +0 -124
  122. pvlib/tests/spectrum/test_spectrl2.py +0 -72
  123. pvlib/tests/test_albedo.py +0 -84
  124. pvlib/tests/test_atmosphere.py +0 -204
  125. pvlib/tests/test_clearsky.py +0 -878
  126. pvlib/tests/test_conftest.py +0 -81
  127. pvlib/tests/test_iam.py +0 -555
  128. pvlib/tests/test_inverter.py +0 -213
  129. pvlib/tests/test_irradiance.py +0 -1441
  130. pvlib/tests/test_location.py +0 -356
  131. pvlib/tests/test_modelchain.py +0 -2020
  132. pvlib/tests/test_numerical_precision.py +0 -124
  133. pvlib/tests/test_pvarray.py +0 -71
  134. pvlib/tests/test_pvsystem.py +0 -2495
  135. pvlib/tests/test_scaling.py +0 -207
  136. pvlib/tests/test_shading.py +0 -391
  137. pvlib/tests/test_singlediode.py +0 -608
  138. pvlib/tests/test_snow.py +0 -212
  139. pvlib/tests/test_soiling.py +0 -230
  140. pvlib/tests/test_solarposition.py +0 -933
  141. pvlib/tests/test_spa.py +0 -425
  142. pvlib/tests/test_temperature.py +0 -470
  143. pvlib/tests/test_tools.py +0 -146
  144. pvlib/tests/test_tracking.py +0 -474
  145. pvlib/tests/test_transformer.py +0 -60
  146. pvlib-0.11.1.dist-info/RECORD +0 -192
  147. {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info/licenses}/AUTHORS.md +0 -0
  148. {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info/licenses}/LICENSE +0 -0
  149. {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info}/top_level.txt +0 -0
@@ -1,644 +0,0 @@
1
- """
2
- test the pvgis IO tools
3
- """
4
- import json
5
- import numpy as np
6
- import pandas as pd
7
- import io
8
- import pytest
9
- import requests
10
- from pvlib.iotools import get_pvgis_tmy, read_pvgis_tmy
11
- from pvlib.iotools import get_pvgis_hourly, read_pvgis_hourly
12
- from pvlib.iotools import get_pvgis_horizon
13
- from ..conftest import (DATA_DIR, RERUNS, RERUNS_DELAY, assert_frame_equal,
14
- assert_series_equal)
15
-
16
-
17
- # PVGIS Hourly tests
18
- # The test files are actual files from PVGIS where the data section have been
19
- # reduced to only a few lines
20
- testfile_radiation_csv = DATA_DIR / \
21
- 'pvgis_hourly_Timeseries_45.000_8.000_SA_30deg_0deg_2016_2016.csv'
22
- testfile_pv_json = DATA_DIR / \
23
- 'pvgis_hourly_Timeseries_45.000_8.000_SA2_10kWp_CIS_5_2a_2013_2014.json'
24
-
25
- index_radiation_csv = \
26
- pd.date_range('20160101 00:10', freq='1h', periods=14, tz='UTC')
27
- index_pv_json = \
28
- pd.date_range('2013-01-01 00:10', freq='1h', periods=10, tz='UTC')
29
-
30
- columns_radiation_csv = [
31
- 'Gb(i)', 'Gd(i)', 'Gr(i)', 'H_sun', 'T2m', 'WS10m', 'Int']
32
- columns_radiation_csv_mapped = [
33
- 'poa_direct', 'poa_sky_diffuse', 'poa_ground_diffuse', 'solar_elevation',
34
- 'temp_air', 'wind_speed', 'Int']
35
- columns_pv_json = [
36
- 'P', 'G(i)', 'H_sun', 'T2m', 'WS10m', 'Int']
37
- columns_pv_json_mapped = [
38
- 'P', 'poa_global', 'solar_elevation', 'temp_air', 'wind_speed', 'Int']
39
-
40
- data_radiation_csv = [
41
- [0.0, 0.0, 0.0, 0.0, 3.44, 1.43, 0.0],
42
- [0.0, 0.0, 0.0, 0.0, 2.94, 1.47, 0.0],
43
- [0.0, 0.0, 0.0, 0.0, 2.43, 1.51, 0.0],
44
- [0.0, 0.0, 0.0, 0.0, 1.93, 1.54, 0.0],
45
- [0.0, 0.0, 0.0, 0.0, 2.03, 1.62, 0.0],
46
- [0.0, 0.0, 0.0, 0.0, 2.14, 1.69, 0.0],
47
- [0.0, 0.0, 0.0, 0.0, 2.25, 1.77, 0.0],
48
- [0.0, 0.0, 0.0, 0.0, 3.06, 1.49, 0.0],
49
- [26.71, 8.28, 0.21, 8.06, 3.87, 1.22, 1.0],
50
- [14.69, 5.76, 0.16, 14.8, 4.67, 0.95, 1.0],
51
- [2.19, 0.94, 0.03, 19.54, 5.73, 0.77, 1.0],
52
- [2.11, 0.94, 0.03, 21.82, 6.79, 0.58, 1.0],
53
- [4.25, 1.88, 0.05, 21.41, 7.84, 0.4, 1.0],
54
- [0.0, 0.0, 0.0, 0.0, 7.43, 0.72, 0.0]]
55
- data_pv_json = [
56
- [0.0, 0.0, 0.0, -0.97, 1.52, 0.0],
57
- [0.0, 0.0, 0.0, -1.06, 1.45, 0.0],
58
- [0.0, 0.0, 0.0, -1.03, 1.45, 0.0],
59
- [0.0, 0.0, 0.0, -0.48, 1.31, 0.0],
60
- [0.0, 0.0, 0.0, -0.09, 1.24, 0.0],
61
- [0.0, 0.0, 0.0, -0.38, 1.17, 0.0],
62
- [0.0, 0.0, 0.0, 0.29, 1.03, 0.0],
63
- [0.0, 0.0, 0.0, 1.0, 0.62, 0.0],
64
- [1187.2, 129.59, 8.06, 0.97, 0.97, 0.0],
65
- [3950.1, 423.28, 14.8, 1.89, 0.69, 0.0]]
66
-
67
- inputs_radiation_csv = {'latitude': 45.0, 'longitude': 8.0, 'elevation': 250.0,
68
- 'radiation_database': 'PVGIS-SARAH',
69
- 'Slope': '30 deg.', 'Azimuth': '0 deg.'}
70
-
71
- metadata_radiation_csv = {
72
- 'Gb(i)': 'Beam (direct) irradiance on the inclined plane (plane of the array) (W/m2)', # noqa: E501
73
- 'Gd(i)': 'Diffuse irradiance on the inclined plane (plane of the array) (W/m2)', # noqa: E501
74
- 'Gr(i)': 'Reflected irradiance on the inclined plane (plane of the array) (W/m2)', # noqa: E501
75
- 'H_sun': 'Sun height (degree)',
76
- 'T2m': '2-m air temperature (degree Celsius)',
77
- 'WS10m': '10-m total wind speed (m/s)',
78
- 'Int': '1 means solar radiation values are reconstructed'}
79
-
80
- inputs_pv_json = {
81
- 'location': {'latitude': 45.0, 'longitude': 8.0, 'elevation': 250.0},
82
- 'meteo_data': {'radiation_db': 'PVGIS-SARAH2', 'meteo_db': 'ERA-Interim',
83
- 'year_min': 2013, 'year_max': 2014, 'use_horizon': True,
84
- 'horizon_db': None, 'horizon_data': 'DEM-calculated'},
85
- 'mounting_system': {'two_axis': {
86
- 'slope': {'value': '-', 'optimal': '-'},
87
- 'azimuth': {'value': '-', 'optimal': '-'}}},
88
- 'pv_module': {'technology': 'CIS', 'peak_power': 10.0, 'system_loss': 5.0}}
89
-
90
-
91
- metadata_pv_json = {
92
- 'inputs': {
93
- 'location':
94
- {'description': 'Selected location', 'variables': {
95
- 'latitude': {'description': 'Latitude', 'units': 'decimal degree'}, # noqa: E501
96
- 'longitude': {'description': 'Longitude', 'units': 'decimal degree'}, # noqa: E501
97
- 'elevation': {'description': 'Elevation', 'units': 'm'}}},
98
- 'meteo_data': {
99
- 'description': 'Sources of meteorological data',
100
- 'variables': {
101
- 'radiation_db': {'description': 'Solar radiation database'}, # noqa: E501
102
- 'meteo_db': {'description': 'Database used for meteorological variables other than solar radiation'}, # noqa: E501
103
- 'year_min': {'description': 'First year of the calculations'}, # noqa: E501
104
- 'year_max': {'description': 'Last year of the calculations'}, # noqa: E501
105
- 'use_horizon': {'description': 'Include horizon shadows'},
106
- 'horizon_db': {'description': 'Source of horizon data'}}},
107
- 'mounting_system': {
108
- 'description': 'Mounting system',
109
- 'choices': 'fixed, vertical_axis, inclined_axis, two_axis',
110
- 'fields': {
111
- 'slope': {'description': 'Inclination angle from the horizontal plane', 'units': 'degree'}, # noqa: E501
112
- 'azimuth': {'description': 'Orientation (azimuth) angle of the (fixed) PV system (0 = S, 90 = W, -90 = E)', 'units': 'degree'}}}, # noqa: E501
113
- 'pv_module': {
114
- 'description': 'PV module parameters',
115
- 'variables': {
116
- 'technology': {'description': 'PV technology'},
117
- 'peak_power': {'description': 'Nominal (peak) power of the PV module', 'units': 'kW'}, # noqa: E501
118
- 'system_loss': {'description': 'Sum of system losses', 'units': '%'}}}}, # noqa: E501
119
- 'outputs': {
120
- 'hourly': {
121
- 'type': 'time series', 'timestamp': 'hourly averages',
122
- 'variables': {
123
- 'P': {'description': 'PV system power', 'units': 'W'},
124
- 'G(i)': {'description': 'Global irradiance on the inclined plane (plane of the array)', 'units': 'W/m2'}, # noqa: E501
125
- 'H_sun': {'description': 'Sun height', 'units': 'degree'},
126
- 'T2m': {'description': '2-m air temperature', 'units': 'degree Celsius'}, # noqa: E501
127
- 'WS10m': {'description': '10-m total wind speed', 'units': 'm/s'}, # noqa: E501
128
- 'Int': {'description': '1 means solar radiation values are reconstructed'}}}}} # noqa: E501
129
-
130
-
131
- def generate_expected_dataframe(values, columns, index):
132
- """Create dataframe from arrays of values, columns and index, in order to
133
- use this dataframe to compare to.
134
- """
135
- expected = pd.DataFrame(index=index, data=values, columns=columns)
136
- expected['Int'] = expected['Int'].astype(int)
137
- expected.index.name = 'time'
138
- expected.index.freq = None
139
- return expected
140
-
141
-
142
- @pytest.fixture
143
- def expected_radiation_csv():
144
- expected = generate_expected_dataframe(
145
- data_radiation_csv, columns_radiation_csv, index_radiation_csv)
146
- return expected
147
-
148
-
149
- @pytest.fixture
150
- def expected_radiation_csv_mapped():
151
- expected = generate_expected_dataframe(
152
- data_radiation_csv, columns_radiation_csv_mapped, index_radiation_csv)
153
- return expected
154
-
155
-
156
- @pytest.fixture
157
- def expected_pv_json():
158
- expected = generate_expected_dataframe(
159
- data_pv_json, columns_pv_json, index_pv_json)
160
- return expected
161
-
162
-
163
- @pytest.fixture
164
- def expected_pv_json_mapped():
165
- expected = generate_expected_dataframe(
166
- data_pv_json, columns_pv_json_mapped, index_pv_json)
167
- return expected
168
-
169
-
170
- # Test read_pvgis_hourly function using two different files with different
171
- # input arguments (to test variable mapping and pvgis_format)
172
- # pytest request.getfixturevalue is used to simplify the input arguments
173
- @pytest.mark.parametrize('testfile,expected_name,metadata_exp,inputs_exp,map_variables,pvgis_format', [ # noqa: E501
174
- (testfile_radiation_csv, 'expected_radiation_csv', metadata_radiation_csv,
175
- inputs_radiation_csv, False, None),
176
- (testfile_radiation_csv, 'expected_radiation_csv_mapped',
177
- metadata_radiation_csv, inputs_radiation_csv, True, 'csv'),
178
- (testfile_pv_json, 'expected_pv_json', metadata_pv_json, inputs_pv_json,
179
- False, None),
180
- (testfile_pv_json, 'expected_pv_json_mapped', metadata_pv_json,
181
- inputs_pv_json, True, 'json')])
182
- def test_read_pvgis_hourly(testfile, expected_name, metadata_exp,
183
- inputs_exp, map_variables, pvgis_format, request):
184
- # Get expected dataframe from fixture
185
- expected = request.getfixturevalue(expected_name)
186
- # Read data from file
187
- out, inputs, metadata = read_pvgis_hourly(
188
- testfile, map_variables=map_variables, pvgis_format=pvgis_format)
189
- # Assert whether dataframe, metadata, and inputs are as expected
190
- assert_frame_equal(out, expected)
191
- assert inputs == inputs_exp
192
- assert metadata == metadata_exp
193
-
194
-
195
- def test_read_pvgis_hourly_bad_extension():
196
- # Test if ValueError is raised if file extension cannot be recognized and
197
- # pvgis_format is not specified
198
- with pytest.raises(ValueError, match="pvgis format 'txt' was unknown"):
199
- read_pvgis_hourly('filename.txt')
200
- # Test if ValueError is raised if an unkonwn pvgis_format is specified
201
- with pytest.raises(ValueError, match="pvgis format 'txt' was unknown"):
202
- read_pvgis_hourly(testfile_pv_json, pvgis_format='txt')
203
- # Test if TypeError is raised if input is a buffer and pvgis_format=None.
204
- # The error text changed in python 3.12. This regex matches both versions:
205
- with pytest.raises(TypeError, match="str.*os.PathLike"):
206
- read_pvgis_hourly(io.StringIO())
207
-
208
-
209
- args_radiation_csv = {
210
- 'surface_tilt': 30, 'surface_azimuth': 180, 'outputformat': 'csv',
211
- 'usehorizon': False, 'userhorizon': None, 'raddatabase': 'PVGIS-SARAH',
212
- 'start': 2016, 'end': 2016, 'pvcalculation': False, 'components': True}
213
-
214
- url_hourly_radiation_csv = 'https://re.jrc.ec.europa.eu/api/seriescalc?lat=45&lon=8&outputformat=csv&angle=30&aspect=0&usehorizon=0&pvtechchoice=crystSi&mountingplace=free&trackingtype=0&components=1&raddatabase=PVGIS-SARAH&startyear=2016&endyear=2016' # noqa: E501
215
-
216
- args_pv_json = {
217
- 'surface_tilt': 30, 'surface_azimuth': 180, 'outputformat': 'json',
218
- 'usehorizon': True, 'userhorizon': None, 'raddatabase': 'PVGIS-SARAH2',
219
- 'start': pd.Timestamp(2013, 1, 1), 'end': pd.Timestamp(2014, 5, 1),
220
- 'pvcalculation': True, 'peakpower': 10, 'pvtechchoice': 'CIS', 'loss': 5,
221
- 'trackingtype': 2, 'optimalangles': True, 'components': False,
222
- 'url': 'https://re.jrc.ec.europa.eu/api/v5_2/'}
223
-
224
- url_pv_json = 'https://re.jrc.ec.europa.eu/api/v5_2/seriescalc?lat=45&lon=8&outputformat=json&angle=30&aspect=0&pvtechchoice=CIS&mountingplace=free&trackingtype=2&components=0&usehorizon=1&raddatabase=PVGIS-SARAH2&startyear=2013&endyear=2014&pvcalculation=1&peakpower=10&loss=5&optimalangles=1' # noqa: E501
225
-
226
-
227
- @pytest.mark.parametrize('testfile,expected_name,args,map_variables,url_test', [ # noqa: E501
228
- (testfile_radiation_csv, 'expected_radiation_csv',
229
- args_radiation_csv, False, url_hourly_radiation_csv),
230
- (testfile_radiation_csv, 'expected_radiation_csv_mapped',
231
- args_radiation_csv, True, url_hourly_radiation_csv),
232
- (testfile_pv_json, 'expected_pv_json', args_pv_json, False, url_pv_json),
233
- (testfile_pv_json, 'expected_pv_json_mapped', args_pv_json, True,
234
- url_pv_json)])
235
- def test_get_pvgis_hourly(requests_mock, testfile, expected_name, args,
236
- map_variables, url_test, request):
237
- """Test that get_pvgis_hourly generates the correct URI request and that
238
- _parse_pvgis_hourly_json and _parse_pvgis_hourly_csv is called correctly"""
239
- # Open local test file containing McClear monthly data
240
- with open(testfile, 'r') as test_file:
241
- mock_response = test_file.read()
242
- # Specify the full URI of a specific example, this ensures that all of the
243
- # inputs are passing on correctly
244
- requests_mock.get(url_test, text=mock_response)
245
- # Make API call - an error is raised if requested URI does not match
246
- out, inputs, metadata = get_pvgis_hourly(
247
- latitude=45, longitude=8, map_variables=map_variables, **args)
248
- # Get expected dataframe from fixture
249
- expected = request.getfixturevalue(expected_name)
250
- # Compare out and expected dataframes
251
- assert_frame_equal(out, expected)
252
-
253
-
254
- def test_get_pvgis_hourly_bad_status_code(requests_mock):
255
- # Test if a HTTPError is raised if a bad request is returned
256
- requests_mock.get(url_pv_json, status_code=400)
257
- with pytest.raises(requests.HTTPError):
258
- get_pvgis_hourly(latitude=45, longitude=8, **args_pv_json)
259
- # Test if HTTPError is raised and error message is returned if avaiable
260
- requests_mock.get(url_pv_json, status_code=400,
261
- json={'message': 'peakpower Mandatory'})
262
- with pytest.raises(requests.HTTPError):
263
- get_pvgis_hourly(latitude=45, longitude=8, **args_pv_json)
264
-
265
-
266
- url_bad_outputformat = 'https://re.jrc.ec.europa.eu/api/seriescalc?lat=45&lon=8&outputformat=basic&angle=0&aspect=0&pvcalculation=0&pvtechchoice=crystSi&mountingplace=free&trackingtype=0&components=1&usehorizon=1&optimalangles=0&optimalinclination=0&loss=0' # noqa: E501
267
-
268
-
269
- def test_get_pvgis_hourly_bad_outputformat(requests_mock):
270
- # Test if a ValueError is raised if an unsupported outputformat is used
271
- # E.g. 'basic' is a valid PVGIS format, but is not supported by pvlib
272
- requests_mock.get(url_bad_outputformat)
273
- with pytest.raises(ValueError):
274
- get_pvgis_hourly(latitude=45, longitude=8, outputformat='basic')
275
-
276
-
277
- url_additional_inputs = 'https://re.jrc.ec.europa.eu/api/seriescalc?lat=55.6814&lon=12.5758&outputformat=csv&angle=0&aspect=0&pvcalculation=1&pvtechchoice=crystSi&mountingplace=free&trackingtype=0&components=1&usehorizon=1&optimalangles=1&optimalinclination=0&loss=2&userhorizon=10%2C15%2C20%2C10&peakpower=5' # noqa: E501
278
-
279
-
280
- def test_get_pvgis_hourly_additional_inputs(requests_mock):
281
- # Test additional inputs, including userhorizons
282
- # Necessary to pass a test file in order for the parser not to fail
283
- with open(testfile_radiation_csv, 'r') as test_file:
284
- mock_response = test_file.read()
285
- requests_mock.get(url_additional_inputs, text=mock_response)
286
- # Make request with userhorizon specified
287
- # Test passes if the request made by get_pvgis_hourly matches exactly the
288
- # url passed to the mock request (url_additional_inputs)
289
- get_pvgis_hourly(
290
- latitude=55.6814, longitude=12.5758, outputformat='csv',
291
- usehorizon=True, userhorizon=[10, 15, 20, 10], pvcalculation=True,
292
- peakpower=5, loss=2, trackingtype=0, components=True,
293
- optimalangles=True)
294
-
295
-
296
- def test_read_pvgis_hourly_empty_file():
297
- # Check if a IOError is raised if file does not contain a data section
298
- with pytest.raises(ValueError, match='No data section'):
299
- read_pvgis_hourly(
300
- io.StringIO('1:1\n2:2\n3:3\n4:4\n5:5\n'),
301
- pvgis_format='csv')
302
-
303
-
304
- # PVGIS TMY tests
305
- @pytest.fixture
306
- def expected():
307
- return pd.read_csv(DATA_DIR / 'pvgis_tmy_test.dat', index_col='time(UTC)')
308
-
309
-
310
- @pytest.fixture
311
- def userhorizon_expected():
312
- return pd.read_json(DATA_DIR / 'tmy_45.000_8.000_userhorizon.json')
313
-
314
-
315
- @pytest.fixture
316
- def month_year_expected():
317
- return [
318
- 2014, 2011, 2008, 2011, 2009, 2011, 2020, 2006, 2006, 2013, 2007, 2018]
319
-
320
-
321
- @pytest.fixture
322
- def inputs_expected():
323
- return {
324
- 'location': {'latitude': 45.0, 'longitude': 8.0, 'elevation': 250.0},
325
- 'meteo_data': {
326
- 'radiation_db': 'PVGIS-SARAH2',
327
- 'meteo_db': 'ERA5',
328
- 'year_min': 2005,
329
- 'year_max': 2020,
330
- 'use_horizon': True,
331
- 'horizon_db': 'DEM-calculated'}}
332
-
333
-
334
- @pytest.fixture
335
- def epw_meta():
336
- return {
337
- 'loc': 'LOCATION',
338
- 'city': 'unknown',
339
- 'state-prov': '-',
340
- 'country': 'unknown',
341
- 'data_type': 'ECMWF/ERA',
342
- 'WMO_code': 'unknown',
343
- 'latitude': 45.0,
344
- 'longitude': 8.0,
345
- 'TZ': 1.0,
346
- 'altitude': 250.0}
347
-
348
-
349
- @pytest.fixture
350
- def meta_expected():
351
- with (DATA_DIR / 'pvgis_tmy_meta.json').open() as f:
352
- return json.load(f)
353
-
354
-
355
- @pytest.fixture
356
- def csv_meta(meta_expected):
357
- return [
358
- f"{k}: {v['description']} ({v['units']})" for k, v
359
- in meta_expected['outputs']['tmy_hourly']['variables'].items()]
360
-
361
-
362
- @pytest.fixture
363
- def pvgis_tmy_mapped_columns():
364
- return ['temp_air', 'relative_humidity', 'ghi', 'dni', 'dhi', 'IR(h)',
365
- 'wind_speed', 'wind_direction', 'pressure']
366
-
367
-
368
- @pytest.mark.remote_data
369
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
370
- def test_get_pvgis_tmy(expected, month_year_expected, inputs_expected,
371
- meta_expected):
372
- pvgis_data = get_pvgis_tmy(45, 8, map_variables=False)
373
- _compare_pvgis_tmy_json(expected, month_year_expected, inputs_expected,
374
- meta_expected, pvgis_data)
375
-
376
-
377
- def _compare_pvgis_tmy_json(expected, month_year_expected, inputs_expected,
378
- meta_expected, pvgis_data):
379
- data, months_selected, inputs, meta = pvgis_data
380
- # check each column of output separately
381
- for outvar in meta_expected['outputs']['tmy_hourly']['variables'].keys():
382
- assert np.allclose(data[outvar], expected[outvar])
383
- assert np.allclose(
384
- [_['month'] for _ in months_selected], np.arange(1, 13, 1))
385
- assert np.allclose(
386
- [_['year'] for _ in months_selected], month_year_expected)
387
- inputs_loc = inputs['location']
388
- assert inputs_loc['latitude'] == inputs_expected['location']['latitude']
389
- assert inputs_loc['longitude'] == inputs_expected['location']['longitude']
390
- assert inputs_loc['elevation'] == inputs_expected['location']['elevation']
391
- inputs_met_data = inputs['meteo_data']
392
- expected_met_data = inputs_expected['meteo_data']
393
- assert (
394
- inputs_met_data['radiation_db'] == expected_met_data['radiation_db'])
395
- assert inputs_met_data['year_min'] == expected_met_data['year_min']
396
- assert inputs_met_data['year_max'] == expected_met_data['year_max']
397
- assert inputs_met_data['use_horizon'] == expected_met_data['use_horizon']
398
- assert inputs_met_data['horizon_db'] == expected_met_data['horizon_db']
399
- assert meta == meta_expected
400
-
401
-
402
- @pytest.mark.remote_data
403
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
404
- def test_get_pvgis_tmy_kwargs(userhorizon_expected):
405
- _, _, inputs, _ = get_pvgis_tmy(45, 8, usehorizon=False,
406
- map_variables=False)
407
- assert inputs['meteo_data']['use_horizon'] is False
408
- data, _, _, _ = get_pvgis_tmy(
409
- 45, 8, userhorizon=[0, 10, 20, 30, 40, 15, 25, 5], map_variables=False)
410
- assert np.allclose(
411
- data['G(h)'], userhorizon_expected['G(h)'].values)
412
- assert np.allclose(
413
- data['Gb(n)'], userhorizon_expected['Gb(n)'].values)
414
- assert np.allclose(
415
- data['Gd(h)'], userhorizon_expected['Gd(h)'].values)
416
- _, _, inputs, _ = get_pvgis_tmy(45, 8, startyear=2005, map_variables=False)
417
- assert inputs['meteo_data']['year_min'] == 2005
418
- _, _, inputs, _ = get_pvgis_tmy(45, 8, endyear=2016, map_variables=False)
419
- assert inputs['meteo_data']['year_max'] == 2016
420
-
421
-
422
- @pytest.mark.remote_data
423
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
424
- def test_get_pvgis_tmy_basic(expected, meta_expected):
425
- pvgis_data = get_pvgis_tmy(45, 8, outputformat='basic',
426
- map_variables=False)
427
- _compare_pvgis_tmy_basic(expected, meta_expected, pvgis_data)
428
-
429
-
430
- def _compare_pvgis_tmy_basic(expected, meta_expected, pvgis_data):
431
- data, _, _, _ = pvgis_data
432
- # check each column of output separately
433
- for outvar in meta_expected['outputs']['tmy_hourly']['variables'].keys():
434
- assert np.allclose(data[outvar], expected[outvar])
435
-
436
-
437
- @pytest.mark.remote_data
438
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
439
- def test_get_pvgis_tmy_coerce_year():
440
- """test utc_offset and coerce_year work as expected"""
441
- base_case, _, _, _ = get_pvgis_tmy(45, 8) # Turin
442
- assert str(base_case.index.tz) == 'UTC'
443
- assert base_case.index.name == 'time(UTC)'
444
- noon_test_data = [
445
- base_case[base_case.index.month == m].iloc[12]
446
- for m in range(1, 13)]
447
- cet_tz = 1 # Turin time is CET
448
- cet_name = 'Etc/GMT-1'
449
- # check indices of rolled data after converting timezone
450
- pvgis_data, _, _, _ = get_pvgis_tmy(45, 8, roll_utc_offset=cet_tz)
451
- jan1_midnight = pd.Timestamp('1990-01-01 00:00:00', tz=cet_name)
452
- dec31_midnight = pd.Timestamp('1990-12-31 23:00:00', tz=cet_name)
453
- assert pvgis_data.index[0] == jan1_midnight
454
- assert pvgis_data.index[-1] == dec31_midnight
455
- assert pvgis_data.index.name == f'time({cet_name})'
456
- # spot check rolled data matches original
457
- for m, test_case in enumerate(noon_test_data):
458
- expected = pvgis_data[pvgis_data.index.month == m+1].iloc[12+cet_tz]
459
- assert all(test_case == expected)
460
- # repeat tests with year coerced
461
- test_yr = 2021
462
- pvgis_data, _, _, _ = get_pvgis_tmy(
463
- 45, 8, roll_utc_offset=cet_tz, coerce_year=test_yr)
464
- jan1_midnight = pd.Timestamp(f'{test_yr}-01-01 00:00:00', tz=cet_name)
465
- dec31_midnight = pd.Timestamp(f'{test_yr}-12-31 23:00:00', tz=cet_name)
466
- assert pvgis_data.index[0] == jan1_midnight
467
- assert pvgis_data.index[-1] == dec31_midnight
468
- assert pvgis_data.index.name == f'time({cet_name})'
469
- for m, test_case in enumerate(noon_test_data):
470
- expected = pvgis_data[pvgis_data.index.month == m+1].iloc[12+cet_tz]
471
- assert all(test_case == expected)
472
- # repeat tests with year coerced but utc offset none or zero
473
- pvgis_data, _, _, _ = get_pvgis_tmy(45, 8, coerce_year=test_yr)
474
- jan1_midnight = pd.Timestamp(f'{test_yr}-01-01 00:00:00', tz='UTC')
475
- dec31_midnight = pd.Timestamp(f'{test_yr}-12-31 23:00:00', tz='UTC')
476
- assert pvgis_data.index[0] == jan1_midnight
477
- assert pvgis_data.index[-1] == dec31_midnight
478
- assert pvgis_data.index.name == 'time(UTC)'
479
- for m, test_case in enumerate(noon_test_data):
480
- expected = pvgis_data[pvgis_data.index.month == m+1].iloc[12]
481
- assert all(test_case == expected)
482
-
483
-
484
- @pytest.mark.remote_data
485
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
486
- def test_get_pvgis_tmy_csv(expected, month_year_expected, inputs_expected,
487
- meta_expected, csv_meta):
488
- pvgis_data = get_pvgis_tmy(45, 8, outputformat='csv', map_variables=False)
489
- _compare_pvgis_tmy_csv(expected, month_year_expected, inputs_expected,
490
- meta_expected, csv_meta, pvgis_data)
491
-
492
-
493
- def _compare_pvgis_tmy_csv(expected, month_year_expected, inputs_expected,
494
- meta_expected, csv_meta, pvgis_data):
495
- data, months_selected, inputs, meta = pvgis_data
496
- # check each column of output separately
497
- for outvar in meta_expected['outputs']['tmy_hourly']['variables'].keys():
498
- assert np.allclose(data[outvar], expected[outvar])
499
- assert np.allclose(
500
- [_['month'] for _ in months_selected], np.arange(1, 13, 1))
501
- assert np.allclose(
502
- [_['year'] for _ in months_selected], month_year_expected)
503
- assert inputs['latitude'] == inputs_expected['location']['latitude']
504
- assert inputs['longitude'] == inputs_expected['location']['longitude']
505
- assert inputs['elevation'] == inputs_expected['location']['elevation']
506
- for meta_value in meta:
507
- if not meta_value:
508
- continue
509
- # this copyright text tends to change (copyright year range increments
510
- # annually, e.g.), so just check the beginning of it:
511
- if meta_value.startswith('PVGIS (c) European'):
512
- continue
513
- assert meta_value in csv_meta
514
-
515
-
516
- @pytest.mark.remote_data
517
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
518
- def test_get_pvgis_tmy_epw(expected, epw_meta):
519
- pvgis_data = get_pvgis_tmy(45, 8, outputformat='epw', map_variables=False)
520
- _compare_pvgis_tmy_epw(expected, epw_meta, pvgis_data)
521
-
522
-
523
- def _compare_pvgis_tmy_epw(expected, epw_meta, pvgis_data):
524
- data, _, _, meta = pvgis_data
525
- assert np.allclose(data.ghi, expected['G(h)'])
526
- assert np.allclose(data.dni, expected['Gb(n)'])
527
- assert np.allclose(data.dhi, expected['Gd(h)'])
528
- assert np.allclose(data.temp_air, expected['T2m'])
529
- assert meta == epw_meta
530
-
531
-
532
- @pytest.mark.remote_data
533
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
534
- def test_get_pvgis_tmy_error():
535
- err_msg = 'outputformat: Incorrect value.'
536
- with pytest.raises(requests.HTTPError, match=err_msg):
537
- get_pvgis_tmy(45, 8, outputformat='bad')
538
- with pytest.raises(requests.HTTPError, match='404 Client Error'):
539
- get_pvgis_tmy(45, 8, url='https://re.jrc.ec.europa.eu/')
540
-
541
-
542
- @pytest.mark.remote_data
543
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
544
- def test_get_pvgis_map_variables(pvgis_tmy_mapped_columns):
545
- actual, _, _, _ = get_pvgis_tmy(45, 8, map_variables=True)
546
- assert all(c in pvgis_tmy_mapped_columns for c in actual.columns)
547
-
548
-
549
- @pytest.mark.remote_data
550
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
551
- def test_read_pvgis_horizon():
552
- pvgis_data, _ = get_pvgis_horizon(35.171051, -106.465158)
553
- horizon_data = pd.read_csv(DATA_DIR / 'test_read_pvgis_horizon.csv',
554
- index_col=0)
555
- horizon_data = horizon_data['horizon_elevation']
556
- assert_series_equal(pvgis_data, horizon_data)
557
-
558
-
559
- @pytest.mark.remote_data
560
- @pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
561
- def test_read_pvgis_horizon_invalid_coords():
562
- with pytest.raises(requests.HTTPError, match='lat: Incorrect value'):
563
- _, _ = get_pvgis_horizon(100, 50) # unfeasible latitude
564
-
565
-
566
- def test_read_pvgis_tmy_map_variables(pvgis_tmy_mapped_columns):
567
- fn = DATA_DIR / 'tmy_45.000_8.000_2005_2020.json'
568
- actual, _, _, _ = read_pvgis_tmy(fn, map_variables=True)
569
- assert all(c in pvgis_tmy_mapped_columns for c in actual.columns)
570
-
571
-
572
- def test_read_pvgis_tmy_json(expected, month_year_expected, inputs_expected,
573
- meta_expected):
574
- fn = DATA_DIR / 'tmy_45.000_8.000_2005_2020.json'
575
- # infer outputformat from file extensions
576
- pvgis_data = read_pvgis_tmy(fn, map_variables=False)
577
- _compare_pvgis_tmy_json(expected, month_year_expected, inputs_expected,
578
- meta_expected, pvgis_data)
579
- # explicit pvgis outputformat
580
- pvgis_data = read_pvgis_tmy(fn, pvgis_format='json', map_variables=False)
581
- _compare_pvgis_tmy_json(expected, month_year_expected, inputs_expected,
582
- meta_expected, pvgis_data)
583
- with fn.open('r') as fbuf:
584
- pvgis_data = read_pvgis_tmy(fbuf, pvgis_format='json',
585
- map_variables=False)
586
- _compare_pvgis_tmy_json(expected, month_year_expected, inputs_expected,
587
- meta_expected, pvgis_data)
588
-
589
-
590
- def test_read_pvgis_tmy_epw(expected, epw_meta):
591
- fn = DATA_DIR / 'tmy_45.000_8.000_2005_2020.epw'
592
- # infer outputformat from file extensions
593
- pvgis_data = read_pvgis_tmy(fn, map_variables=False)
594
- _compare_pvgis_tmy_epw(expected, epw_meta, pvgis_data)
595
- # explicit pvgis outputformat
596
- pvgis_data = read_pvgis_tmy(fn, pvgis_format='epw', map_variables=False)
597
- _compare_pvgis_tmy_epw(expected, epw_meta, pvgis_data)
598
- with fn.open('r') as fbuf:
599
- pvgis_data = read_pvgis_tmy(fbuf, pvgis_format='epw',
600
- map_variables=False)
601
- _compare_pvgis_tmy_epw(expected, epw_meta, pvgis_data)
602
-
603
-
604
- def test_read_pvgis_tmy_csv(expected, month_year_expected, inputs_expected,
605
- meta_expected, csv_meta):
606
- fn = DATA_DIR / 'tmy_45.000_8.000_2005_2020.csv'
607
- # infer outputformat from file extensions
608
- pvgis_data = read_pvgis_tmy(fn, map_variables=False)
609
- _compare_pvgis_tmy_csv(expected, month_year_expected, inputs_expected,
610
- meta_expected, csv_meta, pvgis_data)
611
- # explicit pvgis outputformat
612
- pvgis_data = read_pvgis_tmy(fn, pvgis_format='csv', map_variables=False)
613
- _compare_pvgis_tmy_csv(expected, month_year_expected, inputs_expected,
614
- meta_expected, csv_meta, pvgis_data)
615
- with fn.open('rb') as fbuf:
616
- pvgis_data = read_pvgis_tmy(fbuf, pvgis_format='csv',
617
- map_variables=False)
618
- _compare_pvgis_tmy_csv(expected, month_year_expected, inputs_expected,
619
- meta_expected, csv_meta, pvgis_data)
620
-
621
-
622
- def test_read_pvgis_tmy_basic(expected, meta_expected):
623
- fn = DATA_DIR / 'tmy_45.000_8.000_2005_2020.txt'
624
- # XXX: can't infer outputformat from file extensions for basic
625
- with pytest.raises(ValueError, match="pvgis format 'txt' was unknown"):
626
- read_pvgis_tmy(fn, map_variables=False)
627
- # explicit pvgis outputformat
628
- pvgis_data = read_pvgis_tmy(fn, pvgis_format='basic', map_variables=False)
629
- _compare_pvgis_tmy_basic(expected, meta_expected, pvgis_data)
630
- with fn.open('rb') as fbuf:
631
- pvgis_data = read_pvgis_tmy(fbuf, pvgis_format='basic',
632
- map_variables=False)
633
- _compare_pvgis_tmy_basic(expected, meta_expected, pvgis_data)
634
- # file buffer raises TypeError if passed to pathlib.Path()
635
- with pytest.raises(TypeError):
636
- read_pvgis_tmy(fbuf, map_variables=False)
637
-
638
-
639
- def test_read_pvgis_tmy_exception():
640
- bad_outputformat = 'bad'
641
- err_msg = f"pvgis format '{bad_outputformat:s}' was unknown"
642
- with pytest.raises(ValueError, match=err_msg):
643
- read_pvgis_tmy('filename', pvgis_format=bad_outputformat,
644
- map_variables=False)