source: sasview/DataLoader/data_info.py @ effce1d

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 effce1d was a7a5886, checked in by Gervaise Alina <gervyh@…>, 14 years ago

working pylint

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