-
Joseph Ramsay authored
HRPD decided they want a suffix parameter with the behaviour of the one recently implemented for POLARIS. The suffix-related was moved from POLARIS-specific scripts to abstract instrument in order to expose it to all instruments, should they want to use it
Joseph Ramsay authoredHRPD decided they want a suffix parameter with the behaviour of the one recently implemented for POLARIS. The suffix-related was moved from POLARIS-specific scripts to abstract instrument in order to expose it to all instruments, should they want to use it
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
run_details.py 9.48 KiB
from __future__ import (absolute_import, division, print_function)
from isis_powder.routines import common, yaml_parser
import os
def create_run_details_object(run_number_string, inst_settings, is_vanadium_run, empty_run_call=None,
grouping_file_name_call=None, vanadium_run_call=None,
splined_name_list=None, van_abs_file_name=None):
"""
Creates and returns a run details object which holds various
properties about the current run.
:param run_number_string: The user string for the current run
:param inst_settings: The current instrument object
:param is_vanadium_run: Boolean of if the current run is a vanadium run
:param empty_run_call: (Optional) Callable to setup custom empty run number(s) from mapping file
:param grouping_file_name_call: (Optional) Callable to setup custom grouping file name
:param vanadium_run_call: (Optional) Callable to setup custom van run number(s) from mapping file
:param splined_name_list: (Optional) List of unique properties to generate a splined vanadium name from
:param van_abs_file_name: (Optional) The name of the vanadium absorption file
:return: RunDetails object with attributes set to applicable values
"""
cal_map_dict = RunDetailsWrappedCommonFuncs.get_cal_mapping_dict(
run_number_string=run_number_string, inst_settings=inst_settings)
run_number = common.get_first_run_number(run_number_string=run_number_string)
# Get names of files we will be using
calibration_dir = os.path.normpath(os.path.expanduser(inst_settings.calibration_dir))
label = common.cal_map_dictionary_key_helper(dictionary=cal_map_dict, key="label")
offset_file_name = common.cal_map_dictionary_key_helper(dictionary=cal_map_dict, key="offset_file_name")
# Always make sure the offset file name is included
if splined_name_list:
# Force Python to make a copy so we don't modify original
new_splined_list = list(splined_name_list)
new_splined_list.append(offset_file_name)
else:
new_splined_list = [offset_file_name]
# These can either be generic or custom so defer to another method
results_dict = _get_customisable_attributes(
cal_dict=cal_map_dict, inst_settings=inst_settings, empty_run_call=empty_run_call,
grouping_name_call=grouping_file_name_call, vanadium_run_call=vanadium_run_call,
splined_name_list=new_splined_list)
vanadium_run_string = results_dict["vanadium_runs"]
if is_vanadium_run:
# The run number should be the vanadium number in this case
run_number = vanadium_run_string
output_run_string = vanadium_run_string
else:
# Otherwise set it to the user input
output_run_string = run_number_string
# Get the file extension if set
file_extension = getattr(inst_settings, "file_extension")
if file_extension:
# Prefix dot if user has forgotten to
file_extension = file_extension if file_extension.startswith('.') else '.' + file_extension
# Get the output name suffix if set
suffix = getattr(inst_settings, "suffix")
# Sample empty if there is one as this is instrument specific
sample_empty = getattr(inst_settings, "sample_empty", None)
# Generate the paths
grouping_file_path = os.path.join(calibration_dir, results_dict["grouping_file_name"])
# Offset and splined vanadium is within the correct label folder
offset_file_path = os.path.join(calibration_dir, label, offset_file_name)
splined_van_path = os.path.join(calibration_dir, label, results_dict["splined_van_name"])
unsplined_van_path = os.path.join(calibration_dir, label, results_dict["unsplined_van_name"])
van_absorb_path = os.path.join(calibration_dir, van_abs_file_name) if van_abs_file_name else None
return _RunDetails(empty_run_number=results_dict["empty_runs"], file_extension=file_extension,
run_number=run_number, output_run_string=output_run_string, label=label,
offset_file_path=offset_file_path, grouping_file_path=grouping_file_path,
splined_vanadium_path=splined_van_path, vanadium_run_number=vanadium_run_string,
sample_empty=sample_empty, vanadium_abs_path=van_absorb_path,
unsplined_vanadium_path=unsplined_van_path, output_suffix=suffix)
def _get_customisable_attributes(cal_dict, inst_settings, empty_run_call, grouping_name_call, vanadium_run_call,
splined_name_list):
dict_to_return = {}
if empty_run_call:
empty_runs = empty_run_call.get_result()
else:
empty_runs = common.cal_map_dictionary_key_helper(dictionary=cal_dict, key="empty_run_numbers")
dict_to_return["empty_runs"] = empty_runs
if vanadium_run_call:
vanadium_runs = vanadium_run_call.get_result()
else:
vanadium_runs = common.cal_map_dictionary_key_helper(dictionary=cal_dict, key="vanadium_run_numbers")
dict_to_return["vanadium_runs"] = vanadium_runs
if grouping_name_call:
grouping_name = grouping_name_call.get_result()
else:
grouping_name = inst_settings.grouping_file_name
dict_to_return["grouping_file_name"] = grouping_name
dict_to_return["splined_van_name"] = common.generate_splined_name(vanadium_runs, splined_name_list)
dict_to_return["unsplined_van_name"] = common.generate_unsplined_name(vanadium_runs, splined_name_list)
return dict_to_return
class RunDetailsWrappedCommonFuncs(object):
"""
Creates a compatible signature when using custom functions when
constructing RunDetails objects and calls the common methods underneath.
"""
@staticmethod
def get_cal_mapping_dict(run_number_string, inst_settings):
# Get the python dictionary from the YAML mapping
run_number = common.get_first_run_number(run_number_string=run_number_string)
cal_mapping_dict = yaml_parser.get_run_dictionary(run_number_string=run_number,
file_path=inst_settings.cal_mapping_path)
return cal_mapping_dict
@staticmethod
def cal_dictionary_key_helper(key, append_to_error_message=None, **kwargs):
dictionary = kwargs.pop("forwarded_value")
return common.cal_map_dictionary_key_helper(dictionary=dictionary, key=key,
append_to_error_message=append_to_error_message)
class CustomFuncForRunDetails(object):
# Holds a callable method, associated args and return value so we can pass it in
# as a single method
def __init__(self, user_function=None, *args, **func_kwargs):
if args:
# If we allow args with position Python gets confused as the forwarded value can be in the first place too
raise RuntimeError("Cannot use un-named arguments with callable methods")
self.user_function = user_function
self.function_kwargs = func_kwargs
self._previous_callable = None
self._returned_value = None
self._function_is_executed = False
def _exec_func(self):
forwarded_value = self._previous_callable.get_result() if self._previous_callable else None
if not self.user_function:
# We maybe are the 0th case just return any values we hold
return forwarded_value
if forwarded_value:
self.function_kwargs["forwarded_value"] = forwarded_value
self._returned_value = self.user_function(**self.function_kwargs)
else:
self._returned_value = self.user_function(**self.function_kwargs)
self._function_is_executed = True
def _set_previous_callable(self, previous_callable):
if not previous_callable:
return None
elif not isinstance(previous_callable, CustomFuncForRunDetails):
raise ValueError("Previous callable is not a CustomFuncForRunDetails type")
self._previous_callable = previous_callable
def add_to_func_chain(self, user_function, *args, **func_kwargs):
# Construct a new object that will be the next in line
next_in_chain = CustomFuncForRunDetails(user_function=user_function, *args, **func_kwargs)
next_in_chain._set_previous_callable(self)
return next_in_chain
def get_result(self):
if not self._function_is_executed:
self._exec_func()
return self._returned_value
class _RunDetails(object):
"""
This class holds the full file paths associated with each run and various other useful attributes
"""
def __init__(self, empty_run_number, file_extension, run_number, output_run_string, label,
offset_file_path, grouping_file_path, splined_vanadium_path, vanadium_run_number,
sample_empty, vanadium_abs_path, unsplined_vanadium_path, output_suffix):
# Essential attribute
self.empty_runs = empty_run_number
self.run_number = run_number
self.output_run_string = output_run_string
self.label = label
self.offset_file_path = offset_file_path
self.grouping_file_path = grouping_file_path
self.splined_vanadium_file_path = splined_vanadium_path
self.unsplined_vanadium_file_path = unsplined_vanadium_path
self.vanadium_run_numbers = vanadium_run_number
# Optional
self.file_extension = str(file_extension) if file_extension else None
self.sample_empty = sample_empty
self.vanadium_absorption_path = vanadium_abs_path
self.output_suffix = output_suffix