"""
RefRed template reader.
Adapted from Mantid code.
"""
import time
import xml.dom.minidom
from . import __version__ as VERSION
# Get the mantid version being used, if available
try:
import mantid
MANTID_VERSION = mantid.__version__
except:
MANTID_VERSION = "None"
[docs]
class ReductionParameters(object):
def __init__(self):
# Signal selection
self.data_peak_range = [140, 150]
self.subtract_background = True
self.two_backgrounds: bool = False
self.background_roi = [137, 153, 0, 0]
self.tof_range = [9600., 21600.]
self.select_tof_range = True
self.data_x_range_flag = True
self.data_x_range = [115,210]
# Normalization
self.apply_normalization = True
self.norm_peak_range = [140, 150]
self.subtract_norm_background = True
self.norm_background_roi = [137, 153]
self.norm_x_range_flag = True
self.norm_x_range = [115,210]
# Data files
self.data_files = [0]
self.norm_file = 0
# Clean up options: cut first and last points as needed
self.pre_cut = 1
self.post_cut = 1
# Q range
self.q_min = 0.001
self.q_step = 0.001
self.auto_q_binning = False
# Scattering angle
self.tthd_value = 0
self.ths_value = 0
self.angle_offset = 0.0
self.angle_offset_error = 0.0
# Scaling factor file
self.scaling_factor_file = ''
self.scaling_factor_flag = True
self.slits_width_flag = True
# Incident medium list and selected value
self.incident_medium_list = ['air']
self.incident_medium_index_selected = 0
# Dead time correction
self.dead_time:bool = False
self.paralyzable:bool = True
self.dead_time_value = 4.2
self.dead_time_tof_step = 100
# Calculate emission time delay instead of using an effective distance for all wavelengths
self.use_emission_time:bool = True
[docs]
def from_dict(self, data_dict, permissible=True):
r"""
Update object's attributes with a dictionary with entries of the type attribute_name: attribute_value.
Parameters
----------
permissible: bool
allow keys in data_dict that are not attribute names of ReductionParameters instances. Reading from
`data_dict` will result in this instance having new attributes not defined in `__init__()`
Raises
------
ValueError
when `permissible=False` and one entry (or more) of the dictionary is not an attribute of this object
"""
# check all keys are data_dict are attributes of object `self`
attribute_names = list(vars(self))
if permissible is False and all(key in attribute_names for key in data_dict) is False:
raise ValueError("data_dir contains invalid entries")
# update attribute values
for k, v in data_dict.items():
setattr(self, k, v)
[docs]
def to_xml(self):
"""
Create XML from the current data.
"""
_xml = "<RefLData>\n"
_xml += "<peak_selection_type>narrow</peak_selection_type>\n"
_xml += "<from_peak_pixels>%s</from_peak_pixels>\n" % str(self.data_peak_range[0])
_xml += "<to_peak_pixels>%s</to_peak_pixels>\n" % str(self.data_peak_range[1])
_xml += "<peak_discrete_selection>N/A</peak_discrete_selection>\n"
_xml += "<background_flag>%s</background_flag>\n" % str(self.subtract_background)
_xml += "<two_backgrounds_flag>%s</two_backgrounds_flag>\n" % str(self.two_backgrounds)
_xml += "<back_roi1_from>%s</back_roi1_from>\n" % str(self.background_roi[0])
_xml += "<back_roi1_to>%s</back_roi1_to>\n" % str(self.background_roi[1])
_xml += "<back_roi2_from>%s</back_roi2_from>\n" % str(self.background_roi[2])
_xml += "<back_roi2_to>%s</back_roi2_to>\n" % str(self.background_roi[3])
_xml += "<tof_range_flag>%s</tof_range_flag>\n" % str(self.select_tof_range)
_xml += "<from_tof_range>%s</from_tof_range>\n" % str(self.tof_range[0])
_xml += "<to_tof_range>%s</to_tof_range>\n" % str(self.tof_range[1])
_xml += "<data_sets>%s</data_sets>\n" % ','.join([str(i) for i in self.data_files])
_xml += "<x_min_pixel>%s</x_min_pixel>\n" % str(self.data_x_range[0])
_xml += "<x_max_pixel>%s</x_max_pixel>\n" % str(self.data_x_range[1])
_xml += "<x_range_flag>%s</x_range_flag>\n" % str(self.data_x_range_flag)
_xml += "<tthd_value>%s</tthd_value>\n" % str(self.tthd_value)
_xml += "<ths_value>%s</ths_value>\n" % str(self.ths_value)
_xml += "<norm_flag>%s</norm_flag>\n" % str(self.apply_normalization)
_xml += "<norm_x_range_flag>%s</norm_x_range_flag>\n" % str(self.norm_x_range_flag)
_xml += "<norm_x_max>%s</norm_x_max>\n" % str(self.norm_x_range[1])
_xml += "<norm_x_min>%s</norm_x_min>\n" % str(self.norm_x_range[0])
_xml += "<norm_from_peak_pixels>%s</norm_from_peak_pixels>\n" % str(self.norm_peak_range[0])
_xml += "<norm_to_peak_pixels>%s</norm_to_peak_pixels>\n" % str(self.norm_peak_range[1])
_xml += "<norm_background_flag>%s</norm_background_flag>\n" % str(self.subtract_norm_background)
_xml += "<norm_from_back_pixels>%s</norm_from_back_pixels>\n" % str(self.norm_background_roi[0])
_xml += "<norm_to_back_pixels>%s</norm_to_back_pixels>\n" % str(self.norm_background_roi[1])
_xml += "<norm_dataset>%s</norm_dataset>\n" % str(self.norm_file)
# Q cut
_xml += "<pre_cut>%s</pre_cut>\n" % str(self.pre_cut)
_xml += "<post_cut>%s</post_cut>\n" % str(self.post_cut)
_xml += "<q_min>%s</q_min>\n" % str(self.q_min)
_xml += "<q_step>%s</q_step>\n" % str(self.q_step)
_xml += "<auto_q_binning>%s</auto_q_binning>\n" % str(self.auto_q_binning)
# Angle offset
_xml += "<angle_offset>%s</angle_offset>\n" % str(self.angle_offset)
_xml += "<angle_offset_error>%s</angle_offset_error>\n" % str(self.angle_offset_error)
# scaling factor file name
_xml += "<scaling_factor_flag>%s</scaling_factor_flag>\n" % str(self.scaling_factor_flag)
_xml += "<scaling_factor_file>%s</scaling_factor_file>\n" % str(self.scaling_factor_file)
_xml += "<slits_width_flag>%s</slits_width_flag>\n" % str(self.slits_width_flag)
# Incident medium
_xml += "<incident_medium_list>%s</incident_medium_list>\n" % str(self.incident_medium_list[0])
_xml += "<incident_medium_index_selected>%s</incident_medium_index_selected>\n" % str(self.incident_medium_index_selected)
# Dead time correction
_xml += "<dead_time_correction>%s</dead_time_correction>\n" % str(self.dead_time)
_xml += "<dead_time_paralyzable>%s</dead_time_paralyzable>\n" % str(self.paralyzable)
_xml += "<dead_time_value>%s</dead_time_value>\n" % str(self.dead_time_value)
_xml += "<dead_time_tof_step>%s</dead_time_tof_step>\n" % str(self.dead_time_tof_step)
# Emission time correction
_xml += "<use_emission_time>%s</use_emission_time>\n" % str(self.use_emission_time)
_xml += "</RefLData>\n"
return _xml
[docs]
def from_xml_element(self, instrument_dom):
"""
Read in data from XML
@param xml_str: text to read the data from
"""
#Peak from/to pixels
self.data_peak_range = [getIntElement(instrument_dom, "from_peak_pixels"),
getIntElement(instrument_dom, "to_peak_pixels")]
#data metadata
_tthd_value = getStringElement(instrument_dom, "tthd_value")
if _tthd_value == '':
_tthd_value = 'N/A'
self.tthd_value = _tthd_value
_ths_value = getStringElement(instrument_dom, "ths_value")
if _ths_value == '':
_ths_value = 'N/A'
self.ths_value = _ths_value
#low resolution range
self.data_x_range_flag = getBoolElement(instrument_dom, "x_range_flag",
default=self.data_x_range_flag)
self.data_x_range = [getIntElement(instrument_dom, "x_min_pixel"),
getIntElement(instrument_dom, "x_max_pixel")]
self.norm_x_range_flag = getBoolElement(instrument_dom, "norm_x_range_flag",
default=self.norm_x_range_flag)
self.norm_x_range = [getIntElement(instrument_dom, "norm_x_min"),
getIntElement(instrument_dom, "norm_x_max")]
# background flag
self.subtract_background = getBoolElement(instrument_dom, "background_flag",
default=self.subtract_background)
# use two backgrounds flag
self.two_backgrounds = getBoolElement(instrument_dom, "two_backgrounds_flag",
default=self.two_backgrounds)
# background from/to pixels
self.background_roi = [getIntElement(instrument_dom, "back_roi1_from"),
getIntElement(instrument_dom, "back_roi1_to"),
getIntElement(instrument_dom, "back_roi2_from"),
getIntElement(instrument_dom, "back_roi2_to")]
# TOF range
self.select_tof_range = getBoolElement(instrument_dom, "tof_range_flag",
default=self.select_tof_range)
self.tof_range = [getFloatElement(instrument_dom, "from_tof_range"),
getFloatElement(instrument_dom, "to_tof_range")]
self.data_files = getIntList(instrument_dom, "data_sets")
#with or without norm
self.apply_normalization = getBoolElement(instrument_dom, "norm_flag",
default=self.apply_normalization)
#Peak from/to pixels
self.norm_peak_range = [getIntElement(instrument_dom, "norm_from_peak_pixels"),
getIntElement(instrument_dom, "norm_to_peak_pixels")]
# Background subtraction option
self.subtract_norm_background = getBoolElement(instrument_dom, "norm_background_flag",
default=self.subtract_norm_background)
self.norm_background_roi = [getIntElement(instrument_dom, "norm_from_back_pixels"),
getIntElement(instrument_dom, "norm_to_back_pixels")]
self.norm_file = getIntElement(instrument_dom, "norm_dataset")
# Q cut
self.pre_cut = getIntElement(instrument_dom, "pre_cut", default=self.pre_cut)
self.post_cut = getIntElement(instrument_dom, "post_cut", default=self.post_cut)
self.q_min = getFloatElement(instrument_dom, "q_min", default=self.q_min)
self.q_step = getFloatElement(instrument_dom, "q_step", default=self.q_step)
self.auto_q_binning = getBoolElement(instrument_dom, "auto_q_binning", default=False)
# Angle offset
self.angle_offset = getFloatElement(instrument_dom, "angle_offset", default=self.angle_offset)
self.angle_offset_error = getFloatElement(instrument_dom, "angle_offset_error",
default=self.angle_offset_error)
# Scaling factor file and options
self.scaling_factor_file = getStringElement(instrument_dom, "scaling_factor_file")
self.slits_width_flag = getBoolElement(instrument_dom, "slits_width_flag")
self.scaling_factor_flag = getBoolElement(instrument_dom, "scaling_factor_flag")
# Incident medium selected
if getStringList(instrument_dom, "incident_medium_list") != []:
self.incident_medium_list = getStringList(instrument_dom, "incident_medium_list")
self.incident_medium_index_selected = getIntElement(instrument_dom, "incident_medium_index_selected")
else:
self.incident_medium_list = ['H2O']
self.incident_medium_index_selected = 0
# Dead time correction
self.dead_time = getBoolElement(instrument_dom, "dead_time_correction",
default=self.dead_time)
self.paralyzable = getBoolElement(instrument_dom, "dead_time_paralyzable",
default=self.paralyzable)
self.dead_time_value = getFloatElement(instrument_dom, "dead_time_value",
default=self.dead_time_value)
self.dead_time_tof_step = getFloatElement(instrument_dom, "dead_time_tof_step",
default=self.dead_time_tof_step)
# Emission time
# Defaults to True, but will be skipped if the necessary meta data is not found
self.use_emission_time = getBoolElement(instrument_dom, "use_emission_time",
default=True)
###### Utility functions to read XML content ########################
[docs]
def getText(nodelist):
"""
Utility method to extract text out of an XML node
"""
rc = ""
for node in nodelist:
if node.nodeType == node.TEXT_NODE:
rc = rc + node.data
return rc
[docs]
def getContent(dom, tag):
element_list = dom.getElementsByTagName(tag)
return getText(element_list[0].childNodes) if len(element_list) > 0 else None
[docs]
def getIntElement(dom, tag, default=None):
value = getContent(dom, tag)
return int(value) if value is not None else default
[docs]
def getIntList(dom, tag, default=[]):
value = getContent(dom, tag)
if value is not None and len(value.strip()) > 0:
return list(map(int, value.split(',')))
else:
return default
[docs]
def getFloatElement(dom, tag, default=None):
value = getContent(dom, tag)
return float(value) if value is not None else default
[docs]
def getFloatList(dom, tag, default=[]):
value = getContent(dom, tag)
if value is not None and len(value.strip()) > 0:
return list(map(float, value.split(',')))
else:
return default
[docs]
def getStringElement(dom, tag, default=''):
value = getContent(dom, tag)
return value if value is not None else default
[docs]
def getStringList(dom, tag, _default=[]):
elem_list = []
element_list = dom.getElementsByTagName(tag)
if len(element_list) > 0:
for l in element_list:
elem_list.append(getText(l.childNodes).strip())
return elem_list
[docs]
def getBoolElement(dom, tag, true_tag='true', default=False):
value = getContent(dom, tag)
return value.lower() == true_tag.lower() if value is not None else default
###### Functions to read/write a template file ######################
[docs]
def to_xml(data_sets):
"""
Create XML from the current data.
"""
_xml = "<Reduction>\n"
_xml += " <instrument_name>REFL</instrument_name>\n"
_xml += " <timestamp>%s</timestamp>\n" % time.ctime()
_xml += " <version>%s</version>\n" % VERSION
_xml += " <mantid_version>%s</mantid_version>\n" % MANTID_VERSION
_xml += " <generator>lr_reduction-%s</generator>\n" % VERSION
_xml += "<DataSeries>\n"
for item in data_sets:
_xml += item.to_xml()
_xml += "</DataSeries>\n"
_xml += "</Reduction>\n"
return _xml
[docs]
def from_xml(xml_str):
"""
Read in data from XML string
"""
data_sets = []
dom = xml.dom.minidom.parseString(xml_str)
element_list = dom.getElementsByTagName("Data")
if len(element_list)==0:
element_list = dom.getElementsByTagName("RefLData")
if len(element_list)>0:
for item in element_list:
if item is not None:
data_set = ReductionParameters()
data_set.from_xml_element(item)
data_sets.append(data_set)
if len(data_sets) == 0:
data_sets = [ReductionParameters()]
return data_sets