source: sasview/src/sas/dataloader/data_info.py @ 59b1b92

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 59b1b92 was 5e326a6, checked in by jhbakker, 10 years ago

added sesans_reader for SESANS analysis

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