def test_pvengine_float_inputs_perez_transparency_spacing_full(params): """Test that module transparency and spacing are having the expected effect to calculated PV back side irradiance""" # Irradiance inputs timestamps = dt.datetime(2019, 6, 11, 11) DNI = 1000. DHI = 100. # --- with 0 transparency and spacing # Create models irr_params = {'module_transparency': 0., 'module_spacing_ratio': 0.} irradiance_model = HybridPerezOrdered(**irr_params) pvarray = OrderedPVArray.init_from_dict(params) eng = PVEngine(pvarray, irradiance_model=irradiance_model) # Fit engine eng.fit(timestamps, DNI, DHI, params['solar_zenith'], params['solar_azimuth'], params['surface_tilt'], params['surface_azimuth'], params['rho_ground']) # Run timestep pvarray = eng.run_full_mode(fn_build_report=lambda pvarray: pvarray) no_spacing_transparency_back_qinc = ( pvarray.ts_pvrows[1].back.get_param_weighted('qinc')) # --- with non-0 transparency and spacing # Create models irr_params = {'module_transparency': 0.1, 'module_spacing_ratio': 0.1} irradiance_model = HybridPerezOrdered(**irr_params) pvarray = OrderedPVArray.init_from_dict(params) eng = PVEngine(pvarray, irradiance_model=irradiance_model) # Fit engine eng.fit(timestamps, DNI, DHI, params['solar_zenith'], params['solar_azimuth'], params['surface_tilt'], params['surface_azimuth'], params['rho_ground']) # Run timestep pvarray = eng.run_full_mode(fn_build_report=lambda pvarray: pvarray) # Checks expected_back_qinc = 132.13881181118185 # higher than when params are 0 w_spacing_transparency_back_qinc = ( pvarray.ts_pvrows[1].back.get_param_weighted('qinc')) np.testing.assert_almost_equal( w_spacing_transparency_back_qinc, expected_back_qinc) assert no_spacing_transparency_back_qinc < w_spacing_transparency_back_qinc
def test_pvengine_ts_inputs_perez(params_serial, df_inputs_serial_calculation, fn_report_example): """Test that PV engine works for timeseries inputs""" # Break up inputs (timestamps, surface_tilt, surface_azimuth, solar_zenith, solar_azimuth, dni, dhi) = breakup_df_inputs(df_inputs_serial_calculation) albedo = params_serial['rho_ground'] # Create engine irradiance_model = HybridPerezOrdered() pvarray = OrderedPVArray.init_from_dict( params_serial, param_names=irradiance_model.params) eng = PVEngine(pvarray, irradiance_model=irradiance_model) # Fit engine eng.fit(timestamps, dni, dhi, solar_zenith, solar_azimuth, surface_tilt, surface_azimuth, albedo) # Run all timesteps report = eng.run_full_mode(fn_build_report=fn_report_example) # Check values np.testing.assert_array_almost_equal(report['qinc_front'], [1066.272392, 1065.979824]) np.testing.assert_array_almost_equal(report['qinc_back'], [135.897106, 136.01297]) np.testing.assert_array_almost_equal(report['iso_front'], [42.816637, 42.780206]) np.testing.assert_array_almost_equal(report['iso_back'], [1.727308, 1.726535]) np.testing.assert_array_almost_equal(report['qabs_back'], report['qinc_back'] * 0.97)
def test_run_fast_and_full_modes_sequentially(params, fn_report_example): """Make sure that can run fast and full modes one after the other without making the engine crash""" # Irradiance inputs timestamps = dt.datetime(2019, 6, 11, 11) DNI = 1000. DHI = 100. # Prepare some engine inputs pvarray = OrderedPVArray.init_from_dict(params) fast_mode_pvrow_index = 1 fast_mode_segment_index = 0 # Create engine object eng = PVEngine(pvarray, fast_mode_pvrow_index=fast_mode_pvrow_index, fast_mode_segment_index=fast_mode_segment_index) # Fit engine eng.fit(timestamps, DNI, DHI, params['solar_zenith'], params['solar_azimuth'], params['surface_tilt'], params['surface_azimuth'], params['rho_ground']) # Run fast mode def fn_report(pvarray): return (pvarray.ts_pvrows[1].back.get_param_weighted('qinc')) qinc_fast = eng.run_fast_mode(fn_build_report=fn_report) # Run full mode report = eng.run_full_mode(fn_build_report=fn_report_example) np.testing.assert_allclose(qinc_fast, 119.095285) np.testing.assert_allclose(report['qinc_back'], 116.49050349491)
def test_pvengine_float_inputs_iso(params): """Test that PV engine works for float inputs""" irradiance_model = IsotropicOrdered() pvarray = OrderedPVArray.init_from_dict(params) eng = PVEngine(pvarray, irradiance_model=irradiance_model) # Irradiance inputs timestamps = dt.datetime(2019, 6, 11, 11) DNI = 1000. DHI = 100. # Fit engine eng.fit(timestamps, DNI, DHI, params['solar_zenith'], params['solar_azimuth'], params['surface_tilt'], params['surface_azimuth'], params['rho_ground']) # Checks np.testing.assert_almost_equal(eng.irradiance.direct['front_illum_pvrow'], DNI) # Run timestep pvarray = eng.run_full_mode(fn_build_report=lambda pvarray: pvarray) # Checks assert isinstance(pvarray, OrderedPVArray) np.testing.assert_almost_equal( pvarray.ts_pvrows[0].front.get_param_weighted('qinc'), 1099.22245374) np.testing.assert_almost_equal( pvarray.ts_pvrows[1].front.get_param_weighted('qinc'), 1099.6948573) np.testing.assert_almost_equal( pvarray.ts_pvrows[2].front.get_param_weighted('qinc'), 1102.76149246) # Check absorbed np.testing.assert_almost_equal( pvarray.ts_pvrows[1].front.get_param_weighted('qabs'), 1099.6948573 * 0.99)
def test_pvengine_float_inputs_perez(params): """Test that PV engine works for float inputs""" irradiance_model = HybridPerezOrdered() pvarray = OrderedPVArray.init_from_dict(params) eng = PVEngine(pvarray, irradiance_model=irradiance_model) # Irradiance inputs timestamps = dt.datetime(2019, 6, 11, 11) DNI = 1000. DHI = 100. # Fit engine eng.fit(timestamps, DNI, DHI, params['solar_zenith'], params['solar_azimuth'], params['surface_tilt'], params['surface_azimuth'], params['rho_ground']) # Checks np.testing.assert_almost_equal(eng.irradiance.direct['front_illum_pvrow'], DNI) # Run timestep pvarray = eng.run_full_mode(fn_build_report=lambda pvarray: pvarray) # Checks assert isinstance(pvarray, OrderedPVArray) np.testing.assert_almost_equal( pvarray.ts_pvrows[0].front.get_param_weighted('qinc'), 1110.1164773159298) np.testing.assert_almost_equal( pvarray.ts_pvrows[1].front.get_param_weighted('qinc'), 1110.595903991) np.testing.assert_almost_equal( pvarray.ts_pvrows[2].front.get_param_weighted('qinc'), 1112.37717553) np.testing.assert_almost_equal( pvarray.ts_pvrows[1].back.get_param_weighted('qinc'), 116.49050349491208)
def test_check_direct_shading_continuity(): """Make sure the calculation is correct when direct shading happens. - before v1.3.0, there's a discontinuity (big jump) in prediction when direct shading happens. The values are the same as >=v1.3.0 for no direct shading, but they are different with direct shading. - starting at v1.3.0, the values are more continuous (no big change) when going from no direct shading to direct shading, which means it's most certainly a better implementation. The issue before v1.3.0 could be due to the fact that shadows are merged, and there might be a piece of geometry lost there, but not entirely sure. Since it's still relatively small, will not dig further. Here, we're testing the outputs at 2 timestamps, right before and right after direct shading, when varying only solar zenith. """ # Prepare inputs n = 2 inputs = { 'solar_zenith': [81.275, 81.276], # right at the limit of direct shadg 'solar_azimuth': [295.9557133] * n, 'surface_tilt': [15.18714669] * n, 'surface_azimuth': [270.] * n, 'dni': [1000.] * n, 'dhi': [100.] * n, 'albedo': [0.2] * n, 'times': [dt.datetime(2014, 6, 25, 3)] * n} # Array parameters params = {'n_pvrows': 3, 'axis_azimuth': 0.0, 'pvrow_height': 1.5, 'pvrow_width': 2.5, 'gcr': 0.4} # Create engine and fit to inputs pvarray = OrderedPVArray.init_from_dict(params) eng = PVEngine(pvarray) eng.fit(np.array(inputs['times']), np.array(inputs['dni']), np.array(inputs['dhi']), np.array(inputs['solar_zenith']), np.array(inputs['solar_azimuth']), np.array(inputs['surface_tilt']), np.array(inputs['surface_azimuth']), np.array(inputs['albedo'])) # Check there we are indeed right at the limit of direct shading np.testing.assert_array_equal(pvarray.has_direct_shading, [False, True]) # Run simulation and get output pvarray = eng.run_full_mode(fn_build_report=lambda pvarray: pvarray) out = pvarray.ts_pvrows[1].back.get_param_weighted('qinc') # Check expected outputs: before v1.3.0, expected output is # [20.4971271991293, 21.389095477613356], which shows discontinuity expected_out = [20.497127, 20.50229] np.testing.assert_allclose(out, expected_out)
def run_pvfactors_simulation(data: DataFrame, pvarray_params: dict) -> OrderedPVArray: "Fit and run simulation" pvarray = OrderedPVArray.init_from_dict(pvarray_params) engine = PVEngine(pvarray) pvarray = engine.fit(data.index, data.dni, data.dhi, data.zenith, data.azimuth, data.surface_tilt, data.surface_azimuth, data.albedo) fn_build_report = lambda pvarray: pvarray return engine.run_full_mode(fn_build_report)
def test_check_tilt_zero_discontinuity(): """ Before version 1.5.2, surface_tilt=0 with certain combinations of surface_azimuth and axis_azimuth showed anomolous behavior where the irradiance at zero tilt was significantly different from the irradiance at very small but nonzero tilts. Additionally, the calculated VF matrix could have values outside [0, 1]. See GH #125 """ # expected value calculated for surface_tilt=0.001, so should # not be significantly different from result for surface_tilt=0 rear_qinc_expected = 76.10 timestamps = np.array([dt.datetime(2019, 6, 1, 10)]) solar_azimuth = np.array([135]) solar_zenith = np.array([45]) dni = np.array([200]) dhi = np.array([400]) albedo = np.array([0.2]) surface_tilt = np.array([0.0]) # the discontinuity did not occur for all combinations of # (surface_azimuth, axis_azimuth), so test all four "primary" pairs: for surface_azimuth in [90, 270]: surface_azimuth = np.array([surface_azimuth]) for axis_azimuth in [0, 180]: params = dict(n_pvrows=3, axis_azimuth=axis_azimuth, pvrow_height=2, pvrow_width=1, gcr=0.4) pvarray = OrderedPVArray.init_from_dict(params) eng = PVEngine(pvarray) eng.fit(timestamps, dni, dhi, solar_zenith, solar_azimuth, surface_tilt, surface_azimuth, albedo) # Run simulation and get output eng.run_full_mode() out = pvarray.ts_pvrows[1].back.get_param_weighted('qinc') assert np.all(pvarray.ts_vf_matrix >= 0) assert np.all(pvarray.ts_vf_matrix <= 1) assert rear_qinc_expected == pytest.approx(out[0], abs=1e-2)
def test_engine_w_faoi_fn_in_irradiance_vfcalcs(params, pvmodule_canadian): """Run PV engine calcs with faoi functions for AOI losses""" # Irradiance inputs timestamps = dt.datetime(2019, 6, 11, 11) DNI = 1000. DHI = 100. pvarray = OrderedPVArray.init_from_dict(params) # create faoi function faoi_fn = faoi_fn_from_pvlib_sandia(pvmodule_canadian) # create vf_calculator with faoi function vfcalculator = VFCalculator(faoi_fn_front=faoi_fn, faoi_fn_back=faoi_fn) # create irradiance model with faoi function irradiance_model = HybridPerezOrdered(faoi_fn_front=faoi_fn, faoi_fn_back=faoi_fn) eng = PVEngine(pvarray, irradiance_model=irradiance_model, vf_calculator=vfcalculator) # Make sure aoi methods are available assert eng.vf_calculator.vf_aoi_methods is not None # Fit engine eng.fit(timestamps, DNI, DHI, params['solar_zenith'], params['solar_azimuth'], params['surface_tilt'], params['surface_azimuth'], params['rho_ground']) # Run timestep pvarray = eng.run_full_mode(fn_build_report=lambda pvarray: pvarray) # Checks np.testing.assert_almost_equal( pvarray.ts_pvrows[0].front.get_param_weighted('qinc'), 1110.1164773159298) np.testing.assert_almost_equal( pvarray.ts_pvrows[1].front.get_param_weighted('qinc'), 1110.595903991) np.testing.assert_almost_equal( pvarray.ts_pvrows[2].front.get_param_weighted('qinc'), 1112.37717553) np.testing.assert_almost_equal( pvarray.ts_pvrows[1].back.get_param_weighted('qinc'), 116.49050349491208) # Check absorbed irradiance: calculated using faoi functions np.testing.assert_almost_equal( pvarray.ts_pvrows[2].front.get_param_weighted('qabs'), [1109.1180884]) np.testing.assert_almost_equal( pvarray.ts_pvrows[1].back.get_param_weighted('qabs'), [114.2143503])
def pvfactors_engine_run(data, pvarray_parameters, parallel=0, mode='full'): """My wrapper function to launch the pvfactors engine in parallel. It is mostly for Windows use. In Linux you can directly call run_parallel_engine. It uses MyReportBuilder to generate the output. Args: data (pandas DataFrame): The data to fit the model. pvarray_parameters (dict): The pvfactors dict describing the simulation. parallel (int, optional): Number of threads to launch. Defaults to 0 (just calls PVEngine.run_all_timesteps) mode (str): full or fast depending on the type of back irraadiances. See pvfactors doc. Returns: pandas DataFrame: The results of the simulation, as desired in MyReportBuilder. """ n, row = _get_cut(pvarray_parameters['cut']) rb = Report(n, row) if parallel > 1: report = run_parallel_engine(rb, pvarray_parameters, data.index, data.dni, data.dhi, data.zenith, data.azimuth, data.surface_tilt, data.surface_azimuth, data.albedo, n_processes=parallel) else: pvarray = OrderedPVArray.init_from_dict(pvarray_parameters) engine = PVEngine(pvarray) engine.fit(data.index, data.dni, data.dhi, data.zenith, data.azimuth, data.surface_tilt, data.surface_azimuth, data.albedo, data.ghi) if mode == 'full': report = engine.run_full_mode(rb.build) else: report = engine.run_fast_mode(rb.build, pvrow_index=0, segment_index=0) df_report = pd.DataFrame(report, index=data.index).fillna(0) return df_report
def test_engine_variable_albedo(params, df_inputs_clearsky_8760): """Run PV engine calcs with variable albedo""" n_points = 100 # limiting because circleci is taking very long irradiance_model = HybridPerezOrdered() pvarray = OrderedPVArray.init_from_dict(params) eng = PVEngine(pvarray, irradiance_model=irradiance_model) # Manage timeseries inputs df_inputs = df_inputs_clearsky_8760[:n_points] # Get MET data timestamps = df_inputs.index dni = df_inputs.dni.values dhi = df_inputs.dhi.values solar_zenith = df_inputs.solar_zenith.values solar_azimuth = df_inputs.solar_azimuth.values surface_tilt = df_inputs.surface_tilt.values surface_azimuth = df_inputs.surface_azimuth.values albedo = np.linspace(0.01, 1, num=n_points) # Fit engine eng.fit(timestamps, dni, dhi, solar_zenith, solar_azimuth, surface_tilt, surface_azimuth, albedo) # Run timestep pvarray = eng.run_full_mode(fn_build_report=lambda pvarray: pvarray) # Check the bifacial gain values pvrow = pvarray.ts_pvrows[1] bfg = (np.nansum(pvrow.back.get_param_weighted('qinc')) / np.nansum(pvrow.front.get_param_weighted('qinc'))) * 100. bfg_after_aoi = (np.nansum(pvrow.back.get_param_weighted('qabs')) / np.nansum(pvrow.front.get_param_weighted('qabs'))) * 100. expected_bfg = 14.973198 expected_bfg_after_aoi = 14.670709 np.testing.assert_allclose(bfg, expected_bfg) np.testing.assert_allclose(bfg_after_aoi, expected_bfg_after_aoi)