source: sasview/DataLoader/data_info.py @ ec1c554

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 ec1c554 was 0997158f, checked in by Gervaise Alina <gervyh@…>, 15 years ago

working on documentation

  • Property mode set to 100644
File size: 27.1 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
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
20
21
22#TODO: Keep track of data manipulation in the 'process' data structure.
23#TODO: This module should be independent of plottables. We should write
24#        an adapter class for plottables when needed.
25
26#from sans.guitools.plottables import Data1D as plottable_1D
27from data_util.uncertainty import Uncertainty
28import numpy
29import math
30
31class plottable_1D:
32    """
33    Data1D is a place holder for 1D plottables.
34    """
35    # The presence of these should be mutually exclusive with the presence of Qdev (dx)
36    x = None
37    y = None
38    dx = None
39    dy = None
40    ## Slit smearing length
41    dxl = None
42    ## Slit smearing width
43    dxw = None
44   
45    # Units
46    _xaxis = ''
47    _xunit = ''
48    _yaxis = ''
49    _yunit = ''
50   
51    def __init__(self,x,y,dx=None,dy=None,dxl=None,dxw=None):
52        self.x = numpy.asarray(x)
53        self.y = numpy.asarray(y)
54        if dx is not None: self.dx = numpy.asarray(dx)
55        if dy is not None: self.dy = numpy.asarray(dy)
56        if dxl is not None: self.dxl = numpy.asarray(dxl)
57        if dxw is not None: self.dxw = numpy.asarray(dxw)
58
59    def xaxis(self, label, unit):
60        self._xaxis = label
61        self._xunit = unit
62       
63    def yaxis(self, label, unit):
64        self._yaxis = label
65        self._yunit = unit
66
67class plottable_2D:
68    """
69    Data2D is a place holder for 2D plottables.
70    """
71    xmin = None
72    xmax = None
73    ymin = None
74    ymax = None
75    data = None
76    qx_data     = None
77    qy_data     = None
78    q_data      = None
79    err_data    = None
80    dqx_data    = None
81    dqy_data    = None
82    mask        = None
83   
84    # Units
85    _xaxis = ''
86    _xunit = ''
87    _yaxis = ''
88    _yunit = ''
89    _zaxis = ''
90    _zunit = ''
91   
92    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):
93        self.data = numpy.asarray(data)
94        self.qx_data = numpy.asarray(qx_data)
95        self.qy_data = numpy.asarray(qy_data)
96        self.q_data = numpy.asarray(q_data)
97        self.mask = numpy.asarray(mask)
98        self.err_data = numpy.asarray(err_data)
99        if dqx_data is not None: self.dqx_data = numpy.asarray(dqx_data) 
100        if dqy_data is not None: self.dqy_data = numpy.asarray(dqy_data) 
101               
102    def xaxis(self, label, unit):
103        self._xaxis = label
104        self._xunit = unit
105       
106    def yaxis(self, label, unit):
107        self._yaxis = label
108        self._yunit = unit
109           
110    def zaxis(self, label, unit):
111        self._zaxis = label
112        self._zunit = unit
113
114           
115class Vector:
116    """
117    Vector class to hold multi-dimensional objects
118    """
119    ## x component
120    x = None
121    ## y component
122    y = None
123    ## z component
124    z = None
125   
126    def __init__(self, x=None, y=None, z=None):
127        """
128        Initialization. Components that are not
129        set a set to None by default.
130       
131        :param x: x component
132        :param y: y component
133        :param z: z component
134       
135        """
136        self.x = x
137        self.y = y
138        self.z = z
139       
140    def __str__(self):
141        return "x = %s\ty = %s\tz = %s" % (str(self.x), str(self.y), str(self.z))
142       
143
144class Detector:
145    """
146    Class to hold detector information
147    """
148    ## Name of the instrument [string]
149    name = None
150    ## Sample to detector distance [float] [mm]
151    distance = None
152    distance_unit = 'mm'
153    ## Offset of this detector position in X, Y, (and Z if necessary) [Vector] [mm]
154    offset = None
155    offset_unit = 'm'
156    ## Orientation (rotation) of this detector in roll, pitch, and yaw [Vector] [degrees]
157    orientation = None
158    orientation_unit = 'degree'
159    ## Center of the beam on the detector in X and Y (and Z if necessary) [Vector] [mm]
160    beam_center = None
161    beam_center_unit = 'mm'
162    ## Pixel size in X, Y, (and Z if necessary) [Vector] [mm]
163    pixel_size = None
164    pixel_size_unit = 'mm'
165    ## Slit length of the instrument for this detector.[float] [mm]
166    slit_length = None
167    slit_length_unit = 'mm'
168   
169    def __init__(self):
170        """
171       
172        Initialize class attribute that are objects...
173       
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"""
291Definitions 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       
481        :return: new data set
482       
483        :raise ValueError: raised when two data sets are incompatible
484       
485        """
486        def operation(a, b): return b+a
487        return self._perform_operation(other, operation)
488       
489    def __sub__(self, other):
490        """
491        Subtract two data sets
492       
493        :param other: data set to subtract from the current one
494       
495        :return: new data set
496       
497        :raise ValueError: raised when two data sets are incompatible
498       
499        """
500        def operation(a, b): return a-b
501        return self._perform_operation(other, operation)
502       
503    def __rsub__(self, other):
504        """
505        Subtract two data sets
506       
507        :param other: data set to subtract from the current one
508       
509        :return: new data set
510       
511        :raise ValueError: raised when two data sets are incompatible
512       
513        """
514        def operation(a, b): return b-a
515        return self._perform_operation(other, operation)
516       
517    def __mul__(self, other):
518        """
519        Multiply two data sets
520       
521        :param other: data set to subtract from the current one
522       
523        :return: new data set
524       
525        :raise ValueError: raised when two data sets are incompatible
526       
527        """
528        def operation(a, b): return a*b
529        return self._perform_operation(other, operation)
530       
531    def __rmul__(self, other):
532        """
533        Multiply two data sets
534       
535        :param other: data set to subtract from the current one
536       
537        :return: new data set
538       
539        :raise ValueError: raised when two data sets are incompatible
540        """
541        def operation(a, b): return b*a
542        return self._perform_operation(other, operation)
543       
544    def __div__(self, other):
545        """
546        Divided a data set by another
547       
548        :param other: data set that the current one is divided by
549       
550        :return: new data set
551       
552        :raise ValueError: raised when two data sets are incompatible
553       
554        """
555        def operation(a, b): return a/b
556        return self._perform_operation(other, operation)
557       
558    def __rdiv__(self, other):
559        """
560        Divided a data set by another
561       
562        :param other: data set that the current one is divided by
563       
564        :return: new data set
565       
566        :raise ValueError: raised when two data sets are incompatible
567       
568        """
569        def operation(a, b): return b/a
570        return self._perform_operation(other, operation)           
571           
572class Data1D(plottable_1D, DataInfo):
573    """
574    1D data class
575    """
576    x_unit = '1/A'
577    y_unit = '1/cm'
578   
579    def __init__(self, x, y, dx=None, dy=None):
580        DataInfo.__init__(self)
581        plottable_1D.__init__(self, x, y, dx, dy)
582       
583       
584    def __str__(self):
585        """
586        Nice printout
587        """
588        _str =  "%s\n" % DataInfo.__str__(self)
589   
590        _str += "Data:\n"
591        _str += "   Type:         %s\n" % self.__class__.__name__
592        _str += "   X-axis:       %s\t[%s]\n" % (self._xaxis, self._xunit)
593        _str += "   Y-axis:       %s\t[%s]\n" % (self._yaxis, self._yunit)
594        _str += "   Length:       %g\n" % len(self.x)
595
596        return _str
597
598    def is_slit_smeared(self):
599        """
600        Check whether the data has slit smearing information
601       
602        :return: True is slit smearing info is present, False otherwise
603       
604        """
605        def _check(v):           
606            if (v.__class__==list or v.__class__==numpy.ndarray) \
607                and len(v)>0 and min(v)>0:
608                return True
609           
610            return False
611       
612        return _check(self.dxl) or _check(self.dxw)
613       
614    def clone_without_data(self, length=0, clone=None):
615        """
616        Clone the current object, without copying the data (which
617        will be filled out by a subsequent operation).
618        The data arrays will be initialized to zero.
619       
620        :param length: length of the data array to be initialized
621        :param clone: if provided, the data will be copied to clone
622        """
623        from copy import deepcopy
624       
625        if clone is None or not issubclass(clone.__class__, Data1D):
626            x  = numpy.zeros(length) 
627            dx = numpy.zeros(length) 
628            y  = numpy.zeros(length) 
629            dy = numpy.zeros(length) 
630            clone = Data1D(x, y, dx=dx, dy=dy)
631       
632        clone.title       = self.title
633        clone.run         = self.run
634        clone.filename    = self.filename
635        clone.notes       = deepcopy(self.notes) 
636        clone.process     = deepcopy(self.process) 
637        clone.detector    = deepcopy(self.detector) 
638        clone.sample      = deepcopy(self.sample) 
639        clone.source      = deepcopy(self.source) 
640        clone.collimation = deepcopy(self.collimation) 
641        clone.meta_data   = deepcopy(self.meta_data) 
642        clone.errors      = deepcopy(self.errors) 
643       
644        return clone
645
646    def _validity_check(self, other):
647        """
648        Checks that the data lengths are compatible.
649        Checks that the x vectors are compatible.
650        Returns errors vectors equal to original
651        errors vectors if they were present or vectors
652        of zeros when none was found.
653       
654        :param other: other data set for operation
655       
656        :return: dy for self, dy for other [numpy arrays]
657       
658        :raise ValueError: when lengths are not compatible
659       
660        """
661        dy_other = None
662        if isinstance(other, Data1D):
663            # Check that data lengths are the same
664            if len(self.x) != len(other.x) or \
665                len(self.y) != len(other.y):
666                raise ValueError, "Unable to perform operation: data length are not equal"
667           
668            # Here we could also extrapolate between data points
669            for i in range(len(self.x)):
670                if self.x[i] != other.x[i]:
671                    raise ValueError, "Incompatible data sets: x-values do not match"
672           
673            # Check that the other data set has errors, otherwise
674            # create zero vector
675            dy_other = other.dy
676            if other.dy==None or (len(other.dy) != len(other.y)):
677                dy_other = numpy.zeros(len(other.y))
678           
679        # Check that we have errors, otherwise create zero vector
680        dy = self.dy
681        if self.dy==None or (len(self.dy) != len(self.y)):
682            dy = numpy.zeros(len(self.y))           
683           
684        return dy, dy_other
685
686    def _perform_operation(self, other, operation):
687        """
688        """
689        # First, check the data compatibility
690        dy, dy_other = self._validity_check(other)
691        result = self.clone_without_data(len(self.x))
692       
693        for i in range(len(self.x)):
694            result.x[i] = self.x[i]
695            if self.dx is not None and len(self.x)==len(self.dx):
696                result.dx[i] = self.dx[i]
697           
698            a = Uncertainty(self.y[i], dy[i]**2)
699            if isinstance(other, Data1D):
700                b = Uncertainty(other.y[i], dy_other[i]**2)
701            else:
702                b = other
703           
704            output = operation(a, b)
705            result.y[i] = output.x
706            result.dy[i] = math.sqrt(math.fabs(output.variance))
707        return result
708       
709class Data2D(plottable_2D, DataInfo):
710    """
711    2D data class
712    """
713    ## Units for Q-values
714    Q_unit = '1/A'
715   
716    ## Units for I(Q) values
717    I_unit = '1/cm'
718   
719    ## Vector of Q-values at the center of each bin in x
720    x_bins = None
721   
722    ## Vector of Q-values at the center of each bin in y
723    y_bins = None
724   
725   
726    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):
727        self.y_bins = []
728        self.x_bins = []
729        DataInfo.__init__(self)
730        plottable_2D.__init__(self, data, err_data, qx_data, qy_data, q_data,mask, dqx_data, dqy_data)
731        if len(self.detector)>0:
732            raise RuntimeError, "Data2D: Detector bank already filled at init"
733
734    def __str__(self):
735        _str =  "%s\n" % DataInfo.__str__(self)
736       
737        _str += "Data:\n"
738        _str += "   Type:         %s\n" % self.__class__.__name__
739        _str += "   X- & Y-axis:  %s\t[%s]\n" % (self._yaxis, self._yunit)
740        _str += "   Z-axis:       %s\t[%s]\n" % (self._zaxis, self._zunit)
741        leny = 0
742        if len(self.data)>0:
743            leny = len(self.data)
744        _str += "   Length:       %g \n" % (len(self.data))
745       
746        return _str
747 
748    def clone_without_data(self, length=0, clone=None):
749        """
750        Clone the current object, without copying the data (which
751        will be filled out by a subsequent operation).
752        The data arrays will be initialized to zero.
753       
754        :param length: length of the data array to be initialized
755        :param clone: if provided, the data will be copied to clone
756        """
757        from copy import deepcopy
758       
759        if clone is None or not issubclass(clone.__class__, Data2D): 
760            data     = numpy.zeros(length) 
761            err_data = numpy.zeros(length) 
762            qx_data = numpy.zeros(length)
763            qy_data = numpy.zeros(length)
764            q_data = numpy.zeros(length)
765            mask = numpy.zeros(length)
766            dqx_data = None
767            dqy_data = None
768            clone = Data2D(data, err_data, qx_data, qy_data, q_data,mask, dqx_data=dqx_data, dqy_data=dqy_data)
769
770        clone.title       = self.title
771        clone.run         = self.run
772        clone.filename    = self.filename
773        clone.notes       = deepcopy(self.notes) 
774        clone.process     = deepcopy(self.process) 
775        clone.detector    = deepcopy(self.detector) 
776        clone.sample      = deepcopy(self.sample) 
777        clone.source      = deepcopy(self.source) 
778        clone.collimation = deepcopy(self.collimation) 
779        clone.meta_data   = deepcopy(self.meta_data) 
780        clone.errors      = deepcopy(self.errors) 
781       
782        return clone
783 
784 
785    def _validity_check(self, other):
786        """
787        Checks that the data lengths are compatible.
788        Checks that the x vectors are compatible.
789        Returns errors vectors equal to original
790        errors vectors if they were present or vectors
791        of zeros when none was found.
792       
793        :param other: other data set for operation
794       
795        :return: dy for self, dy for other [numpy arrays]
796       
797        :raise ValueError: when lengths are not compatible
798       
799        """
800        err_other = None
801        if isinstance(other, Data2D):
802            # Check that data lengths are the same
803            if numpy.size(self.data) != numpy.size(other.data):
804                raise ValueError, "Unable to perform operation: data length are not equal"
805               
806            # Check that the scales match
807            #TODO: matching scales?     
808           
809            # Check that the other data set has errors, otherwise
810            # create zero vector
811            #TODO: test this
812            err_other = other.err_data
813            if other.err_data==None or (numpy.size(other.err_data) != numpy.size(other.data)):
814                err_other = numpy.zeros([numpy.size(other.data,0), numpy.size(other.data,1)])
815           
816        # Check that we have errors, otherwise create zero vector
817        err = self.err_data
818        if self.err_data==None or (numpy.size(self.err_data) != numpy.size(self.data)):
819            err = numpy.zeros([numpy.size(self.data,0), numpy.size(self.data,1)])
820           
821        return err, err_other
822 
823 
824    def _perform_operation(self, other, operation):
825        """
826        Perform 2D operations between data sets
827       
828        :param other: other data set
829        :param operation: function defining the operation
830       
831        """
832        # First, check the data compatibility
833        dy, dy_other = self._validity_check(other)
834   
835        result = self.clone_without_data([numpy.size(self.data,0), numpy.size(self.data,1)])
836       
837        for i in range(numpy.size(self.data,0)):
838            for j in range(numpy.size(self.data,1)):
839                result.data[i][j] = self.data[i][j]
840                if self.err_data is not None and numpy.size(self.data)==numpy.size(self.err_data):
841                    result.err_data[i][j] = self.err_data[i][j]
842               
843                a = Uncertainty(self.data[i][j], dy[i][j]**2)
844                if isinstance(other, Data2D):
845                    b = Uncertainty(other.data[i][j], dy_other[i][j]**2)
846                else:
847                    b = other
848               
849                output = operation(a, b)
850                result.data[i][j] = output.x
851                result.err_data[i][j] = math.sqrt(math.fabs(output.variance))
852        return result
853   
854 
855 
Note: See TracBrowser for help on using the repository browser.