Skip to content

Commit

Permalink
Merge pull request #48 from lsst-ts/develop
Browse files Browse the repository at this point in the history
Merge develop into main
  • Loading branch information
jbkalmbach authored Aug 22, 2024
2 parents 5d897dc + 52a4a07 commit 730f6a4
Show file tree
Hide file tree
Showing 10 changed files with 127 additions and 14 deletions.
1 change: 1 addition & 0 deletions bin.src/img_closed_loop.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
19 changes: 19 additions & 0 deletions doc/versionHistory.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,25 @@
##################
Version History
##################
-------------
1.6.2
-------------

* Set group for images.
* Use group pairer for science images.

-------------
1.6.1
-------------

* Update to send sensor_ids instead of sensor_names to ts_ofc

-------------
1.6.0
-------------

* Update to use ts_ofc version 3.2.0.

-------------
1.5.6
-------------
Expand Down
30 changes: 30 additions & 0 deletions policy/config/controllers/oic_controller.yaml
Original file line number Diff line number Diff line change
@@ -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
]
28 changes: 28 additions & 0 deletions policy/config/controllers/pid_controller.yaml
Original file line number Diff line number Diff line change
@@ -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
]
56 changes: 43 additions & 13 deletions python/lsst/ts/imsim/closed_loop_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -446,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,
Expand Down Expand Up @@ -522,31 +530,28 @@ 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(),
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
Expand Down Expand Up @@ -635,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:
Expand Down Expand Up @@ -944,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(
Expand Down Expand Up @@ -989,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:
Expand All @@ -1015,6 +1035,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,
Expand Down Expand Up @@ -1059,6 +1080,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.
Expand Down Expand Up @@ -1108,7 +1131,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
)
Expand Down Expand Up @@ -1332,6 +1355,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,
Expand Down
1 change: 1 addition & 0 deletions python/lsst/ts/imsim/imsim_cmpt.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions python/lsst/ts/imsim/obs_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions tests/testData/imsimConfig/imsimConfigLsstCam.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ output:
rotTelPos: 0.0
seqnum: 1
seeing: *rawSeeing
group: 1

camera: LsstCam

Expand Down
3 changes: 2 additions & 1 deletion tests/test_closed_loop_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions tests/test_obs_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down

0 comments on commit 730f6a4

Please sign in to comment.