source: sasview/src/sas/dataloader/data_info.py @ 49ab5d7

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 49ab5d7 was e4f421c, checked in by Doucet, Mathieu <doucetm@…>, 9 years ago

pylint fixes

  • Property mode set to 100644
File size: 39.9 KB
RevLine 
[a3084ada]1"""
[f60a8c2]2    Module that contains classes to hold information read from
[a3084ada]3    reduced data files.
[e4f421c]4
[f60a8c2]5    A good description of the data members can be found in
[a3084ada]6    the CanSAS 1D XML data format:
[e4f421c]7
[a3084ada]8    http://www.smallangles.net/wgwiki/index.php/cansas1d_documentation
9"""
[f60a8c2]10#####################################################################
[0997158f]11#This software was developed by the University of Tennessee as part of the
12#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
[f60a8c2]13#project funded by the US National Science Foundation.
14#See the license text in license.txt
[0997158f]15#copyright 2008, University of Tennessee
[f60a8c2]16######################################################################
[a3084ada]17
18
[b39c817]19#TODO: Keep track of data manipulation in the 'process' data structure.
[579ba85]20#TODO: This module should be independent of plottables. We should write
21#        an adapter class for plottables when needed.
[b39c817]22
[79492222]23#from sas.guitools.plottables import Data1D as plottable_1D
24from sas.data_util.uncertainty import Uncertainty
[9198b83]25import numpy
26import math
[a3084ada]27
[e4f421c]28class plottable_sesans1D(object):
[b45cde3]29    """
30    SESANS is a place holder for 1D SESANS plottables.
[e4f421c]31
[5e326a6]32    #TODO: This was directly copied from the plottables_1D. Modified Somewhat.
33    #Class has been updated.
[b45cde3]34    """
35    # The presence of these should be mutually
36    # exclusive with the presence of Qdev (dx)
37    x = None
38    y = None
[5e326a6]39    lam = None
[b45cde3]40    dx = None
41    dy = None
[5e326a6]42    dlam = None
[b45cde3]43    ## Slit smearing length
44    dxl = None
45    ## Slit smearing width
46    dxw = None
[e4f421c]47
[b45cde3]48    # Units
49    _xaxis = ''
50    _xunit = ''
51    _yaxis = ''
52    _yunit = ''
[e4f421c]53
[5e326a6]54    def __init__(self, x, y, lam, dx=None, dy=None, dlam=None):
55#        print "SESANS plottable working"
[b45cde3]56        self.x = numpy.asarray(x)
57        self.y = numpy.asarray(y)
[5e326a6]58        self.lam = numpy.asarray(lam)
[b45cde3]59        if dx is not None:
60            self.dx = numpy.asarray(dx)
61        if dy is not None:
62            self.dy = numpy.asarray(dy)
[5e326a6]63        if dlam is not None:
64            self.dlam = numpy.asarray(dlam)
[e4f421c]65
[b45cde3]66    def xaxis(self, label, unit):
67        """
68        set the x axis label and unit
69        """
70        self._xaxis = label
71        self._xunit = unit
[e4f421c]72
[b45cde3]73    def yaxis(self, label, unit):
74        """
75        set the y axis label and unit
76        """
77        self._yaxis = label
78        self._yunit = unit
79
80
[e4f421c]81class plottable_1D(object):
[579ba85]82    """
[0997158f]83    Data1D is a place holder for 1D plottables.
[579ba85]84    """
[a7a5886]85    # The presence of these should be mutually
86    # exclusive with the presence of Qdev (dx)
[579ba85]87    x = None
88    y = None
89    dx = None
90    dy = None
[d00f8ff]91    ## Slit smearing length
92    dxl = None
93    ## Slit smearing width
94    dxw = None
[e4f421c]95
[579ba85]96    # Units
97    _xaxis = ''
98    _xunit = ''
99    _yaxis = ''
100    _yunit = ''
[e4f421c]101
[a7a5886]102    def __init__(self, x, y, dx=None, dy=None, dxl=None, dxw=None):
[2733188]103        self.x = numpy.asarray(x)
104        self.y = numpy.asarray(y)
[a7a5886]105        if dx is not None:
[f60a8c2]106            self.dx = numpy.asarray(dx)
107        if dy is not None:
[a7a5886]108            self.dy = numpy.asarray(dy)
[f60a8c2]109        if dxl is not None:
[a7a5886]110            self.dxl = numpy.asarray(dxl)
[e4f421c]111        if dxw is not None:
[a7a5886]112            self.dxw = numpy.asarray(dxw)
[579ba85]113
114    def xaxis(self, label, unit):
[a7a5886]115        """
116        set the x axis label and unit
117        """
[579ba85]118        self._xaxis = label
119        self._xunit = unit
[e4f421c]120
[579ba85]121    def yaxis(self, label, unit):
[a7a5886]122        """
123        set the y axis label and unit
124        """
[579ba85]125        self._yaxis = label
126        self._yunit = unit
127
[f60a8c2]128
[e4f421c]129class plottable_2D(object):
[8780e9a]130    """
[0997158f]131    Data2D is a place holder for 2D plottables.
[8780e9a]132    """
133    xmin = None
134    xmax = None
135    ymin = None
136    ymax = None
[99d1af6]137    data = None
[f60a8c2]138    qx_data = None
139    qy_data = None
140    q_data = None
141    err_data = None
142    dqx_data = None
143    dqy_data = None
144    mask = None
[e4f421c]145
[99d1af6]146    # Units
147    _xaxis = ''
148    _xunit = ''
149    _yaxis = ''
150    _yunit = ''
151    _zaxis = ''
152    _zunit = ''
[e4f421c]153
[a7a5886]154    def __init__(self, data=None, err_data=None, qx_data=None,
[f60a8c2]155                 qy_data=None, q_data=None, mask=None,
156                 dqx_data=None, dqy_data=None):
[442f42f]157        self.data = numpy.asarray(data)
[3cd95c8]158        self.qx_data = numpy.asarray(qx_data)
159        self.qy_data = numpy.asarray(qy_data)
160        self.q_data = numpy.asarray(q_data)
161        self.mask = numpy.asarray(mask)
[442f42f]162        self.err_data = numpy.asarray(err_data)
[f60a8c2]163        if dqx_data is not None:
[e4f421c]164            self.dqx_data = numpy.asarray(dqx_data)
[f60a8c2]165        if dqy_data is not None:
[e4f421c]166            self.dqy_data = numpy.asarray(dqy_data)
167
[99d1af6]168    def xaxis(self, label, unit):
[a7a5886]169        """
170        set the x axis label and unit
171        """
[99d1af6]172        self._xaxis = label
173        self._xunit = unit
[e4f421c]174
[99d1af6]175    def yaxis(self, label, unit):
[a7a5886]176        """
177        set the y axis label and unit
178        """
[99d1af6]179        self._yaxis = label
180        self._yunit = unit
[e4f421c]181
[99d1af6]182    def zaxis(self, label, unit):
[a7a5886]183        """
184        set the z axis label and unit
185        """
[99d1af6]186        self._zaxis = label
187        self._zunit = unit
[de5c813]188
[e4f421c]189
190class Vector(object):
[a3084ada]191    """
[0997158f]192    Vector class to hold multi-dimensional objects
[a3084ada]193    """
194    ## x component
195    x = None
196    ## y component
197    y = None
198    ## z component
199    z = None
[e4f421c]200
[a3084ada]201    def __init__(self, x=None, y=None, z=None):
202        """
[0997158f]203        Initialization. Components that are not
204        set a set to None by default.
[e4f421c]205
[0997158f]206        :param x: x component
207        :param y: y component
208        :param z: z component
[a3084ada]209        """
210        self.x = x
211        self.y = y
212        self.z = z
[e4f421c]213
[a3084ada]214    def __str__(self):
[a7a5886]215        msg = "x = %s\ty = %s\tz = %s" % (str(self.x), str(self.y), str(self.z))
216        return msg
[a3084ada]217
[e4f421c]218
219class Detector(object):
[a3084ada]220    """
[0997158f]221    Class to hold detector information
[a3084ada]222    """
223    ## Name of the instrument [string]
[fe78c7b]224    name = None
[a3084ada]225    ## Sample to detector distance [float] [mm]
226    distance = None
[b39c817]227    distance_unit = 'mm'
[f60a8c2]228    ## Offset of this detector position in X, Y,
229    #(and Z if necessary) [Vector] [mm]
[d6513cd]230    offset = None
[b39c817]231    offset_unit = 'm'
[a7a5886]232    ## Orientation (rotation) of this detector in roll,
233    # pitch, and yaw [Vector] [degrees]
[d6513cd]234    orientation = None
[8780e9a]235    orientation_unit = 'degree'
[f60a8c2]236    ## Center of the beam on the detector in X and Y
[a7a5886]237    #(and Z if necessary) [Vector] [mm]
[d6513cd]238    beam_center = None
[8780e9a]239    beam_center_unit = 'mm'
[a3084ada]240    ## Pixel size in X, Y, (and Z if necessary) [Vector] [mm]
[d6513cd]241    pixel_size = None
[8780e9a]242    pixel_size_unit = 'mm'
[a3084ada]243    ## Slit length of the instrument for this detector.[float] [mm]
244    slit_length = None
[2e9b98c]245    slit_length_unit = 'mm'
[e4f421c]246
[d6513cd]247    def __init__(self):
248        """
[0997158f]249        Initialize class attribute that are objects...
[d6513cd]250        """
[e4f421c]251        self.offset = Vector()
[d6513cd]252        self.orientation = Vector()
253        self.beam_center = Vector()
[e4f421c]254        self.pixel_size = Vector()
255
[8780e9a]256    def __str__(self):
[e4f421c]257        _str = "Detector:\n"
[8780e9a]258        _str += "   Name:         %s\n" % self.name
259        _str += "   Distance:     %s [%s]\n" % \
260            (str(self.distance), str(self.distance_unit))
261        _str += "   Offset:       %s [%s]\n" % \
262            (str(self.offset), str(self.offset_unit))
263        _str += "   Orientation:  %s [%s]\n" % \
264            (str(self.orientation), str(self.orientation_unit))
265        _str += "   Beam center:  %s [%s]\n" % \
266            (str(self.beam_center), str(self.beam_center_unit))
267        _str += "   Pixel size:   %s [%s]\n" % \
268            (str(self.pixel_size), str(self.pixel_size_unit))
269        _str += "   Slit length:  %s [%s]\n" % \
270            (str(self.slit_length), str(self.slit_length_unit))
271        return _str
[a3084ada]272
[f60a8c2]273
[e4f421c]274class Aperture(object):
[4c00964]275    ## Name
[579ba85]276    name = None
[4c00964]277    ## Type
[579ba85]278    type = None
279    ## Size name
280    size_name = None
[4c00964]281    ## Aperture size [Vector]
[d6513cd]282    size = None
283    size_unit = 'mm'
[4c00964]284    ## Aperture distance [float]
[d6513cd]285    distance = None
286    distance_unit = 'mm'
[e4f421c]287
[d6513cd]288    def __init__(self):
289        self.size = Vector()
[e4f421c]290
291
292class Collimation(object):
[a3084ada]293    """
[0997158f]294    Class to hold collimation information
[a3084ada]295    """
[4c00964]296    ## Name
[fe78c7b]297    name = None
[a3084ada]298    ## Length [float] [mm]
299    length = None
[8780e9a]300    length_unit = 'mm'
301    ## Aperture
[d6513cd]302    aperture = None
[e4f421c]303
[d6513cd]304    def __init__(self):
305        self.aperture = []
[e4f421c]306
[8780e9a]307    def __str__(self):
308        _str = "Collimation:\n"
309        _str += "   Length:       %s [%s]\n" % \
310            (str(self.length), str(self.length_unit))
311        for item in self.aperture:
312            _str += "   Aperture size:%s [%s]\n" % \
313                (str(item.size), str(item.size_unit))
314            _str += "   Aperture_dist:%s [%s]\n" % \
315                (str(item.distance), str(item.distance_unit))
316        return _str
[a3084ada]317
[f60a8c2]318
[e4f421c]319class Source(object):
[a3084ada]320    """
[0997158f]321    Class to hold source information
[f60a8c2]322    """
[4c00964]323    ## Name
[579ba85]324    name = None
[a3084ada]325    ## Radiation type [string]
[579ba85]326    radiation = None
327    ## Beam size name
328    beam_size_name = None
[a3084ada]329    ## Beam size [Vector] [mm]
[d6513cd]330    beam_size = None
[8780e9a]331    beam_size_unit = 'mm'
[a3084ada]332    ## Beam shape [string]
[579ba85]333    beam_shape = None
[a3084ada]334    ## Wavelength [float] [Angstrom]
335    wavelength = None
[8780e9a]336    wavelength_unit = 'A'
[a3084ada]337    ## Minimum wavelength [float] [Angstrom]
338    wavelength_min = None
[8780e9a]339    wavelength_min_unit = 'nm'
[a3084ada]340    ## Maximum wavelength [float] [Angstrom]
341    wavelength_max = None
[8780e9a]342    wavelength_max_unit = 'nm'
[a3084ada]343    ## Wavelength spread [float] [Angstrom]
344    wavelength_spread = None
[8780e9a]345    wavelength_spread_unit = 'percent'
[e4f421c]346
[d6513cd]347    def __init__(self):
348        self.beam_size = Vector()
[e4f421c]349
[8780e9a]350    def __str__(self):
[e4f421c]351        _str = "Source:\n"
[8780e9a]352        _str += "   Radiation:    %s\n" % str(self.radiation)
353        _str += "   Shape:        %s\n" % str(self.beam_shape)
354        _str += "   Wavelength:   %s [%s]\n" % \
355            (str(self.wavelength), str(self.wavelength_unit))
356        _str += "   Waveln_min:   %s [%s]\n" % \
357            (str(self.wavelength_min), str(self.wavelength_min_unit))
358        _str += "   Waveln_max:   %s [%s]\n" % \
359            (str(self.wavelength_max), str(self.wavelength_max_unit))
360        _str += "   Waveln_spread:%s [%s]\n" % \
361            (str(self.wavelength_spread), str(self.wavelength_spread_unit))
362        _str += "   Beam_size:    %s [%s]\n" % \
363            (str(self.beam_size), str(self.beam_size_unit))
364        return _str
[e4f421c]365
366
[f60a8c2]367"""
[0997158f]368Definitions of radiation types
[a3084ada]369"""
[e4f421c]370NEUTRON = 'neutron'
371XRAY = 'x-ray'
372MUON = 'muon'
[a3084ada]373ELECTRON = 'electron'
[e4f421c]374
375
376class Sample(object):
[a3084ada]377    """
[0997158f]378    Class to hold the sample description
[a3084ada]379    """
[579ba85]380    ## Short name for sample
381    name = ''
[a3084ada]382    ## ID
383    ID = ''
384    ## Thickness [float] [mm]
385    thickness = None
[8780e9a]386    thickness_unit = 'mm'
387    ## Transmission [float] [fraction]
[a3084ada]388    transmission = None
[b3efb7d]389    ## Temperature [float] [No Default]
[a3084ada]390    temperature = None
[b3efb7d]391    temperature_unit = None
[a3084ada]392    ## Position [Vector] [mm]
[d6513cd]393    position = None
[8780e9a]394    position_unit = 'mm'
[a3084ada]395    ## Orientation [Vector] [degrees]
[d6513cd]396    orientation = None
[8780e9a]397    orientation_unit = 'degree'
[a3084ada]398    ## Details
[d6513cd]399    details = None
[e4f421c]400
[d6513cd]401    def __init__(self):
[e4f421c]402        self.position = Vector()
[d6513cd]403        self.orientation = Vector()
[e4f421c]404        self.details = []
405
[8780e9a]406    def __str__(self):
[e4f421c]407        _str = "Sample:\n"
[8780e9a]408        _str += "   ID:           %s\n" % str(self.ID)
409        _str += "   Transmission: %s\n" % str(self.transmission)
410        _str += "   Thickness:    %s [%s]\n" % \
411            (str(self.thickness), str(self.thickness_unit))
412        _str += "   Temperature:  %s [%s]\n" % \
413            (str(self.temperature), str(self.temperature_unit))
414        _str += "   Position:     %s [%s]\n" % \
415            (str(self.position), str(self.position_unit))
416        _str += "   Orientation:  %s [%s]\n" % \
417            (str(self.orientation), str(self.orientation_unit))
[e4f421c]418
[8780e9a]419        _str += "   Details:\n"
420        for item in self.details:
421            _str += "      %s\n" % item
[e4f421c]422
[8780e9a]423        return _str
[e4f421c]424
425
426class Process(object):
[8780e9a]427    """
[0997158f]428    Class that holds information about the processes
429    performed on the data.
[8780e9a]430    """
431    name = ''
432    date = ''
[a7a5886]433    description = ''
[d6513cd]434    term = None
435    notes = None
[e4f421c]436
[d6513cd]437    def __init__(self):
438        self.term = []
439        self.notes = []
[e4f421c]440
[8780e9a]441    def __str__(self):
[e4f421c]442        _str = "Process:\n"
[8780e9a]443        _str += "   Name:         %s\n" % self.name
444        _str += "   Date:         %s\n" % self.date
445        _str += "   Description:  %s\n" % self.description
446        for item in self.term:
447            _str += "   Term:         %s\n" % item
448        for item in self.notes:
449            _str += "   Note:         %s\n" % item
450        return _str
[e4f421c]451
452
453class TransmissionSpectrum(object):
[be577e7]454    """
455    Class that holds information about transmission spectrum
456    for white beams and spallation sources.
457    """
458    name = ''
459    timestamp = ''
460    ## Wavelength (float) [A]
461    wavelength = None
462    wavelength_unit = 'A'
463    ## Transmission (float) [unit less]
464    transmission = None
465    transmission_unit = ''
466    ## Transmission Deviation (float) [unit less]
467    transmission_deviation = None
468    transmission_deviation_unit = ''
[e4f421c]469
[be577e7]470    def __init__(self):
471        self.wavelength = []
472        self.transmission = []
473        self.transmission_deviation = []
[e4f421c]474
[be577e7]475    def __str__(self):
[e4f421c]476        _str = "Transmission Spectrum:\n"
[76cd1ae]477        _str += "   Name:             \t{0}\n".format(self.name)
478        _str += "   Timestamp:        \t{0}\n".format(self.timestamp)
479        _str += "   Wavelength unit:  \t{0}\n".format(self.wavelength_unit)
480        _str += "   Transmission unit:\t{0}\n".format(self.transmission_unit)
481        _str += "   Trans. Dev. unit:  \t{0}\n".format(\
482                                            self.transmission_deviation_unit)
483        length_list = [len(self.wavelength), len(self.transmission), \
484                len(self.transmission_deviation)]
485        _str += "   Number of Pts:    \t{0}\n".format(max(length_list))
[be577e7]486        return _str
[e4f421c]487
488
489class DataInfo(object):
[a3084ada]490    """
[0997158f]491    Class to hold the data read from a file.
492    It includes four blocks of data for the
493    instrument description, the sample description,
494    the data itself and any other meta data.
[a3084ada]495    """
[f60a8c2]496    ## Title
[e4f421c]497    title = ''
[a3084ada]498    ## Run number
[e4f421c]499    run = None
[579ba85]500    ## Run name
[e4f421c]501    run_name = None
[a3084ada]502    ## File name
[e4f421c]503    filename = ''
[a3084ada]504    ## Notes
[e4f421c]505    notes = None
[a3084ada]506    ## Processes (Action on the data)
[e4f421c]507    process = None
[8780e9a]508    ## Instrument name
509    instrument = ''
[a3084ada]510    ## Detector information
[e4f421c]511    detector = None
[a3084ada]512    ## Sample information
[e4f421c]513    sample = None
[a3084ada]514    ## Source information
[e4f421c]515    source = None
[8780e9a]516    ## Collimation information
[d6513cd]517    collimation = None
[be577e7]518    ## Transmission Spectrum INfo
519    trans_spectrum = None
[a3084ada]520    ## Additional meta-data
[e4f421c]521    meta_data = None
[8780e9a]522    ## Loading errors
[d6513cd]523    errors = None
[e4f421c]524
[b99ac227]525    def __init__(self):
526        """
[0997158f]527        Initialization
[b99ac227]528        """
[e4f421c]529        ## Title
530        self.title = ''
[b99ac227]531        ## Run number
[e4f421c]532        self.run = []
533        self.run_name = {}
[b99ac227]534        ## File name
[e4f421c]535        self.filename = ''
[b99ac227]536        ## Notes
[e4f421c]537        self.notes = []
[b99ac227]538        ## Processes (Action on the data)
[e4f421c]539        self.process = []
[b99ac227]540        ## Instrument name
541        self.instrument = ''
542        ## Detector information
[e4f421c]543        self.detector = []
[b99ac227]544        ## Sample information
[e4f421c]545        self.sample = Sample()
[b99ac227]546        ## Source information
[e4f421c]547        self.source = Source()
[b99ac227]548        ## Collimation information
549        self.collimation = []
[be577e7]550        ## Transmission Spectrum
[76cd1ae]551        self.trans_spectrum = []
[b99ac227]552        ## Additional meta-data
[e4f421c]553        self.meta_data = {}
[b99ac227]554        ## Loading errors
[f60a8c2]555        self.errors = []
[e4f421c]556
[99abf5d]557    def append_empty_process(self):
[67c7e89]558        """
559        """
560        self.process.append(Process())
[e4f421c]561
[67c7e89]562    def add_notes(self, message=""):
563        """
564        Add notes to datainfo
565        """
566        self.notes.append(message)
[e4f421c]567
[99d1af6]568    def __str__(self):
569        """
[0997158f]570        Nice printout
[99d1af6]571        """
[e4f421c]572        _str = "File:            %s\n" % self.filename
[99d1af6]573        _str += "Title:           %s\n" % self.title
574        _str += "Run:             %s\n" % str(self.run)
575        _str += "Instrument:      %s\n" % str(self.instrument)
576        _str += "%s\n" % str(self.sample)
577        _str += "%s\n" % str(self.source)
578        for item in self.detector:
579            _str += "%s\n" % str(item)
580        for item in self.collimation:
581            _str += "%s\n" % str(item)
582        for item in self.process:
583            _str += "%s\n" % str(item)
584        for item in self.notes:
585            _str += "%s\n" % str(item)
[76cd1ae]586        for item in self.trans_spectrum:
587            _str += "%s\n" % str(item)
[99d1af6]588        return _str
[e4f421c]589
[b39c817]590    # Private method to perform operation. Not implemented for DataInfo,
591    # but should be implemented for each data class inherited from DataInfo
592    # that holds actual data (ex.: Data1D)
[f60a8c2]593    def _perform_operation(self, other, operation):
[a7a5886]594        """
595        Private method to perform operation. Not implemented for DataInfo,
596        but should be implemented for each data class inherited from DataInfo
597        that holds actual data (ex.: Data1D)
598        """
599        return NotImplemented
[e4f421c]600
[365aa1ed]601    def _perform_union(self, other):
602        """
603        Private method to perform union operation. Not implemented for DataInfo,
604        but should be implemented for each data class inherited from DataInfo
605        that holds actual data (ex.: Data1D)
606        """
607        return NotImplemented
[b39c817]608
609    def __add__(self, other):
610        """
[0997158f]611        Add two data sets
[e4f421c]612
[0997158f]613        :param other: data set to add to the current one
614        :return: new data set
615        :raise ValueError: raised when two data sets are incompatible
[b39c817]616        """
[a7a5886]617        def operation(a, b):
618            return a + b
[b39c817]619        return self._perform_operation(other, operation)
[e4f421c]620
[b39c817]621    def __radd__(self, other):
622        """
[0997158f]623        Add two data sets
[e4f421c]624
[0997158f]625        :param other: data set to add to the current one
626        :return: new data set
627        :raise ValueError: raised when two data sets are incompatible
[b39c817]628        """
[a7a5886]629        def operation(a, b):
630            return b + a
[b39c817]631        return self._perform_operation(other, operation)
[e4f421c]632
[b39c817]633    def __sub__(self, other):
634        """
[0997158f]635        Subtract two data sets
[e4f421c]636
[0997158f]637        :param other: data set to subtract from the current one
638        :return: new data set
639        :raise ValueError: raised when two data sets are incompatible
[b39c817]640        """
[a7a5886]641        def operation(a, b):
642            return a - b
[b39c817]643        return self._perform_operation(other, operation)
[e4f421c]644
[b39c817]645    def __rsub__(self, other):
646        """
[0997158f]647        Subtract two data sets
[e4f421c]648
[0997158f]649        :param other: data set to subtract from the current one
650        :return: new data set
651        :raise ValueError: raised when two data sets are incompatible
[b39c817]652        """
[a7a5886]653        def operation(a, b):
654            return b - a
[b39c817]655        return self._perform_operation(other, operation)
[e4f421c]656
[b39c817]657    def __mul__(self, other):
658        """
[0997158f]659        Multiply two data sets
[e4f421c]660
[0997158f]661        :param other: data set to subtract from the current one
662        :return: new data set
663        :raise ValueError: raised when two data sets are incompatible
[b39c817]664        """
[a7a5886]665        def operation(a, b):
666            return a * b
[b39c817]667        return self._perform_operation(other, operation)
[e4f421c]668
[b39c817]669    def __rmul__(self, other):
670        """
[0997158f]671        Multiply two data sets
[e4f421c]672
[0997158f]673        :param other: data set to subtract from the current one
674        :return: new data set
675        :raise ValueError: raised when two data sets are incompatible
[b39c817]676        """
[a7a5886]677        def operation(a, b):
678            return b * a
[b39c817]679        return self._perform_operation(other, operation)
[e4f421c]680
[b39c817]681    def __div__(self, other):
682        """
[0997158f]683        Divided a data set by another
[e4f421c]684
[0997158f]685        :param other: data set that the current one is divided by
686        :return: new data set
687        :raise ValueError: raised when two data sets are incompatible
[b39c817]688        """
[a7a5886]689        def operation(a, b):
690            return a/b
[b39c817]691        return self._perform_operation(other, operation)
[e4f421c]692
[b39c817]693    def __rdiv__(self, other):
694        """
[0997158f]695        Divided a data set by another
[e4f421c]696
[0997158f]697        :param other: data set that the current one is divided by
698        :return: new data set
699        :raise ValueError: raised when two data sets are incompatible
[b39c817]700        """
[a7a5886]701        def operation(a, b):
702            return b/a
[f60a8c2]703        return self._perform_operation(other, operation)
[e4f421c]704
[a48842a2]705    def __or__(self, other):
706        """
707        Union a data set with another
[e4f421c]708
[a48842a2]709        :param other: data set to be unified
710        :return: new data set
711        :raise ValueError: raised when two data sets are incompatible
712        """
713        return self._perform_union(other)
[e4f421c]714
[a48842a2]715    def __ror__(self, other):
716        """
717        Union a data set with another
[e4f421c]718
[a48842a2]719        :param other: data set to be unified
720        :return: new data set
721        :raise ValueError: raised when two data sets are incompatible
722        """
723        return self._perform_union(other)
[e4f421c]724
[5e326a6]725class SESANSData1D(plottable_sesans1D, DataInfo):
726    """
727    SESANS 1D data class
728    """
729    x_unit = 'nm'
730    y_unit = 'a.u.'
[e4f421c]731
[5e326a6]732    def __init__(self, x=None, y=None, lam=None, dy=None, dx=None, dlam=None):
733        DataInfo.__init__(self)
734        plottable_sesans1D.__init__(self, x, y, lam, dx, dy, dlam)
[e4f421c]735
[5e326a6]736    def __str__(self):
737        """
738        Nice printout
739        """
[e4f421c]740        _str = "%s\n" % DataInfo.__str__(self)
[5e326a6]741        _str += "Data:\n"
742        _str += "   Type:         %s\n" % self.__class__.__name__
743        _str += "   X-axis:       %s\t[%s]\n" % (self._xaxis, self._xunit)
744        _str += "   Y-axis:       %s\t[%s]\n" % (self._yaxis, self._yunit)
745        _str += "   Length:       %g\n" % len(self.x)
746        return _str
[e4f421c]747
[5e326a6]748    def clone_without_data(self, length=0, clone=None):
749        """
750        Clone the current object, without copying the data (which
751        will be filled out by a subsequent operation).
752        The data arrays will be initialized to zero.
[e4f421c]753
[5e326a6]754        :param length: length of the data array to be initialized
755        :param clone: if provided, the data will be copied to clone
756        """
757        from copy import deepcopy
758        if clone is None or not issubclass(clone.__class__, Data1D):
[e4f421c]759            x = numpy.zeros(length)
[5e326a6]760            dx = numpy.zeros(length)
[e4f421c]761            y = numpy.zeros(length)
[5e326a6]762            dy = numpy.zeros(length)
763            clone = Data1D(x, y, dx=dx, dy=dy)
[e4f421c]764
765        clone.title = self.title
766        clone.run = self.run
767        clone.filename = self.filename
768        clone.instrument = self.instrument
769        clone.notes = deepcopy(self.notes)
770        clone.process = deepcopy(self.process)
771        clone.detector = deepcopy(self.detector)
772        clone.sample = deepcopy(self.sample)
773        clone.source = deepcopy(self.source)
774        clone.collimation = deepcopy(self.collimation)
[5e326a6]775        clone.trans_spectrum = deepcopy(self.trans_spectrum)
[e4f421c]776        clone.meta_data = deepcopy(self.meta_data)
777        clone.errors = deepcopy(self.errors)
778
[5e326a6]779        return clone
[e4f421c]780
[a3084ada]781class Data1D(plottable_1D, DataInfo):
782    """
[0997158f]783    1D data class
[a3084ada]784    """
[ca10d8e]785    x_unit = '1/A'
786    y_unit = '1/cm'
[e4f421c]787
[a3084ada]788    def __init__(self, x, y, dx=None, dy=None):
[b99ac227]789        DataInfo.__init__(self)
[a3084ada]790        plottable_1D.__init__(self, x, y, dx, dy)
[e4f421c]791
[a3084ada]792    def __str__(self):
793        """
[0997158f]794        Nice printout
[a3084ada]795        """
[e4f421c]796        _str = "%s\n" % DataInfo.__str__(self)
[a3084ada]797        _str += "Data:\n"
798        _str += "   Type:         %s\n" % self.__class__.__name__
799        _str += "   X-axis:       %s\t[%s]\n" % (self._xaxis, self._xunit)
800        _str += "   Y-axis:       %s\t[%s]\n" % (self._yaxis, self._yunit)
801        _str += "   Length:       %g\n" % len(self.x)
802        return _str
803
[4026380]804    def is_slit_smeared(self):
805        """
[0997158f]806        Check whether the data has slit smearing information
807        :return: True is slit smearing info is present, False otherwise
[4026380]808        """
[f60a8c2]809        def _check(v):
[a7a5886]810            if (v.__class__ == list or v.__class__ == numpy.ndarray) \
811                and len(v) > 0 and min(v) > 0:
[4026380]812                return True
813            return False
814        return _check(self.dxl) or _check(self.dxw)
[e4f421c]815
[7d8094b]816    def clone_without_data(self, length=0, clone=None):
[b39c817]817        """
[0997158f]818        Clone the current object, without copying the data (which
819        will be filled out by a subsequent operation).
820        The data arrays will be initialized to zero.
[e4f421c]821
[0997158f]822        :param length: length of the data array to be initialized
823        :param clone: if provided, the data will be copied to clone
[b39c817]824        """
[9198b83]825        from copy import deepcopy
[e4f421c]826
[7d8094b]827        if clone is None or not issubclass(clone.__class__, Data1D):
[e4f421c]828            x = numpy.zeros(length)
[f60a8c2]829            dx = numpy.zeros(length)
[e4f421c]830            y = numpy.zeros(length)
[f60a8c2]831            dy = numpy.zeros(length)
[7d8094b]832            clone = Data1D(x, y, dx=dx, dy=dy)
[e4f421c]833
834        clone.title = self.title
835        clone.run = self.run
836        clone.filename = self.filename
837        clone.instrument = self.instrument
838        clone.notes = deepcopy(self.notes)
839        clone.process = deepcopy(self.process)
840        clone.detector = deepcopy(self.detector)
841        clone.sample = deepcopy(self.sample)
842        clone.source = deepcopy(self.source)
843        clone.collimation = deepcopy(self.collimation)
[76cd1ae]844        clone.trans_spectrum = deepcopy(self.trans_spectrum)
[e4f421c]845        clone.meta_data = deepcopy(self.meta_data)
846        clone.errors = deepcopy(self.errors)
847
[9198b83]848        return clone
849
850    def _validity_check(self, other):
851        """
[0997158f]852        Checks that the data lengths are compatible.
853        Checks that the x vectors are compatible.
854        Returns errors vectors equal to original
855        errors vectors if they were present or vectors
856        of zeros when none was found.
[e4f421c]857
[0997158f]858        :param other: other data set for operation
859        :return: dy for self, dy for other [numpy arrays]
860        :raise ValueError: when lengths are not compatible
[9198b83]861        """
862        dy_other = None
863        if isinstance(other, Data1D):
864            # Check that data lengths are the same
865            if len(self.x) != len(other.x) or \
866                len(self.y) != len(other.y):
[f60a8c2]867                msg = "Unable to perform operation: data length are not equal"
[a7a5886]868                raise ValueError, msg
[9198b83]869            # Here we could also extrapolate between data points
[c2e5898]870            ZERO = 1.0e-12
[9198b83]871            for i in range(len(self.x)):
[c2e5898]872                if math.fabs(self.x[i] - other.x[i]) > ZERO:
[a7a5886]873                    msg = "Incompatible data sets: x-values do not match"
874                    raise ValueError, msg
[e4f421c]875
[9198b83]876            # Check that the other data set has errors, otherwise
877            # create zero vector
878            dy_other = other.dy
[a7a5886]879            if other.dy == None or (len(other.dy) != len(other.y)):
[9198b83]880                dy_other = numpy.zeros(len(other.y))
[e4f421c]881
[9198b83]882        # Check that we have errors, otherwise create zero vector
883        dy = self.dy
[a7a5886]884        if self.dy == None or (len(self.dy) != len(self.y)):
[f60a8c2]885            dy = numpy.zeros(len(self.y))
[e4f421c]886
[9198b83]887        return dy, dy_other
[a3084ada]888
[9198b83]889    def _perform_operation(self, other, operation):
890        """
891        """
892        # First, check the data compatibility
893        dy, dy_other = self._validity_check(other)
894        result = self.clone_without_data(len(self.x))
[e2605a5]895        if self.dxw == None:
896            result.dxw = None
897        else:
898            result.dxw = numpy.zeros(len(self.x))
899        if self.dxl == None:
900            result.dxl = None
901        else:
902            result.dxl = numpy.zeros(len(self.x))
903
[9198b83]904        for i in range(len(self.x)):
905            result.x[i] = self.x[i]
[a7a5886]906            if self.dx is not None and len(self.x) == len(self.dx):
[9198b83]907                result.dx[i] = self.dx[i]
[e2605a5]908            if self.dxw is not None and len(self.x) == len(self.dxw):
909                result.dxw[i] = self.dxw[i]
910            if self.dxl is not None and len(self.x) == len(self.dxl):
911                result.dxl[i] = self.dxl[i]
[e4f421c]912
[9198b83]913            a = Uncertainty(self.y[i], dy[i]**2)
914            if isinstance(other, Data1D):
915                b = Uncertainty(other.y[i], dy_other[i]**2)
[e2605a5]916                if other.dx is not None:
917                    result.dx[i] *= self.dx[i]
918                    result.dx[i] += (other.dx[i]**2)
919                    result.dx[i] /= 2
920                    result.dx[i] = math.sqrt(result.dx[i])
921                if result.dxl is not None and other.dxl is not None:
922                    result.dxl[i] *= self.dxl[i]
[a48842a2]923                    result.dxl[i] += (other.dxl[i]**2)
[e2605a5]924                    result.dxl[i] /= 2
925                    result.dxl[i] = math.sqrt(result.dxl[i])
[9198b83]926            else:
927                b = other
[e4f421c]928
[9198b83]929            output = operation(a, b)
930            result.y[i] = output.x
931            result.dy[i] = math.sqrt(math.fabs(output.variance))
932        return result
[e4f421c]933
[a48842a2]934    def _validity_check_union(self, other):
935        """
936        Checks that the data lengths are compatible.
937        Checks that the x vectors are compatible.
938        Returns errors vectors equal to original
939        errors vectors if they were present or vectors
940        of zeros when none was found.
[e4f421c]941
[a48842a2]942        :param other: other data set for operation
943        :return: bool
944        :raise ValueError: when data types are not compatible
945        """
946        if not isinstance(other, Data1D):
947            msg = "Unable to perform operation: different types of data set"
[e4f421c]948            raise ValueError, msg
[a48842a2]949        return True
950
951    def _perform_union(self, other):
952        """
953        """
954        # First, check the data compatibility
955        self._validity_check_union(other)
956        result = self.clone_without_data(len(self.x) + len(other.x))
957        if self.dy == None or other.dy is None:
958            result.dy = None
959        else:
960            result.dy = numpy.zeros(len(self.x) + len(other.x))
961        if self.dx == None or other.dx is None:
962            result.dx = None
963        else:
964            result.dx = numpy.zeros(len(self.x) + len(other.x))
965        if self.dxw == None or other.dxw is None:
966            result.dxw = None
967        else:
968            result.dxw = numpy.zeros(len(self.x) + len(other.x))
969        if self.dxl == None or other.dxl is None:
970            result.dxl = None
971        else:
972            result.dxl = numpy.zeros(len(self.x) + len(other.x))
973
974        result.x = numpy.append(self.x, other.x)
975        #argsorting
976        ind = numpy.argsort(result.x)
977        result.x = result.x[ind]
978        result.y = numpy.append(self.y, other.y)
979        result.y = result.y[ind]
980        if result.dy != None:
981            result.dy = numpy.append(self.dy, other.dy)
982            result.dy = result.dy[ind]
983        if result.dx is not None:
984            result.dx = numpy.append(self.dx, other.dx)
985            result.dx = result.dx[ind]
986        if result.dxw is not None:
987            result.dxw = numpy.append(self.dxw, other.dxw)
988            result.dxw = result.dxw[ind]
989        if result.dxl is not None:
990            result.dxl = numpy.append(self.dxl, other.dxl)
991            result.dxl = result.dxl[ind]
992        return result
[e4f421c]993
994
[7eaf9f2]995class Data2D(plottable_2D, DataInfo):
[99d1af6]996    """
[0997158f]997    2D data class
[99d1af6]998    """
999    ## Units for Q-values
[ca10d8e]1000    Q_unit = '1/A'
[99d1af6]1001    ## Units for I(Q) values
[ca10d8e]1002    I_unit = '1/cm'
[99d1af6]1003    ## Vector of Q-values at the center of each bin in x
[d6513cd]1004    x_bins = None
[99d1af6]1005    ## Vector of Q-values at the center of each bin in y
[d6513cd]1006    y_bins = None
[e4f421c]1007
[a7a5886]1008    def __init__(self, data=None, err_data=None, qx_data=None,
[f60a8c2]1009                 qy_data=None, q_data=None, mask=None,
1010                 dqx_data=None, dqy_data=None):
[d6513cd]1011        self.y_bins = []
1012        self.x_bins = []
[b99ac227]1013        DataInfo.__init__(self)
[a7a5886]1014        plottable_2D.__init__(self, data, err_data, qx_data,
[f60a8c2]1015                              qy_data, q_data, mask, dqx_data, dqy_data)
[a7a5886]1016        if len(self.detector) > 0:
[b99ac227]1017            raise RuntimeError, "Data2D: Detector bank already filled at init"
[99d1af6]1018
1019    def __str__(self):
[f60a8c2]1020        _str = "%s\n" % DataInfo.__str__(self)
[99d1af6]1021        _str += "Data:\n"
1022        _str += "   Type:         %s\n" % self.__class__.__name__
1023        _str += "   X- & Y-axis:  %s\t[%s]\n" % (self._yaxis, self._yunit)
1024        _str += "   Z-axis:       %s\t[%s]\n" % (self._zaxis, self._zunit)
[3cd95c8]1025        _str += "   Length:       %g \n" % (len(self.data))
[99d1af6]1026        return _str
[e4f421c]1027
[7d8094b]1028    def clone_without_data(self, length=0, clone=None):
[442f42f]1029        """
[0997158f]1030        Clone the current object, without copying the data (which
1031        will be filled out by a subsequent operation).
1032        The data arrays will be initialized to zero.
[e4f421c]1033
[0997158f]1034        :param length: length of the data array to be initialized
1035        :param clone: if provided, the data will be copied to clone
[442f42f]1036        """
1037        from copy import deepcopy
[e4f421c]1038
[f60a8c2]1039        if clone is None or not issubclass(clone.__class__, Data2D):
1040            data = numpy.zeros(length)
1041            err_data = numpy.zeros(length)
[3cd95c8]1042            qx_data = numpy.zeros(length)
1043            qy_data = numpy.zeros(length)
1044            q_data = numpy.zeros(length)
1045            mask = numpy.zeros(length)
1046            dqx_data = None
1047            dqy_data = None
[e4f421c]1048            clone = Data2D(data=data, err_data=err_data,
1049                           qx_data=qx_data, qy_data=qy_data,
[0008f54]1050                           q_data=q_data, mask=mask)
[3cd95c8]1051
[e4f421c]1052        clone.title = self.title
1053        clone.run = self.run
1054        clone.filename = self.filename
1055        clone.instrument = self.instrument
1056        clone.notes = deepcopy(self.notes)
1057        clone.process = deepcopy(self.process)
1058        clone.detector = deepcopy(self.detector)
1059        clone.sample = deepcopy(self.sample)
1060        clone.source = deepcopy(self.source)
[f60a8c2]1061        clone.collimation = deepcopy(self.collimation)
[e4f421c]1062        clone.meta_data = deepcopy(self.meta_data)
1063        clone.errors = deepcopy(self.errors)
1064
[442f42f]1065        return clone
[e4f421c]1066
[442f42f]1067    def _validity_check(self, other):
1068        """
[0997158f]1069        Checks that the data lengths are compatible.
1070        Checks that the x vectors are compatible.
1071        Returns errors vectors equal to original
1072        errors vectors if they were present or vectors
1073        of zeros when none was found.
[e4f421c]1074
[0997158f]1075        :param other: other data set for operation
1076        :return: dy for self, dy for other [numpy arrays]
1077        :raise ValueError: when lengths are not compatible
[442f42f]1078        """
1079        err_other = None
1080        if isinstance(other, Data2D):
1081            # Check that data lengths are the same
[dcf73a4]1082            if len(self.data) != len(other.data) or \
1083                len(self.qx_data) != len(other.qx_data) or \
1084                len(self.qy_data) != len(other.qy_data):
[a7a5886]1085                msg = "Unable to perform operation: data length are not equal"
1086                raise ValueError, msg
[dcf73a4]1087            for ind in range(len(self.data)):
1088                if self.qx_data[ind] != other.qx_data[ind]:
1089                    msg = "Incompatible data sets: qx-values do not match"
1090                    raise ValueError, msg
1091                if self.qy_data[ind] != other.qy_data[ind]:
1092                    msg = "Incompatible data sets: qy-values do not match"
1093                    raise ValueError, msg
[e4f421c]1094
[442f42f]1095            # Check that the scales match
1096            err_other = other.err_data
[a7a5886]1097            if other.err_data == None or \
[dcf73a4]1098                (len(other.err_data) != len(other.data)):
1099                err_other = numpy.zeros(len(other.data))
[e4f421c]1100
[442f42f]1101        # Check that we have errors, otherwise create zero vector
1102        err = self.err_data
[a7a5886]1103        if self.err_data == None or \
[dcf73a4]1104            (len(self.err_data) != len(self.data)):
1105            err = numpy.zeros(len(other.data))
[442f42f]1106        return err, err_other
[e4f421c]1107
[442f42f]1108    def _perform_operation(self, other, operation):
1109        """
[0997158f]1110        Perform 2D operations between data sets
[e4f421c]1111
[0997158f]1112        :param other: other data set
1113        :param operation: function defining the operation
[442f42f]1114        """
1115        # First, check the data compatibility
1116        dy, dy_other = self._validity_check(other)
[e2605a5]1117        result = self.clone_without_data(numpy.size(self.data))
1118        if self.dqx_data == None or self.dqy_data == None:
1119            result.dqx_data = None
1120            result.dqy_data = None
1121        else:
[cdeed9f]1122            result.dqx_data = numpy.zeros(len(self.data))
1123            result.dqy_data = numpy.zeros(len(self.data))
[e2605a5]1124        for i in range(numpy.size(self.data)):
[cdeed9f]1125            result.data[i] = self.data[i]
1126            if self.err_data is not None and \
1127                numpy.size(self.data) == numpy.size(self.err_data):
[e4f421c]1128                result.err_data[i] = self.err_data[i]
[cdeed9f]1129            if self.dqx_data is not None:
1130                result.dqx_data[i] = self.dqx_data[i]
1131            if self.dqy_data is not None:
1132                result.dqy_data[i] = self.dqy_data[i]
1133            result.qx_data[i] = self.qx_data[i]
1134            result.qy_data[i] = self.qy_data[i]
1135            result.q_data[i] = self.q_data[i]
1136            result.mask[i] = self.mask[i]
[e4f421c]1137
[e2605a5]1138            a = Uncertainty(self.data[i], dy[i]**2)
1139            if isinstance(other, Data2D):
1140                b = Uncertainty(other.data[i], dy_other[i]**2)
1141                if other.dqx_data is not None and \
1142                        result.dqx_data is not None:
1143                    result.dqx_data[i] *= self.dqx_data[i]
1144                    result.dqx_data[i] += (other.dqx_data[i]**2)
1145                    result.dqx_data[i] /= 2
[e4f421c]1146                    result.dqx_data[i] = math.sqrt(result.dqx_data[i])
[e2605a5]1147                if other.dqy_data is not None and \
1148                        result.dqy_data is not None:
1149                    result.dqy_data[i] *= self.dqy_data[i]
1150                    result.dqy_data[i] += (other.dqy_data[i]**2)
1151                    result.dqy_data[i] /= 2
1152                    result.dqy_data[i] = math.sqrt(result.dqy_data[i])
1153            else:
1154                b = other
1155            output = operation(a, b)
1156            result.data[i] = output.x
1157            result.err_data[i] = math.sqrt(math.fabs(output.variance))
[442f42f]1158        return result
[e4f421c]1159
[a48842a2]1160    def _validity_check_union(self, other):
1161        """
1162        Checks that the data lengths are compatible.
1163        Checks that the x vectors are compatible.
1164        Returns errors vectors equal to original
1165        errors vectors if they were present or vectors
1166        of zeros when none was found.
[e4f421c]1167
[a48842a2]1168        :param other: other data set for operation
1169        :return: bool
1170        :raise ValueError: when data types are not compatible
1171        """
1172        if not isinstance(other, Data2D):
1173            msg = "Unable to perform operation: different types of data set"
[e4f421c]1174            raise ValueError, msg
[a48842a2]1175        return True
[e4f421c]1176
[a48842a2]1177    def _perform_union(self, other):
1178        """
1179        Perform 2D operations between data sets
[e4f421c]1180
[a48842a2]1181        :param other: other data set
1182        :param operation: function defining the operation
1183        """
1184        # First, check the data compatibility
1185        self._validity_check_union(other)
1186        result = self.clone_without_data(numpy.size(self.data) + \
1187                                         numpy.size(other.data))
1188        result.xmin = self.xmin
1189        result.xmax = self.xmax
1190        result.ymin = self.ymin
1191        result.ymax = self.ymax
1192        if self.dqx_data == None or self.dqy_data == None or \
[e4f421c]1193                other.dqx_data == None or other.dqy_data == None:
[a48842a2]1194            result.dqx_data = None
1195            result.dqy_data = None
1196        else:
1197            result.dqx_data = numpy.zeros(len(self.data) + \
1198                                         numpy.size(other.data))
1199            result.dqy_data = numpy.zeros(len(self.data) + \
1200                                         numpy.size(other.data))
[e4f421c]1201
[a48842a2]1202        result.data = numpy.append(self.data, other.data)
1203        result.qx_data = numpy.append(self.qx_data, other.qx_data)
1204        result.qy_data = numpy.append(self.qy_data, other.qy_data)
1205        result.q_data = numpy.append(self.q_data, other.q_data)
1206        result.mask = numpy.append(self.mask, other.mask)
1207        if result.err_data is not None:
1208            result.err_data = numpy.append(self.err_data, other.err_data) 
1209        if self.dqx_data is not None:
1210            result.dqx_data = numpy.append(self.dqx_data, other.dqx_data)
1211        if self.dqy_data is not None:
1212            result.dqy_data = numpy.append(self.dqy_data, other.dqy_data)
1213
1214        return result
Note: See TracBrowser for help on using the repository browser.