source: sasview/sansview/perspectives/fitting/pagestate.py @ e3d1423

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

work on save state

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