Clear Sky Examples

Overview of Functionality

The captest clear sky functionality is based entirely on wrapping and integrating the clear sky modeling capabilities of the pvlib-python package. The primary intent of this functionality is to provide the option to easily calculate and plot modeled clear sky data as part of the workflow of loading, visualizing, and validating data from pyranometers.

When setting clear_sky to True when calling the CapData load_data method, the csky function will be called when loading data and the modeled clear sky POA and GHI data will be added to the dataframe. The plot method will detect these columns and plot them as dashed lines with the measured irradiance data.

Below is a basic example of this functionality using data from the NREL Solar Radiation Research Library.

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

[1]:
import warnings
warnings.filterwarnings('ignore')
import pandas as pd
import captest as ct
from captest import capdata as pvc

from bokeh.io import output_notebook
output_notebook()
Loading BokehJS ...
[2]:
site = {
    'loc': {'latitude': 39.742, 'longitude': -105.18, 'altitude': 1828.8, 'tz': 'Etc/GMT+7'},
    'sys': {'surface_tilt': 40, 'surface_azimuth': 180, 'albedo': 0.2},
}
[3]:
meas_nrel = ct.load_data('./data/nrel_data.csv', site=site)
before calling get common timestep
1min
[4]:
meas_nrel.plot(default_groups=['irr_comb'], combine={'irr_comb':'poa|ghi'}, width=900)
[4]:

Viewing a few rows of the dataframe within the CapData object shows that the modeled clear sky POA and GHI irradiance have been added as columns when loading the data.

[5]:
meas_nrel.data['3/10/2019 12:00':'3/10/2019 12:03']
[5]:
Global CMP22 (vent/cor) [W/m^2] POA 40-South CMP11 [W/m^2] Deck Dry Bulb Temp [deg C] Avg Wind Speed @ 19ft [m/s] poa_mod_csky ghi_mod_csky
2019-03-10 12:00:00 811.138 1135.58 5.096 3.038 1141.491001 824.188005
2019-03-10 12:01:00 811.313 1137.08 5.081 3.213 1141.760946 824.394070
2019-03-10 12:02:00 813.923 1140.68 5.188 3.987 1142.005324 824.580929
2019-03-10 12:03:00 809.164 1134.86 5.172 3.350 1142.224130 824.748578

Explanation of csky Functions

The clear sky functionality is based around 4 top-level functions: - pvlib_location - pvlib_system - get_tz_index - csky

Location and System wrappers: pvlib_location and pvlib_system

The first two, pvlib_location and pvlib_system, are simply wrappers that generate pvlib location and system objects from a dictionary. The intent of providing these wrapper functions is to allow the captest user to be able to simply specify a dictionary for each without needing to import the entire pvlib package or know where the Location and system objects are within pvlib.

The location dictionary defined in the example can be used with the pvlib_location function to create a pvlib location object.

[6]:
site['loc']
[6]:
{'latitude': 39.742,
 'longitude': -105.18,
 'altitude': 1828.8,
 'tz': 'Etc/GMT+7'}
[7]:
bms_location = pvc.pvlib_location(site['loc'])
[8]:
type(bms_location)
[8]:
pvlib.location.Location
[9]:
bms_location
[9]:
Location:
  name: None
  latitude: 39.742
  longitude: -105.18
  altitude: 1828.8
  tz: Etc/GMT+7

Similarly, the pvlib_system function can be used to generate a pvlib system object.

Creating a pvlib ModelChain object requires providing a system object with module and inverter parameters defined. The captest pvlib_system function provides arbitrary module and inverter parameters, so the captest user does not need to find and specify these. The module and inverter parameters are not used when calculating the clear sky data.

The pvlib_system function also determines from the keywords of the passed dictionary whether the PVSystem pvlib object should have a FixedMount or a SingleAxisTrackerMount. There is a tracking system example below.

[10]:
site['sys']
[10]:
{'surface_tilt': 40, 'surface_azimuth': 180}
[11]:
bms_system = pvc.pvlib_system(site['sys'])
[12]:
type(bms_system)
[12]:
pvlib.pvsystem.PVSystem
[13]:
bms_system
[13]:
PVSystem:
  name: None
  Array:
    name: None
    mount: FixedMount(surface_tilt=40, surface_azimuth=180, racking_model=None, module_height=None)
    module: None
    albedo: 0.25
    module_type: None
    temperature_model_parameters: {'u_c': 29.0, 'u_v': 0.0}
    strings: 1
    modules_per_string: 1
  inverter: None
