source: sasview/sansdataloader/src/sans/dataloader/readers/cansas_reader.py @ d4895dd

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 d4895dd was 0371eae, checked in by Jae Cho <jhjcho@…>, 13 years ago

fixing pylint warnings

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