diff --git a/doc/gen_library_ref_rst_from_class_list.sh b/doc/gen_library_ref_rst_from_class_list.sh new file mode 100755 index 000000000..52b227baf --- /dev/null +++ b/doc/gen_library_ref_rst_from_class_list.sh @@ -0,0 +1,38 @@ +#!/bin/bash +echo "Usage example: " +echo " ./gen_library_ref_rst_from_class_list.sh \\" +echo " ./source/library_reference/common/lib/csp \\" +echo " CSP \\" +echo " \"This is a collection of CSP utilities for OpenCSP." "opencsp.common.lib.csp.Facet, opencsp.common.lib.csp.FacetEnsemble, opencsp.common.lib.csp.HeliostatAbstract, opencsp.common.lib.csp.HeliostatAzEl, opencsp.common.lib.csp.HeliostatConfiguration, opencsp.common.lib.csp.LightPath, opencsp.common.lib.csp.LightPathEnsemble, opencsp.common.lib.csp.LightSource, opencsp.common.lib.csp.LightSourcePoint, opencsp.common.lib.csp.LightSourceSun, opencsp.common.lib.csp.MirrorAbstract, opencsp.common.lib.csp.MirrorParametric, opencsp.common.lib.csp.MirrorParametricRectangular, opencsp.common.lib.csp.MirrorPoint, opencsp.common.lib.csp.OpticOrientationAbstract, opencsp.common.lib.csp.RayTrace, opencsp.common.lib.csp.RayTraceable, opencsp.common.lib.csp.Scene, opencsp.common.lib.csp.SolarField, opencsp.common.lib.csp.StandardPlotOutput, opencsp.common.lib.csp.Tower, opencsp.common.lib.csp.VisualizeOrthorectifiedSlopeAbstract, opencsp.common.lib.csp.sun_position, opencsp.common.lib.csp.sun_track, opencsp.common.lib.csp.visualize_orthorectified_image\"\\" + +DOC_PATH="${1:?Error: DOC_PATH is unset}" +DOC_SHORT_DESCRIPTOR="${2:?Error: DOC_SHORT_DESCRIPTOR is unset}" +DOC_LONG_DESCRIPTOR="${3:?Error: DOC_LONG_DESCRIPTOR is unset}" +CLASS_LIST="${4:?Error: CLASS_LIST is unset}" + +sed_cmd=sed +which gsed &> /dev/null +ret=$? +if [ $ret -eq 0 ]; then + sed_cmd=gsed +fi + +mkdir -p $DOC_PATH || true +rm -i $DOC_PATH/config.rst +rm -i $DOC_PATH/index.rst + +for class in $(echo $CLASS_LIST); do + sans_comma=$(echo $class | tr -d ',') + cat template_config | ${sed_cmd} "s/__MODULE__/$sans_comma/g" >> $DOC_PATH/config.rst +done + +echo "$DOC_SHORT_DESCRIPTOR" >> $DOC_PATH/index.rst +footer_len=$(echo $DOC_SHORT_DESCRIPTOR | wc -c) +python -c "print('=' * ${footer_len})" >> $DOC_PATH/index.rst +echo "" >> $DOC_PATH/index.rst + +echo "$DOC_LONG_DESCRIPTOR" >> $DOC_PATH/index.rst +echo "" >> $DOC_PATH/index.rst +cat template_index >> $DOC_PATH/index.rst + +echo -n "$DOC_PATH" | awk -F 'library_reference/' '{print " "$2"/index.rst"}' >> ./source/library_reference/index.rst diff --git a/doc/source/library_reference/common/lib/csp/config.rst b/doc/source/library_reference/common/lib/csp/config.rst new file mode 100644 index 000000000..b9614a753 --- /dev/null +++ b/doc/source/library_reference/common/lib/csp/config.rst @@ -0,0 +1,257 @@ +Facet +===== + +.. currentmodule:: opencsp.common.lib.csp.Facet + +.. automodule:: opencsp.common.lib.csp.Facet + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.csp.FacetEnsemble + +.. automodule:: opencsp.common.lib.csp.FacetEnsemble + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +Heliostat +========= + +.. currentmodule:: opencsp.common.lib.csp.HeliostatAbstract + +.. automodule:: opencsp.common.lib.csp.HeliostatAbstract + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.csp.HeliostatAzEl + +.. automodule:: opencsp.common.lib.csp.HeliostatAzEl + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.csp.HeliostatConfiguration + +.. automodule:: opencsp.common.lib.csp.HeliostatConfiguration + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +LightPath +========= + +.. currentmodule:: opencsp.common.lib.csp.LightPath + +.. automodule:: opencsp.common.lib.csp.LightPath + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.csp.LightPathEnsemble + +.. automodule:: opencsp.common.lib.csp.LightPathEnsemble + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +LightSource +=========== + +.. currentmodule:: opencsp.common.lib.csp.LightSource + +.. automodule:: opencsp.common.lib.csp.LightSource + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.csp.LightSourcePoint + +.. automodule:: opencsp.common.lib.csp.LightSourcePoint + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.csp.LightSourceSun + +.. automodule:: opencsp.common.lib.csp.LightSourceSun + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +Mirror +====== + +.. currentmodule:: opencsp.common.lib.csp.MirrorAbstract + +.. automodule:: opencsp.common.lib.csp.MirrorAbstract + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.csp.MirrorParametric + +.. automodule:: opencsp.common.lib.csp.MirrorParametric + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.csp.MirrorParametricRectangular + +.. automodule:: opencsp.common.lib.csp.MirrorParametricRectangular + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.csp.MirrorPoint + +.. automodule:: opencsp.common.lib.csp.MirrorPoint + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +RayTrace and OpticOrientation +============================= + +.. currentmodule:: opencsp.common.lib.csp.OpticOrientationAbstract + +.. automodule:: opencsp.common.lib.csp.OpticOrientationAbstract + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.csp.RayTrace + +.. automodule:: opencsp.common.lib.csp.RayTrace + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.csp.RayTraceable + +.. automodule:: opencsp.common.lib.csp.RayTraceable + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +Scene +===== + +.. currentmodule:: opencsp.common.lib.csp.Scene + +.. automodule:: opencsp.common.lib.csp.Scene + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +SolarField +========== + +.. currentmodule:: opencsp.common.lib.csp.SolarField + +.. automodule:: opencsp.common.lib.csp.SolarField + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +Tower +===== + +.. currentmodule:: opencsp.common.lib.csp.Tower + +.. automodule:: opencsp.common.lib.csp.Tower + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +Sun +=== + +.. currentmodule:: opencsp.common.lib.csp.sun_position + +.. automodule:: opencsp.common.lib.csp.sun_position + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.csp.sun_track + +.. automodule:: opencsp.common.lib.csp.sun_track + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +Plotting +======== + +.. currentmodule:: opencsp.common.lib.csp.StandardPlotOutput + +.. automodule:: opencsp.common.lib.csp.StandardPlotOutput + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.csp.VisualizeOrthorectifiedSlopeAbstract + +.. automodule:: opencsp.common.lib.csp.VisualizeOrthorectifiedSlopeAbstract + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + +.. currentmodule:: opencsp.common.lib.csp.visualize_orthorectified_image + +.. automodule:: opencsp.common.lib.csp.visualize_orthorectified_image + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise diff --git a/doc/source/library_reference/common/lib/csp/index.rst b/doc/source/library_reference/common/lib/csp/index.rst new file mode 100644 index 000000000..9b1fea540 --- /dev/null +++ b/doc/source/library_reference/common/lib/csp/index.rst @@ -0,0 +1,9 @@ +CSP +==== + +This is a collection of CSP utilities for OpenCSP. + +.. toctree:: + :maxdepth: 1 + + config.rst \ No newline at end of file diff --git a/doc/source/library_reference/index.rst b/doc/source/library_reference/index.rst index db74f4999..5e348a33a 100644 --- a/doc/source/library_reference/index.rst +++ b/doc/source/library_reference/index.rst @@ -11,4 +11,5 @@ This section describes the OpenCSP classes and interfaces. app/camera_calibration/index.rst app/scene_reconstruction/index.rst common/lib/cv/index.rst - common/lib/camera/index.rst \ No newline at end of file + common/lib/camera/index.rst + common/lib/csp/index.rst diff --git a/doc/template_config b/doc/template_config new file mode 100644 index 000000000..4476b87ad --- /dev/null +++ b/doc/template_config @@ -0,0 +1,9 @@ +.. currentmodule:: __MODULE__ + +.. automodule:: __MODULE__ + :members: + :special-members: __init__ + :undoc-members: + :show-inheritance: + :member-order: groupwise + diff --git a/doc/template_index b/doc/template_index new file mode 100644 index 000000000..15719ec32 --- /dev/null +++ b/doc/template_index @@ -0,0 +1,4 @@ +.. toctree:: + :maxdepth: 1 + + config.rst \ No newline at end of file diff --git a/opencsp/common/lib/csp/HeliostatAbstract.py b/opencsp/common/lib/csp/HeliostatAbstract.py index 4af3d68bb..09c684fec 100644 --- a/opencsp/common/lib/csp/HeliostatAbstract.py +++ b/opencsp/common/lib/csp/HeliostatAbstract.py @@ -42,11 +42,12 @@ class HeliostatAbstract(RayTraceable, OpticOrientationAbstract, ABC): """ Heliostat representation. - Parameters: - ----------- - facet_ensemble: FacetEnsemble - List of facets, in order from top-left to bottom-right (row major order). - The facet names should be their position in the list (1-indexed). + + Parameters + ---------- + facet_ensemble: FacetEnsemble + List of facets, in order from top-left to bottom-right (row major order). + The facet names should be their position in the list (1-indexed). """ def __init__( @@ -129,18 +130,18 @@ def movement_transform(self, config: HeliostatConfiguration) -> TransformXYZ: Example ------- - ```python - # override movement_transform in HeliostatAbstract - def movement_transform(self, az_angle: float, el_angle: float): - '''possible movement_transform for an oversimplified - azimuth and elevation based heliostat.''' - az_rotation = Rotation.from_euler('z', az_angle) - transform_az = TransformXYZ.from_R(az_rotation) - el_rotation = # rotate about the proper vector - transform_el = TransformXYZ.from_R(el_rotation) - composite_transform = transform_el * transform_az - return composite_transform - ``` + .. code-block:: python + + # override movement_transform in HeliostatAbstract + def movement_transform(self, az_angle: float, el_angle: float): + '''possible movement_transform for an oversimplified + azimuth and elevation based heliostat.''' + az_rotation = Rotation.from_euler('z', az_angle) + transform_az = TransformXYZ.from_R(az_rotation) + el_rotation = # rotate about the proper vector + transform_el = TransformXYZ.from_R(el_rotation) + composite_transform = transform_el * transform_az + return composite_transform """ ... diff --git a/opencsp/common/lib/csp/HeliostatConfiguration.py b/opencsp/common/lib/csp/HeliostatConfiguration.py index ece44c7f9..e678d70cd 100644 --- a/opencsp/common/lib/csp/HeliostatConfiguration.py +++ b/opencsp/common/lib/csp/HeliostatConfiguration.py @@ -10,12 +10,42 @@ class HeliostatConfiguration: """ Container for the variables defining the heliostat configuration. - User must provide the type of heliostat in the format in `valid_heliostat_types. + This class allows the user to define a heliostat configuration by specifying + the type of heliostat and its associated parameters. Currently, only the 'az-el' + type is supported, which requires azimuth and elevation angles. + + Parameters + ---------- + heliostat_type : str + The type of heliostat configuration. Must be one of the valid heliostat types. + az : float, optional + The azimuth angle in radians (required for 'az-el' type). + el : float, optional + The elevation angle in radians (required for 'az-el' type). + + Raises + ------ + ValueError + If the provided heliostat type is invalid or if az and el are not provided + for the 'az-el' type. """ + # "ChatGPT 4o-mini" assisted with generating this docstring. def __init__(self, heliostat_type: str, az: float = None, el: float = None) -> None: - + """ + Initializes a HeliostatConfiguration object with the specified type and angles. + + Parameters + ---------- + heliostat_type : str + The type of heliostat configuration. + az : float, optional + The azimuth angle in radians (default is None). + el : float, optional + The elevation angle in radians (default is None). + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. self.valid_heliostat_types = ['az-el'] self.heliostat_type = heliostat_type @@ -31,6 +61,20 @@ def __init__(self, heliostat_type: str, az: float = None, el: float = None) -> N self.el = el def get_values(self): + """ + Retrieves the azimuth and elevation values for the heliostat configuration. + + Returns + ------- + tuple + A tuple containing the azimuth and elevation angles in radians. + + Raises + ------ + ValueError + If the heliostat type is invalid. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. if self.heliostat_type == 'az-el': return self.az, self.el raise ValueError( @@ -42,6 +86,23 @@ def get_values(self): def heliostat_configuration_given_surface_normal_xyz(n_xyz) -> HeliostatConfiguration: + """ + Creates a HeliostatConfiguration from a given surface normal vector. + + This function converts the surface normal coordinates into azimuth and elevation + angles. + + Parameters + ---------- + n_xyz : array-like + A 3-element array or list representing the surface normal vector in 3D space. + + Returns + ------- + HeliostatConfiguration + A HeliostatConfiguration object with the calculated azimuth and elevation angles. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Extract surface normal coordinates. n_x = n_xyz[0] n_y = n_xyz[1] @@ -68,25 +129,79 @@ def heliostat_configuration_given_surface_normal_xyz(n_xyz) -> HeliostatConfigur def face_north() -> HeliostatConfiguration: + """ + Creates a HeliostatConfiguration for a heliostat facing north. + + Returns + ------- + HeliostatConfiguration + A HeliostatConfiguration object with azimuth set to 0 radians and elevation set to 0 radians. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return HeliostatConfiguration('az-el', az=np.deg2rad(0), el=np.deg2rad(0)) def face_south() -> HeliostatConfiguration: + """ + Creates a HeliostatConfiguration for a heliostat facing south. + + Returns + ------- + HeliostatConfiguration + A HeliostatConfiguration object with azimuth set to 180 radians and elevation set to 0 radians. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return HeliostatConfiguration('az-el', az=np.deg2rad(180), el=np.deg2rad(0)) def face_east() -> HeliostatConfiguration: + """ + Creates a HeliostatConfiguration for a heliostat facing east. + + Returns + ------- + HeliostatConfiguration + A HeliostatConfiguration object with azimuth set to 90 radians and elevation set to 0 radians. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return HeliostatConfiguration('az-el', az=np.deg2rad(90), el=np.deg2rad(0)) def face_west() -> HeliostatConfiguration: + """ + Creates a HeliostatConfiguration for a heliostat facing west. + + Returns + ------- + HeliostatConfiguration + A HeliostatConfiguration object with azimuth set to 270 radians and elevation set to 0 radians. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return HeliostatConfiguration('az-el', az=np.deg2rad(270), el=np.deg2rad(0)) def face_up() -> HeliostatConfiguration: + """ + Creates a HeliostatConfiguration for a heliostat facing directly up. + + Returns + ------- + HeliostatConfiguration + A HeliostatConfiguration object with azimuth set to 180 radians and elevation set to 90 radians. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Azinumth for UFACET scans. return HeliostatConfiguration('az-el', az=np.deg2rad(180), el=np.deg2rad(90)) def NSTTF_stow() -> HeliostatConfiguration: + """ + Creates a HeliostatConfiguration for the NSTTF stow position. + + Returns + ------- + HeliostatConfiguration + A HeliostatConfiguration object with azimuth set to 270 radians and elevation set to -85 radians. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return HeliostatConfiguration('az-el', az=np.deg2rad(270), el=np.deg2rad(-85)) diff --git a/opencsp/common/lib/csp/LightPathEnsemble.py b/opencsp/common/lib/csp/LightPathEnsemble.py index 53360a237..703e6df27 100644 --- a/opencsp/common/lib/csp/LightPathEnsemble.py +++ b/opencsp/common/lib/csp/LightPathEnsemble.py @@ -7,8 +7,36 @@ class LightPathEnsemble: + """ + A container for managing a collection of light paths. + + This class aggregates multiple LightPath objects, allowing for operations + such as concatenation and direction management. + + Attributes + ---------- + current_directions : Vxyz + The current directions of the light paths in the ensemble. + init_directions : Vxyz + The initial directions of the light paths in the ensemble. + points_lists : list[list[Pxyz]] + A list of point lists for each light path in the ensemble. + colors : list + A list of colors associated with each light path in the ensemble. + """ + + # "ChatGPT 4o-mini" assisted with generating this docstring. def __init__(self, lps: list[LightPath]) -> None: + """ + Initializes a LightPathEnsemble with the given light paths. + + Parameters + ---------- + lps : list[LightPath] + A list of LightPath objects to initialize the ensemble. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. self.current_directions = Vxyz.merge([lp.current_direction for lp in lps]) self.init_directions = Vxyz.merge([lp.init_direction for lp in lps]) self.points_lists = [lp.points_list for lp in lps] @@ -32,6 +60,26 @@ def __iadd__(self, lpe: 'LightPathEnsemble'): @classmethod def from_parts(cls, init_directions: Uxyz, points: list[Pxyz], curr_directions: Uxyz, colors=[]): + """ + Creates a LightPathEnsemble from its component parts. + + Parameters + ---------- + init_directions : Uxyz + The initial directions of the light paths. + points : list[Pxyz] + A list of point lists for each light path. + curr_directions : Uxyz + The current directions of the light paths. + colors : list, optional + A list of colors associated with each light path (default is empty list). + + Returns + ------- + LightPathEnsemble + A new LightPathEnsemble object constructed from the provided parts. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. lpe = LightPathEnsemble([]) lpe.current_directions = curr_directions lpe.init_directions = init_directions @@ -41,6 +89,23 @@ def from_parts(cls, init_directions: Uxyz, points: list[Pxyz], curr_directions: # # @strict_types def add_steps(self, points: Pxyz, new_current_directions: Uxyz): + """ + Adds new steps to the light paths in the ensemble. + + Parameters + ---------- + points : Pxyz + A list of new points to append to each light path. + new_current_directions : Uxyz + A list of new current directions corresponding to the new points. + + Raises + ------ + ValueError + If the number of points does not match the number of new directions + or if the number of new steps does not match the number of light paths. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. if len(points) != len(new_current_directions): raise ValueError( f"The number of points but be the same as the number of new directions when appending to a LightPathEnsemble.\n \ @@ -64,6 +129,20 @@ def add_steps(self, points: Pxyz, new_current_directions: Uxyz): self.current_directions = new_current_directions # update the current directions def concatenate_in_place(self: 'LightPathEnsemble', lpe1: 'LightPathEnsemble'): + """ + Concatenates another LightPathEnsemble into this one in place. + + Parameters + ---------- + lpe1 : LightPathEnsemble + The LightPathEnsemble to concatenate. + + Returns + ------- + LightPathEnsemble + The updated LightPathEnsemble after concatenation. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. self.current_directions = self.current_directions.concatenate(lpe1.current_directions) self.init_directions = self.init_directions.concatenate(lpe1.init_directions) self.points_lists += lpe1.points_lists @@ -71,6 +150,15 @@ def concatenate_in_place(self: 'LightPathEnsemble', lpe1: 'LightPathEnsemble'): return self def asLightPathList(self) -> list[LightPath]: + """ + Converts the ensemble into a list of LightPath objects. + + Returns + ------- + list[LightPath] + A list of LightPath objects constructed from the ensemble's data. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. lps: list[LightPath] = [] for cd, id, pl in zip(self.current_directions, self.init_directions, self.points_lists): lp = LightPath(pl, id, cd) @@ -82,16 +170,23 @@ def __add__(self, lpe: 'LightPathEnsemble'): return self.concatenate(lpe) def concatenate(self: 'LightPathEnsemble', lpe1: 'LightPathEnsemble'): + """ + Concatenates another LightPathEnsemble and returns a new instance. + + Parameters + ---------- + lpe1 : LightPathEnsemble + The LightPathEnsemble to concatenate. + + Returns + ------- + LightPathEnsemble + A new LightPathEnsemble containing the concatenated data. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. new_lpe = LightPathEnsemble([]) new_lpe.current_directions = self.current_directions.concatenate(lpe1.current_directions) new_lpe.init_directions = self.init_directions.concatenate(lpe1.init_directions) new_lpe.points_lists = self.points_lists + (lpe1.points_lists) new_lpe.colors = self.colors + (lpe1.colors) return new_lpe - - def asLightPathList(self) -> list[LightPath]: - lps: list[LightPath] = [] - for cd, id, pl in zip(self.current_directions, self.init_directions, self.points_lists): - lp = LightPath(pl, id, cd) - lps.append(lp) - return lps diff --git a/opencsp/common/lib/csp/LightSource.py b/opencsp/common/lib/csp/LightSource.py index 93ff99064..476ac4545 100644 --- a/opencsp/common/lib/csp/LightSource.py +++ b/opencsp/common/lib/csp/LightSource.py @@ -4,7 +4,9 @@ from opencsp.common.lib.geometry.Pxyz import Pxyz -class LightSource(ABC): # interface for objects that can be light sources +class LightSource(ABC): + """interface for objects that can be light sources""" + @abstractmethod def get_incident_rays(self, point: Pxyz) -> list[LightPath]: """Returns the rays originating from this light source incident to the point.""" diff --git a/opencsp/common/lib/csp/LightSourcePoint.py b/opencsp/common/lib/csp/LightSourcePoint.py index d475d767e..6595e4ae6 100644 --- a/opencsp/common/lib/csp/LightSourcePoint.py +++ b/opencsp/common/lib/csp/LightSourcePoint.py @@ -6,12 +6,54 @@ class LightSourcePoint(LightSource): + """ + A class representing a point light source in 3D space. + + This class defines a light source located at a specific point in space, + characterized by its position and the ability to generate incident rays + towards a specified point. + """ + + # "ChatGPT 4o-mini" assisted with generating this docstring. def __init__(self, location_in_space: Pxyz) -> None: + """ + Initializes a LightSourcePoint with the specified location in space. + + Parameters + ---------- + location_in_space : Pxyz + The position of the light source in 3D space. + + Raises + ------ + TypeError + If the input location_in_space is not a subclass of Vxyz. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. if not isinstance(location_in_space, Vxyz): raise TypeError(f"Input location_in_space must be subclass of {Vxyz} but is {type(location_in_space)}") self.location_in_space = location_in_space def get_incident_rays(self, point: Pxyz) -> list[LightPath]: + """ + Generates a list of incident rays from the light source to a specified point. + + Parameters + ---------- + point : Pxyz + The target point in 3D space to which the incident rays are directed. + + Returns + ------- + list[LightPath] + A list of LightPath objects representing the incident rays from the light source to the specified point. + + Raises + ------ + TypeError + If the input point is not a subclass of Vxyz. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Check inputs if not isinstance(point, Vxyz): raise TypeError(f"Input point must be subclass of {Vxyz} but is {type(point)}") diff --git a/opencsp/common/lib/csp/LightSourceSun.py b/opencsp/common/lib/csp/LightSourceSun.py index 37537e4f6..92fbf6b17 100644 --- a/opencsp/common/lib/csp/LightSourceSun.py +++ b/opencsp/common/lib/csp/LightSourceSun.py @@ -14,30 +14,73 @@ class LightSourceSun(LightSource): + """ + A class representing a point light source that simulates sunlight. + + This class models the sun as a top-hat function in space, allowing for the generation + of incident rays based on the sun's position in the sky. + + Attributes + ---------- + incident_rays : list[LightPath] + A list of LightPath objects representing the rays incident from the sun. + """ + + # "ChatGPT 4o-mini" assisted with generating this docstring. def __init__(self) -> None: + """ + Initializes a LightSourceSun object with an empty list of incident rays. + + Parameters + ---------- + None + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. self.incident_rays: list[LightPath] = [] def get_incident_rays(self, point: Pxyz) -> list[LightPath]: + """ + Retrieves the incident rays from the light source. + + Parameters + ---------- + point : Pxyz + The point in space for which the incident rays are requested. + + Returns + ------- + list[LightPath] + A list of LightPath objects representing the incident rays. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return self.incident_rays @classmethod def from_given_sun_position( cls, sun_pointing: Uxyz, resolution: int, sun_dia: float = 0.009308, verbose=False ) -> 'LightSourceSun': - """Returns LightSourceSun object initialized from a given pointing direction. Represents - the sun as a tophat function in space. + """ + Creates a LightSourceSun object initialized from a given sun pointing direction. + + Represents the sun as a top-hat function in space. Parameters ---------- sun_pointing : Uxyz - Pointing direction of sun - resolution: float - Number of points in each direction that will be sampled. - sun_dia: float - Angular diameter of the sun, radians. - verbose: bool - To print updates. + The pointing direction of the sun. + resolution : int + The number of points in each direction that will be sampled. + sun_dia : float, optional + The angular diameter of the sun in radians (default is 0.009308). + verbose : bool, optional + If True, prints updates during initialization (default is False). + + Returns + ------- + LightSourceSun + A LightSourceSun object initialized with the specified sun pointing direction. """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Calculate sun ray cone pointing down (z=-1) sun_rays = cls._calc_sun_ray_cone(resolution, sun_dia, verbose) @@ -63,22 +106,29 @@ def from_location_time( verbose=False, ) -> 'LightSourceSun': """ - Returns LightSourceSun object initialized from a given Lat/Lon and time. Represents - the sun as a tophat function in space. + Creates a LightSourceSun object initialized from a given latitude/longitude and time. + + Represents the sun as a top-hat function in space. Parameters - ----------- - loc: tuple(float, float) - The location of the scene in form (latitude, longitude), degrees, WGS84. - time: datetime.datetime - Datetime object. Must have timezone set. - resolution: float - Number of points in each direction that will be sampled. - sun_dia: float - Angular diameter of the sun, radians. - verbose: bool - To print updates. + ---------- + loc : tuple[float, float] + The location of the scene in the form (latitude, longitude) in degrees, WGS84. + time : datetime.datetime + A datetime object representing the time. Must have timezone set. + resolution : int + The number of points in each direction that will be sampled. + sun_dia : float, optional + The angular diameter of the sun in radians (default is 0.009308). + verbose : bool, optional + If True, prints updates during initialization (default is False). + + Returns + ------- + LightSourceSun + A LightSourceSun object initialized based on the specified location and time. """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Calculate direction of sun pointing longitude, latitude = loc alt = pysolar.solar.get_altitude(latitude, longitude, time) @@ -140,22 +190,28 @@ def set_incident_rays( ) -> None: """ Defines the rays that will be used from this light source for ray tracing. - Sets them to self.incident_rays + + Sets them to self.incident_rays. Parameters - ----------- - loc: tuple[float, float] | tuple(float, float) - two elements representing the location of the scene that will see the sun rays. (longitude, latitude) - time: tuple - tuple is in the ymdhmsz convention, (year, month, day, hour, minute, seconf, time zone) - resolution: float - (TODO update for more types of reolustion) the number of points in each direction that will - be sampled. - sun_dia: float - the angular diameter of the sun. Default value is recomended if your reference is on Earth - verbose: bool - If True the funciton will print updates on how many rays have been generated to console. + ---------- + loc : tuple[float, float] + A tuple representing the location of the scene that will see the sun rays in the form (longitude, latitude). + time : tuple + A tuple in the ymdhmsz convention, representing (year, month, day, hour, minute, second, time zone). + resolution : int + The number of points in each direction that will be sampled. + sun_dia : float, optional + The angular diameter of the sun in radians (default is 0.009308). + verbose : bool, optional + If True, prints updates on how many rays have been generated to the console (default is False). + + Raises + ------ + DeprecationWarning + If this method is called, indicating it is deprecated. """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Function is deprecated warn( 'LightSourceSun.set_incident_rays is deprecated. Use initialize_from_solar_position instead.', diff --git a/opencsp/common/lib/csp/MirrorPoint.py b/opencsp/common/lib/csp/MirrorPoint.py index 852a5cd36..8858dd7a6 100644 --- a/opencsp/common/lib/csp/MirrorPoint.py +++ b/opencsp/common/lib/csp/MirrorPoint.py @@ -18,9 +18,14 @@ class MirrorPoint(MirrorAbstract): - """Mirror class representing mirrors with scattered surface point - locations. """ + A class representing a mirror defined by discrete, scattered surface points and corresponding normal vectors. + + This class allows for the representation of a mirror's surface using a set of points and their associated + normal vectors, with options for interpolation methods to define the surface behavior. + """ + + # "ChatGPT 4o-mini" assisted with generating this docstring. def __init__( self, @@ -29,29 +34,26 @@ def __init__( shape: RegionXY, interpolation_type: Literal['given', 'bilinear', 'clough_tocher', 'nearest'] = 'nearest', ) -> None: - """Class representing a mirror defined by discrete, scattered points - and corresponding normal vectors. + """ + Initializes a MirrorPoint object with the specified surface points and normal vectors. Parameters ---------- surface_points : Pxyz - XYZ points on surface of mirror + The XYZ coordinates of points on the surface of the mirror. normal_vectors : Uxyz - XYZ normal vectors corresponding to surface_points - interpolation_type : str - Interpolation type: - - 'given' - Uses given XY points in look-up table - - 'bilinear' - bilinear interpolation - - 'clough_tocher' - Clough-Tocher interpolation - - 'nearest' - nearest neighbor interpolation + The XYZ normal vectors corresponding to the surface points. shape : RegionXY - XY outline of mirror + The XY outline of the mirror. + interpolation_type : Literal['given', 'bilinear', 'clough_tocher', 'nearest'], optional + The type of interpolation to use for the surface and normal vectors (default is 'nearest'). Raises ------ ValueError - If not all normal vectors have a positive z component + If not all normal vectors have a positive z component. """ + # "ChatGPT 4o-mini" assisted with generating this docstring. super().__init__(shape) # initalizes the attributes universal to all mirrors # Save surface points and normal vectors @@ -137,17 +139,73 @@ def _check_in_bounds(self, p_samp: Pxyz) -> None: raise ValueError("Not all points are within mirror perimeter.") def surface_norm_at(self, p: Pxy) -> Vxyz: + """ + Retrieves the surface normal vector at a specified point. + + Parameters + ---------- + p : Pxy + The point at which to retrieve the surface normal. + + Returns + ------- + Vxyz + The normalized surface normal vector at the specified point. + + Raises + ------ + ValueError + If the point is not within the bounds of the mirror. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. self._check_in_bounds(p) pts = self.normals_function(p.x, p.y) return Vxyz(pts.T).normalize() def surface_displacement_at(self, p: Pxy) -> np.ndarray: + """ + Retrieves the surface displacement at a specified point. + + Parameters + ---------- + p : Pxy + The point at which to retrieve the surface displacement. + + Returns + ------- + np.ndarray + The displacement of the surface at the specified point. + + Raises + ------ + ValueError + If the point is not within the bounds of the mirror. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. self._check_in_bounds(p) return self.surface_function(p.x, p.y) def survey_of_points( self, resolution: int = 1, resolution_type: str = "pixelX", random_seed: int | None = None ) -> tuple[Pxyz, Vxyz]: + """ + Surveys points on the mirror surface and retrieves their positions and normals. + + Parameters + ---------- + resolution : int, optional + The resolution for sampling points on the mirror surface (default is 1). + resolution_type : str, optional + The type of resolution to use (default is "pixelX"). + random_seed : int or None, optional + A seed for random number generation (default is None). + + Returns + ------- + tuple[Pxyz, Vxyz] + A tuple containing the sampled points and their corresponding normal vectors. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # If using "given" type samping if self.interpolation_type == 'given': if resolution_type != "given": @@ -165,6 +223,23 @@ def survey_of_points( return points, normals def draw(self, view: View3d, mirror_style: RenderControlMirror, transform: TransformXYZ | None = None) -> None: + """ + Draws the mirror in a 3D view. + + Parameters + ---------- + view : View3d + The 3D view in which to draw the mirror. + mirror_style : RenderControlMirror + The style settings for rendering the mirror. + transform : TransformXYZ or None, optional + A transformation to apply to the mirror when drawing (default is None). + + Returns + ------- + None + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # If no interpolation if self.interpolation_type == 'given': resolution = mirror_style.resolution diff --git a/opencsp/common/lib/csp/OpticOrientationAbstract.py b/opencsp/common/lib/csp/OpticOrientationAbstract.py index 766aa7935..74400998f 100644 --- a/opencsp/common/lib/csp/OpticOrientationAbstract.py +++ b/opencsp/common/lib/csp/OpticOrientationAbstract.py @@ -11,28 +11,28 @@ class OpticOrientationAbstract(ABC): orientations, and can contain child objects (that also extend OpticOrientationAbstract) that transform with it. - Core Attributes: - ---------------- + Core Attributes + --------------- `self._parent`: `OpticOrientationAbstract` - - This is the `OpticOrientationAbstract` object that contains the self. + This is the `OpticOrientationAbstract` object that contains the self. If `self` is the largest object then this attribute will be `None`. If an object already has a parent, then it cannot be added to another object as a child. This should be accessed via the `@property` method `self.parent`. `self._self_to_parent_transform`: `TransformXYZ` - - This is the relatice transformation from the `self` coordinate frame to + This is the relatice transformation from the `self` coordinate frame to the `self._parent` frame. (`@property`) `self.children`: `list[OpticOrientationAbstract]` - - This property is an abstract method that must be implemented by all deriving classes. - There should not be a setter for this atribute. Ideally, there is little information that - is stored in two places. However, for both parents and children to find eachother, + This property is an abstract method that must be implemented by all deriving classes. + There should not be a setter for this attribute. Ideally, there is little information that + is stored in two places. However, for both parents and children to find each other, the information about children must be stored in the parent. When implementing `self.children`, ensure that children hold reference to parents. Derived Attributes ------------------ - Other attributes of a `OpticOrientationAbstract` are caluculated based on the other information + Other attributes of a `OpticOrientationAbstract` are calculated based on the other information in the core attributes. - `_children_to_self_transform` diff --git a/opencsp/common/lib/csp/RayTrace.py b/opencsp/common/lib/csp/RayTrace.py index 45e5f99a3..12446f88a 100644 --- a/opencsp/common/lib/csp/RayTrace.py +++ b/opencsp/common/lib/csp/RayTrace.py @@ -30,7 +30,24 @@ class RayTrace: + """ + A class for performing ray tracing in a given scene. + + This class manages the ray tracing process, including the collection of light paths + and the rendering of the scene based on the light sources and objects present. + """ + + # "ChatGPT 4o-mini" assisted with generating this docstring. def __init__(self, scene: scn.Scene = None) -> None: + """ + Initializes a RayTrace object with the specified scene. + + Parameters + ---------- + scene : scn.Scene, optional + The scene in which to perform ray tracing (default is None). + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. if scene == None: scene = scn.Scene() self.scene = scene @@ -40,6 +57,15 @@ def __init__(self, scene: scn.Scene = None) -> None: @property def light_paths(self): + """ + Retrieves the list of light paths in the ray trace. + + Returns + ------- + list[LightPath] + A list of LightPath objects representing the light paths in the scene. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return self.light_paths_ensemble.asLightPathList() def __str__(self) -> str: @@ -60,26 +86,95 @@ def __add__(self, trace: 'RayTrace'): return sum_trace def ray_count(self) -> int: + """ + Returns the number of light paths in the ray trace. + + Returns + ------- + int + The number of light paths in the ensemble. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return len(self.light_paths_ensemble) def draw(self, view: View3d, trace_style: RenderControlRayTrace = None) -> None: + """ + Draws the light paths in a 3D view. + + Parameters + ---------- + view : View3d + The 3D view in which to draw the light paths. + trace_style : RenderControlRayTrace, optional + The style settings for rendering the light paths (default is None). + + Returns + ------- + None + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. if trace_style == None: trace_style = RenderControlRayTrace() for lp in self.light_paths_ensemble: lp.draw(view, trace_style.light_path_control) def draw_subset(self, view: View3d, count: int, trace_style: RenderControlRayTrace = None): + """ + Draws a subset of light paths in a 3D view. + + Parameters + ---------- + view : View3d + The 3D view in which to draw the light paths. + count : int + The number of light paths to draw. + trace_style : RenderControlRayTrace, optional + The style settings for rendering the light paths (default is None). + + Returns + ------- + None + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. for i in np.floor(np.linspace(0, len(self.light_paths_ensemble) - 1, count)): lp = self.light_paths_ensemble[int(i)] lp.draw(view, trace_style.light_path_control) # @strict_types def add_many_light_paths(self, new_paths: list[LightPath]): + """ + Adds multiple light paths to the ray trace. + + Parameters + ---------- + new_paths : list[LightPath] + A list of LightPath objects to add to the ray trace. + + Returns + ------- + None + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. self.light_paths_ensemble.concatenate_in_place(LightPathEnsemble(new_paths)) @classmethod def from_hdf(cls, filename: str, trace_name: str = "RayTrace") -> 'RayTrace': - """Creates a RayTrace object from an hdf5 file.""" + """ + Creates a RayTrace object from an HDF5 file. + + Parameters + ---------- + filename : str + The path to the HDF5 file containing the ray trace data. + trace_name : str, optional + The name of the trace to load from the file (default is "RayTrace"). + + Returns + ------- + RayTrace + A RayTrace object initialized with data from the specified HDF5 file. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. trace = RayTrace() batch_names: list[str] = list(load_hdf5_datasets([f"RayTrace_{trace_name}/Batches"], filename).values())[0] @@ -101,19 +196,33 @@ def from_hdf(cls, filename: str, trace_name: str = "RayTrace") -> 'RayTrace': def calc_reflected_ray(normal_v: Vxyz, incoming_v: Vxyz) -> Vxyz: """ - Calculates reflected ray directions given the direction of incident - collimated light and surface normal vectors. Note, norm_v and inc_v must - broadcast together. - - Algorithm is explained in "/opencsp_code/doc/common/lib/csp/ReflectedRayAlgorithm.pdf". + Calculates the direction of the reflected ray given the incident ray direction and surface normal. Parameters ---------- + normal_v : Vxyz + The normal vector at the surface point where the reflection occurs. + incoming_v : Vxyz + The direction of the incoming ray. Returns ------- + Vxyz + The direction of the reflected ray. + + Notes + ----- + The algorithm for reflection is based on the formula: + + .. code-block:: text + + \[ + \text{reflected\_ray} = \text{incoming\_ray} - 2 \cdot (\text{normal} \cdot \text{incoming\_ray}) \cdot \text{normal} + \] + norm_v and inc_v must broadcast together. """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Process input vector n = normal_v.normalize().data V0 = incoming_v.normalize().data @@ -126,19 +235,21 @@ def calc_reflected_ray(normal_v: Vxyz, incoming_v: Vxyz) -> Vxyz: def process_vector(vec: np.ndarray, norm: bool = False) -> np.ndarray: """ - Reshapes and converts inputs to floats + Reshapes and converts input vectors to floats. Parameters ---------- - vec : array like, 3xN, length 3, 3x1 - Input vectors - norm : bool - To normalize the vector + vec : array-like, shape (3, N) or (3,) + Input vectors to be processed. + norm : bool, optional + If True, the vector will be normalized (default is False). Returns ------- - 3xN numpy arrays, floats, normalized when appropriate. + np.ndarray + A 3xN array of floats, normalized if specified. """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Reshape and convert to float vec = np.array(vec).astype(float) vec = vec.reshape((3, -1)) @@ -159,8 +270,37 @@ def trace_scene_unvec( save_name: str = f"ray_trace_{time.asctime().replace(' ','_').replace(':','_')}", verbose: bool = False, ) -> RayTrace: - """DEPRICATED \n - TODO TJL:trace_scene_vec is PROBABLY BROKEN!""" + """ + DEPRECATED: Traces rays through the scene using an unvectorized approach. + + Parameters + ---------- + scene : scn.Scene + The scene to trace rays through. + obj_resolution : int + The resolution for sampling points on objects. + random_dist : bool, optional + If True, random distribution of rays will be used (default is False). + store_in_ram : bool, optional + If True, the results will be stored in RAM (default is True). + save_in_file : bool, optional + If True, the results will be saved to a file (default is False). + save_name : str, optional + The name of the file to save the results (default is a timestamped name). + verbose : bool, optional + If True, prints updates during processing (default is False). + + Returns + ------- + RayTrace + A RayTrace object containing the traced rays. + + Raises + ------ + DeprecationWarning + If this function is called, indicating it is deprecated. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. warn( "RayTrace.trace_scene is vectorized and will be faster. This function will be phased out.", DeprecationWarning, @@ -236,6 +376,43 @@ def trace_scene( max_ram_in_use_percent: float = 99, verbose: bool = False, ) -> RayTrace: + """ + Traces rays through the scene using a vectorized approach. + + Parameters + ---------- + scene : scn.Scene + The scene to trace rays through. + obj_resolution : Resolution + The resolution for sampling points on objects. + store_in_ram : bool, optional + If True, the results will be stored in RAM (default is True). + save_in_file : bool, optional + If True, the results will be saved to a file (default is False). + save_name : str, optional + The name of the file to save the results (default is None). + trace_name : str, optional + The name of the trace for saving (default is "Default"). + max_ram_in_use_percent : float, optional + The maximum percentage of RAM to use during tracing (default is 99). + verbose : bool, optional + If True, prints updates during processing (default is False). + + Returns + ------- + RayTrace + A RayTrace object containing the traced rays. + + Raises + ------ + UserWarning + If saving is requested but the flag is set to False. + ValueError + If saving is requested but no file name is provided. + MemoryError + If the maximum RAM usage is exceeded before tracing begins. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # argument validity checks if not save_in_file and save_name != None: warn( @@ -470,6 +647,49 @@ def trace_scene_parallel( trace_name: str = "RayTrace", verbose: bool = False, ) -> RayTrace: + """ + Traces rays through the scene using parallel processing. + + This function divides the ray tracing task among multiple processes to improve performance. + + Parameters + ---------- + scene : scn.Scene + The scene to trace rays through. + obj_resolution : Resolution + The resolution for sampling points on objects. + processor_count : int + The number of processors to use for parallel processing. + resolution_type : str, optional + The type of resolution to use (default is 'pixelX'). + store_in_ram : bool, optional + If True, the results will be stored in RAM (default is True). + max_ram_in_use_percent : float, optional + The maximum percentage of RAM to use during tracing (default is 99.0). + save_in_file : bool, optional + If True, the results will be saved to a file (default is False). + save_file_name : str, optional + The name of the file to save the results (default is None). + trace_name : str, optional + The name of the trace for saving (default is "RayTrace"). + verbose : bool, optional + If True, prints updates during processing (default is False). + + Returns + ------- + RayTrace + A RayTrace object containing the traced rays. + + Raises + ------ + UserWarning + If saving is requested but the flag is set to False. + ValueError + If saving is requested but no file name is provided. + MemoryError + If the maximum RAM usage is exceeded before tracing begins. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. ################# argument validity checks #################### # if not save_in_file and save_file_name != None: # warn("Saving file was specified, but 'save_in_file' flag is set to False. Trace will not be saved.", @@ -568,49 +788,6 @@ def trace_scene_parallel( return ray_trace -def plane_intersect_OLD( - ray_trace: RayTrace, v_plane_center: Vxyz, u_plane_norm: Uxyz, epsilon: float = 1e-6, verbose=False -) -> Vxy: - """ - TODO This was the old way of doing plane intersections. Can probably be removed safely. - """ - if verbose: - tot = len(ray_trace.light_paths) - ten_percent = np.ceil(tot / 10) - checkpoints = [n * ten_percent for n in range(10)] - - points_list = [] - - for idx, lp in enumerate(ray_trace.light_paths): - # Intersect if not parallel or hitting from behind - u = lp.current_direction - d = Vxyz.dot(u, u_plane_norm) - if np.abs(d) > epsilon: - p0 = lp.points_list[-1] - w = p0 - v_plane_center - fac = -Vxyz.dot(u_plane_norm, w) / d - v = u * fac - points_list.append(p0 + v) - # Print output - if verbose and idx in checkpoints: - print(f"{idx / tot:.2%} through finding intersections") - - # Merge into one Vxyz object - if verbose: - print("Merging vectors.") - intersection_points = Vxyz.merge(points_list) - - # Make relative to plane center - if verbose: - print("Rotating.") - intersection_points -= v_plane_center - intersection_points.rotate_in_place(u_plane_norm.align_to(Vxyz((0, 0, 1)))) - - if verbose: - print("Plane intersections caluculated.") - return intersection_points.projXY() - - def plane_intersect( ray_trace: RayTrace, v_plane_center: Vxyz, @@ -621,25 +798,44 @@ def plane_intersect( save_name: str = None, max_ram_in_use_percent: float = 95.0, ): - """Finds all the intersections that occur at a plane from the light paths - in the raytrace. Output points are transformed from the global (i.e. solar field) - reference frame to the local plane reference frame. (3d points are transformed - so the XY plane is perpendicular to the target normal.) - - Parameters: - ----------- - ray_trace (RayTrace): the trace that contains the light paths - v_plane_center (Pxyz): plane center - u_plane_norm (Uxyz): the plane's normal vector - epsilon (float, optional): the threshhold for error when determining if a - ray is parallel to the plane. Defaults to 1e-6. - verbose (bool): to print execution status - - Returns: - -------- - Vxy: intersection points in local plane XY reference frame. """ + Finds all intersections that occur at a specified plane from the light paths in the ray trace. + The output points are transformed from the global reference frame to the local plane reference frame, + where the XY plane is perpendicular to the target normal. + + Parameters + ---------- + ray_trace : RayTrace + The RayTrace object containing the light paths. + v_plane_center : Vxyz + The center point of the plane. + u_plane_norm : Uxyz + The normal vector of the plane. + epsilon : float, optional + The threshold for determining if a ray is parallel to the plane (default is 1e-6). + verbose : bool, optional + If True, prints execution status (default is False). + save_in_file : bool, optional + If True, saves the intersection data to a file (default is False). + save_name : str, optional + The name of the file to save the intersection data (default is None). + max_ram_in_use_percent : float, optional + The maximum percentage of RAM to use during processing (default is 95.0). + + Returns + ------- + Vxy + The intersection points in the local plane XY reference frame. + + Raises + ------ + ValueError + If the input parameters are invalid. + MemoryError + If the maximum RAM usage is exceeded during processing. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Unpack plane intersecting_points = [] @@ -718,27 +914,30 @@ def plane_intersect( def histogram_image(bin_res: float, extent: float, pts: Vxy) -> tuple[np.ndarray, np.ndarray, np.ndarray]: """ - Creates a 2D histogram from scattered points + Creates a 2D histogram from scattered points. + + This function generates a histogram image (point spread function) based on the provided + points in the XY plane, using specified bin resolution and extent. Parameters ---------- bin_res : float - Resolution of image, meters. + The resolution of the histogram bins in meters. extent : float - Width of image area, meters. + The width of the image area in meters. pts : Vxy - Points to calculate XY histogram + A Vxy object containing the points to calculate the XY histogram. Returns ------- - hist : 2D array - Histogram image (PSF). - x : 1d array - X axis, meters. - y : 1d array - Y axis, meters. - + hist : np.ndarray + A 2D array representing the histogram image (point spread function). + x : np.ndarray + A 1D array representing the X axis in meters. + y : np.ndarray + A 1D array representing the Y axis in meters. """ + # "ChatGPT 4o-mini" assisted with generating this docstring. bins = int(extent / bin_res) extent = bin_res * bins rng = [[-extent / 2, extent / 2]] * 2 @@ -750,24 +949,29 @@ def histogram_image(bin_res: float, extent: float, pts: Vxy) -> tuple[np.ndarray def ensquared_energy(pts: Vxy, semi_width_max: float, res: int = 50) -> tuple[np.ndarray, np.ndarray]: - """Calculate ensquared energy as function of square half-width. + """ + Calculate the ensquared energy as a function of square half-width. + + This function computes the fraction of ensquared energy within a square defined by the + semi-widths, based on the provided points. Parameters ---------- - ray_trace : rt.RayTrace - RayTrace object + pts : Vxy + A Vxy object containing the points for which to calculate ensquared energy. semi_width_max : float - Maximum semi_width - res : int - Resolution (number of data points), by defult 50. + The maximum semi-width of the square in meters. + res : int, optional + The resolution (number of data points) for the semi-widths (default is 50). Returns ------- - ndarray - Fraction of ensquared energy - ndarray - Semi-widths, in meters + np.ndarray + An array containing the fraction of ensquared energy for each semi-width. + np.ndarray + An array of semi-widths in meters. """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Calculate widths ws = np.linspace(0, semi_width_max, res) fracs = [] diff --git a/opencsp/common/lib/csp/Scene.py b/opencsp/common/lib/csp/Scene.py index 295246602..c88534bc6 100644 --- a/opencsp/common/lib/csp/Scene.py +++ b/opencsp/common/lib/csp/Scene.py @@ -7,21 +7,83 @@ class Scene(OpticOrientationAbstract): + """ + A class representing a scene containing optical elements and light sources. + + This class manages a collection of objects that can be ray-traced and light sources + that illuminate the scene. It also handles the spatial orientation of these elements. + + Attributes + ---------- + objects : list[RayTraceable] + A list of objects in the scene that can be ray-traced. + light_sources : list[LightSource] + A list of light sources present in the scene. + """ + + # "ChatGPT 4o-mini" assisted with generating this docstring. def __init__(self) -> None: + """ + Initializes a Scene object with empty lists for objects and light sources. + + Parameters + ---------- + None + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. self.objects: list[RayTraceable] = [] self.light_sources: list[LightSource] = [] OpticOrientationAbstract.__init__(self) def add_object(self, new_object: OpticOrientationAbstract) -> None: + """ + Adds a new object to the scene. + + Parameters + ---------- + new_object : OpticOrientationAbstract + The object to be added to the scene. + + Returns + ------- + None + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. self.add_child(new_object) def add_light_source(self, new_light_source: LightSource) -> None: + """ + Adds a new light source to the scene. + + Parameters + ---------- + new_light_source : LightSource + The light source to be added to the scene. + + Returns + ------- + None + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. self.light_sources.append(new_light_source) def set_position_in_space(self, child: OpticOrientationAbstract, transform: TransformXYZ) -> None: """ - Allows for the spacial placement of children inside the scene. + Sets the spatial position of a child object within the scene. + + Parameters + ---------- + child : OpticOrientationAbstract + The child object whose position is to be set. + transform : TransformXYZ + The transformation to apply to the child object. + + Raises + ------ + ValueError + If the child is not a member of the scene. """ + # "ChatGPT 4o-mini" assisted with generating this docstring. if child not in self.children: raise ValueError(f"{child} is not a child of the Scene.") child._self_to_parent_transform = transform @@ -29,6 +91,15 @@ def set_position_in_space(self, child: OpticOrientationAbstract, transform: Tran # override from OpticOrientationAbstract @property def children(self) -> list[OpticOrientationAbstract]: + """ + Retrieves the list of child objects in the scene. + + Returns + ------- + list[OpticOrientationAbstract] + A list of child objects in the scene. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return self.objects # override from OpticOrientationAbstract @@ -39,19 +110,21 @@ def _add_child_helper(self, new_child: OpticOrientationAbstract): def draw_objects(self, view: View3d, render_controls: dict = None): """ - Will draw every `OpticOrientationAbstract` object in the scene. - It determines the render control by taking the type of the object and - looking for that type in the dictionary of render controls. + Draws all objects in the scene using the specified rendering controls. Parameters ---------- - view: View3d - - the view to draw the objects inside + view : View3d + The view in which to draw the objects. + render_controls : dict[type, RenderControl], optional + A dictionary mapping object types to their corresponding render control settings (default is None). - render_controls: dict[type, RenderControl] - - The render control objects that correspond to specific types - - TODO this can be improved + Returns + ------- + None """ + # "ChatGPT 4o-mini" assisted with generating this docstring. + # TODO this can be improved if render_controls is None: render_controls = {} diff --git a/opencsp/common/lib/csp/SolarField.py b/opencsp/common/lib/csp/SolarField.py index bcf464856..8aac81d4e 100644 --- a/opencsp/common/lib/csp/SolarField.py +++ b/opencsp/common/lib/csp/SolarField.py @@ -38,8 +38,13 @@ class SolarField(RayTraceable, OpticOrientationAbstract): """ Representation of a heliostat solar field. + + This class manages a collection of heliostats and their configurations, allowing for + spatial placement and rendering of the solar field. """ + # "ChatGPT 4o-mini" assisted with generating this docstring. + # CONSTRUCTION def __init__( @@ -49,7 +54,21 @@ def __init__( name: str = None, short_name: str = None, ) -> None: - + """ + Initializes a SolarField object with the specified heliostats and location. + + Parameters + ---------- + heliostats : list[HeliostatAbstract] + A list of heliostat objects that make up the solar field. + origin_lon_lat : list[float] | tuple[float, float], optional + The longitude and latitude of the solar field's location (default is None). + name : str, optional + The name of the solar field (default is None). + short_name : str, optional + A short name for the solar field (default is None). + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # assert isinstance(heliostats[0], HeliostatAbstract) self.heliostats = heliostats @@ -73,9 +92,27 @@ def __init__( def set_heliostat_positions(self, positions: list[Pxyz]): """ - Places the heliostats at the points provided. The Pxyzs should appear in the same order - as the heliostats in `self.heliostats`. + Places the heliostats at the specified positions. + + Parameters + ---------- + positions : list[Pxyz] + A list of Pxyz objects representing the positions for each heliostat. + + Returns + ------- + None + + Raises + ------ + ValueError + If the number of positions does not match the number of heliostats. + + Notes + ----- + The Pxyzs should appear in the same order as the heliostats in `self.heliostats`. """ + # "ChatGPT 4o-mini" assisted with generating this docstring. for heliostat, point in zip(self.heliostats, positions): heliostat_position_transform = TransformXYZ.from_V(point) heliostat._self_to_parent_transform = heliostat_position_transform @@ -88,25 +125,26 @@ def from_csv_files( facet_attributes_csv: str, name=None, ): - """returns the solar field that is requested based on the given information - - Paramters - --------- - long_lat: list[float] | tuple[float, float] - the (longitude, latitude) pair that defined the location of - the solar fields real world location - heliostat_attribute_csv: str - filepath to the csv file that contains information about the desired heliostats. - facet_attricute_csv: str - filepath ot the csv file that describes how the facets in the desired heliostats - will be positoned and named. - name: str - optional argument that can be used to define the name of the field + """ + Creates a SolarField object from CSV files containing heliostat and facet attributes. + + Parameters + ---------- + long_lat : list[float] | tuple[float, float] + The (longitude, latitude) pair defining the location of the solar field. + heliostat_attributes_csv : str + The file path to the CSV file containing heliostat attributes. + facet_attributes_csv : str + The file path to the CSV file describing the facets of the heliostats. + name : str, optional + An optional name for the solar field (default is None). Returns ------- - `SolarField` + SolarField + A SolarField object initialized with the specified attributes. """ + # "ChatGPT 4o-mini" assisted with generating this docstring. with open(heliostat_attributes_csv) as h_csv: h_reader = csv.reader(h_csv) h_headers = next(h_reader) @@ -161,6 +199,15 @@ def from_csv_files( @property def children(self): + """ + Retrieves the list of child objects (heliostats) in the solar field. + + Returns + ------- + list[RayTraceable] + A list of heliostat objects in the solar field. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return self.heliostats # override OpticOrientationAbstract @@ -170,8 +217,16 @@ def _add_child_helper(self, new_child: OpticOrientationAbstract): # ACCESS def heliostat_name_list(self) -> list[str]: - """Returns a list of all the names of heliostats in the - solar field. The order is the same as the order the heliostats are stored.""" + """ + Returns a list of all the names of heliostats in the solar field. + + The order is the same as the order the heliostats are stored. + + Returns + ------- + list[str] + A list of heliostat names. + """ name_list = [h.name for h in self.heliostats] return name_list @@ -188,8 +243,25 @@ def heliostat_name_list(self) -> list[str]: # return self._when_ymdhmsz def lookup_heliostat(self, heliostat_name: str) -> HeliostatAbstract: - """Returns the first HeliostatAbstract in the solarfield that matches the given name. - If there are no heliostats that match the given name it throws a KeyError.""" + """ + Returns the first HeliostatAbstract in the solar field that matches the given name. + + Parameters + ---------- + heliostat_name : str + The name of the heliostat to look up. + + Returns + ------- + HeliostatAbstract + The matching heliostat object. + + Raises + ------ + KeyError + If no heliostat with the specified name exists in the solar field. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Check input. matching_heliostats = filter(lambda heliostat: heliostat.name == heliostat_name, self.heliostats) first_matching = next(matching_heliostats, None) @@ -217,8 +289,15 @@ def heliostat_bounding_box_xyz_UNVERIFIED(self): return [xyz_min, xyz_max] def heliostat_bounding_box_xy(self): - """Returns an axis alligned bounding box that only - takes into account the heliostat origins""" + """ + Returns an axis-aligned bounding box that only takes into account the heliostat origins. + + Returns + ------- + list[list[float]] + A list containing the minimum and maximum coordinates of the bounding box in the XY plane. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. heliostat_origins = Pxyz.merge([h._self_to_parent_transform.apply(Pxyz.origin()) for h in self.heliostats]) x_min = min(heliostat_origins.x) x_max = max(heliostat_origins.x) @@ -310,7 +389,23 @@ def draw( solar_field_style: RenderControlSolarField = RenderControlSolarField(), transform: TransformXYZ = None, ) -> None: + """ + Draws the solar field in a 3D view. + + Parameters + ---------- + view : View3d + The 3D view in which to draw the solar field. + solar_field_style : RenderControlSolarField, optional + The style settings for rendering the solar field (default is a new RenderControlSolarField object). + transform : TransformXYZ, optional + A transformation to apply to the solar field when drawing (default is None). + Returns + ------- + None + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. if transform is None: transform = self.self_to_global_tranformation @@ -366,6 +461,20 @@ def draw_figure_UNVERIFIED(self, figure_control, axis_control_m, view_spec, titl return (points, normals) def survey_of_points(self, resolution: Resolution) -> tuple[Pxyz, Vxyz]: + """ + Returns a grid of equispaced points and the normal vectors at those points. + + Parameters + ---------- + resolution : Resolution + The rectangular resolution of the points gathered. + + Returns + ------- + tuple[Pxyz, Vxyz] + A tuple containing the sampled points and their corresponding normal vectors. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Get sample point locations (z=0 plane in "child" reference frame) return self._survey_of_points_helper(resolution, TransformXYZ.identity()) diff --git a/opencsp/common/lib/csp/sun_position.py b/opencsp/common/lib/csp/sun_position.py index 597a72825..c2f6f89dd 100644 --- a/opencsp/common/lib/csp/sun_position.py +++ b/opencsp/common/lib/csp/sun_position.py @@ -30,10 +30,31 @@ def sun_position_aux( refraction=True, ) -> tuple[float, float]: # Boolean. If True, apply refraction correction. """ - John Clark Craig's code. - https://levelup.gitconnected.com/python-sun-position-for-solar-energy-and-research-7a4ead801777 - (slightly adapted) + Calculates the sun's position (azimuth and elevation) based on the given location and time. + + This function computes the sun's apparent location in the sky using the provided + longitude, latitude, and time information. It can also apply a refraction correction. + + Parameters + ---------- + location_lon_lat : tuple[float, float] + A tuple containing the longitude and latitude in radians. + when_ymdhmsz : tuple[float, float, float, float, float, float, float] + A tuple containing the year, month, day, hour, minute, second, and timezone. + refraction : bool, optional + If True, applies refraction correction to the elevation angle (default is True). + + Returns + ------- + tuple[float, float] + A tuple containing the azimuth and elevation angles in degrees. + + Notes + ----- + The algorithm is based on John Clark Craig's implementation for calculating sun position in + https://levelup.gitconnected.com/python-sun-position-for-solar-energy-and-research-7a4ead801777. """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Extract the passed data year, month, day, hour, minute, second, timezone = when_ymdhmsz # Math typing shortcuts @@ -81,9 +102,30 @@ def sun_position_aux( def into_range(x, range_min, range_max): """ - John Clark Craig's code. - https://levelup.gitconnected.com/python-sun-position-for-solar-energy-and-research-7a4ead801777 + Adjusts a value to be within a specified range. + + This function takes a value and wraps it within the specified minimum and maximum range. + + Parameters + ---------- + x : float + The value to adjust. + range_min : float + The minimum value of the range. + range_max : float + The maximum value of the range. + + Returns + ------- + float + The adjusted value wrapped within the specified range. + + Notes + ----- + The algorithm is based on John Clark Craig's implementation for calculating sun position in + https://levelup.gitconnected.com/python-sun-position-for-solar-energy-and-research-7a4ead801777. """ + # "ChatGPT 4o-mini" assisted with generating this docstring. shiftedx = x - range_min delta = range_max - range_min return (((shiftedx % delta) + delta) % delta) + range_min @@ -92,6 +134,28 @@ def into_range(x, range_min, range_max): def sun_position( location_lon_lat: tuple[float, float], when_ymdhmsz: tuple # radians. (longitude, lattiude) pair. ) -> np.ndarray: # (year, month, day, hour, minute, second, timezone) tuple. + """ + Calculates the sun's apparent location in the sky based on the given location and time. + + This function retrieves the sun's azimuth and elevation angles and converts them into a unit vector. + + Parameters + ---------- + location_lon_lat : tuple[float, float] + A tuple containing the longitude and latitude in radians. + when_ymdhmsz : tuple[float, float, float, float, float, float, float] + A tuple containing the year, month, day, hour, minute, second, and timezone. + + Returns + ------- + np.ndarray + A unit vector representing the direction of the sun in 3D space. + + Notes + ----- + The azimuth and elevation angles are calculated using the `sun_position_aux` function. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Example: (2022, 7, 4, 11, 20, 0, -6) => July 4, 2022 at 11:20 am MDT (-6 hours) # Get the Sun's apparent location in the sky azimuth_deg, elevation_deg = sun_position_aux(location_lon_lat, when_ymdhmsz, True) # John Clark Craig's version. diff --git a/opencsp/common/lib/geometry/TransformXYZ.py b/opencsp/common/lib/geometry/TransformXYZ.py index 3b1a88bab..df0546b8c 100644 --- a/opencsp/common/lib/geometry/TransformXYZ.py +++ b/opencsp/common/lib/geometry/TransformXYZ.py @@ -5,16 +5,43 @@ class TransformXYZ: + """ + Representation of a 3D homogeneous spatial transform. + + A TransformXYZ object encapsulates a 4x4 homogeneous transformation matrix, + which includes both rotation and translation components. This class provides methods + for creating transformations, applying them to vectors, and obtaining their inverse. + + Properties + ---------- + matrix : np.ndarray + The 4x4 matrix representation of the transformation. + R : Rotation + The rotation component of the transformation. + R_matrix : np.ndarray + The 3x3 rotation matrix. + V : Vxyz + The translation component of the transformation. + V_matrix : np.ndarray + The translation vector as a length 3 array. + """ + + # "ChatGPT 4o-mini" assisted with generating this docstring. def __init__(self, matrix: np.ndarray): """ - Representation of a 3D homogeneous spatial transform. + Initializes a TransformXYZ object with the given transformation matrix. Parameters ---------- matrix : np.ndarray - 4x4 homogeneous 3D transorm matrix. + A 4x4 homogeneous transformation matrix. + Raises + ------ + ValueError + If the input matrix does not have shape (4, 4). """ + # "ChatGPT 4o-mini" assisted with generating this docstring. # Check 4x4 shape if matrix.shape != (4, 4): raise ValueError('Input matrix must have shape 4x4.') diff --git a/opencsp/common/lib/render_control/RenderControlMirror.py b/opencsp/common/lib/render_control/RenderControlMirror.py index db7725246..bfa34ab03 100644 --- a/opencsp/common/lib/render_control/RenderControlMirror.py +++ b/opencsp/common/lib/render_control/RenderControlMirror.py @@ -5,6 +5,14 @@ class RenderControlMirror: + """ + A class for controlling the rendering of a mirror in a graphical environment. + + This class allows for the configuration of various visual aspects of a mirror, + including its centroid, surface normals, resolution, and styles for rendering. + """ + + # "ChatGPT 4o-mini" assisted with generating this docstring. def __init__( self, centroid: bool = False, @@ -17,6 +25,31 @@ def __init__( point_styles: RenderControlPointSeq = None, number_of_edge_points: int = 20, ) -> None: + """ + Initializes a RenderControlMirror object with the specified parameters. + + Parameters + ---------- + centroid : bool, optional + If True, renders the centroid of the mirror (default is False). + surface_normals : bool, optional + If True, renders the surface normals of the mirror (default is False). + resolution : Resolution, optional + The resolution of the rendering, specified as a Resolution object (default is None). + norm_len : float, optional + The length of the normals to be rendered (default is 1). + norm_res : int, optional + The resolution of the normals (default is 5). + norm_base_style : RenderControlPointSeq, optional + The style for rendering the normals (default is a marker with size 2). + surface_style : RenderControlSurface, optional + The style for rendering the surface of the mirror (default is a new RenderControlSurface object). + point_styles : RenderControlPointSeq, optional + The styles for rendering points on the mirror (default is None, which sets it to a marker with size 2). + number_of_edge_points : int, optional + The number of edge points to be rendered (default is 20). + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. if point_styles == None: self.point_styles = rcps.marker(markersize=2) if resolution is None: @@ -36,8 +69,26 @@ def __init__( def normal_mirror(): + """ + Creates a default RenderControlMirror object with standard settings. + + Returns + ------- + RenderControlMirror + A RenderControlMirror object initialized with default parameters. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return RenderControlMirror() def low_res_mirror(): + """ + Creates a RenderControlMirror object with low resolution. + + Returns + ------- + RenderControlMirror + A RenderControlMirror object initialized with a resolution of 5 pixels in the x direction. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return RenderControlMirror(resolution=Resolution.pixelX(5)) diff --git a/opencsp/common/lib/render_control/RenderControlRayTrace.py b/opencsp/common/lib/render_control/RenderControlRayTrace.py index 4e501bd62..183b03e25 100644 --- a/opencsp/common/lib/render_control/RenderControlRayTrace.py +++ b/opencsp/common/lib/render_control/RenderControlRayTrace.py @@ -2,6 +2,18 @@ class RenderControlRayTrace: + """ + A class for controlling the rendering of ray traces in a graphical environment. + + This class manages the rendering settings for light paths during ray tracing. + + Parameters + ---------- + light_path_control : RenderControlLightPath, optional + The control settings for rendering light paths (default is the default light path control). + """ + + # "ChatGPT 4o-mini" assisted with generating this docstring. def __init__(self, light_path_control=rclp.default_path()) -> None: self.light_path_control = light_path_control @@ -10,4 +22,23 @@ def __init__(self, light_path_control=rclp.default_path()) -> None: def init_current_lengths(init_len=1, current_len=1): + """ + Initializes a RenderControlRayTrace object with specified initial and current lengths for light paths. + + This function creates a RenderControlRayTrace object with a RenderControlLightPath configured + with the provided initial and current lengths. + + Parameters + ---------- + init_len : float, optional + The initial length of the light paths (default is 1). + current_len : float, optional + The current length of the light paths (default is 1). + + Returns + ------- + RenderControlRayTrace + A RenderControlRayTrace object initialized with the specified lengths. + """ + # "ChatGPT 4o-mini" assisted with generating this docstring. return RenderControlRayTrace(rclp.RenderControlLightPath(init_length=init_len, current_length=current_len)) diff --git a/opencsp/test/test_DocStringsExist.py b/opencsp/test/test_DocStringsExist.py index 84fc20591..66a7cea1f 100644 --- a/opencsp/test/test_DocStringsExist.py +++ b/opencsp/test/test_DocStringsExist.py @@ -426,7 +426,7 @@ class test_Docstrings(unittest.TestCase): common_class_list = ( cv_class_list + camera_class_list - # + csp_class_list + + csp_class_list # + cv_class_list # + deflectometry_class_list # + file_class_list