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

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

First implementation of reader for CanSas? format

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