- Timestamp:
- Dec 13, 2013 4:48:36 PM (11 years ago)
- Branches:
- master, ESS_GUI, ESS_GUI_Docs, ESS_GUI_batch_fitting, ESS_GUI_bumps_abstraction, ESS_GUI_iss1116, ESS_GUI_iss879, ESS_GUI_iss959, ESS_GUI_opencl, ESS_GUI_ordering, ESS_GUI_sync_sascalc, costrafo411, magnetic_scatt, release-4.1.1, release-4.1.2, release-4.2.2, release_4.0.1, ticket-1009, ticket-1094-headless, ticket-1242-2d-resolution, ticket-1243, ticket-1249, ticket885, unittest-saveload
- Children:
- a25d242
- Parents:
- f28fd9cf
- Location:
- src/sans/dataloader/readers
- Files:
-
- 2 added
- 1 deleted
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sans/dataloader/readers/cansas_reader.py
rffbe487 reda8972 1 1 """ 2 CanSAS data reader 2 CanSAS data reader - new recursive cansasVersion. 3 3 """ 4 4 ############################################################################ … … 13 13 ############################################################################# 14 14 15 # Known issue: reader not compatible with multiple SASdata entries16 # within a single SASentry. Will raise a runtime error.17 18 #TODO: check that all vectors are written only if they have at19 # least one non-empty value20 #TODO: Writing only allows one SASentry per file.21 # Would be best to allow multiple entries.22 #TODO: Store error list23 #TODO: Allow for additional meta data for each section24 #TODO: Notes need to be implemented. They can be any XML25 # structure in version 1.026 # Process notes have the same problem.27 #TODO: Unit conversion is not complete (temperature units are missing)28 29 15 import logging 30 16 import numpy … … 36 22 from sans.dataloader.data_info import Process 37 23 from sans.dataloader.data_info import Aperture 38 from lxml import etree 24 import xml_reader 39 25 import xml.dom.minidom 26 from cansas_constants import cansasConstants 27 40 28 _ZERO = 1e-16 41 29 HAS_CONVERTER = True … … 44 32 except: 45 33 HAS_CONVERTER = False 46 47 CANSAS_NS = "cansas1d/1.0" 34 35 CANSAS_FORMAT = cansasConstants.CANSAS_FORMAT 36 CANSAS_NS = cansasConstants.CANSAS_NS 48 37 ALLOW_ALL = True 49 38 … … 67 56 return True 68 57 return False 69 58 70 59 71 60 def get_content(location, node): … … 109 98 return value, attr 110 99 111 112 class Reader: 100 101 102 class CANSASError(Exception): 103 """Base class all CANSAS reader exceptions are derived""" 104 pass 105 106 class NotCANSASFileError(CANSASError): 107 def __init__(self): 108 self.value = "This is not a proper CanSAS file." 109 def __str__(self): 110 return repr(self.value) 111 112 class Reader(): 113 113 """ 114 114 Class to load cansas 1D XML files 115 115 116 116 :Dependencies: 117 The CanSas reader requires PyXML 0.8.4 or later. 118 """ 119 ## CanSAS version 120 version = '1.0' 121 ## File type 122 type_name = "CanSAS 1D" 123 ## Wildcards 124 type = ["CanSAS 1D files (*.xml)|*.xml", 125 "CanSAS 1D AVE files (*.AVEx)|*.AVEx", 126 "CanSAS 1D AVE files (*.ABSx)|*.ABSx"] 127 128 ## List of allowed extensions 129 ext = ['.xml', '.XML', '.avex', '.AVEx', '.absx', 'ABSx'] 117 The CanSAS reader requires PyXML 0.8.4 or later. 118 """ 119 ##CanSAS version - defaults to version 1.0 120 cansasVersion = "1.0" 121 ##Data reader 122 reader = xml_reader.XMLreader() 123 errors = [] 130 124 131 125 def __init__(self): 132 126 ## List of errors 133 127 self.errors = [] 134 135 def read(self, path): 136 """ 137 Load data file 138 139 :param path: file path 140 141 :return: Data1D object if a single SASentry was found, 142 or a list of Data1D objects if multiple entries were found, 143 or None of nothing was found 128 129 def isCansas(self): 130 """ 131 Checks to see if the xml file is a CanSAS file 132 """ 133 if self.reader.validateXML(): 134 xmlns = self.reader.xmlroot.keys() 135 if (CANSAS_NS.get(self.cansasVersion).get("ns") == self.reader.xmlroot.get(xmlns[1]).rsplit(" ")[0]): 136 return True 137 return False 138 139 def read(self, xml): 140 """ 141 Validate and read in an xml file in the canSAS format. 142 143 :param xml: A canSAS file path in proper XML format 144 """ 145 # X - Q value; Y - Intensity (Abs) 146 x = numpy.empty(0) 147 y = numpy.empty(0) 148 dx = numpy.empty(0) 149 dy = numpy.empty(0) 150 dxl = numpy.empty(0) 151 dxw = numpy.empty(0) 152 153 # output - Final list of Data1D objects 154 output = [] 155 # ns - Namespace hierarchy for current xml object 156 ns = [] 157 158 try: 159 # Load in the xml file and get the cansas version from the header 160 self.reader.setXMLFile(xml) 161 root = self.reader.xmlroot 162 self.cansasVersion = root.get("version") 163 # Generic values for the cansas file based on the version 164 cansas_defaults = CANSAS_NS.get(self.cansasVersion) 165 166 # Link a schema to the XML file. 167 basename = os.path.basename(xml) 168 base_name = xml_reader.__file__ 169 base = base_name.split("\\sans\\")[0] 170 schema_path = "{0}\\sans\\dataloader\\readers\\schema\\{1}".format(base, cansas_defaults.get("schema")).replace("\\", "/") 171 self.reader.setSchema(schema_path) 172 173 # Try to load the file, but raise an error if unable to. 174 # Check the file matches the XML schema 175 if self.isCansas(): 176 # Get each SASentry from the XML file and add it to a list. 177 entry_list = root.xpath('/ns:SASroot/ns:SASentry', 178 namespaces={'ns': cansas_defaults.get("ns")}) 179 ns.append("SASentry") 180 181 # If there are multiple files, modify the name for each is unique 182 multipleFiles = len(entry_list) - 1 183 n = 0 184 name = basename 185 # Parse each SASentry item 186 for entry in entry_list: 144 187 145 :raise RuntimeError: when the file can't be opened 146 :raise ValueError: when the length of the data vectors are inconsistent 147 """ 148 output = [] 149 if os.path.isfile(path): 150 basename = os.path.basename(path) 151 root, extension = os.path.splitext(basename) 152 if ALLOW_ALL or extension.lower() in self.ext: 153 try: 154 tree = etree.parse(path, parser=etree.ETCompatXMLParser()) 155 # Check the format version number 156 # Specifying the namespace will take care of the file 157 # format version 158 root = tree.getroot() 188 # Define a new Data1D object with zeroes for x and y 189 data1D = Data1D(x,y,dx,dy) 190 data1D.dxl = dxl 191 data1D.dxw = dxw 159 192 160 entry_list = root.xpath('/ns:SASroot/ns:SASentry', 161 namespaces={'ns': CANSAS_NS}) 193 # If more than one SASentry, number each in order 194 if multipleFiles: 195 name += "_{0}".format(n) 196 n += 1 162 197 163 for entry in entry_list: 164 self.errors = [] 165 sas_entry = self._parse_entry(entry) 166 sas_entry.filename = basename 167 168 # Store loading process information 169 sas_entry.errors = self.errors 170 sas_entry.meta_data['loader'] = self.type_name 171 output.append(sas_entry) 172 except: 173 raise RuntimeError, "%s cannot be read \n" % path 174 else: 175 raise RuntimeError, "%s is not a file" % path 176 # Return output consistent with the loader's api 177 if len(output) == 0: 178 #cannot return none when it cannot read 179 #return None 180 raise RuntimeError, "%s cannot be read \n" % path 181 elif len(output) == 1: 182 return output[0] 183 else: 184 return output 185 186 def _parse_entry(self, dom): 187 """ 188 Parse a SASentry 189 190 :param node: SASentry node 191 192 :return: Data1D object 193 """ 194 x = numpy.zeros(0) 195 y = numpy.zeros(0) 196 197 data_info = Data1D(x, y) 198 199 # Look up title 200 self._store_content('ns:Title', dom, 'title', data_info) 201 202 # Look up run number 203 nodes = dom.xpath('ns:Run', namespaces={'ns': CANSAS_NS}) 204 for item in nodes: 205 if item.text is not None: 206 value = item.text.strip() 207 if len(value) > 0: 208 data_info.run.append(value) 209 if item.get('name') is not None: 210 data_info.run_name[value] = item.get('name') 211 212 # Look up instrument name 213 self._store_content('ns:SASinstrument/ns:name', dom, 'instrument', 214 data_info) 215 216 # Notes 217 note_list = dom.xpath('ns:SASnote', namespaces={'ns': CANSAS_NS}) 218 for note in note_list: 198 # Set the Data1D name and then parse the entry. The entry is appended to a list of entry values 199 data1D.filename = name 200 data1D.meta_data["loader"] = "CanSAS 1D" 201 return_value, extras = self._parse_entry(entry, ns, data1D) 202 del extras[:] 203 204 #Final cleanup - Remove empty nodes, verify array sizes are correct 205 return_value.errors = self.errors 206 del self.errors[:] 207 numpy.trim_zeros(return_value.x) 208 numpy.trim_zeros(return_value.y) 209 numpy.trim_zeros(return_value.dy) 210 size_dx = return_value.dx.size 211 size_dxl = return_value.dxl.size 212 size_dxw = return_value.dxw.size 213 if size_dxl == 0 and size_dxw == 0: 214 return_value.dxl = None 215 return_value.dxw = None 216 numpy.trim_zeros(return_value.dx) 217 elif size_dx == 0: 218 return_value.dx = None 219 size_dx = size_dxl 220 numpy.trim_zeros(return_value.dxl) 221 numpy.trim_zeros(return_value.dxw) 222 223 output.append(return_value) 224 else: 225 # If the file does not match the schema, raise this error 226 raise RuntimeError, "%s cannot be read \n" % xml 227 # If an exception occurs while loading the file, give a descriptive output. 228 except Exception: 229 raise RuntimeError, "%s cannot be read \n" % xml 230 231 # Return a list of parsed entries that dataloader can manage 232 return output 233 234 def _create_unique_key(self, dictionary, name, i): 235 if dictionary.get(name) is not None: 236 i += 1 237 name = name.split("_")[0] 238 name += "_{0}".format(i) 239 name = self._create_unique_key(dictionary, name, i) 240 return name 241 242 def _iterate_namespace(self, ns): 243 # The current level to look through in cansas_constants. 244 current_level = CANSAS_FORMAT.get("SASentry") 245 # Defaults for variable and datatype 246 ns_variable = "{0}.meta_data[\"{2}\"] = \"{1}\"" 247 ns_datatype = "content" 248 ns_optional = True 249 for name in ns: 250 if name != "SASentry": 251 current_level = current_level.get("children").get(name, "") 252 if current_level == "": 253 current_level = current_level.get("<any>", "") 254 cl_variable = current_level.get("variable", "") 255 cl_datatype = current_level.get("storeas", "") 256 cl_units_optional = current_level.get("units_required", "") 257 # Where are how to store the variable for the given namespace 258 # The CANSAS_CONSTANTS tree is hierarchical, so is no value, inherit 259 ns_variable = cl_variable if cl_variable != "" else ns_variable 260 ns_datatype = cl_datatype if cl_datatype != "" else ns_datatype 261 ns_optional = cl_units_optional if cl_units_optional != ns_optional else ns_optional 262 return current_level, ns_variable, ns_datatype, ns_optional 263 264 def _unit_conversion(self, new_current_level, attr, data1D, node_value, optional = True): 265 value_unit = '' 266 if 'unit' in attr and 'unit' in new_current_level: 219 267 try: 220 if note.text is not None: 221 note_value = note.text.strip() 222 if len(note_value) > 0: 223 data_info.notes.append(note_value) 224 except: 225 err_mess = "cansas_reader.read: error processing" 226 err_mess += " entry notes\n %s" % sys.exc_value 227 self.errors.append(err_mess) 228 logging.error(err_mess) 229 230 # Sample info ################### 231 entry = get_content('ns:SASsample', dom) 232 if entry is not None: 233 data_info.sample.name = entry.get('name') 234 235 self._store_content('ns:SASsample/ns:ID', 236 dom, 'ID', data_info.sample) 237 self._store_float('ns:SASsample/ns:thickness', 238 dom, 'thickness', data_info.sample) 239 self._store_float('ns:SASsample/ns:transmission', 240 dom, 'transmission', data_info.sample) 241 self._store_float('ns:SASsample/ns:temperature', 242 dom, 'temperature', data_info.sample) 243 244 nodes = dom.xpath('ns:SASsample/ns:details', 245 namespaces={'ns': CANSAS_NS}) 246 for item in nodes: 268 if isinstance(node_value, float) is False: 269 exec("node_value = float({0})".format(node_value)) 270 default_unit = None 271 unitname = new_current_level.get("unit") 272 exec "default_unit = data1D.{0}.lower()".format(unitname) 273 local_unit = attr['unit'].lower() 274 if local_unit != default_unit: 275 if HAS_CONVERTER == True: 276 try: 277 data_conv_q = Converter(attr['unit']) 278 value_unit = default_unit 279 exec "node_value = data_conv_q(node_value, units=data1D.{0})".format(unitname) 280 except: 281 msg = "CanSAS reader: could not convert " 282 msg += "Q unit [%s]; " % attr['unit'], 283 exec "msg += \"expecting [%s]\n %s\" % (data1D.{0}, sys.exc_info()[1])".format(unitname) 284 raise ValueError, msg 285 else: 286 value_unit = local_unit 287 err_mess = "CanSAS reader: unrecognized %s unit [%s];"\ 288 % (node_value, default_unit) 289 err_mess += " expecting [%s]" % local_unit 290 self.errors.append(err_mess) 291 if optional: 292 logging.info(err_mess) 293 else: 294 raise ValueError, err_mess 295 except Exception as e: 296 msg = "CanSAS reader: could not convert " 297 msg += "Q unit [%s]; " % attr['unit'], 298 exec "msg += \"expecting [%s]\n %s\" % (data1D.{0}, sys.exc_info()[1])".format(unitname) 299 self.errors.append(msg) 300 raise ValueError, msg 301 elif 'unit' in attr: 302 value_unit = attr['unit'] 303 node_value = "float({0})".format(node_value) 304 return node_value, value_unit 305 306 def _parse_entry(self, dom, ns, data1D, extras = []): 307 """ 308 Parse a SASEntry - new recursive method for parsing the dom of 309 the CanSAS data format. This will allow multiple data files 310 and extra nodes to be read in simultaneously. 311 312 :param dom: dom object with a namespace base of ns 313 :param ns: A list of element names that lead up to the dom object 314 :param data1D: The data1D object that will be modified 315 """ 316 317 # A portion of every namespace entry 318 base_ns = "{0}{1}{2}".format("{", CANSAS_NS.get(self.cansasVersion).get("ns"), "}") 319 unit = '' 320 321 # Go through each child in the parent element 322 for node in dom: 247 323 try: 248 if item.text is not None: 249 detail_value = item.text.strip() 250 if len(detail_value) > 0: 251 data_info.sample.details.append(detail_value) 252 except: 253 err_mess = "cansas_reader.read: error processing " 254 err_mess += " sample details\n %s" % sys.exc_value 255 self.errors.append(err_mess) 256 logging.error(err_mess) 257 258 # Position (as a vector) 259 self._store_float('ns:SASsample/ns:position/ns:x', 260 dom, 'position.x', data_info.sample) 261 self._store_float('ns:SASsample/ns:position/ns:y', 262 dom, 'position.y', data_info.sample) 263 self._store_float('ns:SASsample/ns:position/ns:z', 264 dom, 'position.z', data_info.sample) 265 266 # Orientation (as a vector) 267 self._store_float('ns:SASsample/ns:orientation/ns:roll', 268 dom, 'orientation.x', data_info.sample) 269 self._store_float('ns:SASsample/ns:orientation/ns:pitch', 270 dom, 'orientation.y', data_info.sample) 271 self._store_float('ns:SASsample/ns:orientation/ns:yaw', 272 dom, 'orientation.z', data_info.sample) 273 274 # Source info ################### 275 entry = get_content('ns:SASinstrument/ns:SASsource', dom) 276 if entry is not None: 277 data_info.source.name = entry.get('name') 278 279 self._store_content('ns:SASinstrument/ns:SASsource/ns:radiation', 280 dom, 'radiation', data_info.source) 281 self._store_content('ns:SASinstrument/ns:SASsource/ns:beam_shape', 282 dom, 'beam_shape', data_info.source) 283 self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength', 284 dom, 'wavelength', data_info.source) 285 self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_min', 286 dom, 'wavelength_min', data_info.source) 287 self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_max', 288 dom, 'wavelength_max', data_info.source) 289 self._store_float('ns:SASinstrument/ns:SASsource/ns:wavelength_spread', 290 dom, 'wavelength_spread', data_info.source) 291 292 # Beam size (as a vector) 293 entry = get_content('ns:SASinstrument/ns:SASsource/ns:beam_size', dom) 294 if entry is not None: 295 data_info.source.beam_size_name = entry.get('name') 296 297 self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:x', 298 dom, 'beam_size.x', data_info.source) 299 self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:y', 300 dom, 'beam_size.y', data_info.source) 301 self._store_float('ns:SASinstrument/ns:SASsource/ns:beam_size/ns:z', 302 dom, 'beam_size.z', data_info.source) 303 304 # Collimation info ################### 305 nodes = dom.xpath('ns:SASinstrument/ns:SAScollimation', 306 namespaces={'ns': CANSAS_NS}) 307 for item in nodes: 308 collim = Collimation() 309 if item.get('name') is not None: 310 collim.name = item.get('name') 311 self._store_float('ns:length', item, 'length', collim) 312 313 # Look for apertures 314 apert_list = item.xpath('ns:aperture', namespaces={'ns': CANSAS_NS}) 315 for apert in apert_list: 316 aperture = Aperture() 317 318 # Get the name and type of the aperture 319 aperture.name = apert.get('name') 320 aperture.type = apert.get('type') 324 # Get the element name and set the current ns level 325 tagname = node.tag.replace(base_ns, "") 326 tagname_original = tagname 327 ns.append(tagname) 328 attr = node.attrib 329 330 # Look for special cases 331 save_data1D = data1D 332 if tagname == "SASdetector": 333 data1D = Detector() 334 elif tagname == "SAScollimation": 335 data1D = Collimation() 336 elif tagname == "SASprocess": 337 data1D = Process() 338 for child in node: 339 if child.tag.replace(base_ns, "") == "term": 340 term_attr = {} 341 for attr in child.keys(): 342 term_attr[attr] = child.get(attr).strip() 343 if child.text is not None: 344 term_attr['value'] = child.text.strip() 345 data1D.term.append(term_attr) 346 elif tagname == "aperture": 347 data1D = Aperture() 348 349 # Get where to store content 350 new_current_level, ns_variable, ns_datatype, optional = self._iterate_namespace(ns) 351 # If the element is a child element, recurse 352 if node.getchildren() is not None: 353 # Returned value is new Data1D object with all previous and new values in it. 354 data1D, extras = self._parse_entry(node, ns, data1D, extras) 321 355 322 self._store_float('ns:distance', apert, 'distance', aperture) 323 324 entry = get_content('ns:size', apert) 325 if entry is not None: 326 aperture.size_name = entry.get('name') 327 328 self._store_float('ns:size/ns:x', apert, 'size.x', aperture) 329 self._store_float('ns:size/ns:y', apert, 'size.y', aperture) 330 self._store_float('ns:size/ns:z', apert, 'size.z', aperture) 331 332 collim.aperture.append(aperture) 333 334 data_info.collimation.append(collim) 335 336 # Detector info ###################### 337 nodes = dom.xpath('ns:SASinstrument/ns:SASdetector', 338 namespaces={'ns': CANSAS_NS}) 339 for item in nodes: 340 341 detector = Detector() 342 343 self._store_content('ns:name', item, 'name', detector) 344 self._store_float('ns:SDD', item, 'distance', detector) 345 346 # Detector offset (as a vector) 347 self._store_float('ns:offset/ns:x', item, 'offset.x', detector) 348 self._store_float('ns:offset/ns:y', item, 'offset.y', detector) 349 self._store_float('ns:offset/ns:z', item, 'offset.z', detector) 350 351 # Detector orientation (as a vector) 352 self._store_float('ns:orientation/ns:roll', item, 'orientation.x', 353 detector) 354 self._store_float('ns:orientation/ns:pitch', item, 'orientation.y', 355 detector) 356 self._store_float('ns:orientation/ns:yaw', item, 'orientation.z', 357 detector) 358 359 # Beam center (as a vector) 360 self._store_float('ns:beam_center/ns:x', item, 'beam_center.x', 361 detector) 362 self._store_float('ns:beam_center/ns:y', item, 'beam_center.y', 363 detector) 364 self._store_float('ns:beam_center/ns:z', item, 'beam_center.z', 365 detector) 366 367 # Pixel size (as a vector) 368 self._store_float('ns:pixel_size/ns:x', item, 'pixel_size.x', 369 detector) 370 self._store_float('ns:pixel_size/ns:y', item, 'pixel_size.y', 371 detector) 372 self._store_float('ns:pixel_size/ns:z', item, 'pixel_size.z', 373 detector) 374 375 self._store_float('ns:slit_length', item, 'slit_length', detector) 376 377 data_info.detector.append(detector) 378 379 # Processes info ###################### 380 nodes = dom.xpath('ns:SASprocess', namespaces={'ns': CANSAS_NS}) 381 for item in nodes: 382 process = Process() 383 self._store_content('ns:name', item, 'name', process) 384 self._store_content('ns:date', item, 'date', process) 385 self._store_content('ns:description', item, 'description', process) 386 387 term_list = item.xpath('ns:term', namespaces={'ns': CANSAS_NS}) 388 for term in term_list: 389 try: 390 term_attr = {} 391 for attr in term.keys(): 392 term_attr[attr] = term.get(attr).strip() 393 if term.text is not None: 394 term_attr['value'] = term.text.strip() 395 process.term.append(term_attr) 396 except: 397 err_mess = "cansas_reader.read: error processing " 398 err_mess += " process term\n %s" % sys.exc_value 399 self.errors.append(err_mess) 400 logging.error(err_mess) 401 402 note_list = item.xpath('ns:SASprocessnote', 403 namespaces={'ns': CANSAS_NS}) 404 for note in note_list: 405 if note.text is not None: 406 process.notes.append(note.text.strip()) 407 408 data_info.process.append(process) 409 410 # Data info ###################### 411 nodes = dom.xpath('ns:SASdata', namespaces={'ns': CANSAS_NS}) 412 if len(nodes) > 1: 413 msg = "CanSAS reader is not compatible with multiple" 414 msg += " SASdata entries" 415 raise RuntimeError, msg 416 417 nodes = dom.xpath('ns:SASdata/ns:Idata', namespaces={'ns': CANSAS_NS}) 418 419 x = numpy.zeros(0) 420 y = numpy.zeros(0) 421 dx = numpy.zeros(0) 422 dy = numpy.zeros(0) 423 dxw = numpy.zeros(0) 424 dxl = numpy.zeros(0) 425 426 for item in nodes: 427 _x, attr = get_float('ns:Q', item) 428 _dx, attr_d = get_float('ns:Qdev', item) 429 _dxl, attr_l = get_float('ns:dQl', item) 430 _dxw, attr_w = get_float('ns:dQw', item) 431 if _dx == None: 432 _dx = 0.0 433 if _dxl == None: 434 _dxl = 0.0 435 if _dxw == None: 436 _dxw = 0.0 437 438 if 'unit' in attr and \ 439 attr['unit'].lower() != data_info.x_unit.lower(): 440 if HAS_CONVERTER == True: 441 try: 442 data_conv_q = Converter(attr['unit']) 443 _x = data_conv_q(_x, units=data_info.x_unit) 444 except: 445 msg = "CanSAS reader: could not convert " 446 msg += "Q unit [%s]; " % attr['unit'], 447 msg += "expecting [%s]\n %s" % (data_info.x_unit, 448 sys.exc_value) 449 raise ValueError, msg 450 356 #Get the information from the node 357 node_value = node.text 358 if node_value is not None: 359 node_value = node_value.strip().replace("\r"," ").replace("\n"," ") 360 if node_value == "": 361 node_value = None 362 363 # If the value is a float, compile with units. 364 if ns_datatype == "float": 365 # If an empty value is given, store as zero. 366 if node_value is None: 367 node_value = "0.0" 368 elif node_value.isspace(): 369 node_value = "0.0" 370 node_value, unit = self._unit_conversion(new_current_level, attr, data1D, node_value, optional) 371 372 # If appending to a dictionary (meta_data | run_name), name sure the key is unique 373 if ns_variable == "{0}.meta_data[\"{2}\"] = \"{1}\"": 374 # If we are within a Process, Detector, Collimation or Aperture instance, pull out old data1D 375 tagname = self._create_unique_key(data1D.meta_data, tagname, 0) 376 if isinstance(data1D, Data1D) == False: 377 store_me = ns_variable.format("data1D", node_value, tagname) 378 extras.append(store_me) 379 ns_variable = None 380 if ns_variable == "{0}.run_name[\"{2}\"] = \"{1}\"": 381 tagname = self._create_unique_key(data1D.run_name, tagname, 0) 382 383 # Check for Data1D object and any extra commands to save 384 if isinstance(data1D, Data1D): 385 for item in extras: 386 exec item 387 # Don't bother saving empty information unless it is a float 388 if ns_variable is not None and node_value is not None and node_value.isspace() == False: 389 # Format a string and then execute it. 390 store_me = ns_variable.format("data1D", node_value, tagname) 391 exec store_me 392 # Get attributes and process them 393 if attr is not None: 394 for key in node.keys(): 395 try: 396 cansas_attrib = new_current_level.get("attributes").get(key) 397 attrib_variable = cansas_attrib.get("variable") 398 if key == 'unit' and unit != '': 399 attrib_value = unit 400 else: 401 attrib_value = node.attrib[key] 402 store_attr = attrib_variable.format("data1D", attrib_value, key) 403 exec store_attr 404 except AttributeError as e: 405 pass 406 407 408 except Exception as e: 409 exc_type, exc_obj, exc_tb = sys.exc_info() 410 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 411 print(e, exc_type, fname, exc_tb.tb_lineno, tagname, exc_obj) 412 finally: 413 # Save special cases in original data1D object and then restore the data1D 414 if tagname_original == "SASdetector": 415 save_data1D.detector.append(data1D) 416 elif tagname_original == "SAScollimation": 417 save_data1D.collimation.append(data1D) 418 elif tagname_original == "SASprocess": 419 save_data1D.process.append(data1D) 420 elif tagname_original == "aperture": 421 save_data1D.aperture.append(data1D) 451 422 else: 452 msg = "CanSAS reader: unrecognized Q unit [%s]; "\ 453 % attr['unit'] 454 msg += "expecting [%s]" % data_info.x_unit 455 raise ValueError, msg 456 457 # Error in Q 458 if 'unit' in attr_d and \ 459 attr_d['unit'].lower() != data_info.x_unit.lower(): 460 if HAS_CONVERTER == True: 461 try: 462 data_conv_q = Converter(attr_d['unit']) 463 _dx = data_conv_q(_dx, units=data_info.x_unit) 464 except: 465 msg = "CanSAS reader: could not convert dQ unit [%s]; "\ 466 % attr['unit'] 467 msg += " expecting " 468 msg += "[%s]\n %s" % (data_info.x_unit, sys.exc_value) 469 raise ValueError, msg 470 471 else: 472 msg = "CanSAS reader: unrecognized dQ unit [%s]; "\ 473 % attr['unit'] 474 msg += "expecting [%s]" % data_info.x_unit 475 raise ValueError, msg 476 477 # Slit length 478 if 'unit' in attr_l and \ 479 attr_l['unit'].lower() != data_info.x_unit.lower(): 480 if HAS_CONVERTER == True: 481 try: 482 data_conv_q = Converter(attr_l['unit']) 483 _dxl = data_conv_q(_dxl, units=data_info.x_unit) 484 except: 485 msg = "CanSAS reader: could not convert dQl unit [%s];"\ 486 % attr['unit'] 487 msg += " expecting [%s]\n %s" % (data_info.x_unit, 488 sys.exc_value) 489 raise ValueError, msg 490 else: 491 msg = "CanSAS reader: unrecognized dQl unit [%s];"\ 492 % attr['unit'] 493 msg += " expecting [%s]" % data_info.x_unit 494 raise ValueError, msg 495 496 # Slit width 497 if 'unit' in attr_w and \ 498 attr_w['unit'].lower() != data_info.x_unit.lower(): 499 if HAS_CONVERTER == True: 500 try: 501 data_conv_q = Converter(attr_w['unit']) 502 _dxw = data_conv_q(_dxw, units=data_info.x_unit) 503 except: 504 msg = "CanSAS reader: could not convert dQw unit [%s];"\ 505 % attr['unit'] 506 msg += " expecting [%s]\n %s" % (data_info.x_unit, 507 sys.exc_value) 508 raise ValueError, msg 509 510 else: 511 msg = "CanSAS reader: unrecognized dQw unit [%s];"\ 512 % attr['unit'] 513 msg += " expecting [%s]" % data_info.x_unit 514 raise ValueError, msg 515 _y, attr = get_float('ns:I', item) 516 _dy, attr_d = get_float('ns:Idev', item) 517 if _dy == None: 518 _dy = 0.0 519 if 'unit' in attr and \ 520 attr['unit'].lower() != data_info.y_unit.lower(): 521 if HAS_CONVERTER == True: 522 try: 523 data_conv_i = Converter(attr['unit']) 524 _y = data_conv_i(_y, units=data_info.y_unit) 525 except: 526 if attr['unit'].lower() == 'count': 527 pass 528 else: 529 msg = "CanSAS reader: could not" 530 msg += " convert I(q) unit [%s];" % str(attr['unit']) 531 msg += " expecting [%s]\n" % str(data_info.y_unit) 532 msg += " %s" % str(sys.exc_value) 533 raise ValueError, msg 534 else: 535 msg = "CanSAS reader: unrecognized I(q) unit [%s];"\ 536 % attr['unit'] 537 msg += " expecting [%s]" % data_info.y_unit 538 raise ValueError, msg 539 540 if 'unit' in attr_d and \ 541 attr_d['unit'].lower() != data_info.y_unit.lower(): 542 if HAS_CONVERTER == True: 543 try: 544 data_conv_i = Converter(attr_d['unit']) 545 _dy = data_conv_i(_dy, units=data_info.y_unit) 546 except: 547 if attr_d['unit'].lower() == 'count': 548 pass 549 else: 550 msg = "CanSAS reader: could not convert dI(q) unit " 551 msg += "[%s]; expecting [%s]\n %s" % (attr_d['unit'], 552 data_info.y_unit, sys.exc_value) 553 raise ValueError, msg 554 else: 555 msg = "CanSAS reader: unrecognized dI(q) unit [%s]; "\ 556 % attr_d['unit'] 557 msg += "expecting [%s]" % data_info.y_unit 558 raise ValueError, msg 559 560 if _x is not None and _y is not None: 561 x = numpy.append(x, _x) 562 y = numpy.append(y, _y) 563 dx = numpy.append(dx, _dx) 564 dy = numpy.append(dy, _dy) 565 dxl = numpy.append(dxl, _dxl) 566 dxw = numpy.append(dxw, _dxw) 567 # Zeros in dx, dy 568 if not numpy.all(dx == 0): 569 dx[dx == 0] = _ZERO 570 if not numpy.all(dy == 0): 571 dy[dy == 0] = _ZERO 572 573 data_info.x = x[x != 0] 574 data_info.y = y[x != 0] 575 data_info.dx = dx[x != 0] 576 577 data_info.dy = dy[x != 0] 578 data_info.dxl = dxl[x != 0] 579 data_info.dxw = dxw[x != 0] 580 581 data_conv_q = None 582 data_conv_i = None 583 584 if HAS_CONVERTER == True and data_info.x_unit != '1/A': 585 data_conv_q = Converter('1/A') 586 # Test it 587 data_conv_q(1.0, data_info.x_unit) 588 589 if HAS_CONVERTER == True and data_info.y_unit != '1/cm': 590 data_conv_i = Converter('1/cm') 591 # Test it 592 data_conv_i(1.0, data_info.y_unit) 593 594 if data_conv_q is not None: 595 data_info.xaxis("\\rm{Q}", data_info.x_unit) 596 else: 597 data_info.xaxis("\\rm{Q}", 'A^{-1}') 598 if data_conv_i is not None: 599 data_info.yaxis("\\rm{Intensity}", data_info.y_unit) 600 else: 601 data_info.yaxis("\\rm{Intensity}", "cm^{-1}") 602 603 return data_info 604 423 save_data1D = data1D 424 data1D = save_data1D 425 # Remove tagname from ns to restore original base 426 ns.remove(tagname_original) 427 428 return data1D, extras 429 605 430 def _to_xml_doc(self, datainfo): 606 431 """ … … 615 440 doc = xml.dom.minidom.Document() 616 441 main_node = doc.createElement("SASroot") 617 main_node.setAttribute(" version", self.version)618 main_node.setAttribute("xmlns", "cansas1d/%s" % self. version)442 main_node.setAttribute("cansasVersion", self.cansasVersion) 443 main_node.setAttribute("xmlns", "cansas1d/%s" % self.cansasVersion) 619 444 main_node.setAttribute("xmlns:xsi", 620 445 "http://www.w3.org/2001/XMLSchema-instance") 621 446 main_node.setAttribute("xsi:schemaLocation", 622 "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self. version)447 "cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.cansasVersion) 623 448 624 449 doc.appendChild(main_node) … … 861 686 fd.write(doc.toprettyxml()) 862 687 fd.close() 863 864 def _store_float(self, location, node, variable, storage, optional=True):865 """866 Get the content of a xpath location and store867 the result. Check that the units are compatible868 with the destination. The value is expected to869 be a float.870 871 The xpath location might or might not exist.872 If it does not exist, nothing is done873 874 :param location: xpath location to fetch875 :param node: node to read the data from876 :param variable: name of the data member to store it in [string]877 :param storage: data object that has the 'variable' data member878 :param optional: if True, no exception will be raised879 if unit conversion can't be done880 881 :raise ValueError: raised when the units are not recognized882 """883 entry = get_content(location, node)884 try:885 value = float(entry.text)886 except:887 value = None888 889 if value is not None:890 # If the entry has units, check to see that they are891 # compatible with what we currently have in the data object892 units = entry.get('unit')893 if units is not None:894 toks = variable.split('.')895 local_unit = None896 exec "local_unit = storage.%s_unit" % toks[0]897 if local_unit != None and units.lower() != local_unit.lower():898 if HAS_CONVERTER == True:899 try:900 conv = Converter(units)901 exec "storage.%s = %g" % (variable,902 conv(value, units=local_unit))903 except:904 err_mess = "CanSAS reader: could not convert"905 err_mess += " %s unit [%s]; expecting [%s]\n %s" \906 % (variable, units, local_unit, sys.exc_value)907 self.errors.append(err_mess)908 if optional:909 logging.info(err_mess)910 else:911 raise ValueError, err_mess912 else:913 err_mess = "CanSAS reader: unrecognized %s unit [%s];"\914 % (variable, units)915 err_mess += " expecting [%s]" % local_unit916 self.errors.append(err_mess)917 if optional:918 logging.info(err_mess)919 else:920 raise ValueError, err_mess921 else:922 exec "storage.%s = value" % variable923 else:924 exec "storage.%s = value" % variable925 926 def _store_content(self, location, node, variable, storage):927 """928 Get the content of a xpath location and store929 the result. The value is treated as a string.930 931 The xpath location might or might not exist.932 If it does not exist, nothing is done933 934 :param location: xpath location to fetch935 :param node: node to read the data from936 :param variable: name of the data member to store it in [string]937 :param storage: data object that has the 'variable' data member938 939 :return: return a list of errors940 """941 entry = get_content(location, node)942 if entry is not None and entry.text is not None:943 exec "storage.%s = entry.text.strip()" % variable
Note: See TracChangeset
for help on using the changeset viewer.