Solar PV Simulation Workflow¶
This example shows the different results of the different PV simulation workflows
Workflow:
- Import required packages
- Create locations DataFrame for single location
- Run simulation for single location
- Plot Capacity factor for single location
- Computate single location, only using ERA5 weather data
- Computate single location with Tracking
- Simulate multiple locations
In [1]:
Copied!
import reskit as rk
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
import reskit as rk
import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline
Single Location based on Sarah and ERA5 Data.¶
In [2]:
Copied!
# Make a dataframe for a single placement
placements = pd.DataFrame()
placements["lon"] = [
6.083,
] # Longitude
placements["lat"] = [
50.775,
] # Latitude
placements["tilt"] = [
32,
] # System tilt in degrees
placements["azimuth"] = [
180,
] # System azimuth in degrees
placements["capacity"] = [
2000,
] # Total system capacity in kW
placements["elev"] = [
250,
] # Altitude in meters
placements
# Make a dataframe for a single placement
placements = pd.DataFrame()
placements["lon"] = [
6.083,
] # Longitude
placements["lat"] = [
50.775,
] # Latitude
placements["tilt"] = [
32,
] # System tilt in degrees
placements["azimuth"] = [
180,
] # System azimuth in degrees
placements["capacity"] = [
2000,
] # Total system capacity in kW
placements["elev"] = [
250,
] # Altitude in meters
placements
Out[2]:
| lon | lat | tilt | azimuth | capacity | elev | |
|---|---|---|---|---|---|---|
| 0 | 6.083 | 50.775 | 32 | 180 | 2000 | 250 |
In [3]:
Copied!
out = rk.solar.openfield_pv_sarah_unvalidated(
placements=placements,
sarah_path=rk.TEST_DATA["sarah-like"],
era5_path=rk.TEST_DATA["era5-like"],
module="WINAICO WSx-240P6", # PV module to use for simulation
)
print("Output is of type:", type(out))
print("Variables in output:")
for i, var in enumerate(list(out.variables)):
print(" {i:2d}: {name:30s} - Dimensions:{dim}".format(i=i, name=var, dim=out[var].dims))
out = rk.solar.openfield_pv_sarah_unvalidated(
placements=placements,
sarah_path=rk.TEST_DATA["sarah-like"],
era5_path=rk.TEST_DATA["era5-like"],
module="WINAICO WSx-240P6", # PV module to use for simulation
)
print("Output is of type:", type(out))
print("Variables in output:")
for i, var in enumerate(list(out.variables)):
print(" {i:2d}: {name:30s} - Dimensions:{dim}".format(i=i, name=var, dim=out[var].dims))
FutureWarning: /home/docs/checkouts/readthedocs.org/user_builds/ethos-reskit/checkouts/align-docs-branding/reskit/solar/workflows/solar_workflow_manager.py:970 Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value 'N' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.FutureWarning: /home/docs/checkouts/readthedocs.org/user_builds/ethos-reskit/checkouts/align-docs-branding/reskit/weather/NCSource.py:258 ChainedAssignmentError: behaviour will change in pandas 3.0! You are setting values through chained assignment. Currently this works in certain cases, but when using Copy-on-Write (which will become the default behaviour in pandas 3.0) this will never work to update the original DataFrame or Series, because the intermediate object on which we are setting values will behave as a copy. A typical example is when you are setting values in a column of a DataFrame, like: df["col"][row_indexer] = value Use `df.loc[row_indexer, "col"] = values` instead, to perform the assignment in a single step and ensure this keeps updating the original `df`. See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
Output is of type: <class 'xarray.core.dataset.Dataset'>
Variables in output:
0: location - Dimensions:('location',)
1: lon - Dimensions:('location',)
2: lat - Dimensions:('location',)
3: tilt - Dimensions:('location',)
4: azimuth - Dimensions:('location',)
5: capacity - Dimensions:('location',)
6: elev - Dimensions:('location',)
7: time - Dimensions:('time',)
8: direct_normal_irradiance - Dimensions:('time', 'location')
9: global_horizontal_irradiance - Dimensions:('time', 'location')
10: surface_wind_speed - Dimensions:('time', 'location')
11: surface_pressure - Dimensions:('time', 'location')
12: surface_air_temperature - Dimensions:('time', 'location')
13: surface_dew_temperature - Dimensions:('time', 'location')
14: solar_azimuth - Dimensions:('time', 'location')
15: apparent_solar_zenith - Dimensions:('time', 'location')
16: extra_terrestrial_irradiance - Dimensions:('time', 'location')
17: air_mass - Dimensions:('time', 'location')
18: diffuse_horizontal_irradiance - Dimensions:('time', 'location')
19: angle_of_incidence - Dimensions:('time', 'location')
20: poa_global - Dimensions:('time', 'location')
21: poa_direct - Dimensions:('time', 'location')
22: poa_diffuse - Dimensions:('time', 'location')
23: poa_sky_diffuse - Dimensions:('time', 'location')
24: poa_ground_diffuse - Dimensions:('time', 'location')
25: cell_temperature - Dimensions:('time', 'location')
26: module_dc_power_at_mpp - Dimensions:('time', 'location')
27: module_dc_voltage_at_mpp - Dimensions:('time', 'location')
28: capacity_factor - Dimensions:('time', 'location')
29: total_system_generation - Dimensions:('time', 'location')
FutureWarning: /home/docs/checkouts/readthedocs.org/user_builds/ethos-reskit/checkouts/align-docs-branding/reskit/solar/workflows/solar_workflow_manager.py:970 Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value 'N' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.
In [4]:
Copied!
# Plot of the capacity factor time series for the single placement
out.capacity_factor.plot()
# Plot of the capacity factor time series for the single placement
out.capacity_factor.plot()
Out[4]:
[<matplotlib.lines.Line2D at 0x79d6a8fd0500>]
Simulate single location, but only using ERA-5¶
In [5]:
Copied!
out = rk.solar.openfield_pv_era5(
placements=placements,
era5_path=rk.TEST_DATA["era5-like"],
global_solar_atlas_ghi_path=rk.TEST_DATA["gsa-ghi-like.tif"],
global_solar_atlas_dni_path=rk.TEST_DATA["gsa-dni-like.tif"],
module="WINAICO WSx-240P6", # PV module to use for simulation
)
print("Output is of type:", type(out))
print("Variables in output:")
for i, var in enumerate(list(out.variables)):
print(" {i:2d}: {name:30s} - Dimensions:{dim}".format(i=i, name=var, dim=out[var].dims))
out = rk.solar.openfield_pv_era5(
placements=placements,
era5_path=rk.TEST_DATA["era5-like"],
global_solar_atlas_ghi_path=rk.TEST_DATA["gsa-ghi-like.tif"],
global_solar_atlas_dni_path=rk.TEST_DATA["gsa-dni-like.tif"],
module="WINAICO WSx-240P6", # PV module to use for simulation
)
print("Output is of type:", type(out))
print("Variables in output:")
for i, var in enumerate(list(out.variables)):
print(" {i:2d}: {name:30s} - Dimensions:{dim}".format(i=i, name=var, dim=out[var].dims))
FutureWarning: /home/docs/checkouts/readthedocs.org/user_builds/ethos-reskit/checkouts/align-docs-branding/reskit/solar/workflows/solar_workflow_manager.py:970 Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value 'N' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.FutureWarning: /home/docs/checkouts/readthedocs.org/user_builds/ethos-reskit/checkouts/align-docs-branding/reskit/weather/NCSource.py:258 ChainedAssignmentError: behaviour will change in pandas 3.0! You are setting values through chained assignment. Currently this works in certain cases, but when using Copy-on-Write (which will become the default behaviour in pandas 3.0) this will never work to update the original DataFrame or Series, because the intermediate object on which we are setting values will behave as a copy. A typical example is when you are setting values in a column of a DataFrame, like: df["col"][row_indexer] = value Use `df.loc[row_indexer, "col"] = values` instead, to perform the assignment in a single step and ensure this keeps updating the original `df`. See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
Output is of type: <class 'xarray.core.dataset.Dataset'>
Variables in output:
0: location - Dimensions:('location',)
1: lon - Dimensions:('location',)
2: lat - Dimensions:('location',)
3: tilt - Dimensions:('location',)
4: azimuth - Dimensions:('location',)
5: capacity - Dimensions:('location',)
6: elev - Dimensions:('location',)
7: LRA_factor_global_horizontal_irradiance - Dimensions:('location',)
8: LRA_factor_direct_normal_irradiance - Dimensions:('location',)
9: time - Dimensions:('time',)
10: global_horizontal_irradiance - Dimensions:('time', 'location')
11: direct_horizontal_irradiance - Dimensions:('time', 'location')
12: surface_wind_speed - Dimensions:('time', 'location')
13: surface_pressure - Dimensions:('time', 'location')
14: surface_air_temperature - Dimensions:('time', 'location')
15: surface_dew_temperature - Dimensions:('time', 'location')
16: snow_albedo - Dimensions:('time', 'location')
17: snow_density - Dimensions:('time', 'location')
18: snow_depth_water_equivalent - Dimensions:('time', 'location')
19: snowfall_water_equivalent - Dimensions:('time', 'location')
20: solar_azimuth - Dimensions:('time', 'location')
21: apparent_solar_zenith - Dimensions:('time', 'location')
22: direct_normal_irradiance - Dimensions:('time', 'location')
23: extra_terrestrial_irradiance - Dimensions:('time', 'location')
24: air_mass - Dimensions:('time', 'location')
25: diffuse_horizontal_irradiance - Dimensions:('time', 'location')
26: angle_of_incidence - Dimensions:('time', 'location')
27: poa_global - Dimensions:('time', 'location')
28: poa_direct - Dimensions:('time', 'location')
29: poa_diffuse - Dimensions:('time', 'location')
30: poa_sky_diffuse - Dimensions:('time', 'location')
31: poa_ground_diffuse - Dimensions:('time', 'location')
32: cell_temperature - Dimensions:('time', 'location')
33: module_dc_power_at_mpp - Dimensions:('time', 'location')
34: module_dc_voltage_at_mpp - Dimensions:('time', 'location')
35: capacity_factor - Dimensions:('time', 'location')
36: total_system_generation - Dimensions:('time', 'location')
FutureWarning: /home/docs/checkouts/readthedocs.org/user_builds/ethos-reskit/checkouts/align-docs-branding/reskit/solar/workflows/solar_workflow_manager.py:970 Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value 'N' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.
In [6]:
Copied!
out.capacity_factor.fillna(0).plot()
out.capacity_factor.fillna(0).plot()
Out[6]:
[<matplotlib.lines.Line2D at 0x79d6a8ca0170>]
Single Location with Tracking¶
In [7]:
Copied!
out = rk.solar.openfield_pv_era5(
placements=placements,
era5_path=rk.TEST_DATA["era5-like"],
global_solar_atlas_ghi_path=rk.TEST_DATA["gsa-ghi-like.tif"],
global_solar_atlas_dni_path=rk.TEST_DATA["gsa-dni-like.tif"],
module="WINAICO WSx-240P6", # Module to use for simulation
tracking="single_axis",
)
out = rk.solar.openfield_pv_era5(
placements=placements,
era5_path=rk.TEST_DATA["era5-like"],
global_solar_atlas_ghi_path=rk.TEST_DATA["gsa-ghi-like.tif"],
global_solar_atlas_dni_path=rk.TEST_DATA["gsa-dni-like.tif"],
module="WINAICO WSx-240P6", # Module to use for simulation
tracking="single_axis",
)
FutureWarning: /home/docs/checkouts/readthedocs.org/user_builds/ethos-reskit/checkouts/align-docs-branding/reskit/solar/workflows/solar_workflow_manager.py:970 Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value 'N' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.FutureWarning: /home/docs/checkouts/readthedocs.org/user_builds/ethos-reskit/checkouts/align-docs-branding/reskit/weather/NCSource.py:258 ChainedAssignmentError: behaviour will change in pandas 3.0! You are setting values through chained assignment. Currently this works in certain cases, but when using Copy-on-Write (which will become the default behaviour in pandas 3.0) this will never work to update the original DataFrame or Series, because the intermediate object on which we are setting values will behave as a copy. A typical example is when you are setting values in a column of a DataFrame, like: df["col"][row_indexer] = value Use `df.loc[row_indexer, "col"] = values` instead, to perform the assignment in a single step and ensure this keeps updating the original `df`. See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
FutureWarning: /home/docs/checkouts/readthedocs.org/user_builds/ethos-reskit/checkouts/align-docs-branding/reskit/solar/workflows/solar_workflow_manager.py:970 Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value 'N' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.
In [8]:
Copied!
out.capacity_factor.fillna(0).plot()
out.capacity_factor.fillna(0).plot()
Out[8]:
[<matplotlib.lines.Line2D at 0x79d6a8d3e930>]
Simulating multiple locations is the same as simulating a single location¶
- It is recommended to simulate multiple locations at once!
In [9]:
Copied!
# Make a placements dataframe with multiple locations
placements = pd.DataFrame()
placements["lon"] = [
6.083,
6.090,
6.095,
6.100,
6.105,
] # Longitude
placements["lat"] = [
50.775,
50.780,
50.785,
50.790,
50.795,
] # Latitude
placements["tilt"] = [
32,
25,
20,
35,
40,
] # System tilt in degrees
placements["azimuth"] = [
180,
180,
180,
180,
180,
] # System azimuth in degrees
placements["capacity"] = [
2000,
2000,
2000,
2000,
2000,
] # Total system capacity in kW
placements["elev"] = [
250,
300,
350,
1000,
-100,
] # Altitude in meters
placements
# Make a placements dataframe with multiple locations
placements = pd.DataFrame()
placements["lon"] = [
6.083,
6.090,
6.095,
6.100,
6.105,
] # Longitude
placements["lat"] = [
50.775,
50.780,
50.785,
50.790,
50.795,
] # Latitude
placements["tilt"] = [
32,
25,
20,
35,
40,
] # System tilt in degrees
placements["azimuth"] = [
180,
180,
180,
180,
180,
] # System azimuth in degrees
placements["capacity"] = [
2000,
2000,
2000,
2000,
2000,
] # Total system capacity in kW
placements["elev"] = [
250,
300,
350,
1000,
-100,
] # Altitude in meters
placements
Out[9]:
| lon | lat | tilt | azimuth | capacity | elev | |
|---|---|---|---|---|---|---|
| 0 | 6.083 | 50.775 | 32 | 180 | 2000 | 250 |
| 1 | 6.090 | 50.780 | 25 | 180 | 2000 | 300 |
| 2 | 6.095 | 50.785 | 20 | 180 | 2000 | 350 |
| 3 | 6.100 | 50.790 | 35 | 180 | 2000 | 1000 |
| 4 | 6.105 | 50.795 | 40 | 180 | 2000 | -100 |
In [10]:
Copied!
# Run simulation with ERA5 workflow
out = rk.solar.openfield_pv_era5(
placements=placements,
era5_path=rk.TEST_DATA["era5-like"],
global_solar_atlas_ghi_path=rk.TEST_DATA["gsa-ghi-like.tif"],
global_solar_atlas_dni_path=rk.TEST_DATA["gsa-dni-like.tif"],
module="WINAICO WSx-240P6", # Module to use for simulation
)
# Run simulation with ERA5 workflow
out = rk.solar.openfield_pv_era5(
placements=placements,
era5_path=rk.TEST_DATA["era5-like"],
global_solar_atlas_ghi_path=rk.TEST_DATA["gsa-ghi-like.tif"],
global_solar_atlas_dni_path=rk.TEST_DATA["gsa-dni-like.tif"],
module="WINAICO WSx-240P6", # Module to use for simulation
)
FutureWarning: /home/docs/checkouts/readthedocs.org/user_builds/ethos-reskit/checkouts/align-docs-branding/reskit/solar/workflows/solar_workflow_manager.py:970 Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value 'N' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.FutureWarning: /home/docs/checkouts/readthedocs.org/user_builds/ethos-reskit/checkouts/align-docs-branding/reskit/weather/NCSource.py:258 ChainedAssignmentError: behaviour will change in pandas 3.0! You are setting values through chained assignment. Currently this works in certain cases, but when using Copy-on-Write (which will become the default behaviour in pandas 3.0) this will never work to update the original DataFrame or Series, because the intermediate object on which we are setting values will behave as a copy. A typical example is when you are setting values in a column of a DataFrame, like: df["col"][row_indexer] = value Use `df.loc[row_indexer, "col"] = values` instead, to perform the assignment in a single step and ensure this keeps updating the original `df`. See the caveats in the documentation: https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
FutureWarning: /home/docs/checkouts/readthedocs.org/user_builds/ethos-reskit/checkouts/align-docs-branding/reskit/solar/workflows/solar_workflow_manager.py:970 Setting an item of incompatible dtype is deprecated and will raise an error in a future version of pandas. Value 'N' has dtype incompatible with float64, please explicitly cast to a compatible dtype first.
In [11]:
Copied!
# Same plot as in single examples, but now for multiple placements
plt.figure(figsize=(6, 4))
for i in range(placements.shape[0]):
out.capacity_factor[:, i].fillna(0).plot(label=i)
plt.legend(ncol=3, title="Placement ID")
plt.title("Capacity Factor Generation")
plt.show()
# Same Plot function as in single examples
out.capacity_factor.fillna(0).plot()
# Same plot as in single examples, but now for multiple placements
plt.figure(figsize=(6, 4))
for i in range(placements.shape[0]):
out.capacity_factor[:, i].fillna(0).plot(label=i)
plt.legend(ncol=3, title="Placement ID")
plt.title("Capacity Factor Generation")
plt.show()
# Same Plot function as in single examples
out.capacity_factor.fillna(0).plot()
Out[11]:
<matplotlib.collections.QuadMesh at 0x79d6a8caee40>