|
| 1 | +import pandas as pd |
| 2 | +import numpy as np |
| 3 | +import pytest |
| 4 | +import pvlib |
| 5 | +from tests.conftest import RERUNS, RERUNS_DELAY |
| 6 | + |
| 7 | + |
| 8 | +@pytest.fixture |
| 9 | +def demo_api_key(): |
| 10 | + # Demo locations: |
| 11 | + # lat=50, lon=10 (Germany) |
| 12 | + # lat=21, lon=79 (India) |
| 13 | + # lat=-3, lon=-60 (Brazil) |
| 14 | + # lat=51, lon=-114 (Canada) |
| 15 | + # lat=24, lon=33 (Egypt) |
| 16 | + return 'demo0000-0000-0000-0000-000000000000' |
| 17 | + |
| 18 | + |
| 19 | +@pytest.fixture |
| 20 | +def demo_url(): |
| 21 | + return 'https://demo.meteonorm.com/v1/' |
| 22 | + |
| 23 | + |
| 24 | +@pytest.fixture |
| 25 | +def expected_meta(): |
| 26 | + meta = { |
| 27 | + 'altitude': 290, |
| 28 | + 'frequency': '1_hour', |
| 29 | + 'parameters': [ |
| 30 | + {'aggregation_method': 'average', |
| 31 | + 'description': 'Diffuse horizontal irradiance', |
| 32 | + 'name': 'diffuse_horizontal_irradiance', |
| 33 | + 'unit': {'description': 'Watt per square meter', 'name': 'W/m**2'}}, |
| 34 | + {'aggregation_method': 'average', |
| 35 | + 'description': 'Diffuse horizontal irradiance with shading taken into account', |
| 36 | + 'name': 'diffuse_horizontal_irradiance_with_shading', |
| 37 | + 'unit': {'description': 'Watt per square meter', 'name': 'W/m**2'}}, |
| 38 | + {'aggregation_method': 'average', |
| 39 | + 'description': 'Diffuse tilted irradiance', |
| 40 | + 'name': 'diffuse_tilted_irradiance', |
| 41 | + 'unit': {'description': 'Watt per square meter', 'name': 'W/m**2'}}, |
| 42 | + {'aggregation_method': 'average', |
| 43 | + 'description': 'Diffuse tilted irradiance with shading taken into account', |
| 44 | + 'name': 'diffuse_tilted_irradiance_with_shading', |
| 45 | + 'unit': {'description': 'Watt per square meter', 'name': 'W/m**2'}}, |
| 46 | + {'aggregation_method': 'average', |
| 47 | + 'description': 'Direct horizontal irradiance', |
| 48 | + 'name': 'direct_horizontal_irradiance', |
| 49 | + 'unit': {'description': 'Watt per square meter', 'name': 'W/m**2'}}, |
| 50 | + {'aggregation_method': 'average', |
| 51 | + 'description': 'Direct horizontal irradiance with shading taken into account', |
| 52 | + 'name': 'direct_horizontal_irradiance_with_shading', |
| 53 | + 'unit': {'description': 'Watt per square meter', 'name': 'W/m**2'}}, |
| 54 | + {'aggregation_method': 'average', |
| 55 | + 'description': 'Direct normal irradiance', |
| 56 | + 'name': 'direct_normal_irradiance', |
| 57 | + 'unit': {'description': 'Watt per square meter', 'name': 'W/m**2'}}, |
| 58 | + {'aggregation_method': 'average', |
| 59 | + 'description': 'Direct normal irradiance with shading taken into account', |
| 60 | + 'name': 'direct_normal_irradiance_with_shading', |
| 61 | + 'unit': {'description': 'Watt per square meter', 'name': 'W/m**2'}}, |
| 62 | + {'aggregation_method': 'average', |
| 63 | + 'description': 'Direct tilted irradiance', |
| 64 | + 'name': 'direct_tilted_irradiance', |
| 65 | + 'unit': {'description': 'Watt per square meter', 'name': 'W/m**2'}}, |
| 66 | + {'aggregation_method': 'average', |
| 67 | + 'description': 'Direct tilted irradiance with shading taken into account', |
| 68 | + 'name': 'direct_tilted_irradiance_with_shading', |
| 69 | + 'unit': {'description': 'Watt per square meter', 'name': 'W/m**2'}}, |
| 70 | + {'aggregation_method': 'average', |
| 71 | + 'description': 'Global horizontal clear sky irradiance', |
| 72 | + 'name': 'global_clear_sky_irradiance', |
| 73 | + 'unit': {'description': 'Watt per square meter', 'name': 'W/m**2'}}, |
| 74 | + {'aggregation_method': 'average', |
| 75 | + 'description': 'Global horizontal irradiance', |
| 76 | + 'name': 'global_horizontal_irradiance', |
| 77 | + 'unit': {'description': 'Watt per square meter', 'name': 'W/m**2'}}, |
| 78 | + {'aggregation_method': 'average', |
| 79 | + 'description': 'Global horizontal irradiance with shading taken into account', |
| 80 | + 'name': 'global_horizontal_irradiance_with_shading', |
| 81 | + 'unit': {'description': 'Watt per square meter', 'name': 'W/m**2'}}, |
| 82 | + {'aggregation_method': 'average', |
| 83 | + 'description': 'Global tilted irradiance', |
| 84 | + 'name': 'global_tilted_irradiance', |
| 85 | + 'unit': {'description': 'Watt per square meter', 'name': 'W/m**2'}}, |
| 86 | + {'aggregation_method': 'average', |
| 87 | + 'description': 'Global tilted irradiance with shading taken into account', |
| 88 | + 'name': 'global_tilted_irradiance_with_shading', |
| 89 | + 'unit': {'description': 'Watt per square meter', 'name': 'W/m**2'}}, |
| 90 | + {'aggregation_method': 'average', |
| 91 | + 'description': 'Power output per kWp installed', |
| 92 | + 'name': 'pv_production', |
| 93 | + 'unit': {'description': 'Watts per kilowatt peak', 'name': 'W/kWp'}}, |
| 94 | + {'aggregation_method': 'average', |
| 95 | + 'description': 'Power output per kWp installed, with shading taken into account', |
| 96 | + 'name': 'pv_production_with_shading', |
| 97 | + 'unit': {'description': 'Watts per kilowatt peak', 'name': 'W/kWp'}}, |
| 98 | + {'aggregation_method': 'average', |
| 99 | + 'description': 'Snow depth', |
| 100 | + 'name': 'snow_depth', |
| 101 | + 'unit': {'description': 'millimeters', 'name': 'mm'}}, |
| 102 | + {'aggregation_method': 'average', |
| 103 | + 'description': 'Air temperature, 2 m above ground.', |
| 104 | + 'name': 'temperature', |
| 105 | + 'unit': {'description': 'degrees Celsius', 'name': '°C'}}], |
| 106 | + 'surface_azimuth': 180, |
| 107 | + 'surface_tilt': 0, |
| 108 | + 'time_zone': 0, |
| 109 | + 'latitude': 50, |
| 110 | + 'longitude': 10, |
| 111 | + } |
| 112 | + return meta |
| 113 | + |
| 114 | + |
| 115 | +@pytest.fixture |
| 116 | +def expected_meteonorm_index(): |
| 117 | + expected_meteonorm_index = \ |
| 118 | + pd.date_range('2023-01-01', '2024-12-31 23:59', freq='1h', tz='UTC') |
| 119 | + expected_meteonorm_index.freq = None |
| 120 | + return expected_meteonorm_index |
| 121 | + |
| 122 | + |
| 123 | +@pytest.fixture |
| 124 | +def expected_metenorm_data(): |
| 125 | + # The first 12 rows of data |
| 126 | + columns = ['dhi', 'diffuse_horizontal_irradiance_with_shading', 'poa_diffuse', |
| 127 | + 'diffuse_tilted_irradiance_with_shading', 'bhi', |
| 128 | + 'direct_horizontal_irradiance_with_shading', 'dni', |
| 129 | + 'direct_normal_irradiance_with_shading', 'poa_direct', |
| 130 | + 'direct_tilted_irradiance_with_shading', 'ghi_clear', 'ghi', |
| 131 | + 'global_horizontal_irradiance_with_shading', 'poa', |
| 132 | + 'global_tilted_irradiance_with_shading', 'pv_production', |
| 133 | + 'pv_production_with_shading', 'snow_depth', 'temp_air'] |
| 134 | + expected = [ |
| 135 | + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 12.25], |
| 136 | + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 11.75], |
| 137 | + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 11.75], |
| 138 | + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 11.5], |
| 139 | + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 11.25], |
| 140 | + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 11.], |
| 141 | + [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 11.], |
| 142 | + [2.5, 2.68309898, 2.67538201, 2.68309898, 0., 0., 0., 0., 0., 0., 0., 2.5, |
| 143 | + 2.68309898, 2.67538201, 2.68309898, 2.34649978, 2.35326557, 0., 11.], |
| 144 | + [40.43632435, 40.41304027, 40.43632435, 40.41304027, 37.06367565, 37.06367565, |
| 145 | + 288.7781947, 288.7781947, 37.06367565, 37.06367565, 98.10113439, 77.5, |
| 146 | + 77.47671591, 77.5, 77.47671591, 67.02141875, 67.00150474, 0., 11.75], |
| 147 | + [60.52591348, 60.51498257, 60.52591348, 60.51498257, 104.47408652, 104.47408652, |
| 148 | + 478.10101591, 478.10101591, 104.47408652, 104.47408652, 191.27910925, 165., |
| 149 | + 164.98906908, 165., 164.98906908, 140.23845, 140.22938131, 0., 12.75], |
| 150 | + [71.90169306, 71.89757085, 71.90169306, 71.89757085, 138.84830694, 138.84830694, |
| 151 | + 508.02986044, 508.02986044, 138.84830694, 138.84830694, 253.85597777, 210.75, |
| 152 | + 210.7458778, 210.75, 210.7458778, 177.07272956, 177.06937293, 0., 13.75], |
| 153 | + [78.20403711, 78.19681926, 78.20403711, 78.19681926, 142.79596289, 142.79596289, |
| 154 | + 494.06576548, 494.06576548, 142.79596289, 142.79596289, 272.34275335, 221., |
| 155 | + 220.99278214, 221., 220.99278214, 185.179657, 185.17380523, 0., 14.], |
| 156 | + ] |
| 157 | + index = pd.date_range('2023-01-01', periods=12, freq='1h', tz='UTC') |
| 158 | + index.freq = None |
| 159 | + expected = pd.DataFrame(expected, index=index, columns=columns) |
| 160 | + expected['snow_depth'] = expected['snow_depth'].astype(np.int64) |
| 161 | + return expected |
| 162 | + |
| 163 | + |
| 164 | +@pytest.mark.remote_data |
| 165 | +@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) |
| 166 | +def test_get_meteonorm_training( |
| 167 | + demo_api_key, demo_url, expected_meta, expected_meteonorm_index, |
| 168 | + expected_metenorm_data): |
| 169 | + data, meta = pvlib.iotools.get_meteonorm( |
| 170 | + latitude=50, longitude=10, |
| 171 | + start='2023-01-01', end='2025-01-01', |
| 172 | + api_key=demo_api_key, |
| 173 | + endpoint='observation/training', |
| 174 | + time_step='1h', |
| 175 | + url=demo_url) |
| 176 | + |
| 177 | + assert meta == expected_meta |
| 178 | + pd.testing.assert_index_equal(data.index, expected_meteonorm_index) |
| 179 | + pd.testing.assert_frame_equal(data.iloc[:12], expected_metenorm_data) |
| 180 | + |
| 181 | + |
| 182 | +@pytest.mark.remote_data |
| 183 | +@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY) |
| 184 | +def test_get_meteonorm_realtime( |
| 185 | + demo_api_key, demo_url, expected_meta, expected_meteonorm_index, |
| 186 | + expected_metenorm_data): |
| 187 | + data, meta = pvlib.iotools.get_meteonorm( |
| 188 | + latitude=21, longitude=79, |
| 189 | + start=pd.Timestamp.now(tz='UTC') - pd.Timedelta(hours=5), |
| 190 | + end=pd.Timestamp.now(tz='UTC') - pd.Timedelta(hours=1), |
| 191 | + surface_tilt=20, surface_azimuth=10, |
| 192 | + parameters=['ghi', 'global_horizontal_irradiance_with_shading'], |
| 193 | + api_key=demo_api_key, |
| 194 | + endpoint='/observation/realtime', |
| 195 | + time_step='1min', |
| 196 | + horizon='flat', |
| 197 | + map_variables=False, |
| 198 | + interval_index=True, |
| 199 | + url=demo_url, |
| 200 | + ) |
| 201 | + assert meta['frequency'] == '1_minute' |
| 202 | + assert meta['lat'] == 21 |
| 203 | + assert meta['lon'] == 79 |
| 204 | + assert meta['surface_tilt'] == 20 |
| 205 | + assert meta['surface_azimuth'] == 10 |
| 206 | + |
| 207 | + assert all(data.columns == pd.Index([ |
| 208 | + 'global_horizontal_irradiance', |
| 209 | + 'global_horizontal_irradiance_with_shading'])) |
| 210 | + assert data.shape == (241, 2) |
| 211 | + # can't test the specific index as it varies due to the |
| 212 | + # use of pd.Timestamp.now |
| 213 | + assert type(data.index) is pd.core.indexes.interval.IntervalIndex |
0 commit comments