"""Module will classes related to PV row geometries"""
import numpy as np
from pvfactors.config import COLOR_DIC
from pvfactors.geometry.base import \
BaseSide, _coords_from_center_tilt_length, PVSegment
from shapely.geometry import GeometryCollection, LineString
from pvfactors.geometry.timeseries import \
TsShadeCollection, TsLineCoords, TsSurface
from pvlib.tools import cosd, sind
[docs]class TsPVRow(object):
"""Timeseries PV row class: this class is a vectorized version of the
PV row geometries. The coordinates and attributes (front and back sides)
are all vectorized."""
[docs] def __init__(self, ts_front_side, ts_back_side, xy_center, index=None,
full_pvrow_coords=None):
"""Initialize timeseries PV row with its front and back sides.
Parameters
----------
ts_front_side : :py:class:`~pvfactors.geometry.pvrow.TsSide`
Timeseries front side of the PV row
ts_back_side : :py:class:`~pvfactors.geometry.pvrow.TsSide`
Timeseries back side of the PV row
xy_center : tuple of float
x and y coordinates of the PV row center point (invariant)
index : int, optional
index of the PV row (Default = None)
full_pvrow_coords : \
:py:class:`~pvfactors.geometry.timeseries.TsLineCoords`, optional
Timeseries coordinates of the full PV row, end to end
(Default = None)
"""
self.front = ts_front_side
self.back = ts_back_side
self.xy_center = xy_center
self.index = index
self.full_pvrow_coords = full_pvrow_coords
@classmethod
def from_raw_inputs(cls, xy_center, width, rotation_vec,
cut, shaded_length_front, shaded_length_back,
index=None, param_names=None):
"""Create timeseries PV row using raw inputs.
Note: shading will always be zero when pv rows are flat.
Parameters
----------
xy_center : tuple of float
x and y coordinates of the PV row center point (invariant)
width : float
width of the PV rows [m]
rotation_vec : np.ndarray
Timeseries rotation values of the PV row [deg]
cut : dict
Discretization scheme of the PV row. Eg {'front': 2, 'back': 4}.
Will create segments of equal length on the designated sides.
shaded_length_front : np.ndarray
Timeseries values of front side shaded length [m]
shaded_length_back : np.ndarray
Timeseries values of back side shaded length [m]
index : int, optional
Index of the pv row (default = None)
param_names : list of str, optional
List of names of surface parameters to use when creating geometries
(Default = None)
Returns
-------
New timeseries PV row object
"""
# Calculate full pvrow coords
pvrow_coords = TsPVRow._calculate_full_coords(
xy_center, width, rotation_vec)
# Calculate normal vectors
dx = pvrow_coords.b2.x - pvrow_coords.b1.x
dy = pvrow_coords.b2.y - pvrow_coords.b1.y
normal_vec_front = np.array([-dy, dx])
# Calculate front side coords
ts_front = TsSide.from_raw_inputs(
xy_center, width, rotation_vec, cut.get('front', 1),
shaded_length_front, n_vector=normal_vec_front,
param_names=param_names)
# Calculate back side coords
ts_back = TsSide.from_raw_inputs(
xy_center, width, rotation_vec, cut.get('back', 1),
shaded_length_back, n_vector=-normal_vec_front,
param_names=param_names)
return cls(ts_front, ts_back, xy_center, index=index,
full_pvrow_coords=pvrow_coords)
@staticmethod
def _calculate_full_coords(xy_center, width, rotation):
"""Method to calculate the full PV row coordinaltes.
Parameters
----------
xy_center : tuple of float
x and y coordinates of the PV row center point (invariant)
width : float
width of the PV rows [m]
rotation : np.ndarray
Timeseries rotation values of the PV row [deg]
Returns
-------
coords: :py:class:`~pvfactors.geometry.timeseries.TsLineCoords`
Timeseries coordinates of full PV row
"""
x_center, y_center = xy_center
radius = width / 2.
# Calculate coords
x1 = radius * cosd(rotation + 180.) + x_center
y1 = radius * sind(rotation + 180.) + y_center
x2 = radius * cosd(rotation) + x_center
y2 = radius * sind(rotation) + y_center
coords = TsLineCoords.from_array(np.array([[x1, y1], [x2, y2]]))
return coords
def surfaces_at_idx(self, idx):
"""Get all PV surface geometries in timeseries PV row for a certain
index.
Parameters
----------
idx : int
Index to use to generate PV surface geometries
Returns
-------
list of :py:class:`~pvfactors.geometry.base.PVSurface` objects
List of PV surfaces
"""
pvrow = self.at(idx)
return pvrow.all_surfaces
def plot_at_idx(self, idx, ax, color_shaded=COLOR_DIC['pvrow_shaded'],
color_illum=COLOR_DIC['pvrow_illum'],
with_surface_index=False):
"""Plot timeseries PV row at a certain index.
Parameters
----------
idx : int
Index to use to plot timeseries PV rows
ax : :py:class:`matplotlib.pyplot.axes` object
Axes for plotting
color_shaded : str, optional
Color to use for plotting the shaded surfaces (Default =
COLOR_DIC['pvrow_shaded'])
color_shaded : str, optional
Color to use for plotting the illuminated surfaces (Default =
COLOR_DIC['pvrow_illum'])
with_surface_index : bool, optional
Plot the surfaces with their index values (Default = False)
"""
pvrow = self.at(idx)
pvrow.plot(ax, color_shaded=color_shaded,
color_illum=color_illum, with_index=with_surface_index)
def at(self, idx):
"""Generate a PV row geometry for the desired index.
Parameters
----------
idx : int
Index to use to generate PV row geometry
Returns
-------
pvrow : :py:class:`~pvfactors.geometry.pvrow.PVRow`
"""
front_geom = self.front.at(idx)
back_geom = self.back.at(idx)
original_line = LineString(
self.full_pvrow_coords.as_array[:, :, idx])
pvrow = PVRow(front_side=front_geom, back_side=back_geom,
index=self.index, original_linestring=original_line)
return pvrow
def update_params(self, new_dict):
"""Update timeseries surface parameters of the PV row.
Parameters
----------
new_dict : dict
Parameters to add or update for the surfaces
"""
self.front.update_params(new_dict)
self.back.update_params(new_dict)
@property
def n_ts_surfaces(self):
"""Number of timeseries surfaces in the ts PV row"""
return self.front.n_ts_surfaces + self.back.n_ts_surfaces
@property
def all_ts_surfaces(self):
"""List of all timeseries surfaces"""
return self.front.all_ts_surfaces + self.back.all_ts_surfaces
@property
def centroid(self):
"""Centroid point of the timeseries pv row"""
centroid = (self.full_pvrow_coords.centroid
if self.full_pvrow_coords is not None else None)
return centroid
@property
def length(self):
"""Length of both sides of the timeseries PV row"""
return self.front.length + self.back.length
@property
def highest_point(self):
"""Timeseries point coordinates of highest point of PV row"""
high_pt = (self.full_pvrow_coords.highest_point
if self.full_pvrow_coords is not None else None)
return high_pt
[docs]class TsSide(object):
"""Timeseries side class: this class is a vectorized version of the
BaseSide geometries. The coordinates and attributes (list of segments,
normal vector) are all vectorized."""
[docs] def __init__(self, segments, n_vector=None):
"""Initialize timeseries side using list of timeseries segments.
Parameters
----------
segments : list of :py:class:`~pvfactors.geometry.pvrow.TsSegment`
List of timeseries segments of the side
n_vector : np.ndarray, optional
Timeseries normal vectors of the side (Default = None)
"""
self.list_segments = segments
self.n_vector = n_vector
@classmethod
def from_raw_inputs(cls, xy_center, width, rotation_vec, cut,
shaded_length, n_vector=None, param_names=None):
"""Create timeseries side using raw PV row inputs.
Note: shading will always be zero when PV rows are flat.
Parameters
----------
xy_center : tuple of float
x and y coordinates of the PV row center point (invariant)
width : float
width of the PV rows [m]
rotation_vec : np.ndarray
Timeseries rotation values of the PV row [deg]
cut : int
Discretization scheme of the PV side.
Will create segments of equal length.
shaded_length : np.ndarray
Timeseries values of side shaded length from lowest point [m]
n_vector : np.ndarray, optional
Timeseries normal vectors of the side
param_names : list of str, optional
List of names of surface parameters to use when creating geometries
(Default = None)
Returns
-------
New timeseries side object
"""
mask_tilted_to_left = rotation_vec >= 0
# Create Ts segments
x_center, y_center = xy_center
radius = width / 2.
segment_length = width / cut
is_not_flat = rotation_vec != 0.
# Calculate coords of shading point
r_shade = radius - shaded_length
x_sh = np.where(
mask_tilted_to_left,
r_shade * cosd(rotation_vec + 180.) + x_center,
r_shade * cosd(rotation_vec) + x_center)
y_sh = np.where(
mask_tilted_to_left,
r_shade * sind(rotation_vec + 180.) + y_center,
r_shade * sind(rotation_vec) + y_center)
# Calculate coords
list_segments = []
for i in range(cut):
# Calculate segment coords
r1 = radius - i * segment_length
r2 = radius - (i + 1) * segment_length
x1 = r1 * cosd(rotation_vec + 180.) + x_center
y1 = r1 * sind(rotation_vec + 180.) + y_center
x2 = r2 * cosd(rotation_vec + 180) + x_center
y2 = r2 * sind(rotation_vec + 180) + y_center
segment_coords = TsLineCoords.from_array(
np.array([[x1, y1], [x2, y2]]))
# Determine lowest and highest points of segment
x_highest = np.where(mask_tilted_to_left, x2, x1)
y_highest = np.where(mask_tilted_to_left, y2, y1)
x_lowest = np.where(mask_tilted_to_left, x1, x2)
y_lowest = np.where(mask_tilted_to_left, y1, y2)
# Calculate illum and shaded coords
x2_illum, y2_illum = x_highest, y_highest
x1_shaded, y1_shaded, x2_shaded, y2_shaded = \
x_lowest, y_lowest, x_lowest, y_lowest
mask_all_shaded = (y_sh > y_highest) & (is_not_flat)
mask_partial_shaded = (y_sh > y_lowest) & (~ mask_all_shaded) \
& (is_not_flat)
# Calculate second boundary point of shade
x2_shaded = np.where(mask_all_shaded, x_highest, x2_shaded)
x2_shaded = np.where(mask_partial_shaded, x_sh, x2_shaded)
y2_shaded = np.where(mask_all_shaded, y_highest, y2_shaded)
y2_shaded = np.where(mask_partial_shaded, y_sh, y2_shaded)
x1_illum = x2_shaded
y1_illum = y2_shaded
illum_coords = TsLineCoords.from_array(
np.array([[x1_illum, y1_illum], [x2_illum, y2_illum]]))
shaded_coords = TsLineCoords.from_array(
np.array([[x1_shaded, y1_shaded], [x2_shaded, y2_shaded]]))
# Create illuminated and shaded collections
is_shaded = False
illum = TsShadeCollection(
[TsSurface(illum_coords, n_vector=n_vector,
param_names=param_names, shaded=is_shaded)],
is_shaded)
is_shaded = True
shaded = TsShadeCollection(
[TsSurface(shaded_coords, n_vector=n_vector,
param_names=param_names, shaded=is_shaded)],
is_shaded)
# Create segment
segment = TsSegment(segment_coords, illum, shaded,
n_vector=n_vector, index=i)
list_segments.append(segment)
return cls(list_segments, n_vector=n_vector)
def surfaces_at_idx(self, idx):
"""Get all PV surface geometries in timeseries side for a certain
index.
Parameters
----------
idx : int
Index to use to generate PV surface geometries
Returns
-------
list of :py:class:`~pvfactors.geometry.base.PVSurface` objects
List of PV surfaces
"""
side_geom = self.at(idx)
return side_geom.all_surfaces
def at(self, idx):
"""Generate a side geometry for the desired index.
Parameters
----------
idx : int
Index to use to generate side geometry
Returns
-------
side : :py:class:`~pvfactors.geometry.base.BaseSide`
"""
list_geom_segments = []
for ts_seg in self.list_segments:
list_geom_segments.append(ts_seg.at(idx))
side = BaseSide(list_geom_segments)
return side
def plot_at_idx(self, idx, ax, color_shaded=COLOR_DIC['pvrow_shaded'],
color_illum=COLOR_DIC['pvrow_illum']):
"""Plot timeseries side at a certain index.
Parameters
----------
idx : int
Index to use to plot timeseries side
ax : :py:class:`matplotlib.pyplot.axes` object
Axes for plotting
color_shaded : str, optional
Color to use for plotting the shaded surfaces (Default =
COLOR_DIC['pvrow_shaded'])
color_shaded : str, optional
Color to use for plotting the illuminated surfaces (Default =
COLOR_DIC['pvrow_illum'])
"""
side_geom = self.at(idx)
side_geom.plot(ax, color_shaded=color_shaded, color_illum=color_illum,
with_index=False)
@property
def shaded_length(self):
"""Timeseries shaded length of the side."""
length = 0.
for seg in self.list_segments:
length += seg.shaded.length
return length
@property
def length(self):
"""Timeseries length of side."""
length = 0.
for seg in self.list_segments:
length += seg.length
return length
def get_param_weighted(self, param):
"""Get timeseries parameter for the side, after weighting by
surface length.
Parameters
----------
param : str
Name of parameter
Returns
-------
np.ndarray
Weighted parameter values
"""
return self.get_param_ww(param) / self.length
def get_param_ww(self, param):
"""Get timeseries parameter from the side's surfaces with weight, i.e.
after multiplying by the surface lengths.
Parameters
----------
param: str
Surface parameter to return
Returns
-------
np.ndarray
Timeseries parameter values multiplied by weights
Raises
------
KeyError
if parameter name not in a surface parameters
"""
value = 0.
for seg in self.list_segments:
value += seg.get_param_ww(param)
return value
def update_params(self, new_dict):
"""Update timeseries surface parameters of the side.
Parameters
----------
new_dict : dict
Parameters to add or update for the surfaces
"""
for seg in self.list_segments:
seg.update_params(new_dict)
@property
def n_ts_surfaces(self):
"""Number of timeseries surfaces in the ts side"""
n_ts_surfaces = 0
for ts_segment in self.list_segments:
n_ts_surfaces += ts_segment.n_ts_surfaces
return n_ts_surfaces
@property
def all_ts_surfaces(self):
"""List of all timeseries surfaces"""
all_ts_surfaces = []
for ts_segment in self.list_segments:
all_ts_surfaces += ts_segment.all_ts_surfaces
return all_ts_surfaces
[docs]class TsSegment(object):
"""A TsSegment is a timeseries segment that has a timeseries shaded
collection and a timeseries illuminated collection."""
[docs] def __init__(self, coords, illum_collection, shaded_collection,
index=None, n_vector=None):
"""Initialize timeseries segment using segment coordinates and
timeseries illuminated and shaded surfaces.
Parameters
----------
coords : :py:class:`~pvfactors.geometry.timeseries.TsLineCoords`
Timeseries coordinates of full segment
illum_collection : \
:py:class:`~pvfactors.geometry.timeseries.TsShadeCollection`
Timeseries collection for illuminated part of segment
shaded_collection : \
:py:class:`~pvfactors.geometry.timeseries.TsShadeCollection`
Timeseries collection for shaded part of segment
index : int, optional
Index of segment (Default = None)
n_vector : np.ndarray, optional
Timeseries normal vectors of the side (Default = None)
"""
self.coords = coords
self.illum = illum_collection
self.shaded = shaded_collection
self.index = index
self.n_vector = n_vector
def surfaces_at_idx(self, idx):
"""Get all PV surface geometries in timeseries segment for a certain
index.
Parameters
----------
idx : int
Index to use to generate PV surface geometries
Returns
-------
list of :py:class:`~pvfactors.geometry.base.PVSurface` objects
List of PV surfaces
"""
segment = self.at(idx)
return segment.all_surfaces
def plot_at_idx(self, idx, ax, color_shaded=COLOR_DIC['pvrow_shaded'],
color_illum=COLOR_DIC['pvrow_illum']):
"""Plot timeseries segment at a certain index.
Parameters
----------
idx : int
Index to use to plot timeseries segment
ax : :py:class:`matplotlib.pyplot.axes` object
Axes for plotting
color_shaded : str, optional
Color to use for plotting the shaded surfaces (Default =
COLOR_DIC['pvrow_shaded'])
color_shaded : str, optional
Color to use for plotting the illuminated surfaces (Default =
COLOR_DIC['pvrow_illum'])
"""
segment = self.at(idx)
segment.plot(ax, color_shaded=color_shaded, color_illum=color_illum,
with_index=False)
def at(self, idx):
"""Generate a PV segment geometry for the desired index.
Parameters
----------
idx : int
Index to use to generate PV segment geometry
Returns
-------
segment : :py:class:`~pvfactors.geometry.base.PVSegment`
"""
# Create illum collection
illum_collection = self.illum.at(idx)
# Create shaded collection
shaded_collection = self.shaded.at(idx)
# Create PV segment
segment = PVSegment(illum_collection=illum_collection,
shaded_collection=shaded_collection,
index=self.index)
return segment
@property
def length(self):
"""Timeseries length of segment."""
return self.illum.length + self.shaded.length
@property
def shaded_length(self):
"""Timeseries length of shaded part of segment."""
return self.shaded.length
@property
def centroid(self):
"""Timeseries point coordinates of the segment's centroid"""
return self.coords.centroid
def get_param_weighted(self, param):
"""Get timeseries parameter for the segment, after weighting by
surface length.
Parameters
----------
param : str
Name of parameter
Returns
-------
np.ndarray
Weighted parameter values
"""
return self.get_param_ww(param) / self.length
def get_param_ww(self, param):
"""Get timeseries parameter from the segment's surfaces with weight,
i.e. after multiplying by the surface lengths.
Parameters
----------
param: str
Surface parameter to return
Returns
-------
np.ndarray
Timeseries parameter values multiplied by weights
"""
return self.illum.get_param_ww(param) + self.shaded.get_param_ww(param)
def update_params(self, new_dict):
"""Update timeseries surface parameters of the segment.
Parameters
----------
new_dict : dict
Parameters to add or update for the surfaces
"""
self.illum.update_params(new_dict)
self.shaded.update_params(new_dict)
@property
def highest_point(self):
"""Timeseries point coordinates of highest point of segment"""
return self.coords.highest_point
@property
def lowest_point(self):
"""Timeseries point coordinates of lowest point of segment"""
return self.coords.lowest_point
@property
def all_ts_surfaces(self):
"""List of all timeseries surfaces in segment"""
return self.illum.list_ts_surfaces + self.shaded.list_ts_surfaces
@property
def n_ts_surfaces(self):
"""Number of timeseries surfaces in the segment"""
return self.illum.n_ts_surfaces + self.shaded.n_ts_surfaces
[docs]class PVRowSide(BaseSide):
"""A PV row side represents the whole surface of one side of a PV row.
At its core it will contain a fixed number of
:py:class:`~pvfactors.geometry.base.PVSegment` objects that will together
constitue one side of a PV row: a PV row side can also be
"discretized" into multiple segments"""
[docs] def __init__(self, list_segments=[]):
"""Initialize PVRowSide using its base class
:py:class:`pvfactors.geometry.base.BaseSide`
Parameters
----------
list_segments : list of :py:class:`~pvfactors.geometry.base.PVSegment`
List of PV segments for PV row side.
"""
super(PVRowSide, self).__init__(list_segments)
[docs]class PVRow(GeometryCollection):
"""A PV row is made of two PV row sides, a front and a back one."""
[docs] def __init__(self, front_side=PVRowSide(), back_side=PVRowSide(),
index=None, original_linestring=None):
"""Initialize PV row.
Parameters
----------
front_side : :py:class:`~pvfactors.geometry.pvrow.PVRowSide`, optional
Front side of the PV Row (Default = Empty PVRowSide)
back_side : :py:class:`~pvfactors.geometry.pvrow.PVRowSide`, optional
Back side of the PV Row (Default = Empty PVRowSide)
index : int, optional
Index of PV row (Default = None)
original_linestring : :py:class:`shapely.geometry.LineString`, optional
Full continuous linestring that the PV row will be made of
(Default = None)
"""
self.front = front_side
self.back = back_side
self.index = index
self.original_linestring = original_linestring
self._all_surfaces = None
super(PVRow, self).__init__([self.front, self.back])
@classmethod
def from_linestring_coords(cls, coords, shaded=False, normal_vector=None,
index=None, cut={}, param_names=[]):
"""Create a PV row with a single PV surface and using linestring
coordinates.
Parameters
----------
coords : list
List of linestring coordinates for the surface
shaded : bool, optional
Shading status desired for the PVRow sides (Default = False)
normal_vector : list, optional
Normal vector for the surface (Default = None)
index : int, optional
Index of PV row (Default = None)
cut : dict, optional
Scheme to decide how many segments to create on each side.
Eg {'front': 3, 'back': 2} will lead to 3 segments on front side
and 2 segments on back side. (Default = {})
param_names : list of str, optional
Names of the surface parameters, eg reflectivity, total incident
irradiance, temperature, etc. (Default = [])
Returns
-------
:py:class:`~pvfactors.geometry.pvrow.PVRow` object
"""
index_single_segment = 0
front_side = PVRowSide.from_linestring_coords(
coords, shaded=shaded, normal_vector=normal_vector,
index=index_single_segment, n_segments=cut.get('front', 1),
param_names=param_names)
if normal_vector is not None:
back_n_vec = - np.array(normal_vector)
else:
back_n_vec = - front_side.n_vector
back_side = PVRowSide.from_linestring_coords(
coords, shaded=shaded, normal_vector=back_n_vec,
index=index_single_segment, n_segments=cut.get('back', 1),
param_names=param_names)
return cls(front_side=front_side, back_side=back_side, index=index,
original_linestring=LineString(coords))
@classmethod
def from_center_tilt_width(cls, xy_center, tilt, width, surface_azimuth,
axis_azimuth, shaded=False, normal_vector=None,
index=None, cut={}, param_names=[]):
"""Create a PV row using mainly the coordinates of the line center,
a tilt angle, and its length.
Parameters
----------
xy_center : tuple
x, y coordinates of center point of desired linestring
tilt : float
surface tilt angle desired [deg]
length : float
desired length of linestring [m]
surface_azimuth : float
Surface azimuth of PV surface [deg]
axis_azimuth : float
Axis azimuth of the PV surface, i.e. direction of axis of rotation
[deg]
shaded : bool, optional
Shading status desired for the PVRow sides (Default = False)
normal_vector : list, optional
Normal vector for the surface (Default = None)
index : int, optional
Index of PV row (Default = None)
cut : dict, optional
Scheme to decide how many segments to create on each side.
Eg {'front': 3, 'back': 2} will lead to 3 segments on front side
and 2 segments on back side. (Default = {})
param_names : list of str, optional
Names of the surface parameters, eg reflectivity, total incident
irradiance, temperature, etc. (Default = [])
Returns
-------
:py:class:`~pvfactors.geometry.pvrow.PVRow` object
"""
coords = _coords_from_center_tilt_length(xy_center, tilt, width,
surface_azimuth, axis_azimuth)
return cls.from_linestring_coords(coords, shaded=shaded,
normal_vector=normal_vector,
index=index, cut=cut,
param_names=param_names)
def plot(self, ax, color_shaded=COLOR_DIC['pvrow_shaded'],
color_illum=COLOR_DIC['pvrow_illum'], with_index=False):
"""Plot the surfaces of the PV Row.
Parameters
----------
ax : :py:class:`matplotlib.pyplot.axes` object
Axes for plotting
color_shaded : str, optional
Color to use for plotting the shaded surfaces (Default =
COLOR_DIC['pvrow_shaded'])
color_shaded : str, optional
Color to use for plotting the illuminated surfaces (Default =
COLOR_DIC['pvrow_illum'])
with_index : bool
Flag to annotate surfaces with their indices (Default = False)
"""
self.front.plot(ax, color_shaded=color_shaded, color_illum=color_illum,
with_index=with_index)
self.back.plot(ax, color_shaded=color_shaded, color_illum=color_illum,
with_index=with_index)
@property
def boundary(self):
"""Boundaries of the PV Row's orginal linestring."""
return self.original_linestring.boundary
@property
def highest_point(self):
"""Highest point of the PV Row."""
b1, b2 = self.boundary
highest_point = b1 if b1.y > b2.y else b2
return highest_point
@property
def lowest_point(self):
"""Lowest point of the PV Row."""
b1, b2 = self.boundary
lowest_point = b1 if b1.y < b2.y else b2
return lowest_point
@property
def all_surfaces(self):
"""List of all the surfaces in the PV row."""
if self._all_surfaces is None:
self._all_surfaces = []
self._all_surfaces += self.front.all_surfaces
self._all_surfaces += self.back.all_surfaces
return self._all_surfaces
@property
def surface_indices(self):
"""List of all surface indices in the PV Row."""
list_indices = []
list_indices += self.front.surface_indices
list_indices += self.back.surface_indices
return list_indices
def update_params(self, new_dict):
"""Update surface parameters for both front and back sides.
Parameters
----------
new_dict : dict
Parameters to add or update for the surface
"""
self.front.update_params(new_dict)
self.back.update_params(new_dict)