Forecasting#
Warning
The pvlib.forecast
module is deprecated as of version 0.9.1
.
Because none of the current pvlib team members are able to continue
maintaining it, the functionality in pvlib.forecast
is deprecated
and will be removed without replacement in a future version. If you
are interested in maintaining this functionality, please let us know.
You can fetch forecast data yourself using siphon
(see the
docs below this warning) and the code from pvlib v0.9.0 as a reference:
https://github.com/pvlib/pvlib-python/blob/v0.9.0/pvlib/forecast.py
The Solar Forecast Arbiter Core offers similar (and more robust) forecast processing functionality and may be a suitable replacement for some users.
pvlib python provides a set of functions and classes that make it easy to obtain weather forecast data and convert that data into a PV power forecast. Users can retrieve standardized weather forecast data relevant to PV power modeling from NOAA/NCEP/NWS models including the GFS, NAM, RAP, HRRR, and the NDFD. A PV power forecast can then be obtained using the weather data as inputs to the comprehensive modeling capabilities of pvlib python. Standardized, open source, reference implementations of forecast methods using publicly available data may help advance the state-of-the-art of solar power forecasting.
pvlib python uses Unidata’s Siphon library to simplify access to real-time forecast data hosted on the Unidata THREDDS catalog. Siphon is great for programatic access of THREDDS data, but we also recommend using tools such as Panoply to easily browse the catalog and become more familiar with its contents.
We do not know of a similarly easy way to access archives of forecast data.
This document demonstrates how to use pvlib python to create a PV power forecast using these tools. The forecast and forecast_to_power Jupyter notebooks provide additional example code.
Warning
The forecast module algorithms and features are highly experimental. The API may change, the functionality may be consolidated into an io module, or the module may be separated into its own package.
Note
This documentation is difficult to reliably build on readthedocs. If you do not see images, try building the documentation on your own machine or see the notebooks linked to above.
Accessing Forecast Data#
The Siphon library provides access to, among others, forecasts from the
Global Forecast System (GFS), North American Model (NAM), High
Resolution Rapid Refresh (HRRR), Rapid Refresh (RAP), and National
Digital Forecast Database (NDFD) on a Unidata THREDDS server.
Unfortunately, many of these models use different names to describe the
same quantity (or a very similar one), and not all variables are present
in all models. For example, on the THREDDS server, the GFS has a field
named
Total_cloud_cover_entire_atmosphere_Mixed_intervals_Average
,
while the NAM has a field named
Total_cloud_cover_entire_atmosphere_single_layer
, and a
similar field in the HRRR is named
Total_cloud_cover_entire_atmosphere
.
pvlib python aims to simplify the access of the model fields relevant
for solar power forecasts. Model data accessed with pvlib python is
returned as a pandas DataFrame with consistent column names:
temp_air, wind_speed, total_clouds, low_clouds, mid_clouds,
high_clouds, dni, dhi, ghi
. To accomplish this, we use an
object-oriented framework in which each weather model is represented by
a class that inherits from a parent
ForecastModel
class.
The parent ForecastModel
class contains the
common code for accessing and parsing the data using Siphon, while the
child model-specific classes (GFS
,
HRRR
, etc.) contain the code necessary to
map and process that specific model’s data to the standardized fields.
The code below demonstrates how simple it is to access and plot forecast data using pvlib python. First, we set up make the basic imports and then set the location and time range data.
In [1]: import pandas as pd
In [2]: import matplotlib.pyplot as plt
In [3]: import datetime
# import pvlib forecast models
In [4]: from pvlib.forecast import GFS, NAM, NDFD, HRRR, RAP
# specify location (Tucson, AZ)
In [5]: latitude, longitude, tz = 32.2, -110.9, 'US/Arizona'
# specify time range.
In [6]: start = pd.Timestamp(datetime.date.today(), tz=tz)
In [7]: end = start + pd.Timedelta(days=7)
In [8]: irrad_vars = ['ghi', 'dni', 'dhi']
Next, we instantiate a GFS model object and get the forecast data from Unidata.
# GFS model, defaults to 0.5 degree resolution
# 0.25 deg available
In [9]: model = GFS()
# retrieve data. returns pandas.DataFrame object
In [10]: raw_data = model.get_data(latitude, longitude, start, end)
---------------------------------------------------------------------------
HTTPError Traceback (most recent call last)
<ipython-input-10-7f04e38c5a09> in <module>
----> 1 raw_data = model.get_data(latitude, longitude, start, end)
~/checkouts/readthedocs.org/user_builds/pvlib-python/checkouts/stable/pvlib/forecast.py in get_data(self, latitude, longitude, start, end, vert_level, query_variables, close_netcdf_data, **kwargs)
290 self.query.accept(self.data_format)
291
--> 292 self.netcdf_data = self.ncss.get_data(self.query)
293
294 # might be better to go to xarray here so that we can handle
~/checkouts/readthedocs.org/user_builds/pvlib-python/envs/stable/lib/python3.7/site-packages/siphon/ncss.py in get_data(self, query)
112
113 """
--> 114 resp = self.get_query(query)
115 return response_handlers(resp, self.unit_handler)
116
~/checkouts/readthedocs.org/user_builds/pvlib-python/envs/stable/lib/python3.7/site-packages/siphon/http_util.py in get_query(self, query)
408 """
409 url = self._base[:-1] if self._base[-1] == '/' else self._base
--> 410 return self.get(url, query)
411
412 def url_path(self, path):
~/checkouts/readthedocs.org/user_builds/pvlib-python/envs/stable/lib/python3.7/site-packages/siphon/http_util.py in get(self, path, params)
493 'Server Error ({1:d}: {2})'.format(resp.request.url,
494 resp.status_code,
--> 495 text))
496 return resp
497
HTTPError: Error accessing https://thredds.ucar.edu/thredds/ncss/grid/grib/NCEP/GFS/Global_0p5deg/Best?var=Medium_cloud_cover_middle_cloud_Mixed_intervals_Average&var=Temperature_surface&var=v-component_of_wind_isobaric&var=High_cloud_cover_high_cloud_Mixed_intervals_Average&var=u-component_of_wind_isobaric&var=Downward_Short-Wave_Radiation_Flux_surface_Mixed_intervals_Average&var=Wind_speed_gust_surface&var=Total_cloud_cover_boundary_layer_cloud_Mixed_intervals_Average&var=Low_cloud_cover_low_cloud_Mixed_intervals_Average&var=Total_cloud_cover_entire_atmosphere_Mixed_intervals_Average&var=Total_cloud_cover_convective_cloud&time_start=2023-03-18T07%3A00%3A00%2B00%3A00&time_end=2023-03-25T07%3A00%3A00%2B00%3A00&longitude=-110.9&latitude=32.2&vertCoord=100000&accept=netcdf
Server Error (500: Throwable exception handled : org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.AssertionError: Multiple feature collections cannot be written as a CF dataset
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1086)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:670)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:779)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:337)
at thredds.servlet.filter.RequestBracketingLogMessageFilter.doFilter(RequestBracketingLogMessageFilter.java:50)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at thredds.servlet.filter.RequestQueryFilter.doFilter(RequestQueryFilter.java:90)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at thredds.servlet.filter.HttpHeadFilter.doFilter(HttpHeadFilter.java:47)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:221)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:177)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:687)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360)
at org.apache.coyote.ajp.AjpProcessor.service(AjpProcessor.java:433)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:891)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1784)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.lang.AssertionError: Multiple feature collections cannot be written as a CF dataset
at thredds.server.ncss.view.dsg.station.StationSubsetWriterNetcdf.<init>(StationSubsetWriterNetcdf.java:43)
at thredds.server.ncss.view.dsg.DsgSubsetWriterFactory.newStationInstance(DsgSubsetWriterFactory.java:87)
at thredds.server.ncss.view.dsg.DsgSubsetWriterFactory.newInstance(DsgSubsetWriterFactory.java:42)
at thredds.server.ncss.controller.NcssGridController.handleRequestGridAsPoint(NcssGridController.java:202)
at thredds.server.ncss.controller.NcssGridController.handleRequest(NcssGridController.java:98)
at jdk.internal.reflect.GeneratedMethodAccessor84.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071)
... 44 more
)
In [11]: print(raw_data.head())
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-11-634079c84f2d> in <module>
----> 1 print(raw_data.head())
NameError: name 'raw_data' is not defined
It will be useful to process this data before using it with pvlib. For example, the column names are non-standard, the temperature is in Kelvin, the wind speed is broken into east/west and north/south components, and most importantly, most of the irradiance data is missing. The forecast module provides a number of methods to fix these problems.
In [12]: data = raw_data
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-12-42913fd6f823> in <module>
----> 1 data = raw_data
NameError: name 'raw_data' is not defined
# rename the columns according the key/value pairs in model.variables.
In [13]: data = model.rename(data)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-13-866842c1d864> in <module>
----> 1 data = model.rename(data)
NameError: name 'data' is not defined
# convert temperature
In [14]: data['temp_air'] = model.kelvin_to_celsius(data['temp_air'])
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-14-a99ee6715c3b> in <module>
----> 1 data['temp_air'] = model.kelvin_to_celsius(data['temp_air'])
NameError: name 'data' is not defined
# convert wind components to wind speed
In [15]: data['wind_speed'] = model.uv_to_speed(data)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-15-8d56658f3ef3> in <module>
----> 1 data['wind_speed'] = model.uv_to_speed(data)
NameError: name 'data' is not defined
# calculate irradiance estimates from cloud cover.
# uses a cloud_cover to ghi to dni model or a
# uses a cloud cover to transmittance to irradiance model.
# this step is discussed in more detail in the next section
In [16]: irrad_data = model.cloud_cover_to_irradiance(data['total_clouds'])
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-16-e2c5ef73f145> in <module>
----> 1 irrad_data = model.cloud_cover_to_irradiance(data['total_clouds'])
NameError: name 'data' is not defined
In [17]: data = data.join(irrad_data, how='outer')
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-17-ac2b0be819f0> in <module>
----> 1 data = data.join(irrad_data, how='outer')
NameError: name 'data' is not defined
# keep only the final data
In [18]: data = data[model.output_variables]
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-18-326034b3c0c7> in <module>
----> 1 data = data[model.output_variables]
NameError: name 'data' is not defined
In [19]: print(data.head())
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-19-b952a40701c3> in <module>
----> 1 print(data.head())
NameError: name 'data' is not defined
Much better.
The GFS class’s
process_data()
method combines these steps
in a single function. In fact, each forecast model class
implements its own process_data
method since the data from each
weather model is slightly different. The process_data
functions are
designed to be explicit about how the data is being processed, and users
are strongly encouraged to read the source code of these methods.
In [20]: data = model.process_data(raw_data)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-20-3e2b66d76339> in <module>
----> 1 data = model.process_data(raw_data)
NameError: name 'raw_data' is not defined
In [21]: print(data.head())
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-21-b952a40701c3> in <module>
----> 1 print(data.head())
NameError: name 'data' is not defined
Users can easily implement their own process_data
methods on
inherited classes or implement similar stand-alone functions.
The forecast model classes also implement a
get_processed_data()
method that
combines the get_data()
and
process_data()
calls.
In [22]: data = model.get_processed_data(latitude, longitude, start, end)
---------------------------------------------------------------------------
HTTPError Traceback (most recent call last)
<ipython-input-22-0acd31bc63ff> in <module>
----> 1 data = model.get_processed_data(latitude, longitude, start, end)
~/checkouts/readthedocs.org/user_builds/pvlib-python/checkouts/stable/pvlib/forecast.py in get_processed_data(self, *args, **kwargs)
337 Processed forecast data
338 """
--> 339 return self.process_data(self.get_data(*args, **kwargs), **kwargs)
340
341 def rename(self, data, variables=None):
~/checkouts/readthedocs.org/user_builds/pvlib-python/checkouts/stable/pvlib/forecast.py in get_data(self, latitude, longitude, start, end, vert_level, query_variables, close_netcdf_data, **kwargs)
290 self.query.accept(self.data_format)
291
--> 292 self.netcdf_data = self.ncss.get_data(self.query)
293
294 # might be better to go to xarray here so that we can handle
~/checkouts/readthedocs.org/user_builds/pvlib-python/envs/stable/lib/python3.7/site-packages/siphon/ncss.py in get_data(self, query)
112
113 """
--> 114 resp = self.get_query(query)
115 return response_handlers(resp, self.unit_handler)
116
~/checkouts/readthedocs.org/user_builds/pvlib-python/envs/stable/lib/python3.7/site-packages/siphon/http_util.py in get_query(self, query)
408 """
409 url = self._base[:-1] if self._base[-1] == '/' else self._base
--> 410 return self.get(url, query)
411
412 def url_path(self, path):
~/checkouts/readthedocs.org/user_builds/pvlib-python/envs/stable/lib/python3.7/site-packages/siphon/http_util.py in get(self, path, params)
493 'Server Error ({1:d}: {2})'.format(resp.request.url,
494 resp.status_code,
--> 495 text))
496 return resp
497
HTTPError: Error accessing https://thredds.ucar.edu/thredds/ncss/grid/grib/NCEP/GFS/Global_0p5deg/Best?var=Downward_Short-Wave_Radiation_Flux_surface_Mixed_intervals_Average&var=Wind_speed_gust_surface&var=Low_cloud_cover_low_cloud_Mixed_intervals_Average&var=Total_cloud_cover_entire_atmosphere_Mixed_intervals_Average&var=Total_cloud_cover_convective_cloud&var=Medium_cloud_cover_middle_cloud_Mixed_intervals_Average&var=Temperature_surface&var=v-component_of_wind_isobaric&var=High_cloud_cover_high_cloud_Mixed_intervals_Average&var=u-component_of_wind_isobaric&var=Total_cloud_cover_boundary_layer_cloud_Mixed_intervals_Average&time_start=2023-03-18T07%3A00%3A00%2B00%3A00&time_end=2023-03-25T07%3A00%3A00%2B00%3A00&longitude=-110.9&latitude=32.2&vertCoord=100000&accept=netcdf
Server Error (500: Throwable exception handled : org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.AssertionError: Multiple feature collections cannot be written as a CF dataset
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1086)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:670)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:779)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:337)
at thredds.servlet.filter.RequestBracketingLogMessageFilter.doFilter(RequestBracketingLogMessageFilter.java:50)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at thredds.servlet.filter.RequestQueryFilter.doFilter(RequestQueryFilter.java:90)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at thredds.servlet.filter.HttpHeadFilter.doFilter(HttpHeadFilter.java:47)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:221)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:177)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:687)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360)
at org.apache.coyote.ajp.AjpProcessor.service(AjpProcessor.java:433)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:891)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1784)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.lang.AssertionError: Multiple feature collections cannot be written as a CF dataset
at thredds.server.ncss.view.dsg.station.StationSubsetWriterNetcdf.<init>(StationSubsetWriterNetcdf.java:43)
at thredds.server.ncss.view.dsg.DsgSubsetWriterFactory.newStationInstance(DsgSubsetWriterFactory.java:87)
at thredds.server.ncss.view.dsg.DsgSubsetWriterFactory.newInstance(DsgSubsetWriterFactory.java:42)
at thredds.server.ncss.controller.NcssGridController.handleRequestGridAsPoint(NcssGridController.java:202)
at thredds.server.ncss.controller.NcssGridController.handleRequest(NcssGridController.java:98)
at jdk.internal.reflect.GeneratedMethodAccessor84.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071)
... 44 more
)
In [23]: print(data.head())
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-23-b952a40701c3> in <module>
----> 1 print(data.head())
NameError: name 'data' is not defined
Cloud cover and radiation#
All of the weather models currently accessible by pvlib include one or more cloud cover forecasts. For example, below we plot the GFS cloud cover forecasts.
# plot cloud cover percentages
In [24]: cloud_vars = ['total_clouds', 'low_clouds',
....: 'mid_clouds', 'high_clouds']
....:
In [25]: data[cloud_vars].plot();
In [26]: plt.ylabel('Cloud cover %');
In [27]: plt.xlabel('Forecast Time ({})'.format(tz));
In [28]: plt.title('GFS 0.5 deg forecast for lat={}, lon={}'
....: .format(latitude, longitude));
....:
In [29]: plt.legend();

However, many of forecast models do not include radiation components in their output fields, or if they do then the radiation fields suffer from poor solar position or radiative transfer algorithms. It is often more accurate to create empirically derived radiation forecasts from the weather models’ cloud cover forecasts.
pvlib python provides two basic ways to convert cloud cover forecasts to irradiance forecasts. One method assumes a linear relationship between cloud cover and GHI, applies the scaling to a clear sky climatology, and then uses the DISC model to calculate DNI. The second method assumes a linear relationship between cloud cover and atmospheric transmittance, and then uses the Campbell-Norman model to calculate GHI, DNI, and DHI [Cam98]. Campbell-Norman is an approximation of Liu-Jordan [Liu60].
Caveat emptor: these algorithms are not rigorously verified! The purpose of the forecast module is to provide a few exceedingly simple options for users to play with before they develop their own models. We strongly encourage pvlib users first read the source code and second to implement new cloud cover to irradiance algorithms.
The essential parts of the clear sky scaling algorithm are as follows. Clear sky scaling of climatological GHI is also used in Larson et. al. [Lar16].
solpos = location.get_solarposition(cloud_cover.index)
cs = location.get_clearsky(cloud_cover.index, model='ineichen')
# offset and cloud cover in decimal units here
# larson et. al. use offset = 0.35
ghi = (offset + (1 - offset) * (1 - cloud_cover)) * ghi_clear
dni = disc(ghi, solpos['zenith'], cloud_cover.index)['dni']
dhi = ghi - dni * np.cos(np.radians(solpos['zenith']))
The figure below shows the result of the total cloud cover to irradiance conversion using the clear sky scaling algorithm.
# plot irradiance data
In [30]: data = model.rename(raw_data)
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-30-eb2d672f9ae8> in <module>
----> 1 data = model.rename(raw_data)
NameError: name 'raw_data' is not defined
In [31]: irrads = model.cloud_cover_to_irradiance(data['total_clouds'], how='clearsky_scaling')
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-31-a0ec160ac5de> in <module>
----> 1 irrads = model.cloud_cover_to_irradiance(data['total_clouds'], how='clearsky_scaling')
NameError: name 'data' is not defined
In [32]: irrads.plot();
In [33]: plt.ylabel('Irradiance ($W/m^2$)');
In [34]: plt.xlabel('Forecast Time ({})'.format(tz));
In [35]: plt.title('GFS 0.5 deg forecast for lat={}, lon={} using "clearsky_scaling"'
....: .format(latitude, longitude));
....:
In [36]: plt.legend();

The essential parts of the Campbell-Norman cloud cover to irradiance algorithm are as follows.
# cloud cover in percentage units here
transmittance = ((100.0 - cloud_cover) / 100.0) * 0.75
# irrads is a DataFrame containing ghi, dni, dhi
irrads = campbell_norman(apparent_zenith, transmittance)
The figure below shows the result of the Campbell-Norman total cloud cover to irradiance conversion.
# plot irradiance data
In [37]: irrads = model.cloud_cover_to_irradiance(data['total_clouds'], how='campbell_norman')
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-37-bcb5c732b78b> in <module>
----> 1 irrads = model.cloud_cover_to_irradiance(data['total_clouds'], how='campbell_norman')
NameError: name 'data' is not defined
In [38]: irrads.plot();
In [39]: plt.ylabel('Irradiance ($W/m^2$)');
In [40]: plt.xlabel('Forecast Time ({})'.format(tz));
In [41]: plt.title('GFS 0.5 deg forecast for lat={}, lon={} using "campbell_norman"'
....: .format(latitude, longitude));
....:
In [42]: plt.legend();

Most weather model output has a fairly coarse time resolution, at least an hour. The irradiance forecasts have the same time resolution as the weather data. However, it is straightforward to interpolate the cloud cover forecasts onto a higher resolution time domain, and then recalculate the irradiance.
In [43]: resampled_data = data.resample('5min').interpolate()
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-43-880ab2b0dc0f> in <module>
----> 1 resampled_data = data.resample('5min').interpolate()
NameError: name 'data' is not defined
In [44]: resampled_irrads = model.cloud_cover_to_irradiance(resampled_data['total_clouds'], how='clearsky_scaling')
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-44-6ff5301ab621> in <module>
----> 1 resampled_irrads = model.cloud_cover_to_irradiance(resampled_data['total_clouds'], how='clearsky_scaling')
NameError: name 'resampled_data' is not defined
In [45]: resampled_irrads.plot();
In [46]: plt.ylabel('Irradiance ($W/m^2$)');
In [47]: plt.xlabel('Forecast Time ({})'.format(tz));
In [48]: plt.title('GFS 0.5 deg forecast for lat={}, lon={} resampled'
....: .format(latitude, longitude));
....:
In [49]: plt.legend();

Users may then recombine resampled_irrads and resampled_data using
slicing pandas.concat()
or pandas.DataFrame.join()
.
We reiterate that the open source code enables users to customize the model processing to their liking.
- Lar16
Larson et. al. “Day-ahead forecasting of solar power output from photovoltaic plants in the American Southwest” Renewable Energy 91, 11-20 (2016).
- Cam98
Campbell, G. S., J. M. Norman (1998) An Introduction to Environmental Biophysics. 2nd Ed. New York: Springer.
- Liu60
B. Y. Liu and R. C. Jordan, The interrelationship and characteristic distribution of direct, diffuse, and total solar radiation, Solar Energy 4, 1 (1960).
Weather Models#
Next, we provide a brief description of the weather models available to pvlib users. Note that the figures are generated when this documentation is compiled so they will vary over time.
GFS#
The Global Forecast System (GFS) is the US model that provides forecasts for the entire globe. The GFS is updated every 6 hours. The GFS is run at two resolutions, 0.25 deg and 0.5 deg, and is available with 3 hour time resolution. Forecasts from GFS model were shown above. Use the GFS, among others, if you want forecasts for 1-7 days or if you want forecasts for anywhere on Earth.
HRRR#
The High Resolution Rapid Refresh (HRRR) model is perhaps the most accurate model, however, it is only available for ~15 hours. It is updated every hour and runs at 3 km resolution. The HRRR excels in severe weather situations. See the NOAA ESRL HRRR page for more information. Use the HRRR, among others, if you want forecasts for less than 24 hours. The HRRR model covers the continental United States.
In [50]: model = HRRR()
In [51]: data = model.get_processed_data(latitude, longitude, start, end)
---------------------------------------------------------------------------
HTTPError Traceback (most recent call last)
<ipython-input-51-0acd31bc63ff> in <module>
----> 1 data = model.get_processed_data(latitude, longitude, start, end)
~/checkouts/readthedocs.org/user_builds/pvlib-python/checkouts/stable/pvlib/forecast.py in get_processed_data(self, *args, **kwargs)
337 Processed forecast data
338 """
--> 339 return self.process_data(self.get_data(*args, **kwargs), **kwargs)
340
341 def rename(self, data, variables=None):
~/checkouts/readthedocs.org/user_builds/pvlib-python/checkouts/stable/pvlib/forecast.py in get_data(self, latitude, longitude, start, end, vert_level, query_variables, close_netcdf_data, **kwargs)
290 self.query.accept(self.data_format)
291
--> 292 self.netcdf_data = self.ncss.get_data(self.query)
293
294 # might be better to go to xarray here so that we can handle
~/checkouts/readthedocs.org/user_builds/pvlib-python/envs/stable/lib/python3.7/site-packages/siphon/ncss.py in get_data(self, query)
112
113 """
--> 114 resp = self.get_query(query)
115 return response_handlers(resp, self.unit_handler)
116
~/checkouts/readthedocs.org/user_builds/pvlib-python/envs/stable/lib/python3.7/site-packages/siphon/http_util.py in get_query(self, query)
408 """
409 url = self._base[:-1] if self._base[-1] == '/' else self._base
--> 410 return self.get(url, query)
411
412 def url_path(self, path):
~/checkouts/readthedocs.org/user_builds/pvlib-python/envs/stable/lib/python3.7/site-packages/siphon/http_util.py in get(self, path, params)
493 'Server Error ({1:d}: {2})'.format(resp.request.url,
494 resp.status_code,
--> 495 text))
496 return resp
497
HTTPError: Error accessing https://thredds.ucar.edu/thredds/ncss/grid/grib/NCEP/HRRR/CONUS_2p5km/Best?var=Total_cloud_cover_entire_atmosphere&var=Wind_speed_gust_surface&var=High_cloud_cover_high_cloud&var=Pressure_surface&var=Low_cloud_cover_low_cloud&var=Temperature_height_above_ground&var=v-component_of_wind_height_above_ground&var=Medium_cloud_cover_middle_cloud&var=u-component_of_wind_height_above_ground&time_start=2023-03-18T07%3A00%3A00%2B00%3A00&time_end=2023-03-25T07%3A00%3A00%2B00%3A00&longitude=-110.9&latitude=32.2&accept=netcdf
Server Error (400: Request contains variables with different feature types, which is not supported for writing in NETCDF3 format. Select a different format or choose variables that either uniformly have do not have a vertical dimension.)
In [52]: data[irrad_vars].plot();
In [53]: plt.ylabel('Irradiance ($W/m^2$)');
In [54]: plt.xlabel('Forecast Time ({})'.format(tz));
In [55]: plt.title('HRRR 3 km forecast for lat={}, lon={}'
....: .format(latitude, longitude));
....:
In [56]: plt.legend();

