source: sasview/DataLoader/readers/cansas_reader.py @ 8e36cdd

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 8e36cdd was b0d0723, checked in by Mathieu Doucet <doucetm@…>, 15 years ago

dataloader: ported cansas reader to lxml

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