source: sasview/guiframe/state_reader.py @ 9c169f4

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 9c169f4 was a07e72f, checked in by Gervaise Alina <gervyh@…>, 14 years ago

remove other type of data into sansview

  • Property mode set to 100644
File size: 70.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.guiframe.dataFitting import Data1D
34from sans.guiframe.dataFitting import Data2D
35from DataLoader.data_info import Collimation
36from DataLoader.data_info import Detector
37from DataLoader.data_info import Process
38from DataLoader.data_info import Aperture
39from lxml import etree
40import xml.dom.minidom
41from DataLoader.readers.cansas_reader import Reader as CansasReader
42has_converter = True
43try:
44    from data_util.nxsunit import Converter
45except:
46    has_converter = False
47
48CANSAS_NS = "cansas1d/1.0"
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 Reader1D(CansasReader):
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 = []
122
123    ## List of allowed extensions
124    ext = [] 
125   
126    def __init__(self):
127        CansasReader.__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                        msg = "CanSAS reader: could not convert I(q) unit [%s];"
518                        msg += " expecting [%s]\n  %s" % (attr['unit'], 
519                                            data_info.y_unit, sys.exc_value)
520                        raise ValueError, msg
521                else:
522                    msg = "CanSAS reader: unrecognized I(q) unit [%s];"
523                    msg += " expecting [%s]" % (attr['unit'], data_info.y_unit)
524                    raise ValueError, msg
525                       
526            if attr_d.has_key('unit') and \
527            attr_d['unit'].lower() != data_info.y_unit.lower():
528                if has_converter==True:
529                    try:
530                        data_conv_i = Converter(attr_d['unit'])
531                        _dy = data_conv_i(_dy, units=data_info.y_unit)
532                    except:
533                        msg = "CanSAS reader: could not convert dI(q) unit "
534                        msg += "[%s]; expecting [%s]\n  %s"  % (attr_d['unit'],
535                                             data_info.y_unit, sys.exc_value)
536                        raise ValueError, msg
537                else:
538                    msg = "CanSAS reader: unrecognized dI(q) unit [%s]; "
539                    msg += "expecting [%s]" % (attr_d['unit'], data_info.y_unit)
540                    raise ValueError, msg
541               
542            if _x is not None and _y is not None:
543                x  = numpy.append(x, _x)
544                y  = numpy.append(y, _y)
545                dx = numpy.append(dx, _dx)
546                dy = numpy.append(dy, _dy)
547                dxl = numpy.append(dxl, _dxl)
548                dxw = numpy.append(dxw, _dxw)
549               
550        data_info.x = x
551        data_info.y = y
552        data_info.dx = dx
553        data_info.dy = dy
554        data_info.dxl = dxl
555        data_info.dxw = dxw
556       
557        data_conv_q = None
558        data_conv_i = None
559       
560        if has_converter == True and data_info.x_unit != '1/A':
561            data_conv_q = Converter('1/A')
562            # Test it
563            data_conv_q(1.0, output.Q_unit)
564           
565        if has_converter == True and data_info.y_unit != '1/cm':
566            data_conv_i = Converter('1/cm')
567            # Test it
568            data_conv_i(1.0, output.I_unit)                   
569               
570        if data_conv_q is not None:
571            data_info.xaxis("\\rm{Q}", data_info.x_unit)
572        else:
573            data_info.xaxis("\\rm{Q}", 'A^{-1}')
574        if data_conv_i is not None:
575            data_info.yaxis("\\rm{Intensity}", data_info.y_unit)
576        else:
577            data_info.yaxis("\\rm{Intensity}","cm^{-1}")
578       
579        return data_info
580
581    def _to_xml_doc(self, datainfo):
582        """
583        Create an XML document to contain the content of a Data1D
584       
585        :param datainfo: Data1D object
586        """
587       
588        if not issubclass(datainfo.__class__, Data1D):
589            raise RuntimeError, "The cansas writer expects a Data1D instance"
590       
591        doc = xml.dom.minidom.Document()
592        main_node = doc.createElement("SASroot")
593        main_node.setAttribute("version", self.version)
594        main_node.setAttribute("xmlns", "cansas1d/%s" % self.version)
595        main_node.setAttribute("xmlns:xsi",
596                               "http://www.w3.org/2001/XMLSchema-instance")
597        main_node.setAttribute("xsi:schemaLocation",
598                               "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.version)
599       
600        doc.appendChild(main_node)
601       
602        entry_node = doc.createElement("SASentry")
603        main_node.appendChild(entry_node)
604       
605        write_node(doc, entry_node, "Title", datainfo.title)
606        for item in datainfo.run:
607            runname = {}
608            if datainfo.run_name.has_key(item) and \
609            len(str(datainfo.run_name[item]))>1:
610                runname = {'name': datainfo.run_name[item] }
611            write_node(doc, entry_node, "Run", item, runname)
612       
613        # Data info
614        node = doc.createElement("SASdata")
615        entry_node.appendChild(node)
616       
617        for i in range(len(datainfo.x)):
618            pt = doc.createElement("Idata")
619            node.appendChild(pt)
620            write_node(doc, pt, "Q", datainfo.x[i], {'unit':datainfo.x_unit})
621            if len(datainfo.y)>=i:
622                write_node(doc, pt, "I", datainfo.y[i],
623                            {'unit':datainfo.y_unit})
624            if datainfo.dx != None and len(datainfo.dx) >= i:
625                write_node(doc, pt, "Qdev", datainfo.dx[i],
626                            {'unit':datainfo.x_unit})
627            if datainfo.dxl != None and len(datainfo.dxl) >= i:
628                write_node(doc, pt, "dQl", datainfo.dxl[i],
629                            {'unit':datainfo.x_unit})   
630            if datainfo.dxw != None and len(datainfo.dxw) >= i:
631                write_node(doc, pt, "dQw", datainfo.dxw[i],
632                            {'unit':datainfo.x_unit})             
633            if datainfo.dy != None and len(datainfo.dy) >= i:
634                write_node(doc, pt, "Idev", datainfo.dy[i],
635                            {'unit':datainfo.y_unit})
636
637       
638        # Sample info
639        sample = doc.createElement("SASsample")
640        if datainfo.sample.name is not None:
641            sample.setAttribute("name", str(datainfo.sample.name))
642        entry_node.appendChild(sample)
643        write_node(doc, sample, "ID", str(datainfo.sample.ID))
644        write_node(doc, sample, "thickness", datainfo.sample.thickness,
645                   {"unit":datainfo.sample.thickness_unit})
646        write_node(doc, sample, "transmission", datainfo.sample.transmission)
647        write_node(doc, sample, "temperature", datainfo.sample.temperature,
648                   {"unit":datainfo.sample.temperature_unit})
649       
650        for item in datainfo.sample.details:
651            write_node(doc, sample, "details", item)
652       
653        pos = doc.createElement("position")
654        written = write_node(doc, pos, "x", datainfo.sample.position.x,
655                             {"unit":datainfo.sample.position_unit})
656        written = written | write_node(doc, pos, "y",
657                                       datainfo.sample.position.y,
658                                       {"unit":datainfo.sample.position_unit})
659        written = written | write_node(doc, pos, "z",
660                                       datainfo.sample.position.z,
661                                       {"unit":datainfo.sample.position_unit})
662        if written == True:
663            sample.appendChild(pos)
664       
665        ori = doc.createElement("orientation")
666        written = write_node(doc, ori, "roll",
667                             datainfo.sample.orientation.x,
668                             {"unit":datainfo.sample.orientation_unit})
669        written = written | write_node(doc, ori, "pitch",
670                                       datainfo.sample.orientation.y,
671                                    {"unit":datainfo.sample.orientation_unit})
672        written = written | write_node(doc, ori, "yaw",
673                                       datainfo.sample.orientation.z,
674                                    {"unit":datainfo.sample.orientation_unit})
675        if written == True:
676            sample.appendChild(ori)
677       
678        # Instrument info
679        instr = doc.createElement("SASinstrument")
680        entry_node.appendChild(instr)
681       
682        write_node(doc, instr, "name", datainfo.instrument)
683       
684        #   Source
685        source = doc.createElement("SASsource")
686        if datainfo.source.name is not None:
687            source.setAttribute("name", str(datainfo.source.name))
688        instr.appendChild(source)
689       
690        write_node(doc, source, "radiation", datainfo.source.radiation)
691        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
692        size = doc.createElement("beam_size")
693        if datainfo.source.beam_size_name is not None:
694            size.setAttribute("name", str(datainfo.source.beam_size_name))
695        written = write_node(doc, size, "x", datainfo.source.beam_size.x,
696                             {"unit":datainfo.source.beam_size_unit})
697        written = written | write_node(doc, size, "y",
698                                       datainfo.source.beam_size.y,
699                                       {"unit":datainfo.source.beam_size_unit})
700        written = written | write_node(doc, size, "z",
701                                       datainfo.source.beam_size.z,
702                                       {"unit":datainfo.source.beam_size_unit})
703        if written == True:
704            source.appendChild(size)
705           
706        write_node(doc, source, "wavelength",
707                   datainfo.source.wavelength,
708                   {"unit":datainfo.source.wavelength_unit})
709        write_node(doc, source, "wavelength_min",
710                   datainfo.source.wavelength_min,
711                   {"unit":datainfo.source.wavelength_min_unit})
712        write_node(doc, source, "wavelength_max",
713                   datainfo.source.wavelength_max,
714                   {"unit":datainfo.source.wavelength_max_unit})
715        write_node(doc, source, "wavelength_spread",
716                   datainfo.source.wavelength_spread,
717                   {"unit":datainfo.source.wavelength_spread_unit})
718       
719        #   Collimation
720        for item in datainfo.collimation:
721            coll = doc.createElement("SAScollimation")
722            if item.name is not None:
723                coll.setAttribute("name", str(item.name))
724            instr.appendChild(coll)
725           
726            write_node(doc, coll, "length", item.length,
727                       {"unit":item.length_unit})
728           
729            for apert in item.aperture:
730                ap = doc.createElement("aperture")
731                if apert.name is not None:
732                    ap.setAttribute("name", str(apert.name))
733                if apert.type is not None:
734                    ap.setAttribute("type", str(apert.type))
735                coll.appendChild(ap)
736               
737                write_node(doc, ap, "distance", apert.distance,
738                           {"unit":apert.distance_unit})
739               
740                size = doc.createElement("size")
741                if apert.size_name is not None:
742                    size.setAttribute("name", str(apert.size_name))
743                written = write_node(doc, size, "x", apert.size.x,
744                                     {"unit":apert.size_unit})
745                written = written | write_node(doc, size, "y", apert.size.y,
746                                               {"unit":apert.size_unit})
747                written = written | write_node(doc, size, "z", apert.size.z,
748                                               {"unit":apert.size_unit})
749                if written == True:
750                    ap.appendChild(size)
751
752        #   Detectors
753        for item in datainfo.detector:
754            det = doc.createElement("SASdetector")
755            written = write_node(doc, det, "name", item.name)
756            written = written | write_node(doc, det, "SDD", item.distance,
757                                           {"unit":item.distance_unit})
758            written = written | write_node(doc, det, "slit_length",
759                                           item.slit_length,
760                                           {"unit":item.slit_length_unit})
761            if written == True:
762                instr.appendChild(det)
763           
764            off = doc.createElement("offset")
765            written = write_node(doc, off, "x", item.offset.x,
766                                 {"unit":item.offset_unit})
767            written = written | write_node(doc, off, "y", item.offset.y,
768                                           {"unit":item.offset_unit})
769            written = written | write_node(doc, off, "z", item.offset.z,
770                                           {"unit":item.offset_unit})
771            if written == True:
772                det.appendChild(off)
773           
774            center = doc.createElement("beam_center")
775            written = write_node(doc, center, "x", item.beam_center.x,
776                                 {"unit":item.beam_center_unit})
777            written = written | write_node(doc, center, "y",
778                                           item.beam_center.y,
779                                           {"unit":item.beam_center_unit})
780            written = written | write_node(doc, center, "z",
781                                           item.beam_center.z,
782                                           {"unit":item.beam_center_unit})
783            if written == True:
784                det.appendChild(center)
785               
786            pix = doc.createElement("pixel_size")
787            written = write_node(doc, pix, "x", item.pixel_size.x,
788                                 {"unit":item.pixel_size_unit})
789            written = written | write_node(doc, pix, "y", item.pixel_size.y,
790                                           {"unit":item.pixel_size_unit})
791            written = written | write_node(doc, pix, "z", item.pixel_size.z,
792                                           {"unit":item.pixel_size_unit})
793            if written == True:
794                det.appendChild(pix)
795               
796            ori = doc.createElement("orientation")
797            written = write_node(doc, ori, "roll",  item.orientation.x,
798                                 {"unit":item.orientation_unit})
799            written = written | write_node(doc, ori, "pitch",
800                                           item.orientation.y,
801                                           {"unit":item.orientation_unit})
802            written = written | write_node(doc, ori, "yaw",
803                                           item.orientation.z,
804                                           {"unit":item.orientation_unit})
805            if written == True:
806                det.appendChild(ori)
807               
808       
809        # Processes info
810        for item in datainfo.process:
811            node = doc.createElement("SASprocess")
812            entry_node.appendChild(node)
813
814            write_node(doc, node, "name", item.name)
815            write_node(doc, node, "date", item.date)
816            write_node(doc, node, "description", item.description)
817            for term in item.term:
818                value = term['value']
819                del term['value']
820                write_node(doc, node, "term", value, term)
821            for note in item.notes:
822                write_node(doc, node, "SASprocessnote", note)
823       
824        # Return the document, and the SASentry node associated with
825        # the data we just wrote
826        return doc, entry_node
827           
828    def write(self, filename, datainfo):
829        """
830        Write the content of a Data1D as a CanSAS XML file
831       
832        :param filename: name of the file to write
833        :param datainfo: Data1D object
834        """
835        # Create XML document
836        doc, sasentry = self._to_xml_doc(datainfo)
837        # Write the file
838        fd = open(filename, 'w')
839        fd.write(doc.toprettyxml())
840        fd.close()
841       
842    def _store_float(self, location, node, variable, storage, optional=True):
843        """
844        Get the content of a xpath location and store
845        the result. Check that the units are compatible
846        with the destination. The value is expected to
847        be a float.
848       
849        The xpath location might or might not exist.
850        If it does not exist, nothing is done
851       
852        :param location: xpath location to fetch
853        :param node: node to read the data from
854        :param variable: name of the data member to store it in [string]
855        :param storage: data object that has the 'variable' data member
856        :param optional: if True, no exception will be raised
857            if unit conversion can't be done
858
859        :raise ValueError: raised when the units are not recognized
860        """
861        entry = get_content(location, node)
862        try:
863            value = float(entry.text)
864        except:
865            value = None
866           
867        if value is not None:
868            # If the entry has units, check to see that they are
869            # compatible with what we currently have in the data object
870            units = entry.get('unit')
871            if units is not None:
872                toks = variable.split('.')
873                exec "local_unit = storage.%s_unit" % toks[0]
874                if units.lower()!=local_unit.lower():
875                    if has_converter==True:
876                        try:
877                            conv = Converter(units)
878                            exec "storage.%s = %g" % (variable,
879                                            conv(value, units=local_unit))
880                        except:
881                            err_mess = "CanSAS reader: could not convert"
882                            err_mess += " %s unit [%s]; expecting [%s]\n  %s" \
883                                % (variable, units, local_unit, sys.exc_value)
884                            self.errors.append(err_mess)
885                            if optional:
886                                logging.info(err_mess)
887                            else:
888                                raise ValueError, err_mess
889                    else:
890                        err_mess = "CanSAS reader: unrecognized %s unit [%s];"
891                        err_mess += " expecting [%s]" % (variable, 
892                                                         units, local_unit)
893                        self.errors.append(err_mess)
894                        if optional:
895                            logging.info(err_mess)
896                        else:
897                            raise ValueError, err_mess
898                else:
899                    exec "storage.%s = value" % variable
900            else:
901                exec "storage.%s = value" % variable
902               
903    def _store_content(self, location, node, variable, storage):
904        """
905        Get the content of a xpath location and store
906        the result. The value is treated as a string.
907       
908        The xpath location might or might not exist.
909        If it does not exist, nothing is done
910       
911        :param location: xpath location to fetch
912        :param node: node to read the data from
913        :param variable: name of the data member to store it in [string]
914        :param storage: data object that has the 'variable' data member
915       
916        :return: return a list of errors
917        """
918        entry = get_content(location, node)
919        if entry is not None and entry.text is not None:
920            exec "storage.%s = entry.text.strip()" % variable
921
922           
923
924class Reader2D(CansasReader):
925    """
926    Class to load a .fitv fitting file
927    """
928    ## File type
929    type_name = "Fitting"
930   
931    ## Wildcards
932    type = ["Fitting files (*.fitv)|*.fitv"
933            "SANSView file (*.svs)|*.svs"]
934    ## List of allowed extensions
935    ext=['.fitv', '.FITV', '.svs', 'SVS']   
936   
937    def __init__(self):
938        CansasReader.__init__(self)
939        """
940        Initialize the call-back method to be called
941        after we load a file
942       
943        :param call_back: call-back method
944        :param cansas:  True = files will be written/read in CanSAS format
945                        False = write CanSAS format
946           
947        """
948        ## Call back method to be executed after a file is read
949        #self.call_back = call_back
950        ## CanSAS format flag
951        self.cansas = cansas
952        self.state = None
953       
954    def read(self, path):
955        """
956        Load a new P(r) inversion state from file
957       
958        :param path: file path
959       
960        """
961        if self.cansas == True:
962            return self._read_cansas(path)
963     
964    def _to_xml_doc(self, datainfo):
965        """
966        Create an XML document to contain the content of a Data2D
967       
968        :param datainfo: Data2D object
969       
970        """
971        if not issubclass(datainfo.__class__, Data2D):
972            raise RuntimeError, "The cansas writer expects a Data2D instance"
973       
974        doc = xml.dom.minidom.Document()
975        main_node = doc.createElement("SASroot")
976        main_node.setAttribute("version", self.version)
977        main_node.setAttribute("xmlns", "cansas1d/%s" % self.version)
978        main_node.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
979        main_node.setAttribute("xsi:schemaLocation", "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.version)
980       
981        doc.appendChild(main_node)
982       
983        entry_node = doc.createElement("SASentry")
984        main_node.appendChild(entry_node)
985       
986        write_node(doc, entry_node, "Title", datainfo.title)
987        if datainfo is not None:
988            write_node(doc, entry_node, "data_class", datainfo.__class__.__name__)
989        for item in datainfo.run:
990            runname = {}
991            if datainfo.run_name.has_key(item) and len(str(datainfo.run_name[item]))>1:
992                runname = {'name': datainfo.run_name[item] }
993            write_node(doc, entry_node, "Run", item, runname)
994        # Data info
995        new_node = doc.createElement("SASdata")
996        entry_node.appendChild(new_node)
997        for item in list_of_data_2d_attr:
998            element = doc.createElement(item[0])
999            exec "element.setAttribute(item[0], str(datainfo.%s))"%(item[1])
1000            new_node.appendChild(element)
1001           
1002        for item in list_of_data2d_values:
1003            root_node = doc.createElement(item[0])
1004            new_node.appendChild(root_node)
1005           
1006            exec "temp_list = datainfo.%s"%item[1]
1007
1008            if temp_list is None or len(temp_list)== 0:
1009                element = doc.createElement(item[0])
1010                exec "element.appendChild(doc.createTextNode(str(%s)))"%temp_list
1011                root_node.appendChild(element)
1012            else:
1013                for value in temp_list:
1014                    element = doc.createElement(item[0])
1015                    exec "element.setAttribute(item[0], str(%s))"%value
1016                    root_node.appendChild(element)
1017       
1018        # Sample info
1019        sample = doc.createElement("SASsample")
1020        if datainfo.sample.name is not None:
1021            sample.setAttribute("name", str(datainfo.sample.name))
1022        entry_node.appendChild(sample)
1023        write_node(doc, sample, "ID", str(datainfo.sample.ID))
1024        write_node(doc, sample, "thickness", datainfo.sample.thickness, {"unit":datainfo.sample.thickness_unit})
1025        write_node(doc, sample, "transmission", datainfo.sample.transmission)
1026        write_node(doc, sample, "temperature", datainfo.sample.temperature, {"unit":datainfo.sample.temperature_unit})
1027       
1028        for item in datainfo.sample.details:
1029            write_node(doc, sample, "details", item)
1030       
1031        pos = doc.createElement("position")
1032        written = write_node(doc, pos, "x", datainfo.sample.position.x, {"unit":datainfo.sample.position_unit})
1033        written = written | write_node(doc, pos, "y", datainfo.sample.position.y, {"unit":datainfo.sample.position_unit})
1034        written = written | write_node(doc, pos, "z", datainfo.sample.position.z, {"unit":datainfo.sample.position_unit})
1035        if written == True:
1036            sample.appendChild(pos)
1037       
1038        ori = doc.createElement("orientation")
1039        written = write_node(doc, ori, "roll",  datainfo.sample.orientation.x, {"unit":datainfo.sample.orientation_unit})
1040        written = written | write_node(doc, ori, "pitch", datainfo.sample.orientation.y, {"unit":datainfo.sample.orientation_unit})
1041        written = written | write_node(doc, ori, "yaw",   datainfo.sample.orientation.z, {"unit":datainfo.sample.orientation_unit})
1042        if written == True:
1043            sample.appendChild(ori)
1044       
1045        # Instrument info
1046        instr = doc.createElement("SASinstrument")
1047        entry_node.appendChild(instr)
1048       
1049        write_node(doc, instr, "name", datainfo.instrument)
1050       
1051        #   Source
1052        source = doc.createElement("SASsource")
1053        if datainfo.source.name is not None:
1054            source.setAttribute("name", str(datainfo.source.name))
1055        instr.appendChild(source)
1056       
1057        write_node(doc, source, "radiation", datainfo.source.radiation)
1058        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
1059        size = doc.createElement("beam_size")
1060        if datainfo.source.beam_size_name is not None:
1061            size.setAttribute("name", str(datainfo.source.beam_size_name))
1062        written = write_node(doc, size, "x", datainfo.source.beam_size.x, {"unit":datainfo.source.beam_size_unit})
1063        written = written | write_node(doc, size, "y", datainfo.source.beam_size.y, {"unit":datainfo.source.beam_size_unit})
1064        written = written | write_node(doc, size, "z", datainfo.source.beam_size.z, {"unit":datainfo.source.beam_size_unit})
1065        if written == True:
1066            source.appendChild(size)
1067           
1068        write_node(doc, source, "wavelength", datainfo.source.wavelength, {"unit":datainfo.source.wavelength_unit})
1069        write_node(doc, source, "wavelength_min", datainfo.source.wavelength_min, {"unit":datainfo.source.wavelength_min_unit})
1070        write_node(doc, source, "wavelength_max", datainfo.source.wavelength_max, {"unit":datainfo.source.wavelength_max_unit})
1071        write_node(doc, source, "wavelength_spread", datainfo.source.wavelength_spread, {"unit":datainfo.source.wavelength_spread_unit})
1072       
1073        #   Collimation
1074        for item in datainfo.collimation:
1075            coll = doc.createElement("SAScollimation")
1076            if item.name is not None:
1077                coll.setAttribute("name", str(item.name))
1078            instr.appendChild(coll)
1079           
1080            write_node(doc, coll, "length", item.length, {"unit":item.length_unit})
1081           
1082            for apert in item.aperture:
1083                ap = doc.createElement("aperture")
1084                if apert.name is not None:
1085                    ap.setAttribute("name", str(apert.name))
1086                if apert.type is not None:
1087                    ap.setAttribute("type", str(apert.type))
1088                coll.appendChild(ap)
1089               
1090                write_node(doc, ap, "distance", apert.distance, {"unit":apert.distance_unit})
1091               
1092                size = doc.createElement("size")
1093                if apert.size_name is not None:
1094                    size.setAttribute("name", str(apert.size_name))
1095                written = write_node(doc, size, "x", apert.size.x, {"unit":apert.size_unit})
1096                written = written | write_node(doc, size, "y", apert.size.y, {"unit":apert.size_unit})
1097                written = written | write_node(doc, size, "z", apert.size.z, {"unit":apert.size_unit})
1098                if written == True:
1099                    ap.appendChild(size)
1100
1101        #   Detectors
1102        for item in datainfo.detector:
1103            det = doc.createElement("SASdetector")
1104            written = write_node(doc, det, "name", item.name)
1105            written = written | write_node(doc, det, "SDD", item.distance, {"unit":item.distance_unit})
1106            written = written | write_node(doc, det, "slit_length", item.slit_length, {"unit":item.slit_length_unit})
1107            if written == True:
1108                instr.appendChild(det)
1109           
1110            off = doc.createElement("offset")
1111            written = write_node(doc, off, "x", item.offset.x, {"unit":item.offset_unit})
1112            written = written | write_node(doc, off, "y", item.offset.y, {"unit":item.offset_unit})
1113            written = written | write_node(doc, off, "z", item.offset.z, {"unit":item.offset_unit})
1114            if written == True:
1115                det.appendChild(off)
1116           
1117            center = doc.createElement("beam_center")
1118            written = write_node(doc, center, "x", item.beam_center.x, {"unit":item.beam_center_unit})
1119            written = written | write_node(doc, center, "y", item.beam_center.y, {"unit":item.beam_center_unit})
1120            written = written | write_node(doc, center, "z", item.beam_center.z, {"unit":item.beam_center_unit})
1121            if written == True:
1122                det.appendChild(center)
1123               
1124            pix = doc.createElement("pixel_size")
1125            written = write_node(doc, pix, "x", item.pixel_size.x, {"unit":item.pixel_size_unit})
1126            written = written | write_node(doc, pix, "y", item.pixel_size.y, {"unit":item.pixel_size_unit})
1127            written = written | write_node(doc, pix, "z", item.pixel_size.z, {"unit":item.pixel_size_unit})
1128            if written == True:
1129                det.appendChild(pix)
1130               
1131            ori = doc.createElement("orientation")
1132            written = write_node(doc, ori, "roll",  item.orientation.x, {"unit":item.orientation_unit})
1133            written = written | write_node(doc, ori, "pitch", item.orientation.y, {"unit":item.orientation_unit})
1134            written = written | write_node(doc, ori, "yaw",   item.orientation.z, {"unit":item.orientation_unit})
1135            if written == True:
1136                det.appendChild(ori)
1137               
1138        # Processes info
1139        for item in datainfo.process:
1140            node = doc.createElement("SASprocess")
1141            entry_node.appendChild(node)
1142
1143            write_node(doc, node, "name", item.name)
1144            write_node(doc, node, "date", item.date)
1145            write_node(doc, node, "description", item.description)
1146            for term in item.term:
1147                value = term['value']
1148                del term['value']
1149                write_node(doc, node, "term", value, term)
1150            for note in item.notes:
1151                write_node(doc, node, "SASprocessnote", note)
1152        # Return the document, and the SASentry node associated with
1153        # the data we just wrote
1154        return doc, entry_node
1155   
1156    def _parse_state(self, entry):
1157        """
1158        Read a fit result from an XML node
1159       
1160        :param entry: XML node to read from
1161       
1162        :return: PageState object
1163        """
1164        # Create an empty state
1165        state = None   
1166        # Locate the P(r) node
1167        try:
1168            nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME, namespaces={'ns': CANSAS_NS})
1169            if nodes !=[]:
1170                # Create an empty state
1171                state =  PageState()
1172                state.fromXML(node=nodes[0])
1173        except:
1174            logging.info("XML document does not contain fitting information.\n %s" % sys.exc_value)
1175           
1176        return state
1177   
1178   
1179                   
1180    def _parse_entry(self, dom):
1181        """
1182        Parse a SASentry
1183       
1184        :param node: SASentry node
1185       
1186        :return: Data1D/Data2D object
1187       
1188        """
1189        node = dom.xpath('ns:data_class', namespaces={'ns': CANSAS_NS})
1190        if not node or node[0].text.lstrip().rstrip() != "Data2D":
1191            return CansasReader._parse_entry(self, dom)
1192       
1193        #Parse 2D
1194        data_info = Data2D()
1195       
1196        # Look up title     
1197        self._store_content('ns:Title', dom, 'title', data_info)
1198       
1199        # Look up run number   
1200        nodes = dom.xpath('ns:Run', namespaces={'ns': CANSAS_NS})
1201        for item in nodes:   
1202            if item.text is not None:
1203                value = item.text.strip()
1204                if len(value) > 0:
1205                    data_info.run.append(value)
1206                    if item.get('name') is not None:
1207                        data_info.run_name[value] = item.get('name')
1208                           
1209        # Look up instrument name             
1210        self._store_content('ns:SASinstrument/ns:name', dom, 'instrument', data_info)
1211
1212        # Notes
1213        note_list = dom.xpath('ns:SASnote', namespaces={'ns': CANSAS_NS})
1214        for note in note_list:
1215            try:
1216                if note.text is not None:
1217                    note_value = note.text.strip()
1218                    if len(note_value) > 0:
1219                        data_info.notes.append(note_value)
1220            except:
1221                err_mess = "cansas_reader.read: error processing entry notes\n  %s" % sys.exc_value
1222                self.errors.append(err_mess)
1223                logging.error(err_mess)
1224       
1225        # Sample info ###################
1226        entry = get_content('ns:SASsample', dom)
1227        if entry is not None:
1228            data_info.sample.name = entry.get('name')
1229           
1230        self._store_content('ns:SASsample/ns:ID', 
1231                     dom, 'ID', data_info.sample)                   
1232        self._store_float('ns:SASsample/ns:thickness', 
1233                     dom, 'thickness', data_info.sample)
1234        self._store_float('ns:SASsample/ns:transmission', 
1235                     dom, 'transmission', data_info.sample)
1236        self._store_float('ns:SASsample/ns:temperature', 
1237                     dom, 'temperature', data_info.sample)
1238       
1239        nodes = dom.xpath('ns:SASsample/ns:details', namespaces={'ns': CANSAS_NS})
1240        for item in nodes:
1241            try:
1242                if item.text is not None:
1243                    detail_value = item.text.strip()
1244                    if len(detail_value) > 0:
1245                        data_info.sample.details.append(detail_value)
1246            except:
1247                err_mess = "cansas_reader.read: error processing sample details\n  %s" % sys.exc_value
1248                self.errors.append(err_mess)
1249                logging.error(err_mess)
1250       
1251        # Position (as a vector)
1252        self._store_float('ns:SASsample/ns:position/ns:x', 
1253                     dom, 'position.x', data_info.sample)         
1254        self._store_float('ns:SASsample/ns:position/ns:y', 
1255                     dom, 'position.y', data_info.sample)         
1256        self._store_float('ns:SASsample/ns:position/ns:z', 
1257                     dom, 'position.z', data_info.sample)         
1258       
1259        # Orientation (as a vector)
1260        self._store_float('ns:SASsample/ns:orientation/ns:roll', 
1261                     dom, 'orientation.x', data_info.sample)         
1262        self._store_float('ns:SASsample/ns:orientation/ns:pitch', 
1263                     dom, 'orientation.y', data_info.sample)         
1264        self._store_float('ns:SASsample/ns:orientation/ns:yaw', 
1265                     dom, 'orientation.z', data_info.sample)         
1266       
1267        # Source info ###################
1268        entry = get_content('ns:SASinstrument/ns:SASsource', dom)
1269        if entry is not None:
1270            data_info.source.name = entry.get('name')
1271       
1272        self._store_content('ns:SASinstrument/ns:SASsource/ns:radiation', 
1273                     dom, 'radiation', data_info.source)                   
1274        self._store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape', 
1275                     dom, 'beam_shape', data_info.source)                   
1276        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength', 
1277                     dom, 'wavelength', data_info.source)         
1278        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min', 
1279                     dom, 'wavelength_min', data_info.source)         
1280        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max', 
1281                     dom, 'wavelength_max', data_info.source)         
1282        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread', 
1283                     dom, 'wavelength_spread', data_info.source)   
1284       
1285        # Beam size (as a vector)   
1286        entry = get_content('ns:SASinstrument/ns:SASsource/ns:beam_size', dom)
1287        if entry is not None:
1288            data_info.source.beam_size_name = entry.get('name')
1289           
1290        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x', 
1291                     dom, 'beam_size.x', data_info.source)   
1292        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y', 
1293                     dom, 'beam_size.y', data_info.source)   
1294        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z', 
1295                     dom, 'beam_size.z', data_info.source)   
1296       
1297        # Collimation info ###################
1298        nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation', namespaces={'ns': CANSAS_NS})
1299        for item in nodes:
1300            collim = Collimation()
1301            if item.get('name') is not None:
1302                collim.name = item.get('name')
1303            self._store_float('ns:length', item, 'length', collim) 
1304           
1305            # Look for apertures
1306            apert_list = item.xpath('ns:aperture', namespaces={'ns': CANSAS_NS})
1307            for apert in apert_list:
1308                aperture =  Aperture()
1309               
1310                # Get the name and type of the aperture
1311                aperture.name = apert.get('name')
1312                aperture.type = apert.get('type')
1313                   
1314                self._store_float('ns:distance', apert, 'distance', aperture)   
1315               
1316                entry = get_content('ns:size', apert)
1317                if entry is not None:
1318                    aperture.size_name = entry.get('name')
1319               
1320                self._store_float('ns:size/ns:x', apert, 'size.x', aperture)   
1321                self._store_float('ns:size/ns:y', apert, 'size.y', aperture)   
1322                self._store_float('ns:size/ns:z', apert, 'size.z', aperture)
1323               
1324                collim.aperture.append(aperture)
1325               
1326            data_info.collimation.append(collim)
1327       
1328        # Detector info ######################
1329        nodes = dom.xpath('ns:SASinstrument/ns:SASdetector', namespaces={'ns': CANSAS_NS})
1330        for item in nodes:
1331           
1332            detector = Detector()
1333           
1334            self._store_content('ns:name', item, 'name', detector)
1335            self._store_float('ns:SDD', item, 'distance', detector)   
1336           
1337            # Detector offset (as a vector)
1338            self._store_float('ns:offset/ns:x', item, 'offset.x', detector)   
1339            self._store_float('ns:offset/ns:y', item, 'offset.y', detector)   
1340            self._store_float('ns:offset/ns:z', item, 'offset.z', detector)   
1341           
1342            # Detector orientation (as a vector)
1343            self._store_float('ns:orientation/ns:roll',  item, 'orientation.x', detector)   
1344            self._store_float('ns:orientation/ns:pitch', item, 'orientation.y', detector)   
1345            self._store_float('ns:orientation/ns:yaw',   item, 'orientation.z', detector)   
1346           
1347            # Beam center (as a vector)
1348            self._store_float('ns:beam_center/ns:x', item, 'beam_center.x', detector)   
1349            self._store_float('ns:beam_center/ns:y', item, 'beam_center.y', detector)   
1350            self._store_float('ns:beam_center/ns:z', item, 'beam_center.z', detector)   
1351           
1352            # Pixel size (as a vector)
1353            self._store_float('ns:pixel_size/ns:x', item, 'pixel_size.x', detector)   
1354            self._store_float('ns:pixel_size/ns:y', item, 'pixel_size.y', detector)   
1355            self._store_float('ns:pixel_size/ns:z', item, 'pixel_size.z', detector)   
1356           
1357            self._store_float('ns:slit_length', item, 'slit_length', detector)
1358           
1359            data_info.detector.append(detector)   
1360
1361        # Processes info ######################
1362        nodes = dom.xpath('ns:SASprocess', namespaces={'ns': CANSAS_NS})
1363        for item in nodes:
1364            process = Process()
1365            self._store_content('ns:name', item, 'name', process)
1366            self._store_content('ns:date', item, 'date', process)
1367            self._store_content('ns:description', item, 'description', process)
1368           
1369            term_list = item.xpath('ns:term', namespaces={'ns': CANSAS_NS})
1370            for term in term_list:
1371                try:
1372                    term_attr = {}
1373                    for attr in term.keys():
1374                        term_attr[attr] = term.get(attr).strip()
1375                    if term.text is not None:
1376                        term_attr['value'] = term.text.strip()
1377                        process.term.append(term_attr)
1378                except:
1379                    err_mess = "cansas_reader.read: error processing process term\n  %s" % sys.exc_value
1380                    self.errors.append(err_mess)
1381                    logging.error(err_mess)
1382           
1383            note_list = item.xpath('ns:SASprocessnote', namespaces={'ns': CANSAS_NS})
1384            for note in note_list:
1385                if note.text is not None:
1386                    process.notes.append(note.text.strip())
1387           
1388            data_info.process.append(process)
1389           
1390           
1391        # Data info ######################
1392        nodes = dom.xpath('ns:SASdata', namespaces={'ns': CANSAS_NS})
1393        if len(nodes)>1:
1394            raise RuntimeError, "CanSAS reader is not compatible with multiple SASdata entries"
1395       
1396        for entry in nodes:
1397            for item in list_of_data_2d_attr:
1398                #get node
1399                node = get_content('ns:%s'%item[0], entry)
1400                exec "data_info.%s = parse_entry_helper(node, item)"%(item[1])
1401                   
1402            for item in list_of_data2d_values:
1403                field = get_content('ns:%s'%item[0], entry)
1404                list = []
1405                if field is not None:
1406                    list = [parse_entry_helper(node, item) for node in field]
1407                exec "data_info.%s = numpy.array(list)"%item[0]
1408       
1409        return data_info
1410
1411    def _read_cansas(self, path):
1412        """
1413        Load data and P(r) information from a CanSAS XML file.
1414       
1415        :param path: file path
1416       
1417        :return: Data1D object if a single SASentry was found,
1418                    or a list of Data1D objects if multiple entries were found,
1419                    or None of nothing was found
1420                   
1421        :raise RuntimeError: when the file can't be opened
1422        :raise ValueError: when the length of the data vectors are inconsistent
1423       
1424        """
1425        output = []
1426        basename  = os.path.basename(path)
1427        root, extension = os.path.splitext(basename)
1428        ext = extension.lower()
1429        try:
1430            if os.path.isfile(path):
1431               
1432                #TODO: eventually remove the check for .xml once
1433                # the P(r) writer/reader is truly complete.
1434                if  ext in self.ext or \
1435                    ext == '.xml':
1436                   
1437                    tree = etree.parse(path, parser=etree.ETCompatXMLParser())
1438                    # Check the format version number
1439                    # Specifying the namespace will take care of the file format version
1440                    root = tree.getroot()
1441                    entry_list = root.xpath('ns:SASentry', namespaces={'ns': CANSAS_NS})
1442                    for entry in entry_list:   
1443                        try:
1444                            sas_entry = self._parse_entry(entry)
1445                        except:
1446                            raise
1447                        fitstate = self._parse_state(entry)
1448                       
1449                        #state could be None when .svs file is loaded
1450                        #in this case, skip appending to output
1451                        if fitstate != None:
1452                            sas_entry.meta_data['fitstate'] = fitstate
1453                            sas_entry.filename = fitstate.file
1454                            output.append(sas_entry)
1455            else:
1456                self.call_back(format=ext)
1457                raise RuntimeError, "%s is not a file" % path
1458
1459            # Return output consistent with the loader's api
1460            if len(output)==0:
1461                self.call_back(state=None, datainfo=None,format=ext)
1462                return None
1463            else:
1464                for ind in range(len(output)):
1465                    # Call back to post the new state
1466                    state = output[ind].meta_data['fitstate']
1467                    t = time.localtime(state.timestamp)
1468                    time_str = time.strftime("%b %d %H:%M", t)
1469                    # Check that no time stamp is already appended
1470                    max_char = state.file.find("[")
1471                    if max_char < 0:
1472                        max_char = len(state.file)
1473                    original_fname = state.file[0:max_char]
1474                    state.file = original_fname +' [' + time_str + ']'
1475                   
1476                       
1477                    if state is not None and state.is_data is not None:
1478                        exec 'output[%d].is_data = state.is_data'% ind
1479                     
1480                    output[ind].filename = state.file
1481                    state.data = output[ind]
1482                    state.data.name = output[ind].filename #state.data_name
1483                    state.data.id = state.data_id
1484                    if state.is_data is not None:
1485                        state.data.is_data = state.is_data
1486                    if output[ind].run_name is not None and\
1487                        len(output[ind].run_name) != 0 :
1488                        name = output[ind].run_name
1489                    else: 
1490                        name=original_fname
1491                    state.data.group_id = name
1492                    #store state in fitting
1493                    self.call_back(state=state, datainfo=output[ind],format=ext)
1494                    self.state= state
1495                return output
1496             
1497        except:
1498            #self.call_back(format=ext)
1499            self.state= state
1500            raise
1501           
1502    def write(self, filename, datainfo=None, fitstate=None):
1503        """
1504        Write the content of a Data1D as a CanSAS XML file only for standalone
1505       
1506        :param filename: name of the file to write
1507        :param datainfo: Data1D object
1508        :param fitstate: PageState object
1509       
1510        """
1511        # Sanity check
1512        if self.cansas == True:
1513           
1514            # Add fitting information to the XML document
1515            doc = self.write_toXML(datainfo, fitstate)
1516            # Write the XML document
1517            fd = open(filename, 'w')
1518            fd.write(doc.toprettyxml())
1519            fd.close()
1520        else:
1521            fitstate.toXML(file=filename)
1522       
1523    def write_toXML(self, datainfo=None, state=None):
1524        """
1525        Write toXML, a helper for write() , could be used by guimanager._on_save()
1526       
1527        : return: xml doc
1528        """
1529
1530        if state.data is None:
1531            data = Data2D() 
1532        else: 
1533            #make sure title and data run is filled up.
1534            if state.data.title == None or state.data.title=='':
1535                 state.data.title = state.data.name
1536            if state.data.run_name == None or state.data.run_name=={}: 
1537                state.data.run = [str(state.data.name)]
1538                state.data.run_name[0] = state.data.name
1539            doc, sasentry = self._to_xml_doc(data)
1540           
1541
1542        if state is not None:
1543            state.toXML(doc=doc, file=data.name, entry_node=sasentry)
1544           
1545        return doc
1546 
1547if __name__ == "__main__":
1548    state = PageState(parent=None)
1549    #state.toXML()
1550    """
1551   
1552    file = open("test_state", "w")
1553    pickle.dump(state, file)
1554    print pickle.dumps(state)
1555    state.data_name = "hello---->"
1556    pickle.dump(state, file)
1557    file = open("test_state", "r")
1558    new_state= pickle.load(file)
1559    print "new state", new_state
1560    new_state= pickle.load(file)
1561    print "new state", new_state
1562    #print "state", state
1563    """
1564    import bsddb
1565    import pickle
1566    db= bsddb.btopen('file_state.db', 'c')
1567    val = (pickle.dumps(state), "hello", "hi")
1568    db['state1']= pickle.dumps(val)
1569    print pickle.loads(db['state1'])
1570    state.data_name = "hello---->22"
1571    db['state2']= pickle.dumps(state)
1572    state.data_name = "hello---->2"
1573    db['state3']= pickle.dumps(state)
1574    del db['state3']
1575    state.data_name = "hello---->3"
1576    db['state4']= pickle.dumps(state)
1577    new_state = pickle.loads(db['state1'])
1578    #print db.last()
1579    db.set_location('state2')
1580    state.data_name = "hello---->5"
1581    db['aastate5']= pickle.dumps(state)
1582    db.keys().sort()
1583    print pickle.loads(db['state2'])
1584 
1585    db.close() 
1586
1587    logging.basicConfig(level=logging.ERROR,
1588                        format='%(asctime)s %(levelname)s %(message)s',
1589                        filename='cansas_reader.log',
1590                        filemode='w')
1591    reader = Reader()
1592    print reader.read("../test/cansas1d.xml")
1593    #print reader.read("../test/latex_smeared.xml")
1594   
1595   
1596                       
Note: See TracBrowser for help on using the repository browser.