Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
231 changes: 150 additions & 81 deletions emuloop.py

Large diffs are not rendered by default.

226 changes: 143 additions & 83 deletions emulooptime.py

Large diffs are not rendered by default.

49 changes: 44 additions & 5 deletions example_loop.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,43 @@
from typing import Generator, Optional

import numpy as np
from genie_python import genie as g
from genie_python.genie_script_generator import ScriptDefinition, cast_parameters_to


def inclusive_float_range_with_step_flip(start: float, stop: float, step: float) -> Generator:
"""
If we are counting downwards from start to stop automatically flips step to be negative.
Inclusive of stop. Only tested for float values.

Parameters:
start (float): the value to start the range from
stop (float): the value to stop the range at
step (float): the steps to take from start to stop

Returns:
The range from start to stop including all steps in between.

Examples:
>>> inclusive_float_range_with_step_flip(0.5, 2, 0.5) == [0.5, 1, 1.5, 2]
>>> inclusive_float_range_with_step_flip(2, 0.5, 0.5) == [2, 1.5, 1, 0.5]
"""
# Get the modulo so we know to stop early like arrange if the steps don't fit evenly.
modulo = abs(stop - start) % abs(step)
if stop > start:
vstop = stop - modulo
else:
vstop = stop + modulo
for i in np.linspace(start, vstop, int(abs(vstop - start) / abs(step)) + 1):
if ((i >= start) and (i <= stop)) or (
(i >= stop) and (i <= start)
): # Check inserted here to ensure scan remains within defined range
yield i


class DoRun(ScriptDefinition):
@cast_parameters_to(start_temp=float, stop_temp=float, step_temp=float)
def run(self, start_temp=1.0, stop_temp=1.0, step_temp=0.5):
def run(self, start_temp: float = 1.0, stop_temp: float = 1.0, step_temp: float = 0.5) -> None:
# Execute the loop once
if start_temp == stop_temp:
step_temp = 1.0
Expand All @@ -16,30 +48,37 @@ def run(self, start_temp=1.0, stop_temp=1.0, step_temp=0.5):
else:
stop_temp -= small_amount
# Regular range can't use floats
for temp in np.arange(start_temp, stop_temp, step_temp):
for temp in inclusive_float_range_with_step_flip(start_temp, stop_temp, step_temp):
g.cset("temperature", temp)
g.begin(quiet=True)
g.waitfor_time(seconds=30)
g.end(quiet=True)

@cast_parameters_to(start_temp=float, stop_temp=float, step_temp=float)
def parameters_valid(self, start_temp=1.0, stop_temp=1.0, step_temp=0.5):
def parameters_valid(
self, start_temp: float = 1.0, stop_temp: float = 1.0, step_temp: float = 0.5
) -> Optional[str]:
errors = ""
if start_temp == 0 or stop_temp == 0:
errors += "Cannot go to zero kelvin\n"
if start_temp < stop_temp and step_temp < 0.0:
errors += "Stepping backwards when stop temp is higher than start temp\n"
elif start_temp > stop_temp and step_temp > 0.0:
errors += "Stepping forward when stop temp is lower than start temp\n"
if errors != "":
return errors
return None

@cast_parameters_to(start_temp=float, stop_temp=float, step_temp=float)
def estimate_time(self, start_temp=1.0, stop_temp=1.0, step_temp=0.5):
def estimate_time(
self, start_temp: float = 1.0, stop_temp: float = 1.0, step_temp: float = 0.5
) -> int:
if stop_temp >= start_temp:
steps = round((stop_temp - start_temp) / step_temp)
estimated_time = 30 + steps * 30
return estimated_time
else:
return 0

def get_help(self):
def get_help(self) -> str:
return "An example config to show a looping mechanism"
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
genie_python>=15.1.0-rc1
8 changes: 4 additions & 4 deletions test_emu/test_casters.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import unittest

import numpy as np
from emu import float_or_keep as float_or_keep_emu
from emu import magnet_device_type as magnet_device_type_emu
from emu import magnet_devices as magnet_devices_emu
from hamcrest import assert_that, calling, raises

from emuloop import cast_custom_expression as cast_custom_expression_emuloop
from emuloop import float_or_keep as float_or_keep_emuloop
from emuloop import magnet_device_type as magnet_device_type_emuloop
from emuloop import magnet_devices as magnet_devices_emuloop
from emulooptime import float_or_keep as float_or_keep_emu
from emulooptime import magnet_device_type as magnet_device_type_emu
from emulooptime import magnet_devices as magnet_devices_emu


class TestMagnetCaster(unittest.TestCase):
Expand Down Expand Up @@ -82,7 +82,7 @@ def test_GIVEN_keep_different_cases_WHEN_cast_THEN_return_none(self):
self.assertIsNone(float_or_keep_emuloop(keep.upper()))

