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

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 af03ddd 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
RevLine 
[8780e9a]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
[b39c817]25has_converter = True
26try:
27    from data_util.nxsunit import Converter
28except:
29    has_converter = False
30
[8780e9a]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:
[b39c817]95            value = float(content)   
[8780e9a]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:
[b39c817]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           
[8780e9a]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
[99d1af6]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       
[8780e9a]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.