Skip to content

Commit 8058272

Browse files
lboemanwholmgren
authored andcommitted
Surfrad data reader (#595)
* surfrad reader * minor updates * coding style fixes * DatetimeIndex updates for compatibility with pandas 0.14.0 * documentation updates * Doc updates, addition to what's new and api * bump min pandas version to 0.15.0, update tests and docs, add map_variables option * style fix * typo in whatsnew/doc update * rebase, small fix to pvsystems.py * remove whitespace * fix duplicate whatsnew entry after rebase
1 parent ed3b018 commit 8058272

File tree

9 files changed

+1700
-5
lines changed

9 files changed

+1700
-5
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ install:
6969
pip uninstall numpy --yes;
7070
pip uninstall pandas --yes;
7171
pip install --no-cache-dir numpy==1.10.1;
72-
pip install --no-cache-dir pandas==0.14.0;
72+
pip install --no-cache-dir pandas==0.15.0;
7373
fi
7474
- conda list
7575
- echo $PATH

docs/sphinx/source/api.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ relevant to solar energy modeling.
311311
iotools.read_tmy3
312312
iotools.read_srml
313313
iotools.read_srml_month_from_solardat
314+
iotools.read_surfrad
314315

315316
A :py:class:`~pvlib.location.Location` object may be created from metadata
316317
in some files.

docs/sphinx/source/whatsnew/v0.6.1.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ release.
99
**Python 2.7 support will end on June 1, 2019**. Releases made after this
1010
date will require Python 3. (:issue:`501`)
1111

12+
**Minimum pandas requirement bumped 0.14.0=>0.15.0**
13+
1214

1315
API Changes
1416
~~~~~~~~~~~
@@ -30,7 +32,7 @@ Enhancements
3032
* Created :py:func:`pvlib.iotools.read_srml` and
3133
:py:func:`pvlib.iotools.read_srml_month_from_solardat` to read University of
3234
Oregon Solar Radiation Monitoring Laboratory data. (:issue:`589`)
33-
35+
* Created :py:func:`pvlib.iotools.read_surfrad` to read NOAA SURFRAD data. (:issue:`590`)
3436

3537
Bug fixes
3638
~~~~~~~~~

pvlib/data/surfrad-slv16001.dat

Lines changed: 1442 additions & 0 deletions
Large diffs are not rendered by default.

pvlib/iotools/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
from pvlib.iotools.tmy import read_tmy3 # noqa: F401
33
from pvlib.iotools.srml import read_srml # noqa: F401
44
from pvlib.iotools.srml import read_srml_month_from_solardat # noqa: F401
5+
from pvlib.iotools.surfrad import read_surfrad # noqa: F401

pvlib/iotools/surfrad.py

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
"""
2+
Import functions for NOAA SURFRAD Data.
3+
"""
4+
import io
5+
6+
try:
7+
# python 2 compatibility
8+
from urllib2 import urlopen, Request
9+
except ImportError:
10+
from urllib.request import urlopen, Request
11+
12+
import pandas as pd
13+
import numpy as np
14+
15+
SURFRAD_COLUMNS = [
16+
'year', 'jday', 'month', 'day', 'hour', 'minute', 'dt', 'zen',
17+
'dw_solar', 'dw_solar_flag', 'uw_solar', 'uw_solar_flag', 'direct_n',
18+
'direct_n_flag', 'diffuse', 'diffuse_flag', 'dw_ir', 'dw_ir_flag',
19+
'dw_casetemp', 'dw_casetemp_flag', 'dw_dometemp', 'dw_dometemp_flag',
20+
'uw_ir', 'uw_ir_flag', 'uw_casetemp', 'uw_casetemp_flag', 'uw_dometemp',
21+
'uw_dometemp_flag', 'uvb', 'uvb_flag', 'par', 'par_flag', 'netsolar',
22+
'netsolar_flag', 'netir', 'netir_flag', 'totalnet', 'totalnet_flag',
23+
'temp', 'temp_flag', 'rh', 'rh_flag', 'windspd', 'windspd_flag',
24+
'winddir', 'winddir_flag', 'pressure', 'pressure_flag']
25+
26+
# Dictionary mapping surfrad variables to pvlib names
27+
VARIABLE_MAP = {
28+
'zen': 'solar_zenith',
29+
'dw_solar': 'ghi',
30+
'dw_solar_flag': 'ghi_flag',
31+
'direct_n': 'dni',
32+
'direct_n_flag': 'dni_flag',
33+
'diffuse': 'dhi',
34+
'diffuse_flag': 'dhi_flag',
35+
'temp': 'temp_air',
36+
'temp_flag': 'temp_air_flag',
37+
'windspd': 'wind_speed',
38+
'windspd_flag': 'wind_speed_flag',
39+
'winddir': 'wind_direction',
40+
'winddir_flag': 'wind_direction_flag',
41+
'rh': 'relative_humidity',
42+
'rh_flag': 'relative_humidity_flag'
43+
}
44+
45+
46+
def read_surfrad(filename, map_variables=True):
47+
"""Read in a daily NOAA SURFRAD[1] file.
48+
49+
Parameters
50+
----------
51+
filename: str
52+
Filepath or url.
53+
map_variables: bool
54+
When true, renames columns of the Dataframe to pvlib variable names
55+
where applicable. See variable SURFRAD_COLUMNS.
56+
57+
Returns
58+
-------
59+
Tuple of the form (data, metadata).
60+
61+
data: Dataframe
62+
Dataframe with the fields found below.
63+
metadata: dict
64+
Site metadata included in the file.
65+
66+
Notes
67+
-----
68+
Metadata dictionary includes the following fields:
69+
70+
=============== ====== ===============
71+
Key Format Description
72+
=============== ====== ===============
73+
station String site name
74+
latitude Float site latitude
75+
longitude Float site longitude
76+
elevation Int site elevation
77+
surfrad_version Int surfrad version
78+
tz String Timezone (UTC)
79+
=============== ====== ===============
80+
81+
Dataframe includes the following fields:
82+
83+
======================= ====== ==========================================
84+
raw, mapped Format Description
85+
======================= ====== ==========================================
86+
**Mapped field names are returned when the map_variables argument is True**
87+
---------------------------------------------------------------------------
88+
year int year as 4 digit int
89+
jday int day of year 1-365(or 366)
90+
month int month (1-12)
91+
day int day of month(1-31)
92+
hour int hour (0-23)
93+
minute int minute (0-59)
94+
dt float decimal time i.e. 23.5 = 2330
95+
zen, solar_zenith float solar zenith angle (deg)
96+
**Fields below have associated qc flags labeled <field>_flag.**
97+
---------------------------------------------------------------------------
98+
dw_solar, ghi float downwelling global solar(W/m^2)
99+
uw_solar float updownwelling global solar(W/m^2)
100+
direct_n, dni float direct normal solar (W/m^2)
101+
diffuse, dhi float downwelling diffuse solar (W/m^2)
102+
dw_ir float downwelling thermal infrared (W/m^2)
103+
dw_casetemp float downwelling IR case temp (K)
104+
dw_dometemp float downwelling IR dome temp (K)
105+
uw_ir float upwelling thermal infrared (W/m^2)
106+
uw_casetemp float upwelling IR case temp (K)
107+
uw_dometemp float upwelling IR case temp (K)
108+
uvb float global uvb (miliWatts/m^2)
109+
par float photosynthetically active radiation(W/m^2)
110+
netsolar float net solar (dw_solar - uw_solar) (W/m^2)
111+
netir float net infrared (dw_ir - uw_ir) (W/m^2)
112+
totalnet float net radiation (netsolar+netir) (W/m^2)
113+
temp, temp_air float 10-meter air temperature (?C)
114+
rh, relative_humidity float relative humidity (%)
115+
windspd, wind_speed float wind speed (m/s)
116+
winddir, wind_direction float wind direction (deg, clockwise from north)
117+
pressure float station pressure (mb)
118+
======================= ====== ==========================================
119+
120+
See README files located in the station directories in the SURFRAD
121+
data archives[2] for details on SURFRAD daily data files.
122+
123+
References
124+
----------
125+
[1] NOAA Earth System Research Laboratory Surface Radiation Budget Network
126+
`SURFRAD Homepage <https://www.esrl.noaa.gov/gmd/grad/surfrad/>`_
127+
[2] NOAA SURFRAD Data Archive
128+
`SURFRAD Archive <ftp://aftp.cmdl.noaa.gov/data/radiation/surfrad/>`_
129+
"""
130+
if filename.startswith('ftp'):
131+
req = Request(filename)
132+
response = urlopen(req)
133+
file_buffer = io.StringIO(response.read().decode(errors='ignore'))
134+
else:
135+
file_buffer = open(filename, 'r')
136+
137+
# Read and parse the first two lines to build the metadata dict.
138+
station = file_buffer.readline()
139+
file_metadata = file_buffer.readline()
140+
141+
metadata_list = file_metadata.split()
142+
metadata = {}
143+
metadata['name'] = station.strip()
144+
metadata['latitude'] = float(metadata_list[0])
145+
metadata['longitude'] = float(metadata_list[1])
146+
metadata['elevation'] = float(metadata_list[2])
147+
metadata['surfrad_version'] = int(metadata_list[-1])
148+
metadata['tz'] = 'UTC'
149+
150+
data = pd.read_csv(file_buffer, delim_whitespace=True,
151+
header=None, names=SURFRAD_COLUMNS)
152+
file_buffer.close()
153+
154+
data = format_index(data)
155+
missing = data == -9999.9
156+
data = data.where(~missing, np.NaN)
157+
158+
if map_variables:
159+
data.rename(columns=VARIABLE_MAP, inplace=True)
160+
return data, metadata
161+
162+
163+
def format_index(data):
164+
"""Create UTC localized DatetimeIndex for the dataframe.
165+
166+
Parameters
167+
----------
168+
data: Dataframe
169+
Must contain columns 'year', 'jday', 'hour' and
170+
'minute'.
171+
172+
Return
173+
------
174+
data: Dataframe
175+
Dataframe with a DatetimeIndex localized to UTC.
176+
"""
177+
year = data.year.apply(str)
178+
jday = data.jday.apply(lambda x: '{:03d}'.format(x))
179+
hours = data.hour.apply(lambda x: '{:02d}'.format(x))
180+
minutes = data.minute.apply(lambda x: '{:02d}'.format(x))
181+
index = pd.to_datetime(year + jday + hours + minutes, format="%Y%j%H%M")
182+
data.index = index
183+
data = data.tz_localize('UTC')
184+
return data

pvlib/pvsystem.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2648,8 +2648,8 @@ def adrinverter(v_dc, p_dc, inverter, vtol=0.10):
26482648
mppt_hi = inverter['MPPTHi']
26492649
mppt_low = inverter['MPPTLow']
26502650

2651-
v_lim_upper = np.nanmax([v_max, vdc_max, mppt_hi]) * (1 + vtol)
2652-
v_lim_lower = np.nanmax([v_min, mppt_low]) * (1 - vtol)
2651+
v_lim_upper = float(np.nanmax([v_max, vdc_max, mppt_hi]) * (1 + vtol))
2652+
v_lim_lower = float(np.nanmax([v_min, mppt_low]) * (1 - vtol))
26532653

26542654
pdc = p_dc / p_nom
26552655
vdc = v_dc / v_nom

pvlib/test/test_surfrad.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import inspect
2+
import os
3+
4+
from pandas import Timestamp, DatetimeIndex
5+
from pandas.util.testing import network
6+
7+
from pvlib.iotools import surfrad
8+
9+
test_dir = os.path.dirname(
10+
os.path.abspath(inspect.getfile(inspect.currentframe())))
11+
testfile = os.path.join(test_dir, '../data/surfrad-slv16001.dat')
12+
network_testfile = ('ftp://aftp.cmdl.noaa.gov/data/radiation/surfrad/'
13+
'Alamosa_CO/2016/slv16001.dat')
14+
15+
16+
@network
17+
def test_read_surfrad_network():
18+
# If this test begins failing, SURFRAD's data structure or data
19+
# archive may have changed.
20+
local_data, _ = surfrad.read_surfrad(testfile)
21+
network_data, _ = surfrad.read_surfrad(network_testfile)
22+
assert local_data.equals(network_data)
23+
24+
25+
def test_read_surfrad_columns_no_map():
26+
data, _ = surfrad.read_surfrad(testfile, map_variables=False)
27+
assert 'zen' in data.columns
28+
assert 'temp' in data.columns
29+
assert 'par' in data.columns
30+
assert 'pressure' in data.columns
31+
32+
33+
def test_read_surfrad_columns_map():
34+
data, _ = surfrad.read_surfrad(testfile)
35+
assert 'solar_zenith' in data.columns
36+
assert 'ghi' in data.columns
37+
assert 'ghi_flag' in data.columns
38+
assert 'dni' in data.columns
39+
assert 'dni_flag' in data.columns
40+
assert 'dhi' in data.columns
41+
assert 'dhi_flag' in data.columns
42+
assert 'wind_direction' in data.columns
43+
assert 'wind_direction_flag' in data.columns
44+
assert 'wind_speed' in data.columns
45+
assert 'wind_speed_flag' in data.columns
46+
assert 'temp_air' in data.columns
47+
assert 'temp_air_flag' in data.columns
48+
49+
50+
def test_format_index():
51+
start = Timestamp('20160101 00:00')
52+
expected = DatetimeIndex(start=start, periods=1440, freq='1min', tz='UTC')
53+
actual, _ = surfrad.read_surfrad(testfile)
54+
assert actual.index.equals(expected)
55+
56+
57+
def test_read_surfrad_metadata():
58+
expected = {'name': 'Alamosa',
59+
'latitude': 37.70,
60+
'longitude': 105.92,
61+
'elevation': 2317,
62+
'surfrad_version': 1,
63+
'tz': 'UTC'}
64+
_, metadata = surfrad.read_surfrad(testfile)
65+
assert metadata == expected

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
URL = 'https://github.com/pvlib/pvlib-python'
3939

4040
INSTALL_REQUIRES = ['numpy >= 1.10.1',
41-
'pandas >= 0.14.0',
41+
'pandas >= 0.15.0',
4242
'pytz',
4343
'six',
4444
]

0 commit comments

Comments
 (0)