def test_GIVEN_string_convertable_to_float_WHEN_cast_THEN_return_casted_value(self):
for float_val in np.arange(4.0, 5.0, 0.2):
for float_val in np.linspace(4.0, 5.0, int(abs(4.0 - 5.0) / 0.2) + 1):
self.assertEqual(float_or_keep_emu(str(float_val)), float_val)
self.assertEqual(float_or_keep_emuloop(str(float_val)), float_val)

Expand Down
49 changes: 0 additions & 49 deletions test_emu/test_emu_param_validation.py

This file was deleted.

24 changes: 12 additions & 12 deletions test_emu/test_emuloop_param_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ def setUp(self):
"start_temperature": "keep",
"stop_temperature": "keep",
"step_temperature": "1",
"start_field": "keep",
"stop_field": "keep",
"start_field": "1",
"stop_field": "1",
"step_field": "1",
"custom": "None",
"mevents": "10",
Expand All @@ -61,8 +61,8 @@ def setUp(self):
"start_temperature": "keep",
"stop_temperature": "1.0",
"step_temperature": "1",
"start_field": "keep",
"stop_field": "keep",
"start_field": "1",
"stop_field": "1",
"step_field": "1",
"custom": "None",
"mevents": "10",
Expand All @@ -75,8 +75,8 @@ def setUp(self):
"start_temperature": "1.0",
"stop_temperature": "keep",
"step_temperature": "1",
"start_field": "keep",
"stop_field": "keep",
"start_field": "1",
"stop_field": "1",
"step_field": "1",
"custom": "None",
"mevents": "10",
Expand All @@ -89,12 +89,12 @@ def setUp(self):
"start_temperature": "keep",
"stop_temperature": "keep",
"step_temperature": "1",
"start_field": "keep",
"stop_field": "1.0",
"start_field": "1",
"stop_field": "keep",
"step_field": "1",
"custom": "None",
"mevents": "10",
"magnet_device": "TF",
"magnet_device": "N/A",
},
start_stop_must_both_be_keep_error_message.format("field"),
),
Expand All @@ -103,12 +103,12 @@ def setUp(self):
"start_temperature": "keep",
"stop_temperature": "keep",
"step_temperature": "1",
"start_field": "1.0",
"stop_field": "keep",
"start_field": "keep",
"stop_field": "1",
"step_field": "1",
"custom": "None",
"mevents": "10",
"magnet_device": "TF",
"magnet_device": "N/A",
},
start_stop_must_both_be_keep_error_message.format("field"),
),
Expand Down
33 changes: 33 additions & 0 deletions test_emu/test_emuloop_range.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,36 @@ def test_GIVEN_inverted_float_range_WHEN_get_range_THEN_inclusive_AND_expected_v
returned_range.append(i)
# THEN inclusive AND expected values
self.assertEqual([2, 1.5, 1, 0.5], returned_range)

def test_GIVEN_float_range_AND_difference_not_divisible_by_step_WHEN_get_range_matches_arrange(
self,
):
start, stop, step = 1, 2, 0.3
# WHEN get range
returned_range = []
for i in inclusive_float_range_with_step_flip(start, stop, step):
returned_range.append(i)
# THEN inclusive AND expected values
self.assertEqual([1, 1.3, 1.6, 1.9], returned_range)

def test_GIVEN_inverted_float_range_AND_difference_not_divisible_by_step_WHEN_get_range_matches_arrange(
self,
):
start, stop, step = 2, 1, 0.3
# WHEN get range
returned_range = []
for i in inclusive_float_range_with_step_flip(start, stop, step):
returned_range.append(i)
# THEN inclusive AND expected values
self.assertEqual([2, 1.7, 1.4, 1.1], returned_range)

def test_GIVEN_inverted_float_range_AND_difference_not_divisible_by_step_AND_negative_step_WHEN_get_range_matches_arrange(
self,
):
start, stop, step = 2, 1, -0.3
# WHEN get range
returned_range = []
for i in inclusive_float_range_with_step_flip(start, stop, step):
returned_range.append(i)
# THEN inclusive AND expected values
self.assertEqual([2, 1.7, 1.4, 1.1], returned_range)
31 changes: 11 additions & 20 deletions test_emu/test_emuloop_run.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
class TestRun(unittest.TestCase):
def setUp(self):
self.script_definition = DoRun()
self.script_definition.check_mevents_and_begin_waitfor_mevents_end = MagicMock()
self.check_mevents_mock = MagicMock()
self.script_definition.check_mevents_and_begin_waitfor_mevents_end = self.check_mevents_mock
inst.reset_mock()

