Changeset 19b628f in sasview for src/sans/dataloader
- Timestamp:
- Dec 27, 2013 2:34:16 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:
- 17a25d4
- Parents:
- c614c02
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
src/sans/dataloader/readers/cansas_reader.py
rffbe487 r19b628f 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 CanS asreader requires PyXML 0.8.4 or later.117 The CanSAS reader requires PyXML 0.8.4 or later. 118 118 """ 119 ## CanSAS version 120 version = '1.0' 121 ## File type 122 type_name = "CanSAS 1D" 119 ##CanSAS version - defaults to version 1.0 120 cansasVersion = "1.0" 121 ##Data reader 122 reader = xml_reader.XMLreader() 123 errors = [] 124 125 type_name = "canSAS" 126 123 127 ## Wildcards 124 type = ["CanSAS 1D files (*.xml)|*.xml", 125 "CanSAS 1D AVE files (*.AVEx)|*.AVEx", 126 "CanSAS 1D AVE files (*.ABSx)|*.ABSx"] 127 128 type = ["XML files (*.xml)|*.xml"] 128 129 ## List of allowed extensions 129 ext = ['.xml', '.XML', '.avex', '.AVEx', '.absx', 'ABSx'] 130 ext = ['.xml', '.XML'] 131 132 ## Flag to bypass extension check 133 allow_all = True 130 134 131 135 def __init__(self): 132 136 ## List of errors 133 137 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 138 139 def isCansas(self): 140 """ 141 Checks to see if the xml file is a CanSAS file 142 """ 143 if self.reader.validateXML(): 144 xmlns = self.reader.xmlroot.keys() 145 if (CANSAS_NS.get(self.cansasVersion).get("ns") == self.reader.xmlroot.get(xmlns[1]).rsplit(" ")[0]): 146 return True 147 return False 148 149 def read(self, xml): 150 """ 151 Validate and read in an xml file in the canSAS format. 152 153 :param xml: A canSAS file path in proper XML format 154 """ 155 # X - Q value; Y - Intensity (Abs) 156 x = numpy.empty(0) 157 y = numpy.empty(0) 158 dx = numpy.empty(0) 159 dy = numpy.empty(0) 160 dxl = numpy.empty(0) 161 dxw = numpy.empty(0) 162 163 # output - Final list of Data1D objects 164 output = [] 165 # ns - Namespace hierarchy for current xml object 166 ns = [] 167 168 # Check that the file exists 169 if os.path.isfile(xml): 170 basename = os.path.basename(xml) 171 _, extension = os.path.splitext(basename) 172 # If the fiel type is not allowed, return nothing 173 if extension in self.ext or self.allow_all: 174 base_name = xml_reader.__file__ 175 base = base_name.split("\\sans\\")[0] 176 177 # Load in the xml file and get the cansas version from the header 178 self.reader.setXMLFile(xml) 179 root = self.reader.xmlroot 180 if root is None: 181 root = {} 182 self.cansasVersion = root.get("version", "1.0") 183 184 # Generic values for the cansas file based on the version 185 cansas_defaults = CANSAS_NS.get(self.cansasVersion, "1.0") 186 schema_path = "{0}\\sans\\dataloader\\readers\\schema\\{1}".format(base, cansas_defaults.get("schema")).replace("\\", "/") 187 188 # Link a schema to the XML file. 189 self.reader.setSchema(schema_path) 190 191 # Try to load the file, but raise an error if unable to. 192 # Check the file matches the XML schema 193 try: 194 if self.isCansas(): 195 # Get each SASentry from the XML file and add it to a list. 196 entry_list = root.xpath('/ns:SASroot/ns:SASentry', 197 namespaces={'ns': cansas_defaults.get("ns")}) 198 ns.append("SASentry") 199 200 # If there are multiple files, modify the name for each is unique 201 multipleFiles = len(entry_list) - 1 202 n = 0 203 name = basename 204 # Parse each SASentry item 205 for entry in entry_list: 206 207 # Define a new Data1D object with zeroes for x and y 208 data1D = Data1D(x,y,dx,dy) 209 data1D.dxl = dxl 210 data1D.dxw = dxw 211 212 # If more than one SASentry, number each in order 213 if multipleFiles: 214 name += "_{0}".format(n) 215 n += 1 216 217 # Set the Data1D name and then parse the entry. The entry is appended to a list of entry values 218 data1D.filename = name 219 data1D.meta_data["loader"] = "CanSAS 1D" 220 return_value, extras = self._parse_entry(entry, ns, data1D) 221 del extras[:] 222 223 #Final cleanup - Remove empty nodes, verify array sizes are correct 224 for error in self.errors: 225 return_value.errors.append(error) 226 del self.errors[:] 227 numpy.trim_zeros(return_value.x) 228 numpy.trim_zeros(return_value.y) 229 numpy.trim_zeros(return_value.dy) 230 size_dx = return_value.dx.size 231 size_dxl = return_value.dxl.size 232 size_dxw = return_value.dxw.size 233 if size_dxl == 0 and size_dxw == 0: 234 return_value.dxl = None 235 return_value.dxw = None 236 numpy.trim_zeros(return_value.dx) 237 elif size_dx == 0: 238 return_value.dx = None 239 size_dx = size_dxl 240 numpy.trim_zeros(return_value.dxl) 241 numpy.trim_zeros(return_value.dxw) 242 243 output.append(return_value) 244 else: 245 value = self.reader.findInvalidXML() 246 output.append("Invalid XML at: {0}".format(value)) 247 except: 248 # If the file does not match the schema, raise this error 249 raise RuntimeError, "%s cannot be read \n" % xml 250 return output 251 # Return a list of parsed entries that dataloader can manage 252 return None 253 254 def _create_unique_key(self, dictionary, name, i): 255 if dictionary.get(name) is not None: 256 i += 1 257 name = name.split("_")[0] 258 name += "_{0}".format(i) 259 name = self._create_unique_key(dictionary, name, i) 260 return name 261 262 def _iterate_namespace(self, ns): 263 # The current level to look through in cansas_constants. 264 current_level = CANSAS_FORMAT.get("SASentry") 265 # Defaults for variable and datatype 266 ns_variable = "{0}.meta_data[\"{2}\"] = \"{1}\"" 267 ns_datatype = "content" 268 ns_optional = True 269 for name in ns: 270 if name != "SASentry": 271 current_level = current_level.get("children").get(name, "") 272 if current_level == "": 273 current_level = current_level.get("<any>", "") 274 cl_variable = current_level.get("variable", "") 275 cl_datatype = current_level.get("storeas", "") 276 cl_units_optional = current_level.get("units_required", "") 277 # Where are how to store the variable for the given namespace 278 # The CANSAS_CONSTANTS tree is hierarchical, so is no value, inherit 279 ns_variable = cl_variable if cl_variable != "" else ns_variable 280 ns_datatype = cl_datatype if cl_datatype != "" else ns_datatype 281 ns_optional = cl_units_optional if cl_units_optional != ns_optional else ns_optional 282 return current_level, ns_variable, ns_datatype, ns_optional 283 284 def _unit_conversion(self, new_current_level, attr, data1D, node_value, optional = True): 285 value_unit = '' 286 if 'unit' in attr and new_current_level.get('unit') is not None: 287 try: 288 if isinstance(node_value, float) is False: 289 exec("node_value = float({0})".format(node_value)) 290 default_unit = None 291 unitname = new_current_level.get("unit") 292 exec "default_unit = data1D.{0}".format(unitname) 293 local_unit = attr['unit'] 294 if local_unit.lower() != default_unit.lower() and local_unit is not None\ 295 and local_unit.lower() != "none" and default_unit is not None: 296 if HAS_CONVERTER == True: 297 try: 298 data_conv_q = Converter(attr['unit']) 299 value_unit = default_unit 300 exec "node_value = data_conv_q(node_value, units=data1D.{0})".format(unitname) 301 except: 302 err_msg = "CanSAS reader: could not convert " 303 err_msg += "Q unit {0}; ".format(local_unit) 304 intermediate = "err_msg += \"expecting [{1}] {2}\".format(data1D.{0}, sys.exc_info()[1])".format(unitname, "{0}", "{1}") 305 exec intermediate 306 self.errors.append(err_msg) 307 if optional: 308 logging.info(err_msg) 309 else: 310 raise ValueError, err_msg 311 else: 312 value_unit = local_unit 313 err_msg = "CanSAS reader: unrecognized %s unit [%s];"\ 314 % (node_value, default_unit) 315 err_msg += " expecting [%s]" % local_unit 316 self.errors.append(err_msg) 317 if optional: 318 logging.info(err_msg) 319 else: 320 raise ValueError, err_msg 321 else: 322 value_unit = local_unit 323 except: 324 err_msg = "CanSAS reader: could not convert " 325 err_msg += "Q unit [%s]; " % attr['unit'], 326 exec "err_msg += \"expecting [%s]\n %s\" % (data1D.{0}, sys.exc_info()[1])".format(unitname) 327 self.errors.append(err_msg) 328 if optional: 329 logging.info(err_msg) 330 else: 331 raise ValueError, err_msg 332 elif 'unit' in attr: 333 value_unit = attr['unit'] 334 node_value = "float({0})".format(node_value) 335 return node_value, value_unit 336 337 def _parse_entry(self, dom, ns, data1D, extras = []): 338 """ 339 Parse a SASEntry - new recursive method for parsing the dom of 340 the CanSAS data format. This will allow multiple data files 341 and extra nodes to be read in simultaneously. 342 343 :param dom: dom object with a namespace base of ns 344 :param ns: A list of element names that lead up to the dom object 345 :param data1D: The data1D object that will be modified 346 """ 347 348 # A portion of every namespace entry 349 base_ns = "{0}{1}{2}".format("{", CANSAS_NS.get(self.cansasVersion).get("ns"), "}") 350 unit = '' 351 352 # Go through each child in the parent element 353 for node in dom: 354 try: 355 # Get the element name and set the current ns level 356 tagname = node.tag.replace(base_ns, "") 357 tagname_original = tagname 358 ns.append(tagname) 359 attr = node.attrib 360 361 # Look for special cases 362 save_data1D = data1D 363 if tagname == "SASdetector": 364 data1D = Detector() 365 elif tagname == "SAScollimation": 366 data1D = Collimation() 367 elif tagname == "SASprocess": 368 data1D = Process() 369 for child in node: 370 if child.tag.replace(base_ns, "") == "term": 371 term_attr = {} 372 for attr in child.keys(): 373 term_attr[attr] = ' '.join(child.get(attr).split()) 374 if child.text is not None: 375 term_attr['value'] = ' '.join(child.text.split()) 376 data1D.term.append(term_attr) 377 elif tagname == "aperture": 378 data1D = Aperture() 379 380 # Get where to store content 381 new_current_level, ns_variable, ns_datatype, optional = self._iterate_namespace(ns) 382 # If the element is a child element, recurse 383 if node.getchildren() is not None: 384 # Returned value is new Data1D object with all previous and new values in it. 385 data1D, extras = self._parse_entry(node, ns, data1D, extras) 144 386 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() 387 #Get the information from the node 388 node_value = node.text 389 if node_value == "": 390 node_value = None 391 if node_value is not None: 392 node_value = ' '.join(node_value.split()) 393 394 # If the value is a float, compile with units. 395 if ns_datatype == "float": 396 # If an empty value is given, store as zero. 397 if node_value is None or node_value.isspace() or node_value.lower() == "nan": 398 node_value = "0.0" 399 node_value, unit = self._unit_conversion(new_current_level, attr, data1D, node_value, optional) 159 400 160 entry_list = root.xpath('/ns:SASroot/ns:SASentry', 161 namespaces={'ns': CANSAS_NS}) 162 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: 219 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: 247 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') 321 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 401 # If appending to a dictionary (meta_data | run_name), name sure the key is unique 402 if ns_variable == "{0}.meta_data[\"{2}\"] = \"{1}\"": 403 # If we are within a Process, Detector, Collimation or Aperture instance, pull out old data1D 404 tagname = self._create_unique_key(data1D.meta_data, tagname, 0) 405 if isinstance(data1D, Data1D) == False: 406 store_me = ns_variable.format("data1D", node_value, tagname) 407 extras.append(store_me) 408 ns_variable = None 409 if ns_variable == "{0}.run_name[\"{2}\"] = \"{1}\"": 410 tagname = self._create_unique_key(data1D.run_name, tagname, 0) 411 412 # Check for Data1D object and any extra commands to save 413 if isinstance(data1D, Data1D): 414 for item in extras: 415 exec item 416 # Don't bother saving empty information unless it is a float 417 if ns_variable is not None and node_value is not None and node_value.isspace() == False: 418 # Format a string and then execute it. 419 store_me = ns_variable.format("data1D", node_value, tagname) 420 exec store_me 421 # Get attributes and process them 422 if attr is not None: 423 for key in node.keys(): 424 try: 425 cansas_attrib = new_current_level.get("attributes").get(key) 426 attrib_variable = cansas_attrib.get("variable") 427 if key == 'unit' and unit != '': 428 attrib_value = unit 429 else: 430 attrib_value = node.attrib[key] 431 store_attr = attrib_variable.format("data1D", attrib_value, key) 432 exec store_attr 433 except AttributeError as e: 434 pass 435 436 437 except Exception as e: 438 exc_type, exc_obj, exc_tb = sys.exc_info() 439 fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] 440 print(e, exc_type, fname, exc_tb.tb_lineno, tagname, exc_obj) 441 finally: 442 # Save special cases in original data1D object and then restore the data1D 443 if tagname_original == "SASdetector": 444 save_data1D.detector.append(data1D) 445 elif tagname_original == "SAScollimation": 446 save_data1D.collimation.append(data1D) 447 elif tagname_original == "SASprocess": 448 save_data1D.process.append(data1D) 449 elif tagname_original == "aperture": 450 save_data1D.aperture.append(data1D) 451 451 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 452 save_data1D = data1D 453 data1D = save_data1D 454 # Remove tagname from ns to restore original base 455 ns.remove(tagname_original) 456 457 return data1D, extras 458 605 459 def _to_xml_doc(self, datainfo): 606 460 """ … … 613 467 raise RuntimeError, "The cansas writer expects a Data1D instance" 614 468 469 ns = CANSAS_NS.get(self.cansasVersion).get("ns") 615 470 doc = xml.dom.minidom.Document() 616 471 main_node = doc.createElement("SASroot") 617 main_node.setAttribute("version", self. version)618 main_node.setAttribute("xmlns", "cansas1d/%s" % self.version)472 main_node.setAttribute("version", self.cansasVersion) 473 main_node.setAttribute("xmlns", ns) 619 474 main_node.setAttribute("xmlns:xsi", 620 475 "http://www.w3.org/2001/XMLSchema-instance") 621 476 main_node.setAttribute("xsi:schemaLocation", 622 " cansas1d/%s http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd" % self.version)477 "{0} http://svn.smallangles.net/svn/canSAS/1dwg/trunk/cansas1d.xsd".format(ns)) 623 478 624 479 doc.appendChild(main_node) … … 646 501 write_node(doc, pt, "I", datainfo.y[i], 647 502 {'unit': datainfo.y_unit}) 503 if datainfo.dy != None and len(datainfo.dy) >= i: 504 write_node(doc, pt, "Idev", datainfo.dy[i], 505 {'unit': datainfo.y_unit}) 648 506 if datainfo.dx != None and len(datainfo.dx) >= i: 649 507 write_node(doc, pt, "Qdev", datainfo.dx[i], 508 {'unit': datainfo.x_unit}) 509 if datainfo.dxw != None and len(datainfo.dxw) >= i: 510 write_node(doc, pt, "dQw", datainfo.dxw[i], 650 511 {'unit': datainfo.x_unit}) 651 512 if datainfo.dxl != None and len(datainfo.dxl) >= i: 652 513 write_node(doc, pt, "dQl", datainfo.dxl[i], 653 514 {'unit': datainfo.x_unit}) 654 if datainfo.dxw != None and len(datainfo.dxw) >= i: 655 write_node(doc, pt, "dQw", datainfo.dxw[i], 656 {'unit': datainfo.x_unit}) 657 if datainfo.dy != None and len(datainfo.dy) >= i: 658 write_node(doc, pt, "Idev", datainfo.dy[i], 659 {'unit': datainfo.y_unit}) 515 516 # Transmission Spectrum Info 517 if len(datainfo.trans_spectrum.wavelength) > 0: 518 node = doc.createElement("SAStransmission_spectrum") 519 entry_node.appendChild(node) 520 for i in range(len(datainfo.trans_spectrum.wavelength)): 521 pt = doc.createElement("Tdata") 522 node.appendChild(pt) 523 write_node(doc, pt, "Lambda", datainfo.trans_spectrum.wavelength[i], 524 {'unit': datainfo.trans_spectrum.wavelength_unit}) 525 write_node(doc, pt, "T", datainfo.trans_spectrum.transmission[i], 526 {'unit': datainfo.trans_spectrum.transmission_unit}) 527 if datainfo.trans_spectrum.transmission_deviation != None \ 528 and len(datainfo.trans_spectrum.transmission_deviation) >= i: 529 write_node(doc, pt, "Tdev", datainfo.trans_spectrum.transmission_deviation[i], 530 {'unit': datainfo.trans_spectrum.transmission_deviation_unit}) 660 531 661 532 # Sample info … … 670 541 write_node(doc, sample, "temperature", datainfo.sample.temperature, 671 542 {"unit": datainfo.sample.temperature_unit}) 672 673 for item in datainfo.sample.details:674 write_node(doc, sample, "details", item)675 543 676 544 pos = doc.createElement("position") … … 699 567 sample.appendChild(ori) 700 568 569 for item in datainfo.sample.details: 570 write_node(doc, sample, "details", item) 571 701 572 # Instrument info 702 573 instr = doc.createElement("SASinstrument") … … 710 581 source.setAttribute("name", str(datainfo.source.name)) 711 582 instr.appendChild(source) 712 713 583 write_node(doc, source, "radiation", datainfo.source.radiation) 714 write_node(doc, source, "beam_shape", datainfo.source.beam_shape)584 715 585 size = doc.createElement("beam_size") 716 586 if datainfo.source.beam_size_name is not None: … … 727 597 source.appendChild(size) 728 598 599 write_node(doc, source, "beam_shape", datainfo.source.beam_shape) 729 600 write_node(doc, source, "wavelength", 730 601 datainfo.source.wavelength, … … 758 629 coll.appendChild(ap) 759 630 760 write_node(doc, ap, "distance", apert.distance,761 {"unit": apert.distance_unit})762 763 631 size = doc.createElement("size") 764 632 if apert.size_name is not None: … … 772 640 if written == True: 773 641 ap.appendChild(size) 642 643 write_node(doc, ap, "distance", apert.distance, 644 {"unit": apert.distance_unit}) 774 645 775 646 # Detectors … … 779 650 written = written | write_node(doc, det, "SDD", item.distance, 780 651 {"unit": item.distance_unit}) 781 written = written | write_node(doc, det, "slit_length",782 item.slit_length,783 {"unit": item.slit_length_unit})784 652 if written == True: 785 653 instr.appendChild(det) … … 794 662 if written == True: 795 663 det.appendChild(off) 664 665 ori = doc.createElement("orientation") 666 written = write_node(doc, ori, "roll", item.orientation.x, 667 {"unit": item.orientation_unit}) 668 written = written | write_node(doc, ori, "pitch", 669 item.orientation.y, 670 {"unit": item.orientation_unit}) 671 written = written | write_node(doc, ori, "yaw", 672 item.orientation.z, 673 {"unit": item.orientation_unit}) 674 if written == True: 675 det.appendChild(ori) 796 676 797 677 center = doc.createElement("beam_center") … … 816 696 if written == True: 817 697 det.appendChild(pix) 818 819 ori = doc.createElement("orientation") 820 written = write_node(doc, ori, "roll", item.orientation.x, 821 {"unit": item.orientation_unit}) 822 written = written | write_node(doc, ori, "pitch", 823 item.orientation.y, 824 {"unit": item.orientation_unit}) 825 written = written | write_node(doc, ori, "yaw", 826 item.orientation.z, 827 {"unit": item.orientation_unit}) 828 if written == True: 829 det.appendChild(ori) 830 698 written = written | write_node(doc, det, "slit_length", 699 item.slit_length, 700 {"unit": item.slit_length_unit}) 701 831 702 # Processes info 832 703 for item in datainfo.process: … … 843 714 for note in item.notes: 844 715 write_node(doc, node, "SASprocessnote", note) 845 716 if len(item.notes) == 0: 717 write_node(doc, node, "SASprocessnote", "") 718 719 # Note info 720 if len(datainfo.notes) == 0: 721 node = doc.createElement("SASnote") 722 entry_node.appendChild(node) 723 if node.hasChildNodes(): 724 for child in node.childNodes: 725 node.removeChild(child) 726 else: 727 for item in datainfo.notes: 728 node = doc.createElement("SASnote") 729 entry_node.appendChild(node) 730 node.appendChild(doc.createTextNode(item)) 731 846 732 # Return the document, and the SASentry node associated with 847 733 # the data we just wrote … … 861 747 fd.write(doc.toprettyxml()) 862 748 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.