source: sasview/src/sas/dataloader/data_info.py @ e4f421c

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

pylint fixes

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