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

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

working on documentation

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