Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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 @@ -36,6 +36,7 @@ of sources and file formats relevant to solar energy modeling.
iotools.get_cams
iotools.read_cams
iotools.parse_cams
iotools.get_acis_precipitation

A :py:class:`~pvlib.location.Location` object may be created from metadata
in some files.
Expand Down
2 changes: 2 additions & 0 deletions docs/sphinx/source/whatsnew/v0.9.6.rst
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ Enhancements
* :py:func:`pvlib.iotools.get_psm3` now uses the new NSRDB 3.2.2 endpoint for
hourly and half-hourly single-year datasets. (:issue:`1591`, :pull:`1736`)
* The default solar position algorithm (NREL SPA) is now 50-100% faster. (:pull:`1748`)
* Added function to retrieve gridded precipitation data from the ACIS service
from NOAA's RCCs: :py:func:`~pvlib.iotools.get_acis_precipitation`. (:issue:`1293`, :pull:`1777`)

Bug fixes
~~~~~~~~~
Expand Down
1 change: 1 addition & 0 deletions pvlib/iotools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@
from pvlib.iotools.sodapro import get_cams # noqa: F401
from pvlib.iotools.sodapro import read_cams # noqa: F401
from pvlib.iotools.sodapro import parse_cams # noqa: F401
from pvlib.iotools.acis import get_acis_precipitation # noqa: F401
105 changes: 105 additions & 0 deletions pvlib/iotools/acis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import requests
import pandas as pd
import numpy as np


def get_acis_precipitation(latitude, longitude, start, end, dataset,
Comment thread
kandersolar marked this conversation as resolved.
Outdated
url="https://data.rcc-acis.org/GridData", **kwargs):
Comment thread
kandersolar marked this conversation as resolved.
Outdated
"""
Retrieve daily gridded precipitation data from the Applied Climate
Comment thread
kandersolar marked this conversation as resolved.
Outdated
Information System (ACIS).

The Applied Climate Information System (ACIS) was developed and is
maintained by the NOAA Regional Climate Centers (RCCs) and brings together
climate data from many sources.
Comment thread
AdamRJensen marked this conversation as resolved.
Outdated

Parameters
----------
latitude : float
in decimal degrees, between -90 and 90, north is positive
longitude : float
in decimal degrees, between -180 and 180, east is positive
start : datetime-like
First day of the requested period
end : datetime-like
Last day of the requested period
dataset : int
A number indicating which gridded dataset to query. Options include:

* 1: NRCC Interpolated
* 2: Multi-Sensor Precipitation Estimates
* 3: NRCC Hi-Res
* 21: PRISM

See [1]_ for the full list of options.

url : str, default: 'https://data.rcc-acis.org/GridData'
API endpoint URL
kwargs:
Optional parameters passed to ``requests.get``.
Comment thread
kandersolar marked this conversation as resolved.
Outdated

Returns
-------
data : pandas.Series
Daily rainfall [mm]
metadata : dict
Coordinates for the selected grid cell

Raises
------
requests.HTTPError
A message from the ACIS server if the request is rejected

Notes
-----
The returned precipitation values are 24-hour aggregates, but
the aggregation period may not be midnight to midnight in local time.
For example, PRISM data is aggregated from 12:00 to 12:00 UTC,
meaning PRISM data labeled May 26 reflects to the 24 hours ending at
7:00am Eastern Standard Time on May 26.

Examples
--------
>>> prism, metadata = get_acis_precipitation(40.0, -80.0, '2020-01-01',
>>> '2020-12-31', dataset=21)

References
----------
.. [1] `ACIS Web Services <http://www.rcc-acis.org/docs_webservices.html>`_
.. [2] `ACIS Gridded Data <http://www.rcc-acis.org/docs_gridded.html>`_
.. [3] `NRCC <http://www.nrcc.cornell.edu/>`_
.. [4] `Multisensor Precipitation Estimates
<https://www.weather.gov/marfc/Multisensor_Precipitation>`_
.. [5] `PRISM <https://prism.oregonstate.edu/>`_
"""
elems = [
# other variables exist, but are not of interest for PV modeling
{"name": "pcpn", "interval": "dly", "units": "mm"},
]
Comment thread
kandersolar marked this conversation as resolved.
params = {
'loc': f"{longitude},{latitude}",
'sdate': pd.to_datetime(start).strftime('%Y-%m-%d'),
'edate': pd.to_datetime(end).strftime('%Y-%m-%d'),
Comment thread
AdamRJensen marked this conversation as resolved.
Outdated
'grid': str(dataset),
'elems': elems,
'output': 'json',
'meta': ["ll"], # "elev" should work, but it errors for some databases
Comment thread
AdamRJensen marked this conversation as resolved.
Outdated
}
response = requests.post(url, json=params,
headers={"Content-Type": "application/json"},
**kwargs)
response.raise_for_status()
payload = response.json()

if "error" in payload:
raise requests.HTTPError(payload['error'], response=response)

metadata = payload['meta']
metadata['latitude'] = metadata.pop('lat')
metadata['longitude'] = metadata.pop('lon')

df = pd.DataFrame(payload['data'], columns=['date', 'precipitation'])
rainfall = df.set_index('date')['precipitation']
rainfall = rainfall.replace(-999, np.nan)
rainfall.index = pd.to_datetime(rainfall.index)
return rainfall, metadata
37 changes: 37 additions & 0 deletions pvlib/tests/iotools/test_acis.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
tests for :mod:`pvlib.iotools.acis`
"""

import pandas as pd
import pytest
from pvlib.iotools import get_acis_precipitation
from ..conftest import (RERUNS, RERUNS_DELAY, assert_series_equal)
from requests import HTTPError


@pytest.mark.parametrize('dataset,expected,lat,lon', [
(1, [0.76, 1.78, 1.52, 0.76, 0.0], 40.0, -80.0),
(2, [0.05, 2.74, 1.43, 0.92, 0.0], 40.0083, -79.9653),
(3, [0.0, 2.79, 1.52, 1.02, 0.0], 40.0, -80.0),
(21, [0.6, 1.8, 1.9, 1.2, 0.0], 40.0, -80.0),
])
@pytest.mark.remote_data
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
def test_get_acis_precipitation(dataset, expected, lat, lon):
st = '2012-01-01'
ed = '2012-01-05'
precipitation, meta = get_acis_precipitation(40, -80, st, ed, dataset)
idx = pd.date_range(st, ed, freq='d')
idx.name = 'date'
idx.freq = None
expected = pd.Series(expected, index=idx, name='precipitation')
assert_series_equal(precipitation, expected)
assert meta == {'latitude': lat, 'longitude': lon}


@pytest.mark.remote_data
@pytest.mark.flaky(reruns=RERUNS, reruns_delay=RERUNS_DELAY)
def test_get_acis_precipitation_error():
with pytest.raises(HTTPError, match='invalid grid'):
# 50 is not a valid dataset (or "grid", in ACIS lingo)
get_acis_precipitation(40, -80, '2012-01-01', '2012-01-05', 50)