Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create pvlib.iotools.get_solrad #1967

Merged
merged 17 commits into from Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/sphinx/source/reference/iotools.rst
Expand Up @@ -26,6 +26,7 @@ of sources and file formats relevant to solar energy modeling.
iotools.read_midc_raw_data_from_nrel
iotools.read_crn
iotools.read_solrad
iotools.get_solrad
iotools.get_psm3
iotools.read_psm3
iotools.parse_psm3
Expand Down
3 changes: 3 additions & 0 deletions docs/sphinx/source/whatsnew/v0.10.4.rst
Expand Up @@ -8,6 +8,8 @@ v0.10.4 (Anticipated March, 2024)
Enhancements
~~~~~~~~~~~~
* Added the Huld PV model used by PVGIS (:pull:`1940`)
* Added :py:func:`~pvlib.iotools.get_solrad` for fetching irradiance data from
the SOLRAD ground station network. (:pull:`1967`)


Bug fixes
Expand All @@ -33,3 +35,4 @@ Contributors
* Cliff Hansen (:ghuser:`cwhanse`)
* :ghuser:`matsuobasho`
* Adam R. Jensen (:ghuser:`AdamRJensen`)
* Kevin Anderson (:ghuser:`kandersolar`)
1 change: 1 addition & 0 deletions pvlib/iotools/__init__.py
Expand Up @@ -8,6 +8,7 @@
from pvlib.iotools.midc import read_midc_raw_data_from_nrel # noqa: F401
from pvlib.iotools.crn import read_crn # noqa: F401
from pvlib.iotools.solrad import read_solrad # noqa: F401
from pvlib.iotools.solrad import get_solrad # noqa: F401
from pvlib.iotools.psm3 import get_psm3 # noqa: F401
from pvlib.iotools.psm3 import read_psm3 # noqa: F401
from pvlib.iotools.psm3 import parse_psm3 # noqa: F401
Expand Down
81 changes: 81 additions & 0 deletions pvlib/iotools/solrad.py
Expand Up @@ -3,6 +3,8 @@

import numpy as np
import pandas as pd
import urllib
import warnings

# pvlib conventions
BASE_HEADERS = (
Expand Down Expand Up @@ -63,6 +65,10 @@
A dataframe with DatetimeIndex and all of the variables in the
file.

See Also
--------
get_solrad

Notes
-----
SOLRAD data resolution is described by the README_SOLRAD.txt:
Expand Down Expand Up @@ -121,3 +127,78 @@
pass

return data


def get_solrad(station, start, end,
url="https://gml.noaa.gov/aftp/data/radiation/solrad/"):
"""Request data from NOAA SOLRAD and read it into a Dataframe.

A list of stations and their descriptions can be found in [1]_,
The data files are described in [2]_.

Data is returned for complete days, including ``start`` and ``end``.

Parameters
----------
station : str
Three letter station abbreviation.
start : datetime-like
First day of the requested period
end : datetime-like
Last day of the requested period
url : str, default: 'https://gml.noaa.gov/aftp/data/radiation/solrad/'
API endpoint URL

Returns
-------
data : pd.DataFrame
Dataframe with data from SOLRAD.
meta : dict
Metadata.

See Also
--------
read_solrad

Notes
-----
Recent SOLRAD data is 1-minute averages. Prior to 2015-01-01, it was
3-minute averages.

References
----------
.. [1] https://gml.noaa.gov/grad/solrad/index.html
.. [2] https://gml.noaa.gov/aftp/data/radiation/solrad/README_SOLRAD.txt

Examples
--------
>>> # Retrieve one month of irradiance data from the ABQ SOLRAD station
>>> data, metadata = pvlib.iotools.get_solrad(
>>> station='abq', start="2020-01-01", end="2020-01-31")
"""
# Use pd.to_datetime so that strings (e.g. '2021-01-01') are accepted
start = pd.to_datetime(start)
end = pd.to_datetime(end)

# Generate list of filenames
dates = pd.date_range(start.floor('d'), end, freq='d')
station = station.lower()
filenames = [
f"{station}/{d.year}/{station}{d.strftime('%y')}{d.dayofyear:03}.dat"
for d in dates
]

dfs = [] # Initialize list of monthly dataframes
for f in filenames:
try:
dfi = read_solrad(url + f)
dfs.append(dfi)
except urllib.error.HTTPError:
warnings.warn(f"The following file was not found: {f}")

Check warning on line 197 in pvlib/iotools/solrad.py

View check run for this annotation

Codecov / codecov/patch

pvlib/iotools/solrad.py#L196-L197

Added lines #L196 - L197 were not covered by tests
AdamRJensen marked this conversation as resolved.
Show resolved Hide resolved

data = pd.concat(dfs, axis='rows')

meta = {'station': station,
'filenames': filenames}

return data, meta
33 changes: 33 additions & 0 deletions pvlib/tests/iotools/test_solrad.py
Expand Up @@ -99,3 +99,36 @@ def test_read_solrad(testfile, index, columns, values, dtypes):
expected[col] = expected[col].astype(_dtype)
out = solrad.read_solrad(testfile)
assert_frame_equal(out, expected)


@pytest.mark.remote_data
@pytest.mark.parametrize('testfile, station', [
(testfile, 'abq'),
(testfile_mad, 'msn'),
])
def test_get_solrad(testfile, station):
df, meta = solrad.get_solrad(station, "2019-02-25", "2019-02-25")

assert meta['station'] == station
assert isinstance(meta['filenames'], list)

assert len(df) == 1440
assert df.index[0] == pd.to_datetime('2019-02-25 00:00+00:00')
assert df.index[-1] == pd.to_datetime('2019-02-25 23:59+00:00')

expected = solrad.read_solrad(testfile)
actual = df.reindex(expected.index)
# ABQ test file has an unexplained NaN in row 4; just verify first 3 rows
assert_frame_equal(actual.iloc[:3], expected.iloc[:3])


@pytest.mark.remote_data
def test_get_solrad_missing_day():
# data availability begins for ABQ on 2002-02-01 (DOY 32), so requesting
# data before that will raise a warning
message = 'The following file was not found: abq/2002/abq02031.dat'
with pytest.warns(UserWarning, match=message):
df, meta = solrad.get_solrad('abq', '2002-01-31', '2002-02-01')

# but the data for 2022-02-01 is still returned
assert not df.empty