RAP#
The Rapid Refresh (RAP) model is the parent model for the HRRR. It is updated every hour and runs at 40, 20, and 13 km resolutions. Only the 20 and 40 km resolutions are currently available in pvlib. It is also excels in severe weather situations. See the NOAA ESRL HRRR page for more information. Use the RAP, among others, if you want forecasts for less than 24 hours. The RAP model covers most of North America.
In [57]: model = RAP()
In [58]: data = model.get_processed_data(latitude, longitude, start, end)
In [59]: data[irrad_vars].plot();
In [60]: plt.ylabel('Irradiance ($W/m^2$)');
In [61]: plt.xlabel('Forecast Time ({})'.format(tz));
In [62]: plt.title('RAP 13 km forecast for lat={}, lon={}'
....: .format(latitude, longitude));
....:
In [63]: plt.legend();

NAM#
The North American Mesoscale model covers, not surprisingly, North America. It is updated every 6 hours. pvlib provides access to 20 km resolution NAM data with a time horizon of up to 4 days.
In [64]: model = NAM()
In [65]: data = model.get_processed_data(latitude, longitude, start, end)
In [66]: data[irrad_vars].plot();
In [67]: plt.ylabel('Irradiance ($W/m^2$)');
In [68]: plt.xlabel('Forecast Time ({})'.format(tz));
In [69]: plt.title('NAM 20 km forecast for lat={}, lon={}'
....: .format(latitude, longitude));
....:
In [70]: plt.legend();

