source: sasview/DataLoader/data_info.py @ ccb560a

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.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since ccb560a was b39c817, checked in by Mathieu Doucet <doucetm@…>, 16 years ago

Added unit converter for CanSAS format reader

  • Property mode set to 100644
File size: 16.6 KB
Line 
1"""
2    Module that contains classes to hold information read from
3    reduced data files.
4   
5    A good description of the data members can be found in
6    the CanSAS 1D XML data format:
7   
8    http://www.smallangles.net/wgwiki/index.php/cansas1d_documentation
9"""
10
11"""
12This software was developed by the University of Tennessee as part of the
13Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
14project funded by the US National Science Foundation.
15
16See the license text in license.txt
17
18copyright 2008, University of Tennessee
19"""
20
21#TODO: Keep track of data manipulation in the 'process' data structure.
22
23from sans.guitools.plottables import Data1D as plottable_1D
24from data_util.uncertainty import Uncertainty
25import numpy
26import math
27
28class Data2D:
29    """
30        Data2D is a place holder for 2D plottables, which are
31        not yet implemented.
32    """
33    xmin = None
34    xmax = None
35    ymin = None
36    ymax = None
37    image = None
38 
39class Vector:
40    """
41        Vector class to hold multi-dimensional objects
42    """
43    ## x component
44    x = None
45    ## y component
46    y = None
47    ## z component
48    z = None
49   
50    def __init__(self, x=None, y=None, z=None):
51        """
52            Initialization. Components that are not
53            set a set to None by default.
54           
55            @param x: x component
56            @param y: y component
57            @param z: z component
58        """
59        self.x = x
60        self.y = y
61        self.z = z
62       
63    def __str__(self):
64        return "x = %s\ty = %s\tz = %s" % (str(self.x), str(self.y), str(self.z))
65       
66
67class Detector:
68    """
69        Class to hold detector information
70    """
71    ## Name of the instrument [string]
72    name = ''
73    ## Sample to detector distance [float] [mm]
74    distance = None
75    distance_unit = 'mm'
76    ## Offset of this detector position in X, Y, (and Z if necessary) [Vector] [mm]
77    offset = Vector()
78    offset_unit = 'm'
79    ## Orientation (rotation) of this detector in roll, pitch, and yaw [Vector] [degrees]
80    orientation = Vector()
81    orientation_unit = 'degree'
82    ## Center of the beam on the detector in X and Y (and Z if necessary) [Vector] [pixel]
83    beam_center = Vector()
84    beam_center_unit = 'mm'
85    ## Pixel size in X, Y, (and Z if necessary) [Vector] [mm]
86    pixel_size = Vector()
87    pixel_size_unit = 'mm'
88    ## Slit length of the instrument for this detector.[float] [mm]
89    slit_length = None
90    slit_length_unit = 'mm'
91   
92    def __str__(self):
93        _str  = "Detector:\n"
94        _str += "   Name:         %s\n" % self.name
95        _str += "   Distance:     %s [%s]\n" % \
96            (str(self.distance), str(self.distance_unit))
97        _str += "   Offset:       %s [%s]\n" % \
98            (str(self.offset), str(self.offset_unit))
99        _str += "   Orientation:  %s [%s]\n" % \
100            (str(self.orientation), str(self.orientation_unit))
101        _str += "   Beam center:  %s [%s]\n" % \
102            (str(self.beam_center), str(self.beam_center_unit))
103        _str += "   Pixel size:   %s [%s]\n" % \
104            (str(self.pixel_size), str(self.pixel_size_unit))
105        _str += "   Slit length:  %s [%s]\n" % \
106            (str(self.slit_length), str(self.slit_length_unit))
107        return _str
108
109class Collimation:
110    """
111        Class to hold collimation information
112    """
113    class Aperture:
114        # Aperture size [Vector]
115        size = Vector()
116        size_unit = 'mm'
117        # Aperture distance [float]
118        distance = None
119        distance_unit = 'mm'
120   
121    ## Length [float] [mm]
122    length = None
123    length_unit = 'mm'
124    ## Aperture
125    aperture = []
126   
127    def __str__(self):
128        _str = "Collimation:\n"
129        _str += "   Length:       %s [%s]\n" % \
130            (str(self.length), str(self.length_unit))
131        for item in self.aperture:
132            _str += "   Aperture size:%s [%s]\n" % \
133                (str(item.size), str(item.size_unit))
134            _str += "   Aperture_dist:%s [%s]\n" % \
135                (str(item.distance), str(item.distance_unit))
136        return _str
137
138class Source:
139    """
140        Class to hold source information
141    """ 
142    ## Radiation type [string]
143    radiation = ''
144    ## Beam size [Vector] [mm]
145    beam_size = Vector()
146    beam_size_unit = 'mm'
147    ## Beam shape [string]
148    beam_shape = ''
149    ## Wavelength [float] [Angstrom]
150    wavelength = None
151    wavelength_unit = 'A'
152    ## Minimum wavelength [float] [Angstrom]
153    wavelength_min = None
154    wavelength_min_unit = 'nm'
155    ## Maximum wavelength [float] [Angstrom]
156    wavelength_max = None
157    wavelength_max_unit = 'nm'
158    ## Wavelength spread [float] [Angstrom]
159    wavelength_spread = None
160    wavelength_spread_unit = 'percent'
161   
162    def __str__(self):
163        _str  = "Source:\n"
164        _str += "   Radiation:    %s\n" % str(self.radiation)
165        _str += "   Shape:        %s\n" % str(self.beam_shape)
166        _str += "   Wavelength:   %s [%s]\n" % \
167            (str(self.wavelength), str(self.wavelength_unit))
168        _str += "   Waveln_min:   %s [%s]\n" % \
169            (str(self.wavelength_min), str(self.wavelength_min_unit))
170        _str += "   Waveln_max:   %s [%s]\n" % \
171            (str(self.wavelength_max), str(self.wavelength_max_unit))
172        _str += "   Waveln_spread:%s [%s]\n" % \
173            (str(self.wavelength_spread), str(self.wavelength_spread_unit))
174        _str += "   Beam_size:    %s [%s]\n" % \
175            (str(self.beam_size), str(self.beam_size_unit))
176        return _str
177   
178   
179"""
180    Definitions of radiation types
181"""
182NEUTRON  = 'neutron'
183XRAY     = 'x-ray'
184MUON     = 'muon'
185ELECTRON = 'electron'
186   
187class Sample:
188    """
189        Class to hold the sample description
190    """
191    ## ID
192    ID = ''
193    ## Thickness [float] [mm]
194    thickness = None
195    thickness_unit = 'mm'
196    ## Transmission [float] [fraction]
197    transmission = None
198    ## Temperature [float] [C]
199    temperature = None
200    temperature_unit = 'C'
201    ## Position [Vector] [mm]
202    position = Vector()
203    position_unit = 'mm'
204    ## Orientation [Vector] [degrees]
205    orientation = Vector()
206    orientation_unit = 'degree'
207    ## Details
208    details = []
209   
210    def __str__(self):
211        _str  = "Sample:\n"
212        _str += "   ID:           %s\n" % str(self.ID)
213        _str += "   Transmission: %s\n" % str(self.transmission)
214        _str += "   Thickness:    %s [%s]\n" % \
215            (str(self.thickness), str(self.thickness_unit))
216        _str += "   Temperature:  %s [%s]\n" % \
217            (str(self.temperature), str(self.temperature_unit))
218        _str += "   Position:     %s [%s]\n" % \
219            (str(self.position), str(self.position_unit))
220        _str += "   Orientation:  %s [%s]\n" % \
221            (str(self.orientation), str(self.orientation_unit))
222       
223        _str += "   Details:\n"
224        for item in self.details:
225            _str += "      %s\n" % item
226           
227        return _str
228 
229class Process:
230    """
231        Class that holds information about the processes
232        performed on the data.
233    """
234    name = ''
235    date = ''
236    description= ''
237    term = []
238    notes = []
239   
240    def __str__(self):
241        _str  = "Process:\n"
242        _str += "   Name:         %s\n" % self.name
243        _str += "   Date:         %s\n" % self.date
244        _str += "   Description:  %s\n" % self.description
245        for item in self.term:
246            _str += "   Term:         %s\n" % item
247        for item in self.notes:
248            _str += "   Note:         %s\n" % item
249        return _str
250   
251 
252class DataInfo:
253    """
254        Class to hold the data read from a file.
255        It includes four blocks of data for the
256        instrument description, the sample description,
257        the data itself and any other meta data.
258    """
259    ## Title
260    title      = ''
261    ## Run number
262    run        = None
263    ## File name
264    filename   = ''
265    ## Notes
266    notes      = []
267    ## Processes (Action on the data)
268    process    = []
269    ## Instrument name
270    instrument = ''
271    ## Detector information
272    detector   = []
273    ## Sample information
274    sample     = Sample()
275    ## Source information
276    source     = Source()
277    ## Collimation information
278    collimation = []
279    ## Additional meta-data
280    meta_data  = {}
281    ## Loading errors
282    errors = []
283           
284    # Private method to perform operation. Not implemented for DataInfo,
285    # but should be implemented for each data class inherited from DataInfo
286    # that holds actual data (ex.: Data1D)
287    def _perform_operation(self, other, operation): return NotImplemented
288
289    def __add__(self, other):
290        """
291            Add two data sets
292           
293            @param other: data set to add to the current one
294            @return: new data set
295            @raise ValueError: raised when two data sets are incompatible
296        """
297        def operation(a, b): return a+b
298        return self._perform_operation(other, operation)
299       
300    def __radd__(self, other):
301        """
302            Add two data sets
303           
304            @param other: data set to add to the current one
305            @return: new data set
306            @raise ValueError: raised when two data sets are incompatible
307        """
308        def operation(a, b): return b+a
309        return self._perform_operation(other, operation)
310       
311    def __sub__(self, other):
312        """
313            Subtract two data sets
314           
315            @param other: data set to subtract from the current one
316            @return: new data set
317            @raise ValueError: raised when two data sets are incompatible
318        """
319        def operation(a, b): return a-b
320        return self._perform_operation(other, operation)
321       
322    def __rsub__(self, other):
323        """
324            Subtract two data sets
325           
326            @param other: data set to subtract from the current one
327            @return: new data set
328            @raise ValueError: raised when two data sets are incompatible
329        """
330        def operation(a, b): return b-a
331        return self._perform_operation(other, operation)
332       
333    def __mul__(self, other):
334        """
335            Multiply two data sets
336           
337            @param other: data set to subtract from the current one
338            @return: new data set
339            @raise ValueError: raised when two data sets are incompatible
340        """
341        def operation(a, b): return a*b
342        return self._perform_operation(other, operation)
343       
344    def __rmul__(self, other):
345        """
346            Multiply two data sets
347           
348            @param other: data set to subtract from the current one
349            @return: new data set
350            @raise ValueError: raised when two data sets are incompatible
351        """
352        def operation(a, b): return b*a
353        return self._perform_operation(other, operation)
354       
355    def __div__(self, other):
356        """
357            Divided a data set by another
358           
359            @param other: data set that the current one is divided by
360            @return: new data set
361            @raise ValueError: raised when two data sets are incompatible
362        """
363        def operation(a, b): return a/b
364        return self._perform_operation(other, operation)
365       
366    def __rdiv__(self, other):
367        """
368            Divided a data set by another
369           
370            @param other: data set that the current one is divided by
371            @return: new data set
372            @raise ValueError: raised when two data sets are incompatible
373        """
374        def operation(a, b): return b/a
375        return self._perform_operation(other, operation)           
376           
377class Data1D(plottable_1D, DataInfo):
378    """
379        1D data class
380    """
381    x_unit = '1/A'
382    y_unit = '1/cm'
383   
384    def __init__(self, x, y, dx=None, dy=None):
385        plottable_1D.__init__(self, x, y, dx, dy)
386       
387    def __str__(self):
388        """
389            Nice printout
390        """
391        _str =  "File:            %s\n" % self.filename
392        _str += "Title:           %s\n" % self.title
393        _str += "Run:             %s\n" % str(self.run)
394        _str += "Instrument:      %s\n" % str(self.instrument)
395        _str += "%s\n" % str(self.sample)
396        _str += "%s\n" % str(self.source)
397        for item in self.detector:
398            _str += "%s\n" % str(item)
399        for item in self.collimation:
400            _str += "%s\n" % str(item)
401        for item in self.process:
402            _str += "%s\n" % str(item)
403        for item in self.notes:
404            _str += "%s\n" % str(item)
405       
406        _str += "Data:\n"
407        _str += "   Type:         %s\n" % self.__class__.__name__
408        _str += "   X-axis:       %s\t[%s]\n" % (self._xaxis, self._xunit)
409        _str += "   Y-axis:       %s\t[%s]\n" % (self._yaxis, self._yunit)
410        _str += "   Length:       %g\n" % len(self.x)
411
412        return _str
413
414    def clone_without_data(self, length=0):
415        """
416            Clone the current object, without copying the data (which
417            will be filled out by a subsequent operation).
418            The data arrays will be initialized to zero.
419           
420            @param length: length of the data array to be initialized
421        """
422        from copy import deepcopy
423       
424        x  = numpy.zeros(length) 
425        dx = numpy.zeros(length) 
426        y  = numpy.zeros(length) 
427        dy = numpy.zeros(length) 
428       
429        clone = Data1D(x, y, dx=dx, dy=dy)
430        clone.title       = self.title
431        clone.run         = self.run
432        clone.filename    = self.filename
433        clone.notes       = deepcopy(self.notes) 
434        clone.process     = deepcopy(self.process) 
435        clone.detector    = deepcopy(self.detector) 
436        clone.sample      = deepcopy(self.sample) 
437        clone.source      = deepcopy(self.source) 
438        clone.collimation = deepcopy(self.collimation) 
439        clone.meta_data   = deepcopy(self.meta_data) 
440        clone.errors      = deepcopy(self.errors) 
441       
442        return clone
443
444    def _validity_check(self, other):
445        """
446            Checks that the data lengths are compatible.
447            Checks that the x vectors are compatible.
448            Returns errors vectors equal to original
449            errors vectors if they were present or vectors
450            of zeros when none was found.
451           
452            @param other: other data set for operation
453            @return: dy for self, dy for other [numpy arrays]
454            @raise ValueError: when lengths are not compatible
455        """
456        dy_other = None
457        if isinstance(other, Data1D):
458            # Check that data lengths are the same
459            if len(self.x) != len(other.x) or \
460                len(self.y) != len(other.y):
461                raise ValueError, "Unable to perform operation: data length are not equal"
462           
463            # Here we could also extrapolate between data points
464            for i in range(len(self.x)):
465                if self.x[i] != other.x[i]:
466                    raise ValueError, "Incompatible data sets: x-values do not match"
467           
468            # Check that the other data set has errors, otherwise
469            # create zero vector
470            dy_other = other.dy
471            if other.dy==None or (len(other.dy) != len(other.y)):
472                dy_other = numpy.zeros(len(other.y))
473           
474        # Check that we have errors, otherwise create zero vector
475        dy = self.dy
476        if self.dy==None or (len(self.dy) != len(self.y)):
477            dy = numpy.zeros(len(self.y))           
478           
479        return dy, dy_other
480
481    def _perform_operation(self, other, operation):
482        """
483        """
484        # First, check the data compatibility
485        dy, dy_other = self._validity_check(other)
486        result = self.clone_without_data(len(self.x))
487       
488        for i in range(len(self.x)):
489            result.x[i] = self.x[i]
490            if self.dx is not None and len(self.x)==len(self.dx):
491                result.dx[i] = self.dx[i]
492           
493            a = Uncertainty(self.y[i], dy[i]**2)
494            if isinstance(other, Data1D):
495                b = Uncertainty(other.y[i], dy_other[i]**2)
496            else:
497                b = other
498           
499            output = operation(a, b)
500            result.y[i] = output.x
501            result.dy[i] = math.sqrt(math.fabs(output.variance))
502        return result
503       
Note: See TracBrowser for help on using the repository browser.