diff --git a/src/crystallography_bluesky/i15_1/plans/blower_collection.py b/src/crystallography_bluesky/i15_1/plans/blower_collection.py index 23a8398..34e7f1b 100644 --- a/src/crystallography_bluesky/i15_1/plans/blower_collection.py +++ b/src/crystallography_bluesky/i15_1/plans/blower_collection.py @@ -20,6 +20,7 @@ setup_zebra_for_hardware_triggering, ) from crystallography_bluesky.i15_1.plans.single_temperature_collection import ( + Q_MAX_OPTIONS, CollectionSpecPerTemp, TemperatureCollectionSpecification, get_collection_specification, @@ -58,6 +59,7 @@ def blower_collection( ramp_rate_c_per_min: float, settle_time: float, scan_type: ScanType, + q_max: Q_MAX_OPTIONS | None = None, generic_collection_devices: GenericCollectionDevices = devices, blower: Blower = blower, baseline_devices: list[StandardReadable] | None = None, @@ -69,7 +71,7 @@ def blower_collection( yield from bps.abs_set(blower.ramp_rate_c_per_sec, ramp_rate_c_per_min / 60) single_collection_spec, total_frames = get_collection_specification( - time_per_collection, exposure_time_per_frame + time_per_collection, exposure_time_per_frame, q_max ) minimum_dead_time = 0.0001 # Minimum value independent of timebase trigger_pulse_width = 0.0001 # Assumes zebra is set to seconds timebase diff --git a/src/crystallography_bluesky/i15_1/plans/single_temperature_collection.py b/src/crystallography_bluesky/i15_1/plans/single_temperature_collection.py index 692eb36..392bd6a 100644 --- a/src/crystallography_bluesky/i15_1/plans/single_temperature_collection.py +++ b/src/crystallography_bluesky/i15_1/plans/single_temperature_collection.py @@ -75,12 +75,15 @@ def no_temperature_must_not_have_units(self) -> Self: TemperatureCollectionSpecification: TypeAlias = list[CollectionSpecPerTemp] -COLLECTION_SPEC_FILEPATH = ( - "/dls_sw/i15-1/software/daq_configuration/collection_specification.txt" +BASE_COLLECTION_SPEC_FILEPATH = ( + "/dls_sw/i15-1/software/daq_configuration/" + + "collection_specifications/collection_specification" ) devices = inject("") +Q_MAX_OPTIONS = Literal[25, 30, 35, 40] + def _calculate_number_of_frames( fraction_of_time: float, @@ -90,8 +93,14 @@ def _calculate_number_of_frames( return ceil((fraction_of_time * full_collection_time) / exposure_time_per_frame) +def _get_collection_spec_filepath(q_max: Q_MAX_OPTIONS | None): + return BASE_COLLECTION_SPEC_FILEPATH + (f"_{str(q_max)}" if q_max else "") + ".txt" + + def get_collection_specification( - full_collection_time: float, exposure_time_per_frame: float + full_collection_time: float, + exposure_time_per_frame: float, + q_max: Q_MAX_OPTIONS | None = None, ) -> tuple[CollectionSpecification, int]: """The standard collection specification is defined in configuration but needs conversion: @@ -100,9 +109,11 @@ def get_collection_specification( * The transmission is just a float, we want to convert to one of the aperture options. """ + filepath = _get_collection_spec_filepath(q_max) + LOGGER.info(f"Using collection spec from {filepath}") config_client = get_config_client() collection_spec_from_config = config_client.get_file_contents( - COLLECTION_SPEC_FILEPATH, + filepath, CollectionSpecFromConfig, reset_cached_result=True, ).tth_angle_to_specification @@ -222,6 +233,7 @@ def data_collection( full_collection_time: float, exposure_time_per_frame: float, scan_type: DataCollectionScanType | AuxiliaryScanType, + q_max: Q_MAX_OPTIONS | None = None, generic_collection_devices: GenericCollectionDevices = devices, baseline_devices: list[StandardReadable] | None = None, metadata: dict[str, Any] | None = None, @@ -231,7 +243,7 @@ def data_collection( trigger_pulse_width = 0.0001 # Assumes zebra is set to seconds timebase collection_spec, total_frames = get_collection_specification( - full_collection_time, exposure_time_per_frame + full_collection_time, exposure_time_per_frame, q_max ) first_frames_value = next(iter(collection_spec)).frames diff --git a/tests/unit_tests/i15_1/test_single_temperature_collection.py b/tests/unit_tests/i15_1/test_single_temperature_collection.py index 143ab31..dd4f3a6 100644 --- a/tests/unit_tests/i15_1/test_single_temperature_collection.py +++ b/tests/unit_tests/i15_1/test_single_temperature_collection.py @@ -27,9 +27,11 @@ GenericCollectionDevices, ) from crystallography_bluesky.i15_1.plans.single_temperature_collection import ( - COLLECTION_SPEC_FILEPATH, + BASE_COLLECTION_SPEC_FILEPATH, + Q_MAX_OPTIONS, CollectionSpecPerPosition, _calculate_number_of_frames, + _get_collection_spec_filepath, data_collection, inner_collection, ) @@ -262,7 +264,9 @@ def test_data_collection_gets_positions_to_fraction_from_config_server( ) ) mock_collection_spec_config_client.get_file_contents.assert_called_once_with( - COLLECTION_SPEC_FILEPATH, CollectionSpecification, reset_cached_result=True + BASE_COLLECTION_SPEC_FILEPATH + ".txt", + CollectionSpecification, + reset_cached_result=True, ) @@ -431,3 +435,34 @@ async def test_if_software_read_faster_than_time_between_frames_then_sleep_calle assert not caplog.text assert mock_sleep.call_count == 10 assert mock_sleep.call_args_list == [call(pytest.approx(0.9)) for _ in range(10)] + + +@pytest.mark.parametrize( + "q_max, expected_filepath", + [ + ( + 25, + "/dls_sw/i15-1/software/daq_configuration/collection_specifications/collection_specification_25.txt", + ), + ( + 30, + "/dls_sw/i15-1/software/daq_configuration/collection_specifications/collection_specification_30.txt", + ), + ( + 35, + "/dls_sw/i15-1/software/daq_configuration/collection_specifications/collection_specification_35.txt", + ), + ( + 40, + "/dls_sw/i15-1/software/daq_configuration/collection_specifications/collection_specification_40.txt", + ), + ( + None, + "/dls_sw/i15-1/software/daq_configuration/collection_specifications/collection_specification.txt", + ), + ], +) +def test__get_collection_spec_filepath_returns_expected_filepath( + q_max: Q_MAX_OPTIONS | None, expected_filepath: str +): + assert _get_collection_spec_filepath(q_max) == expected_filepath