NREL MIDC station network#

The Measurement and Instrumentation Data Center (MIDC) is operated by NREL and provides irradiance and meteorological data from a number of ground stations in the U.S. The stations vary in quality, with some stations measuring all three components with high-quality instruments and other stations featuring a rotating shadow band pyranometer.

The most notable station is the Baseline Measurement System (BMS) at NREL’s Solar Radiation Research Laboratory (SRRL) outside of Denver, Colorado. The BMS features the world’s largest collection of operating pyranometers and pyrheliometers. A number of sky imagers, PV reference cells, and spectral radiometers are also located at the site. Instruments at the BMS are cleaned each weekday and frequently calibrated. Thus, due to the large collection of co-located and well maintained instruments, the BMS data is ideal for comparing different types of instruments.

Note, the MIDC includes several inactive stations. Also, several of the active stations are no longer cleaned or calibrated frequently. For these reasons, the SolarStations listing only includes the SRRL BMS, SOLARTAC, and Flatirons M2 sites, as these measures all three irradiance components and are active. See the map below for the locations of the stations.

Station Identifier Station full name Station Abbreviation Latitude Longitude Elevation Active
Loading... (need help?)
Make this Notebook Trusted to load map: File -> Trust Notebook

Data retrieval#

Data from the MIDC can be retrieved from the MIDC website or using the MIDC raw data API.

Note

If you use data from the MIDC in any publication, make sure to cite it. As an example, the citation for the BMS site is:

Andreas, A.; Stoffel, T.; (1981). NREL Solar Radiation Research Laboratory (SRRL): Baseline Measurement System (BMS); Golden, Colorado (Data); NREL Report No. DA-5500-56488. http://dx.doi.org/10.5439/1052221

Conveniently, the pvlib-python library features a wrapper around the API making retrieving data a breeze. The use of the function is shown below, demonstrating how to retrieve five days of data from the BMS:

import pvlib

data = pvlib.iotools.read_midc_raw_data_from_nrel(
    site='BMS',  # station identifier
    start=pd.Timestamp(2020,6,1),
    end=pd.Timestamp(2020,6,5))

# show a subset of the data
show(data.iloc[:5, 5:10], dom="tpr")
---------------------------------------------------------------------------
KeyboardInterrupt                         Traceback (most recent call last)
/tmp/ipykernel_2032/3074963547.py in <module>
      4     site='BMS',  # station identifier
      5     start=pd.Timestamp(2020,6,1),
----> 6     end=pd.Timestamp(2020,6,5))
      7 
      8 # show a subset of the data

/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/site-packages/pvlib/iotools/midc.py in read_midc_raw_data_from_nrel(site, start, end, variable_map, timeout)
    255     # so first parse the header to determine the number of data columns
    256     # to parse
--> 257     csv_request = requests.get(url, timeout=timeout, params=args)
    258     csv_request.raise_for_status()
    259     raw_csv = io.StringIO(csv_request.text)

/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/site-packages/requests/api.py in get(url, params, **kwargs)
     71     """
     72 
---> 73     return request("get", url, params=params, **kwargs)
     74 
     75 

/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/site-packages/requests/api.py in request(method, url, **kwargs)
     57     # cases, and look like a memory leak in others.
     58     with sessions.Session() as session:
---> 59         return session.request(method=method, url=url, **kwargs)
     60 
     61 

/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/site-packages/requests/sessions.py in request(self, method, url, params, data, headers, cookies, files, auth, timeout, allow_redirects, proxies, hooks, stream, verify, cert, json)
    585         }
    586         send_kwargs.update(settings)
--> 587         resp = self.send(prep, **send_kwargs)
    588 
    589         return resp

/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/site-packages/requests/sessions.py in send(self, request, **kwargs)
    721             # Redirect resolving generator.
    722             gen = self.resolve_redirects(r, request, **kwargs)
--> 723             history = [resp for resp in gen]
    724         else:
    725             history = []

/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/site-packages/requests/sessions.py in <listcomp>(.0)
    721             # Redirect resolving generator.
    722             gen = self.resolve_redirects(r, request, **kwargs)
--> 723             history = [resp for resp in gen]
    724         else:
    725             history = []

/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/site-packages/requests/sessions.py in resolve_redirects(self, resp, req, stream, timeout, verify, cert, proxies, yield_requests, **adapter_kwargs)
    272                     proxies=proxies,
    273                     allow_redirects=False,
--> 274                     **adapter_kwargs,
    275                 )
    276 

/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/site-packages/requests/sessions.py in send(self, request, **kwargs)
    743 
    744         if not stream:
--> 745             r.content
    746 
    747         return r

/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/site-packages/requests/models.py in content(self)
    897                 self._content = None
    898             else:
--> 899                 self._content = b"".join(self.iter_content(CONTENT_CHUNK_SIZE)) or b""
    900 
    901         self._content_consumed = True

/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/site-packages/requests/models.py in generate()
    814             if hasattr(self.raw, "stream"):
    815                 try:
--> 816                     yield from self.raw.stream(chunk_size, decode_content=True)
    817                 except ProtocolError as e:
    818                     raise ChunkedEncodingError(e)

/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/site-packages/urllib3/response.py in stream(self, amt, decode_content)
    621         """
    622         if self.chunked and self.supports_chunked_reads():
--> 623             for line in self.read_chunked(amt, decode_content=decode_content):
    624                 yield line
    625         else:

/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/site-packages/urllib3/response.py in read_chunked(self, amt, decode_content)
    813 
    814             while True:
--> 815                 self._update_chunk_length()
    816                 if self.chunk_left == 0:
    817                     break

/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/site-packages/urllib3/response.py in _update_chunk_length(self)
    743         if self.chunk_left is not None:
    744             return
--> 745         line = self._fp.fp.readline()
    746         line = line.split(b";", 1)[0]
    747         try:

/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/socket.py in readinto(self, b)
    587         while True:
    588             try:
--> 589                 return self._sock.recv_into(b)
    590             except timeout:
    591                 self._timeout_occurred = True

/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/ssl.py in recv_into(self, buffer, nbytes, flags)
   1069                   "non-zero flags not allowed in calls to recv_into() on %s" %
   1070                   self.__class__)
-> 1071             return self.read(nbytes, buffer)
   1072         else:
   1073             return super().recv_into(buffer, nbytes, flags)

/opt/hostedtoolcache/Python/3.7.14/x64/lib/python3.7/ssl.py in read(self, len, buffer)
    927         try:
    928             if buffer is not None:
--> 929                 return self._sslobj.read(len, buffer)
    930             else:
    931                 return self._sslobj.read(len)

KeyboardInterrupt: 

The retrieved BMS dataset contains numerous instruments measuring the same irradiance component. Let’s, for example, compare the global horizontal irradiance (GHI) measured by a high-quality CMP22 pyranometer with that of a low-cost CM3 pyranometer:

import matplotlib.pyplot as plt

fig, axes = plt.subplots(ncols=2, figsize=(10,4))
# plot both measurement as a time-series
data[['Global CMP22 (vent/cor) [W/m^2]', 'Global CM3 (cor) [W/m^2]']].plot(
    ax=axes[0], alpha=0.8, ylim=[-20, 1500])
# compare measurements with a scatter plot
data.plot.scatter(ax=axes[1], s=1, grid=True,
                  x='Global CMP22 (vent/cor) [W/m^2]',
                  y='Global CM3 (cor) [W/m^2]',
                  xlim=[-20, 1300], ylim=[-20, 1300])
fig.tight_layout()
_images/station_network_midc_6_0.png