@patch.dict("sys.modules", inst=inst)
Expand All @@ -34,9 +35,7 @@ def test_GIVEN_both_temp_field_scans_WHEN_run_THEN_scans_run_once_for_each_set(
inst.lf0.assert_called_once()
self.assertEqual(inst.settemp.call_count, 10)
self.assertEqual(inst.setmag.call_count, 10 * 10)
self.assertEqual(
self.script_definition.check_mevents_and_begin_waitfor_mevents_end.call_count, 10 * 10
)
self.assertEqual(self.check_mevents_mock.call_count, 10 * 10)

@patch.dict("sys.modules", inst=inst)
@patch("six.moves.builtins.eval")
Expand All @@ -59,9 +58,7 @@ def test_GIVEN_temp_scan_field_point_WHEN_run_THEN_setmag_called_once_AND_scan_r
inst.lf0.assert_called_once()
self.assertEqual(inst.settemp.call_count, 10)
inst.setmag.assert_called_once()
self.assertEqual(
self.script_definition.check_mevents_and_begin_waitfor_mevents_end.call_count, 10
)
self.assertEqual(self.check_mevents_mock.call_count, 10)

@patch.dict("sys.modules", inst=inst)
@patch("six.moves.builtins.eval")
Expand All @@ -84,9 +81,7 @@ def test_GIVEN_field_scan_temp_point_WHEN_run_THEN_settemp_called_once_AND_scan_
inst.tf0.assert_called_once()
inst.settemp.assert_called_once()
self.assertEqual(inst.setmag.call_count, 10)
self.assertEqual(
self.script_definition.check_mevents_and_begin_waitfor_mevents_end.call_count, 10
)
self.assertEqual(self.check_mevents_mock.call_count, 10)

@patch.dict("sys.modules", inst=inst)
@patch("six.moves.builtins.eval")
Expand All @@ -109,7 +104,7 @@ def test_GIVEN_both_field_and_temp_points_WHEN_run_THEN_temp_mag_run_called_once
inst.tf0.assert_called_once()
inst.settemp.assert_called_once()
inst.setmag.assert_called_once()
self.script_definition.check_mevents_and_begin_waitfor_mevents_end.assert_called_once()
self.check_mevents_mock.assert_called_once()

@patch.dict("sys.modules", inst=inst)
@patch("six.moves.builtins.eval")
Expand All @@ -132,9 +127,7 @@ def test_GIVEN_one_of_start_stop_field_is_keep_AND_temp_scan_WHEN_run_THEN_setma
inst.tf0.assert_not_called()
self.assertEqual(inst.settemp.call_count, 10)
inst.setmag.assert_not_called()
self.assertEqual(
self.script_definition.check_mevents_and_begin_waitfor_mevents_end.call_count, 10
)
self.assertEqual(self.check_mevents_mock.call_count, 10)

@patch.dict("sys.modules", inst=inst)
@patch("six.moves.builtins.eval")
Expand All @@ -157,7 +150,7 @@ def test_GIVEN_one_of_start_stop_field_is_keep_AND_temp_point_WHEN_run_THEN_setm
inst.tf0.assert_not_called()
inst.settemp.assert_called_once()
inst.setmag.assert_not_called()
self.script_definition.check_mevents_and_begin_waitfor_mevents_end.assert_called_once()
self.check_mevents_mock.assert_called_once()

@patch.dict("sys.modules", inst=inst)
@patch("six.moves.builtins.eval")
Expand All @@ -180,9 +173,7 @@ def test_GIVEN_one_of_start_stop_temp_is_keep_AND_field_scan_WHEN_run_THEN_sette
inst.tf0.assert_called_once()
self.assertEqual(inst.setmag.call_count, 10)
inst.settemp.assert_not_called()
self.assertEqual(
self.script_definition.check_mevents_and_begin_waitfor_mevents_end.call_count, 10
)
self.assertEqual(self.check_mevents_mock.call_count, 10)

@patch.dict("sys.modules", inst=inst)
@patch("six.moves.builtins.eval")
Expand All @@ -205,7 +196,7 @@ def test_GIVEN_one_of_start_stop_temp_is_keep_AND_field_point_WHEN_run_THEN_sett
inst.tf0.assert_called_once()
inst.setmag.assert_called_once()
inst.settemp.assert_not_called()
self.script_definition.check_mevents_and_begin_waitfor_mevents_end.assert_called_once()
self.check_mevents_mock.assert_called_once()

@patch.dict("sys.modules", inst=inst)
@patch("six.moves.builtins.eval")
Expand All @@ -228,7 +219,7 @@ def test_GIVEN_keep_temp_and_field_WHEN_run_THEN_settemp_and_setmag_not_called_A
inst.tf0.assert_not_called()
inst.setmag.assert_not_called()
inst.settemp.assert_not_called()
self.script_definition.check_mevents_and_begin_waitfor_mevents_end.assert_called_once()
self.check_mevents_mock.assert_called_once()


class TestEmuRunHelpers(unittest.TestCase):
Expand Down
Loading