Run full timeseries simulations

In this section, we will learn how to:

  • run full timeseries simulations using the PVEngine class, and visualize some of the results

  • run full timeseries simulations using the run_timeseries_engine() function

Imports and settings

[1]:
# Import external libraries
import os
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime
import pandas as pd
import warnings

# Settings
%matplotlib inline
np.set_printoptions(precision=3, linewidth=300)
warnings.filterwarnings('ignore')
# Paths
LOCAL_DIR = os.getcwd()
DATA_DIR = os.path.join(LOCAL_DIR, 'data')
filepath = os.path.join(DATA_DIR, 'test_df_inputs_MET_clearsky_tucson.csv')

Get timeseries inputs

[2]:
def export_data(fp):
    tz = 'US/Arizona'
    df = pd.read_csv(fp, index_col=0)
    df.index = pd.DatetimeIndex(df.index).tz_convert(tz)
    return df

df = export_data(filepath)
df_inputs = df.iloc[:24, :]
[3]:
# Plot the data
f, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(12, 3))
df_inputs[['dni', 'dhi']].plot(ax=ax1)
df_inputs[['solar_zenith', 'solar_azimuth']].plot(ax=ax2)
df_inputs[['surface_tilt', 'surface_azimuth']].plot(ax=ax3)
plt.show()
../_images/tutorials_Run_full_timeseries_simulations_6_0.png
[4]:
# Use a fixed albedo
albedo = 0.2

Prepare PV array parameters

[5]:
pvarray_parameters = {
    'n_pvrows': 3,            # number of pv rows
    'pvrow_height': 1,        # height of pvrows (measured at center / torque tube)
    'pvrow_width': 1,         # width of pvrows
    'axis_azimuth': 0.,       # azimuth angle of rotation axis
    'gcr': 0.4,               # ground coverage ratio
    'rho_front_pvrow': 0.01,  # pv row front surface reflectivity
    'rho_back_pvrow': 0.03,   # pv row back surface reflectivity
}

Run single timestep with PVEngine and inspect results

Instantiate the PVEngine class and fit it to the data

[6]:
from pvfactors.engine import PVEngine
from pvfactors.geometry import OrderedPVArray

# Create ordered PV array
pvarray = OrderedPVArray.init_from_dict(pvarray_parameters)
# Create engine
engine = PVEngine(pvarray)
# Fit engine to data
engine.fit(df_inputs.index, df_inputs.dni, df_inputs.dhi,
           df_inputs.solar_zenith, df_inputs.solar_azimuth,
           df_inputs.surface_tilt, df_inputs.surface_azimuth,
           albedo)

The user can run a simulation for a single timestep and plot the returned PV array

[7]:
# Get the PV array
pvarray = engine.run_full_mode(fn_build_report=lambda pvarray: pvarray)

# Plot pvarray shapely geometries
f, ax = plt.subplots(figsize=(10, 3))
pvarray.plot_at_idx(15, ax, with_surface_index=True)
ax.set_title(df.index[15])
plt.show()
../_images/tutorials_Run_full_timeseries_simulations_14_0.png

The user can inspect the results very easily thanks to the simple geometry API

[8]:
# Get the calculated outputs from the pv array
center_row_front_incident_irradiance = pvarray.ts_pvrows[1].front.get_param_weighted('qinc')
left_row_back_reflected_incident_irradiance = pvarray.ts_pvrows[0].back.get_param_weighted('reflection')
right_row_back_isotropic_incident_irradiance = pvarray.ts_pvrows[2].back.get_param_weighted('isotropic')

print("Incident irradiance on front surface of middle pv row: \n{} W/m2"
      .format(center_row_front_incident_irradiance))
print("Reflected irradiance on back surface of left pv row: \n{} W/m2"
      .format(left_row_back_reflected_incident_irradiance))
print("Isotropic irradiance on back surface of right pv row: \n{} W/m2"
      .format(right_row_back_isotropic_incident_irradiance))
