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

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 0a5c8f5 was 0a5c8f5, checked in by Jae Cho <jhjcho@…>, 12 years ago

accept non standard extension for xml

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