NDFD#
The National Digital Forecast Database is not a model, but rather a collection of forecasts made by National Weather Service offices across the country. It is updated every 6 hours. Use the NDFD, among others, for forecasts at all time horizons. The NDFD is available for the United States.
In [71]: model = NDFD()
In [72]: data = model.get_processed_data(latitude, longitude, start, end)
---------------------------------------------------------------------------
HTTPError Traceback (most recent call last)
<ipython-input-72-0acd31bc63ff> in <module>
----> 1 data = model.get_processed_data(latitude, longitude, start, end)
~/checkouts/readthedocs.org/user_builds/pvlib-python/checkouts/stable/pvlib/forecast.py in get_processed_data(self, *args, **kwargs)
337 Processed forecast data
338 """
--> 339 return self.process_data(self.get_data(*args, **kwargs), **kwargs)
340
341 def rename(self, data, variables=None):
~/checkouts/readthedocs.org/user_builds/pvlib-python/checkouts/stable/pvlib/forecast.py in get_data(self, latitude, longitude, start, end, vert_level, query_variables, close_netcdf_data, **kwargs)
290 self.query.accept(self.data_format)
291
--> 292 self.netcdf_data = self.ncss.get_data(self.query)
293
294 # might be better to go to xarray here so that we can handle
~/checkouts/readthedocs.org/user_builds/pvlib-python/envs/stable/lib/python3.7/site-packages/siphon/ncss.py in get_data(self, query)
112
113 """
--> 114 resp = self.get_query(query)
115 return response_handlers(resp, self.unit_handler)
116
~/checkouts/readthedocs.org/user_builds/pvlib-python/envs/stable/lib/python3.7/site-packages/siphon/http_util.py in get_query(self, query)
408 """
409 url = self._base[:-1] if self._base[-1] == '/' else self._base
--> 410 return self.get(url, query)
411
412 def url_path(self, path):
~/checkouts/readthedocs.org/user_builds/pvlib-python/envs/stable/lib/python3.7/site-packages/siphon/http_util.py in get(self, path, params)
493 'Server Error ({1:d}: {2})'.format(resp.request.url,
494 resp.status_code,
--> 495 text))
496 return resp
497
HTTPError: Error accessing https://thredds.ucar.edu/thredds/ncss/grid/grib/NCEP/NDFD/NWS/CONUS/CONDUIT/Best?var=Temperature_height_above_ground&var=Wind_speed_height_above_ground&var=Total_cloud_cover_surface&time_start=2023-03-18T07%3A00%3A00%2B00%3A00&time_end=2023-03-25T07%3A00%3A00%2B00%3A00&longitude=-110.9&latitude=32.2&accept=netcdf
Server Error (500: Throwable exception handled : org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.AssertionError: Multiple feature collections cannot be written as a CF dataset
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1086)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:670)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:779)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:337)
at thredds.servlet.filter.RequestBracketingLogMessageFilter.doFilter(RequestBracketingLogMessageFilter.java:50)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at thredds.servlet.filter.RequestQueryFilter.doFilter(RequestQueryFilter.java:90)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at thredds.servlet.filter.HttpHeadFilter.doFilter(HttpHeadFilter.java:47)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:221)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:177)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:687)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360)
at org.apache.coyote.ajp.AjpProcessor.service(AjpProcessor.java:433)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:891)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1784)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.lang.AssertionError: Multiple feature collections cannot be written as a CF dataset
at thredds.server.ncss.view.dsg.station.StationSubsetWriterNetcdf.<init>(StationSubsetWriterNetcdf.java:43)
at thredds.server.ncss.view.dsg.DsgSubsetWriterFactory.newStationInstance(DsgSubsetWriterFactory.java:87)
at thredds.server.ncss.view.dsg.DsgSubsetWriterFactory.newInstance(DsgSubsetWriterFactory.java:42)
at thredds.server.ncss.controller.NcssGridController.handleRequestGridAsPoint(NcssGridController.java:202)
at thredds.server.ncss.controller.NcssGridController.handleRequest(NcssGridController.java:98)
at jdk.internal.reflect.GeneratedMethodAccessor84.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071)
... 44 more
)
In [73]: data[irrad_vars].plot();
In [74]: plt.ylabel('Irradiance ($W/m^2$)');
In [75]: plt.xlabel('Forecast Time ({})'.format(tz));
In [76]: plt.title('NDFD forecast for lat={}, lon={}'
....: .format(latitude, longitude));
....: plt.legend();
....: plt.close();
....:
File "<ipython-input-76-1c7cdda0217f>", line 3
plt.legend();
^
IndentationError: unexpected indent
PV Power Forecast#
Finally, we demonstrate the application of the weather forecast data to a PV power forecast. Please see the remainder of the pvlib documentation for details.
In [77]: from pvlib.pvsystem import PVSystem, retrieve_sam
In [78]: from pvlib.temperature import TEMPERATURE_MODEL_PARAMETERS
In [79]: from pvlib.tracking import SingleAxisTracker
In [80]: from pvlib.modelchain import ModelChain
In [81]: sandia_modules = retrieve_sam('sandiamod')
In [82]: cec_inverters = retrieve_sam('cecinverter')
In [83]: module = sandia_modules['Canadian_Solar_CS5P_220M___2009_']
In [84]: inverter = cec_inverters['SMA_America__SC630CP_US__with_ABB_EcoDry_Ultra_transformer_']
In [85]: temperature_model_parameters = TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass']
# model a big tracker for more fun
In [86]: system = SingleAxisTracker(module_parameters=module, inverter_parameters=inverter, temperature_model_parameters=temperature_model_parameters, modules_per_string=15, strings_per_inverter=300)
# fx is a common abbreviation for forecast
In [87]: fx_model = GFS()
In [88]: fx_data = fx_model.get_processed_data(latitude, longitude, start, end)
---------------------------------------------------------------------------
HTTPError Traceback (most recent call last)
<ipython-input-88-16464ad503f1> in <module>
----> 1 fx_data = fx_model.get_processed_data(latitude, longitude, start, end)
~/checkouts/readthedocs.org/user_builds/pvlib-python/checkouts/stable/pvlib/forecast.py in get_processed_data(self, *args, **kwargs)
337 Processed forecast data
338 """
--> 339 return self.process_data(self.get_data(*args, **kwargs), **kwargs)
340
341 def rename(self, data, variables=None):
~/checkouts/readthedocs.org/user_builds/pvlib-python/checkouts/stable/pvlib/forecast.py in get_data(self, latitude, longitude, start, end, vert_level, query_variables, close_netcdf_data, **kwargs)
290 self.query.accept(self.data_format)
291
--> 292 self.netcdf_data = self.ncss.get_data(self.query)
293
294 # might be better to go to xarray here so that we can handle
~/checkouts/readthedocs.org/user_builds/pvlib-python/envs/stable/lib/python3.7/site-packages/siphon/ncss.py in get_data(self, query)
112
113 """
--> 114 resp = self.get_query(query)
115 return response_handlers(resp, self.unit_handler)
116
~/checkouts/readthedocs.org/user_builds/pvlib-python/envs/stable/lib/python3.7/site-packages/siphon/http_util.py in get_query(self, query)
408 """
409 url = self._base[:-1] if self._base[-1] == '/' else self._base
--> 410 return self.get(url, query)
411
412 def url_path(self, path):
~/checkouts/readthedocs.org/user_builds/pvlib-python/envs/stable/lib/python3.7/site-packages/siphon/http_util.py in get(self, path, params)
493 'Server Error ({1:d}: {2})'.format(resp.request.url,
494 resp.status_code,
--> 495 text))
496 return resp
497
HTTPError: Error accessing https://thredds.ucar.edu/thredds/ncss/grid/grib/NCEP/GFS/Global_0p5deg/Best?var=Medium_cloud_cover_middle_cloud_Mixed_intervals_Average&var=Temperature_surface&var=v-component_of_wind_isobaric&var=High_cloud_cover_high_cloud_Mixed_intervals_Average&var=u-component_of_wind_isobaric&var=Downward_Short-Wave_Radiation_Flux_surface_Mixed_intervals_Average&var=Wind_speed_gust_surface&var=Total_cloud_cover_boundary_layer_cloud_Mixed_intervals_Average&var=Low_cloud_cover_low_cloud_Mixed_intervals_Average&var=Total_cloud_cover_entire_atmosphere_Mixed_intervals_Average&var=Total_cloud_cover_convective_cloud&time_start=2023-03-18T07%3A00%3A00%2B00%3A00&time_end=2023-03-25T07%3A00%3A00%2B00%3A00&longitude=-110.9&latitude=32.2&vertCoord=100000&accept=netcdf
Server Error (500: Throwable exception handled : org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.AssertionError: Multiple feature collections cannot be written as a CF dataset
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1086)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:964)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:670)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:779)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:227)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:337)
at thredds.servlet.filter.RequestBracketingLogMessageFilter.doFilter(RequestBracketingLogMessageFilter.java:50)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at thredds.servlet.filter.RequestQueryFilter.doFilter(RequestQueryFilter.java:90)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at thredds.servlet.filter.HttpHeadFilter.doFilter(HttpHeadFilter.java:47)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:346)
at org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:221)
at org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:186)
at org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:354)
at org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:267)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.logging.log4j.web.Log4jServletFilter.doFilter(Log4jServletFilter.java:71)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:189)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:162)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:177)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:97)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:541)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:135)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)
at org.apache.catalina.valves.AbstractAccessLogValve.invoke(AbstractAccessLogValve.java:687)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:78)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:360)
at org.apache.coyote.ajp.AjpProcessor.service(AjpProcessor.java:433)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:65)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:891)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1784)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at org.apache.tomcat.util.threads.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1191)
at org.apache.tomcat.util.threads.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:659)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base/java.lang.Thread.run(Thread.java:829)
Caused by: java.lang.AssertionError: Multiple feature collections cannot be written as a CF dataset
at thredds.server.ncss.view.dsg.station.StationSubsetWriterNetcdf.<init>(StationSubsetWriterNetcdf.java:43)
at thredds.server.ncss.view.dsg.DsgSubsetWriterFactory.newStationInstance(DsgSubsetWriterFactory.java:87)
at thredds.server.ncss.view.dsg.DsgSubsetWriterFactory.newInstance(DsgSubsetWriterFactory.java:42)
at thredds.server.ncss.controller.NcssGridController.handleRequestGridAsPoint(NcssGridController.java:202)
at thredds.server.ncss.controller.NcssGridController.handleRequest(NcssGridController.java:98)
at jdk.internal.reflect.GeneratedMethodAccessor84.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:205)
at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:150)
at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:117)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:895)
at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:808)
at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1071)
... 44 more
)
# use a ModelChain object to calculate modeling intermediates
In [89]: mc = ModelChain(system, fx_model.location)
# extract relevant data for model chain
In [90]: mc.run_model(fx_data);
Now we plot a couple of modeling intermediates and the forecast power. Here’s the forecast plane of array irradiance…
In [91]: mc.results.total_irrad.plot();
In [92]: plt.ylabel('Plane of array irradiance ($W/m^2$)');
In [93]: plt.legend(loc='best');

…the cell and module temperature…
In [94]: mc.results.cell_temperature.plot();
In [95]: plt.ylabel('Cell Temperature (C)');

…and finally AC power…
In [96]: mc.results.ac.fillna(0).plot();
In [97]: plt.ylim(0, None);
In [98]: plt.ylabel('AC Power (W)');
