[80c5d46] | 1 | """ |
---|
| 2 | CanSAS 2D data reader for reading HDF5 formatted CanSAS files. |
---|
| 3 | """ |
---|
| 4 | |
---|
| 5 | import numpy as np |
---|
| 6 | import re |
---|
| 7 | import os |
---|
| 8 | import sys |
---|
| 9 | |
---|
| 10 | from sas.sascalc.dataloader.readers.xml_reader import XMLreader |
---|
[fafe52a] | 11 | from sas.sascalc.dataloader.data_info import plottable_1D, Data1D, DataInfo, Sample, Source |
---|
[80c5d46] | 12 | from sas.sascalc.dataloader.data_info import Process, Aperture, Collimation, TransmissionSpectrum, Detector |
---|
[fafe52a] | 13 | from sas.sascalc.dataloader.loader_exceptions import FileContentsException, DataReaderException |
---|
[80c5d46] | 14 | |
---|
| 15 | class Reader(XMLreader): |
---|
| 16 | """ |
---|
[fafe52a] | 17 | A class for reading in Anton Paar .pdh files |
---|
[80c5d46] | 18 | """ |
---|
| 19 | |
---|
| 20 | ## Logged warnings or messages |
---|
| 21 | logging = None |
---|
| 22 | ## List of errors for the current data set |
---|
| 23 | errors = None |
---|
| 24 | ## Raw file contents to be processed |
---|
| 25 | raw_data = None |
---|
| 26 | ## For recursion and saving purposes, remember parent objects |
---|
| 27 | parent_list = None |
---|
| 28 | ## Data type name |
---|
| 29 | type_name = "Anton Paar SAXSess" |
---|
| 30 | ## Wildcards |
---|
| 31 | type = ["Anton Paar SAXSess Files (*.pdh)|*.pdh"] |
---|
| 32 | ## List of allowed extensions |
---|
| 33 | ext = ['.pdh', '.PDH'] |
---|
| 34 | ## Flag to bypass extension check |
---|
| 35 | allow_all = False |
---|
| 36 | |
---|
[a235f715] | 37 | def reset_state(self): |
---|
[fafe52a] | 38 | self.current_dataset = plottable_1D(np.empty(0), np.empty(0), np.empty(0), np.empty(0)) |
---|
| 39 | self.current_datainfo = DataInfo() |
---|
[80c5d46] | 40 | self.datasets = [] |
---|
| 41 | self.raw_data = None |
---|
| 42 | self.errors = set() |
---|
| 43 | self.logging = [] |
---|
| 44 | self.output = [] |
---|
| 45 | self.detector = Detector() |
---|
| 46 | self.collimation = Collimation() |
---|
| 47 | self.aperture = Aperture() |
---|
| 48 | self.process = Process() |
---|
| 49 | self.source = Source() |
---|
| 50 | self.sample = Sample() |
---|
| 51 | self.trans_spectrum = TransmissionSpectrum() |
---|
| 52 | self.upper = 5 |
---|
| 53 | self.lower = 5 |
---|
| 54 | |
---|
[fafe52a] | 55 | def get_file_contents(self): |
---|
[80c5d46] | 56 | """ |
---|
| 57 | This is the general read method that all SasView data_loaders must have. |
---|
| 58 | |
---|
| 59 | :param filename: A path for an XML formatted Anton Paar SAXSess data file. |
---|
| 60 | :return: List of Data1D objects or a list of errors. |
---|
| 61 | """ |
---|
| 62 | |
---|
| 63 | ## Reinitialize the class when loading a new data file to reset all class variables |
---|
[a235f715] | 64 | self.reset_state() |
---|
[a5bd87a] | 65 | buff = self.readall() |
---|
[fafe52a] | 66 | self.raw_data = buff.splitlines() |
---|
| 67 | self.read_data() |
---|
[80c5d46] | 68 | |
---|
| 69 | def read_data(self): |
---|
[fafe52a] | 70 | correctly_loaded = True |
---|
| 71 | error_message = "" |
---|
| 72 | |
---|
[80c5d46] | 73 | q_unit = "1/nm" |
---|
| 74 | i_unit = "1/um^2" |
---|
[fafe52a] | 75 | try: |
---|
| 76 | self.current_datainfo.title = self.raw_data[0] |
---|
| 77 | self.current_datainfo.meta_data["Keywords"] = self.raw_data[1] |
---|
| 78 | line3 = self.raw_data[2].split() |
---|
| 79 | line4 = self.raw_data[3].split() |
---|
| 80 | line5 = self.raw_data[4].split() |
---|
| 81 | self.data_points = int(line3[0]) |
---|
| 82 | self.lower = 5 |
---|
| 83 | self.upper = self.lower + self.data_points |
---|
| 84 | self.source.radiation = 'x-ray' |
---|
| 85 | normal = float(line4[3]) |
---|
| 86 | self.current_datainfo.source.radiation = "x-ray" |
---|
| 87 | self.current_datainfo.source.name = "Anton Paar SAXSess Instrument" |
---|
| 88 | self.current_datainfo.source.wavelength = float(line4[4]) |
---|
| 89 | xvals = [] |
---|
| 90 | yvals = [] |
---|
| 91 | dyvals = [] |
---|
| 92 | for i in range(self.lower, self.upper): |
---|
| 93 | index = i - self.lower |
---|
| 94 | data = self.raw_data[i].split() |
---|
| 95 | xvals.insert(index, normal * float(data[0])) |
---|
| 96 | yvals.insert(index, normal * float(data[1])) |
---|
| 97 | dyvals.insert(index, normal * float(data[2])) |
---|
| 98 | except Exception as e: |
---|
| 99 | error_message = "Couldn't load {}.\n".format(self.f_open.name) |
---|
| 100 | error_message += e.message |
---|
| 101 | raise FileContentsException(error_message) |
---|
[a235f715] | 102 | self.current_dataset.x = np.append(self.current_dataset.x, xvals) |
---|
| 103 | self.current_dataset.y = np.append(self.current_dataset.y, yvals) |
---|
| 104 | self.current_dataset.dy = np.append(self.current_dataset.dy, dyvals) |
---|
| 105 | if self.data_points != self.current_dataset.x.size: |
---|
[fafe52a] | 106 | error_message += "Not all data points could be loaded.\n" |
---|
| 107 | correctly_loaded = False |
---|
[a235f715] | 108 | if self.current_dataset.x.size != self.current_dataset.y.size: |
---|
[fafe52a] | 109 | error_message += "The x and y data sets are not the same size.\n" |
---|
| 110 | correctly_loaded = False |
---|
[a235f715] | 111 | if self.current_dataset.y.size != self.current_dataset.dy.size: |
---|
[fafe52a] | 112 | error_message += "The y and dy datasets are not the same size.\n" |
---|
| 113 | correctly_loaded = False |
---|
| 114 | |
---|
[a235f715] | 115 | self.current_dataset.xaxis("Q", q_unit) |
---|
| 116 | self.current_dataset.yaxis("Intensity", i_unit) |
---|
| 117 | xml_intermediate = self.raw_data[self.upper:] |
---|
| 118 | xml = ''.join(xml_intermediate) |
---|
[fafe52a] | 119 | try: |
---|
| 120 | self.set_xml_string(xml) |
---|
| 121 | dom = self.xmlroot.xpath('/fileinfo') |
---|
| 122 | self._parse_child(dom) |
---|
| 123 | except Exception as e: |
---|
| 124 | # Data loaded but XML metadata has an error |
---|
| 125 | error_message += "Data points have been loaded but there was an " |
---|
| 126 | error_message += "error reading XML metadata: " + e.message |
---|
| 127 | correctly_loaded = False |
---|
| 128 | self.send_to_output() |
---|
| 129 | if not correctly_loaded: |
---|
| 130 | raise DataReaderException(error_message) |
---|
[a235f715] | 131 | |
---|
| 132 | def _parse_child(self, dom, parent=''): |
---|
| 133 | """ |
---|
| 134 | Recursive method for stepping through the embedded XML |
---|
| 135 | :param dom: XML node with or without children |
---|
| 136 | """ |
---|
| 137 | for node in dom: |
---|
| 138 | tagname = node.tag |
---|
| 139 | value = node.text |
---|
| 140 | attr = node.attrib |
---|
| 141 | key = attr.get("key", '') |
---|
| 142 | if len(node.getchildren()) > 1: |
---|
| 143 | self._parse_child(node, key) |
---|
| 144 | if key == "SampleDetector": |
---|
[fafe52a] | 145 | self.current_datainfo.detector.append(self.detector) |
---|
[a235f715] | 146 | self.detector = Detector() |
---|
| 147 | else: |
---|
| 148 | if key == "value": |
---|
| 149 | if parent == "Wavelength": |
---|
[fafe52a] | 150 | self.current_datainfo.source.wavelength = value |
---|
[a235f715] | 151 | elif parent == "SampleDetector": |
---|
| 152 | self.detector.distance = value |
---|
| 153 | elif parent == "Temperature": |
---|
[fafe52a] | 154 | self.current_datainfo.sample.temperature = value |
---|
[a235f715] | 155 | elif parent == "CounterSlitLength": |
---|
| 156 | self.detector.slit_length = value |
---|
| 157 | elif key == "unit": |
---|
| 158 | value = value.replace("_", "") |
---|
| 159 | if parent == "Wavelength": |
---|
[fafe52a] | 160 | self.current_datainfo.source.wavelength_unit = value |
---|
[a235f715] | 161 | elif parent == "SampleDetector": |
---|
| 162 | self.detector.distance_unit = value |
---|
| 163 | elif parent == "X": |
---|
| 164 | self.current_dataset.xaxis(self.current_dataset._xaxis, value) |
---|
| 165 | elif parent == "Y": |
---|
| 166 | self.current_dataset.yaxis(self.current_dataset._yaxis, value) |
---|
| 167 | elif parent == "Temperature": |
---|
[fafe52a] | 168 | self.current_datainfo.sample.temperature_unit = value |
---|
[a235f715] | 169 | elif parent == "CounterSlitLength": |
---|
| 170 | self.detector.slit_length_unit = value |
---|
| 171 | elif key == "quantity": |
---|
| 172 | if parent == "X": |
---|
| 173 | self.current_dataset.xaxis(value, self.current_dataset._xunit) |
---|
| 174 | elif parent == "Y": |
---|
| 175 | self.current_dataset.yaxis(value, self.current_dataset._yunit) |
---|