[14]:
bms_system.arrays[0].module_parameters.head()
[14]:
Vintage              2006
Area                1.312
Material            mc-Si
Cells_in_Series        72
Parallel_Strings        1
Name: Advent_Solar_AS160___2006_, dtype: object
[15]:
bms_system.inverter_parameters.head()
[15]:
Vac            208
Pso       2.089607
Paco         250.0
Pdco    259.588593
Vdco          40.0
Name: ABB__MICRO_0_25_I_OUTD_US_208__208V_, dtype: object

Get Timezone Index get_tz_index

The pvlib methods used to calculate clear sky irradiance require a time-zone aware datetime index as an argument. The get_tz_index function returns a time-zone aware datetime index given a datetime index or a dataframe. If the passed argument is already timezone aware, the index will be used as is; otherwise, the timezone will be set using the timezone in the location dictionary.

Be sure the timezone matches the timezone of the measured data.

For example, the time stamps of the example data from NREL are in MST and do not adjust for daylight savings, so specifying a timezone that does follow daylight savings, like ‘America/Denver’ will cause an error. Please refer to the pvlib documentaiton, which has a helpful section on timezones, for more information.

The example usage of get_tz_index below matches the usage within the csky function in the example above. The function returns a time-zone aware datetime index.

[16]:
print(meas_nrel.data.index.tz)
None
[17]:
pvc.get_tz_index(meas_nrel.data, site['loc'])
[17]:
DatetimeIndex(['2019-03-10 00:00:00-07:00', '2019-03-10 00:01:00-07:00',
               '2019-03-10 00:02:00-07:00', '2019-03-10 00:03:00-07:00',
               '2019-03-10 00:04:00-07:00', '2019-03-10 00:05:00-07:00',
               '2019-03-10 00:06:00-07:00', '2019-03-10 00:07:00-07:00',
               '2019-03-10 00:08:00-07:00', '2019-03-10 00:09:00-07:00',
               ...
               '2019-03-17 23:50:00-07:00', '2019-03-17 23:51:00-07:00',
               '2019-03-17 23:52:00-07:00', '2019-03-17 23:53:00-07:00',
               '2019-03-17 23:54:00-07:00', '2019-03-17 23:55:00-07:00',
               '2019-03-17 23:56:00-07:00', '2019-03-17 23:57:00-07:00',
               '2019-03-17 23:58:00-07:00', '2019-03-17 23:59:00-07:00'],
              dtype='datetime64[us, Etc/GMT+7]', length=11520, freq=None)

Passing the datetime index rather than the dataframe will return the same time-zone aware index.

[18]:
pvc.get_tz_index(meas_nrel.data.index, site['loc'])
[18]:
DatetimeIndex(['2019-03-10 00:00:00-07:00', '2019-03-10 00:01:00-07:00',
               '2019-03-10 00:02:00-07:00', '2019-03-10 00:03:00-07:00',
               '2019-03-10 00:04:00-07:00', '2019-03-10 00:05:00-07:00',
               '2019-03-10 00:06:00-07:00', '2019-03-10 00:07:00-07:00',
               '2019-03-10 00:08:00-07:00', '2019-03-10 00:09:00-07:00',
               ...
               '2019-03-17 23:50:00-07:00', '2019-03-17 23:51:00-07:00',
               '2019-03-17 23:52:00-07:00', '2019-03-17 23:53:00-07:00',
               '2019-03-17 23:54:00-07:00', '2019-03-17 23:55:00-07:00',
               '2019-03-17 23:56:00-07:00', '2019-03-17 23:57:00-07:00',
               '2019-03-17 23:58:00-07:00', '2019-03-17 23:59:00-07:00'],
              dtype='datetime64[us, Etc/GMT+7]', length=11520, freq=None)

The below example shows the behavior if the time_source, dataframe or datetime index, already has a timezone when passed to get_tz_index. The timezone of the time_source is used and a warning is raised to alert the user that the timezone of the time_source and the passed location dictionary do not match.

