source: sasview/src/sas/sascalc/dataloader/readers/sesans_reader.py @ 8e0dcac

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.2.2ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 8e0dcac was 8e0dcac, checked in by Adam Washington <adam.washington@…>, 7 years ago

Handle missing columns in .ses format

The uncertainties on spin echo length and lambda aren't strictly
required, so we should handle files that do not have them. SasView?
seems to be unhappy with an uncertainty of zero, so we've put in a
small default value. If the user isn't happy with the default value,
then they should have measured the real one.

  • Property mode set to 100644
File size: 5.0 KB
Line 
1"""
2    SESANS reader (based on ASCII reader)
3
4    Reader for .ses or .sesans file format
5
6    Jurrian Bakker
7"""
8import logging
9import numpy as np
10import os
11from sas.sascalc.dataloader.data_info import Data1D
12
13# Check whether we have a converter available
14has_converter = True
15try:
16    from sas.sascalc.data_util.nxsunit import Converter
17except:
18    has_converter = False
19_ZERO = 1e-16
20
21
22class Reader:
23    """
24    Class to load sesans files (6 columns).
25    """
26    # File type
27    type_name = "SESANS"
28
29    # Wildcards
30    type = ["SESANS files (*.ses)|*.ses",
31            "SESANS files (*..sesans)|*.sesans"]
32    # List of allowed extensions
33    ext = ['.ses', '.SES', '.sesans', '.SESANS']
34
35    # Flag to bypass extension check
36    allow_all = True
37
38    def read(self, path):
39        """
40        Load data file
41
42        :param path: file path
43
44        :return: SESANSData1D object, or None
45
46        :raise RuntimeError: when the file can't be opened
47        :raise ValueError: when the length of the data vectors are inconsistent
48        """
49        if os.path.isfile(path):
50            basename = os.path.basename(path)
51            _, extension = os.path.splitext(basename)
52            if not (self.allow_all or extension.lower() in self.ext):
53                raise RuntimeError("{} has an unrecognized file extension".format(path))
54        else:
55            raise RunetimeError("{} is not a file".format(path))
56        with open(path, 'r') as input_f:
57            # Read in binary mode since GRASP frequently has no-ascii
58            # characters that brakes the open operation
59            line = input_f.readline()
60            params = {}
61            while not line.startswith("BEGIN_DATA"):
62                terms = line.split()
63                if len(terms) >= 2:
64                    params[terms[0]] = " ".join(terms[1:])
65                line = input_f.readline()
66            self.params = params
67            headers = input_f.readline().split()
68
69            data = np.loadtxt(input_f)
70            if data.size < 1:
71                raise RuntimeError("{} is empty".format(path))
72            x = data[:, headers.index("SpinEchoLength")]
73            if "SpinEchoLength_error" in headers:
74                dx = data[:, headers.index("SpinEchoLength_error")]
75            else:
76                dx = x*0.05
77            lam = data[:, headers.index("Wavelength")]
78            if "Wavelength_error" in headers:
79                dlam = data[:, headers.index("Wavelength_error")]
80            else:
81                dlam = lam*0.05
82            y = data[:, headers.index("Depolarisation")]
83            dy = data[:, headers.index("Depolarisation_error")]
84
85            lam_unit = self._unit_fetch("Wavelength")
86            x, x_unit = self._unit_conversion(x, "A", self._unit_fetch("SpinEchoLength"))
87            dx, dx_unit = self._unit_conversion(
88                dx, lam_unit,
89                self._unit_fetch("SpinEchoLength"))
90            dlam, dlam_unit = self._unit_conversion(
91                dlam, lam_unit,
92                self._unit_fetch("Wavelength"))
93            y_unit = self._unit_fetch("Depolarisation")
94
95            output = Data1D(x=x, y=y, lam=lam, dy=dy, dx=dx, dlam=dlam,
96                            isSesans=True)
97
98            output.y_unit = y_unit
99            output.x_unit = x_unit
100            output.source.wavelength_unit = lam_unit
101            output.source.wavelength = lam
102            self.filename = output.filename = basename
103            output.xaxis(r"\rm{z}", x_unit)
104            # Adjust label to ln P/(lam^2 t), remove lam column refs
105            output.yaxis(r"\rm{ln(P)/(t \lambda^2)}", y_unit)
106            # Store loading process information
107            output.meta_data['loader'] = self.type_name
108            output.sample.name = params["Sample"]
109            output.sample.ID = params["DataFileTitle"]
110            output.sample.thickness = float(
111                self._unit_conversion(
112                    params["Thickness"], "cm", self._unit_fetch("Thickness"))[0])
113
114            output.sample.zacceptance = (
115                float(params["Theta_zmax"]),
116                self._unit_fetch("Theta_zmax"))
117
118            output.sample.yacceptance = (
119                float(params["Theta_ymax"]),
120                self._unit_fetch("Theta_ymax"))
121            return output
122
123    @staticmethod
124    def _unit_conversion(value, value_unit, default_unit):
125        """
126        Performs unit conversion on a measurement.
127
128        :param value: The magnitude of the measurement
129        :param value_unit: a string containing the final desired unit
130        :param default_unit: a string containing the units of the original measurement
131        :return: The magnitude of the measurement in the new units
132        """
133        # (float, string, string) -> float
134        if has_converter and value_unit != default_unit:
135            data_conv_q = Converter(value_unit)
136            value = data_conv_q(value, units=default_unit)
137            new_unit = default_unit
138        else:
139            new_unit = value_unit
140        return value, new_unit
141
142    def _unit_fetch(self, unit):
143        return self.params[unit+"_unit"]
Note: See TracBrowser for help on using the repository browser.