def test_calculate_back_horizon_shading(): arguments = { 'surface_azimuth': 0.0, 'surface_tilt': 30.0, 'gcr': 0.5, 'n_pvrows': 2, 'pvrow_height': 1.5, 'pvrow_width': 1., 'rho_ground': 0.2, 'rho_pvrow_back': 0.03, 'rho_pvrow_front': 0.01, 'solar_azimuth': 90.0, 'solar_zenith': 30.0, 'circumsolar_angle': 50., 'horizon_band_angle': 6.5, 'calculate_front_circ_horizon_shading': False, 'circumsolar_model': 'gaussian' } # Create shapely PV array array = Array(**arguments) # Test the horizon band shading part solar_zenith = 45. solar_azimuth = 90. surface_tilt = 30. surface_azimuth = 0. dni = 0. luminance_isotropic = 0. luminance_circumsolar = 0. poa_horizon = 1. poa_circumsolar = 0. array.update_irradiance_terms_perez(solar_zenith, solar_azimuth, surface_tilt, surface_azimuth, dni, luminance_isotropic, luminance_circumsolar, poa_horizon, poa_circumsolar) expected_horizon_shading = np.array([90.25751984, 0.0974248]) calculated_horizon_shading = array.surface_registry.query( 'pvrow_index==0 and surface_side=="back"')[[ 'horizon_band_shading_pct', 'horizon_term' ]].values[0] calculated_no_horizon_shading = array.surface_registry.query( 'pvrow_index==1 and surface_side=="back"')[[ 'horizon_band_shading_pct', 'horizon_term' ]].values[0] TOL = 1e-7 np.testing.assert_allclose(expected_horizon_shading, calculated_horizon_shading, atol=0, rtol=TOL) np.testing.assert_allclose(np.array([0., 1.]), calculated_no_horizon_shading, atol=0, rtol=TOL)
def test_plotting(): """ Check that the plotting functions are functional (only on local machine) """ is_ci = os.environ.get('CI', False) if not is_ci: import matplotlib.pyplot as plt from pvfactors.plot import plot_pvarray # Create array where sun vector is in the direction of the modules arguments = { 'n_pvrows': 3, 'pvrow_height': 1.5, 'solar_zenith': 30, 'solar_azimuth': 0., 'array_azimuth': 180., 'pvrow_width': 1., 'gcr': 0.3, 'array_tilt': 0. } array = Array(**arguments) f, ax = plt.subplots(figsize=(10, 5)) _ = plot_pvarray(ax, array) # Test with interrow forward shading arguments = { 'n_pvrows': 5, 'pvrow_height': 3.0, 'solar_zenith': 30, 'solar_azimuth': 180., 'array_azimuth': 180., 'pvrow_width': 3.0, 'gcr': 0.9, 'array_tilt': 20. } array = Array(**arguments) f, ax = plt.subplots() _ = plot_pvarray(ax, array) # Test with interrow backward shading arguments = { 'n_pvrows': 5, 'pvrow_height': 3.0, 'solar_zenith': 60, 'solar_azimuth': 0., 'array_azimuth': 180., 'pvrow_width': 3.0, 'gcr': 0.9, 'array_tilt': -20. } array = Array(**arguments) f, ax = plt.subplots() _ = plot_pvarray(ax, array) else: print("Not running 'test_plotting' in CI")
def test_consistent_qinc(): """ Test that the values of the calculated incident irradiance on all the surfaces (discretized or not) stays consistent """ arguments = { 'n_pvrows': 5, 'pvrow_height': 1., 'solar_zenith': 70, 'solar_azimuth': 180., 'surface_azimuth': 180., 'pvrow_width': 1.5, 'gcr': 0.6, 'surface_tilt': 30., 'cut': [(0, 5, 'front'), (4, 2, 'front')] } array = Array(**arguments) # Run a calculation for the given configuration dni = 1e3 dhi = 1e2 luminance_isotropic = dhi luminance_circumsolar = 0. poa_horizon = 0. poa_circumsolar = 0. solar_zenith = 20. solar_azimuth = 180. tracker_theta = 20. surface_azimuth = 180. array.calculate_radiosities_perez(solar_zenith, solar_azimuth, tracker_theta, surface_azimuth, dni, luminance_isotropic, luminance_circumsolar, poa_horizon, poa_circumsolar) # Compare to expected values expected_qinc = np.array([ 1103.10852238, 1097.25580244, 1096.27281294, 1095.61848916, 1093.44645666, 47.62848148, 37.3519236, 36.41100695, 36.49146269, 45.94191771, 993.12051239, 991.41490791, 991.3692459, 991.83044071, 1039.63791536, 1039.23551228, 1026.62401361, 49.74148561, 44.25032849, 43.80024727, 44.00294823, 76.10124014, 69.32324555, 69.60603804, 71.31511657, 93.12702949, 1103.09038367, 1103.07239864, 1103.05456561, 1103.03688292, 1097.08812485 ]) tol = 1e-8 np.testing.assert_allclose(array.surface_registry.qinc, expected_qinc, atol=0, rtol=tol, equal_nan=True)
def test_create_array(): """ Check that the pvrows know what's the index of their neighbors. """ # PV array parameters arguments = { 'n_pvrows': 3, 'pvrow_height': 1.5, 'pvrow_width': 1., 'gcr': 0.3, 'array_tilt': 20. } # Create vf array array = Array(**arguments) # Run some sanity checks on the creation of the vf array assert len(array.pvrows) == 3 assert isinstance(array.pvrows[0], PVRowLine) assert isinstance(array.pvrows[0].lines[0], LinePVArray) assert array.line_registry.shape[0] == 13 # Check that the expected neighbors are correct tol = 1e-8 expected_pvrow_neighbors = np.array([ np.nan, 0., 1., np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, 1., 2., np.nan ]) calculated_pvrow_neighbors = ( array.surface_registry.index_pvrow_neighbor.values) assert np.allclose(calculated_pvrow_neighbors, expected_pvrow_neighbors, atol=tol, rtol=0, equal_nan=True)
def test_discretized_surfaces(): """ Functional test to check that the discretization is working """ # Create vf pv array with discretization request arguments = { 'n_pvrows': 3, 'pvrow_height': 1.5, 'pvrow_width': 1., 'gcr': 0.4, 'array_tilt': 20., 'cut': [(0, 5, 'front'), (1, 3, 'front')] } array = Array(**arguments) # Check that the number of discretized surfaces is correct front_pvrow_0_has_5_surfaces = ( array.surface_registry.loc[ (array.surface_registry.pvrow_index == 0) & (array.surface_registry.surface_side == 'front'), :].shape[0] == 5 ) front_pvrow_1_has_3_surfaces = ( array.surface_registry.loc[ (array.surface_registry.pvrow_index == 1) & (array.surface_registry.surface_side == 'front'), :].shape[0] == 3 ) assert front_pvrow_0_has_5_surfaces & front_pvrow_1_has_3_surfaces
def test_interrow_shading(): """ Testing the ability of the model to find direct shading between pvrows """ # Forward direct shading of the pvrows arguments = { 'n_pvrows': 5, 'pvrow_height': 3., 'solar_zenith': 30, 'solar_azimuth': 180., 'array_azimuth': 180., 'pvrow_width': 3., 'gcr': 0.9, 'array_tilt': 20. } array = Array(**arguments) # There should be 4 pvrows with direct shading assert (array.line_registry.loc[ (array.line_registry.line_type == 'pvrow') & array.line_registry.shaded] .shape[0] == 4) # Backward direct shading of the pvrows (sun in the back of the modules) arguments = { 'n_pvrows': 5, 'pvrow_height': 3.0, 'solar_zenith': 60, 'solar_azimuth': 0., 'array_azimuth': 180., 'pvrow_width': 3.0, 'gcr': 0.9, 'array_tilt': -20. } array = Array(**arguments) # There should still be 4 pvrows with direct shading assert (array.line_registry.loc[ (array.line_registry.line_type == 'pvrow') & array.line_registry.shaded] .shape[0] == 4) print("Done.")
def test_view_factor_matrix(): """ Check that the calculation of view factors remains consistent """ # PV array parameters arguments = { 'n_pvrows': 2, 'pvrow_height': 1.5, 'solar_zenith': 30, 'solar_azimuth': 180., 'surface_azimuth': 180., 'pvrow_width': 1.0, 'gcr': 0.4, 'surface_tilt': 30. } array = Array(**arguments) # Expected values expected_vf_matrix = np.array([ [0., 0., 0., 0., 0., 0.06328772, 0., 0., 0., 0., 0., 0.93671228], [0., 0., 0., 0., 0., 0.0342757, 0., 0.01414893, 0., 0.05586108, 0., 0.89571429], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.23662653, 0.04159081, 0.72178267], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.04671626, 0.23662653, 0.71665722], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.10675226, 0.21878649, 0.67446125], [0.00064976, 0.0003519, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.99899834], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.00084338, 0.0032498, 0.99590682], [0., 0.00565957, 0., 0., 0., 0., 0., 0., 0., 0.09305653, 0., 0.9012839], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0.28720098, 0.00351237, 0.70928665], [0., 0.05586108, 0.27323278, 0.05394329, 0.14361376, 0., 0.08101242, 0.23264133, 0.11107537, 0., 0., 0.04861999], [0., 0., 0.04802493, 0.27323278, 0.29433335, 0., 0.31216481, 0., 0.00135841, 0., 0., 0.07088572], [0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.]]) # Perform comparison using very small absolute tolerance tol = 1e-8 assert np.allclose(array.vf_matrix, expected_vf_matrix, atol=tol, rtol=0)
def test_calculate_radiosities_serially_simple(): """ Check that the results of the radiosity calculation using the isotropic diffuse sky stay consistent """ # Simple sky and array configuration df_inputs = pd.DataFrame( np.array( [[80., 0., 70., 180., 1e3, 1e2], [20., 180., 40., 180., 1e3, 1e2], [70.4407256, 248.08690811, 42.4337927, 270., 1000., 100.]]), columns=['solar_zenith', 'solar_azimuth', 'array_tilt', 'array_azimuth', 'dni', 'dhi'], index=[0, 1, 2] ) arguments = { 'n_pvrows': 3, 'pvrow_height': 1.5, 'pvrow_width': 1., 'gcr': 0.3, } array = Array(**arguments) # Calculate irradiance terms df_outputs, df_bifacial_gains = ( calculate_radiosities_serially_simple(array, df_inputs)) # Check that the outputs are as expected expected_outputs_array = np.array([ [31.601748050014145, 6.289069752504206, 3.5833558115691035], [632.0349610002829, 125.78139505008411, 71.66711623138208], [2.2784386524603493, 31.554019855401, 28.05923970649779], [75.94795508201167, 1051.8006618467002, 935.3079902165931], [31.87339865348199, 6.377687102750911, 1.814318872353118], [637.4679730696398, 127.55374205501823, 36.286377447062364], [2.2047681015326277, 31.218033061227334, 27.857908527655677], [73.4922700510876, 1040.6011020409114, 928.596950921856], [46.79602079759552, 7.215187943800262, 2.1664217462458804], [935.9204159519105, 144.30375887600525, 43.328434924917616], [2.2998617834782267, 31.167227926438414, 27.776289194444438], [76.66205944927422, 1038.9075975479473, 925.8763064814813], [True, False, False]], dtype=object) tol = 1e-8 assert np.allclose(expected_outputs_array[:-1, :].astype(float), df_outputs.values[:-1, :].astype(float), atol=tol, rtol=0, equal_nan=True)
def test_serial_circumsolar_shading_calculation(): """ Calculate and save results from front surface circumsolar shading on pvrows. Test that it functions with the given data. """ # Choose a PV array configuration and pass the arguments necessary for # the calculation to be triggered: # eg 'calculate_front_circ_horizon_shading' arguments = { 'array_azimuth': 90.0, 'array_tilt': 20.0, 'cut': [(1, 5, 'front')], 'gcr': 0.3, 'n_pvrows': 2, 'pvrow_height': 1.5, 'pvrow_width': 1., 'rho_ground': 0.2, 'rho_pvrow_back': 0.03, 'rho_pvrow_front': 0.01, 'solar_azimuth': 90.0, 'solar_zenith': 30.0, 'circumsolar_angle': 50., 'horizon_band_angle': 6.5, 'calculate_front_circ_horizon_shading': True, 'circumsolar_model': 'gaussian' } save = (1, 'front') # Load inputs for the serial calculation test_file = os.path.join( TEST_DATA, 'file_test_serial_circumsolar_shading_calculation.csv') df_inputs = pd.read_csv(test_file, index_col=0) df_inputs.index = pd.DatetimeIndex(df_inputs.index) # Create shapely PV array array = Array(**arguments) # Run the calculation for functional testing df_outputs, df_bifacial, df_inputs_perez, df_outputs_segments = ( calculate_radiosities_serially_perez((arguments, df_inputs, save)) )
def test_view_matrix(): """ Test that the view matrix provides the expected views between surfaces """ # PV array parameters arguments = { 'n_pvrows': 2, 'pvrow_height': 1.5, 'solar_zenith': 30, 'solar_azimuth': 180., 'surface_azimuth': 180., 'pvrow_width': 1., 'gcr': 0.3, 'surface_tilt': 20. } array = Array(**arguments) # Expected values of the view matrix expected_view_matrix = np.array([ [0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 4], [0, 0, 0, 0, 0, 8, 0, 8, 0, 10, 0, 4], [0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 1], [9, 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 1], [0, 9, 0, 0, 0, 0, 0, 0, 0, 7, 0, 1], [0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 7, 1], [0, 10, 6, 6, 6, 0, 6, 6, 6, 0, 0, 5], [0, 0, 6, 6, 6, 0, 6, 0, 6, 0, 0, 5], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]) # Compare with expectations: make sure to remove the sky from the views n_shape = expected_view_matrix.shape[0] np.testing.assert_array_equal(array.view_matrix, expected_view_matrix) finite_surfaces_view_matrix = array.view_matrix[n_shape - 1:, n_shape - 1] # Make sure that the matrix is symmetric: # "if I can see you, you can see me" assert is_symmetric(finite_surfaces_view_matrix)
def test_merge_shadows(): """ When direct shading happens between pvrows, the shadow of the rows on the ground is supposed to form a continuous shadow (because of the overlap). Test that this functionally works """ # Use specific vf array configuration leading to direct shading arguments = { 'n_pvrows': 5, 'pvrow_height': 2., 'solar_zenith': 30, 'solar_azimuth': 0., 'array_azimuth': 180., 'pvrow_width': 3, 'gcr': 0.9, 'array_tilt': -20. } array = Array(**arguments) # There should be 1 continuous shadow on the groud, but 4 distinct ground # shaded areas, delimited by what the front and the back of the pvrows # can see assert ( array.line_registry.loc[(array.line_registry.line_type == 'ground') & array.line_registry.shaded].shape[0] == 4)
def test_irradiance_terms_perez_but_isotropic(): """ Check that the irradiance terms calculated for this configuration and using the isotropic diffuse sky dome is working as expected There is some direct shading on the back surface in this configuration """ # Simple sky and array configuration dni = 1e3 dhi = 1e2 solar_zenith = 80. solar_azimuth = 0. surface_tilt = 70. surface_azimuth = 180. arguments = { 'n_pvrows': 3, 'pvrow_height': 1.5, 'pvrow_width': 1., 'gcr': 0.4, } # Create vf array array = Array(**arguments) # Calculate irradiance terms array.update_view_factors(solar_zenith, solar_azimuth, surface_tilt, surface_azimuth) array.update_irradiance_terms_perez(solar_zenith, solar_azimuth, surface_tilt, surface_azimuth, dni, dhi, 0., 0., 0.) # Check that the values are as expected expected_irradiance_terms = np.array([ 0., 0., 0., 0., 173.64817767, 173.64817767, 173.64817767, 173.64817767, 173.64817767, 0., 0., 866.02540378, 866.02540378, 866.02540378, 100. ]) tol = 1e-8 np.testing.assert_allclose(array.irradiance_terms, expected_irradiance_terms, atol=tol, rtol=0)
def array_timeseries_calculate(pvarray_parameters, timestamps, solar_zenith, solar_azimuth, array_tilt, array_azimuth, dni, luminance_isotropic, luminance_circumsolar, poa_horizon, poa_circumsolar): """ Calculate the view factor radiosity and irradiance terms for multiple times. The function inputs assume a diffuse sky dome as represented in the Perez diffuse sky transposition model (from ``pvlib-python`` implementation). :param dict pvarray_parameters: parameters used to instantiate ``pvarray.Array`` class :param array-like timestamps: simulation timestamps :param array-like solar_zenith: solar zenith angles :param array-like solar_azimuth: solar azimuth angles :param array-like array_tilt: pv module tilt angles :param array-like array_azimuth: pv array azimuth angles :param array-like dni: values for direct normal irradiance :param array-like luminance_isotropic: luminance of the isotropic sky dome :param array-like luminance_circumsolar: luminance of circumsolar area :param array-like poa_horizon: POA irradiance horizon component :param array-like poa_circumsolar: POA irradiance circumsolar component :return: ``df_registries``. Concatenated form of the ``pvarray.Array`` surface registries. :rtype: :class:`pandas.DataFrame` """ # Instantiate array array = Array(**pvarray_parameters) # We want to save the whole registry for each timestamp list_registries = [] # We want to record the skipped_ts skipped_ts = [] # Use for printing progress n = len(timestamps) i = 1 for idx, ts in enumerate(timestamps): try: if ((isinstance(solar_zenith[idx], float)) & (solar_zenith[idx] <= 90.)): # Run calculation only if daytime array.calculate_radiosities_perez( solar_zenith[idx], solar_azimuth[idx], array_tilt[idx], array_azimuth[idx], dni[idx], luminance_isotropic[idx], luminance_circumsolar[idx], poa_horizon[idx], poa_circumsolar[idx]) # Save the whole registry registry = deepcopy(array.surface_registry) registry['timestamps'] = ts list_registries.append(registry) else: skipped_ts.append(ts) except Exception as err: LOGGER.debug("Unexpected error: {0}".format(err)) skipped_ts.append(ts) # Printing progress print_progress(i, n, prefix='Progress:', suffix='Complete', bar_length=50) i += 1 # Concatenate all surface registries into one dataframe if list_registries: if skipped_ts: df_skipped = pd.DataFrame( np.nan, columns=Array.registry_cols, index=range(len(skipped_ts))).assign(timestamps=skipped_ts) list_registries.append(df_skipped) df_registries = (pd.concat( list_registries, axis=0, join='outer', sort=False).sort_values(by=['timestamps']).reset_index(drop=True)) else: df_registries = pd.DataFrame( np.nan, columns=Array.registry_cols, index=range(len(timestamps))).assign(timestamps=timestamps) return df_registries
def test_array_calculate_timeseries(): """ Check that the timeseries results of the radiosity calculation using the isotropic diffuse sky approach stay consistent """ # Simple sky and array configuration df_inputs = pd.DataFrame( np.array([[80., 0., 70., 180., 1e3, 1e2], [20., 180., 40., 180., 1e3, 1e2], [70.4407256, 248.08690811, 42.4337927, 270., 1000., 100.]]), columns=[ 'solar_zenith', 'solar_azimuth', 'array_tilt', 'array_azimuth', 'dni', 'dhi' ], index=[0, 1, 2]) arguments = { 'n_pvrows': 3, 'pvrow_height': 1.5, 'pvrow_width': 1., 'gcr': 0.3, } array = Array(**arguments) # Break up inputs (timestamps, array_tilt, array_azimuth, solar_zenith, solar_azimuth, dni, dhi) = breakup_df_inputs(df_inputs) # Fill in the missing pieces luminance_isotropic = dhi luminance_circumsolar = np.zeros(len(timestamps)) poa_horizon = np.zeros(len(timestamps)) poa_circumsolar = np.zeros(len(timestamps)) # Run timeseries calculation df_registries = array_timeseries_calculate(arguments, timestamps, solar_zenith, solar_azimuth, array_tilt, array_azimuth, dni, luminance_isotropic, luminance_circumsolar, poa_horizon, poa_circumsolar) # Calculate surface averages for pvrows df_outputs = get_average_pvrow_outputs(df_registries, values=['q0', 'qinc'], include_shading=False) # Check that the outputs are as expected expected_outputs_array = np.array( [[31.60177482, 6.28906975, 3.58335581], [632.03549634, 125.78139505, 71.66711623], [2.27843869, 31.55401986, 28.05923971], [75.94795617, 1051.80066185, 935.30799022], [31.87339866, 6.3776871, 1.81431887], [637.46797317, 127.55374206, 36.28637745], [2.20476856, 31.21803306, 27.85790853], [73.49228524, 1040.60110204, 928.59695092], [46.7960208, 7.21518794, 2.16642175], [935.92041595, 144.30375888, 43.32843492], [2.29986192, 31.16722793, 27.77628919], [76.66206408, 1038.90759755, 925.87630648], [True, False, False]], dtype=object) tol = 1e-8 assert np.allclose(expected_outputs_array[:-1, :].astype(float), df_outputs.values.T, atol=tol, rtol=0, equal_nan=True)
def calculate_radiosities_serially_perez(args): """ Calculate the view factor radiosity and irradiance terms for multiple times. The calculations will be sequential, and they will assume a diffuse sky dome as calculated in the Perez diffuse sky transposition model (from ``pvlib-python`` implementation). :param args: tuple of at least two arguments: ``(arguments, df_inputs)``, where ``arguments`` is a ``dict`` that contains the array parameters used to instantiate a :class:`pvarray.Array` object, and ``df_inputs`` is a :class:`pandas.DataFrame` with the following columns: ['solar_zenith', 'solar_azimuth', 'array_tilt', 'array_azimuth', 'dhi', 'dni'], and with the following units: ['deg', 'deg', 'deg', 'deg', 'W/m2', 'W/m2']. A possible 3rd argument for the tuple is ``save_segments``, which is a ``tuple`` of two elements used to save all the irradiance terms calculated for one side of a PV row; e.g. ``(1, 'front')`` the first element is an ``int`` for the PV row index, and the second element a ``str`` to specify the side of the PV row, 'front' or 'back' :return: ``df_outputs, df_bifacial_gain, df_inputs_perez, ipoa_dict``; :class:`pandas.DataFrame` objects and ``dict`` where ``df_outputs`` contains *averaged* irradiance terms for all PV row sides and at each time stamp; ``df_bifacial_gain`` contains the calculation of back-surface over front-surface irradiance for all PV rows and at each time stamp; ``df_inputs_perez`` contains the intermediate input and output values from the Perez model calculation in ``pvlib-python``; ``ipoa_dict`` is not ``None`` only when the ``save_segments`` input is specified, and it is otherwise a ``dict`` where the keys are all the calculated irradiance terms' names, and the values are :class:`pandas.DataFrame` objects containing the calculated values for all the segments of the PV string side (it is a way of getting detailed values instead of averages) """ if len(args) == 3: arguments, df_inputs, save_segments = args else: arguments, df_inputs = args save_segments = None array = Array(**arguments) # Pre-process df_inputs to use the expected format of pvlib's perez model: # only positive tilt angles, and switching azimuth angles df_inputs_before_perez = df_inputs.copy() df_inputs_before_perez.loc[df_inputs.array_tilt < 0, 'array_azimuth'] = ( np.remainder( df_inputs_before_perez.loc[df_inputs.array_tilt < 0, 'array_azimuth'] + 180., 360.) ) df_inputs_before_perez.array_tilt = np.abs(df_inputs_before_perez .array_tilt) # Calculate the perez inputs df_inputs_perez = perez_diffuse_luminance(df_inputs_before_perez) # Post process: in vf model tilt angles can be negative and azimuth is # generally fixed df_inputs_perez.loc[:, ['array_azimuth', 'array_tilt']] = ( df_inputs.loc[:, ['array_azimuth', 'array_tilt']] ) # Create index df_outputs cols = ['q0', 'qinc', 'circumsolar_term', 'horizon_term', 'direct_term', 'irradiance_term', 'isotropic_term', 'reflection_term'] iterables = [ range(array.n_pvrows), ['front', 'back'], cols ] multiindex = pd.MultiIndex.from_product(iterables, names=['pvrow', 'side', 'term']) # Initialize df_outputs df_outputs = pd.DataFrame(np.nan, columns=df_inputs_perez.index, index=multiindex) df_outputs.sort_index(inplace=True) df_outputs.loc['array_is_shaded', :] = np.nan # Initialize df_outputs_segments if save_segments is not None: n_cols = len(array.pvrows[save_segments[0]].cut_points) cols_segments = range(n_cols + 1) irradiance_terms_segments = [ 'qinc', 'direct_term', 'circumsolar_term', 'horizon_term', 'isotropic_term', 'reflection_term', 'circumsolar_shading_pct', 'horizon_band_shading_pct' ] iterables_segments = [ irradiance_terms_segments, cols_segments ] multiindex_segments = pd.MultiIndex.from_product( iterables_segments, names=['irradiance_term', 'segment_index']) df_outputs_segments = pd.DataFrame(np.nan, columns=df_inputs_perez.index, index=multiindex_segments) df_outputs_segments.sort_index(inplace=True) df_outputs_segments = df_outputs_segments.transpose() else: df_outputs_segments = None idx_slice = pd.IndexSlice n = df_inputs_perez.shape[0] i = 1 for idx, row in df_inputs_perez.iterrows(): try: if ((isinstance(row['solar_zenith'], float)) & (row['solar_zenith'] <= 90.)): array.calculate_radiosities_perez(row['solar_zenith'], row['solar_azimuth'], row['array_tilt'], row['array_azimuth'], row['dni'], row['luminance_isotropic'], row['luminance_circumsolar'], row['poa_horizon'], row['poa_circumsolar']) # TODO: this will only work if there is no shading on the # surfaces # Format data to save all the surfaces for a pvrow if save_segments is not None: # Select the surface of the pv row with the segments and the # right columns df_pvrow = array.surface_registry.loc[ (array.surface_registry.pvrow_index == save_segments[0]) & (array.surface_registry.surface_side == save_segments[1]), irradiance_terms_segments + ['shaded'] ] # Check that no segment has direct shading before saving # results if df_pvrow.shaded.sum() == 0: # Add the data to the output variable by looping over # irradiance terms for irradiance_term in irradiance_terms_segments: df_outputs_segments.loc[ idx, idx_slice[irradiance_term, :]] = ( df_pvrow[irradiance_term].values ) # Format data to save averages for all pvrows and sides array.surface_registry.loc[:, cols] = ( array.surface_registry.loc[:, cols].apply( lambda x: x * array.surface_registry['area'], axis=0) ) df_summed = array.surface_registry.groupby( ['pvrow_index', 'surface_side']).sum() df_avg_irradiance = ( df_summed.div(df_summed['area'], axis=0).loc[ idx_slice[:, :], cols].sort_index().stack()) # # Assign values to df_outputs df_outputs.loc[idx_slice[:, :, cols], idx] = ( df_avg_irradiance.loc[idx_slice[:, :, cols]] ) df_outputs.loc['array_is_shaded', idx] = ( array.has_direct_shading ) except Exception as err: LOGGER.debug("Unexpected error: {0}".format(err)) print_progress(i, n, prefix='Progress:', suffix='Complete', bar_length=50) i += 1 try: bifacial_gains = (df_outputs.loc[ idx_slice[:, 'back', 'qinc'], :].values / df_outputs.loc[ idx_slice[:, 'front', 'qinc'], :].values) df_bifacial_gain = pd.DataFrame(bifacial_gains.T, index=df_inputs_perez.index, columns=range(array.n_pvrows)) except Exception as err: LOGGER.warning("Error in calculation of bifacial gain %s" % err) df_bifacial_gain = pd.DataFrame( np.nan * np.ones((len(df_inputs.index), array.n_pvrows)), index=df_inputs.index, columns=range(array.n_pvrows)) return (df_outputs.transpose(), df_bifacial_gain, df_inputs_perez, df_outputs_segments)