Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions solarfarmer/weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
============== ===========
``ghi`` ``GHI``
``dhi`` ``DHI``
``poa`` ``POA``
``gti`` ``POA``
``temp_air`` ``TAmb``
``wind_speed`` ``WS``
``pressure`` ``Pressure``
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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",
Expand All @@ -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²",
Expand Down
23 changes: 21 additions & 2 deletions tests/test_weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Loading