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

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

set x=0 data point as x=1e-16 instead of skip reading

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