From 69dab192124a26919e911b4156005d8aef7b53c3 Mon Sep 17 00:00:00 2001 From: gmegh Date: Mon, 22 Jul 2024 11:34:01 -0700 Subject: [PATCH 1/3] Update to use ts_ofc v3.2.0 --- bin.src/img_closed_loop.py | 1 + doc/versionHistory.rst | 6 ++++ policy/config/controllers/oic_controller.yaml | 30 +++++++++++++++++++ policy/config/controllers/pid_controller.yaml | 28 +++++++++++++++++ python/lsst/ts/imsim/closed_loop_task.py | 28 +++++++++++++---- tests/test_closed_loop_task.py | 3 +- 6 files changed, 89 insertions(+), 7 deletions(-) create mode 100644 policy/config/controllers/oic_controller.yaml create mode 100644 policy/config/controllers/pid_controller.yaml diff --git a/bin.src/img_closed_loop.py b/bin.src/img_closed_loop.py index 93461a9..88f9e75 100644 --- a/bin.src/img_closed_loop.py +++ b/bin.src/img_closed_loop.py @@ -56,6 +56,7 @@ args.iter_num, args.pipeline_file, args.config_pointer_file, + args.controller_config_file, args.turn_off_sky_background, args.turn_off_atmosphere, args.turn_off_wavefront_estimates, diff --git a/doc/versionHistory.rst b/doc/versionHistory.rst index 50e874d..c3a9f9b 100644 --- a/doc/versionHistory.rst +++ b/doc/versionHistory.rst @@ -5,6 +5,12 @@ ################## Version History ################## +------------- +1.6.0 +------------- + +* Update to use ts_ofc version 3.2.0. + ------------- 1.5.6 ------------- diff --git a/policy/config/controllers/oic_controller.yaml b/policy/config/controllers/oic_controller.yaml new file mode 100644 index 0000000..e1a82a6 --- /dev/null +++ b/policy/config/controllers/oic_controller.yaml @@ -0,0 +1,30 @@ +# Description: Optimal Integral Controller (OIC) configuration file +# +# Name: OIC +# xref is the reference to the controller, it can be ['x0', 'x00', '0'] +# kp is the proportional gain +# ki is the integral gain defined as ki = kp*T/T_i +# kd is the derivative gain defined as kd = kp*T_d/T +# derivative_filter_coeff is the coefficient of the derivative filter +# setpoint is the setpoint for each degree of freedom (array of 50 elements) + +description: Optimal Integral Controller (OIC) + +name: OIC +xref: x00 +kp: -1.0 +ki: 0 +kd: 0 +derivative_filter_coeff: 1.0 +setpoint: [ + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0 +] diff --git a/policy/config/controllers/pid_controller.yaml b/policy/config/controllers/pid_controller.yaml new file mode 100644 index 0000000..0171b70 --- /dev/null +++ b/policy/config/controllers/pid_controller.yaml @@ -0,0 +1,28 @@ +# Description: Proportional-Integral-Derivative Controller (PID) configuration file +# +# Name: PID +# kp is the proportional gain +# ki is the integral gain defined as ki = kp*T/T_i +# kd is the derivative gain defined as kd = kp*T_d/T +# derivative_filter_coeff is the coefficient of the derivative filter +# setpoint is the setpoint for each degree of freedom (array of 50 elements) + +description: Proportional-Integral-Derivative Controller + +name: PID +kp: 0.9 +ki: 0.0 +kd: 0.0 +derivative_filter_coeff: 1.0 +setpoint: [ + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0 +] diff --git a/python/lsst/ts/imsim/closed_loop_task.py b/python/lsst/ts/imsim/closed_loop_task.py index b026c3c..cfa3982 100644 --- a/python/lsst/ts/imsim/closed_loop_task.py +++ b/python/lsst/ts/imsim/closed_loop_task.py @@ -27,6 +27,7 @@ from argparse import ArgumentParser from copy import deepcopy from glob import glob +from pathlib import Path import astropy import numpy as np @@ -186,7 +187,7 @@ def _set_sky_sim_based_on_opd_field_pos( ) star_id += 1 - def config_ofc_calc(self, cam_type: CamType) -> None: + def config_ofc_calc(self, cam_type: CamType, controller_config_file: str) -> None: """Configure the OFC calculator. OFC: Optical feedback calculator. @@ -195,9 +196,15 @@ def config_ofc_calc(self, cam_type: CamType) -> None: ---------- cam_type : lsst.ts.imsim.utils.CamType Camera type. + controller_config_file : str + Controller configuration file. """ - self.ofc_calc = OFC(OFCData(cam_type.value)) + ofc_data = OFCData(cam_type.value) + ofc_data.controller_filename = ( + Path(get_config_dir()) / "controllers" / controller_config_file + ) + self.ofc_calc = OFC(ofc_data) self.ofc_calc.ofc_data.znmin = 4 self.ofc_calc.ofc_data.zn_selected = np.arange( self.ofc_calc.ofc_data.znmin, self.max_noll_index + 1 @@ -387,7 +394,7 @@ def _run_sim( code will just default to sending it to stdout. (The default is "".) """ - state_0 = self.ofc_calc.ofc_controller.aggregated_state + state_0 = self.ofc_calc.controller.aggregated_state self.imsim_cmpt.dof_in_um = state_0 # If using wavefront sensors we measure one per pair @@ -541,12 +548,11 @@ def _run_sim( wfe=wfe, sensor_names=sensor_names, filter_name=obs_metadata.band.upper(), - gain=-1, rotation_angle=obs_metadata.rotator_angle, ) # Set the new aggregated DOF to phosimCmpt - dof_in_um = self.ofc_calc.ofc_controller.aggregated_state + dof_in_um = self.ofc_calc.controller.aggregated_state self.imsim_cmpt.dof_in_um = dof_in_um # Save the DOF file @@ -1015,6 +1021,7 @@ def run_img( iter_num: int, pipeline_file: str, imsim_config_pointer_file: str, + controller_config_file: str, turn_off_sky_background: bool, turn_off_atmosphere: bool, turn_off_wavefront_estimates: bool, @@ -1059,6 +1066,8 @@ def run_img( Path to pointer file with locations of yaml configuration files for imsim submodules. If empty string then the code will use the default in policy/config for the given inst. + controller_config_file : str + Path to the controller configuration file. turn_off_sky_background : bool If set to True then the closed loop will simulate images without sky background. @@ -1108,7 +1117,7 @@ def run_img( self.config_sky_sim( cam_type, obs_metadata, path_sky_file=path_sky_file, star_mag=star_mag ) - self.config_ofc_calc(cam_type) + self.config_ofc_calc(cam_type, controller_config_file) self.imsim_cmpt = ImsimCmpt( num_of_zk=self.max_noll_index - self.ofc_calc.ofc_data.znmin + 1 ) @@ -1332,6 +1341,13 @@ def set_default_parser(parser: ArgumentParser) -> ArgumentParser: help="Imsim Configuration Pointer File.", ) + parser.add_argument( + "--controller_config_file", + type=str, + default="oic_controller.yaml", + help="Path to the controller configuration file.", + ) + parser.add_argument( "--sky_seed", type=int, diff --git a/tests/test_closed_loop_task.py b/tests/test_closed_loop_task.py index a3eda20..e34ee3e 100644 --- a/tests/test_closed_loop_task.py +++ b/tests/test_closed_loop_task.py @@ -70,7 +70,7 @@ def test_config_sky_sim_with_sky_file(self): def test_config_ofc_calc(self): cam_type = CamType.LsstCam self.closed_loop_task.max_noll_index = 28 - self.closed_loop_task.config_ofc_calc(cam_type) + self.closed_loop_task.config_ofc_calc(cam_type, "oic_controller.yaml") ofc_calc = self.closed_loop_task.ofc_calc self.assertEqual(ofc_calc.ofc_data.name, "lsst") @@ -131,6 +131,7 @@ def test_set_default_parser(self): self.assertEqual(args.output, "") self.assertFalse(args.clobber) self.assertEqual(args.config_pointer_file, "") + self.assertEqual(args.controller_config_file, "oic_controller.yaml") self.assertEqual(args.pipeline_file, "") self.assertEqual(args.sky_seed, 42) self.assertEqual(args.pert_seed, 11) From e283c231964182ce63364e5768c0c873dbef512d Mon Sep 17 00:00:00 2001 From: gmegh Date: Thu, 1 Aug 2024 15:32:00 -0700 Subject: [PATCH 2/3] Send sensor_ids instead of sensor_names to ts_ofc --- doc/versionHistory.rst | 6 ++++++ python/lsst/ts/imsim/closed_loop_task.py | 12 +++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/doc/versionHistory.rst b/doc/versionHistory.rst index c3a9f9b..347d9c0 100644 --- a/doc/versionHistory.rst +++ b/doc/versionHistory.rst @@ -5,6 +5,12 @@ ################## Version History ################## +------------- +1.6.1 +------------- + +* Update to send sensor_ids instead of sensor_names to ts_ofc + ------------- 1.6.0 ------------- diff --git a/python/lsst/ts/imsim/closed_loop_task.py b/python/lsst/ts/imsim/closed_loop_task.py index cfa3982..bcdc70d 100644 --- a/python/lsst/ts/imsim/closed_loop_task.py +++ b/python/lsst/ts/imsim/closed_loop_task.py @@ -529,24 +529,22 @@ def _run_sim( [sensor_wfe.annular_zernike_poly for sensor_wfe in list_of_wf_err] ) - sensor_names = np.array( - [sensor_wfe.sensor_name for sensor_wfe in list_of_wf_err] + sensor_ids = np.array( + [sensor_wfe.sensor_id for sensor_wfe in list_of_wf_err] ) # Only include the fwhm data from sensor we are simulating # (e.g. only raft centers instead of full FAM). if self.use_ccd_img: - fwhm_idx = [ - ref_sensor_name_list.index(sens_name) for sens_name in sensor_names - ] + fwhm_idx = [ref_sensor_id_list.index(sens_id) for sens_id in sensor_ids] fwhm = fwhm[fwhm_idx] # Pass data to OFC - self.ofc_calc.set_fwhm_data(fwhm, sensor_names) + self.ofc_calc.set_fwhm_data(fwhm, sensor_ids) self.ofc_calc.calculate_corrections( wfe=wfe, - sensor_names=sensor_names, + sensor_ids=sensor_ids, filter_name=obs_metadata.band.upper(), rotation_angle=obs_metadata.rotator_angle, ) From e58f01e5d8048eeccb63ad9aa541362de4d4a866 Mon Sep 17 00:00:00 2001 From: Bryce Kalmbach Date: Thu, 8 Aug 2024 15:39:13 -0700 Subject: [PATCH 3/3] Add group_id to obs_metadata. Add group pairer to pipeline when running science sensors. --- doc/versionHistory.rst | 7 +++++++ python/lsst/ts/imsim/closed_loop_task.py | 16 ++++++++++++++++ python/lsst/ts/imsim/imsim_cmpt.py | 1 + python/lsst/ts/imsim/obs_metadata.py | 1 + .../testData/imsimConfig/imsimConfigLsstCam.yaml | 1 + tests/test_obs_metadata.py | 1 + 6 files changed, 27 insertions(+) diff --git a/doc/versionHistory.rst b/doc/versionHistory.rst index 347d9c0..b5bf03c 100644 --- a/doc/versionHistory.rst +++ b/doc/versionHistory.rst @@ -5,6 +5,13 @@ ################## Version History ################## +------------- +1.6.2 +------------- + +* Set group for images. +* Use group pairer for science images. + ------------- 1.6.1 ------------- diff --git a/python/lsst/ts/imsim/closed_loop_task.py b/python/lsst/ts/imsim/closed_loop_task.py index bcdc70d..5696fc7 100644 --- a/python/lsst/ts/imsim/closed_loop_task.py +++ b/python/lsst/ts/imsim/closed_loop_task.py @@ -453,6 +453,7 @@ def _run_sim( self.imsim_cmpt.output_img_dir = output_img_dir # Generate the sky images and calculate the wavefront error + obs_metadata.group_id = str(obs_metadata.seq_num) if cam_type == CamType.LsstCam: self._generate_images( obs_metadata, @@ -639,6 +640,9 @@ def _generate_images( base_config_yaml["output"]["nproc"] = num_pro base_config_yaml["image"]["random_seed"] = sky_seed base_config_yaml["input"]["telescope"]["fea"]["m1m3_lut"]["seed"] = pert_seed + base_config_yaml["output"]["readout"]["added_keywords"][ + "GROUPID" + ] = obs_metadata.group_id if turn_off_sky_background: base_config_yaml["image"]["sky_level"] = 0 if turn_off_atmosphere: @@ -948,13 +952,24 @@ def write_wep_configuration( filter_type_name = self.map_filter_ref_to_g(filter_type_name) # Set defocal offset for camera + science_cutout_config = """config: + maxRecenterDistance: 100 + python: | + from lsst.ts.wep.task.pairTask import GroupPairer + config.pairer.retarget(GroupPairer) + """ if cam_type in [CamType.LsstCam, CamType.LsstFamCam]: if cam_type == CamType.LsstCam: cut_out_task = "Cwfs" + cut_out_config = """config: + maxRecenterDistance: 100 +""" else: cut_out_task = "ScienceSensor" + cut_out_config = science_cutout_config elif cam_type in [CamType.ComCam]: cut_out_task = "ScienceSensor" + cut_out_config = science_cutout_config with open(pipeline_yaml_path, "w") as fp: fp.write( @@ -993,6 +1008,7 @@ def write_wep_configuration( class: lsst.ts.wep.task.generateDonutCatalogWcsTask.GenerateDonutCatalogWcsTask cutOutDonuts{cut_out_task}Task: class: lsst.ts.wep.task.cutOutDonuts{cut_out_task}Task.CutOutDonuts{cut_out_task}Task + {cut_out_config} calcZernikesTask: class: lsst.ts.wep.task.calcZernikesTask.CalcZernikesTask config: diff --git a/python/lsst/ts/imsim/imsim_cmpt.py b/python/lsst/ts/imsim/imsim_cmpt.py index ce57ac0..fd697b8 100644 --- a/python/lsst/ts/imsim/imsim_cmpt.py +++ b/python/lsst/ts/imsim/imsim_cmpt.py @@ -331,6 +331,7 @@ def add_config_header(self, obs_metadata: ObsMetadata) -> str: ) header_text += f" focusZ: {obs_metadata.focus_z}\n" header_text += f" seeing: {obs_metadata.raw_seeing}\n" + header_text += f" group: {obs_metadata.group_id}" return header_text diff --git a/python/lsst/ts/imsim/obs_metadata.py b/python/lsst/ts/imsim/obs_metadata.py index 0e58207..9f2ee26 100644 --- a/python/lsst/ts/imsim/obs_metadata.py +++ b/python/lsst/ts/imsim/obs_metadata.py @@ -47,6 +47,7 @@ class ObsMetadata: parallactic_angle: float = field(init=False) alt: float = field(init=False) az: float = field(init=False) + group_id: str = "1" def __post_init__(self) -> None: """Populate the zenith and parallactic angles diff --git a/tests/testData/imsimConfig/imsimConfigLsstCam.yaml b/tests/testData/imsimConfig/imsimConfigLsstCam.yaml index f5412e2..d934c1b 100644 --- a/tests/testData/imsimConfig/imsimConfigLsstCam.yaml +++ b/tests/testData/imsimConfig/imsimConfigLsstCam.yaml @@ -312,6 +312,7 @@ output: rotTelPos: 0.0 seqnum: 1 seeing: *rawSeeing + group: 1 camera: LsstCam diff --git a/tests/test_obs_metadata.py b/tests/test_obs_metadata.py index 5ab3efc..7e26349 100644 --- a/tests/test_obs_metadata.py +++ b/tests/test_obs_metadata.py @@ -48,6 +48,7 @@ def test_obs_metadata(self): self.assertEqual(self.obs_meta_test.focus_z, 0.0) self.assertAlmostEqual(self.obs_meta_test.zenith, 51.63121964610822) self.assertAlmostEqual(self.obs_meta_test.parallactic_angle, -130.1781932025212) + self.assertEqual(self.obs_meta_test.group_id, "1") def test_calc_parallactic_angle(self): sirius = FixedTarget.from_name("sirius")