Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/sphinx/source/reference/iotools.rst
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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 @@ def read_solrad(filename):
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 @@ def read_solrad(filename):
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}")

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
Original file line number Diff line number Diff line change
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