diff --git a/contrib/test_data_generation/sofast_fringe/generate_test_data_single_facet.py b/contrib/test_data_generation/sofast_fringe/generate_test_data_single_facet.py index ccc049e74..f92c41a90 100644 --- a/contrib/test_data_generation/sofast_fringe/generate_test_data_single_facet.py +++ b/contrib/test_data_generation/sofast_fringe/generate_test_data_single_facet.py @@ -8,12 +8,15 @@ import matplotlib.pyplot as plt import numpy as np -from opencsp.app.sofast.lib.ImageCalibrationScaling import ImageCalibrationScaling -from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe as Measurement -from opencsp.app.sofast.lib.ProcessSofastFringe import ProcessSofastFringe as Sofast from opencsp.app.sofast.lib.DisplayShape import DisplayShape as Display from opencsp.app.sofast.lib.DefinitionFacet import DefinitionFacet +from opencsp.app.sofast.lib.ImageCalibrationScaling import ImageCalibrationScaling +from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe as Measurement +from opencsp.app.sofast.lib.ProcessSofastFringe import ProcessSofastFringe +from opencsp.app.sofast.lib.SpatialOrientation import SpatialOrientation from opencsp.common.lib.camera.Camera import Camera +from opencsp.common.lib.deflectometry.Surface2DParabolic import Surface2DParabolic +from opencsp.common.lib.deflectometry.Surface2DPlano import Surface2DPlano from opencsp.common.lib.opencsp_path.opencsp_root_path import opencsp_code_dir @@ -22,6 +25,7 @@ def generate_dataset( file_camera: str, file_display: str, file_calibration: str, + file_orientation: str, file_facet: str, surface_type: Literal['parabolic', 'plano'], robust_ls: bool, @@ -37,27 +41,25 @@ def generate_dataset( display = Display.load_from_hdf(file_display) measurement = Measurement.load_from_hdf(file_measurement) calibration = ImageCalibrationScaling.load_from_hdf(file_calibration) + orientation = SpatialOrientation.load_from_hdf(file_orientation) facet_data = DefinitionFacet.load_from_json(file_facet) # Calibrate fringes measurement.calibrate_fringe_images(calibration) # Creates sofast object - sofast = Sofast(measurement, camera, display) + sofast = ProcessSofastFringe(measurement, orientation, camera, display) # Update mask calculation options - sofast.params.mask_keep_largest_area = True + sofast.params.mask.keep_largest_area = True # Define surface data if surface_type == 'parabolic': - surface_data = dict( - surface_type=surface_type, - initial_focal_lengths_xy=(100.0, 100.0), - robust_least_squares=robust_ls, - downsample=10, + surface_data = Surface2DParabolic( + initial_focal_lengths_xy=(100.0, 100.0), robust_least_squares=robust_ls, downsample=10 ) elif surface_type == 'plano': - surface_data = dict(surface_type=surface_type, robust_least_squares=robust_ls, downsample=10) + surface_data = Surface2DPlano(robust_least_squares=robust_ls, downsample=10) # Process optic data sofast.process_optic_singlefacet(facet_data, surface_data) @@ -66,6 +68,7 @@ def generate_dataset( sofast.save_to_hdf(file_dataset_out) display.save_to_hdf(file_dataset_out) camera.save_to_hdf(file_dataset_out) + orientation.save_to_hdf(file_dataset_out) calibration.save_to_hdf(file_dataset_out) print(f'All data saved to: {file_dataset_out:s}') @@ -82,64 +85,69 @@ def generate_dataset( if __name__ == '__main__': # Generate measurement set 1 data - base_dir = join(opencsp_code_dir(), 'test/data/measurements_sofast_fringe') + base_dir = join(opencsp_code_dir(), 'test/data') # Nominal generate_dataset( - file_measurement=join(base_dir, 'measurement_facet.h5'), - file_camera=join(base_dir, 'camera.h5'), - file_display=join(base_dir, 'display_distorted_2d.h5'), - file_calibration=join(base_dir, 'image_calibration.h5'), - file_facet=join(base_dir, 'Facet_NSTTF.json'), + file_measurement=join(base_dir, 'sofast_fringe/data_measurement/measurement_facet.h5'), + file_camera=join(base_dir, 'sofast_common/camera_sofast_downsampled.h5'), + file_display=join(base_dir, 'sofast_common/display_distorted_2d.h5'), + file_calibration=join(base_dir, 'sofast_fringe/data_measurement/image_calibration.h5'), + file_orientation=join(base_dir, 'sofast_common/spatial_orientation.h5'), + file_facet=join(base_dir, 'sofast_common/Facet_NSTTF.json'), surface_type='parabolic', robust_ls=True, - file_dataset_out=join(base_dir, 'calculations_facet/data.h5'), + file_dataset_out=join(base_dir, 'sofast_fringe/data_expected_facet/data.h5'), ) # Rectangular display generate_dataset( - file_measurement=join(base_dir, 'measurement_facet.h5'), - file_camera=join(base_dir, 'camera.h5'), - file_display=join(base_dir, 'display_rectangular.h5'), - file_calibration=join(base_dir, 'image_calibration.h5'), - file_facet=join(base_dir, 'Facet_NSTTF.json'), + file_measurement=join(base_dir, 'sofast_fringe/data_measurement/measurement_facet.h5'), + file_camera=join(base_dir, 'sofast_common/camera_sofast_downsampled.h5'), + file_display=join(base_dir, 'sofast_common/display_rectangular.h5'), + file_calibration=join(base_dir, 'sofast_fringe/data_measurement/image_calibration.h5'), + file_orientation=join(base_dir, 'sofast_common/spatial_orientation.h5'), + file_facet=join(base_dir, 'sofast_common/Facet_NSTTF.json'), surface_type='parabolic', robust_ls=True, - file_dataset_out=join(base_dir, 'calculations_facet/data_rectangular.h5'), + file_dataset_out=join(base_dir, 'sofast_fringe/data_expected_facet/data_rectangular.h5'), ) # 3D distorted display generate_dataset( - file_measurement=join(base_dir, 'measurement_facet.h5'), - file_camera=join(base_dir, 'camera.h5'), - file_display=join(base_dir, 'display_distorted_3d.h5'), - file_calibration=join(base_dir, 'image_calibration.h5'), - file_facet=join(base_dir, 'Facet_NSTTF.json'), + file_measurement=join(base_dir, 'sofast_fringe/data_measurement/measurement_facet.h5'), + file_camera=join(base_dir, 'sofast_common/camera_sofast_downsampled.h5'), + file_display=join(base_dir, 'sofast_common/display_distorted_3d.h5'), + file_calibration=join(base_dir, 'sofast_fringe/data_measurement/image_calibration.h5'), + file_orientation=join(base_dir, 'sofast_common/spatial_orientation.h5'), + file_facet=join(base_dir, 'sofast_common/Facet_NSTTF.json'), surface_type='parabolic', robust_ls=True, - file_dataset_out=join(base_dir, 'calculations_facet/data_distorted_3d.h5'), + file_dataset_out=join(base_dir, 'sofast_fringe/data_expected_facet/data_distorted_3d.h5'), ) # No robust least squares generate_dataset( - file_measurement=join(base_dir, 'measurement_facet.h5'), - file_camera=join(base_dir, 'camera.h5'), - file_display=join(base_dir, 'display_distorted_2d.h5'), - file_calibration=join(base_dir, 'image_calibration.h5'), - file_facet=join(base_dir, 'Facet_NSTTF.json'), + file_measurement=join(base_dir, 'sofast_fringe/data_measurement/measurement_facet.h5'), + file_camera=join(base_dir, 'sofast_common/camera_sofast_downsampled.h5'), + file_display=join(base_dir, 'sofast_common/display_distorted_2d.h5'), + file_calibration=join(base_dir, 'sofast_fringe/data_measurement/image_calibration.h5'), + file_orientation=join(base_dir, 'sofast_common/spatial_orientation.h5'), + file_facet=join(base_dir, 'sofast_common/Facet_NSTTF.json'), surface_type='parabolic', robust_ls=False, - file_dataset_out=join(base_dir, 'calculations_facet/data_no_ls.h5'), + file_dataset_out=join(base_dir, 'sofast_fringe/data_expected_facet/data_no_ls.h5'), ) # Plano optic generate_dataset( - file_measurement=join(base_dir, 'measurement_facet.h5'), - file_camera=join(base_dir, 'camera.h5'), - file_display=join(base_dir, 'display_distorted_2d.h5'), - file_calibration=join(base_dir, 'image_calibration.h5'), - file_facet=join(base_dir, 'Facet_NSTTF.json'), + file_measurement=join(base_dir, 'sofast_fringe/data_measurement/measurement_facet.h5'), + file_camera=join(base_dir, 'sofast_common/camera_sofast_downsampled.h5'), + file_display=join(base_dir, 'sofast_common/display_distorted_2d.h5'), + file_calibration=join(base_dir, 'sofast_fringe/data_measurement/image_calibration.h5'), + file_orientation=join(base_dir, 'sofast_common/spatial_orientation.h5'), + file_facet=join(base_dir, 'sofast_common/Facet_NSTTF.json'), surface_type='plano', robust_ls=True, - file_dataset_out=join(base_dir, 'calculations_facet/data_plano.h5'), + file_dataset_out=join(base_dir, 'sofast_fringe/data_expected_facet/data_plano.h5'), ) diff --git a/opencsp/app/sofast/lib/ParamsMaskCalculation.py b/opencsp/app/sofast/lib/ParamsMaskCalculation.py new file mode 100644 index 000000000..9155ec9e3 --- /dev/null +++ b/opencsp/app/sofast/lib/ParamsMaskCalculation.py @@ -0,0 +1,68 @@ +from dataclasses import dataclass + +from opencsp.common.lib.tool import hdf5_tools + + +@dataclass +class ParamsMaskCalculation(hdf5_tools.HDF5_IO_Abstract): + """Parameters for calculating mask""" + + hist_thresh: float = 0.5 + """Defines threshold to use when calculating optic mask. Uses a histogram of pixel values + of the mask difference image (light image - dark image). This is the fraction of the way + from the first histogram peak (most common dark pixel value) to the the last histogram peak + (most common light pixel value).""" + filt_width: int = 9 + """Side length of square kernel used to filter mask image""" + filt_thresh: int = 4 + """Threshold (minimum number of active pixels) to use when removing small active mask areas.""" + thresh_active_pixels: float = 0.05 + """If number of active mask pixels is below this fraction of total image pixels, thow error.""" + keep_largest_area: bool = False + """Flag to apply processing step that keeps only the largest mask area""" + + def save_to_hdf(self, file: str, prefix: str = ''): + """Saves data to given HDF5 file. Data is stored in PREFIX + ParamsMaskCalculation/... + + Parameters + ---------- + file : str + HDF file to save to + prefix : str + Prefix to append to folder path within HDF file (folders must be separated by "/") + """ + data = [self.hist_thresh, self.filt_width, self.filt_thresh, self.thresh_active_pixels, self.keep_largest_area] + datasets = [ + prefix + 'ParamsMaskCalculation/hist_thresh', + prefix + 'ParamsMaskCalculation/filt_width', + prefix + 'ParamsMaskCalculation/filt_thresh', + prefix + 'ParamsMaskCalculation/thresh_active_pixels', + prefix + 'ParamsMaskCalculation/keep_largest_area', + ] + hdf5_tools.save_hdf5_datasets(data, datasets, file) + + @classmethod + def load_from_hdf(cls, file: str, prefix: str = ''): + """Loads data from given file. Assumes data is stored as: PREFIX + ParamsMaskCalculation/Field_1 + + Parameters + ---------- + file : str + HDF file to load from + prefix : str, optional + Prefix to append to folder path within HDF file (folders must be separated by "/"). + Default is empty string ''. + """ + + # Load sofast parameters + datasets = [ + prefix + 'ParamsMaskCalculation/hist_thresh', + prefix + 'ParamsMaskCalculation/filt_width', + prefix + 'ParamsMaskCalculation/filt_thresh', + prefix + 'ParamsMaskCalculation/thresh_active_pixels', + prefix + 'ParamsMaskCalculation/keep_largest_area', + ] + data = hdf5_tools.load_hdf5_datasets(datasets, file) + data['keep_largest_area'] = bool(data['keep_largest_area']) + + return cls(**data) diff --git a/opencsp/app/sofast/lib/ParamsOpticGeometry.py b/opencsp/app/sofast/lib/ParamsOpticGeometry.py index 2553e79ac..8e48ec041 100644 --- a/opencsp/app/sofast/lib/ParamsOpticGeometry.py +++ b/opencsp/app/sofast/lib/ParamsOpticGeometry.py @@ -1,10 +1,10 @@ from dataclasses import dataclass -import opencsp.common.lib.tool.hdf5_tools as hdf5_tools +from opencsp.common.lib.tool import hdf5_tools @dataclass -class ParamsOpticGeometry: +class ParamsOpticGeometry(hdf5_tools.HDF5_IO_Abstract): """Parameter dataclass for processing optic geometry""" perimeter_refine_axial_search_dist: float = 50.0 @@ -38,3 +38,25 @@ def save_to_hdf(self, file: str, prefix: str = ''): prefix + 'ParamsOpticGeometry/facet_corns_refine_frac_keep', ] hdf5_tools.save_hdf5_datasets(data, datasets, file) + + @classmethod + def load_from_hdf(cls, file: str, prefix: str = ''): + """Loads data from given file. Assumes data is stored as: PREFIX + ParamsOpticGeometry/Field_1 + + Parameters + ---------- + file : str + HDF file to load from + prefix : str, optional + Prefix to append to folder path within HDF file (folders must be separated by "/"). + Default is empty string ''. + """ + datasets = [ + prefix + 'ParamsOpticGeometry/perimeter_refine_axial_search_dist', + prefix + 'ParamsOpticGeometry/perimeter_refine_perpendicular_search_dist', + prefix + 'ParamsOpticGeometry/facet_corns_refine_step_length', + prefix + 'ParamsOpticGeometry/facet_corns_refine_perpendicular_search_dist', + prefix + 'ParamsOpticGeometry/facet_corns_refine_frac_keep', + ] + data = hdf5_tools.load_hdf5_datasets(datasets, file) + return cls(**data) diff --git a/opencsp/app/sofast/lib/ParamsSofastFringe.py b/opencsp/app/sofast/lib/ParamsSofastFringe.py index dcf82c375..fe06d7a73 100644 --- a/opencsp/app/sofast/lib/ParamsSofastFringe.py +++ b/opencsp/app/sofast/lib/ParamsSofastFringe.py @@ -1,29 +1,29 @@ -"""Parameter dataclass for SofastFringe -""" +"""Parameter dataclass for SofastFringe""" from dataclasses import dataclass, field - -from opencsp.app.sofast.lib.ParamsOpticGeometry import ParamsOpticGeometry from opencsp.app.sofast.lib.DebugOpticsGeometry import DebugOpticsGeometry +from opencsp.app.sofast.lib.ParamsMaskCalculation import ParamsMaskCalculation +from opencsp.app.sofast.lib.ParamsOpticGeometry import ParamsOpticGeometry from opencsp.common.lib.deflectometry.SlopeSolverDataDebug import SlopeSolverDataDebug -import opencsp.common.lib.tool.hdf5_tools as hdf5_tools +from opencsp.common.lib.tool import hdf5_tools @dataclass -class ParamsSofastFringe: +class ParamsSofastFringe(hdf5_tools.HDF5_IO_Abstract): """Parameters for SofastFringe processing calculation""" - mask_hist_thresh: float = 0.5 - mask_filt_width: int = 9 - mask_filt_thresh: int = 4 - mask_thresh_active_pixels: float = 0.05 - mask_keep_largest_area: bool = False - geometry_params: ParamsOpticGeometry = field(default_factory=ParamsOpticGeometry) + # Parameters + mask: ParamsMaskCalculation = field(default_factory=ParamsMaskCalculation) + """Parameters for calculating optic mask""" + geometry: ParamsOpticGeometry = field(default_factory=ParamsOpticGeometry) + """Parameters for calculating optic geometry""" # Debug objects - slope_solver_data_debug: SlopeSolverDataDebug = field(default_factory=SlopeSolverDataDebug) - geometry_data_debug: DebugOpticsGeometry = field(default_factory=DebugOpticsGeometry) + debug_slope_solver: SlopeSolverDataDebug = field(default_factory=SlopeSolverDataDebug) + """Debug options for slope solving""" + debug_geometry: DebugOpticsGeometry = field(default_factory=DebugOpticsGeometry) + """Debug options for geometry processing""" def save_to_hdf(self, file: str, prefix: str = ''): """Saves data to given HDF5 file. Data is stored in PREFIX + ParamsSofastFringe/... @@ -35,20 +35,26 @@ def save_to_hdf(self, file: str, prefix: str = ''): prefix : str Prefix to append to folder path within HDF file (folders must be separated by "/") """ - data = [ - self.mask_hist_thresh, - self.mask_filt_width, - self.mask_filt_thresh, - self.mask_thresh_active_pixels, - self.mask_keep_largest_area, - ] - datasets = [ - prefix + 'ParamsSofastFringe/mask_hist_thresh', - prefix + 'ParamsSofastFringe/mask_filt_width', - prefix + 'ParamsSofastFringe/mask_filt_thresh', - prefix + 'ParamsSofastFringe/mask_thresh_active_pixels', - prefix + 'ParamsSofastFringe/mask_keep_largest_area', - ] - hdf5_tools.save_hdf5_datasets(data, datasets, file) - - self.geometry_params.save_to_hdf(file, prefix + 'ParamsSofastFringe/') + self.mask.save_to_hdf(file, prefix + 'ParamsSofastFringe/') + self.geometry.save_to_hdf(file, prefix + 'ParamsSofastFringe/') + + @classmethod + def load_from_hdf(cls, file: str, prefix: str = ''): + """Loads data from given file. Assumes data is stored as: PREFIX + ParamsSofastFringe/Field_1 + + Parameters + ---------- + file : str + HDF file to load from + prefix : str, optional + Prefix to append to folder path within HDF file (folders must be separated by "/"). + Default is empty string ''. + """ + # Load geometry parameters + params_mask = ParamsMaskCalculation.load_from_hdf(file, prefix + '/ParamsSofastFringe/') + params_geometry = ParamsOpticGeometry.load_from_hdf(file, prefix + '/ParamsSofastFringe/') + + # Load sofast parameters + data = {'geometry': params_geometry, 'mask': params_mask} + + return cls(**data) diff --git a/opencsp/app/sofast/lib/ProcessSofastFringe.py b/opencsp/app/sofast/lib/ProcessSofastFringe.py index 353020fe5..b56f49d54 100644 --- a/opencsp/app/sofast/lib/ProcessSofastFringe.py +++ b/opencsp/app/sofast/lib/ProcessSofastFringe.py @@ -293,10 +293,10 @@ def _process_optic_undefined_geometry(self) -> None: # Calculate raw mask params = [ - self.params.mask_hist_thresh, - self.params.mask_filt_width, - self.params.mask_filt_thresh, - self.params.mask_thresh_active_pixels, + self.params.mask.hist_thresh, + self.params.mask.filt_width, + self.params.mask.filt_thresh, + self.params.mask.thresh_active_pixels, ] mask_raw = ip.calc_mask_raw(self.measurement.mask_images, *params) @@ -309,11 +309,11 @@ def _process_optic_undefined_geometry(self) -> None: self.data_error, ) = po.process_undefined_geometry( mask_raw, - self.params.mask_keep_largest_area, + self.params.mask.keep_largest_area, self.measurement.dist_optic_screen, self.orientation, self.camera, - self.params.geometry_data_debug, + self.params.debug_geometry, ) # Save data @@ -334,22 +334,22 @@ def _process_optic_singlefacet_geometry(self, facet_data: DefinitionFacet) -> No self.num_facets = 1 self.optic_type = 'single' - if self.params.geometry_data_debug.debug_active: + if self.params.debug_geometry.debug_active: lt.info('Sofast image processing debug on.') - if self.params.slope_solver_data_debug.debug_active: + if self.params.debug_slope_solver.debug_active: lt.info('SlopeSolver debug on.') # Calculate raw mask params = [ - self.params.mask_hist_thresh, - self.params.mask_filt_width, - self.params.mask_filt_thresh, - self.params.mask_thresh_active_pixels, + self.params.mask.hist_thresh, + self.params.mask.filt_width, + self.params.mask.filt_thresh, + self.params.mask.thresh_active_pixels, ] mask_raw = ip.calc_mask_raw(self.measurement.mask_images, *params) # If enabled, keep only the largest mask area - if self.params.mask_keep_largest_area: + if self.params.mask.keep_largest_area: mask_raw2 = ip.keep_largest_mask_area(mask_raw) mask_raw = np.logical_and(mask_raw, mask_raw2) @@ -366,8 +366,8 @@ def _process_optic_singlefacet_geometry(self, facet_data: DefinitionFacet) -> No self.measurement.dist_optic_screen, self.orientation, self.camera, - self.params.geometry_params, - self.params.geometry_data_debug, + self.params.geometry, + self.params.debug_geometry, ) # Save data @@ -400,19 +400,19 @@ def _process_optic_multifacet_geometry( # Calculate mask params = [ - self.params.mask_hist_thresh, - self.params.mask_filt_width, - self.params.mask_filt_thresh, - self.params.mask_thresh_active_pixels, + self.params.mask.hist_thresh, + self.params.mask.filt_width, + self.params.mask.filt_thresh, + self.params.mask.thresh_active_pixels, ] mask_raw = ip.calc_mask_raw(self.measurement.mask_images, *params) - if self.params.mask_keep_largest_area: + if self.params.mask.keep_largest_area: lt.warn( '"keep_largest_area" mask processing option cannot be used ' 'for multifacet ensembles. This will be turned off.' ) - self.params.mask_keep_largest_area = False + self.params.mask.keep_largest_area = False ( self.data_geometry_general, @@ -428,8 +428,8 @@ def _process_optic_multifacet_geometry( self.orientation, self.camera, self.measurement.dist_optic_screen, - self.params.geometry_params, - self.params.geometry_data_debug, + self.params.geometry, + self.params.debug_geometry, ) # Initialize data dictionaries @@ -518,8 +518,8 @@ def _solve_slopes(self, surfaces: list[Surface2DAbstract]) -> None: self.data_characterization_facet = [] for facet_idx in range(self.num_facets): # Check debug status - if self.params.slope_solver_data_debug.debug_active: - self.params.slope_solver_data_debug.optic_data = self.data_facet_def[facet_idx] + if self.params.debug_slope_solver.debug_active: + self.params.debug_slope_solver.optic_data = self.data_facet_def[facet_idx] # Instantiate slope solver object kwargs = { @@ -531,7 +531,7 @@ def _solve_slopes(self, surfaces: list[Surface2DAbstract]) -> None: 'v_align_point_optic': self.data_geometry_facet[facet_idx].v_align_point_facet, 'dist_optic_screen': self.data_geometry_facet[facet_idx].measure_point_screen_distance, 'surface': surfaces[facet_idx], - 'debug': self.params.slope_solver_data_debug, + 'debug': self.params.debug_slope_solver, } # Instantiate slope solver diff --git a/opencsp/app/sofast/lib/calculation_data_classes.py b/opencsp/app/sofast/lib/calculation_data_classes.py index 52c5a5388..2fb457b03 100644 --- a/opencsp/app/sofast/lib/calculation_data_classes.py +++ b/opencsp/app/sofast/lib/calculation_data_classes.py @@ -12,11 +12,11 @@ from opencsp.common.lib.geometry.Vxy import Vxy from opencsp.common.lib.geometry.Vxyz import Vxyz from opencsp.app.sofast.lib.SpatialOrientation import SpatialOrientation -import opencsp.common.lib.tool.hdf5_tools as hdf5_tools +from opencsp.common.lib.tool import hdf5_tools @dataclass -class CalculationDataGeometryGeneral: +class CalculationDataGeometryGeneral(hdf5_tools.HDF5_SaveAbstract): """Data class used in deflectometry measurments. Saves general geometry and orientation data associated with a measurement.""" @@ -63,7 +63,7 @@ def save_to_hdf(self, file: str, prefix: str = ''): @dataclass -class CalculationDataGeometryFacet: +class CalculationDataGeometryFacet(hdf5_tools.HDF5_SaveAbstract): """Data class used in deflectometry calculations of a single facet. Holds geometric/orientation data of a single facet measurement setup. """ @@ -110,7 +110,7 @@ def save_to_hdf(self, file: str, prefix: str = ''): @dataclass -class CalculationError: +class CalculationError(hdf5_tools.HDF5_SaveAbstract): """Data class used in deflectometry calculations. Holds data on measurement/calculation errors during deflectometry calculations. """ @@ -152,7 +152,7 @@ def save_to_hdf(self, file: str, prefix: str = ''): @dataclass -class CalculationImageProcessingFacet: +class CalculationImageProcessingFacet(hdf5_tools.HDF5_SaveAbstract): """Data class used in deflectometry calculations of a single facet. Holds image processing data of a single facet measurement. """ @@ -194,7 +194,7 @@ def save_to_hdf(self, file: str, prefix: str = ''): @dataclass -class CalculationImageProcessingGeneral: +class CalculationImageProcessingGeneral(hdf5_tools.HDF5_SaveAbstract): """Data class used in deflectometry calculations. Holds general image processing calculations from a deflectometry measurement. """ @@ -233,7 +233,7 @@ def save_to_hdf(self, file: str, prefix: str = ''): @dataclass -class CalculationFacetEnsemble: +class CalculationFacetEnsemble(hdf5_tools.HDF5_SaveAbstract): """Data class used in deflectometry calculations. Holds calculations relating to facet ensembles. """ diff --git a/opencsp/app/sofast/test/test_ParamsOpticGeometry.py b/opencsp/app/sofast/test/test_ParamsOpticGeometry.py new file mode 100644 index 000000000..16b893eb3 --- /dev/null +++ b/opencsp/app/sofast/test/test_ParamsOpticGeometry.py @@ -0,0 +1,26 @@ +import unittest +from os.path import join, dirname + +from opencsp.app.sofast.lib.ParamsOpticGeometry import ParamsOpticGeometry +import opencsp.common.lib.tool.file_tools as ft + + +class TestParamsOpticGeometry(unittest.TestCase): + def test_save_load_hdf(self): + # Define save dir + dir_save = join(dirname(__file__), 'data/output/ParamsOpticGeometry') + ft.create_directories_if_necessary(dir_save) + file_save = join(dir_save, 'params_optic_geometry.h5') + + # Instantiate with defaults + params = ParamsOpticGeometry() + + # Test save + params.save_to_hdf(file_save) + + # Test load + ParamsOpticGeometry.load_from_hdf(file_save) + + +if __name__ == '__main__': + unittest.main() diff --git a/opencsp/app/sofast/test/test_ParamsSofastFringe.py b/opencsp/app/sofast/test/test_ParamsSofastFringe.py new file mode 100644 index 000000000..425519996 --- /dev/null +++ b/opencsp/app/sofast/test/test_ParamsSofastFringe.py @@ -0,0 +1,26 @@ +import unittest +from os.path import join, dirname + +from opencsp.app.sofast.lib.ParamsSofastFringe import ParamsSofastFringe +import opencsp.common.lib.tool.file_tools as ft + + +class TestParamsSofastFringe(unittest.TestCase): + def test_save_load_hdf(self): + # Define save dir + dir_save = join(dirname(__file__), 'data/output/ParamsSofastFringe') + ft.create_directories_if_necessary(dir_save) + file_save = join(dir_save, 'params_sofast_fringe.h5') + + # Instantiate with defaults + params = ParamsSofastFringe() + + # Test save + params.save_to_hdf(file_save) + + # Test load + ParamsSofastFringe.load_from_hdf(file_save) + + +if __name__ == '__main__': + unittest.main() diff --git a/opencsp/app/sofast/test/test_SpatialOrientation.py b/opencsp/app/sofast/test/test_SpatialOrientation.py index ffe7255c6..6db9ad4ee 100644 --- a/opencsp/app/sofast/test/test_SpatialOrientation.py +++ b/opencsp/app/sofast/test/test_SpatialOrientation.py @@ -24,8 +24,8 @@ def setUpClass(cls): # Load data datasets = [ - 'DataSofastCalculation/geometry/general/r_optic_cam_refine_1', - 'DataSofastCalculation/geometry/general/v_cam_optic_cam_refine_2', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/r_optic_cam_refine_1', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/v_cam_optic_cam_refine_2', ] ori = SpatialOrientation.load_from_hdf(data_file_facet) diff --git a/opencsp/app/sofast/test/test_image_processing.py b/opencsp/app/sofast/test/test_image_processing.py index db909c971..1fe032780 100644 --- a/opencsp/app/sofast/test/test_image_processing.py +++ b/opencsp/app/sofast/test/test_image_processing.py @@ -54,15 +54,15 @@ def test_calc_mask_raw(self): mask_raw = ip.calc_mask_raw(data['mask_images']) # Test - datasets = ['DataSofastCalculation/image_processing/general/mask_raw'] + datasets = ['DataSofastCalculation/general/CalculationImageProcessingGeneral/mask_raw'] data = load_hdf5_datasets(datasets, self.data_file_facet) np.testing.assert_allclose(data['mask_raw'], mask_raw) def test_mask_centroid(self): """Tests image_processing.centroid_mask()""" datasets = [ - 'DataSofastCalculation/image_processing/general/v_mask_centroid_image', - 'DataSofastCalculation/image_processing/general/mask_raw', + 'DataSofastCalculation/general/CalculationImageProcessingGeneral/v_mask_centroid_image', + 'DataSofastCalculation/general/CalculationImageProcessingGeneral/mask_raw', ] # Load test data @@ -94,8 +94,8 @@ def test_keep_largest_mask_area(self): def test_edges_from_mask(self): """Tests image_processing.edges_from_mask()""" datasets = [ - 'DataSofastCalculation/image_processing/general/v_edges_image', - 'DataSofastCalculation/image_processing/general/mask_raw', + 'DataSofastCalculation/general/CalculationImageProcessingGeneral/v_edges_image', + 'DataSofastCalculation/general/CalculationImageProcessingGeneral/mask_raw', ] # Load test data @@ -110,16 +110,16 @@ def test_edges_from_mask(self): def test_refine_mask_perimeter(self): """Tests image_processing.refine_mask_perimeter()""" datasets = [ - 'DataSofastCalculation/image_processing/general/v_edges_image', - 'DataSofastCalculation/image_processing/general/loop_optic_image_exp', - 'DataSofastCalculation/image_processing/facet_000/loop_facet_image_refine', + 'DataSofastCalculation/general/CalculationImageProcessingGeneral/v_edges_image', + 'DataSofastCalculation/general/CalculationImageProcessingGeneral/loop_optic_image_exp', + 'DataSofastCalculation/facet/facet_000/CalculationImageProcessingFacet/loop_facet_image_refine', ] # Get default parameters from Sofast class params = ParamsSofastFringe() args = [ - params.geometry_params.perimeter_refine_axial_search_dist, - params.geometry_params.perimeter_refine_perpendicular_search_dist, + params.geometry.perimeter_refine_axial_search_dist, + params.geometry.perimeter_refine_perpendicular_search_dist, ] # Load test data @@ -190,8 +190,8 @@ def test_unwrap_phase(self): # Load test data datasets = [ - 'DataSofastCalculation/geometry/facet_000/v_screen_points_fractional_screens', - 'DataSofastCalculation/image_processing/facet_000/mask_processed', + 'DataSofastCalculation/facet/facet_000/CalculationDataGeometryFacet/v_screen_points_fractional_screens', + 'DataSofastCalculation/facet/facet_000/CalculationImageProcessingFacet/mask_processed', ] data = load_hdf5_datasets(datasets, self.data_file_facet) measurement = MeasurementSofastFringe.load_from_hdf(self.data_file_measurement_facet) @@ -217,9 +217,9 @@ def test_unwrap_phase(self): def test_calculate_active_pixel_pointing_vectors(self): """Tests image_processing.calculate_active_pixel_pointing_vectors()""" datasets = [ - 'DataSofastCalculation/geometry/facet_000/u_pixel_pointing_facet', - 'DataSofastCalculation/geometry/general/r_optic_cam_refine_1', - 'DataSofastCalculation/image_processing/facet_000/mask_processed', + 'DataSofastCalculation/facet/facet_000/CalculationDataGeometryFacet/u_pixel_pointing_facet', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/r_optic_cam_refine_1', + 'DataSofastCalculation/facet/facet_000/CalculationImageProcessingFacet/mask_processed', ] # Load test data diff --git a/opencsp/app/sofast/test/test_integration_multi_facet.py b/opencsp/app/sofast/test/test_integration_multi_facet.py index 4e4554cfe..0dc1c1b6c 100644 --- a/opencsp/app/sofast/test/test_integration_multi_facet.py +++ b/opencsp/app/sofast/test/test_integration_multi_facet.py @@ -69,21 +69,21 @@ def setUpClass(cls, base_dir: str | None = None): sofast = ProcessSofastFringe(measurement, orientation, camera, display) # Update parameters - sofast.params.mask_hist_thresh = params['mask_hist_thresh'] - sofast.params.mask_filt_width = params['mask_filt_width'] - sofast.params.mask_filt_thresh = params['mask_filt_thresh'] - sofast.params.mask_thresh_active_pixels = params['mask_thresh_active_pixels'] - sofast.params.mask_keep_largest_area = params['mask_keep_largest_area'] - - sofast.params.geometry_params.perimeter_refine_axial_search_dist = params['perimeter_refine_axial_search_dist'] - sofast.params.geometry_params.perimeter_refine_perpendicular_search_dist = params[ + sofast.params.mask.hist_thresh = params['mask_hist_thresh'] + sofast.params.mask.filt_width = params['mask_filt_width'] + sofast.params.mask.filt_thresh = params['mask_filt_thresh'] + sofast.params.mask.thresh_active_pixels = params['mask_thresh_active_pixels'] + sofast.params.mask.keep_largest_area = params['mask_keep_largest_area'] + + sofast.params.geometry.perimeter_refine_axial_search_dist = params['perimeter_refine_axial_search_dist'] + sofast.params.geometry.perimeter_refine_perpendicular_search_dist = params[ 'perimeter_refine_perpendicular_search_dist' ] - sofast.params.geometry_params.facet_corns_refine_step_length = params['facet_corns_refine_step_length'] - sofast.params.geometry_params.facet_corns_refine_perpendicular_search_dist = params[ + sofast.params.geometry.facet_corns_refine_step_length = params['facet_corns_refine_step_length'] + sofast.params.geometry.facet_corns_refine_perpendicular_search_dist = params[ 'facet_corns_refine_perpendicular_search_dist' ] - sofast.params.geometry_params.facet_corns_refine_frac_keep = params['facet_corns_refine_frac_keep'] + sofast.params.geometry.facet_corns_refine_frac_keep = params['facet_corns_refine_frac_keep'] # Load array data datasets = [ diff --git a/opencsp/app/sofast/test/test_integration_single_facet.py b/opencsp/app/sofast/test/test_integration_single_facet.py index 17dca8277..8e8c9284b 100644 --- a/opencsp/app/sofast/test/test_integration_single_facet.py +++ b/opencsp/app/sofast/test/test_integration_single_facet.py @@ -11,12 +11,12 @@ from opencsp.app.sofast.lib.DefinitionFacet import DefinitionFacet from opencsp.app.sofast.lib.ImageCalibrationScaling import ImageCalibrationScaling from opencsp.app.sofast.lib.MeasurementSofastFringe import MeasurementSofastFringe +from opencsp.app.sofast.lib.ParamsSofastFringe import ParamsSofastFringe from opencsp.app.sofast.lib.ProcessSofastFringe import ProcessSofastFringe from opencsp.app.sofast.lib.SpatialOrientation import SpatialOrientation from opencsp.common.lib.camera.Camera import Camera from opencsp.common.lib.deflectometry.Surface2DPlano import Surface2DPlano from opencsp.common.lib.deflectometry.Surface2DParabolic import Surface2DParabolic -from opencsp.common.lib.geometry.Vxyz import Vxyz from opencsp.common.lib.opencsp_path.opencsp_root_path import opencsp_code_dir from opencsp.common.lib.tool.hdf5_tools import load_hdf5_datasets @@ -55,85 +55,26 @@ def setUpClass(cls, base_dir: str | None = None): # Load data from all datasets for file_dataset in cls.files_dataset: - # Load display + # Load components camera = Camera.load_from_hdf(file_dataset) orientation = SpatialOrientation.load_from_hdf(file_dataset) calibration = ImageCalibrationScaling.load_from_hdf(file_dataset) display = DisplayShape.load_from_hdf(file_dataset) + facet_data = DefinitionFacet.load_from_hdf(file_dataset, 'DataSofastInput/optic_definition/facet_000/') + params_sofast = ParamsSofastFringe.load_from_hdf(file_dataset, 'DataSofastInput/') + if 'plano' in os.path.basename(file_dataset): + surface = Surface2DPlano.load_from_hdf(file_dataset, 'DataSofastInput/optic_definition/facet_000/') + else: + surface = Surface2DParabolic.load_from_hdf(file_dataset, 'DataSofastInput/optic_definition/facet_000/') # Calibrate measurement measurement.calibrate_fringe_images(calibration) - # Load surface definition - surface_data = load_hdf5_datasets( - [ - 'DataSofastInput/surface_params/facet_000/surface_type', - 'DataSofastInput/surface_params/facet_000/robust_least_squares', - 'DataSofastInput/surface_params/facet_000/downsample', - ], - file_dataset, - ) - surface_data['robust_least_squares'] = bool(surface_data['robust_least_squares']) - if surface_data['surface_type'] == 'parabolic': - surface_data.update( - load_hdf5_datasets( - ['DataSofastInput/surface_params/facet_000/initial_focal_lengths_xy'], file_dataset - ) - ) - surface = Surface2DParabolic( - surface_data['initial_focal_lengths_xy'], - surface_data['robust_least_squares'], - surface_data['downsample'], - ) - else: - surface = Surface2DPlano(surface_data['robust_least_squares'], surface_data['downsample']) - - # Load optic data - facet_data = load_hdf5_datasets( - [ - 'DataSofastInput/optic_definition/facet_000/v_centroid_facet', - 'DataSofastInput/optic_definition/facet_000/v_facet_corners', - ], - file_dataset, - ) - facet_data = DefinitionFacet(Vxyz(facet_data['v_facet_corners']), Vxyz(facet_data['v_centroid_facet'])) - - # Load sofast params - datasets = [ - 'DataSofastInput/sofast_params/mask_hist_thresh', - 'DataSofastInput/sofast_params/mask_filt_width', - 'DataSofastInput/sofast_params/mask_filt_thresh', - 'DataSofastInput/sofast_params/mask_thresh_active_pixels', - 'DataSofastInput/sofast_params/mask_keep_largest_area', - 'DataSofastInput/sofast_params/perimeter_refine_axial_search_dist', - 'DataSofastInput/sofast_params/perimeter_refine_perpendicular_search_dist', - 'DataSofastInput/sofast_params/facet_corns_refine_step_length', - 'DataSofastInput/sofast_params/facet_corns_refine_perpendicular_search_dist', - 'DataSofastInput/sofast_params/facet_corns_refine_frac_keep', - ] - params = load_hdf5_datasets(datasets, file_dataset) - # Instantiate sofast object sofast = ProcessSofastFringe(measurement, orientation, camera, display) # Update parameters - sofast.params.mask_hist_thresh = params['mask_hist_thresh'] - sofast.params.mask_filt_width = params['mask_filt_width'] - sofast.params.mask_filt_thresh = params['mask_filt_thresh'] - sofast.params.mask_thresh_active_pixels = params['mask_thresh_active_pixels'] - sofast.params.mask_keep_largest_area = params['mask_keep_largest_area'] - - sofast.params.geometry_params.perimeter_refine_axial_search_dist = params[ - 'perimeter_refine_axial_search_dist' - ] - sofast.params.geometry_params.perimeter_refine_perpendicular_search_dist = params[ - 'perimeter_refine_perpendicular_search_dist' - ] - sofast.params.geometry_params.facet_corns_refine_step_length = params['facet_corns_refine_step_length'] - sofast.params.geometry_params.facet_corns_refine_perpendicular_search_dist = params[ - 'facet_corns_refine_perpendicular_search_dist' - ] - sofast.params.geometry_params.facet_corns_refine_frac_keep = params['facet_corns_refine_frac_keep'] + sofast.params = params_sofast # Run SOFAST sofast.process_optic_singlefacet(facet_data, surface) @@ -144,21 +85,21 @@ def setUpClass(cls, base_dir: str | None = None): cls.v_surf_points_facet.append(sofast.data_characterization_facet[0].v_surf_points_facet.data) def test_slopes(self): - datasets = ['DataSofastCalculation/facet/facet_000/slopes_facet_xy'] + datasets = ['DataSofastCalculation/facet/facet_000/SlopeSolverData/slopes_facet_xy'] for idx, file in enumerate(self.files_dataset): with self.subTest(i=idx): data = load_hdf5_datasets(datasets, file) np.testing.assert_allclose(data['slopes_facet_xy'], self.slopes[idx], atol=1e-7, rtol=0) def test_surf_coefs(self): - datasets = ['DataSofastCalculation/facet/facet_000/surf_coefs_facet'] + datasets = ['DataSofastCalculation/facet/facet_000/SlopeSolverData/surf_coefs_facet'] for idx, file in enumerate(self.files_dataset): with self.subTest(i=idx): data = load_hdf5_datasets(datasets, file) np.testing.assert_allclose(data['surf_coefs_facet'], self.surf_coefs[idx], atol=1e-8, rtol=0) def test_int_points(self): - datasets = ['DataSofastCalculation/facet/facet_000/v_surf_points_facet'] + datasets = ['DataSofastCalculation/facet/facet_000/SlopeSolverData/v_surf_points_facet'] for idx, file in enumerate(self.files_dataset): with self.subTest(i=idx): data = load_hdf5_datasets(datasets, file) diff --git a/opencsp/app/sofast/test/test_integration_undefined.py b/opencsp/app/sofast/test/test_integration_undefined.py index 75f764398..15ce0aa21 100644 --- a/opencsp/app/sofast/test/test_integration_undefined.py +++ b/opencsp/app/sofast/test/test_integration_undefined.py @@ -65,20 +65,20 @@ def test_undefined(self): sofast = ProcessSofastFringe(measurement, orientation, camera, display) # Update parameters - sofast.params.mask_hist_thresh = params['mask_hist_thresh'] - sofast.params.mask_filt_width = params['mask_filt_width'] - sofast.params.mask_filt_thresh = params['mask_filt_thresh'] - sofast.params.mask_thresh_active_pixels = params['mask_thresh_active_pixels'] - sofast.params.mask_keep_largest_area = params['mask_keep_largest_area'] - sofast.params.geometry_params.perimeter_refine_axial_search_dist = params['perimeter_refine_axial_search_dist'] - sofast.params.geometry_params.perimeter_refine_perpendicular_search_dist = params[ + sofast.params.mask.hist_thresh = params['mask_hist_thresh'] + sofast.params.mask.filt_width = params['mask_filt_width'] + sofast.params.mask.filt_thresh = params['mask_filt_thresh'] + sofast.params.mask.thresh_active_pixels = params['mask_thresh_active_pixels'] + sofast.params.mask.keep_largest_area = params['mask_keep_largest_area'] + sofast.params.geometry.perimeter_refine_axial_search_dist = params['perimeter_refine_axial_search_dist'] + sofast.params.geometry.perimeter_refine_perpendicular_search_dist = params[ 'perimeter_refine_perpendicular_search_dist' ] - sofast.params.geometry_params.facet_corns_refine_step_length = params['facet_corns_refine_step_length'] - sofast.params.geometry_params.facet_corns_refine_perpendicular_search_dist = params[ + sofast.params.geometry.facet_corns_refine_step_length = params['facet_corns_refine_step_length'] + sofast.params.geometry.facet_corns_refine_perpendicular_search_dist = params[ 'facet_corns_refine_perpendicular_search_dist' ] - sofast.params.geometry_params.facet_corns_refine_frac_keep = params['facet_corns_refine_frac_keep'] + sofast.params.geometry.facet_corns_refine_frac_keep = params['facet_corns_refine_frac_keep'] # Define surface data surface = Surface2DParabolic( diff --git a/opencsp/app/sofast/test/test_spatial_processing.py b/opencsp/app/sofast/test/test_spatial_processing.py index ba8be45b1..3a91aa98b 100644 --- a/opencsp/app/sofast/test/test_spatial_processing.py +++ b/opencsp/app/sofast/test/test_spatial_processing.py @@ -32,8 +32,8 @@ def setUpClass(cls): def test_t_from_distance(self): datasets = [ - 'DataSofastCalculation/image_processing/general/v_mask_centroid_image', - 'DataSofastCalculation/geometry/general/v_cam_optic_cam_exp', + 'DataSofastCalculation/general/CalculationImageProcessingGeneral/v_mask_centroid_image', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/v_cam_optic_cam_exp', ] # Load test data @@ -53,8 +53,8 @@ def test_t_from_distance(self): def test_r_from_position(self): datasets = [ - 'DataSofastCalculation/geometry/general/v_cam_optic_cam_exp', - 'DataSofastCalculation/geometry/general/r_optic_cam_exp', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/v_cam_optic_cam_exp', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/r_optic_cam_exp', ] # Load test data @@ -70,12 +70,12 @@ def test_r_from_position(self): def test_calc_rt_from_img_pts(self): datasets = [ - 'DataSofastCalculation/geometry/general/r_optic_cam_exp', - 'DataSofastCalculation/geometry/general/v_cam_optic_cam_exp', - 'DataSofastCalculation/geometry/general/r_optic_cam_refine_1', - 'DataSofastCalculation/geometry/general/v_cam_optic_cam_refine_1', - 'DataSofastCalculation/image_processing/facet_000/loop_facet_image_refine', - 'DataSofastInput/optic_definition/facet_000/v_facet_corners', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/r_optic_cam_exp', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/v_cam_optic_cam_exp', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/r_optic_cam_refine_1', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/v_cam_optic_cam_refine_1', + 'DataSofastCalculation/facet/facet_000/CalculationImageProcessingFacet/loop_facet_image_refine', + 'DataSofastInput/optic_definition/facet_000/DefinitionFacet/v_facet_corners', ] # Load test data @@ -94,8 +94,8 @@ def test_calc_rt_from_img_pts(self): def test_distance_error(self): datasets = [ - 'DataSofastCalculation/error/error_optic_screen_dist_2', - 'DataSofastCalculation/geometry/general/v_cam_optic_cam_refine_2', + 'DataSofastCalculation/general/CalculationError/error_dist_optic_screen_2', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/v_cam_optic_cam_refine_2', ] # Load test data @@ -108,15 +108,15 @@ def test_distance_error(self): ) # Test - np.testing.assert_allclose(data['error_optic_screen_dist_2'], error_optic_screen_dist_2) + np.testing.assert_allclose(data['error_dist_optic_screen_2'], error_optic_screen_dist_2) def test_reprojection_error(self): datasets = [ - 'DataSofastCalculation/error/error_reprojection_2', - 'DataSofastCalculation/image_processing/facet_000/loop_facet_image_refine', - 'DataSofastCalculation/geometry/general/r_optic_cam_refine_1', - 'DataSofastCalculation/geometry/general/v_cam_optic_cam_refine_2', - 'DataSofastInput/optic_definition/facet_000/v_facet_corners', + 'DataSofastCalculation/general/CalculationError/error_reprojection_2', + 'DataSofastCalculation/facet/facet_000/CalculationImageProcessingFacet/loop_facet_image_refine', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/r_optic_cam_refine_1', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/v_cam_optic_cam_refine_2', + 'DataSofastInput/optic_definition/facet_000/DefinitionFacet/v_facet_corners', ] # Load test data @@ -136,9 +136,9 @@ def test_reprojection_error(self): def test_refine_v_distance(self): datasets = [ - 'DataSofastCalculation/geometry/general/v_cam_optic_cam_refine_1', - 'DataSofastCalculation/geometry/general/v_cam_optic_cam_refine_2', - 'DataSofastCalculation/geometry/general/r_optic_cam_refine_1', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/v_cam_optic_cam_refine_1', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/v_cam_optic_cam_refine_2', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/r_optic_cam_refine_1', ] # Load test data diff --git a/opencsp/common/lib/deflectometry/Surface2DParabolic.py b/opencsp/common/lib/deflectometry/Surface2DParabolic.py index 78b93e496..778a743d5 100644 --- a/opencsp/common/lib/deflectometry/Surface2DParabolic.py +++ b/opencsp/common/lib/deflectometry/Surface2DParabolic.py @@ -271,6 +271,16 @@ def shift_all(self, v_align_optic_step: Vxyz) -> None: self.v_optic_screen_optic += v_align_optic_step def save_to_hdf(self, file: str, prefix: str = ''): + """Saves data to given file. Data is stored as: PREFIX + ParamsSurface/Field_1 + + Parameters + ---------- + file : str + HDF file to save to + prefix : str, optional + Prefix to append to folder path within HDF file (folders must be separated by "/"). + Default is empty string ''. + """ data = [self.initial_focal_lengths_xy, self.robust_least_squares, self.downsample, 'parabolic'] datasets = [ prefix + 'ParamsSurface/initial_focal_lengths_xy', @@ -282,6 +292,16 @@ def save_to_hdf(self, file: str, prefix: str = ''): @classmethod def load_from_hdf(cls, file: str, prefix: str = ''): + """Loads data from given file. Assumes data is stored as: PREFIX + ParamsSurface/Field_1 + + Parameters + ---------- + file : str + HDF file to load from + prefix : str, optional + Prefix to append to folder path within HDF file (folders must be separated by "/"). + Default is empty string ''. + """ # Check surface type data = load_hdf5_datasets([prefix + 'ParamsSurface/surface_type'], file) if data['surface_type'] != 'parabolic': diff --git a/opencsp/common/lib/deflectometry/test/test_SlopeSolver.py b/opencsp/common/lib/deflectometry/test/test_SlopeSolver.py index aae80105d..7546f1d80 100644 --- a/opencsp/common/lib/deflectometry/test/test_SlopeSolver.py +++ b/opencsp/common/lib/deflectometry/test/test_SlopeSolver.py @@ -33,15 +33,11 @@ def setUpClass(cls): # Create spatial orientation objects datasets = [ - 'DataSofastCalculation/geometry/general/r_optic_cam_refine_1', - 'DataSofastCalculation/geometry/general/v_cam_optic_cam_refine_2', - 'DataSofastCalculation/geometry/facet_000/u_pixel_pointing_facet', - 'DataSofastCalculation/geometry/facet_000/u_cam_measure_point_facet', - 'DataSofastCalculation/geometry/facet_000/v_screen_points_facet', - 'DataSofastInput/surface_params/facet_000/initial_focal_lengths_xy', - 'DataSofastInput/surface_params/facet_000/downsample', - 'DataSofastInput/surface_params/facet_000/robust_least_squares', - 'DataSofastInput/surface_params/facet_000/surface_type', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/r_optic_cam_refine_1', + 'DataSofastCalculation/general/CalculationDataGeometryGeneral/v_cam_optic_cam_refine_2', + 'DataSofastCalculation/facet/facet_000/CalculationDataGeometryFacet/u_pixel_pointing_facet', + 'DataSofastCalculation/facet/facet_000/CalculationDataGeometryFacet/u_cam_measure_point_facet', + 'DataSofastCalculation/facet/facet_000/CalculationDataGeometryFacet/v_screen_points_facet', ] # Load data data = h5.load_hdf5_datasets(datasets, cls.data_file_facet) @@ -54,11 +50,7 @@ def setUpClass(cls): ori.orient_optic_cam(r_cam_optic, v_cam_optic_cam) # Perform calculations - surface = Surface2DParabolic( - initial_focal_lengths_xy=data['initial_focal_lengths_xy'], - robust_least_squares=bool(data['robust_least_squares']), - downsample=data['downsample'], - ) + surface = Surface2DParabolic.load_from_hdf(cls.data_file_facet, 'DataSofastInput/optic_definition/facet_000/') kwargs = { 'v_optic_cam_optic': ori.v_optic_cam_optic, 'u_active_pixel_pointing_optic': Uxyz(data['u_pixel_pointing_facet']), @@ -77,20 +69,24 @@ def setUpClass(cls): cls.data_slope = ss.get_data() def test_transform_alignment(self): - data = h5.load_hdf5_datasets(['DataSofastCalculation/facet/facet_000/trans_alignment'], self.data_file_facet) + data = h5.load_hdf5_datasets( + ['DataSofastCalculation/facet/facet_000/SlopeSolverData/trans_alignment'], self.data_file_facet + ) np.testing.assert_allclose(data['trans_alignment'], self.data_slope.trans_alignment.matrix, atol=1e-8, rtol=0) def test_int_pts(self): data = h5.load_hdf5_datasets( - ['DataSofastCalculation/facet/facet_000/v_surf_points_facet'], self.data_file_facet + ['DataSofastCalculation/facet/facet_000/SlopeSolverData/v_surf_points_facet'], self.data_file_facet ) np.testing.assert_allclose( data['v_surf_points_facet'], self.data_slope.v_surf_points_facet.data, atol=1e-8, rtol=0 ) def test_slopes(self): - data = h5.load_hdf5_datasets(['DataSofastCalculation/facet/facet_000/slopes_facet_xy'], self.data_file_facet) + data = h5.load_hdf5_datasets( + ['DataSofastCalculation/facet/facet_000/SlopeSolverData/slopes_facet_xy'], self.data_file_facet + ) np.testing.assert_allclose(data['slopes_facet_xy'], self.data_slope.slopes_facet_xy, atol=1e-8, rtol=0) def test_save_hdf(self): diff --git a/opencsp/test/data/sofast_fringe/data_expected_facet/data.h5 b/opencsp/test/data/sofast_fringe/data_expected_facet/data.h5 index f39e5f9a0..19574189d 100644 Binary files a/opencsp/test/data/sofast_fringe/data_expected_facet/data.h5 and b/opencsp/test/data/sofast_fringe/data_expected_facet/data.h5 differ diff --git a/opencsp/test/data/sofast_fringe/data_expected_facet/data_distorted_3d.h5 b/opencsp/test/data/sofast_fringe/data_expected_facet/data_distorted_3d.h5 index 06af13cb9..39e41eb37 100644 Binary files a/opencsp/test/data/sofast_fringe/data_expected_facet/data_distorted_3d.h5 and b/opencsp/test/data/sofast_fringe/data_expected_facet/data_distorted_3d.h5 differ diff --git a/opencsp/test/data/sofast_fringe/data_expected_facet/data_no_ls.h5 b/opencsp/test/data/sofast_fringe/data_expected_facet/data_no_ls.h5 index 8dcf1b17a..f3605ce53 100644 Binary files a/opencsp/test/data/sofast_fringe/data_expected_facet/data_no_ls.h5 and b/opencsp/test/data/sofast_fringe/data_expected_facet/data_no_ls.h5 differ diff --git a/opencsp/test/data/sofast_fringe/data_expected_facet/data_plano.h5 b/opencsp/test/data/sofast_fringe/data_expected_facet/data_plano.h5 index 30ad264fc..cbc8963da 100644 Binary files a/opencsp/test/data/sofast_fringe/data_expected_facet/data_plano.h5 and b/opencsp/test/data/sofast_fringe/data_expected_facet/data_plano.h5 differ diff --git a/opencsp/test/data/sofast_fringe/data_expected_facet/data_rectangular.h5 b/opencsp/test/data/sofast_fringe/data_expected_facet/data_rectangular.h5 index 4a565a681..dd6695da7 100644 Binary files a/opencsp/test/data/sofast_fringe/data_expected_facet/data_rectangular.h5 and b/opencsp/test/data/sofast_fringe/data_expected_facet/data_rectangular.h5 differ