source: sasview/sansview/perspectives/fitting/pagestate.py @ 43db4a8

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 43db4a8 was 0b12abb5, checked in by Gervaise Alina <gervyh@…>, 14 years ago

working on save fitting state

  • Property mode set to 100644
File size: 51.6 KB
Line 
1
2
3################################################################################
4#This software was developed by the University of Tennessee as part of the
5#Distributed Data Analysis of Neutron Scattering Experiments (DANSE)
6#project funded by the US National Science Foundation.
7#
8#See the license text in license.txt
9#
10#copyright 2009, University of Tennessee
11################################################################################
12
13import time
14import os
15import sys
16import copy
17import logging
18import numpy
19
20import xml.dom.minidom
21from xml.dom.minidom import parse
22from lxml import etree
23
24import DataLoader
25from DataLoader.readers.cansas_reader import Reader as CansasReader
26from DataLoader.readers.cansas_reader import get_content, write_node
27from DataLoader.data_info import Data2D, Detector
28
29#Information to read/write state as xml
30FITTING_NODE_NAME = 'fitting_plug_in'
31CANSAS_NS = "cansas1d/1.0"
32
33list_of_data_attributes = [["is_data", "is_data", "bool"],
34                      ["group_id", "data_group_id", "string"],
35                      ["data_name", "data_name", "string"],
36                      ["data_id", "data_id", "string"],
37                      ["name", "name", "string"],
38                      ["data_name", "data_name", "string"]]
39list_of_state_attributes = [["qmin", "qmin", "float"],
40                      ["qmax", "qmax", "float"],
41                      ["npts", "npts", "float"],
42                      ["shape_rbutton", "shape_rbutton", "bool"],
43                      ["shape_indep_rbutton", "shape_indep_rbutton", "bool"],
44                      ["plugin_rbutton", "plugin_rbutton","bool"],
45                      ["struct_rbutton", "struct_rbutton", "bool"],
46                      ["formfactorcombobox", "formfactorcombobox", "float"],
47                      ["structurecombobox", "structurecombobox", "float"],
48                      ["disp_box", "disp_box", "float"],
49                      ["enable_smearer","enable_smearer","bool"],
50                      ["disable_smearer","disable_smearer","bool"],
51                      ["pinhole_smearer","pinhole_smearer","bool"],
52                      ["slit_smearer","slit_smearer","bool"],
53                      ["enable_disp","enable_disp","bool"],
54                      ["disable_disp","disable_disp","bool"],
55                      ["slit_smearer","slit_smearer","bool"],
56                      ["enable2D","enable2D","bool"],
57                      ["cb1","cb1","bool"],
58                      ["tcChi","tcChi","float"],
59                     ["smearer", "smearer", "float"],
60                     ["smear_type","smear_type", "string"],
61                     ["dq_l", "dq_l", "string"],
62                     ["dq_r","dq_r", "string"]]
63
64list_of_model_attributes = [["values", "values"],
65                            ["weights", "weights"]]
66
67list_of_state_parameters = [["parameters", "parameters"] ,                     
68                            ["orientation_parameters", "orientation_params"],
69                            ["dispersity_parameters", "orientation_params_disp"],
70                            ["fixed_param", "fixed_param"],                     
71                            ["fittable_param","fittable_param"]]
72list_of_data_2d_attr = [["xmin", "xmin","float"],
73                        ["xmax","xmax","float"],
74                        ["ymin","ymin","float"],
75                        ["ymax","ymax","float"],
76                        ["_xaxis","_xaxis", "string"],
77                        ["_xunit", "_xunit", "string"],
78                        ["_yaxis","_yaxis","string"],
79                        ["_yunit","_yunit","string"],
80                        ["_zaxis","_zaxis","string"],
81                        ["_zunit","_zunit","string"]]
82list_of_data2d_values = [["qx_data","qx_data","float"],
83                         ["qy_data","qy_data","float"],
84                         ["dqx_data","dqx_data","float"],
85                         ["dqy_data","dqy_data","float"],
86                         ["data","data","float"],
87                         ["q_data","q_data","float"],
88                         ["err_data","err_data","float"],
89                         ["mask","mask","bool"]]
90
91def parse_entry_helper( node, item):
92    """
93    Create a numpy list from value extrated from the node
94   
95    :param node: node from each the value is stored
96    :param item: list name of three strings.the two first are name of data
97        attribute and the third one is the type of the value of that
98        attribute. type can be string, float, bool, etc.
99   
100    : return: numpy array
101    """
102    if node is not None:
103        if item[2] == "string":
104            return str(node.get(item[0]).strip())
105        elif item[2] == "bool":
106            try:
107                exec "value = bool(node.get(item[0]).strip())"
108                return value
109            except:
110                return None
111        else:
112            try:
113                return float(node.get(item[0]))
114            except:
115                return None
116           
117           
118class PageState(object):
119    """
120    Contains information to reconstruct a page of the fitpanel.
121    """
122    def __init__(self, parent=None, model=None, data=None):
123       
124        """
125        Initialize the current state
126       
127        :param model: a selected model within a page
128        :param data:
129       
130        """
131        self.file = None
132        #Time of state creation
133        self.timestamp = time.time()
134        ## Data member to store the dispersion object created
135        self._disp_obj_dict = {}
136        #------------------------
137        #Data used for fitting
138        self.data = data
139        #save additional information on data that dataloader.reader does not read
140        self.is_data = None
141        self.data_name = ""
142       
143        if self.data is not None:
144            self.data_name = self.data.name
145        self.data_id = None
146        if self.data is not None and hasattr(self.data, "id"):
147            self.data_id = self.data.id
148        self.data_group_id = None
149        if self.data is not None and hasattr(self.data, "group_id"):
150            self.data_group_id = self.data.group_id
151       
152        ## reset True change the state of exsiting button
153        self.reset = False
154       
155        #engine type
156        self.engine_type = None
157        # flag to allow data2D plot
158        self.enable2D = False
159        # model on which the fit would be performed
160        self.model = model
161       
162        #fit page manager
163        self.manager = None
164        #Store the parent of this panel parent
165        # For this application fitpanel is the parent
166        self.parent  = parent
167        # Event_owner is the owner of model event
168        self.event_owner = None
169        ##page name
170        self.page_name = ""
171        # Contains link between  model ,all its parameters, and panel organization
172        self.parameters = []
173        # Contains list of parameters that cannot be fitted and reference to
174        #panel objects
175        self.fixed_param = []
176        # Contains list of parameters with dispersity and reference to
177        #panel objects
178        self.fittable_param = []
179        ## orientation parameters
180        self.orientation_params = []
181        ## orientation parmaters for gaussian dispersity
182        self.orientation_params_disp = []
183        ## smearer info
184        self.smearer = None
185        self.smear_type = None
186        self.dq_l = None
187        self.dq_r = None
188
189        #list of dispersion paramaters
190        self.disp_list =[]
191        if self.model is not None:
192            self.disp_list = self.model.getDispParamList()
193        self._disp_obj_dict = {}
194        self.disp_cb_dict = {}
195        self.values = []
196        self.weights = []
197                   
198        #contains link between a model and selected parameters to fit
199        self.param_toFit = []
200        ##dictionary of model type and model class
201        self.model_list_box = None
202        ## save the state of the context menu
203        self.saved_states = {}
204        ## save selection of combobox
205        self.formfactorcombobox = None
206        self.structurecombobox  = None
207        ## radio box to select type of model
208        self.shape_rbutton = False
209        self.shape_indep_rbutton = False
210        self.struct_rbutton = False
211        self.plugin_rbutton = False
212        ## the indice of the current selection
213        self.disp_box = 0
214        ## Qrange
215        ## Q range
216        self.qmin = 0.001
217        self.qmax = 0.1
218        #reset data range
219        self.qmax_x = None
220        self.qmin_x = None
221       
222        self.npts = None
223        self.name = ""
224        ## enable smearering state
225        self.enable_smearer = False
226        self.disable_smearer = True
227        self.pinhole_smearer = False
228        self.slit_smearer   = False
229        ## disperity selection
230        self.enable_disp = False
231        self.disable_disp = True
232       
233        ## state of selected all check button
234        self.cb1 = False
235        ## store value of chisqr
236        self.tcChi = None
237   
238    def clone(self):
239        """
240        Create a new copy of the current object
241        """
242        model = None
243        if self.model is not None:
244            model = self.model.clone()
245            model.name = self.model.name
246        obj = PageState(self.parent, model=model)
247        obj.file = copy.deepcopy(self.file)
248        obj.data = copy.deepcopy(self.data)
249        if self.data is not None:
250            self.data_name = self.data.name
251        obj.data_name = self.data_name
252        obj.is_data = self.is_data
253        obj.model_list_box = copy.deepcopy(self.model_list_box)
254        obj.engine_type = copy.deepcopy(self.engine_type)
255       
256        obj.formfactorcombobox = self.formfactorcombobox
257        obj.structurecombobox  = self.structurecombobox 
258       
259        obj.shape_rbutton = self.shape_rbutton
260        obj.shape_indep_rbutton = self.shape_indep_rbutton
261        obj.struct_rbutton = self.struct_rbutton
262        obj.plugin_rbutton = self.plugin_rbutton
263       
264        obj.manager = self.manager
265        obj.event_owner = self.event_owner
266        obj.disp_list = copy.deepcopy(self.disp_list)
267       
268        obj.enable2D = copy.deepcopy(self.enable2D)
269        obj.parameters = copy.deepcopy(self.parameters)
270        obj.fixed_param = copy.deepcopy(self.fixed_param)
271        obj.fittable_param = copy.deepcopy(self.fittable_param)
272        obj.orientation_params =  copy.deepcopy(self.orientation_params)
273        obj.orientation_params_disp =  copy.deepcopy(self.orientation_params_disp)
274        obj.enable_disp = copy.deepcopy(self.enable_disp)
275        obj.disable_disp = copy.deepcopy(self.disable_disp)
276        obj.tcChi = self.tcChi
277 
278        if len(self._disp_obj_dict)>0:
279            for k , v in self._disp_obj_dict.iteritems():
280                obj._disp_obj_dict[k]= v
281        if len(self.disp_cb_dict)>0:
282            for k , v in self.disp_cb_dict.iteritems():
283                obj.disp_cb_dict[k]= v
284               
285        obj.values = copy.deepcopy(self.values)
286        obj.weights = copy.deepcopy(self.weights)
287        obj.enable_smearer = copy.deepcopy(self.enable_smearer)
288        obj.disable_smearer = copy.deepcopy(self.disable_smearer)
289        obj.pinhole_smearer = copy.deepcopy(self.pinhole_smearer)
290        obj.slit_smearer = copy.deepcopy(self.slit_smearer)
291        obj.smear_type = copy.deepcopy(self.smear_type)
292        obj.dq_l = copy.deepcopy(self.dq_l)
293        obj.dq_r = copy.deepcopy(self.dq_r)
294
295        obj.disp_box = copy.deepcopy(self.disp_box)
296        obj.qmin = copy.deepcopy(self.qmin)
297        obj.qmax = copy.deepcopy(self.qmax)
298        obj.npts = copy.deepcopy(self.npts )
299        obj.cb1 = copy.deepcopy(self.cb1)
300        obj.smearer = copy.deepcopy(self.smearer)
301       
302        for name, state in self.saved_states.iteritems():
303            copy_name = copy.deepcopy(name)
304            copy_state = state.clone()
305            obj.saved_states[copy_name]= copy_state
306        return obj
307   
308    def _repr_helper(self, list, rep):
309        """
310        Helper method to print a state
311        """
312        for item in list:
313            rep += "parameter name: %s \n"%str(item[1])
314            rep += "value: %s\n"%str(item[2])
315            rep += "selected: %s\n"%str(item[0])
316            rep += "error displayed : %s \n"%str(item[4][0])
317            rep += "error value:%s \n"%str(item[4][1])
318            rep += "minimum displayed : %s \n"%str(item[5][0])
319            rep += "minimum value : %s \n"%str(item[5][1])
320            rep += "maximum displayed : %s \n"%str(item[6][0])
321            rep += "maximum value : %s \n"%str(item[6][1])
322            rep += "parameter unit: %s\n\n"%str(item[7])
323        return rep
324   
325    def __repr__(self):
326        """
327        output string for printing
328        """
329        rep = "\nState name: %s\n"%self.file
330        t = time.localtime(self.timestamp)
331        time_str = time.strftime("%b %d %H:%M", t)
332        rep += "State created on : %s\n"%time_str
333        rep += "State form factor combobox selection: %s\n"%self.formfactorcombobox
334        rep += "State structure factor combobox selection: %s\n"%self.structurecombobox
335        rep += "is data : %s\n"%self.is_data
336        rep += "data's name : %s\n"%self.data_name
337        rep += "data's id : %s\n"%self.data_id
338        rep += "model type (form factor) selected: %s\n"%self.shape_rbutton
339        rep += "model type (shape independent) selected: %s\n"%self.shape_indep_rbutton
340        rep += "model type (structure factor) selected: %s\n"%self.struct_rbutton
341        rep += "model type (plug-in ) selected: %s\n"%self.plugin_rbutton
342        rep += "data : %s\n"% str(self.data)
343        rep += "Plotting Range: min: %s, max: %s, steps: %s\n"%(str(self.qmin),
344                                                str(self.qmax),str(self.npts))
345        rep += "Dispersion selection : %s\n"%str(self.disp_box)
346        rep += "Smearing enable : %s\n"%str(self.enable_smearer)
347        rep += "Smearing disable : %s\n"%str(self.disable_smearer)
348        rep += "Pinhole smearer enable : %s\n"%str(self.pinhole_smearer)
349        rep += "Slit smearer enable : %s\n"%str(self.slit_smearer)
350        rep += "Dispersity enable : %s\n"%str(self.enable_disp)
351        rep += "Dispersity disable : %s\n"%str(self.disable_disp)
352        rep += "Slit smearer enable: %s\n"%str(self.slit_smearer)
353        rep += "2D enable : %s\n"%str(self.enable2D)
354        rep += "All parameters checkbox selected: %s\n"%(self.cb1)
355        rep += "Value of Chisqr : %s\n"%str(self.tcChi)
356        rep += "Smear object : %s\n"%str(self.smearer)
357        rep += "Smear type : %s\n"%(self.smear_type)
358        rep += "dq_l  : %s\n"%self.dq_l
359        rep += "dq_r  : %s\n"%self.dq_r
360       
361        rep += "model  : %s\n\n"% str(self.model)
362        rep += "number parameters(self.parameters): %s\n"%len(self.parameters)
363        rep += self._repr_helper( list=self.parameters, rep=rep)
364        rep += "number orientation parameters"
365        rep += "(self.orientation_params): %s\n"%len(self.orientation_params)
366        rep += self._repr_helper( list=self.orientation_params, rep=rep)
367        rep += "number dispersity parameters"
368        rep += "(self.orientation_params_disp): %s\n"%len(self.orientation_params_disp)
369        rep += self._repr_helper( list=self.orientation_params_disp, rep=rep)
370       
371        return rep
372   
373    def _toXML_helper(self, list, element, newdoc):
374        """
375        Helper method to create xml file for saving state
376        """
377        for item in list:
378            sub_element = newdoc.createElement('parameter')
379            sub_element.setAttribute('name', str(item[1]))
380            sub_element.setAttribute('value', str(item[2]))
381            sub_element.setAttribute('selected_to_fit', str(item[0]))
382            sub_element.setAttribute('error_displayed', str(item[4][0]))
383            sub_element.setAttribute('error_value', str(item[4][1]))
384            sub_element.setAttribute('minimum_displayed', str(item[5][0]))
385            sub_element.setAttribute('minimum_value', str(item[5][1]))
386            sub_element.setAttribute('maximum_displayed', str(item[6][0]))
387            sub_element.setAttribute('maximum_value', str(item[6][1]))
388            sub_element.setAttribute('unit', str(item[7]))
389            element.appendChild(sub_element)
390       
391    def toXML(self, file="fitting_state.fitv", doc=None, entry_node=None):
392        """
393        Writes the state of the InversionControl panel to file, as XML.
394       
395        Compatible with standalone writing, or appending to an
396        already existing XML document. In that case, the XML document
397        is required. An optional entry node in the XML document may also be given.
398       
399        :param file: file to write to
400        :param doc: XML document object [optional]
401        :param entry_node: XML node within the XML document at which we will append the data [optional]
402       
403        """
404        from xml.dom.minidom import getDOMImplementation
405
406        # Check whether we have to write a standalone XML file
407        if doc is None:
408            impl = getDOMImplementation()
409            doc_type = impl.createDocumentType(FITTING_NODE_NAME, "1.0", "1.0")     
410            newdoc = impl.createDocument(None, FITTING_NODE_NAME, doc_type)
411            top_element = newdoc.documentElement
412        else:
413            # We are appending to an existing document
414            newdoc = doc
415            top_element = newdoc.createElement(FITTING_NODE_NAME)
416            if entry_node is None:
417                newdoc.documentElement.appendChild(top_element)
418            else:
419                entry_node.appendChild(top_element)
420           
421        attr = newdoc.createAttribute("version")
422        attr.nodeValue = '1.0'
423        top_element.setAttributeNode(attr)
424       
425        # File name
426        element = newdoc.createElement("filename")
427        if self.file is not None:
428            element.appendChild(newdoc.createTextNode(str(self.file)))
429        else:
430            element.appendChild(newdoc.createTextNode(str(file)))
431        top_element.appendChild(element)
432       
433        element = newdoc.createElement("timestamp")
434        element.appendChild(newdoc.createTextNode(time.ctime(self.timestamp)))
435        attr = newdoc.createAttribute("epoch")
436        attr.nodeValue = str(self.timestamp)
437        element.setAttributeNode(attr)
438        top_element.appendChild(element)
439        # Inputs
440        inputs = newdoc.createElement("Attributes")
441        top_element.appendChild(inputs)
442       
443
444        if self.data is not None and hasattr(self.data, "group_id"):
445            self.data_group_id = self.data.group_id
446        if self.data is not None and hasattr(self.data, "is_data"):
447            self.is_data = self.data.is_data
448        if self.data is not None:
449            self.data_name = self.data.name
450        if self.data is not None and hasattr(self.data, "id"):
451            self.data_id = self.data.id
452       
453        for item in list_of_data_attributes:
454            element = newdoc.createElement(item[0])
455            exec "element.setAttribute(item[0], str(self.%s))"%(item[1])
456            inputs.appendChild(element)   
457       
458        for item in list_of_state_attributes:
459            element = newdoc.createElement(item[0])
460            exec "element.setAttribute(item[0], str(self.%s))"%(item[1])
461            inputs.appendChild(element)
462           
463        for item in list_of_model_attributes:
464            element = newdoc.createElement(item[0])
465            exec "list = self.%s"%item[1]
466            for value in list:
467                exec "element.appendChild(newdoc.createTextNode(str(%s)))"%value
468            inputs.appendChild(element)
469           
470        for item in list_of_state_parameters:
471            element = newdoc.createElement(item[0])
472            exec "self._toXML_helper(list=self.%s, element=element, newdoc=newdoc)"%item[1]                       
473            inputs.appendChild(element)
474       
475        # Save the file
476        if doc is None:
477            fd = open(file, 'w')
478            fd.write(newdoc.toprettyxml())
479            fd.close()
480            return None
481        else:
482            return newdoc.toprettyxml()
483       
484    def _fromXML_helper(self, node, list):
485        """
486        Helper function to write state to xml
487        """
488        for item in node:
489            try:
490                name = item.get('name')
491            except:
492                name = None
493            try:
494                value = item.get('value')
495            except:
496                value = None
497            try:
498                selected_to_fit = bool(item.get('selected_to_fit'))
499            except:
500                selected_to_fit = None
501            try:
502                error_displayed = item.get('error_displayed')
503            except:
504                error_displayed = None
505            try: 
506                error_value = item.get('error_value')
507            except:
508                error_value = None
509            try:
510                minimum_displayed = bool(item.get('minimum_displayed'))
511            except:
512                minimum_displayed = None
513            try:
514                minimum_value = item.get('minimum_value')
515            except:
516                minimum_value = None
517            try:
518                maximum_displayed = bool(item.get('maximum_displayed'))
519            except:
520                maximum_displayed = None
521            try:
522                maximum_value = item.get('maximum_value')
523            except:
524                maximum_value = None
525            try:
526                unit = item.get('unit')
527            except:
528                unit = None
529            list.append([selected_to_fit, name, value, "+/-",[error_displayed, error_value],
530                         [minimum_displayed,minimum_value],[maximum_displayed,maximum_value], unit])
531       
532    def fromXML(self, file=None, node=None):
533        """
534        Load fitting state from a file
535       
536        :param file: .fitv file
537        :param node: node of a XML document to read from
538       
539        """
540        if file is not None:
541            msg = "PageState no longer supports non-CanSAS"
542            msg += " format for fitting files"
543            raise RuntimeError, msg
544           
545        if node.get('version')and node.get('version') == '1.0':
546           
547            # Get file name
548            entry = get_content('ns:filename', node)
549            if entry is not None:
550                self.file = entry.text.strip()
551               
552            # Get time stamp
553            entry = get_content('ns:timestamp', node)
554            if entry is not None and entry.get('epoch'):
555                try:
556                    self.timestamp = float(entry.get('epoch'))
557                except:
558                    msg = "PageState.fromXML: Could not"
559                    msg += " read timestamp\n %s" % sys.exc_value
560                    logging.error(msg)
561           
562            # Parse fitting attributes
563            entry = get_content('ns:Attributes', node)
564            for item in list_of_data_attributes:
565                node = get_content('ns:%s'%item[0], entry)
566                try:
567                    exec "self.%s = parse_entry_helper(node, item)"%item[0]
568                   
569                except:
570                    raise
571           
572            if entry is not None:
573               
574                for item in list_of_state_attributes:
575                    node = get_content('ns:%s'%item[0], entry)
576                    try:
577                        exec "self.%s = parse_entry_helper(node, item)"%str(item[0])
578                    except:
579                        raise
580                   
581                for item in list_of_model_attributes:
582                    node = get_content("ns:%s"%item[0], entry)
583                    list = []
584                    for value in node:
585                        try:
586                            list.append(float(value)) 
587                        except:
588                            list.append(None)
589                    exec "self.%s = list"%item[1]
590               
591                for item in list_of_state_parameters:
592                    node = get_content("ns:%s"%item[0], entry)
593                    exec "self._fromXML_helper(node=node, list=self.%s)"%item[1]
594                   
595
596class Reader(CansasReader):
597    """
598    Class to load a .fitv fitting file
599    """
600    ## File type
601    type_name = "Fitting"
602   
603    ## Wildcards
604    type = ["Fitting files (*.fitv)|*.fitv"]
605    ## List of allowed extensions
606    ext=['.fitv', '.FITV'] 
607   
608    def __init__(self, call_back=None, cansas=True):
609        CansasReader.__init__(self)
610        """
611        Initialize the call-back method to be called
612        after we load a file
613       
614        :param call_back: call-back method
615        :param cansas:  True = files will be written/read in CanSAS format
616                        False = write CanSAS format
617           
618        """
619        ## Call back method to be executed after a file is read
620        self.call_back = call_back
621        ## CanSAS format flag
622        self.cansas = cansas
623       
624    def read(self, path):
625        """
626        Load a new P(r) inversion state from file
627       
628        :param path: file path
629       
630        """
631        if self.cansas == True:
632            return self._read_cansas(path)
633     
634    def _data2d_to_xml_doc(self, datainfo):
635        """
636        Create an XML document to contain the content of a Data2D
637       
638        :param datainfo: Data2D object
639       
640        """
641        if not issubclass(datainfo.__class__, Data2D):
642            raise RuntimeError, "The cansas writer expects a Data2D instance"
643       
644        doc = xml.dom.minidom.Document()
645        main_node = doc.createElement("SASroot")
646        main_node.setAttribute("version", self.version)
647        main_node.setAttribute("xmlns", "cansas1d/%s" % self.version)
648        main_node.setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance")
649        main_node.setAttribute("xsi:schemaLocation", "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.version)
650       
651        doc.appendChild(main_node)
652       
653        entry_node = doc.createElement("SASentry")
654        main_node.appendChild(entry_node)
655       
656        write_node(doc, entry_node, "Title", datainfo.title)
657        if datainfo is not None:
658            write_node(doc, entry_node, "data_class", datainfo.__class__.__name__)
659        for item in datainfo.run:
660            runname = {}
661            if datainfo.run_name.has_key(item) and len(str(datainfo.run_name[item]))>1:
662                runname = {'name': datainfo.run_name[item] }
663            write_node(doc, entry_node, "Run", item, runname)
664        # Data info
665        new_node = doc.createElement("SASdata")
666        entry_node.appendChild(new_node)
667        for item in list_of_data_2d_attr:
668            element = doc.createElement(item[0])
669            exec "element.setAttribute(item[0], str(datainfo.%s))"%(item[1])
670            new_node.appendChild(element)
671           
672        for item in list_of_data2d_values:
673            root_node = doc.createElement(item[0])
674            new_node.appendChild(root_node)
675           
676            exec "temp_list = datainfo.%s"%item[1]
677
678            if temp_list is None or len(temp_list)== 0:
679                element = doc.createElement(item[0])
680                exec "element.appendChild(doc.createTextNode(str(%s)))"%temp_list
681                root_node.appendChild(element)
682            else:
683                for value in temp_list:
684                    element = doc.createElement(item[0])
685                    exec "element.setAttribute(item[0], str(%s))"%value
686                    root_node.appendChild(element)
687       
688        # Sample info
689        sample = doc.createElement("SASsample")
690        if datainfo.sample.name is not None:
691            sample.setAttribute("name", str(datainfo.sample.name))
692        entry_node.appendChild(sample)
693        write_node(doc, sample, "ID", str(datainfo.sample.ID))
694        write_node(doc, sample, "thickness", datainfo.sample.thickness, {"unit":datainfo.sample.thickness_unit})
695        write_node(doc, sample, "transmission", datainfo.sample.transmission)
696        write_node(doc, sample, "temperature", datainfo.sample.temperature, {"unit":datainfo.sample.temperature_unit})
697       
698        for item in datainfo.sample.details:
699            write_node(doc, sample, "details", item)
700       
701        pos = doc.createElement("position")
702        written = write_node(doc, pos, "x", datainfo.sample.position.x, {"unit":datainfo.sample.position_unit})
703        written = written | write_node(doc, pos, "y", datainfo.sample.position.y, {"unit":datainfo.sample.position_unit})
704        written = written | write_node(doc, pos, "z", datainfo.sample.position.z, {"unit":datainfo.sample.position_unit})
705        if written == True:
706            sample.appendChild(pos)
707       
708        ori = doc.createElement("orientation")
709        written = write_node(doc, ori, "roll",  datainfo.sample.orientation.x, {"unit":datainfo.sample.orientation_unit})
710        written = written | write_node(doc, ori, "pitch", datainfo.sample.orientation.y, {"unit":datainfo.sample.orientation_unit})
711        written = written | write_node(doc, ori, "yaw",   datainfo.sample.orientation.z, {"unit":datainfo.sample.orientation_unit})
712        if written == True:
713            sample.appendChild(ori)
714       
715        # Instrument info
716        instr = doc.createElement("SASinstrument")
717        entry_node.appendChild(instr)
718       
719        write_node(doc, instr, "name", datainfo.instrument)
720       
721        #   Source
722        source = doc.createElement("SASsource")
723        if datainfo.source.name is not None:
724            source.setAttribute("name", str(datainfo.source.name))
725        instr.appendChild(source)
726       
727        write_node(doc, source, "radiation", datainfo.source.radiation)
728        write_node(doc, source, "beam_shape", datainfo.source.beam_shape)
729        size = doc.createElement("beam_size")
730        if datainfo.source.beam_size_name is not None:
731            size.setAttribute("name", str(datainfo.source.beam_size_name))
732        written = write_node(doc, size, "x", datainfo.source.beam_size.x, {"unit":datainfo.source.beam_size_unit})
733        written = written | write_node(doc, size, "y", datainfo.source.beam_size.y, {"unit":datainfo.source.beam_size_unit})
734        written = written | write_node(doc, size, "z", datainfo.source.beam_size.z, {"unit":datainfo.source.beam_size_unit})
735        if written == True:
736            source.appendChild(size)
737           
738        write_node(doc, source, "wavelength", datainfo.source.wavelength, {"unit":datainfo.source.wavelength_unit})
739        write_node(doc, source, "wavelength_min", datainfo.source.wavelength_min, {"unit":datainfo.source.wavelength_min_unit})
740        write_node(doc, source, "wavelength_max", datainfo.source.wavelength_max, {"unit":datainfo.source.wavelength_max_unit})
741        write_node(doc, source, "wavelength_spread", datainfo.source.wavelength_spread, {"unit":datainfo.source.wavelength_spread_unit})
742       
743        #   Collimation
744        for item in datainfo.collimation:
745            coll = doc.createElement("SAScollimation")
746            if item.name is not None:
747                coll.setAttribute("name", str(item.name))
748            instr.appendChild(coll)
749           
750            write_node(doc, coll, "length", item.length, {"unit":item.length_unit})
751           
752            for apert in item.aperture:
753                ap = doc.createElement("aperture")
754                if apert.name is not None:
755                    ap.setAttribute("name", str(apert.name))
756                if apert.type is not None:
757                    ap.setAttribute("type", str(apert.type))
758                coll.appendChild(ap)
759               
760                write_node(doc, ap, "distance", apert.distance, {"unit":apert.distance_unit})
761               
762                size = doc.createElement("size")
763                if apert.size_name is not None:
764                    size.setAttribute("name", str(apert.size_name))
765                written = write_node(doc, size, "x", apert.size.x, {"unit":apert.size_unit})
766                written = written | write_node(doc, size, "y", apert.size.y, {"unit":apert.size_unit})
767                written = written | write_node(doc, size, "z", apert.size.z, {"unit":apert.size_unit})
768                if written == True:
769                    ap.appendChild(size)
770
771        #   Detectors
772        for item in datainfo.detector:
773            det = doc.createElement("SASdetector")
774            written = write_node(doc, det, "name", item.name)
775            written = written | write_node(doc, det, "SDD", item.distance, {"unit":item.distance_unit})
776            written = written | write_node(doc, det, "slit_length", item.slit_length, {"unit":item.slit_length_unit})
777            if written == True:
778                instr.appendChild(det)
779           
780            off = doc.createElement("offset")
781            written = write_node(doc, off, "x", item.offset.x, {"unit":item.offset_unit})
782            written = written | write_node(doc, off, "y", item.offset.y, {"unit":item.offset_unit})
783            written = written | write_node(doc, off, "z", item.offset.z, {"unit":item.offset_unit})
784            if written == True:
785                det.appendChild(off)
786           
787            center = doc.createElement("beam_center")
788            written = write_node(doc, center, "x", item.beam_center.x, {"unit":item.beam_center_unit})
789            written = written | write_node(doc, center, "y", item.beam_center.y, {"unit":item.beam_center_unit})
790            written = written | write_node(doc, center, "z", item.beam_center.z, {"unit":item.beam_center_unit})
791            if written == True:
792                det.appendChild(center)
793               
794            pix = doc.createElement("pixel_size")
795            written = write_node(doc, pix, "x", item.pixel_size.x, {"unit":item.pixel_size_unit})
796            written = written | write_node(doc, pix, "y", item.pixel_size.y, {"unit":item.pixel_size_unit})
797            written = written | write_node(doc, pix, "z", item.pixel_size.z, {"unit":item.pixel_size_unit})
798            if written == True:
799                det.appendChild(pix)
800               
801            ori = doc.createElement("orientation")
802            written = write_node(doc, ori, "roll",  item.orientation.x, {"unit":item.orientation_unit})
803            written = written | write_node(doc, ori, "pitch", item.orientation.y, {"unit":item.orientation_unit})
804            written = written | write_node(doc, ori, "yaw",   item.orientation.z, {"unit":item.orientation_unit})
805            if written == True:
806                det.appendChild(ori)
807               
808        # Processes info
809        for item in datainfo.process:
810            node = doc.createElement("SASprocess")
811            entry_node.appendChild(node)
812
813            write_node(doc, node, "name", item.name)
814            write_node(doc, node, "date", item.date)
815            write_node(doc, node, "description", item.description)
816            for term in item.term:
817                value = term['value']
818                del term['value']
819                write_node(doc, node, "term", value, term)
820            for note in item.notes:
821                write_node(doc, node, "SASprocessnote", note)
822        # Return the document, and the SASentry node associated with
823        # the data we just wrote
824        return doc, entry_node
825   
826    def _parse_state(self, entry):
827        """
828        Read a fit result from an XML node
829       
830        :param entry: XML node to read from
831       
832        :return: PageState object
833        """
834        # Create an empty state
835        state = PageState()
836        # Locate the P(r) node
837        try:
838            nodes = entry.xpath('ns:%s' % FITTING_NODE_NAME, namespaces={'ns': CANSAS_NS})
839            state.fromXML(node=nodes[0])
840        except:
841            logging.info("XML document does not contain fitting information.\n %s" % sys.exc_value)
842           
843        return state
844   
845   
846                   
847    def _parse_entry(self, dom):
848        """
849        Parse a SASentry
850       
851        :param node: SASentry node
852       
853        :return: Data1D/Data2D object
854       
855        """
856        node = dom.xpath('ns:data_class', namespaces={'ns': CANSAS_NS})
857        if not node or node[0].text.lstrip().rstrip() != "Data2D":
858            return CansasReader._parse_entry(self, dom)
859       
860        #Parse 2D
861        data_info = Data2D()
862       
863        # Look up title     
864        self._store_content('ns:Title', dom, 'title', data_info)
865       
866        # Look up run number   
867        nodes = dom.xpath('ns:Run', namespaces={'ns': CANSAS_NS})
868        for item in nodes:   
869            if item.text is not None:
870                value = item.text.strip()
871                if len(value) > 0:
872                    data_info.run.append(value)
873                    if item.get('name') is not None:
874                        data_info.run_name[value] = item.get('name')
875                           
876        # Look up instrument name             
877        self._store_content('ns:SASinstrument/ns:name', dom, 'instrument', data_info)
878
879        # Notes
880        note_list = dom.xpath('ns:SASnote', namespaces={'ns': CANSAS_NS})
881        for note in note_list:
882            try:
883                if note.text is not None:
884                    note_value = note.text.strip()
885                    if len(note_value) > 0:
886                        data_info.notes.append(note_value)
887            except:
888                err_mess = "cansas_reader.read: error processing entry notes\n  %s" % sys.exc_value
889                self.errors.append(err_mess)
890                logging.error(err_mess)
891       
892        # Sample info ###################
893        entry = get_content('ns:SASsample', dom)
894        if entry is not None:
895            data_info.sample.name = entry.get('name')
896           
897        self._store_content('ns:SASsample/ns:ID', 
898                     dom, 'ID', data_info.sample)                   
899        self._store_float('ns:SASsample/ns:thickness', 
900                     dom, 'thickness', data_info.sample)
901        self._store_float('ns:SASsample/ns:transmission', 
902                     dom, 'transmission', data_info.sample)
903        self._store_float('ns:SASsample/ns:temperature', 
904                     dom, 'temperature', data_info.sample)
905       
906        nodes = dom.xpath('ns:SASsample/ns:details', namespaces={'ns': CANSAS_NS})
907        for item in nodes:
908            try:
909                if item.text is not None:
910                    detail_value = item.text.strip()
911                    if len(detail_value) > 0:
912                        data_info.sample.details.append(detail_value)
913            except:
914                err_mess = "cansas_reader.read: error processing sample details\n  %s" % sys.exc_value
915                self.errors.append(err_mess)
916                logging.error(err_mess)
917       
918        # Position (as a vector)
919        self._store_float('ns:SASsample/ns:position/ns:x', 
920                     dom, 'position.x', data_info.sample)         
921        self._store_float('ns:SASsample/ns:position/ns:y', 
922                     dom, 'position.y', data_info.sample)         
923        self._store_float('ns:SASsample/ns:position/ns:z', 
924                     dom, 'position.z', data_info.sample)         
925       
926        # Orientation (as a vector)
927        self._store_float('ns:SASsample/ns:orientation/ns:roll', 
928                     dom, 'orientation.x', data_info.sample)         
929        self._store_float('ns:SASsample/ns:orientation/ns:pitch', 
930                     dom, 'orientation.y', data_info.sample)         
931        self._store_float('ns:SASsample/ns:orientation/ns:yaw', 
932                     dom, 'orientation.z', data_info.sample)         
933       
934        # Source info ###################
935        entry = get_content('ns:SASinstrument/ns:SASsource', dom)
936        if entry is not None:
937            data_info.source.name = entry.get('name')
938       
939        self._store_content('ns:SASinstrument/ns:SASsource/ns:radiation', 
940                     dom, 'radiation', data_info.source)                   
941        self._store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape', 
942                     dom, 'beam_shape', data_info.source)                   
943        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength', 
944                     dom, 'wavelength', data_info.source)         
945        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min', 
946                     dom, 'wavelength_min', data_info.source)         
947        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max', 
948                     dom, 'wavelength_max', data_info.source)         
949        self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread', 
950                     dom, 'wavelength_spread', data_info.source)   
951       
952        # Beam size (as a vector)   
953        entry = get_content('ns:SASinstrument/ns:SASsource/ns:beam_size', dom)
954        if entry is not None:
955            data_info.source.beam_size_name = entry.get('name')
956           
957        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x', 
958                     dom, 'beam_size.x', data_info.source)   
959        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y', 
960                     dom, 'beam_size.y', data_info.source)   
961        self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z', 
962                     dom, 'beam_size.z', data_info.source)   
963       
964        # Collimation info ###################
965        nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation', namespaces={'ns': CANSAS_NS})
966        for item in nodes:
967            collim = Collimation()
968            if item.get('name') is not None:
969                collim.name = item.get('name')
970            self._store_float('ns:length', item, 'length', collim) 
971           
972            # Look for apertures
973            apert_list = item.xpath('ns:aperture', namespaces={'ns': CANSAS_NS})
974            for apert in apert_list:
975                aperture =  Aperture()
976               
977                # Get the name and type of the aperture
978                aperture.name = apert.get('name')
979                aperture.type = apert.get('type')
980                   
981                self._store_float('ns:distance', apert, 'distance', aperture)   
982               
983                entry = get_content('ns:size', apert)
984                if entry is not None:
985                    aperture.size_name = entry.get('name')
986               
987                self._store_float('ns:size/ns:x', apert, 'size.x', aperture)   
988                self._store_float('ns:size/ns:y', apert, 'size.y', aperture)   
989                self._store_float('ns:size/ns:z', apert, 'size.z', aperture)
990               
991                collim.aperture.append(aperture)
992               
993            data_info.collimation.append(collim)
994       
995        # Detector info ######################
996        nodes = dom.xpath('ns:SASinstrument/ns:SASdetector', namespaces={'ns': CANSAS_NS})
997        for item in nodes:
998           
999            detector = Detector()
1000           
1001            self._store_content('ns:name', item, 'name', detector)
1002            self._store_float('ns:SDD', item, 'distance', detector)   
1003           
1004            # Detector offset (as a vector)
1005            self._store_float('ns:offset/ns:x', item, 'offset.x', detector)   
1006            self._store_float('ns:offset/ns:y', item, 'offset.y', detector)   
1007            self._store_float('ns:offset/ns:z', item, 'offset.z', detector)   
1008           
1009            # Detector orientation (as a vector)
1010            self._store_float('ns:orientation/ns:roll',  item, 'orientation.x', detector)   
1011            self._store_float('ns:orientation/ns:pitch', item, 'orientation.y', detector)   
1012            self._store_float('ns:orientation/ns:yaw',   item, 'orientation.z', detector)   
1013           
1014            # Beam center (as a vector)
1015            self._store_float('ns:beam_center/ns:x', item, 'beam_center.x', detector)   
1016            self._store_float('ns:beam_center/ns:y', item, 'beam_center.y', detector)   
1017            self._store_float('ns:beam_center/ns:z', item, 'beam_center.z', detector)   
1018           
1019            # Pixel size (as a vector)
1020            self._store_float('ns:pixel_size/ns:x', item, 'pixel_size.x', detector)   
1021            self._store_float('ns:pixel_size/ns:y', item, 'pixel_size.y', detector)   
1022            self._store_float('ns:pixel_size/ns:z', item, 'pixel_size.z', detector)   
1023           
1024            self._store_float('ns:slit_length', item, 'slit_length', detector)
1025           
1026            data_info.detector.append(detector)   
1027
1028        # Processes info ######################
1029        nodes = dom.xpath('ns:SASprocess', namespaces={'ns': CANSAS_NS})
1030        for item in nodes:
1031            process = Process()
1032            self._store_content('ns:name', item, 'name', process)
1033            self._store_content('ns:date', item, 'date', process)
1034            self._store_content('ns:description', item, 'description', process)
1035           
1036            term_list = item.xpath('ns:term', namespaces={'ns': CANSAS_NS})
1037            for term in term_list:
1038                try:
1039                    term_attr = {}
1040                    for attr in term.keys():
1041                        term_attr[attr] = term.get(attr).strip()
1042                    if term.text is not None:
1043                        term_attr['value'] = term.text.strip()
1044                        process.term.append(term_attr)
1045                except:
1046                    err_mess = "cansas_reader.read: error processing process term\n  %s" % sys.exc_value
1047                    self.errors.append(err_mess)
1048                    logging.error(err_mess)
1049           
1050            note_list = item.xpath('ns:SASprocessnote', namespaces={'ns': CANSAS_NS})
1051            for note in note_list:
1052                if note.text is not None:
1053                    process.notes.append(note.text.strip())
1054           
1055            data_info.process.append(process)
1056           
1057           
1058        # Data info ######################
1059        nodes = dom.xpath('ns:SASdata', namespaces={'ns': CANSAS_NS})
1060        if len(nodes)>1:
1061            raise RuntimeError, "CanSAS reader is not compatible with multiple SASdata entries"
1062       
1063        for entry in nodes:
1064            for item in list_of_data_2d_attr:
1065                #get node
1066                node = get_content('ns:%s'%item[0], entry)
1067                exec "data_info.%s = parse_entry_helper(node, item)"%(item[1])
1068                   
1069            for item in list_of_data2d_values:
1070                field = get_content('ns:%s'%item[0], entry)
1071                list = []
1072                if field is not None:
1073                    list = [parse_entry_helper(node, item) for node in field]
1074                exec "data_info.%s = numpy.array(list)"%item[0]
1075       
1076        return data_info
1077
1078    def _read_cansas(self, path):
1079        """
1080        Load data and P(r) information from a CanSAS XML file.
1081       
1082        :param path: file path
1083       
1084        :return: Data1D object if a single SASentry was found,
1085                    or a list of Data1D objects if multiple entries were found,
1086                    or None of nothing was found
1087                   
1088        :raise RuntimeError: when the file can't be opened
1089        :raise ValueError: when the length of the data vectors are inconsistent
1090       
1091        """
1092        output = []
1093        try:
1094            if os.path.isfile(path):
1095                basename  = os.path.basename(path)
1096                root, extension = os.path.splitext(basename)
1097                #TODO: eventually remove the check for .xml once
1098                # the P(r) writer/reader is truly complete.
1099                if  extension.lower() in self.ext or \
1100                    extension.lower() == '.xml':
1101                   
1102                    tree = etree.parse(path, parser=etree.ETCompatXMLParser())
1103                    # Check the format version number
1104                    # Specifying the namespace will take care of the file format version
1105                    root = tree.getroot()
1106                    entry_list = root.xpath('ns:SASentry', namespaces={'ns': CANSAS_NS})
1107                    for entry in entry_list:
1108                        try:
1109                            sas_entry = self._parse_entry(entry)
1110                        except:
1111                            raise
1112                        fitstate = self._parse_state(entry)
1113                        sas_entry.meta_data['fitstate'] = fitstate
1114                        sas_entry.filename = fitstate.file
1115                        output.append(sas_entry)
1116            else:
1117                raise RuntimeError, "%s is not a file" % path
1118           
1119            # Return output consistent with the loader's api
1120            if len(output)==0:
1121                return None
1122            elif len(output)==1:
1123                # Call back to post the new state
1124                state = output[0].meta_data['fitstate']
1125                t = time.localtime(state.timestamp)
1126                time_str = time.strftime("%b %d %H:%M", t)
1127                # Check that no time stamp is already appended
1128                max_char = state.file.find("[")
1129                if max_char < 0:
1130                    max_char = len(state.file)
1131                state.file = state.file[0:max_char] +' [' + time_str + ']'
1132               
1133                   
1134                if state is not None and state.is_data is not None:
1135                    exec 'output[0].is_data = state.is_data' 
1136                 
1137                output[0].filename = state.file
1138                state.data = output[0]
1139                state.data.name = state.data_name
1140                state.data.id = state.data_id
1141                state.data.id = state.data_id
1142                if state.is_data is not None:
1143                    state.data.is_data = state.is_data
1144                state.data.group_id = output[0].filename
1145             
1146                self.call_back(state=state, datainfo=output[0])
1147                return output[0]
1148            else:
1149                return output               
1150        except:
1151            raise
1152           
1153    def write(self, filename, datainfo=None, fitstate=None):
1154        """
1155        Write the content of a Data1D as a CanSAS XML file
1156       
1157        :param filename: name of the file to write
1158        :param datainfo: Data1D object
1159        :param fitstate: PageState object
1160       
1161        """
1162        # Sanity check
1163        if self.cansas == True:
1164           
1165            # Add fitting information to the XML document
1166            if fitstate is not None:
1167                if fitstate.data is None:
1168                    data = DataLoader.data_info.Data1D(x=[], y=[])   
1169                elif issubclass(fitstate.data.__class__, DataLoader.data_info.Data1D):
1170                    data = fitstate.data
1171                    doc, sasentry = self._to_xml_doc(data)
1172                else:
1173                    data = fitstate.data
1174                    doc, sasentry = self._data2d_to_xml_doc(data)
1175                fitstate.toXML(doc=doc, file=data.name, entry_node=sasentry)
1176            # Write the XML document
1177            fd = open(filename, 'w')
1178            fd.write(doc.toprettyxml())
1179            fd.close()
1180        else:
1181            fitstate.toXML(file=filename)
1182       
1183"""     
1184Example: ::
1185 
1186    if __name__ == "__main__":
1187        state = PageState(parent=None)
1188        state.toXML()
1189        print "state", state
1190   
1191"""
Note: See TracBrowser for help on using the repository browser.