Source code for sas.sascalc.dataloader.readers.anton_paar_saxs_reader

"""
    CanSAS 2D data reader for reading HDF5 formatted CanSAS files.
"""

import numpy as np
import re
import os
import sys

from sas.sascalc.dataloader.readers.xml_reader import XMLreader
from sas.sascalc.dataloader.data_info import plottable_1D, Data1D, Sample, Source
from sas.sascalc.dataloader.data_info import Process, Aperture, Collimation, TransmissionSpectrum, Detector


[docs]class Reader(XMLreader): """ A class for reading in CanSAS v2.0 data files. The existing iteration opens Mantid generated HDF5 formatted files with file extension .h5/.H5. Any number of data sets may be present within the file and any dimensionality of data may be used. Currently 1D and 2D SAS data sets are supported, but future implementations will include 1D and 2D SESANS data. This class assumes a single data set for each sasentry. :Dependencies: The CanSAS HDF5 reader requires h5py v2.5.0 or later. """ ## Logged warnings or messages logging = None ## List of errors for the current data set errors = None ## Raw file contents to be processed raw_data = None ## Data set being modified current_dataset = None ## For recursion and saving purposes, remember parent objects parent_list = None ## Data type name type_name = "Anton Paar SAXSess" ## Wildcards type = ["Anton Paar SAXSess Files (*.pdh)|*.pdh"] ## List of allowed extensions ext = ['.pdh', '.PDH'] ## Flag to bypass extension check allow_all = False ## List of files to return output = None
[docs] def reset_state(self): self.current_dataset = Data1D(np.empty(0), np.empty(0), np.empty(0), np.empty(0)) self.datasets = [] self.raw_data = None self.errors = set() self.logging = [] self.output = [] self.detector = Detector() self.collimation = Collimation() self.aperture = Aperture() self.process = Process() self.source = Source() self.sample = Sample() self.trans_spectrum = TransmissionSpectrum() self.upper = 5 self.lower = 5
[docs] def read(self, filename): """ This is the general read method that all SasView data_loaders must have. :param filename: A path for an XML formatted Anton Paar SAXSess data file. :return: List of Data1D objects or a list of errors. """ ## Reinitialize the class when loading a new data file to reset all class variables self.reset_state() ## Check that the file exists if os.path.isfile(filename): basename = os.path.basename(filename) _, extension = os.path.splitext(basename) # If the file type is not allowed, return empty list if extension in self.ext or self.allow_all: ## Load the data file input_f = open(filename, 'r') buff = input_f.read() self.raw_data = buff.splitlines() self.read_data() return self.output
[docs] def read_data(self): q_unit = "1/nm" i_unit = "1/um^2" self.current_dataset.title = self.raw_data[0] self.current_dataset.meta_data["Keywords"] = self.raw_data[1] line3 = self.raw_data[2].split() line4 = self.raw_data[3].split() line5 = self.raw_data[4].split() self.data_points = int(line3[0]) self.lower = 5 self.upper = self.lower + self.data_points self.source.radiation = 'x-ray' normal = float(line4[3]) self.current_dataset.source.radiation = "x-ray" self.current_dataset.source.name = "Anton Paar SAXSess Instrument" self.current_dataset.source.wavelength = float(line4[4]) xvals = [] yvals = [] dyvals = [] for i in range(self.lower, self.upper): index = i - self.lower data = self.raw_data[i].split() xvals.insert(index, normal * float(data[0])) yvals.insert(index, normal * float(data[1])) dyvals.insert(index, normal * float(data[2])) self.current_dataset.x = np.append(self.current_dataset.x, xvals) self.current_dataset.y = np.append(self.current_dataset.y, yvals) self.current_dataset.dy = np.append(self.current_dataset.dy, dyvals) if self.data_points != self.current_dataset.x.size: self.errors.add("Not all data was loaded properly.") if self.current_dataset.dx.size != self.current_dataset.x.size: dxvals = np.zeros(self.current_dataset.x.size) self.current_dataset.dx = dxvals if self.current_dataset.x.size != self.current_dataset.y.size: self.errors.add("The x and y data sets are not the same size.") if self.current_dataset.y.size != self.current_dataset.dy.size: self.errors.add("The y and dy datasets are not the same size.") self.current_dataset.errors = self.errors self.current_dataset.xaxis("Q", q_unit) self.current_dataset.yaxis("Intensity", i_unit) xml_intermediate = self.raw_data[self.upper:] xml = ''.join(xml_intermediate) self.set_xml_string(xml) dom = self.xmlroot.xpath('/fileinfo') self._parse_child(dom) self.output.append(self.current_dataset)
def _parse_child(self, dom, parent=''): """ Recursive method for stepping through the embedded XML :param dom: XML node with or without children """ for node in dom: tagname = node.tag value = node.text attr = node.attrib key = attr.get("key", '') if len(node.getchildren()) > 1: self._parse_child(node, key) if key == "SampleDetector": self.current_dataset.detector.append(self.detector) self.detector = Detector() else: if key == "value": if parent == "Wavelength": self.current_dataset.source.wavelength = value elif parent == "SampleDetector": self.detector.distance = value elif parent == "Temperature": self.current_dataset.sample.temperature = value elif parent == "CounterSlitLength": self.detector.slit_length = value elif key == "unit": value = value.replace("_", "") if parent == "Wavelength": self.current_dataset.source.wavelength_unit = value elif parent == "SampleDetector": self.detector.distance_unit = value elif parent == "X": self.current_dataset.xaxis(self.current_dataset._xaxis, value) elif parent == "Y": self.current_dataset.yaxis(self.current_dataset._yaxis, value) elif parent == "Temperature": self.current_dataset.sample.temperature_unit = value elif parent == "CounterSlitLength": self.detector.slit_length_unit = value elif key == "quantity": if parent == "X": self.current_dataset.xaxis(value, self.current_dataset._xunit) elif parent == "Y": self.current_dataset.yaxis(value, self.current_dataset._yunit)