source: sasview/src/sans/dataloader/readers/cansas_reader.py @ 76cd1ae

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 76cd1ae was 76cd1ae, checked in by Jeff Krzywon <jeffery.krzywon@…>, 10 years ago

Fix for the datainfo window not loading and bug fixes for the cansas data reader.

Fixes/changes:
(1) datainfo window is now loading for every data file I can test
(2) transmission spectrum information (but not data) is listed in datainfo window
(3) more than one transmission spectra can be loaded for each Data1D object
(4) fixed a bug in the cansas reader that allowed any file to be loaded as data if and only if another data file was already loaded
(5) fixed the cansas writer to include transmission spectrum data and output data in strict canSAS format
(6) increased the pylint score of cansas_constants.py to above 7
(7) increased the pylint score for all files I have modified

  • Property mode set to 100644
File size: 36.1 KB
Line 
1"""
2    CanSAS data reader - new recursive cansas_version.
3"""
4############################################################################
5#This software was developed by the University of Tennessee as part of the
6#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
7#project funded by the US National Science Foundation.
8#If you use DANSE applications to do scientific research that leads to
9#publication, we ask that you acknowledge the use of the software with the
10#following sentence:
11#This work benefited from DANSE software developed under NSF award DMR-0520547.
12#copyright 2008,2009 University of Tennessee
13#############################################################################
14
15import logging
16import numpy
17import os
18import sys
19from sans.dataloader.data_info import Data1D
20from sans.dataloader.data_info import Collimation
21from sans.dataloader.data_info import TransmissionSpectrum
22from sans.dataloader.data_info import Detector
23from sans.dataloader.data_info import Process
24from sans.dataloader.data_info import Aperture
25import sans.dataloader.readers.xml_reader as xml_reader
26import xml.dom.minidom
27from sans.dataloader.readers.cansas_constants import cansasConstants
28
29_ZERO = 1e-16
30HAS_CONVERTER = True
31try:
32    from sans.data_util.nxsunit import Converter
33except:
34    HAS_CONVERTER = False
35
36constants = cansasConstants()   
37CANSAS_FORMAT = constants.format
38CANSAS_NS = constants.ns
39ALLOW_ALL = True
40
41
42def write_node(doc, parent, name, value, attr={}):
43    """
44    :param doc: document DOM
45    :param parent: parent node
46    :param name: tag of the element
47    :param value: value of the child text node
48    :param attr: attribute dictionary
49   
50    :return: True if something was appended, otherwise False
51    """
52    if value is not None:
53        node = doc.createElement(name)
54        node.appendChild(doc.createTextNode(str(value)))
55        for item in attr:
56            node.setAttribute(item, attr[item])
57        parent.appendChild(node)
58        return True
59    return False
60               
61
62def get_content(location, node):
63    """
64    Get the first instance of the content of a xpath location.
65   
66    :param location: xpath location
67    :param node: node to start at
68   
69    :return: Element, or None
70    """
71    nodes = node.xpath(location, namespaces={'ns': CANSAS_NS})
72   
73    if len(nodes) > 0:
74        return nodes[0]
75    else:
76        return None
77
78
79def get_float(location, node):
80    """
81    Get the content of a node as a float
82   
83    :param location: xpath location
84    :param node: node to start at
85    """
86    nodes = node.xpath(location, namespaces={'ns': CANSAS_NS})
87   
88    value = None
89    attr = {}
90    if len(nodes) > 0:
91        try:
92            value = float(nodes[0].text)
93        except:
94            # Could not pass, skip and return None
95            msg = "cansas_reader.get_float: could not "
96            msg += " convert '%s' to float" % nodes[0].text
97            logging.error(msg)
98        if nodes[0].get('unit') is not None:
99            attr['unit'] = nodes[0].get('unit')
100    return value, attr
101
102
103
104class CANSASError(Exception):
105    """Base class all CANSAS reader exceptions are derived"""
106    pass
107
108class NotCANSASFileError(CANSASError):
109    def __init__(self):
110        self.value = "This is not a proper CanSAS file."
111    def __str__(self):
112        return repr(self.value)
113
114class Reader():
115    """
116    Class to load cansas 1D XML files
117   
118    :Dependencies:
119        The CanSAS reader requires PyXML 0.8.4 or later.
120    """
121    ##CanSAS version - defaults to version 1.0
122    cansas_version = "1.0"
123    ##Data reader
124    reader = xml_reader.XMLreader()
125    errors = []
126   
127    type_name = "canSAS"
128   
129    ## Wildcards
130    type = ["XML files (*.xml)|*.xml"]
131    ## List of allowed extensions
132    ext = ['.xml', '.XML']
133   
134    ## Flag to bypass extension check
135    allow_all = True
136   
137    def __init__(self):
138        ## List of errors
139        self.errors = []
140       
141    def is_cansas(self):
142        """
143        Checks to see if the xml file is a CanSAS file
144        """
145        if self.reader.validateXML():
146            xmlns = self.reader.xmlroot.keys()
147            if (CANSAS_NS.get(self.cansas_version).get("ns") == \
148                    self.reader.xmlroot.get(xmlns[1]).rsplit(" ")[0]):
149                return True
150        return False
151   
152    def read(self, xml):
153        """
154        Validate and read in an xml file in the canSAS format.
155       
156        :param xml: A canSAS file path in proper XML format
157        """
158        # X - Q value; Y - Intensity (Abs)
159        x = numpy.empty(0)
160        y = numpy.empty(0)
161        dx = numpy.empty(0)
162        dy = numpy.empty(0)
163        dxl = numpy.empty(0)
164        dxw = numpy.empty(0)
165       
166        # output - Final list of Data1D objects
167        output = []
168        # ns - Namespace hierarchy for current xml object
169        ns = []
170       
171        # Check that the file exists
172        if os.path.isfile(xml):
173            basename = os.path.basename(xml)
174            _, extension = os.path.splitext(basename)
175            # If the fiel type is not allowed, return nothing
176            if extension in self.ext or self.allow_all:
177                base_name = xml_reader.__file__
178                base_name = base_name.replace("\\","/")
179                base = base_name.split("/sans/")[0]
180               
181                # Load in xml file and get the cansas version from the header
182                self.reader.setXMLFile(xml)
183                root = self.reader.xmlroot
184                if root is None:
185                    root = {}
186                self.cansas_version = root.get("version", "1.0")
187               
188                # Generic values for the cansas file based on the version
189                cansas_defaults = CANSAS_NS.get(self.cansas_version, "1.0")
190                schema_path = "{0}/sans/dataloader/readers/schema/{1}".format\
191                        (base, cansas_defaults.get("schema")).replace("\\", "/")
192               
193                # Link a schema to the XML file.
194                self.reader.setSchema(schema_path)
195           
196                # Try to load the file, but raise an error if unable to.
197                # Check the file matches the XML schema
198                try:
199                    if self.is_cansas():
200                        # Get each SASentry from XML file and add it to a list.
201                        entry_list = root.xpath('/ns:SASroot/ns:SASentry',
202                                namespaces={'ns': cansas_defaults.get("ns")})
203                        ns.append("SASentry")
204                       
205                        # If multiple files, modify the name for each is unique
206                        multiple_files = len(entry_list) - 1
207                        increment = 0
208                        name = basename
209                        # Parse each SASentry item
210                        for entry in entry_list:
211                            # Define a new Data1D object with zeroes for x and y
212                            data1d = Data1D(x,y,dx,dy)
213                            data1d.dxl = dxl
214                            data1d.dxw = dxw
215                           
216                            # If more than one SASentry, increment each in order
217                            if multiple_files:
218                                name += "_{0}".format(increment)
219                                increment += 1
220                           
221                            # Set the Data1D name and then parse the entry.
222                            # The entry is appended to a list of entry values
223                            data1d.filename = name
224                            data1d.meta_data["loader"] = "CanSAS 1D"
225                            return_value, extras = \
226                                self._parse_entry(entry, ns, data1d)
227                            del extras[:]
228                           
229                            # Final cleanup
230                            # Remove empty nodes, verify array sizes are correct
231                            for error in self.errors:
232                                return_value.errors.append(error)
233                            del self.errors[:]
234                            numpy.trim_zeros(return_value.x)
235                            numpy.trim_zeros(return_value.y)
236                            numpy.trim_zeros(return_value.dy)
237                            size_dx = return_value.dx.size
238                            size_dxl = return_value.dxl.size
239                            size_dxw = return_value.dxw.size
240                            if size_dxl == 0 and size_dxw == 0:
241                                return_value.dxl = None
242                                return_value.dxw = None
243                                numpy.trim_zeros(return_value.dx)
244                            elif size_dx == 0:
245                                return_value.dx = None
246                                size_dx = size_dxl
247                                numpy.trim_zeros(return_value.dxl)
248                                numpy.trim_zeros(return_value.dxw)
249                            output.append(return_value)
250                    else:
251                        value = self.reader.findInvalidXML()
252                        output.append("Invalid XML at: {0}".format(value))
253                except:
254                    # If the file does not match the schema, raise this error
255                    raise RuntimeError, "%s cannot be read \increment" % xml
256                return output
257        # Return a list of parsed entries that dataloader can manage
258        return None
259   
260    def _create_unique_key(self, dictionary, name, i):
261        """
262        Create a unique key value for any dictionary to prevent overwriting
263       
264       
265        :param dictionary: A dictionary with any number of entries
266        :param name: The index of the item to be added to dictionary
267        :param i: The number to be appended to the name
268        """
269        if dictionary.get(name) is not None:
270            i += 1
271            name = name.split("_")[0]
272            name += "_{0}".format(i)
273            name = self._create_unique_key(dictionary, name, i)
274        return name
275   
276    def _iterate_namespace(self, namespace):
277        """
278        Method to iterate through a cansas constants tree based on a list of
279        names
280       
281        :param namespace: A list of names that match the tree structure of
282            cansas_constants
283        """
284        # The current level to look through in cansas_constants.
285        current_level = CANSAS_FORMAT.get("SASentry")
286        # Defaults for variable and datatype
287        ns_variable = "{0}.meta_data[\"{2}\"] = \"{1}\""
288        ns_datatype = "content"
289        ns_optional = True
290        for name in namespace:
291            if name != "SASentry":
292                current_level = current_level.get("children").get(name, "")
293                if current_level == "":
294                    current_level = current_level.get("<any>", "")
295                cl_variable = current_level.get("variable", "")
296                cl_datatype = current_level.get("storeas", "")
297                cl_units_optional = current_level.get("units_required", "")
298                # Where are how to store the variable for the given namespace
299                # CANSAS_CONSTANTS tree is hierarchical, so is no value, inherit
300                ns_variable = cl_variable if cl_variable != "" else ns_variable
301                ns_datatype = cl_datatype if cl_datatype != "" else ns_datatype
302                ns_optional = cl_units_optional if cl_units_optional != \
303                                    ns_optional else ns_optional
304        return current_level, ns_variable, ns_datatype, ns_optional
305   
306   
307    def _unit_conversion(self, new_current_level, attr, data1d, \
308                                    node_value, optional = True):
309        """
310        A unit converter method used to convert the data included in the file
311        to the default units listed in data_info
312       
313        :param new_current_level: cansas_constants level as returned by
314            _iterate_namespace
315        :param attr: The attributes of the node
316        :param data1d: Where the values will be saved
317        :param node_value: The value of the current dom node
318        :param optional: Boolean that says if the units are required
319        """
320        value_unit = ''
321        if 'unit' in attr and new_current_level.get('unit') is not None:
322            try:
323                if isinstance(node_value, float) is False:
324                    exec("node_value = float({0})".format(node_value))
325                default_unit = None
326                unitname = new_current_level.get("unit")
327                exec "default_unit = data1d.{0}".format(unitname)
328                local_unit = attr['unit']
329                if local_unit.lower() != default_unit.lower() and \
330                    local_unit is not None and local_unit.lower() != "none" and\
331                     default_unit is not None:
332                    if HAS_CONVERTER == True:
333                        try:
334                            data_conv_q = Converter(attr['unit'])
335                            value_unit = default_unit
336                            exec "node_value = data_conv_q(node_value, units=data1d.{0})".format(unitname)
337                        except:
338                            err_msg = "CanSAS reader: could not convert "
339                            err_msg += "Q unit {0}; ".format(local_unit)
340                            intermediate = "err_msg += \"expecting [{1}]  {2}\".format(data1d.{0}, sys.exc_info()[1])".format(unitname, "{0}", "{1}")
341                            exec intermediate
342                            self.errors.append(err_msg)
343                            if optional:
344                                logging.info(err_msg)
345                            else:
346                                raise ValueError, err_msg
347                    else:
348                        value_unit = local_unit
349                        err_msg = "CanSAS reader: unrecognized %s unit [%s];"\
350                        % (node_value, default_unit)
351                        err_msg += " expecting [%s]" % local_unit
352                        self.errors.append(err_msg)
353                        if optional:
354                            logging.info(err_msg)
355                        else:
356                            raise ValueError, err_msg
357                else:
358                    value_unit = local_unit
359            except:
360                err_msg = "CanSAS reader: could not convert "
361                err_msg += "Q unit [%s]; " % attr['unit'],
362                exec "err_msg += \"expecting [%s]\n  %s\" % (data1d.{0}, sys.exc_info()[1])".format(unitname)
363                self.errors.append(err_msg)
364                if optional:
365                    logging.info(err_msg)
366                else:
367                    raise ValueError, err_msg
368        elif 'unit' in attr:
369            value_unit = attr['unit']
370        node_value = "float({0})".format(node_value)
371        return node_value, value_unit
372   
373    def _parse_entry(self, dom, ns, data1d, extras = []):
374        """
375        Parse a SASEntry - new recursive method for parsing the dom of
376            the CanSAS data format. This will allow multiple data files
377            and extra nodes to be read in simultaneously.
378       
379        :param dom: dom object with a namespace base of ns
380        :param ns: A list of element names that lead up to the dom object
381        :param data1d: The data1d object that will be modified
382        :param extras: Any values that should go into meta_data when data1d
383            is not a Data1D object
384        """
385         
386        # A portion of every namespace entry
387        base_ns = "{0}{1}{2}".format("{", \
388                            CANSAS_NS.get(self.cansas_version).get("ns"), "}")
389        unit = ''
390       
391        # Go through each child in the parent element
392        for node in dom:
393            try:
394                # Get the element name and set the current ns level
395                tagname = node.tag.replace(base_ns, "")
396                tagname_original = tagname
397                ns.append(tagname)
398                attr = node.attrib
399               
400                # Look for special cases
401                save_data1d = data1d
402                if tagname == "SASdetector":
403                    data1d = Detector()
404                elif tagname == "SAScollimation":
405                    data1d = Collimation()
406                elif tagname == "SAStransmission_spectrum":
407                    data1d = TransmissionSpectrum()
408                elif tagname == "SASprocess":
409                    data1d = Process()
410                    for child in node:
411                        if child.tag.replace(base_ns, "") == "term":
412                            term_attr = {}
413                            for attr in child.keys():
414                                term_attr[attr] = \
415                                    ' '.join(child.get(attr).split())
416                            if child.text is not None:
417                                term_attr['value'] = \
418                                    ' '.join(child.text.split())
419                            data1d.term.append(term_attr)
420                elif tagname == "aperture":
421                    data1d = Aperture()
422               
423                # Get where to store content
424                new_current_level, ns_var, ns_datatype, \
425                                    optional = self._iterate_namespace(ns)
426                # If the element is a child element, recurse
427                if node.getchildren() is not None:
428                    # Returned value is new Data1D object with all previous and new values in it.
429                    data1d, extras = self._parse_entry(node, ns, data1d, extras)
430                   
431                #Get the information from the node
432                node_value = node.text
433                if node_value == "":
434                    node_value = None
435                if node_value is not None:
436                    node_value = ' '.join(node_value.split())
437               
438                # If the value is a float, compile with units.
439                if ns_datatype == "float":
440                    # If an empty value is given, store as zero.
441                    if node_value is None or node_value.isspace() \
442                                            or node_value.lower() == "nan":
443                        node_value = "0.0"
444                    node_value, unit = self._unit_conversion(new_current_level,\
445                                             attr, data1d, node_value, optional)
446                   
447                # If appending to a dictionary (meta_data | run_name), name sure the key is unique
448                if ns_var == "{0}.meta_data[\"{2}\"] = \"{1}\"":
449                    # If we are within a Process, Detector, Collimation or Aperture instance, pull out old data1d
450                    tagname = self._create_unique_key(data1d.meta_data, tagname, 0)
451                    if isinstance(data1d, Data1D) == False:
452                        store_me = ns_var.format("data1d", node_value, tagname)
453                        extras.append(store_me)
454                        ns_var = None
455                if ns_var == "{0}.run_name[\"{2}\"] = \"{1}\"":
456                    tagname = self._create_unique_key(data1d.run_name, tagname, 0)
457               
458                # Check for Data1D object and any extra commands to save
459                if isinstance(data1d, Data1D):
460                    for item in extras:
461                        exec item
462                # Don't bother saving empty information unless it is a float
463                if ns_var is not None and node_value is not None and \
464                            node_value.isspace() == False:
465                    # Format a string and then execute it.
466                    store_me = ns_var.format("data1d", node_value, tagname)
467                    exec store_me
468                # Get attributes and process them
469                if attr is not None:
470                    for key in node.keys():
471                        try:
472                            cansas_attrib = new_current_level.get("attributes").get(key)
473                            attrib_variable = cansas_attrib.get("variable")
474                            if key == 'unit' and unit != '':
475                                attrib_value = unit
476                            else:
477                                attrib_value = node.attrib[key]
478                            store_attr = attrib_variable.format("data1d", \
479                                                            attrib_value, key)
480                            exec store_attr
481                        except AttributeError as e:
482                            pass
483                           
484                     
485            except Exception as e:
486                exc_type, exc_obj, exc_tb = sys.exc_info()
487                fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1]
488                print(e, exc_type, fname, exc_tb.tb_lineno, tagname, exc_obj)
489            finally:
490                # Save special cases in original data1d object
491                # then restore the data1d
492                if tagname_original == "SASdetector":
493                    save_data1d.detector.append(data1d)
494                elif tagname_original == "SAScollimation":
495                    save_data1d.collimation.append(data1d)
496                elif tagname == "SAStransmission_spectrum":
497                    save_data1d.trans_spectrum.append(data1d)
498                elif tagname_original == "SASprocess":
499                    save_data1d.process.append(data1d)
500                elif tagname_original == "aperture":
501                    save_data1d.aperture.append(data1d)
502                else:
503                    save_data1d = data1d
504                data1d = save_data1d
505                # Remove tagname from ns to restore original base
506                ns.remove(tagname_original)
507       
508        return data1d, extras
509       
510    def _to_xml_doc(self, datainfo):
511        """
512        Create an XML document to contain the content of a Data1D
513       
514        :param datainfo: Data1D object
515        """
516       
517        if not issubclass(datainfo.__class__, Data1D):
518            raise RuntimeError, "The cansas writer expects a Data1D instance"
519       
520        ns = CANSAS_NS.get(self.cansas_version).get("ns")
521        doc = xml.dom.minidom.Document()
522        main_node = doc.createElement("SASroot")
523        main_node.setAttribute("version", self.cansas_version)
524        main_node.setAttribute("xmlns", ns)
525        main_node.setAttribute("xmlns:xsi",
526                               "http://www.w3.org/2001/XMLSchema-instance")
527        main_node.setAttribute("xsi:schemaLocation",
528                               "{0} http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd".format(ns))
529       
530        doc.appendChild(main_node)
531       
532        entry_node = doc.createElement("SASentry")
533        main_node.appendChild(entry_node)
534       
535        write_node(doc, entry_node, "Title", datainfo.title)
536        for item in datainfo.run:
537            runname = {}
538            if item in datainfo.run_name and \
539            len(str(datainfo.run_name[item])) > 1:
540                runname = {'name': datainfo.run_name[item]}
541            write_node(doc, entry_node, "Run", item, runname)
542       
543        # Data info
544        node = doc.createElement("SASdata")
545        entry_node.appendChild(node)
546       
547        for i in range(len(datainfo.x)):
548            pt = doc.createElement("Idata")
549            node.appendChild(pt)
550            write_node(doc, pt, "Q", datainfo.x[i], {'unit': datainfo.x_unit})
551            if len(datainfo.y) >= i:
552                write_node(doc, pt, "I", datainfo.y[i],
553                            {'unit': datainfo.y_unit})
554            if datainfo.dy != None and len(datainfo.dy) > i:
555                write_node(doc, pt, "Idev", datainfo.dy[i],
556                            {'unit': datainfo.y_unit})
557            if datainfo.dx != None and len(datainfo.dx) > i:
558                write_node(doc, pt, "Qdev", datainfo.dx[i],
559                            {'unit': datainfo.x_unit})
560            if datainfo.dxw != None and len(datainfo.dxw) > i:
561                write_node(doc, pt, "dQw", datainfo.dxw[i],
562                            {'unit': datainfo.x_unit})
563            if datainfo.dxl != None and len(datainfo.dxl) > i:
564                write_node(doc, pt, "dQl", datainfo.dxl[i],
565                            {'unit': datainfo.x_unit})
566
567        # Transmission Spectrum Info
568        for i in range(len(datainfo.trans_spectrum)):
569            spectrum = datainfo.trans_spectrum[i]
570            node = doc.createElement("SAStransmission_spectrum")
571            entry_node.appendChild(node)
572            for i in range(len(spectrum.wavelength)):
573                pt = doc.createElement("Tdata")
574                node.appendChild(pt)
575                write_node(doc, pt, "Lambda", spectrum.wavelength[i], 
576                           {'unit': spectrum.wavelength_unit})
577                write_node(doc, pt, "T", spectrum.transmission[i], 
578                           {'unit': spectrum.transmission_unit})
579                if spectrum.transmission_deviation != None \
580                and len(spectrum.transmission_deviation) >= i:
581                    write_node(doc, pt, "Tdev", \
582                               spectrum.transmission_deviation[i], \
583                               {'unit': spectrum.transmission_deviation_unit})
584
585        # Sample info
586        sample = doc.createElement("SASsample")
587        if datainfo.sample.name is not None:
588            sample.setAttribute("name", str(datainfo.sample.name))
589        entry_node.appendChild(sample)
590        write_node(doc, sample, "ID", str(datainfo.sample.ID))
591        write_node(doc, sample, "thickness", datainfo.sample.thickness,
592                   {"unit": datainfo.sample.thickness_unit})
593        write_node(doc, sample, "transmission", datainfo.sample.transmission)
594        write_node(doc, sample, "temperature", datainfo.sample.temperature,
595                   {"unit": datainfo.sample.temperature_unit})
596       
597        pos = doc.createElement("position")
598        written = write_node(doc, pos, "x", datainfo.sample.position.x,
599                             {"unit": datainfo.sample.position_unit})
600        written = written | write_node(doc, pos, "y",
601                                       datainfo.sample.position.y,
602                                       {"unit": datainfo.sample.position_unit})
603        written = written | write_node(doc, pos, "z",
604                                       datainfo.sample.position.z,
605                                       {"unit": datainfo.sample.position_unit})
606        if written == True:
607            sample.appendChild(pos)
608       
609        ori = doc.createElement("orientation")
610        written = write_node(doc, ori, "roll",
611                             datainfo.sample.orientation.x,
612                             {"unit": datainfo.sample.orientation_unit})
613        written = written | write_node(doc, ori, "pitch",
614                                       datainfo.sample.orientation.y,
615                                    {"unit": datainfo.sample.orientation_unit})
616        written = written | write_node(doc, ori, "yaw",
617                                       datainfo.sample.orientation.z,
618                                    {"unit": datainfo.sample.orientation_unit})
619        if written == True:
620            sample.appendChild(ori)
621       
622        for item in datainfo.sample.details:
623            write_node(doc, sample, "details", item)
624       
625        # Instrument info
626        instr = doc.createElement("SASinstrument")
627        entry_node.appendChild(instr)
628       
629        write_node(doc, instr, "name", datainfo.instrument)
630       
631        #   Source
632        source = doc.createElement("SASsource")
633        if datainfo.source.name is not None:
634            source.setAttribute("name", str(datainfo.source.name))
635        instr.appendChild(source)
636        write_node(doc, source, "radiation", datainfo.source.radiation)
637       
638        size = doc.createElement("beam_size")
639        if datainfo.source.beam_size_name is not None:
640            size.setAttribute("name", str(datainfo.source.beam_size_name))
641        written = write_node(doc, size, "x", datainfo.source.beam_size.x,
642                             {"unit": datainfo.source.beam_size_unit})
643        written = written | write_node(doc, size, "y",
644                                       datainfo.source.beam_size.y,
645                                       {"unit": datainfo.source.beam_size_unit})
646        written = written | write_node(doc, size, "z",
647                                       datainfo.source.beam_size.z,
648                                       {"unit": datainfo.source.beam_size_unit})
649        if written == True:
650            source.appendChild(size)
651           
652        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
653        write_node(doc, source, "wavelength",
654                   datainfo.source.wavelength,
655                   {"unit": datainfo.source.wavelength_unit})
656        write_node(doc, source, "wavelength_min",
657                   datainfo.source.wavelength_min,
658                   {"unit": datainfo.source.wavelength_min_unit})
659        write_node(doc, source, "wavelength_max",
660                   datainfo.source.wavelength_max,
661                   {"unit": datainfo.source.wavelength_max_unit})
662        write_node(doc, source, "wavelength_spread",
663                   datainfo.source.wavelength_spread,
664                   {"unit": datainfo.source.wavelength_spread_unit})
665       
666        #   Collimation
667        for item in datainfo.collimation:
668            coll = doc.createElement("SAScollimation")
669            if item.name is not None:
670                coll.setAttribute("name", str(item.name))
671            instr.appendChild(coll)
672           
673            write_node(doc, coll, "length", item.length,
674                       {"unit": item.length_unit})
675           
676            for apert in item.aperture:
677                ap = doc.createElement("aperture")
678                if apert.name is not None:
679                    ap.setAttribute("name", str(apert.name))
680                if apert.type is not None:
681                    ap.setAttribute("type", str(apert.type))
682                coll.appendChild(ap)
683               
684                size = doc.createElement("size")
685                if apert.size_name is not None:
686                    size.setAttribute("name", str(apert.size_name))
687                written = write_node(doc, size, "x", apert.size.x,
688                                     {"unit": apert.size_unit})
689                written = written | write_node(doc, size, "y", apert.size.y,
690                                               {"unit": apert.size_unit})
691                written = written | write_node(doc, size, "z", apert.size.z,
692                                               {"unit": apert.size_unit})
693                if written == True:
694                    ap.appendChild(size)
695               
696                write_node(doc, ap, "distance", apert.distance,
697                           {"unit": apert.distance_unit})
698
699        #   Detectors
700        for item in datainfo.detector:
701            det = doc.createElement("SASdetector")
702            written = write_node(doc, det, "name", item.name)
703            written = written | write_node(doc, det, "SDD", item.distance,
704                                           {"unit": item.distance_unit})
705            if written == True:
706                instr.appendChild(det)
707           
708            off = doc.createElement("offset")
709            written = write_node(doc, off, "x", item.offset.x,
710                                 {"unit": item.offset_unit})
711            written = written | write_node(doc, off, "y", item.offset.y,
712                                           {"unit": item.offset_unit})
713            written = written | write_node(doc, off, "z", item.offset.z,
714                                           {"unit": item.offset_unit})
715            if written == True:
716                det.appendChild(off)
717               
718            ori = doc.createElement("orientation")
719            written = write_node(doc, ori, "roll", item.orientation.x,
720                                 {"unit": item.orientation_unit})
721            written = written | write_node(doc, ori, "pitch",
722                                           item.orientation.y,
723                                           {"unit": item.orientation_unit})
724            written = written | write_node(doc, ori, "yaw",
725                                           item.orientation.z,
726                                           {"unit": item.orientation_unit})
727            if written == True:
728                det.appendChild(ori)
729           
730            center = doc.createElement("beam_center")
731            written = write_node(doc, center, "x", item.beam_center.x,
732                                 {"unit": item.beam_center_unit})
733            written = written | write_node(doc, center, "y",
734                                           item.beam_center.y,
735                                           {"unit": item.beam_center_unit})
736            written = written | write_node(doc, center, "z",
737                                           item.beam_center.z,
738                                           {"unit": item.beam_center_unit})
739            if written == True:
740                det.appendChild(center)
741               
742            pix = doc.createElement("pixel_size")
743            written = write_node(doc, pix, "x", item.pixel_size.x,
744                                 {"unit": item.pixel_size_unit})
745            written = written | write_node(doc, pix, "y", item.pixel_size.y,
746                                           {"unit": item.pixel_size_unit})
747            written = written | write_node(doc, pix, "z", item.pixel_size.z,
748                                           {"unit": item.pixel_size_unit})
749            if written == True:
750                det.appendChild(pix)
751            written = written | write_node(doc, det, "slit_length",
752                                           item.slit_length,
753                                           {"unit": item.slit_length_unit})
754           
755        # Processes info
756        for item in datainfo.process:
757            node = doc.createElement("SASprocess")
758            entry_node.appendChild(node)
759
760            write_node(doc, node, "name", item.name)
761            write_node(doc, node, "date", item.date)
762            write_node(doc, node, "description", item.description)
763            for term in item.term:
764                value = term['value']
765                del term['value']
766                write_node(doc, node, "term", value, term)
767            for note in item.notes:
768                write_node(doc, node, "SASprocessnote", note)
769            if len(item.notes) == 0:
770                write_node(doc, node, "SASprocessnote", "")
771               
772        # Note info
773        if len(datainfo.notes) == 0:
774            node = doc.createElement("SASnote")
775            entry_node.appendChild(node)
776            if node.hasChildNodes():
777                for child in node.childNodes:
778                    node.removeChild(child)
779        else:
780            for item in datainfo.notes:
781                node = doc.createElement("SASnote")
782                entry_node.appendChild(node)
783                node.appendChild(doc.createTextNode(item))
784               
785        # Return the document, and the SASentry node associated with
786        # the data we just wrote
787        return doc, entry_node
788           
789    def write(self, filename, datainfo):
790        """
791        Write the content of a Data1D as a CanSAS XML file
792       
793        :param filename: name of the file to write
794        :param datainfo: Data1D object
795        """
796        # Create XML document
797        doc, _ = self._to_xml_doc(datainfo)
798        # Write the file
799        fd = open(filename, 'w')
800        fd.write(doc.toprettyxml())
801        fd.close()
Note: See TracBrowser for help on using the repository browser.