[19]:
df = meas_nrel.data.copy()
[20]:
df.index = df.index.tz_localize('America/Caracas')
[21]:
site['loc']
[21]:
{'latitude': 39.742,
 'longitude': -105.18,
 'altitude': 1828.8,
 'tz': 'Etc/GMT+7'}
[22]:
pvc.get_tz_index(df, site['loc'])
[22]:
DatetimeIndex(['2019-03-10 00:00:00-04:00', '2019-03-10 00:01:00-04:00',
               '2019-03-10 00:02:00-04:00', '2019-03-10 00:03:00-04:00',
               '2019-03-10 00:04:00-04:00', '2019-03-10 00:05:00-04:00',
               '2019-03-10 00:06:00-04:00', '2019-03-10 00:07:00-04:00',
               '2019-03-10 00:08:00-04:00', '2019-03-10 00:09:00-04:00',
               ...
               '2019-03-17 23:50:00-04:00', '2019-03-17 23:51:00-04:00',
               '2019-03-17 23:52:00-04:00', '2019-03-17 23:53:00-04:00',
               '2019-03-17 23:54:00-04:00', '2019-03-17 23:55:00-04:00',
               '2019-03-17 23:56:00-04:00', '2019-03-17 23:57:00-04:00',
               '2019-03-17 23:58:00-04:00', '2019-03-17 23:59:00-04:00'],
              dtype='datetime64[us, America/Caracas]', length=11520, freq=None)

Clear Sky Function csky

The clear sky function calls pvlib_location, pvlib_system, and get_tz_index and genarates pvlib objects to calculate modeled clear sky GHI and POA. The essential functionality is shown in the example where the clear sky POA and GHI are added to the imported data. csky can also return any componenet of the modeled csky data directly as shown below.

[23]:
poa_ghi = pvc.csky(meas_nrel.data, loc=site['loc'], sys=site['sys'], concat=False, output='both')
poa_ghi['3/10/2019 12:00':'3/10/2019 12:05']
[23]:
poa_mod_csky ghi_mod_csky
2019-03-10 12:00:00 1146.311585 824.188005
2019-03-10 12:01:00 1146.582735 824.394070
2019-03-10 12:02:00 1146.828206 824.580929
2019-03-10 12:03:00 1147.047993 824.748578
2019-03-10 12:04:00 1147.242092 824.897015
2019-03-10 12:05:00 1147.410500 825.026235
[24]:
all_irrad_comp = pvc.csky(meas_nrel.data, loc=site['loc'], sys=site['sys'], concat=False, output='all')
all_irrad_comp['3/10/2019 12:00':'3/10/2019 12:05']
[24]:
poa_global poa_direct poa_diffuse poa_sky_diffuse poa_ground_diffuse ghi dni dhi
2019-03-10 12:00:00 1146.311585 984.217429 162.094157 137.991236 24.102920 824.188005 987.437200 111.472601
2019-03-10 12:01:00 1146.582735 984.456650 162.126085 138.017138 24.108947 824.394070 987.479473 111.493626
2019-03-10 12:02:00 1146.828206 984.673210 162.154996 138.040585 24.114411 824.580929 987.517792 111.512690
2019-03-10 12:03:00 1147.047993 984.867103 162.180890 138.061576 24.119314 824.748578 987.552159 111.529795
2019-03-10 12:04:00 1147.242092 985.038326 162.203766 138.080111 24.123655 824.897015 987.582579 111.544938
2019-03-10 12:05:00 1147.410500 985.186876 162.223624 138.096190 24.127434 825.026235 987.609054 111.558122

Tracking System Example

It is possible to calculate the POA irradiance for a single-axis tracking system by defining location dictionary with tracker specific values.

[25]:
meas_nrel_trck = pvc.CapData('meas_nrel_track')
[26]:
site_tracker = {
    'loc': {'latitude': 39.742, 'longitude': -105.18, 'altitude': 1828.8, 'tz': 'Etc/GMT+7'},
    'sys': {'axis_tilt': 0, 'axis_azimuth': 0, 'max_angle': 52, 'backtrack': True, 'gcr': 0.2, 'albedo': 0.2},
}
[27]:
meas_nrel_trck = ct.load_data('./data/nrel_data.csv', site=site_tracker)
before calling get common timestep
1min
[28]:
meas_nrel_trck.plot(default_groups=['irr_comb'], combine={'irr_comb':'poa|ghi'}, width=900)
[28]: