From 4fefb1873c3fdc33c2ca816211694783f90399dc Mon Sep 17 00:00:00 2001 From: Javier Lopez Lorente Date: Tue, 19 May 2026 21:31:18 +0200 Subject: [PATCH] Add support for POA column in TSV file --- solarfarmer/weather.py | 29 ++++++++++++++++++++--------- tests/test_weather.py | 23 +++++++++++++++++++++-- 2 files changed, 41 insertions(+), 11 deletions(-) diff --git a/solarfarmer/weather.py b/solarfarmer/weather.py index cd304af..3663e08 100644 --- a/solarfarmer/weather.py +++ b/solarfarmer/weather.py @@ -37,6 +37,8 @@ ============== =========== ``ghi`` ``GHI`` ``dhi`` ``DHI`` +``poa`` ``POA`` +``gti`` ``POA`` ``temp_air`` ``TAmb`` ``wind_speed`` ``WS`` ``pressure`` ``Pressure`` @@ -164,6 +166,8 @@ def check_sequential_year_timestamps(file_path: str | pathlib.Path) -> None: PVLIB_COLUMN_MAP: dict[str, str] = { "ghi": "GHI", "dhi": "DHI", + "poa": "POA", + "gti": "POA", "temp_air": "TAmb", "wind_speed": "WS", "pressure": "Pressure", @@ -267,8 +271,8 @@ def from_pvlib( Parameters ---------- df : pandas.DataFrame - pvlib-style DataFrame (columns ``ghi``, ``dhi``, ``temp_air``, - ``wind_speed``, ``pressure``) with a DatetimeIndex. pvlib does not + pvlib-style DataFrame (columns ``ghi``, ``dhi``, ``poa``/``gti``, + ``temp_air``, ``wind_speed``, ``pressure``) with a DatetimeIndex. pvlib does not standardise units across data sources, so check that the units from your source match what SolarFarmer expects (see :data:`TSV_COLUMNS`). This function applies ``pressure_pa_to_mbar=True``, dividing the @@ -428,13 +432,6 @@ def shift_period_end_to_beginning(df: pd.DataFrame) -> pd.DataFrame: "note": "ISO 8601 with mandatory UTC offset; T separator required; no seconds. Must be the first column.", "aliases": ["Date", "DateTime", "Time"], }, - { - "name": "GHI", - "unit": "W/m²", - "range": (0, 1300), - "note": "Required unless POA is provided instead.", - "aliases": ["GHI", "ghi", "Glob", "SolarTotal"], - }, { "name": "TAmb", "unit": "°C", @@ -443,6 +440,20 @@ def shift_period_end_to_beginning(df: pd.DataFrame) -> pd.DataFrame: }, ], "optional": [ + { + "name": "GHI", + "unit": "W/m²", + "range": (0, 1300), + "note": "Global horizontal irradiance. Either GHI or POA must be provided.", + "aliases": ["GHI", "ghi", "Glob", "SolarTotal"], + }, + { + "name": "POA", + "unit": "W/m²", + "range": (0, 1300), + "note": "Plane of array irradiance. Either POA or GHI must be provided.", + "aliases": ["POA", "Gpoa", "Plane"], + }, { "name": "DHI", "unit": "W/m²", diff --git a/tests/test_weather.py b/tests/test_weather.py index ee70973..c2eb507 100644 --- a/tests/test_weather.py +++ b/tests/test_weather.py @@ -166,8 +166,27 @@ def pvlib_df(self): def test_columns_renamed(self, tmp_path, pvlib_df): out = from_pvlib(pvlib_df, tmp_path / "out.tsv") header = out.read_text().splitlines()[0] - for sf_col in PVLIB_COLUMN_MAP.values(): - assert sf_col in header + # Only assert SF columns whose source column is actually present in the fixture. + for pvlib_col, sf_col in PVLIB_COLUMN_MAP.items(): + if pvlib_col in pvlib_df.columns: + assert sf_col in header + + @pytest.mark.parametrize("poa_col", ["poa", "gti"]) + def test_poa_column_mapped(self, tmp_path, poa_col): + """pvlib 'poa' and 'gti' columns should both map to SolarFarmer 'POA'.""" + pd = pytest.importorskip("pandas") + idx = pd.date_range("2020-01-01", periods=3, freq="h", tz="UTC") + df = pd.DataFrame( + { + poa_col: [0.0, 450.0, 750.0], + "temp_air": [5.0, 15.0, 25.0], + }, + index=idx, + ) + out = from_pvlib(df, tmp_path / "out.tsv") + header = out.read_text().splitlines()[0].split("\t") + assert "POA" in header + assert poa_col not in header # raw pvlib name must be gone def test_pressure_converted(self, tmp_path, pvlib_df): out = from_pvlib(pvlib_df, tmp_path / "out.tsv")