source: sasview/DataLoader/data_info.py @ 54180f9

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 54180f9 was 3cd95c8, checked in by Jae Cho <jhjcho@…>, 14 years ago

new sans qmap 2D reader that enables reading TOF sans data as well as some other type of detector data

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