Incident irradiance on front surface of middle pv row:
[    nan     nan     nan     nan     nan     nan     nan 117.633 587.344 685.115 652.526 616.77  618.875 656.024 685.556 550.172  87.66      nan     nan     nan     nan     nan     nan     nan] W/m2
Reflected irradiance on back surface of left pv row:
[   nan    nan    nan    nan    nan    nan    nan  8.375  6.597 39.275 58.563 68.346 64.176 47.593 32.984 25.216  7.044    nan    nan    nan    nan    nan    nan    nan] W/m2
Isotropic irradiance on back surface of right pv row:
[  nan   nan   nan   nan   nan   nan   nan 0.076 2.15  3.116 1.697 0.199 0.414 2.627 4.208 2.83  0.066   nan   nan   nan   nan   nan   nan   nan] W/m2

Run multiple timesteps with PVEngine

The users can also obtain a “report” that will look like whatever the users want, and which will rely on the simple geometry API shown above. Here is an example:

[9]:
# Create a function that will build a report
from pvfactors.report import example_fn_build_report

# Run full simulation
report = engine.run_full_mode(fn_build_report=example_fn_build_report)

# Print results (report is defined by report function passed by user)
df_report = pd.DataFrame(report, index=df_inputs.index)
df_report.iloc[6:11]
[9]:
qinc_front qinc_back iso_front iso_back
2019-01-01 07:00:00-07:00 NaN NaN NaN NaN
2019-01-01 08:00:00-07:00 117.632919 9.703464 5.070103 0.076232
2019-01-01 09:00:00-07:00 587.344197 4.906038 12.087407 2.150237
2019-01-01 10:00:00-07:00 685.115436 33.478098 17.516188 3.115967
2019-01-01 11:00:00-07:00 652.526254 52.534503 24.250780 1.697046
[10]:
f, ax = plt.subplots(1, 2, figsize=(10, 3))
df_report[['qinc_front', 'qinc_back']].plot(ax=ax[0])
df_report[['iso_front', 'iso_back']].plot(ax=ax[1])
plt.show()
../_images/tutorials_Run_full_timeseries_simulations_20_0.png
A function that builds a report needs to be specified, otherwise nothing will be returned by the simulation.
Here is an example of a report function that will return the total incident irradiance ('qinc') on the back surface of the rightmost PV row. A good way to get started building the reporting function is to use the example provided in the report.py module of the pvfactors package.
[11]:
def new_fn_build_report(pvarray): return {'total_inc_back': pvarray.ts_pvrows[1].back.get_param_weighted('qinc')}

Now we can run the timeseries simulation again using the same engine but a different report function.

[12]:
# Run full simulation using new report function
new_report = engine.run_full_mode(fn_build_report=new_fn_build_report)

# Print results
df_new_report = pd.DataFrame(new_report, index=df_inputs.index)
df_new_report.iloc[6:11]
[12]:
total_inc_back
2019-01-01 07:00:00-07:00 NaN
2019-01-01 08:00:00-07:00 9.703464
2019-01-01 09:00:00-07:00 4.906038
2019-01-01 10:00:00-07:00 33.478098
2019-01-01 11:00:00-07:00 52.534503
[13]:
f, ax = plt.subplots(figsize=(5, 3))
df_new_report.plot(ax=ax)
plt.show()
../_images/tutorials_Run_full_timeseries_simulations_25_0.png
We can see in the printed output the new report generated by the simulation run.
For convenience, we’ve been using dictionaries as the data structure holding the reports, but it could be anything else, like numpy arrays, pandas dataframes, etc.

Run one or multiple timesteps with the run_timeseries_engine() function

The same thing can be accomplished using a function from the run.py module of the pvfactors package.
But only the report will be returned.
[14]:
# import function
from pvfactors.run import run_timeseries_engine

# run simulation using new_fn_build_report
report_from_fn = run_timeseries_engine(new_fn_build_report, pvarray_parameters, df_inputs.index,
                                       df_inputs.dni, df_inputs.dhi,
                                       df_inputs.solar_zenith, df_inputs.solar_azimuth,
                                       df_inputs.surface_tilt, df_inputs.surface_azimuth,
                                       albedo)

# make a dataframe out of the report
df_report_from_fn = pd.DataFrame(report_from_fn, index=df_inputs.index)
[15]:
f, ax = plt.subplots(figsize=(5, 3))
df_report_from_fn.plot(ax=ax)
plt.show()
../_images/tutorials_Run_full_timeseries_simulations_30_0.png

The plot above shows that we get the same results as previously.