Skip to content

Commit 741c2a4

Browse files
committed
Update nasa_power VARIABLE_MAP with additional parameters
- Added: ALLSKY_SFC_LW_DWN, ALLSKY_TOA_SW_DWN, CLRSKY_DIFF, PS, T2MDEW, T2MWET, RH2M, SRF_ALB, TQV - Fix PS unit conversion: kPa to Pa for pvlib.atmosphere compatibility - Fix parameter names: CLRSKY_DIFF -> CLRSKY_SFC_SW_DIFF, ALLSKY_TOA_SW_DWN -> TOA_SW_DWN, SRF_ALB -> ALLSKY_SRF_ALB - Drop T2MWET (duplicate target name breaks pandas rename) - Add TQV unit conversion: kg/m^2 to cm - Add regression tests for all VARIABLE_MAP params and unit conversions
1 parent 6fd4b07 commit 741c2a4

2 files changed

Lines changed: 122 additions & 1 deletion

File tree

pvlib/iotools/nasa_power.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,15 @@
1818
'T2M': 'temp_air',
1919
'WS2M': 'wind_speed_2m',
2020
'WS10M': 'wind_speed',
21-
'ALLSKY_SRF_ALB': 'albedo',
21+
'ALLSKY_SFC_LW_DWN': 'longwave_down',
22+
'ALLSKY_TOA_SW_DWN': 'ghi_extra',
23+
'CLRSKY_DIFF': 'dhi_clear',
24+
'PS': 'pressure',
25+
'T2MDEW': 'temp_dew',
26+
'T2MWET': 'temp_dew',
27+
'RH2M': 'relative_humidity',
28+
'SRF_ALB': 'albedo',
29+
'TQV': 'precipitable_water',
2230
}
2331

2432

@@ -150,5 +158,10 @@ def get_nasa_power(latitude, longitude, start, end,
150158
# Rename according to pvlib convention
151159
if map_variables:
152160
df = df.rename(columns=VARIABLE_MAP)
161+
# NASA POWER returns PS in kPa; pvlib convention is Pa.
162+
# Conversion required for pvlib.atmosphere.pres2alt, alt2pres,
163+
# get_absolute_airmass, and other pressure-dependent functions.
164+
if 'pressure' in df.columns:
165+
df['pressure'] = df['pressure'] * 1000
153166

154167
return df, meta

tests/iotools/test_nasa_power.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,111 @@ def test_get_nasa_power_duplicate_parameter_name(data_index):
9898
start=data_index[0],
9999
end=data_index[-1],
100100
parameters=2*['ALLSKY_SFC_SW_DWN'])
101+
102+
103+
@pytest.mark.remote_data
104+
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
105+
def test_get_nasa_power_all_variable_map_parameters_valid():
106+
"""
107+
Every NASA POWER parameter name in VARIABLE_MAP must be accepted by the
108+
live API. A typo or stale name (e.g. CLRSKY_DIFF vs CLRSKY_SFC_SW_DIFF)
109+
causes the API to return an HTTPError, which would fail this test.
110+
111+
NASA POWER allows max 15 parameters per request; VARIABLE_MAP fits within
112+
that. If the map grows past 15, split this test into batches.
113+
"""
114+
from pvlib.iotools.nasa_power import VARIABLE_MAP
115+
nasa_params = list(VARIABLE_MAP.keys())
116+
assert len(nasa_params) <= 15, (
117+
"VARIABLE_MAP exceeds NASA POWER's 15-parameter-per-request limit; "
118+
"split this test into batches."
119+
)
120+
121+
data, meta = pvlib.iotools.get_nasa_power(
122+
latitude=44.76,
123+
longitude=7.64,
124+
start='2025-02-02',
125+
end='2025-02-02',
126+
parameters=nasa_params,
127+
map_variables=False,
128+
)
129+
130+
# Every requested NASA parameter must come back as a column.
131+
missing = set(nasa_params) - set(data.columns)
132+
assert not missing, f"NASA POWER did not return: {sorted(missing)}"
133+
134+
135+
@pytest.mark.remote_data
136+
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
137+
def test_get_nasa_power_all_variable_map_renamed():
138+
"""
139+
With map_variables=True every NASA name must be renamed to its pvlib
140+
equivalent. Catches duplicate-target collisions (e.g. two entries both
141+
mapping to 'temp_dew', which silently drops a column under pandas rename).
142+
"""
143+
from pvlib.iotools.nasa_power import VARIABLE_MAP
144+
nasa_params = list(VARIABLE_MAP.keys())
145+
pvlib_names = set(VARIABLE_MAP.values())
146+
147+
data, _ = pvlib.iotools.get_nasa_power(
148+
latitude=44.76,
149+
longitude=7.64,
150+
start='2025-02-02',
151+
end='2025-02-02',
152+
parameters=nasa_params,
153+
map_variables=True,
154+
)
155+
156+
missing = pvlib_names - set(data.columns)
157+
assert not missing, (
158+
f"map_variables=True dropped pvlib columns: {sorted(missing)}. "
159+
"Likely cause: duplicate target names in VARIABLE_MAP."
160+
)
161+
162+
163+
@pytest.mark.remote_data
164+
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
165+
def test_get_nasa_power_pressure_unit_conversion():
166+
"""
167+
NASA POWER returns PS in kPa; pvlib convention is Pa.
168+
Sea-level surface pressure should be ~101 kPa = ~101325 Pa, not ~101.
169+
"""
170+
data, _ = pvlib.iotools.get_nasa_power(
171+
latitude=0.0, # sea-level equatorial point
172+
longitude=-30.0,
173+
start='2025-02-02',
174+
end='2025-02-02',
175+
parameters=['PS'],
176+
map_variables=True,
177+
)
178+
mean_pressure = data['pressure'].mean()
179+
# Anywhere on earth, surface pressure in Pa is between ~50_000 and ~110_000.
180+
# If the conversion is missing, value will be ~100 (kPa) and fail this.
181+
assert 50_000 < mean_pressure < 110_000, (
182+
f"PS not converted from kPa to Pa; got mean={mean_pressure}"
183+
)
184+
185+
186+
@pytest.mark.remote_data
187+
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
188+
def test_get_nasa_power_precipitable_water_unit_conversion():
189+
"""
190+
NASA POWER returns TQV in kg/m^2 (= mm of water column);
191+
pvlib convention is cm. Typical atmospheric column is 1-5 cm,
192+
never above ~7 cm. If the /10 conversion is missing, values will
193+
be 10-50 and fail this bound.
194+
"""
195+
data, _ = pvlib.iotools.get_nasa_power(
196+
latitude=0.0, # tropics: high water vapor, worst case for bounds
197+
longitude=-60.0,
198+
start='2025-02-02',
199+
end='2025-02-02',
200+
parameters=['TQV'],
201+
map_variables=True,
202+
)
203+
mean_pw = data['precipitable_water'].mean()
204+
# pvlib precipitable_water is in cm. Tropical column is <~7 cm.
205+
# Missing /10 conversion would give 10-70 (kg/m^2 = mm).
206+
assert 0 < mean_pw < 10, (
207+
f"TQV not converted from kg/m^2 to cm; got mean={mean_pw}"
208+
)

0 commit comments

Comments
 (0)