source: sasview/src/sas/sascalc/dataloader/readers/sesans_reader.py @ 5ae40e7

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 5ae40e7 was 5ae40e7, checked in by Adam Washington <adam.washington@…>, 7 years ago

Throw an exception if sesans file has conflicting units

We've decided to mandate identical units for the current file format
version to allow other programs to import the files more easily. This
may change in later versions

  • Property mode set to 100644
File size: 5.8 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(
54                    "{} has an unrecognized file extension".format(path))
55        else:
56            raise RuntimeError("{} is not a file".format(path))
57        with open(path, 'r') as input_f:
58            # Read in binary mode since GRASP frequently has no-ascii
59            # characters that brakes the open operation
60            line = input_f.readline()
61            params = {}
62            while not line.startswith("BEGIN_DATA"):
63                terms = line.split()
64                if len(terms) >= 2:
65                    params[terms[0]] = " ".join(terms[1:])
66                line = input_f.readline()
67            self.params = params
68
69            if "SpinEchoLength_unit" not in self.params:
70                raise RuntimeError("SpinEchoLength has no units")
71            if "Wavelength_unit" not in self.params:
72                raise RuntimeError("Wavelength has no units")
73            if params["SpinEchoLength_unit"] != params["Wavelength_unit"]:
74                raise RuntimeError("The spin echo data has rudely used "
75                                   "different units for the spin echo length "
76                                   "and the wavelength.  While sasview could "
77                                   "handle this instance, it is a violation "
78                                   "of the file format and will not be "
79                                   "handled by other software.")
80
81            headers = input_f.readline().split()
82
83            data = np.loadtxt(input_f)
84            if data.size < 1:
85                raise RuntimeError("{} is empty".format(path))
86            x = data[:, headers.index("SpinEchoLength")]
87            if "SpinEchoLength_error" in headers:
88                dx = data[:, headers.index("SpinEchoLength_error")]
89            else:
90                dx = x*0.05
91            lam = data[:, headers.index("Wavelength")]
92            if "Wavelength_error" in headers:
93                dlam = data[:, headers.index("Wavelength_error")]
94            else:
95                dlam = lam*0.05
96            y = data[:, headers.index("Depolarisation")]
97            dy = data[:, headers.index("Depolarisation_error")]
98
99            lam_unit = self._unit_fetch("Wavelength")
100            x, x_unit = self._unit_conversion(x, "A", self._unit_fetch("SpinEchoLength"))
101            dx, dx_unit = self._unit_conversion(
102                dx, lam_unit,
103                self._unit_fetch("SpinEchoLength"))
104            dlam, dlam_unit = self._unit_conversion(
105                dlam, lam_unit,
106                self._unit_fetch("Wavelength"))
107            y_unit = self._unit_fetch("Depolarisation")
108
109            output = Data1D(x=x, y=y, lam=lam, dy=dy, dx=dx, dlam=dlam,
110                            isSesans=True)
111
112            output.y_unit = y_unit
113            output.x_unit = x_unit
114            output.source.wavelength_unit = lam_unit
115            output.source.wavelength = lam
116            self.filename = output.filename = basename
117            output.xaxis(r"\rm{z}", x_unit)
118            # Adjust label to ln P/(lam^2 t), remove lam column refs
119            output.yaxis(r"\rm{ln(P)/(t \lambda^2)}", y_unit)
120            # Store loading process information
121            output.meta_data['loader'] = self.type_name
122            output.sample.name = params["Sample"]
123            output.sample.ID = params["DataFileTitle"]
124            output.sample.thickness = self._unit_conversion(
125                float(params["Thickness"]), "cm",
126                self._unit_fetch("Thickness"))[0]
127
128            output.sample.zacceptance = (
129                float(params["Theta_zmax"]),
130                self._unit_fetch("Theta_zmax"))
131
132            output.sample.yacceptance = (
133                float(params["Theta_ymax"]),
134                self._unit_fetch("Theta_ymax"))
135            return output
136
137    @staticmethod
138    def _unit_conversion(value, value_unit, default_unit):
139        """
140        Performs unit conversion on a measurement.
141
142        :param value: The magnitude of the measurement
143        :param value_unit: a string containing the final desired unit
144        :param default_unit: a string containing the units of the original measurement
145        :return: The magnitude of the measurement in the new units
146        """
147        # (float, string, string) -> float
148        if has_converter and value_unit != default_unit:
149            data_conv_q = Converter(default_unit)
150            value = data_conv_q(value, units=value_unit)
151            new_unit = default_unit
152        else:
153            new_unit = value_unit
154        return value, new_unit
155
156    def _unit_fetch(self, unit):
157        return self.params[unit+"_unit"]
Note: See TracBrowser for help on using the repository browser.