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

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 d6513cd was d6513cd, checked in by Mathieu Doucet <doucetm@…>, 16 years ago

Data loader update (still working)

  • Property mode set to 100644
File size: 18.2 KB
Line 
1"""
2This software was developed by the University of Tennessee as part of the
3Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
4project funded by the US National Science Foundation.
5
6See the license text in license.txt
7
8copyright 2008, University of Tennessee
9"""
10
11#TODO: Unit conversion
12#TODO: Store error list
13#TODO: convert from pixel to mm for beam center...
14#TODO: Allow for additional meta data for each section
15#TODO: Notes need to be implemented. They can be any XML structure in version 1.0
16#      Process notes have the same problem.
17
18
19import logging
20import numpy
21import os, sys
22from DataLoader.data_info import Data1D, Collimation, Detector, Process, Aperture
23from xml import xpath
24
25has_converter = True
26try:
27    from data_util.nxsunit import Converter
28except:
29    has_converter = False
30
31def get_node_text(node):
32    """
33        Get the text context of a node
34       
35        @param node: node to read from
36        @return: content, attribute list
37    """
38    content = None
39    attr    = {}
40    for item in node.childNodes:
41        if item.nodeName.find('text')>=0 \
42            and len(item.nodeValue.strip())>0:
43            content = item.nodeValue.strip()
44            break
45       
46    if node.hasAttributes():
47        for i in range(node.attributes.length):
48            attr[node.attributes.item(i).nodeName] \
49                = node.attributes.item(i).nodeValue
50
51    return content, attr
52
53def get_content(location, node):
54    """
55        Get the first instance of the content of a xpath location
56       
57        @param location: xpath location
58        @param node: node to start at
59    """
60    value = None
61    attr  = {}
62    nodes = xpath.Evaluate(location, node)
63    if len(nodes)>0:
64        try:
65            # Skip comments and empty lines
66            for item in nodes[0].childNodes:
67                if item.nodeName.find('text')>=0 \
68                    and len(item.nodeValue.strip())>0:
69                    value = item.nodeValue.strip()
70                    break
71               
72            if nodes[0].hasAttributes():
73                for i in range(nodes[0].attributes.length):
74                    attr[nodes[0].attributes.item(i).nodeName] \
75                        = nodes[0].attributes.item(i).nodeValue
76        except:
77            # problem reading the node. Skip it and return that
78            # nothing was found
79            logging.error("cansas_reader.get_content: %s\n  %s" % (location, sys.exc_value))
80       
81    return value, attr
82
83def get_float(location, node):
84    """
85        Get the content of a node as a float
86       
87        @param location: xpath location
88        @param node: node to start at
89    """
90    value = None
91    attr = {}
92    content, attr = get_content(location, node)
93    if content is not None:
94        try:
95            value = float(content)   
96        except:
97            # Could not pass, skip and return None
98            logging.error("cansas_reader.get_float: could not convert '%s' to float" % content)
99       
100    return value, attr
101
102def _store_float(location, node, variable, storage):
103    """
104        Get the content of a xpath location and store
105        the result. Check that the units are compatible
106        with the destination. The value is expected to
107        be a float.
108       
109        The xpath location might or might not exist.
110        If it does not exist, nothing is done
111       
112        @param location: xpath location to fetch
113        @param node: node to read the data from
114        @param variable: name of the data member to store it in [string]
115        @param storage: data object that has the 'variable' data member
116       
117        @raise ValueError: raised when the units are not recognized
118    """
119    value, attr = get_float(location, node)
120    if value is not None:
121        # If the entry has units, check to see that they are
122        # compatible with what we currently have in the data object
123        if attr.has_key('unit'):
124            toks = variable.split('.')
125            exec "local_unit = storage.%s_unit.lower()" % toks[0]
126            if attr['unit'].lower()!=local_unit:
127                if has_converter==True:
128                    try:
129                        conv = Converter(attr['unit'])
130                        exec "storage.%s = %g" % (variable, conv(value, units=local_unit))
131                    except:
132                        raise ValueError, "CanSAS reader: could not convert %s unit [%s]; expecting [%s]\n  %s" \
133                        % (variable, attr['unit'], local_unit, sys.exc_value)
134                else:
135                    raise ValueError, "CanSAS reader: unrecognized %s unit [%s]; expecting [%s]" \
136                        % (variable, attr['unit'], local_unit)
137            else:
138                exec "storage.%s = value" % variable
139        else:
140            exec "storage.%s = value" % variable
141           
142
143def _store_content(location, node, variable, storage):
144    """
145        Get the content of a xpath location and store
146        the result. The value is treated as a string.
147       
148        The xpath location might or might not exist.
149        If it does not exist, nothing is done
150       
151        @param location: xpath location to fetch
152        @param node: node to read the data from
153        @param variable: name of the data member to store it in [string]
154        @param storage: data object that has the 'variable' data member
155    """
156    value, attr = get_content(location, node)
157    if value is not None:
158        exec "storage.%s = value" % variable
159
160
161class Reader:
162    """
163        Class to load cansas 1D XML files
164       
165        Dependencies:
166            The CanSas reader requires PyXML 0.8.4 or later.
167    """
168    ## CanSAS version
169    version = '1.0'
170    ## File type
171    type = ["CanSAS 1D files (*.xml)|*.xml"]
172    ## List of allowed extensions
173    ext=['.xml', '.XML'] 
174   
175    def read(self, path):
176        """
177            Load data file
178           
179            @param path: file path
180            @return: Data1D object if a single SASentry was found,
181                        or a list of Data1D objects if multiple entries were found,
182                        or None of nothing was found
183            @raise RuntimeError: when the file can't be opened
184            @raise ValueError: when the length of the data vectors are inconsistent
185        """
186        from xml.dom.minidom import parse
187       
188        output = []
189       
190        if os.path.isfile(path):
191            basename  = os.path.basename(path)
192            root, extension = os.path.splitext(basename)
193            if extension.lower() in self.ext:
194               
195                dom = parse(path)
196               
197                # Check the format version number
198                nodes = xpath.Evaluate('SASroot', dom)
199                if nodes[0].hasAttributes():
200                    for i in range(nodes[0].attributes.length):
201                        if nodes[0].attributes.item(i).nodeName=='version':
202                            if nodes[0].attributes.item(i).nodeValue != self.version:
203                                raise ValueError, "cansas_reader: unrecognized version number %s" % \
204                                    nodes[0].attributes.item(i).nodeValue
205               
206                entry_list = xpath.Evaluate('SASroot/SASentry', dom)
207                for entry in entry_list:
208                    sas_entry = self._parse_entry(entry)
209                    sas_entry.filename = basename
210                    output.append(sas_entry)
211               
212        else:
213            raise RuntimeError, "%s is not a file" % path
214       
215        # Return output consistent with the loader's api
216        if len(output)==0:
217            return None
218        elif len(output)==1:
219            return output[0]
220        else:
221            return output               
222               
223    def _parse_entry(self, dom):
224        """
225            Parse a SASentry
226           
227            @param node: SASentry node
228            @return: Data1D object
229        """
230        x = numpy.zeros(0)
231        y = numpy.zeros(0)
232       
233        data_info = Data1D(x, y)
234       
235        # Look up title
236        _store_content('Title', dom, 'title', data_info)
237        # Look up run number                   
238        _store_content('Run', dom, 'run', data_info)                   
239        # Look up instrument name             
240        value, attr = get_content('SASinstrument', dom)
241        if attr.has_key('name'):
242            data_info.instrument = attr['name']
243
244        note_list = xpath.Evaluate('SASnote', dom)
245        for note in note_list:
246            try:
247                note_value, note_attr = get_node_text(note)
248                if note_value is not None:
249                    data_info.notes.append(note_value)
250            except:
251                logging.error("cansas_reader.read: error processing entry notes\n  %s" % sys.exc_value)
252
253       
254        # Sample info ###################
255        _store_content('SASsample/ID', 
256                     dom, 'ID', data_info.sample)                   
257        _store_float('SASsample/thickness', 
258                     dom, 'thickness', data_info.sample)
259        _store_float('SASsample/transmission', 
260                     dom, 'transmission', data_info.sample)
261        _store_float('SASsample/temperature', 
262                     dom, 'temperature', data_info.sample)
263        nodes = xpath.Evaluate('SASsample/details', dom)
264        for item in nodes:
265            try:
266                detail_value, detail_attr = get_node_text(item)
267                if detail_value is not None:
268                    data_info.sample.details.append(detail_value)
269            except:
270                logging.error("cansas_reader.read: error processing sample details\n  %s" % sys.exc_value)
271       
272        # Position (as a vector)
273        _store_float('SASsample/position/x', 
274                     dom, 'position.x', data_info.sample)         
275        _store_float('SASsample/position/y', 
276                     dom, 'position.y', data_info.sample)         
277        _store_float('SASsample/position/z', 
278                     dom, 'position.z', data_info.sample)         
279       
280        # Orientation (as a vector)
281        _store_float('SASsample/orientation/roll', 
282                     dom, 'orientation.x', data_info.sample)         
283        _store_float('SASsample/orientation/pitch', 
284                     dom, 'orientation.y', data_info.sample)         
285        _store_float('SASsample/orientation/yaw', 
286                     dom, 'orientation.z', data_info.sample)         
287       
288        # Source info ###################
289        _store_content('SASinstrument/SASsource/radiation', 
290                     dom, 'radiation', data_info.source)                   
291        _store_content('SASinstrument/SASsource/beam_shape', 
292                     dom, 'beam_shape', data_info.source)                   
293        _store_float('SASinstrument/SASsource/wavelength', 
294                     dom, 'wavelength', data_info.source)         
295        _store_float('SASinstrument/SASsource/wavelength_min', 
296                     dom, 'wavelength_min', data_info.source)         
297        _store_float('SASinstrument/SASsource/wavelength_max', 
298                     dom, 'wavelength_max', data_info.source)         
299        _store_float('SASinstrument/SASsource/wavelength_spread', 
300                     dom, 'wavelength_spread', data_info.source)   
301       
302        # Beam size (as a vector)     
303        _store_float('SASinstrument/SASsource/beam_size/x', 
304                     dom, 'beam_size.x', data_info.source)   
305        _store_float('SASinstrument/SASsource/beam_size/y', 
306                     dom, 'beam_size.y', data_info.source)   
307        _store_float('SASinstrument/SASsource/beam_size/z', 
308                     dom, 'beam_size.z', data_info.source)   
309       
310        # Collimation info ###################
311        nodes = xpath.Evaluate('SASinstrument/SAScollimation', dom)
312        for item in nodes:
313            collim = Collimation()
314            _store_float('length', item, 'length', collim) 
315           
316            # Look for apertures
317            apert_list = xpath.Evaluate('aperture', item)
318            for apert in apert_list:
319                aperture =  Aperture()
320                _store_float('distance', apert, 'distance', aperture)   
321                _store_float('size/x', apert, 'size.x', aperture)   
322                _store_float('size/y', apert, 'size.y', aperture)   
323                _store_float('size/z', apert, 'size.z', aperture)
324               
325                collim.aperture.append(aperture)
326               
327            data_info.collimation.append(collim)
328       
329        # Detector info ######################
330        nodes = xpath.Evaluate('SASinstrument/SASdetector', dom)
331        for item in nodes:
332           
333            detector = Detector()
334           
335            _store_content('name', item, 'name', detector)
336            _store_float('SDD', item, 'distance', detector)   
337           
338            # Detector offset (as a vector)
339            _store_float('offset/x', item, 'offset.x', detector)   
340            _store_float('offset/y', item, 'offset.y', detector)   
341            _store_float('offset/z', item, 'offset.z', detector)   
342           
343            # Detector orientation (as a vector)
344            _store_float('orientation/pitch', item, 'orientation.x', detector)   
345            _store_float('orientation/yaw',   item, 'orientation.y', detector)   
346            _store_float('orientation/roll',  item, 'orientation.z', detector)   
347           
348            # Beam center (as a vector)
349            _store_float('beam_center/x', item, 'beam_center.x', detector)   
350            _store_float('beam_center/y', item, 'beam_center.y', detector)   
351            _store_float('beam_center/z', item, 'beam_center.z', detector)   
352           
353            # Pixel size (as a vector)
354            _store_float('pixel_size/x', item, 'pixel_size.x', detector)   
355            _store_float('pixel_size/y', item, 'pixel_size.y', detector)   
356            _store_float('pixel_size/z', item, 'pixel_size.z', detector)   
357           
358            _store_float('slit_length', item, 'slit_length', detector)
359           
360            data_info.detector.append(detector)   
361
362        # Processes info ######################
363        nodes = xpath.Evaluate('SASprocess', dom)
364        for item in nodes:
365            process = Process()
366            _store_content('name', item, 'name', process)
367            _store_content('date', item, 'date', process)
368            _store_content('description', item, 'description', process)
369           
370            term_list = xpath.Evaluate('term', item)
371            for term in term_list:
372                try:
373                    term_value, term_attr = get_node_text(term)
374                    term_attr['value'] = term_value
375                    if term_value is not None:
376                        process.term.append(term_attr)
377                except:
378                    logging.error("cansas_reader.read: error processing process term\n  %s" % sys.exc_value)
379           
380            note_list = xpath.Evaluate('SASprocessnote', item)
381            for note in note_list:
382                try:
383                    note_value, note_attr = get_node_text(note)
384                    if note_value is not None:
385                        process.notes.append(note_value)
386                except:
387                    logging.error("cansas_reader.read: error processing process notes\n  %s" % sys.exc_value)
388           
389           
390            data_info.process.append(process)
391           
392           
393        # Data info ######################
394        nodes = xpath.Evaluate('SASdata/Idata', dom)
395        x  = numpy.zeros(0)
396        y  = numpy.zeros(0)
397        dx = numpy.zeros(0)
398        dy = numpy.zeros(0)
399       
400        for item in nodes:
401            _x, attr = get_float('Q', item)
402            _dx, attr = get_float('Qdev', item)
403            if _dx == None:
404                _dx = 0.0
405            if attr.has_key('unit') and attr['unit'].lower() != data_info.x_unit.lower():
406                raise ValueError, "CanSAS reader: unrecognized %s unit [%s]; expecting [%s]" \
407                    % (variable, attr['unit'], local_unit)
408               
409            _y, attr = get_float('I', item)
410            _dy, attr = get_float('Idev', item)
411            if _dy == None:
412                _dy = 0.0
413            if attr.has_key('unit') and attr['unit'].lower() != data_info.y_unit.lower():
414                raise ValueError, "CanSAS reader: unrecognized %s unit [%s]; expecting [%s]" \
415                    % (variable, attr['unit'], local_unit)
416               
417            if _x is not None and _y is not None:
418                x  = numpy.append(x, _x)
419                y  = numpy.append(x, _y)
420                dx = numpy.append(x, _dx)
421                dy = numpy.append(x, _dy)
422           
423        data_info.x = x
424        data_info.y = y
425        data_info.dx = dx
426        data_info.dy = dy
427       
428        data_conv_q = None
429        data_conv_i = None
430       
431        if has_converter == True and data_info.x_unit != '1/A':
432            data_conv_q = Converter('1/A')
433            # Test it
434            data_conv_q(1.0, output.Q_unit)
435           
436        if has_converter == True and data_info.y_unit != '1/cm':
437            data_conv_i = Converter('1/cm')
438            # Test it
439            data_conv_i(1.0, output.I_unit)           
440   
441       
442        if data_conv_q is not None:
443            data_info.xaxis("\\rm{Q}", data_info.x_unit)
444        else:
445            data_info.xaxis("\\rm{Q}", 'A^{-1}')
446        if data_conv_i is not None:
447            data_info.yaxis("\\{I(Q)}", data_info.y_unit)
448        else:
449            data_info.yaxis("\\rm{I(Q)}","cm^{-1}")
450       
451        return data_info
452
453   
454if __name__ == "__main__": 
455    logging.basicConfig(level=logging.ERROR,
456                        format='%(asctime)s %(levelname)s %(message)s',
457                        filename='cansas_reader.log',
458                        filemode='w')
459    reader = Reader()
460    print reader.read("../test/cansas1d.xml")
461   
462   
463                       
Note: See TracBrowser for help on using the repository browser.