source: sasview/src/sans/dataloader/readers/cansas_reader.py @ 19b628f

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

The last update was not complete. For some reason a single file was missed.

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