This page was generated from docs/examples/captest_class.ipynb. Interactive online version: Binder badge

Capacity Test with CapTest Class

This example shows use of the CapTest class to conduct a capacity test. The CapTest class provides an entry point to define the type of test (regression formula) and define other test level parameters like the test tolerance and reporting conditions.

This example performs the same test as the Complete Capacity Testing example, but uses the run_test function to apply the filters and then displays the filtering steps visually using the scatter_filters method.

Imports

[1]:
import inspect
import warnings
# warnings.filterwarnings('ignore')
from IPython.display import display, Markdown

import pandas as pd

# import captest as pvc
import captest as ct
from captest import capdata as pvc
from captest.captest import CapTest
from bokeh.io import output_notebook, show

# uncomment below two lines to use cptest.scatter_hv in notebook
import holoviews as hv
hv.extension('bokeh')

#if working offline with the CapData.plot() method may fail
#run 'export BOKEH_RESOURCES=inline' at the command line before
#running the jupyter notebook

output_notebook()
Loading BokehJS ...

Load and Plot Measured Data

Load the measured data with the load_data method, which returns a CapData object. This example uses a column grouping defined in an excel file.

The test_setups function provides a convenient way to review the available test setups. Passing descriptions=True will show a written description of the test setups.

[2]:
ct.test_setups()
All options
============================================================
e2848_default
bifi_e2848_etotal_rear_shade_sim
bifi_e2848_etotal_rear_shade_meas
bifi_power_tc_meas_tbom
bifi_power_tc_calc_tbom
bifi_power_tc_etotal_rear_shade_sim
bifi_power_tc_etotal_rear_shade_meas
e2848_spec_corrected_poa
bifi_e2848_etotal_rear_shade_sim_spec_corrected
bifi_e2848_etotal_rear_shade_meas_spec_corrected
[3]:
## uncomment line below to review the full docstring with all parameters for the CapTest class
# display(Markdown(f"```\n{inspect.getdoc(CapTest)}\n```"))
[4]:
ts = CapTest.from_params(
    test_setup='e2848_default',
    meas_path='./data/example_meas_data.csv',
    sim_path='./data/pvsyst_example_HourlyRes_2.CSV',
    test_tolerance='+/- 3',
    ac_nameplate=6_000,
    meas_load_kwargs={
        'group_columns': './data/column_groups.xlsx',
    }
)
/home/docs/checkouts/readthedocs.org/user_builds/pvcaptest/checkouts/latest/src/captest/io.py:207: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  data_file = pd.read_csv(
/home/docs/checkouts/readthedocs.org/user_builds/pvcaptest/checkouts/latest/src/captest/io.py:237: UserWarning: Could not infer format, so each element will be parsed individually, falling back to `dateutil`. To ensure parsing is consistent and as-expected, please specify a format.
  data_file = pd.read_csv(
Aggregating the below 1 columns of the real_pwr_mtr group using the sum function. New column name: real_pwr_mtr_sum_agg:
    Example Project_Elkor Production Meter_Elkor Production Meter, KW


Aggregating the below 2 columns of the irr_poa group using the mean function. New column name: irr_poa_mean_agg:
    Example Project_Weather Station 1 (Standard w/ POA GHI)_Weather Station 1 (Standard w/ POA GHI), Sun
    Example Project_Weather Station 2 (Standard with POA GHI)_Weather Station 2 (Standard with POA GHI), Sun


Aggregating the below 2 columns of the temp_amb group using the mean function. New column name: temp_amb_mean_agg:
    Example Project_Weather Station 1 (Standard w/ POA GHI)_Weather Station 1 (Standard w/ POA GHI), TempF
    Example Project_Weather Station 2 (Standard with POA GHI)_Weather Station 2 (Standard with POA GHI), TempF


Aggregating the below 2 columns of the wind_speed group using the mean function. New column name: wind_speed_mean_agg:
    Example Project_Weather Station 1 (Standard w/ POA GHI)_Weather Station 1 (Standard w/ POA GHI), WindSpeed
    Example Project_Weather Station 2 (Standard with POA GHI)_Weather Station 2 (Standard with POA GHI), WindSpeed


[5]:
ts.meas.agg_sensors(agg_map={'real_pwr_inv':'sum'}, verbose=True)
Aggregating the below 8 columns of the real_pwr_inv group using the sum function. New column name: real_pwr_inv_sum_agg:
    Example Project_Inverter 1_Inverter 1, KW
    Example Project_Inverter 2_Inverter 2, KW
    Example Project_Inverter 3_Inverter 3, KW
    Example Project_Inverter 4_Inverter 4, KW
    Example Project_Inverter 5_Inverter 5, KW
    Example Project_Inverter 6_Inverter 6, KW
    Example Project_Inverter 7_Inverter 7, KW
    Example Project_Inverter 8_Inverter 8, KW


Note, the full functionality of the dashboard requires a live notebook. Try installing to run or using the launch binder button at the top of the page.

[6]:
combine = {'inv_sum_mtr_pwr': ['mtr', 'inv.*agg'], 'irr_all':['irr_poa', 'irr_ghi'], 'temp_all':['temp_amb', 'temp_mod']}
default_groups = ['inv_sum_mtr_pwr', 'irr_all', 'temp_all']
ts.meas.plot(combine=combine, default_groups=default_groups)
Matplotlib is building the font cache; this may take a moment.
[6]:

Filtering Measured Data

The CapData class provides a number of convience methods to apply filtering steps as defined in ASTM E2848. The following section demonstrates the use of the more commonly used filtering steps to remove measured data points.

[7]:
# Uncomment and run to copy over the filtered dataset with the unfiltered data.
ts.meas.reset_filter()
[8]:
measured_filters = [
    (pvc.CapData.filter_custom, (pd.DataFrame.dropna, ), {}),
    (pvc.CapData.filter_irr, (ts.min_irr, ts.max_irr), {}),
    (pvc.CapData.filter_outliers, (), {}),
    (pvc.CapData.fit_regression, (), {'filter':True, 'summary':False}),
    (pvc.CapData.rep_cond, (), {}),
    (pvc.CapData.filter_irr, (ts.rep_irr_filter_low, ts.rep_irr_filter_high), {'ref_val':'rep_irr'}),
    (pvc.CapData.fit_regression, (), {}),
]
[9]:
pvc.run_test(ts.meas, measured_filters)
NOTE: Regression used to filter outlying points.


Reporting conditions saved to rc attribute.
          poa      t_amb    w_vel
0  734.711919  25.331894  2.23974
                                 OLS Regression Results
=======================================================================================
Dep. Variable:                  power   R-squared (uncentered):                   0.999
Model:                            OLS   Adj. R-squared (uncentered):              0.999
Method:                 Least Squares   F-statistic:                          3.169e+04
Date:                Fri, 17 Jul 2026   Prob (F-statistic):                   6.69e-237
Time:                        21:20:36   Log-Likelihood:                         -2265.6
No. Observations:                 169   AIC:                                      4539.
Df Residuals:                     165   BIC:                                      4552.
Df Model:                           4
Covariance Type:            nonrobust
==================================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
----------------------------------------------------------------------------------
poa             8125.8043    256.472     31.683      0.000    7619.415    8632.193
I(poa * poa)      -0.4066      0.207     -1.961      0.052      -0.816       0.003
I(poa * t_amb)   -68.3047      7.544     -9.054      0.000     -83.201     -53.409
I(poa * w_vel)    -2.7849     14.492     -0.192      0.848     -31.399      25.829
==============================================================================
Omnibus:                        8.481   Durbin-Watson:                   0.797
Prob(Omnibus):                  0.014   Jarque-Bera (JB):                8.154
Skew:                          -0.485   Prob(JB):                       0.0170
Kurtosis:                       2.534   Cond. No.                     1.14e+04
==============================================================================

Notes:
[1] R² is computed without centering (uncentered) since the model does not contain a constant.
[2] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[3] The condition number is large, 1.14e+04. This might indicate that there are
strong multicollinearity or other numerical problems.
[10]:
ts.meas.get_summary()
[10]:
function_name pts_after_filter pts_removed filter_arguments
meas Custom Custom 1424 16 dropna()
Irradiance Irradiance 420 1004 high=1400, low=400
Outliers Outliers 403 17 EllipticEnvelope(support_fraction=0.9, contami...
Regression Regression 375 28 n_std=2
RepCond RepCond 375 0 front_poa=poa, irr_bal=False, percent_filter=20
Irradiance-1 Irradiance 169 206 high=1.2, low=0.8, ref_val=734.7119186629333
[11]:
(
    ts.meas.scatter_filters().opts(tools=['lasso_select']) +
    ts.meas.timeseries_filters().opts(width=1000)
)
[11]:
[12]:
ct.plotting.ScatterPlot(cd=ts.meas, timeseries=True, tc_power=True, tc_mode='replace', split_day=True).view()
/home/docs/checkouts/readthedocs.org/user_builds/pvcaptest/checkouts/latest/src/captest/plotting.py:1167: UserWarning: Column 'ghi_mod_csky' not found in data; falling back to split_time='12:30'.
  split_time = self.split_time or util.detect_solar_noon(self.cd.data)
[12]:

Load and Filter PVsyst Data

Running CapTest.from_params at the beginning of the notebook loaded the PVsyst data into CapTest.sim.data. So, we can begin with the PVsyst data filtering.

[13]:
# Write over cptest.flt_sim dataframe with a copy of the original unfiltered dataframe
ts.sim.reset_filter()
[14]:
simulated_filters = [
    (pvc.CapData.filter_time, (), {'test_date':'10/11/1990', 'days':ts.sim_days}),
    (pvc.CapData.filter_irr, (ts.min_irr, 930), {}),
    (pvc.CapData.filter_pvsyst, (), {}),
    (pvc.CapData.filter_irr, (ts.rep_irr_filter_low, ts.rep_irr_filter_high), {'ref_val':ts.meas.rc['poa'][0]}),
    (pvc.CapData.fit_regression, (), {}),
]
[15]:
pvc.run_test(ts.sim, simulated_filters)
                                 OLS Regression Results
=======================================================================================
Dep. Variable:                  power   R-squared (uncentered):                   1.000
Model:                            OLS   Adj. R-squared (uncentered):              1.000
Method:                 Least Squares   F-statistic:                          1.751e+06
Date:                Fri, 17 Jul 2026   Prob (F-statistic):                   1.84e-160
Time:                        21:20:37   Log-Likelihood:                         -745.80
No. Observations:                  68   AIC:                                      1500.
Df Residuals:                      64   BIC:                                      1508.
Df Model:                           4
Covariance Type:            nonrobust
==================================================================================
                     coef    std err          t      P>|t|      [0.025      0.975]
----------------------------------------------------------------------------------
poa             7793.7065     27.180    286.743      0.000    7739.408    7848.005
I(poa * poa)      -0.9763      0.028    -34.659      0.000      -1.033      -0.920
I(poa * t_amb)   -31.6192      0.574    -55.122      0.000     -32.765     -30.473
I(poa * w_vel)    -0.2145      1.391     -0.154      0.878      -2.994       2.565
==============================================================================
Omnibus:                        4.052   Durbin-Watson:                   1.444
Prob(Omnibus):                  0.132   Jarque-Bera (JB):                2.846
Skew:                          -0.340   Prob(JB):                        0.241
Kurtosis:                       2.263   Cond. No.                     8.89e+03
==============================================================================

Notes:
[1] R² is computed without centering (uncentered) since the model does not contain a constant.
[2] Standard Errors assume that the covariance matrix of the errors is correctly specified.
[3] The condition number is large, 8.89e+03. This might indicate that there are
strong multicollinearity or other numerical problems.
[16]:
ts.sim.get_summary()
[16]:
function_name pts_after_filter pts_removed filter_arguments
pvsyst Time Time 721 8039 days=30, drop=False, test_date=10/11/1990
Irradiance Irradiance 136 585 high=930, low=400
Pvsyst Pvsyst 136 0 Default arguments
Irradiance-1 Irradiance 68 68 high=1.2, low=0.8, ref_val=734.7119186629333
[17]:
ts.sim.scatter_filters()
[17]:

Results

The get_summary and captest_results_check_pvalues functions display the results of filtering on simulated and measured data and the final capacity test results comparing measured capacity to expected capacity, respectively.

[18]:
ts.get_summary()
[18]:
function_name pts_after_filter pts_removed filter_arguments
meas Custom Custom 1424 16 dropna()
Irradiance Irradiance 420 1004 high=1400, low=400
Outliers Outliers 403 17 EllipticEnvelope(support_fraction=0.9, contami...
Regression Regression 375 28 n_std=2
RepCond RepCond 375 0 front_poa=poa, irr_bal=False, percent_filter=20
Irradiance-1 Irradiance 169 206 high=1.2, low=0.8, ref_val=734.7119186629333
pvsyst Time Time 721 8039 days=30, drop=False, test_date=10/11/1990
Irradiance Irradiance 136 585 high=930, low=400
Pvsyst Pvsyst 136 0 Default arguments
Irradiance-1 Irradiance 68 68 high=1.2, low=0.8, ref_val=734.7119186629333
[19]:
ts.rc
[19]:
poa t_amb w_vel
0 734.711919 25.331894 2.23974
[20]:
ts.meas.print_points_summary(ts.hrs_req)
length of test period to date: 5 days
sufficient points have been collected. 150.0 points required; 169 points collected
[21]:
ts.captest_results_check_pvalues()
97.060% - Cap Ratio
101.910% - Cap Ratio after pval check
[21]:
  das_pvals sim_pvals das_params sim_params
poa 0.00000 0.00000 8,125.80431 7,793.70650
I(poa * poa) 0.05160 0.00000 -0.40662 -0.97634
I(poa * t_amb) 0.00000 0.00000 -68.30466 -31.61922
I(poa * w_vel) 0.84785 0.87797 -2.78493 -0.21446

The overlay_scatters function can be used to overlay the final scatter plots from scatter plots of all filtering steps produced above.

[22]:
ts.overlay_scatters()
[22]: