Skip to content

Commit

Permalink
Fixes in unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
gmegh committed Aug 15, 2024
1 parent 8cadcf1 commit 20e007c
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 69 deletions.
39 changes: 25 additions & 14 deletions python/lsst/ts/externalscripts/base_parameter_march.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def __init__(self, index, descr="Perform a parameter march.") -> None:
self.ocps = None

self.config = None
self.dofs = np.zeros(50)

self.total_offset = 0.0
self.iterations_started = False
Expand Down Expand Up @@ -102,6 +103,16 @@ def get_schema(cls) -> dict:
description: Configuration for BaseParameterMarch.
type: object
properties:
az:
description: Azimuth position to point to.
type: number
minimum: 0
maximum: 360
el:
description: Elevation position to point to.
type: number
minimum: 0
maximum: 90
filter:
description: Filter name or ID; if omitted the filter is not changed.
anyOf:
Expand Down Expand Up @@ -174,11 +185,11 @@ def get_schema(cls) -> dict:
type: string
oneOf:
- required:
- dof
- dofs
- range
- n_steps
- required:
- dof
- dofs
- step_sequence
additionalProperties: false
"""
Expand Down Expand Up @@ -224,10 +235,9 @@ async def configure(self, config: types.SimpleNamespace) -> None:
config.range = config.step_sequence[-1] - config.step_sequence[0]
config.n_steps = len(config.step_sequence)
elif hasattr(config, "range"):
config.step_sequence = [
-config.range + 2 * i * config.range / (config.n_steps - 1)
for i in range(config.n_steps)
]
config.step_sequence = np.linspace(
-config.range, config.range, config.n_steps
).tolist()

if hasattr(config, "rotation_sequence"):
if isinstance(config.rotation_sequence, (int, float)):
Expand All @@ -242,6 +252,7 @@ async def configure(self, config: types.SimpleNamespace) -> None:
raise TypeError("rotation_sequence must be either a number or a list.")

self.config = config
self.dofs = np.array(config.dofs)

await super().configure(config=config)

Expand Down Expand Up @@ -324,8 +335,8 @@ async def parameter_march(self) -> None:

start_position = self.config.step_sequence[0]

offset_values = start_position * self.config.dofs
cam_hex, m2_hex, m1m3_bend, m2_bend = self.format_values(offset_values)
offset_values = start_position * self.dofs
cam_hex, m2_hex, m1m3_bend, m2_bend = await self.format_values(offset_values)

await self.checkpoint(
f"Step 1/{self.config.n_steps} starting positions:\n"
Expand All @@ -337,7 +348,7 @@ async def parameter_march(self) -> None:
self.log.info("Offset dofs to starting position.")

# Apply dof vector with offset
offset_dof_data = self.tcs.rem.mtaos.cmd_offsetDOF.DataType()
offset_dof_data = await self.tcs.rem.mtaos.cmd_offsetDOF.DataType()
for i, dof_offset in enumerate(offset_values):
offset_dof_data.value[i] = dof_offset
await self.tcs.rem.mtaos.cmd_offsetDOF.start(data=offset_dof_data)
Expand All @@ -361,8 +372,8 @@ async def parameter_march(self) -> None:
)

# Apply dof vector with offset
offset_dof_data = self.tcs.rem.mtaos.cmd_offsetDOF.DataType()
for i, dof_offset in enumerate(self.config.dofs * offset):
offset_dof_data = await self.tcs.rem.mtaos.cmd_offsetDOF.DataType()
for i, dof_offset in enumerate(self.dofs * offset):
offset_dof_data.value[i] = dof_offset
await self.tcs.rem.mtaos.cmd_offsetDOF.start(data=offset_dof_data)

Expand Down Expand Up @@ -421,11 +432,11 @@ async def cleanup(self):
f"Returning telescope to original position by moving "
f"{self.total_offset} back along dofs vector {self.config.dofs}."
)
offset_dof_data = self.tcs.rem.mtaos.cmd_offsetDOF.DataType()
for i, dof_offset in enumerate(self.config.dofs * -self.total_offset):
offset_dof_data = await self.tcs.rem.mtaos.cmd_offsetDOF.DataType()
for i, dof_offset in enumerate(self.dofs * -self.total_offset):
offset_dof_data.value[i] = dof_offset
await self.tcs.rem.mtaos.cmd_offsetDOF.start(data=offset_dof_data)
except Exception:
self.log.exception(
"Error while trying to return hexapod to its original position."
"Error while trying to return telescope to its original position."
)
15 changes: 9 additions & 6 deletions python/lsst/ts/externalscripts/maintel/parameter_march_comcam.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ def __init__(self, index, descr="Perform a parameter march with ComCam.") -> Non
self.mtcs = None
self.comcam = None

self.dz = 1500 # microns, offset for out-of-focus images

self.instrument_name = "LSSTComCam"

@property
Expand Down Expand Up @@ -95,13 +97,14 @@ async def take_images(

self.log.info("Taking intra-focal image")

print(self.config)
intra_visit_id = await self.camera.take_cwfs(
exptime=self.config.exp_time,
n=1,
group_id=supplemented_group_id,
filter=self.filter,
filter=self.config.filter,
reason="INTRA" + ("" if self.reason is None else f"_{self.reason}"),
program=self.program,
program=self.config.program,
)

self.log.debug("Moving to extra-focal position")
Expand All @@ -117,9 +120,9 @@ async def take_images(
exptime=self.config.exp_time,
n=1,
group_id=supplemented_group_id,
filter=self.filter,
filter=self.config.filter,
reason="EXTRA" + ("" if self.reason is None else f"_{self.reason}"),
program=self.program,
program=self.config.program,
)

self.log.info("Send processing request to RA OCPS.")
Expand All @@ -144,9 +147,9 @@ async def take_images(
exptime=self.config.exp_time,
n=1,
group_id=self.group_id,
filter=self.filter,
filter=self.config.filter,
reason="INFOCUS" + ("" if self.reason is None else f"_{self.reason}"),
program=self.program,
program=self.config.program,
)

try:
Expand Down
81 changes: 56 additions & 25 deletions tests/maintel/test_maintel_parameter_march_comcam.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ def mock_mtcs(self):
}
)

self.script.format_values = unittest.mock.AsyncMock()
self.script.format_values.return_value = (
[f"+{i*0.1:.2f} um" for i in range(5)],
[f"+{i*0.1:.2f} arcsec" for i in range(5, 10)],
[f"+{i*0.1:.2f} um" for i in range(10, 30)],
[f"+{i*0.1:.2f} um" for i in range(30, 50)],
)

def mock_camera(self):
"""Mock camera instance and its methods."""
self.script.comcam = mock.AsyncMock()
Expand All @@ -77,65 +85,68 @@ def mock_ocps(self):

async def test_configure(self):
config = {
"az": 35,
"el": 15,
"filter": "g",
"exp_time": 30.0,
"dofs": [1] * 50,
"rotation_sequence": 50,
"range": 1,
"n_steps": 11,
"program": "BLOCK-TXXX",
}

async with self.make_script():
await self.configure_script(**config)

assert self.script.config.az == 35
assert self.script.config.el == 15
assert self.script.config.filter == "g"
assert self.script.config.exp_time == 30.0
assert np.array_equal(self.script.config.dofs, [1] * 50)
assert self.script.config.rotation_sequence == [50] * 11
assert self.script.config.step_sequence == [
-1,
-0.8,
-0.6,
-0.4,
-0.2,
0,
0.2,
0.4,
0.6,
0.8,
1,
]
assert self.script.config.step_sequence == np.linspace(-1, 1, 11).tolist()
assert self.script.config.range == 1
assert self.script.config.n_steps == 11
assert self.script.config.program == "BLOCK-TXXX"

async def test_configure_step_and_rotation_sequence(self):
config = {
"az": 35,
"el": 15,
"filter": "g",
"exp_time": 30.0,
"dofs": [1] * 50,
"rotation_sequence": [10, 20, 30, 40, 50],
"step_sequence": [0, 100, 200, 300, 400],
"program": "BLOCK-TXXX",
}

async with self.make_script():
await self.configure_script(**config)

assert self.script.config.az == 35
assert self.script.config.el == 15
assert self.script.config.filter == "g"
assert self.script.config.exp_time == 30.0
assert np.array_equal(self.script.config.dofs, [1] * 50)
assert self.script.config.range == 400
assert self.script.config.n_steps == 5
assert self.script.config.step_sequence == [0, 100, 200, 300, 400]
assert self.script.config.rotation_sequence == [10, 20, 30, 40, 50]
assert self.script.config.program == "BLOCK-TXXX"

async def test_configure_ignore(self):
config = {
"az": 35,
"el": 15,
"filter": "g",
"exp_time": 30.0,
"dofs": [1] * 50,
"rotation_sequence": [10, 20, 30, 40, 50],
"step_sequence": [0, 100, 200, 300, 400],
"ignore": ["mtm1m3", "mtrotator"],
"program": "BLOCK-TXXX",
}

async with self.make_script():
Expand All @@ -145,13 +156,16 @@ async def test_configure_ignore(self):

await self.configure_script(**config)

assert self.script.config.az == 35
assert self.script.config.el == 15
assert self.script.config.filter == "g"
assert self.script.config.exp_time == 30.0
assert np.array_equal(self.script.config.dofs, [1] * 50)
assert self.script.config.range == 400
assert self.script.config.n_steps == 5
assert self.script.config.step_sequence == [0, 100, 200, 300, 400]
assert self.script.config.rotation_sequence == [10, 20, 30, 40, 50]
assert self.script.config.program == "BLOCK-TXXX"

# Verify that the ignored components are correctly set to False
assert not self.script.mtcs.check.mtm1m3
Expand All @@ -160,14 +174,18 @@ async def test_configure_ignore(self):
async def test_invalid_configuration(self):
bad_configs = [
{
"az": 35,
"el": 15,
"filter": "g",
"exp_time": 30.0,
"dofs": np.ones(51),
"dofs": [1] * 51,
"rotation_sequence": [10, 20, 30, 40, 50],
"step_sequence": [0, 100, 200, 300, 400],
"ignore": ["mtm1m3", "mtrotator"],
},
{
"az": 35,
"el": 15,
"filter": "g",
"exp_time": 30.0,
"dofs": [1] * 50,
Expand All @@ -184,11 +202,14 @@ async def test_invalid_configuration(self):

async def test_parameter_march(self):
config = {
"az": 35,
"el": 15,
"filter": "g",
"exp_time": 30.0,
"dofs": [1] * 50,
"rotation_sequence": [10, 20, 30, 40, 50],
"step_sequence": [0, 100, 200, 300, 400],
"program": "BLOCK-TXXX",
}

async with self.make_script():
Expand All @@ -198,23 +219,29 @@ async def test_parameter_march(self):
# Check if the hexapod moved the expected number of times
assert (
self.script.mtcs.rem.mtaos.cmd_offsetDOF.start.call_count
== config["n_steps"] + 1
== self.script.config.n_steps + 1
)

# Check if the camera took the expected number of images
assert self.script.comcam.take_acq.call_count == config["n_steps"]
assert self.script.comcam.take_acq.call_count == self.script.config.n_steps

# Check if the OCPS command was called
self.script.ocps.cmd_execute.set_start.assert_called_once()
assert (
self.script.ocps.cmd_execute.set_start.call_count
== self.script.config.n_steps
)

async def test_focus_sweep_sim_mode(self):
async def test_parameter_march_sim_mode(self):
config = {
"az": 35,
"el": 15,
"filter": "g",
"exp_time": 30.0,
"dofs": [1] * 50,
"rotation_sequence": [10, 20, 30, 40, 50],
"step_sequence": [0, 100, 200, 300, 400],
"sim": True,
"program": "BLOCK-TXXX",
}

async with self.make_script():
Expand All @@ -224,14 +251,17 @@ async def test_focus_sweep_sim_mode(self):
# Check if the hexapod moved the expected number of times
assert (
self.script.mtcs.rem.mtaos.cmd_offsetDOF.start.call_count
== config["n_steps"] + 1
== self.script.config.n_steps + 1
)

# Check if the camera took the expected number of images
assert self.script.comcam.take_acq.call_count == config["n_steps"]
assert self.script.comcam.take_acq.call_count == self.script.config.n_steps

# Check if the OCPS command was called
self.script.ocps.cmd_execute.set_start.assert_called_once()
assert (
self.script.ocps.cmd_execute.set_start.call_count
== self.script.config.n_steps
)

# Verify that simulation mode is set correctly
assert self.script.comcam.simulation_mode
Expand All @@ -243,12 +273,13 @@ async def test_cleanup(self):
"dofs": [1] * 50,
"rotation_sequence": [10, 20, 30, 40, 50],
"step_sequence": [0, 100, 200, 300, 400],
"program": "BLOCK-TXXX",
}

async with self.make_script():
await self.configure_script(**config)

# Simulate an error during the focus sweep to trigger cleanup
# Simulate an error during the parameter march to trigger cleanup
self.script.iterations_started = True
self.script.total_offset = 400 # Simulate some offset
with mock.patch.object(
Expand All @@ -260,13 +291,13 @@ async def test_cleanup(self):
await self.script.cleanup()

# Ensure the hexapod is returned to the original position
offset_dof_data = self.tcs.rem.mtaos.cmd_offsetDOF.DataType()
offset_dof_data = await self.script.mtcs.rem.mtaos.cmd_offsetDOF.DataType()
for i, dof_offset in enumerate(
self.script.config.dofs * -self.script.total_offset
self.script.dofs * -self.script.total_offset
):
offset_dof_data.value[i] = dof_offset
self.script.mtcs.rem.mtaos.cmd_offsetDOF.start.assert_any_call(
self.script.total_offset
data=offset_dof_data
)

async def test_executable(self):
Expand Down
Loading

0 comments on commit 20e007c

Please sign in to comment.