Flicker Modeling#
** Shadow functions **
- hopp.simulation.technologies.layout.shadow_flicker.get_time_zone(lat, lon)#
- Parameters:
lat (float)
lon (float)
- Return type:
<module ‘pytz.tzinfo’ from ‘/home/docs/checkouts/readthedocs.org/user_builds/hopp/envs/latest/lib/python3.11/site-packages/pytz/tzinfo.py’>
- hopp.simulation.technologies.layout.shadow_flicker.get_sun_pos(lat, lon, step_in_minutes=60, n=8760, start_hr=0, steps=None)#
Calculates the sun azimuth & elevation angles at each time step in provided range
- Parameters:
lat (float) – latitude, degrees
lon (float) – longitude, degrees
step_in_minutes (float) – the number of minutes between each step
n (int) – number of steps
start_hr (int) – hour of first day of the year
steps (range | None) – if given, calculate for the timesteps in the range, ignoring start_hr and n
- Returns:
array of sun azimuth, array of sun elevation, datetime of each entry
- Return type:
Tuple[ndarray, ndarray, list]
- hopp.simulation.technologies.layout.shadow_flicker.blade_pos_of_rotated_ellipse(radius_x, radius_y, rotation_theta, blade_theta, center_x, center_y)#
Parametric equation for rotated ellipse
- Parameters:
radius_x (float) – radius of ellipse along x-axis
radius_y (float) – radius of ellipse along y-axis
rotation_theta (float | ndarray) – rotation of ellipse in radians
blade_theta (float | ndarray) – angle of blade in radians
center_x (float) – ellipse center x coordinate
center_y (float) – ellipse center y coordinate
- Returns:
(x, y) coordinate of the blade tip along rotated ellipse
- Return type:
Tuple[float, float]
- hopp.simulation.technologies.layout.shadow_flicker.get_turbine_shadow_polygons(blade_length, blade_angle, azi_ang, elv_ang, wind_dir, tower_shadow=True, tower_height=None)#
Calculates the (x, y) coordinates of a wind turbine’s shadow, which depends on the sun azimuth and elevation.
The dimensions of the tower and blades are in fixed ratios to the blade_length. The blade angle is the degrees from z-axis, whereas the wind direction is where the turbine is pointing towards (if None, north is assumed).
In spherical coordinates, blade angle is phi and wind direction is theta, with 0 at north, moving clockwise.
The output shadow polygon is relative to the turbine located at (0, 0).
- Parameters:
blade_length (float) – meters, radius in spherical coords
blade_angle (float | None) – degrees from z-axis, or None to use ellipse as swept area
azi_ang (float) – azimuth degrees, clockwise from north as 0
elv_ang (float) – elevation degrees, from x-y plane as 0
wind_dir – degrees from north, clockwise, determines which direction rotor is facing
tower_shadow (bool) – if false, do not include the tower’s shadow
tower_height (float | None)
- Returns:
(shadow polygon, shadow angle from north) if shadow exists, otherwise (None, None)
- Return type:
Tuple[None | Polygon | MultiPolygon, float]
- hopp.simulation.technologies.layout.shadow_flicker.get_turbine_shadows_timeseries(blade_length, steps, angles_per_step, azi_ang, elv_ang, wind_ang=None, tower_shadow=True)#
Calculate turbine shadows for a number of equally-spaced blade angles per time step. Returns a list of turbine shadows per time step, where each entry has a shadow for each angle.
- Parameters:
blade_length (float) – meters
steps (range) – which timesteps to calculate
angles_per_step (int) – number of blade angles per timestep
elv_ang (list | ndarray) – array of elevation angles, degrees
azi_ang (list | ndarray) – array of azimuth angles, degrees
wind_ang (list | None) – array of wind direction degrees with 0 as north, degrees
tower_shadow (bool) – if false, do not include the tower’s shadow
- Returns:
list of turbine shadows per time step
- Return type:
List[List[None | Polygon | MultiPolygon]]
- hopp.simulation.technologies.layout.shadow_flicker.shadow_cast_over_panel(panel_x, panel_y, n_mod, blade_length, blade_angle, azi_ang, elv_ang, wind_dir=None)#
Calculates which cells in a string of PV panels are shaded. The panel is located at a (panel_x, panel_y) distance from the turbine at (0, 0). Shadow shape depends on the sun azimuth and elevation angle.
The PV panel is assumed to be a 96-cell, 1.488 x 0.992 m panel with 12.4 x 12.4 cm cells, 12x8 cells with 2, 4, and 2 columns of cells per diode for total of 3 substrings.
Turbine dimensions depend on blade_length and shape of the blades depend on blade_angle and wind_dir– see get_turbine_shadow_polygons for more details.
- Parameters:
panel_x (float) – distance from turbine to bottom-left corner of panels
panel_y (float) – degrees from x-axis to bottom-left corner of panels
n_mod (int) – number of modules in a string ( n x 1 solar array)
blade_length (float) – meters, radius in spherical coords
blade_angle (float) – degrees from xv-plane, 90-inclination/theta in spherical coords
azi_ang (float) – azimuth degrees
elv_ang (float) – elevation degrees
wind_dir (float) – degrees from north, clockwise, determines which dir rotor is facing, azimuth/phi in spherical coord
- Returns:
grid of cells where 1 means shaded, turbine shadow polygon
- Return type:
Tuple[ndarray, Polygon] | None
- hopp.simulation.technologies.layout.shadow_flicker.create_turbines_in_grid(dx, dy, theta, n_turbines_per_side)#
Sets up turbines in a grid. Returns a list of the turbine positions and a Polygon including them.
- Parameters:
dx (float) – x distance between turbines in grid
dy (float) – y distance
theta (float | ndarray) – rotation of grid
n_turbines_per_side (int)
- Returns:
- Return type:
Tuple[list, Polygon]
- hopp.simulation.technologies.layout.shadow_flicker.get_turbine_grid_shadow(shadow_polygons, turb_pos)#
Calculate shadow polygons for each step in simulation for each turbine in the grid
- Returns:
list with dimension [step_per_hour, angles_per_step]
- Parameters:
shadow_polygons (MultiPolygon | None)
turb_pos (list)
- Return type:
List[Polygon | MultiPolygon] | None
- hopp.simulation.technologies.layout.shadow_flicker.create_module_cells_mesh(mod_x, mod_y, mod_width, mod_height, n_module)#
For a string of PV modules, create an array of meshgrids having a point for each cell.
- Parameters:
mod_x (float) – x coordinate of corner of panel
mod_y (float) – y coordinate
mod_width (float) – single module’s width
mod_height (float) – module’s height
n_module (int) – number of modules per string
- Returns:
n_module array of meshgrids
- hopp.simulation.technologies.layout.shadow_flicker.shadow_over_module_cells(module_mesh, turbine_shadow)#
For a meshgrid where each point is a cell in a PV module, identify which cells are in the turbine_shadow.
- Parameters:
module_mesh (ndarray) – meshgrid
turbine_shadow (Polygon | MultiPolygon) – polygon
- Returns:
2-D array with same coordinates as the PV module with values 0 (unshaded) or 1 (shaded)
- hopp.simulation.technologies.layout.shadow_flicker.create_pv_string_points(x_coord, y_coord, mod_width, mod_height, string_width, string_height)#
- Parameters:
x_coord (float)
y_coord (float)
mod_width (float)
mod_height (float)
string_width (float)
string_height (float)
- Returns:
- Return type:
Tuple[Polygon, ndarray]
** PV assumptions **
- hopp.simulation.technologies.layout.pv_module.spe_power(spe_eff_level, spe_rad_level, spe_area)#
Computes the module power per the SPE model
- Return type:
float
- hopp.simulation.technologies.layout.pv_module.get_module_attribs(model, only_ref_vals=True)#
Returns the module attributes for either the PVsamv1 or PVWattsv8 models, see: https://nrel-pysam.readthedocs.io/en/main/modules/Pvsamv1.html#module-group
This function extracts module attributes from a given PV model or parameter dictionary. If only_ref_vals is set to True, only the reference values (e.g., I_sc_ref, V_mp_ref) are returned; otherwise, all model-specific parameters are included.
- Parameters:
model (Union[pv_simple.Pvwattsv8, pv_detailed.Pvsamv1, dict]) – The PV model (PVsamv1 or PVWattsv8) or a dictionary of parameters.
only_ref_vals (bool, optional) – If True, only returns the reference values. If False, includes all model-specific parameters. Defaults to True.
- Returns:
dict –
- A dictionary containing module attributes. If only_ref_vals is True, the dictionary includes:
area (float): Module area [m²].
aspect_ratio (float): Module aspect ratio [-].
length (float): Module length [m].
width (float): Module width [m].
I_mp_ref (float): Reference current at maximum power point [A].
I_sc_ref (float): Reference short-circuit current [A].
P_mp_ref (float): Reference power at maximum power point [kW].
V_mp_ref (float): Reference voltage at maximum power point [V].
V_oc_ref (float): Reference open-circuit voltage [V].
If only_ref_vals is False, additional model-specific attributes are included.
- Raises:
Exception – If the module model number is not recognized.
- Return type:
dict
- hopp.simulation.technologies.layout.pv_module.set_module_attribs(model, params)#
Sets the module model parameters for either the PVsamv1 or PVWattsv8 models.
This function assigns the required parameters to the given model. It verifies that all necessary parameters are provided based on the selected module type and raises an exception if any required parameters are missing.
- Parameters:
model (Union[pv_simple.Pvwattsv8, pv_detailed.Pvsamv1]) – The PVWattsv8 or PVsamv1 model instance.
params (dict) – Dictionary containing parameter key-value pairs required for the respective module model.
- Raises:
Exception – If not all required parameters are provided or if the module model number is unrecognized.
** Flicker models **
- class hopp.simulation.technologies.layout.flicker_mismatch.FlickerMismatch(lat, lon, angles_per_step=1, blade_length=35, solar_resource_data=None, wind_dir=None, gridcell_width=1.488, gridcell_height=0.992, gridcells_per_string=10)#
Bases:
objectSimulates a wind turbine’s flicker over a grid for a given location. The shadow cast by the tower and the three blades are calculated for each of the simulation steps: number of blade angles (evenly spaced) per step of the hour.
The turbine is located at (0, 0) and a set of 2D arrays give the flicker losses at grid cell / coordinate. This ‘heatmap’ can have variable length and width, determined by ‘diam_mult_nwe’ and ‘diam_mult_s’, and can be normalized in several ways:
The ‘poa’ heat map is produced as a loss ratio relative to unshaded areas (0 - 1). This loss ratio is with respect to plane-of-array irradiance, as calculated for a single-axis tracking system using PVWattsv8.
The ‘power’ heat map is another loss ratio (0 - 1), but with respect to power production of an unshaded string of panels as modeled by PVMismatch. This is calculated by modeling panels at each grid location, grouped into strings, and simulating the power of each string.
The ‘time’ heat map is weighted by the number of timesteps each grid cell is shaded over the total timesteps simulated.
All heat maps are normalized by the number of timesteps simulated.
- Variables:
n_hours – number of hours in year
steps_per_hour – number of time steps to run each hour
diam_mult_nwe – in number of turbine diameters, the distance of the heat map’s north, west and east end from the turbine at (0, 0)
diam_mult_s – similarly, the number of turbine diameters the heatmap extends from (0, 0) south
periodic – if true, then the top of the heatmap continues onto the bottom, and vice versa for the east / west
turbine_tower_shadow – if true, then include the tower shadow
- Parameters:
lat (float)
lon (float)
angles_per_step (int | None)
blade_length (int)
solar_resource_data (dict | None)
wind_dir (list | None)
gridcell_width (float)
gridcell_height (float)
gridcells_per_string (int)
- n_hours: int = 8760#
- steps_per_hour: int = 1#
- diam_mult_nwe: int = 8#
- diam_mult_s: int = 4#
- periodic: bool = False#
- turbine_tower_shadow: bool = True#
- __init__(lat, lon, angles_per_step=1, blade_length=35, solar_resource_data=None, wind_dir=None, gridcell_width=1.488, gridcell_height=0.992, gridcells_per_string=10)#
Setup file output paths, the solar panel array, and the heat map template.
Also load irradiance and turbine shadow data.
- Parameters:
lat (float) – latitude
lon (float) – longitude
blade_length (int) – meters
angles_per_step (int | None) – number of blade angles to simulate every timestep
solar_resource_data (dict | None) – PySAM’s solar resource data: NREL/pysam
wind_dir (list | None) – wind direction degrees, 0 as north, time series of len(8760 * steps_per_hour)
gridcell_width (float) – grid cells of the heat map dimension
gridcell_height (float) – grid cells of the heat map dimension
gridcells_per_string (int) – for ‘poa’ heatmaps
- Return type:
None
- _create_pool(n_procs)#
Initialize a multiprocessing pool where each simulation step can be partitioned (by modulo operator) to split up work among different FlickerMismatch instances. :param n_procs:
- Parameters:
n_procs (int)
- Return type:
Pool
- _setup_wind_dir(wind_dir_degrees)#
- _setup_irradiance()#
Compute solar azimuth and elevation degrees; Compute plane-of-array irradiance for a single-axis tracking PVwatts system :return:
- static get_turb_site(diam)#
Return a polygon with the dimensions of the grid
- Parameters:
diam (int)
- Return type:
Polygon
- static _setup_heatmap_template(bounds, gridcell_width=1.488, gridcell_height=0.992)#
Create the points where each panel is located and the heat map grid template :param bounds: [min x, min y, max x, max y] of the grid :param gridcell_width: width of cells in the heat map :param gridcell_height: height of cells in the heat map :return: MultiPoint of panel locations, (heat map grid, x coordinates, y coordinates)
- Parameters:
bounds (list)
gridcell_width (float)
gridcell_height (float)
- Return type:
tuple
- static get_turb_pos_indices(heat_map_template)#
Get the indices for the heat map template of the cell where the turbine is located
- Parameters:
heat_map_template (ndarray)
- Return type:
tuple
- _setup_array()#
Setup the solar panel array within the grid as a Point per panel
- Return type:
None
- _setup_string_points(array_points)#
Divide up the array of solar panels into strings. If FlickerMismatch.periodic, then a string can continue from the bottom edge of the grid back to the top, rather than running off the grid entirely.
- Parameters:
array_points (Point | MultiPoint)
- Returns:
a list of which points belong in which string, dim [n_string, FlickerMismatch.modules_per_string]
- Return type:
list
- static _calculate_shading(weight, shadows, site_points, heat_map, gridcell_width, gridcell_height, normalize_by_area=False)#
Update the heat_map with shading losses in POA irradiance
- Parameters:
weight (float) – loss to apply to shaded cells
shadows (list) – list of shadow (Multi)Polygons for each blade angle
site_points (MultiPoint) – points of solar panels
heat_map (ndarray) – array with shading losses
gridcell_width (float) – width of cells in the heat map
gridcell_height (float) – height of cells in the heat map
normalize_by_area – if True, normalize weight per cell by how much area is shaded
- Return type:
None
- static _calculate_power_loss(poa, elv_ang, shadows, array_points, heat_map_flicker, gridcell_width, gridcell_height, xs_min, ys_min, poa_shading_ratio=0.9)#
Update the heat map with flicker losses, using an unshaded string as baseline for normalizing
- Parameters:
poa (float) – irradiance
elv_ang (float) – solar elevation degree
shadows (list) – list of shadow (Multi)Polygons for each blade angle
array_points (list) – list of solar panels, [# strands, # strings per strand, FlickerMismatch.modules_per_string]
heat_map_flicker (ndarray) – array with flicker losses
gridcell_width (float) – width of cells in the heat map
gridcell_height (float) – height of cells in the heat map
xs_min (float) – min of heat map grid’s x coordinates
ys_min (float) – min of heat map grid’s y coordinates
poa_shading_ratio (float) – how much of the poa is blocked by the shadow
- _calculate_turbine_shadow(ind)#
- Parameters:
ind (int)
- Return type:
List[None | Polygon | MultiPolygon]
- create_heat_maps(steps, weight_option)#
Create shadow and flicker heat maps for a given range of simulation steps
- Parameters:
weight_option (tuple) – tuple of selected weighting options, producing a heatmap each - “poa”: weight by plane-of-array irradiance - “power”: weight by power loss of pvmismatch module - “time”: weight by number of timesteps shaded
steps (range) – which steps to run, must be within range calculated by steps_per_hour x angles_per_step
- Returns:
shadow heat map, flicker heat map
- Return type:
tuple
- run_parallel(n_procs, weight_option, intervals=None)#
Runs create_heat_maps_irradiance in parallel
- Parameters:
n_procs (int)
weight_option (tuple) – tuple of selected weighting options, producing a heatmap each - “poa”: weight by plane-of-array irradiance - “power”: weight by power loss of pvmismatch module - “time”: weight by number of timesteps shaded
intervals (Sequence[range] | None) – list of ranges to simulate; if none, simulate entire weather file’s records
- Returns:
heat_map_shadow, heat_map_flicker
- plot_on_site(plot_array=True, plot_points=True)#