source: sasview/sansdataloader/src/sans/dataloader/data_info.py @ 0320d36

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 0320d36 was bd365666, checked in by Jae Cho <jhjcho@…>, 12 years ago

added qx_data, qy_data checking

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