source: sasview/DataLoader/readers/cansas_reader.py @ 8176aad

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

Update to readers, with unit conversion

  • Property mode set to 100644
File size: 17.8 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
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 =  collim.Aperture()
320               
321                _store_float('distance', apert, 'distance', aperture)   
322                _store_float('size/x', apert, 'size.x', aperture)   
323                _store_float('size/y', apert, 'size.y', aperture)   
324                _store_float('size/z', apert, 'size.z', aperture)
325               
326                collim.aperture.append(aperture)
327               
328            data_info.collimation.append(collim)
329       
330        # Detector info ######################
331        nodes = xpath.Evaluate('SASinstrument/SASdetector', dom)
332        for item in nodes:
333           
334            detector = Detector()
335           
336            _store_content('name', item, 'name', detector)
337            _store_float('SDD', item, 'distance', detector)   
338           
339            # Detector offset (as a vector)
340            _store_float('offset/x', item, 'offset.x', detector)   
341            _store_float('offset/y', item, 'offset.y', detector)   
342            _store_float('offset/z', item, 'offset.z', detector)   
343           
344            # Detector orientation (as a vector)
345            _store_float('orientation/pitch', item, 'orientation.x', detector)   
346            _store_float('orientation/yaw',   item, 'orientation.y', detector)   
347            _store_float('orientation/roll',  item, 'orientation.z', detector)   
348           
349            # Beam center (as a vector)
350            _store_float('beam_center/x', item, 'beam_center.x', detector)   
351            _store_float('beam_center/y', item, 'beam_center.y', detector)   
352            _store_float('beam_center/z', item, 'beam_center.z', detector)   
353           
354            # Pixel size (as a vector)
355            _store_float('pixel_size/x', item, 'pixel_size.x', detector)   
356            _store_float('pixel_size/y', item, 'pixel_size.y', detector)   
357            _store_float('pixel_size/z', item, 'pixel_size.z', detector)   
358           
359            _store_float('slit_length', item, 'slit_length', detector)
360           
361            data_info.detector.append(detector)   
362
363        # Processes info ######################
364        nodes = xpath.Evaluate('SASprocess', dom)
365        for item in nodes:
366            process = Process()
367            _store_content('name', item, 'name', process)
368            _store_content('date', item, 'date', process)
369            _store_content('description', item, 'description', process)
370           
371            term_list = xpath.Evaluate('term', item)
372            for term in term_list:
373                try:
374                    term_value, term_attr = get_node_text(term)
375                    term_attr['value'] = term_value
376                    if term_value is not None:
377                        process.term.append(term_attr)
378                except:
379                    logging.error("cansas_reader.read: error processing process term\n  %s" % sys.exc_value)
380           
381            note_list = xpath.Evaluate('SASprocessnote', item)
382            for note in note_list:
383                try:
384                    note_value, note_attr = get_node_text(note)
385                    if note_value is not None:
386                        process.notes.append(note_value)
387                except:
388                    logging.error("cansas_reader.read: error processing process notes\n  %s" % sys.exc_value)
389           
390           
391            data_info.process.append(process)
392           
393           
394        # Data info ######################
395        nodes = xpath.Evaluate('SASdata/Idata', dom)
396        x  = numpy.zeros(0)
397        y  = numpy.zeros(0)
398        dx = numpy.zeros(0)
399        dy = numpy.zeros(0)
400       
401        for item in nodes:
402            _x, attr = get_float('Q', item)
403            _dx, attr = get_float('Qdev', item)
404            if _dx == None:
405                _dx = 0.0
406            if attr.has_key('unit') and attr['unit'].lower() != data_info.x_unit.lower():
407                raise ValueError, "CanSAS reader: unrecognized %s unit [%s]; expecting [%s]" \
408                    % (variable, attr['unit'], local_unit)
409               
410            _y, attr = get_float('I', item)
411            _dy, attr = get_float('Idev', item)
412            if _dy == None:
413                _dy = 0.0
414            if attr.has_key('unit') and attr['unit'].lower() != data_info.y_unit.lower():
415                raise ValueError, "CanSAS reader: unrecognized %s unit [%s]; expecting [%s]" \
416                    % (variable, attr['unit'], local_unit)
417               
418            if _x is not None and _y is not None:
419                x  = numpy.append(x, _x)
420                y  = numpy.append(x, _y)
421                dx = numpy.append(x, _dx)
422                dy = numpy.append(x, _dy)
423           
424        data_info.x = x
425        data_info.y = y
426        data_info.dx = dx
427        data_info.dy = dy
428        if data_conv_q is not None:
429            data_info.xaxis("\\rm{Q}", output.x_unit)
430        else:
431            data_info.xaxis("\\rm{Q}", 'A^{-1}')
432        if data_conv_i is not None:
433            data_info.yaxis("\\{I(Q)}", output.y_unit)
434        else:
435            data_info.yaxis("\\rm{I(Q)}","cm^{-1}")
436       
437        return data_info
438
439   
440if __name__ == "__main__": 
441    logging.basicConfig(level=logging.ERROR,
442                        format='%(asctime)s %(levelname)s %(message)s',
443                        filename='cansas_reader.log',
444                        filemode='w')
445    reader = Reader()
446    print reader.read("../test/cansas1d.xml")
447   
448   
449                       
Note: See TracBrowser for help on using the repository browser.