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

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 a96d246 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
Line 
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
8copyright 2008, 2009, University of Tennessee
9"""
10# Known issue: reader not compatible with multiple SASdata entries
11# within a single SASentry. Will raise a runtime error.
12
13#TODO: check that all vectors are written only if they have at least one non-empty value
14#TODO: Writing only allows one SASentry per file. Would be best to allow multiple entries.
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.
19#TODO: Unit conversion is not complete (temperature units are missing)
20
21
22import logging
23import numpy
24import os, sys
25from DataLoader.data_info import Data1D, Collimation, Detector, Process, Aperture
26from lxml import etree
27import xml.dom.minidom
28
29has_converter = True
30try:
31    from data_util.nxsunit import Converter
32except:
33    has_converter = False
34
35CANSAS_NS = "cansas1d/1.0"
36
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
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
61        @return: Element, or None
62    """
63    nodes = node.xpath(location, namespaces={'ns': CANSAS_NS})
64   
65    if len(nodes)>0:
66        return nodes[0]
67    else:
68        return None
69
70def get_float(location, node):
71    """
72        Get the content of a node as a float
73       
74        @param location: xpath location
75        @param node: node to start at
76    """
77    nodes = node.xpath(location, namespaces={'ns': CANSAS_NS})
78   
79    value = None
80    attr = {}
81   
82    if len(nodes)>0:
83        try:
84            value = float(nodes[0].text)   
85        except:
86            # Could not pass, skip and return None
87            logging.error("cansas_reader.get_float: could not convert '%s' to float" % nodes[0].text)
88       
89        if nodes[0].get('unit') is not None:
90            attr['unit'] = nodes[0].get('unit')
91           
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    """
111    entry = get_content(location, node)
112    try:
113        value = float(entry.text)
114    except:
115        value = None
116       
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
120        units = entry.get('unit')
121        if units is not None:
122            toks = variable.split('.')
123            exec "local_unit = storage.%s_unit" % toks[0]
124            if units.lower()!=local_unit.lower():
125                if has_converter==True:
126                    try:
127                        conv = Converter(units)
128                        exec "storage.%s = %g" % (variable, conv(value, units=local_unit))
129                    except:
130                        raise ValueError, "CanSAS reader: could not convert %s unit [%s]; expecting [%s]\n  %s" \
131                        % (variable, units, local_unit, sys.exc_value)
132                else:
133                    raise ValueError, "CanSAS reader: unrecognized %s unit [%s]; expecting [%s]" \
134                        % (variable, units, local_unit)
135            else:
136                exec "storage.%s = value" % variable
137        else:
138            exec "storage.%s = value" % variable
139           
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    """
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
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
169    type_name = "CanSAS 1D"
170    ## Wildcards
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               
193                tree = etree.parse(path, parser=etree.ETCompatXMLParser())
194                # Check the format version number
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})
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       
228        # Look up title     
229        _store_content('ns:Title', dom, 'title', data_info)
230       
231        # Look up run number   
232        nodes = dom.xpath('ns:Run', namespaces={'ns': CANSAS_NS})
233        for item in nodes:   
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')
240                           
241        # Look up instrument name             
242        _store_content('ns:SASinstrument/ns:name', dom, 'instrument', data_info)
243
244        # Notes
245        note_list = dom.xpath('ns:SASnote', namespaces={'ns': CANSAS_NS})
246        for note in note_list:
247            try:
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)
252            except:
253                logging.error("cansas_reader.read: error processing entry notes\n  %s" % sys.exc_value)
254       
255        # Sample info ###################
256        entry = get_content('ns:SASsample', dom)
257        if entry is not None:
258            data_info.sample.name = entry.get('name')
259           
260        _store_content('ns:SASsample/ns:ID', 
261                     dom, 'ID', data_info.sample)                   
262        _store_float('ns:SASsample/ns:thickness', 
263                     dom, 'thickness', data_info.sample)
264        _store_float('ns:SASsample/ns:transmission', 
265                     dom, 'transmission', data_info.sample)
266        _store_float('ns:SASsample/ns:temperature', 
267                     dom, 'temperature', data_info.sample)
268       
269        nodes = dom.xpath('ns:SASsample/ns:details', namespaces={'ns': CANSAS_NS})
270        for item in nodes:
271            try:
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)
276            except:
277                logging.error("cansas_reader.read: error processing sample details\n  %s" % sys.exc_value)
278       
279        # Position (as a vector)
280        _store_float('ns:SASsample/ns:position/ns:x', 
281                     dom, 'position.x', data_info.sample)         
282        _store_float('ns:SASsample/ns:position/ns:y', 
283                     dom, 'position.y', data_info.sample)         
284        _store_float('ns:SASsample/ns:position/ns:z', 
285                     dom, 'position.z', data_info.sample)         
286       
287        # Orientation (as a vector)
288        _store_float('ns:SASsample/ns:orientation/ns:roll', 
289                     dom, 'orientation.x', data_info.sample)         
290        _store_float('ns:SASsample/ns:orientation/ns:pitch', 
291                     dom, 'orientation.y', data_info.sample)         
292        _store_float('ns:SASsample/ns:orientation/ns:yaw', 
293                     dom, 'orientation.z', data_info.sample)         
294       
295        # Source info ###################
296        entry = get_content('ns:SASinstrument/ns:SASsource', dom)
297        if entry is not None:
298            data_info.source.name = entry.get('name')
299       
300        _store_content('ns:SASinstrument/ns:SASsource/ns:radiation', 
301                     dom, 'radiation', data_info.source)                   
302        _store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape', 
303                     dom, 'beam_shape', data_info.source)                   
304        _store_float('ns:SASinstrument/ns:SASsource/ns:wavelength', 
305                     dom, 'wavelength', data_info.source)         
306        _store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min', 
307                     dom, 'wavelength_min', data_info.source)         
308        _store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max', 
309                     dom, 'wavelength_max', data_info.source)         
310        _store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread', 
311                     dom, 'wavelength_spread', data_info.source)   
312       
313        # Beam size (as a vector)   
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')
317           
318        _store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x', 
319                     dom, 'beam_size.x', data_info.source)   
320        _store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y', 
321                     dom, 'beam_size.y', data_info.source)   
322        _store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z', 
323                     dom, 'beam_size.z', data_info.source)   
324       
325        # Collimation info ###################
326        nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation', namespaces={'ns': CANSAS_NS})
327        for item in nodes:
328            collim = Collimation()
329            if item.get('name') is not None:
330                collim.name = item.get('name')
331            _store_float('ns:length', item, 'length', collim) 
332           
333            # Look for apertures
334            apert_list = item.xpath('ns:aperture', namespaces={'ns': CANSAS_NS})
335            for apert in apert_list:
336                aperture =  Aperture()
337               
338                # Get the name and type of the aperture
339                aperture.name = apert.get('name')
340                aperture.type = apert.get('type')
341                   
342                _store_float('ns:distance', apert, 'distance', aperture)   
343               
344                entry = get_content('ns:size', apert)
345                if entry is not None:
346                    aperture.size_name = entry.get('name')
347               
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)
351               
352                collim.aperture.append(aperture)
353               
354            data_info.collimation.append(collim)
355       
356        # Detector info ######################
357        nodes = dom.xpath('ns:SASinstrument/ns:SASdetector', namespaces={'ns': CANSAS_NS})
358        for item in nodes:
359           
360            detector = Detector()
361           
362            _store_content('ns:name', item, 'name', detector)
363            _store_float('ns:SDD', item, 'distance', detector)   
364           
365            # Detector offset (as a vector)
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)   
369           
370            # Detector orientation (as a vector)
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)   
374           
375            # Beam center (as a vector)
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)   
379           
380            # Pixel size (as a vector)
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)   
384           
385            _store_float('ns:slit_length', item, 'slit_length', detector)
386           
387            data_info.detector.append(detector)   
388
389        # Processes info ######################
390        nodes = dom.xpath('ns:SASprocess', namespaces={'ns': CANSAS_NS})
391        for item in nodes:
392            process = Process()
393            _store_content('ns:name', item, 'name', process)
394            _store_content('ns:date', item, 'date', process)
395            _store_content('ns:description', item, 'description', process)
396           
397            term_list = item.xpath('ns:term', namespaces={'ns': CANSAS_NS})
398            for term in term_list:
399                try:
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()
405                        process.term.append(term_attr)
406                except:
407                    logging.error("cansas_reader.read: error processing process term\n  %s" % sys.exc_value)
408           
409            note_list = item.xpath('ns:SASprocessnote', namespaces={'ns': CANSAS_NS})
410            for note in note_list:
411                if note.text is not None:
412                    process.notes.append(note.text.strip())
413           
414            data_info.process.append(process)
415           
416           
417        # Data info ######################
418        nodes = dom.xpath('ns:SASdata', namespaces={'ns': CANSAS_NS})
419        if len(nodes)>1:
420            raise RuntimeError, "CanSAS reader is not compatible with multiple SASdata entries"
421       
422        nodes = dom.xpath('ns:SASdata/ns:Idata', namespaces={'ns': CANSAS_NS})
423
424        x  = numpy.zeros(0)
425        y  = numpy.zeros(0)
426        dx = numpy.zeros(0)
427        dy = numpy.zeros(0)
428        dxw = numpy.zeros(0)
429        dxl = numpy.zeros(0)
430       
431        for item in nodes:
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)
436            if _dx == None:
437                _dx = 0.0
438            if _dxl == None:
439                _dxl = 0.0
440            if _dxw == None:
441                _dxw = 0.0
442               
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)
454            # Error in Q
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)
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)
490                   
491            _y, attr = get_float('ns:I', item)
492            _dy, attr_d = get_float('ns:Idev', item)
493            if _dy == None:
494                _dy = 0.0
495            if attr.has_key('unit') and attr['unit'].lower() != data_info.y_unit.lower():
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)
517               
518            if _x is not None and _y is not None:
519                x  = numpy.append(x, _x)
520                y  = numpy.append(y, _y)
521                dx = numpy.append(dx, _dx)
522                dy = numpy.append(dy, _dy)
523                dxl = numpy.append(dxl, _dxl)
524                dxw = numpy.append(dxw, _dxw)
525               
526           
527        data_info.x = x
528        data_info.y = y
529        data_info.dx = dx
530        data_info.dy = dy
531        data_info.dxl = dxl
532        data_info.dxw = dxw
533       
534        data_conv_q = None
535        data_conv_i = None
536       
537        if has_converter == True and data_info.x_unit != '1/A':
538            data_conv_q = Converter('1/A')
539            # Test it
540            data_conv_q(1.0, output.Q_unit)
541           
542        if has_converter == True and data_info.y_unit != '1/cm':
543            data_conv_i = Converter('1/cm')
544            # Test it
545            data_conv_i(1.0, output.I_unit)                   
546               
547        if data_conv_q is not None:
548            data_info.xaxis("\\rm{Q}", data_info.x_unit)
549        else:
550            data_info.xaxis("\\rm{Q}", 'A^{-1}')
551        if data_conv_i is not None:
552            data_info.yaxis("\\rm{Intensity}", data_info.y_unit)
553        else:
554            data_info.yaxis("\\rm{Intensity}","cm^{-1}")
555       
556        return data_info
557
558    def _to_xml_doc(self, datainfo):
559        """
560            Create an XML document to contain the content of a Data1D
561           
562            @param datainfo: Data1D object
563        """
564       
565        if not issubclass(datainfo.__class__, Data1D):
566            raise RuntimeError, "The cansas writer expects a Data1D instance"
567       
568        doc = xml.dom.minidom.Document()
569        main_node = doc.createElement("SASroot")
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       
575        doc.appendChild(main_node)
576       
577        entry_node = doc.createElement("SASentry")
578        main_node.appendChild(entry_node)
579       
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)
587       
588        # Data info
589        node = doc.createElement("SASdata")
590        entry_node.appendChild(node)
591       
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})
598            if datainfo.dx !=None and len(datainfo.dx)>=i:
599                write_node(doc, pt, "Qdev", datainfo.dx[i], {'unit':datainfo.x_unit})
600            if datainfo.dy !=None and len(datainfo.dy)>=i:
601                write_node(doc, pt, "Idev", datainfo.dy[i], {'unit':datainfo.y_unit})
602
603       
604        # Sample info
605        sample = doc.createElement("SASsample")
606        if datainfo.sample.name is not None:
607            sample.setAttribute("name", str(datainfo.sample.name))
608        entry_node.appendChild(sample)
609        write_node(doc, sample, "ID", str(datainfo.sample.ID))
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")
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})
621        if written == True:
622            sample.appendChild(pos)
623       
624        ori = doc.createElement("orientation")
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})
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")
639        if datainfo.source.name is not None:
640            source.setAttribute("name", str(datainfo.source.name))
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)
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           
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")
662            if item.name is not None:
663                coll.setAttribute("name", str(item.name))
664            instr.appendChild(coll)
665           
666            write_node(doc, coll, "length", item.length, {"unit":item.length_unit})
667           
668            for apert in item.aperture:
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)
675               
676                write_node(doc, ap, "distance", apert.distance, {"unit":apert.distance_unit})
677               
678                size = doc.createElement("size")
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)
686
687        #   Detectors
688        for item in datainfo.detector:
689            det = doc.createElement("SASdetector")
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)
695           
696            off = doc.createElement("offset")
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)
702           
703            center = doc.createElement("beam_center")
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               
710            pix = doc.createElement("pixel_size")
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               
724       
725        # Processes info
726        for item in datainfo.process:
727            node = doc.createElement("SASprocess")
728            entry_node.appendChild(node)
729
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)
739       
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)
753        # Write the file
754        fd = open(filename, 'w')
755        fd.write(doc.toprettyxml())
756        fd.close()
757       
758       
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")
766    #print reader.read("../test/latex_smeared.xml")
767   
768   
769                       
Note: See TracBrowser for help on using the repository browser.