|
5 | 5 |
|
6 | 6 |
|
7 | 7 | import pandas as pd |
| 8 | +from pvlib.location import Location |
| 9 | +from pvlib.modelchain import ModelChain |
8 | 10 | import pytest |
9 | 11 |
|
10 | 12 |
|
11 | | -from solarperformanceinsight_api import compute, storage, models |
| 13 | +from solarperformanceinsight_api import compute, storage, models, pvmodeling |
12 | 14 |
|
13 | 15 |
|
14 | 16 | pytestmark = pytest.mark.usefixtures("add_example_db_data") |
@@ -348,64 +350,125 @@ class Res: |
348 | 350 | assert pd.isna(nans).all() |
349 | 351 |
|
350 | 352 |
|
351 | | -def test_process_single_modelchain(mocker): |
| 353 | +# pytest param ids are helpful finding combos that fail |
| 354 | +@pytest.mark.parametrize( |
| 355 | + "tempcols", |
| 356 | + ( |
| 357 | + # pytest parm ids are |
| 358 | + pytest.param(["temp_air", "wind_speed"], id="standard_temp"), |
| 359 | + pytest.param(["temp_air"], id="air_temp_only"), |
| 360 | + pytest.param(["module_temperature"], id="module_temp"), |
| 361 | + pytest.param(["cell_temperature"], id="cell_temp"), |
| 362 | + pytest.param(["module_temperature", "wind_speed"], id="module_temp+ws"), |
| 363 | + pytest.param(["cell_temperature", "wind_speed"], id="cell_temp+ws"), |
| 364 | + ), |
| 365 | +) |
| 366 | +@pytest.mark.parametrize( |
| 367 | + "method,colmap", |
| 368 | + ( |
| 369 | + pytest.param("run_model", {}, id="run_model"), |
| 370 | + pytest.param( |
| 371 | + "run_model_from_poa", |
| 372 | + {"ghi": "poa_global", "dni": "poa_direct", "dhi": "poa_diffuse"}, |
| 373 | + id="run_model_poa", |
| 374 | + ), |
| 375 | + pytest.param( |
| 376 | + "run_model_from_effective_irradiance", |
| 377 | + {"ghi": "effective_irradiance", "dni": "noped", "dhi": "nah"}, |
| 378 | + id="run_model_eff", |
| 379 | + ), |
| 380 | + ), |
| 381 | +) |
| 382 | +def test_process_single_modelchain( |
| 383 | + system_def, either_tracker, method, colmap, tempcols |
| 384 | +): |
| 385 | + # full run through a modelchain with a fixed tilt single array, |
| 386 | + # fixed tilt two array, and single axis tracker single array |
352 | 387 | tshift = dt.timedelta(minutes=5) |
353 | | - df = pd.DataFrame({"poa_global": [1.0]}, index=[pd.Timestamp("2020-01-01T12:00")]) |
354 | | - df.index.name = "time" |
355 | | - shifted = df.shift(freq=-tshift) |
356 | | - |
357 | | - class Res: |
358 | | - ac = df["poa_global"] |
359 | | - total_irrad = (df, df) |
360 | | - effective_irradiance = (df, df) |
361 | | - cell_temperature = (df, df) |
362 | | - solar_position = pd.DataFrame({"zenith": 91.0}, index=df.index) |
363 | | - |
364 | | - class Sys: |
365 | | - arrays = [0, 1] |
366 | | - |
367 | | - class MC: |
368 | | - results = Res() |
369 | | - system = Sys() |
370 | | - |
371 | | - def run_model(self, data): |
372 | | - pd.testing.assert_frame_equal(df, data[0]) |
373 | | - return self |
374 | | - |
375 | | - with pytest.raises(AttributeError): |
376 | | - compute.process_single_modelchain(MC(), [df], "run_from_poa", tshift, 0) |
| 388 | + index = pd.DatetimeIndex([pd.Timestamp("2020-01-01T12:00:00-07:00")], name="time") |
| 389 | + tempdf = pd.DataFrame( |
| 390 | + { |
| 391 | + "temp_air": [25.0], |
| 392 | + "wind_speed": [10.0], |
| 393 | + "module_temperature": [30.0], |
| 394 | + "cell_temperature": [32.0], |
| 395 | + "poa_global": [1100.0], |
| 396 | + }, |
| 397 | + index=index, |
| 398 | + )[tempcols] |
| 399 | + irrad = pd.DataFrame( |
| 400 | + { |
| 401 | + "ghi": [1100.0], |
| 402 | + "dni": [1000.0], |
| 403 | + "dhi": [100.0], |
| 404 | + }, |
| 405 | + index=index, |
| 406 | + ).rename(columns=colmap) |
| 407 | + df = pd.concat([irrad, tempdf], axis=1) |
| 408 | + inv, _, multi = either_tracker |
| 409 | + location = Location(latitude=32.1, longitude=-110.8, altitude=2000, name="test") |
| 410 | + pvsys = pvmodeling.construct_pvsystem(inv) |
| 411 | + mc = ModelChain(system=pvsys, location=location, **dict(inv._modelchain_models)) |
| 412 | + weather = [df] |
| 413 | + if multi: |
| 414 | + weather.append(df) |
377 | 415 |
|
378 | 416 | # shifted (df - 5min) goes in, and shifted right (df) goes to be processed |
379 | | - dblist, summary = compute.process_single_modelchain( |
380 | | - MC(), [shifted], "run_model", tshift, 0 |
381 | | - ) |
382 | | - pd.testing.assert_frame_equal( |
383 | | - summary, |
384 | | - pd.DataFrame( |
385 | | - { |
386 | | - "performance": [1.0], |
387 | | - "poa_global": [1.0], |
388 | | - "effective_irradiance": [1.0], |
389 | | - "cell_temperature": [1.0], |
390 | | - "zenith": [91.0], |
391 | | - }, |
392 | | - index=shifted.index, |
393 | | - ), |
394 | | - ) |
| 417 | + dblist, summary = compute.process_single_modelchain(mc, weather, method, tshift, 0) |
| 418 | + assert summary.performance.iloc[0] == 250.0 |
| 419 | + assert set(summary.columns) == { |
| 420 | + "performance", |
| 421 | + "poa_global", |
| 422 | + "effective_irradiance", |
| 423 | + "cell_temperature", |
| 424 | + "zenith", |
| 425 | + } |
| 426 | + pd.testing.assert_index_equal(summary.index, df.index) |
395 | 427 |
|
396 | 428 | # performance for the inverter, and weather for each array |
397 | | - assert {d.schema_path for d in dblist} == { |
398 | | - "/inverters/0", |
399 | | - "/inverters/0/arrays/0", |
400 | | - "/inverters/0/arrays/1", |
401 | | - } |
402 | | - inv0arr0_weather = pd.read_feather(BytesIO(dblist[1].data)) |
403 | | - exp_weather = shifted.copy() |
404 | | - exp_weather.loc[:, "effective_irradiance"] = 1.0 |
405 | | - exp_weather.loc[:, "cell_temperature"] = 1.0 |
| 429 | + if multi: |
| 430 | + assert {d.schema_path for d in dblist} == { |
| 431 | + "/inverters/0", |
| 432 | + "/inverters/0/arrays/0", |
| 433 | + "/inverters/0/arrays/1", |
| 434 | + } |
| 435 | + else: |
| 436 | + assert {d.schema_path for d in dblist} == { |
| 437 | + "/inverters/0", |
| 438 | + "/inverters/0/arrays/0", |
| 439 | + } |
| 440 | + |
| 441 | + inv_perf = list( |
| 442 | + filter( |
| 443 | + lambda x: x.type == "performance data" and x.schema_path == "/inverters/0", |
| 444 | + dblist, |
| 445 | + ) |
| 446 | + )[0] |
406 | 447 | pd.testing.assert_frame_equal( |
407 | | - inv0arr0_weather, exp_weather.astype("float32").reset_index() |
| 448 | + pd.read_feather(BytesIO(inv_perf.data)), |
| 449 | + pd.DataFrame( |
| 450 | + {"performance": [250.0]}, dtype="float32", index=df.index |
| 451 | + ).reset_index(), |
| 452 | + ) |
| 453 | + arr0_weather_df = pd.read_feather( |
| 454 | + BytesIO( |
| 455 | + list( |
| 456 | + filter( |
| 457 | + lambda x: x.type == "weather data" |
| 458 | + and x.schema_path == "/inverters/0/arrays/0", |
| 459 | + dblist, |
| 460 | + ) |
| 461 | + )[0].data |
| 462 | + ) |
408 | 463 | ) |
| 464 | + assert set(arr0_weather_df.columns) == { |
| 465 | + "poa_global", |
| 466 | + "effective_irradiance", |
| 467 | + "cell_temperature", |
| 468 | + "time", |
| 469 | + } |
| 470 | + # pvlib>0.9.0a2 |
| 471 | + assert not pd.isna(arr0_weather_df.cell_temperature).any() |
409 | 472 |
|
410 | 473 |
|
411 | 474 | def test_run_performance_job(stored_job, auth0_id, nocommit_transaction, mocker): |
|
0 commit comments