source: sasview/DataLoader/readers/cansas_reader.py @ da9ac4e6

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

working pylint

  • Property mode set to 100644
File size: 40.1 KB
RevLine 
[8780e9a]1
2
[0997158f]3############################################################################
4#This software was developed by the University of Tennessee as part of the
5#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
6#project funded by the US National Science Foundation.
7#If you use DANSE applications to do scientific research that leads to
8#publication, we ask that you acknowledge the use of the software with the
9#following sentence:
10#This work benefited from DANSE software developed under NSF award DMR-0520547.
11#copyright 2008,2009 University of Tennessee
12#############################################################################
13
[579ba85]14# Known issue: reader not compatible with multiple SASdata entries
15# within a single SASentry. Will raise a runtime error.
[8780e9a]16
[a7a5886]17#TODO: check that all vectors are written only if they have at
18#    least one non-empty value
19#TODO: Writing only allows one SASentry per file.
20#     Would be best to allow multiple entries.
[8780e9a]21#TODO: Store error list
22#TODO: Allow for additional meta data for each section
[a7a5886]23#TODO: Notes need to be implemented. They can be any XML
24#    structure in version 1.0
[8780e9a]25#      Process notes have the same problem.
[e390933]26#TODO: Unit conversion is not complete (temperature units are missing)
[8780e9a]27
28
29import logging
30import numpy
[a7a5886]31import os
32import sys
33from DataLoader.data_info import Data1D
34from DataLoader.data_info import Collimation
35from DataLoader.data_info import Detector
36from DataLoader.data_info import Process
37from DataLoader.data_info import Aperture
[b0d0723]38from lxml import etree
39import xml.dom.minidom
[8780e9a]40
[b39c817]41has_converter = True
42try:
43    from data_util.nxsunit import Converter
44except:
45    has_converter = False
46
[b0d0723]47CANSAS_NS = "cansas1d/1.0"
48
[4c00964]49def write_node(doc, parent, name, value, attr={}):
50    """
[0997158f]51    :param doc: document DOM
52    :param parent: parent node
53    :param name: tag of the element
54    :param value: value of the child text node
55    :param attr: attribute dictionary
56   
57    :return: True if something was appended, otherwise False
[4c00964]58    """
59    if value is not None:
60        node = doc.createElement(name)
61        node.appendChild(doc.createTextNode(str(value)))
62        for item in attr:
63            node.setAttribute(item, attr[item])
64        parent.appendChild(node)
65        return True
66    return False
67
[8780e9a]68def get_content(location, node):
69    """
[0997158f]70    Get the first instance of the content of a xpath location.
71   
72    :param location: xpath location
73    :param node: node to start at
74   
75    :return: Element, or None
[8780e9a]76    """
[b0d0723]77    nodes = node.xpath(location, namespaces={'ns': CANSAS_NS})
78   
[8780e9a]79    if len(nodes)>0:
[b0d0723]80        return nodes[0]
81    else:
82        return None
[8780e9a]83
84def get_float(location, node):
85    """
[0997158f]86    Get the content of a node as a float
87   
88    :param location: xpath location
89    :param node: node to start at
[8780e9a]90    """
[b0d0723]91    nodes = node.xpath(location, namespaces={'ns': CANSAS_NS})
92   
[8780e9a]93    value = None
94    attr = {}
[a7a5886]95    if len(nodes) > 0:
[8780e9a]96        try:
[b0d0723]97            value = float(nodes[0].text)   
[8780e9a]98        except:
99            # Could not pass, skip and return None
[a7a5886]100            msg = "cansas_reader.get_float: could not "
101            msg += " convert '%s' to float" % nodes[0].text
102            logging.error(msg)
[b0d0723]103        if nodes[0].get('unit') is not None:
104            attr['unit'] = nodes[0].get('unit')
[8780e9a]105    return value, attr
106
[b39c817]107           
[8780e9a]108class Reader:
109    """
[0997158f]110    Class to load cansas 1D XML files
111   
112    :Dependencies:
113        The CanSas reader requires PyXML 0.8.4 or later.
[8780e9a]114    """
115    ## CanSAS version
116    version = '1.0'
117    ## File type
[28caa03]118    type_name = "CanSAS 1D"
119    ## Wildcards
[7406040]120    type = ["CanSAS 1D files (*.xml)|*.xml",
121                        "CanSAS 1D AVE files (*.AVEx)|*.AVEx",
122                         "CanSAS 1D AVE files (*.ABSx)|*.ABSx"]
123
[8780e9a]124    ## List of allowed extensions
[a7a5886]125    ext = ['.xml', '.XML','.avex', '.AVEx', '.absx', 'ABSx'] 
[8780e9a]126   
[fe78c7b]127    def __init__(self):
128        ## List of errors
129        self.errors = []
130   
[8780e9a]131    def read(self, path):
132        """
[0997158f]133        Load data file
134       
135        :param path: file path
136       
137        :return: Data1D object if a single SASentry was found,
138                    or a list of Data1D objects if multiple entries were found,
139                    or None of nothing was found
140                   
141        :raise RuntimeError: when the file can't be opened
142        :raise ValueError: when the length of the data vectors are inconsistent
[8780e9a]143        """
144        output = []
145        if os.path.isfile(path):
146            basename  = os.path.basename(path)
147            root, extension = os.path.splitext(basename)
148            if extension.lower() in self.ext:
149               
[b0d0723]150                tree = etree.parse(path, parser=etree.ETCompatXMLParser())
[8780e9a]151                # Check the format version number
[a7a5886]152                # Specifying the namespace will take care of the file
153                # format version
[b0d0723]154                root = tree.getroot()
155               
[a7a5886]156                entry_list = root.xpath('/ns:SASroot/ns:SASentry',
157                                         namespaces={'ns': CANSAS_NS})
[8780e9a]158               
159                for entry in entry_list:
[fe78c7b]160                    self.errors = []
[8780e9a]161                    sas_entry = self._parse_entry(entry)
162                    sas_entry.filename = basename
[fe78c7b]163                   
164                    # Store loading process information
165                    sas_entry.errors = self.errors
166                    sas_entry.meta_data['loader'] = self.type_name
[8780e9a]167                    output.append(sas_entry)
168               
169        else:
170            raise RuntimeError, "%s is not a file" % path
171        # Return output consistent with the loader's api
172        if len(output)==0:
173            return None
174        elif len(output)==1:
175            return output[0]
176        else:
177            return output               
178               
179    def _parse_entry(self, dom):
180        """
[0997158f]181        Parse a SASentry
182       
183        :param node: SASentry node
184       
185        :return: Data1D object
[8780e9a]186        """
187        x = numpy.zeros(0)
188        y = numpy.zeros(0)
189       
190        data_info = Data1D(x, y)
191       
[b0d0723]192        # Look up title     
[fe78c7b]193        self._store_content('ns:Title', dom, 'title', data_info)
[b0d0723]194       
[579ba85]195        # Look up run number   
[b0d0723]196        nodes = dom.xpath('ns:Run', namespaces={'ns': CANSAS_NS})
[579ba85]197        for item in nodes:   
[b0d0723]198            if item.text is not None:
199                value = item.text.strip()
200                if len(value) > 0:
201                    data_info.run.append(value)
202                    if item.get('name') is not None:
203                        data_info.run_name[value] = item.get('name')
[579ba85]204                           
[8780e9a]205        # Look up instrument name             
[a7a5886]206        self._store_content('ns:SASinstrument/ns:name', dom, 'instrument',
207                             data_info)
[8780e9a]208
[b0d0723]209        # Notes
210        note_list = dom.xpath('ns:SASnote', namespaces={'ns': CANSAS_NS})
[8780e9a]211        for note in note_list:
212            try:
[b0d0723]213                if note.text is not None:
214                    note_value = note.text.strip()
215                    if len(note_value) > 0:
216                        data_info.notes.append(note_value)
[8780e9a]217            except:
[a7a5886]218                err_mess = "cansas_reader.read: error processing"
219                err_mess += " entry notes\n  %s" % sys.exc_value
[fe78c7b]220                self.errors.append(err_mess)
221                logging.error(err_mess)
[8780e9a]222       
223        # Sample info ###################
[b0d0723]224        entry = get_content('ns:SASsample', dom)
225        if entry is not None:
226            data_info.sample.name = entry.get('name')
[579ba85]227           
[fe78c7b]228        self._store_content('ns:SASsample/ns:ID', 
[8780e9a]229                     dom, 'ID', data_info.sample)                   
[fe78c7b]230        self._store_float('ns:SASsample/ns:thickness', 
[8780e9a]231                     dom, 'thickness', data_info.sample)
[fe78c7b]232        self._store_float('ns:SASsample/ns:transmission', 
[8780e9a]233                     dom, 'transmission', data_info.sample)
[fe78c7b]234        self._store_float('ns:SASsample/ns:temperature', 
[8780e9a]235                     dom, 'temperature', data_info.sample)
[b0d0723]236       
[a7a5886]237        nodes = dom.xpath('ns:SASsample/ns:details', 
238                          namespaces={'ns': CANSAS_NS})
[8780e9a]239        for item in nodes:
240            try:
[b0d0723]241                if item.text is not None:
242                    detail_value = item.text.strip()
243                    if len(detail_value) > 0:
244                        data_info.sample.details.append(detail_value)
[8780e9a]245            except:
[a7a5886]246                err_mess = "cansas_reader.read: error processing "
247                err_mess += " sample details\n  %s" % sys.exc_value
[fe78c7b]248                self.errors.append(err_mess)
249                logging.error(err_mess)
[8780e9a]250       
251        # Position (as a vector)
[fe78c7b]252        self._store_float('ns:SASsample/ns:position/ns:x', 
[8780e9a]253                     dom, 'position.x', data_info.sample)         
[fe78c7b]254        self._store_float('ns:SASsample/ns:position/ns:y', 
[8780e9a]255                     dom, 'position.y', data_info.sample)         
[fe78c7b]256        self._store_float('ns:SASsample/ns:position/ns:z', 
[8780e9a]257                     dom, 'position.z', data_info.sample)         
258       
259        # Orientation (as a vector)
[fe78c7b]260        self._store_float('ns:SASsample/ns:orientation/ns:roll', 
[8780e9a]261                     dom, 'orientation.x', data_info.sample)         
[fe78c7b]262        self._store_float('ns:SASsample/ns:orientation/ns:pitch', 
[8780e9a]263                     dom, 'orientation.y', data_info.sample)         
[fe78c7b]264        self._store_float('ns:SASsample/ns:orientation/ns:yaw', 
[8780e9a]265                     dom, 'orientation.z', data_info.sample)         
266       
267        # Source info ###################
[b0d0723]268        entry = get_content('ns:SASinstrument/ns:SASsource', dom)
269        if entry is not None:
270            data_info.source.name = entry.get('name')
[4c00964]271       
[fe78c7b]272        self._store_content('ns:SASinstrument/ns:SASsource/ns:radiation', 
[8780e9a]273                     dom, 'radiation', data_info.source)                   
[fe78c7b]274        self._store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape', 
[8780e9a]275                     dom, 'beam_shape', data_info.source)                   
[fe78c7b]276        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength', 
[8780e9a]277                     dom, 'wavelength', data_info.source)         
[fe78c7b]278        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min', 
[8780e9a]279                     dom, 'wavelength_min', data_info.source)         
[fe78c7b]280        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max', 
[8780e9a]281                     dom, 'wavelength_max', data_info.source)         
[fe78c7b]282        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread', 
[8780e9a]283                     dom, 'wavelength_spread', data_info.source)   
284       
[579ba85]285        # Beam size (as a vector)   
[b0d0723]286        entry = get_content('ns:SASinstrument/ns:SASsource/ns:beam_size', dom)
287        if entry is not None:
288            data_info.source.beam_size_name = entry.get('name')
[579ba85]289           
[fe78c7b]290        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x', 
[8780e9a]291                     dom, 'beam_size.x', data_info.source)   
[fe78c7b]292        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y', 
[8780e9a]293                     dom, 'beam_size.y', data_info.source)   
[fe78c7b]294        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z', 
[8780e9a]295                     dom, 'beam_size.z', data_info.source)   
296       
297        # Collimation info ###################
[a7a5886]298        nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation', 
299                          namespaces={'ns': CANSAS_NS})
[8780e9a]300        for item in nodes:
301            collim = Collimation()
[b0d0723]302            if item.get('name') is not None:
303                collim.name = item.get('name')
[fe78c7b]304            self._store_float('ns:length', item, 'length', collim) 
[8780e9a]305           
306            # Look for apertures
[b0d0723]307            apert_list = item.xpath('ns:aperture', namespaces={'ns': CANSAS_NS})
[8780e9a]308            for apert in apert_list:
[d6513cd]309                aperture =  Aperture()
[4c00964]310               
311                # Get the name and type of the aperture
[b0d0723]312                aperture.name = apert.get('name')
313                aperture.type = apert.get('type')
[4c00964]314                   
[fe78c7b]315                self._store_float('ns:distance', apert, 'distance', aperture)   
[579ba85]316               
[b0d0723]317                entry = get_content('ns:size', apert)
318                if entry is not None:
319                    aperture.size_name = entry.get('name')
[579ba85]320               
[fe78c7b]321                self._store_float('ns:size/ns:x', apert, 'size.x', aperture)   
322                self._store_float('ns:size/ns:y', apert, 'size.y', aperture)   
323                self._store_float('ns:size/ns:z', apert, 'size.z', aperture)
[8780e9a]324               
325                collim.aperture.append(aperture)
326               
327            data_info.collimation.append(collim)
328       
329        # Detector info ######################
[a7a5886]330        nodes = dom.xpath('ns:SASinstrument/ns:SASdetector',
331                           namespaces={'ns': CANSAS_NS})
[8780e9a]332        for item in nodes:
333           
334            detector = Detector()
335           
[fe78c7b]336            self._store_content('ns:name', item, 'name', detector)
337            self._store_float('ns:SDD', item, 'distance', detector)   
[8780e9a]338           
339            # Detector offset (as a vector)
[fe78c7b]340            self._store_float('ns:offset/ns:x', item, 'offset.x', detector)   
341            self._store_float('ns:offset/ns:y', item, 'offset.y', detector)   
342            self._store_float('ns:offset/ns:z', item, 'offset.z', detector)   
[8780e9a]343           
344            # Detector orientation (as a vector)
[a7a5886]345            self._store_float('ns:orientation/ns:roll',  item, 'orientation.x',
346                               detector)   
347            self._store_float('ns:orientation/ns:pitch', item, 'orientation.y',
348                               detector)   
349            self._store_float('ns:orientation/ns:yaw',   item, 'orientation.z',
350                               detector)   
[8780e9a]351           
352            # Beam center (as a vector)
[a7a5886]353            self._store_float('ns:beam_center/ns:x', item, 'beam_center.x',
354                               detector)   
355            self._store_float('ns:beam_center/ns:y', item, 'beam_center.y', 
356                              detector)   
357            self._store_float('ns:beam_center/ns:z', item, 'beam_center.z',
358                               detector)   
[8780e9a]359           
360            # Pixel size (as a vector)
[a7a5886]361            self._store_float('ns:pixel_size/ns:x', item, 'pixel_size.x',
362                               detector)   
363            self._store_float('ns:pixel_size/ns:y', item, 'pixel_size.y',
364                               detector)   
365            self._store_float('ns:pixel_size/ns:z', item, 'pixel_size.z',
366                               detector)   
[8780e9a]367           
[fe78c7b]368            self._store_float('ns:slit_length', item, 'slit_length', detector)
[8780e9a]369           
370            data_info.detector.append(detector)   
371
372        # Processes info ######################
[b0d0723]373        nodes = dom.xpath('ns:SASprocess', namespaces={'ns': CANSAS_NS})
[8780e9a]374        for item in nodes:
375            process = Process()
[fe78c7b]376            self._store_content('ns:name', item, 'name', process)
377            self._store_content('ns:date', item, 'date', process)
378            self._store_content('ns:description', item, 'description', process)
[8780e9a]379           
[b0d0723]380            term_list = item.xpath('ns:term', namespaces={'ns': CANSAS_NS})
[8780e9a]381            for term in term_list:
382                try:
[b0d0723]383                    term_attr = {}
384                    for attr in term.keys():
385                        term_attr[attr] = term.get(attr).strip()
386                    if term.text is not None:
387                        term_attr['value'] = term.text.strip()
[8780e9a]388                        process.term.append(term_attr)
389                except:
[a7a5886]390                    err_mess = "cansas_reader.read: error processing "
391                    err_mess += " process term\n  %s" % sys.exc_value
[fe78c7b]392                    self.errors.append(err_mess)
393                    logging.error(err_mess)
[8780e9a]394           
[a7a5886]395            note_list = item.xpath('ns:SASprocessnote', 
396                                   namespaces={'ns': CANSAS_NS})
[8780e9a]397            for note in note_list:
[b0d0723]398                if note.text is not None:
399                    process.notes.append(note.text.strip())
[8780e9a]400           
401            data_info.process.append(process)
402           
403           
404        # Data info ######################
[b0d0723]405        nodes = dom.xpath('ns:SASdata', namespaces={'ns': CANSAS_NS})
[a7a5886]406        if len(nodes) > 1:
407            msg = "CanSAS reader is not compatible with multiple"
408            msg += " SASdata entries"
409            raise RuntimeError, msg
[579ba85]410       
[b0d0723]411        nodes = dom.xpath('ns:SASdata/ns:Idata', namespaces={'ns': CANSAS_NS})
412
[8780e9a]413        x  = numpy.zeros(0)
414        y  = numpy.zeros(0)
415        dx = numpy.zeros(0)
416        dy = numpy.zeros(0)
[d00f8ff]417        dxw = numpy.zeros(0)
418        dxl = numpy.zeros(0)
[8780e9a]419       
420        for item in nodes:
[b0d0723]421            _x, attr = get_float('ns:Q', item)
422            _dx, attr_d = get_float('ns:Qdev', item)
423            _dxl, attr_l = get_float('ns:dQl', item)
424            _dxw, attr_w = get_float('ns:dQw', item)
[8780e9a]425            if _dx == None:
426                _dx = 0.0
[d00f8ff]427            if _dxl == None:
428                _dxl = 0.0
429            if _dxw == None:
430                _dxw = 0.0
[8780e9a]431               
[a7a5886]432            if attr.has_key('unit') and \
433                attr['unit'].lower() != data_info.x_unit.lower():
[e390933]434                if has_converter==True:
435                    try:
436                        data_conv_q = Converter(attr['unit'])
437                        _x = data_conv_q(_x, units=data_info.x_unit)
438                    except:
[a7a5886]439                        msg =  "CanSAS reader: could not convert "
440                        msg += "Q unit [%s]; " 
441                        msg += "expecting [%s]\n  %s" % (attr['unit'], 
442                                  data_info.x_unit, sys.exc_value)
443                        raise ValueError, msg
444                       
[e390933]445                else:
[a7a5886]446                    msg = "CanSAS reader: unrecognized Q unit [%s]; "
447                    msg += "expecting [%s]" % (attr['unit'], data_info.x_unit)
448                    raise ValueError, msg
449                       
[d00f8ff]450            # Error in Q
[a7a5886]451            if attr_d.has_key('unit') and \
452                attr_d['unit'].lower() != data_info.x_unit.lower():
[e390933]453                if has_converter==True:
454                    try:
455                        data_conv_q = Converter(attr_d['unit'])
456                        _dx = data_conv_q(_dx, units=data_info.x_unit)
457                    except:
[a7a5886]458                        msg = "CanSAS reader: could not convert dQ unit [%s];"
459                        msg += " expecting " 
460                        msg += "[%s]\n  %s" % (attr['unit'],
461                                                data_info.x_unit, sys.exc_value)
462                        raise ValueError, msg
463                       
[e390933]464                else:
[a7a5886]465                    msg = "CanSAS reader: unrecognized dQ unit [%s]; "
466                    msg += "expecting [%s]" % (attr['unit'], data_info.x_unit)
467                    raise ValueError,  msg
468                       
[d00f8ff]469            # Slit length
[a7a5886]470            if attr_l.has_key('unit') and \
471                attr_l['unit'].lower() != data_info.x_unit.lower():
472                if has_converter == True:
[d00f8ff]473                    try:
474                        data_conv_q = Converter(attr_l['unit'])
475                        _dxl = data_conv_q(_dxl, units=data_info.x_unit)
476                    except:
[a7a5886]477                        msg = "CanSAS reader: could not convert dQl unit [%s];"
478                        msg += " expecting [%s]\n  %s" % (attr['unit'],
479                                             data_info.x_unit, sys.exc_value)
480                        raise ValueError, msg
481                       
[d00f8ff]482                else:
[a7a5886]483                    msg = "CanSAS reader: unrecognized dQl unit [%s];"
484                    msg += " expecting [%s]" % (attr['unit'], data_info.x_unit)
485                    raise ValueError, msg
486                       
[d00f8ff]487            # Slit width
[a7a5886]488            if attr_w.has_key('unit') and \
489            attr_w['unit'].lower() != data_info.x_unit.lower():
490                if has_converter == True:
[d00f8ff]491                    try:
492                        data_conv_q = Converter(attr_w['unit'])
493                        _dxw = data_conv_q(_dxw, units=data_info.x_unit)
494                    except:
[a7a5886]495                        msg = "CanSAS reader: could not convert dQw unit [%s];"
496                        msg += " expecting [%s]\n  %s" % (attr['unit'], 
497                                                data_info.x_unit, sys.exc_value)
498                        raise ValueError, msg
499                       
[d00f8ff]500                else:
[a7a5886]501                    msg = "CanSAS reader: unrecognized dQw unit [%s];"
502                    msg += " expecting [%s]" % (attr['unit'], data_info.x_unit)
503                    raise ValueError, msg   
[b0d0723]504            _y, attr = get_float('ns:I', item)
505            _dy, attr_d = get_float('ns:Idev', item)
[8780e9a]506            if _dy == None:
507                _dy = 0.0
[a7a5886]508            if attr.has_key('unit') and \
509            attr['unit'].lower() != data_info.y_unit.lower():
[e390933]510                if has_converter==True:
511                    try:
512                        data_conv_i = Converter(attr['unit'])
513                        _y = data_conv_i(_y, units=data_info.y_unit)
514                    except:
[a7a5886]515                        msg = "CanSAS reader: could not convert I(q) unit [%s];"
516                        msg += " expecting [%s]\n  %s" % (attr['unit'], 
517                                            data_info.y_unit, sys.exc_value)
518                        raise ValueError, msg
[e390933]519                else:
[a7a5886]520                    msg = "CanSAS reader: unrecognized I(q) unit [%s];"
521                    msg += " expecting [%s]" % (attr['unit'], data_info.y_unit)
522                    raise ValueError, msg
523                       
524            if attr_d.has_key('unit') and \
525            attr_d['unit'].lower() != data_info.y_unit.lower():
[e390933]526                if has_converter==True:
527                    try:
528                        data_conv_i = Converter(attr_d['unit'])
529                        _dy = data_conv_i(_dy, units=data_info.y_unit)
530                    except:
[a7a5886]531                        msg = "CanSAS reader: could not convert dI(q) unit "
532                        msg += "[%s]; expecting [%s]\n  %s"  % (attr_d['unit'],
533                                             data_info.y_unit, sys.exc_value)
534                        raise ValueError, msg
[e390933]535                else:
[a7a5886]536                    msg = "CanSAS reader: unrecognized dI(q) unit [%s]; "
537                    msg += "expecting [%s]" % (attr_d['unit'], data_info.y_unit)
538                    raise ValueError, msg
[8780e9a]539               
540            if _x is not None and _y is not None:
541                x  = numpy.append(x, _x)
[579ba85]542                y  = numpy.append(y, _y)
543                dx = numpy.append(dx, _dx)
544                dy = numpy.append(dy, _dy)
[d00f8ff]545                dxl = numpy.append(dxl, _dxl)
546                dxw = numpy.append(dxw, _dxw)
547               
[8780e9a]548        data_info.x = x
549        data_info.y = y
550        data_info.dx = dx
551        data_info.dy = dy
[d00f8ff]552        data_info.dxl = dxl
553        data_info.dxw = dxw
[d6513cd]554       
555        data_conv_q = None
556        data_conv_i = None
557       
[ca10d8e]558        if has_converter == True and data_info.x_unit != '1/A':
559            data_conv_q = Converter('1/A')
[d6513cd]560            # Test it
561            data_conv_q(1.0, output.Q_unit)
562           
[ca10d8e]563        if has_converter == True and data_info.y_unit != '1/cm':
564            data_conv_i = Converter('1/cm')
[d6513cd]565            # Test it
[e390933]566            data_conv_i(1.0, output.I_unit)                   
567               
[99d1af6]568        if data_conv_q is not None:
[d6513cd]569            data_info.xaxis("\\rm{Q}", data_info.x_unit)
[99d1af6]570        else:
571            data_info.xaxis("\\rm{Q}", 'A^{-1}')
572        if data_conv_i is not None:
[0e2aa40]573            data_info.yaxis("\\rm{Intensity}", data_info.y_unit)
[99d1af6]574        else:
[0e2aa40]575            data_info.yaxis("\\rm{Intensity}","cm^{-1}")
[99d1af6]576       
[8780e9a]577        return data_info
578
[b3de3a45]579    def _to_xml_doc(self, datainfo):
[4c00964]580        """
[0997158f]581        Create an XML document to contain the content of a Data1D
582       
583        :param datainfo: Data1D object
[4c00964]584        """
585       
[7d8094b]586        if not issubclass(datainfo.__class__, Data1D):
[4c00964]587            raise RuntimeError, "The cansas writer expects a Data1D instance"
588       
589        doc = xml.dom.minidom.Document()
590        main_node = doc.createElement("SASroot")
[fee780b]591        main_node.setAttribute("version", self.version)
592        main_node.setAttribute("xmlns", "cansas1d/%s" % self.version)
[a7a5886]593        main_node.setAttribute("xmlns:xsi",
594                               "http://www.w3.org/2001/XMLSchema-instance")
595        main_node.setAttribute("xsi:schemaLocation",
596                               "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.version)
[fee780b]597       
[4c00964]598        doc.appendChild(main_node)
599       
600        entry_node = doc.createElement("SASentry")
601        main_node.appendChild(entry_node)
602       
[579ba85]603        write_node(doc, entry_node, "Title", datainfo.title)
604        for item in datainfo.run:
605            runname = {}
[a7a5886]606            if datainfo.run_name.has_key(item) and \
607            len(str(datainfo.run_name[item]))>1:
[579ba85]608                runname = {'name': datainfo.run_name[item] }
609            write_node(doc, entry_node, "Run", item, runname)
[4c00964]610       
611        # Data info
612        node = doc.createElement("SASdata")
613        entry_node.appendChild(node)
614       
[579ba85]615        for i in range(len(datainfo.x)):
616            pt = doc.createElement("Idata")
617            node.appendChild(pt)
618            write_node(doc, pt, "Q", datainfo.x[i], {'unit':datainfo.x_unit})
619            if len(datainfo.y)>=i:
[a7a5886]620                write_node(doc, pt, "I", datainfo.y[i],
621                            {'unit':datainfo.y_unit})
[5b396b3]622            if datainfo.dx !=None and len(datainfo.dx)>=i:
[a7a5886]623                write_node(doc, pt, "Qdev", datainfo.dx[i],
624                            {'unit':datainfo.x_unit})
[f31701c]625            if datainfo.dy !=None and len(datainfo.dy)>=i:
[a7a5886]626                write_node(doc, pt, "Idev", datainfo.dy[i],
627                            {'unit':datainfo.y_unit})
[579ba85]628
629       
[4c00964]630        # Sample info
631        sample = doc.createElement("SASsample")
[579ba85]632        if datainfo.sample.name is not None:
633            sample.setAttribute("name", str(datainfo.sample.name))
[4c00964]634        entry_node.appendChild(sample)
[579ba85]635        write_node(doc, sample, "ID", str(datainfo.sample.ID))
[a7a5886]636        write_node(doc, sample, "thickness", datainfo.sample.thickness,
637                   {"unit":datainfo.sample.thickness_unit})
[4c00964]638        write_node(doc, sample, "transmission", datainfo.sample.transmission)
[a7a5886]639        write_node(doc, sample, "temperature", datainfo.sample.temperature,
640                   {"unit":datainfo.sample.temperature_unit})
[4c00964]641       
642        for item in datainfo.sample.details:
643            write_node(doc, sample, "details", item)
644       
645        pos = doc.createElement("position")
[a7a5886]646        written = write_node(doc, pos, "x", datainfo.sample.position.x,
647                             {"unit":datainfo.sample.position_unit})
648        written = written | write_node(doc, pos, "y",
649                                       datainfo.sample.position.y,
650                                       {"unit":datainfo.sample.position_unit})
651        written = written | write_node(doc, pos, "z",
652                                       datainfo.sample.position.z,
653                                       {"unit":datainfo.sample.position_unit})
[4c00964]654        if written == True:
655            sample.appendChild(pos)
656       
657        ori = doc.createElement("orientation")
[a7a5886]658        written = write_node(doc, ori, "roll",
659                             datainfo.sample.orientation.x,
660                             {"unit":datainfo.sample.orientation_unit})
661        written = written | write_node(doc, ori, "pitch",
662                                       datainfo.sample.orientation.y,
663                                    {"unit":datainfo.sample.orientation_unit})
664        written = written | write_node(doc, ori, "yaw",
665                                       datainfo.sample.orientation.z,
666                                    {"unit":datainfo.sample.orientation_unit})
[4c00964]667        if written == True:
668            sample.appendChild(ori)
669       
670        # Instrument info
671        instr = doc.createElement("SASinstrument")
672        entry_node.appendChild(instr)
673       
674        write_node(doc, instr, "name", datainfo.instrument)
675       
676        #   Source
677        source = doc.createElement("SASsource")
[579ba85]678        if datainfo.source.name is not None:
679            source.setAttribute("name", str(datainfo.source.name))
[4c00964]680        instr.appendChild(source)
681       
682        write_node(doc, source, "radiation", datainfo.source.radiation)
683        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
[579ba85]684        size = doc.createElement("beam_size")
685        if datainfo.source.beam_size_name is not None:
686            size.setAttribute("name", str(datainfo.source.beam_size_name))
[a7a5886]687        written = write_node(doc, size, "x", datainfo.source.beam_size.x,
688                             {"unit":datainfo.source.beam_size_unit})
689        written = written | write_node(doc, size, "y",
690                                       datainfo.source.beam_size.y,
691                                       {"unit":datainfo.source.beam_size_unit})
692        written = written | write_node(doc, size, "z",
693                                       datainfo.source.beam_size.z,
694                                       {"unit":datainfo.source.beam_size_unit})
[579ba85]695        if written == True:
696            source.appendChild(size)
697           
[a7a5886]698        write_node(doc, source, "wavelength",
699                   datainfo.source.wavelength,
700                   {"unit":datainfo.source.wavelength_unit})
701        write_node(doc, source, "wavelength_min",
702                   datainfo.source.wavelength_min,
703                   {"unit":datainfo.source.wavelength_min_unit})
704        write_node(doc, source, "wavelength_max",
705                   datainfo.source.wavelength_max,
706                   {"unit":datainfo.source.wavelength_max_unit})
707        write_node(doc, source, "wavelength_spread",
708                   datainfo.source.wavelength_spread,
709                   {"unit":datainfo.source.wavelength_spread_unit})
[4c00964]710       
711        #   Collimation
712        for item in datainfo.collimation:
713            coll = doc.createElement("SAScollimation")
[579ba85]714            if item.name is not None:
715                coll.setAttribute("name", str(item.name))
[4c00964]716            instr.appendChild(coll)
717           
[a7a5886]718            write_node(doc, coll, "length", item.length,
719                       {"unit":item.length_unit})
[4c00964]720           
721            for apert in item.aperture:
[579ba85]722                ap = doc.createElement("aperture")
723                if apert.name is not None:
724                    ap.setAttribute("name", str(apert.name))
725                if apert.type is not None:
726                    ap.setAttribute("type", str(apert.type))
727                coll.appendChild(ap)
[4c00964]728               
[a7a5886]729                write_node(doc, ap, "distance", apert.distance,
730                           {"unit":apert.distance_unit})
[4c00964]731               
732                size = doc.createElement("size")
[579ba85]733                if apert.size_name is not None:
734                    size.setAttribute("name", str(apert.size_name))
[a7a5886]735                written = write_node(doc, size, "x", apert.size.x,
736                                     {"unit":apert.size_unit})
737                written = written | write_node(doc, size, "y", apert.size.y,
738                                               {"unit":apert.size_unit})
739                written = written | write_node(doc, size, "z", apert.size.z,
740                                               {"unit":apert.size_unit})
[579ba85]741                if written == True:
742                    ap.appendChild(size)
[4c00964]743
744        #   Detectors
745        for item in datainfo.detector:
746            det = doc.createElement("SASdetector")
[579ba85]747            written = write_node(doc, det, "name", item.name)
[a7a5886]748            written = written | write_node(doc, det, "SDD", item.distance,
749                                           {"unit":item.distance_unit})
750            written = written | write_node(doc, det, "slit_length",
751                                           item.slit_length,
752                                           {"unit":item.slit_length_unit})
[579ba85]753            if written == True:
754                instr.appendChild(det)
[4c00964]755           
756            off = doc.createElement("offset")
[a7a5886]757            written = write_node(doc, off, "x", item.offset.x,
758                                 {"unit":item.offset_unit})
759            written = written | write_node(doc, off, "y", item.offset.y,
760                                           {"unit":item.offset_unit})
761            written = written | write_node(doc, off, "z", item.offset.z,
762                                           {"unit":item.offset_unit})
[579ba85]763            if written == True:
764                det.appendChild(off)
[4c00964]765           
766            center = doc.createElement("beam_center")
[a7a5886]767            written = write_node(doc, center, "x", item.beam_center.x,
768                                 {"unit":item.beam_center_unit})
769            written = written | write_node(doc, center, "y",
770                                           item.beam_center.y,
771                                           {"unit":item.beam_center_unit})
772            written = written | write_node(doc, center, "z",
773                                           item.beam_center.z,
774                                           {"unit":item.beam_center_unit})
[579ba85]775            if written == True:
776                det.appendChild(center)
777               
[4c00964]778            pix = doc.createElement("pixel_size")
[a7a5886]779            written = write_node(doc, pix, "x", item.pixel_size.x,
780                                 {"unit":item.pixel_size_unit})
781            written = written | write_node(doc, pix, "y", item.pixel_size.y,
782                                           {"unit":item.pixel_size_unit})
783            written = written | write_node(doc, pix, "z", item.pixel_size.z,
784                                           {"unit":item.pixel_size_unit})
[579ba85]785            if written == True:
786                det.appendChild(pix)
787               
788            ori = doc.createElement("orientation")
[a7a5886]789            written = write_node(doc, ori, "roll",  item.orientation.x,
790                                 {"unit":item.orientation_unit})
791            written = written | write_node(doc, ori, "pitch",
792                                           item.orientation.y,
793                                           {"unit":item.orientation_unit})
794            written = written | write_node(doc, ori, "yaw",
795                                           item.orientation.z,
796                                           {"unit":item.orientation_unit})
[579ba85]797            if written == True:
798                det.appendChild(ori)
799               
[4c00964]800       
[579ba85]801        # Processes info
[4c00964]802        for item in datainfo.process:
803            node = doc.createElement("SASprocess")
804            entry_node.appendChild(node)
805
[579ba85]806            write_node(doc, node, "name", item.name)
807            write_node(doc, node, "date", item.date)
808            write_node(doc, node, "description", item.description)
809            for term in item.term:
810                value = term['value']
811                del term['value']
812                write_node(doc, node, "term", value, term)
813            for note in item.notes:
814                write_node(doc, node, "SASprocessnote", note)
[4c00964]815       
[b3de3a45]816        # Return the document, and the SASentry node associated with
817        # the data we just wrote
818        return doc, entry_node
819           
820    def write(self, filename, datainfo):
821        """
[0997158f]822        Write the content of a Data1D as a CanSAS XML file
823       
824        :param filename: name of the file to write
825        :param datainfo: Data1D object
[b3de3a45]826        """
827        # Create XML document
828        doc, sasentry = self._to_xml_doc(datainfo)
[4c00964]829        # Write the file
830        fd = open(filename, 'w')
831        fd.write(doc.toprettyxml())
832        fd.close()
833       
[fe78c7b]834    def _store_float(self, location, node, variable, storage, optional=True):
835        """
[0997158f]836        Get the content of a xpath location and store
837        the result. Check that the units are compatible
838        with the destination. The value is expected to
839        be a float.
840       
841        The xpath location might or might not exist.
842        If it does not exist, nothing is done
843       
844        :param location: xpath location to fetch
845        :param node: node to read the data from
846        :param variable: name of the data member to store it in [string]
847        :param storage: data object that has the 'variable' data member
[a7a5886]848        :param optional: if True, no exception will be raised
849            if unit conversion can't be done
[0997158f]850
851        :raise ValueError: raised when the units are not recognized
[fe78c7b]852        """
853        entry = get_content(location, node)
854        try:
855            value = float(entry.text)
856        except:
857            value = None
858           
859        if value is not None:
860            # If the entry has units, check to see that they are
861            # compatible with what we currently have in the data object
862            units = entry.get('unit')
863            if units is not None:
864                toks = variable.split('.')
865                exec "local_unit = storage.%s_unit" % toks[0]
866                if units.lower()!=local_unit.lower():
867                    if has_converter==True:
868                        try:
869                            conv = Converter(units)
[a7a5886]870                            exec "storage.%s = %g" % (variable,
871                                            conv(value, units=local_unit))
[fe78c7b]872                        except:
[a7a5886]873                            err_mess = "CanSAS reader: could not convert"
874                            err_mess += " %s unit [%s]; expecting [%s]\n  %s" \
[fe78c7b]875                                % (variable, units, local_unit, sys.exc_value)
876                            self.errors.append(err_mess)
877                            if optional:
878                                logging.info(err_mess)
879                            else:
880                                raise ValueError, err_mess
881                    else:
[a7a5886]882                        err_mess = "CanSAS reader: unrecognized %s unit [%s];"
883                        err_mess += " expecting [%s]" % (variable, 
884                                                         units, local_unit)
[fe78c7b]885                        self.errors.append(err_mess)
886                        if optional:
887                            logging.info(err_mess)
888                        else:
889                            raise ValueError, err_mess
890                else:
891                    exec "storage.%s = value" % variable
892            else:
893                exec "storage.%s = value" % variable
894               
895    def _store_content(self, location, node, variable, storage):
896        """
[0997158f]897        Get the content of a xpath location and store
898        the result. The value is treated as a string.
899       
900        The xpath location might or might not exist.
901        If it does not exist, nothing is done
902       
903        :param location: xpath location to fetch
904        :param node: node to read the data from
905        :param variable: name of the data member to store it in [string]
906        :param storage: data object that has the 'variable' data member
907       
908        :return: return a list of errors
[fe78c7b]909        """
910        entry = get_content(location, node)
911        if entry is not None and entry.text is not None:
912            exec "storage.%s = entry.text.strip()" % variable
913
914           
915           
[8780e9a]916if __name__ == "__main__": 
917    logging.basicConfig(level=logging.ERROR,
918                        format='%(asctime)s %(levelname)s %(message)s',
919                        filename='cansas_reader.log',
920                        filemode='w')
921    reader = Reader()
922    print reader.read("../test/cansas1d.xml")
[b0d0723]923    #print reader.read("../test/latex_smeared.xml")
[8780e9a]924   
925   
926                       
Note: See TracBrowser for help on using the repository browser.