Package reflectometry :: Package reduction :: Module nexus

Source Code for Module reflectometry.reduction.nexus

  1  #!/usr/bin/env python 
  2  # This program is public domain 
  3   
  4  """ 
  5  High level interface to NeXus files. 
  6   
  7  Unlike the nxs routines which implement the NeXus API directly, the 
  8  nexus routines preload the entire file structure into memory and use 
  9  a natural syntax for navigating the data hierarchy.  Large datasets 
 10  are not read until they are needed, and may be read or written one 
 11  slab at a time. 
 12   
 13  There are a number of functions which operate on files:: 
 14   
 15    * tree = read(file)   loads a structure from a file 
 16    * write(file, tree)   saves a structure to a file 
 17    * dir(file)           display the contents of a file 
 18   
 19  The tree returned from read() has an entry for each group, field and 
 20  attribute.  You can traverse the hierarchy using the names of the 
 21  groups.  For example, tree.Histogram1.instrument.detector.distance 
 22  is a field containing the distance to each pixel in the detector. 
 23  NeXus attribute names are tagged with a leading 'A', so for example, 
 24  tree.Histogram1.instrument.detector.distance.Aunits contains the 
 25  units attribute for the detector distances. 
 26   
 27  Properties of the nodes in the tree are referenced by nx attributes. 
 28  Depending on the node type, different nx attributes may be available. 
 29   
 30  Nodes (class NXnode) have attributes shared by both groups and fields:: 
 31      * nxname     node name 
 32      * nxclass    node class for groups, 'SDS' for fields 
 33      * nxgroup    group containing the entry, or None for the root 
 34      * nxattrs    dictionary of NeXus attributes for the node 
 35   
 36  Groups (class NXgroup) have attributes for accessing children:: 
 37      * nxentries  dictionary of entries within the group 
 38      * nxcomponent('nxclass')  return group entries of a particular class 
 39      * nxdir()    print the list of entries in the group 
 40      * nxtree()   print the list of entries and subentries in the group 
 41      * nxplot()   plot signal and axes for the group, if available 
 42   
 43  Fields (class SDS) have attributes for accessing data: 
 44      * nxdims     dimensions of data in the field 
 45      * nxtype     data type 
 46      * nxdata     data in the field 
 47      * nxdata_as('units')      data returned in particular units 
 48      * nxslab     slab context for the field 
 49   
 50  Linked fields (class NXlink) have attributes for accessing the link:: 
 51      * nxlink     reference to the linked field 
 52   
 53  Unknown fields (class Unknown) are groups with a name that doesn't 
 54  start with 'NX'.  These groups are not read or written. 
 55   
 56  NeXus attributes (class NXattr) have a type and a value only:: 
 57      * nxtype     attribute type 
 58      * nxdata     attribute data 
 59   
 60  Data can be stored in the NeXus file in a variety of units, depending 
 61  on which facility is storing the file.  This makes life difficult 
 62  for reduction and analysis programs which must know the units they 
 63  are working with.  Our solution to this problem is to allow you to 
 64  retrieve data from the file in particular units.  For example, if 
 65  detector distance is stored in the file using millimeters you can 
 66  retrieve them in meters using:: 
 67      entry.instrument.detector.distance.nxdata_as('m') 
 68  See help for nxsunit for more details on the unit formats supported. 
 69   
 70  The slab interface to field data works by opening the file handle 
 71  and keeping it open as long as the slab interface is needed.  This 
 72  is done in python 2.5 using the with statement.  Once the context 
 73  is entered, get() and put() methods on the node allow you to read 
 74  and write data a slab at a time.  For example:: 
 75   
 76      # Read a Ni x Nj x Nk array one vector at a time 
 77      with node.nxslab: 
 78          size = [1,1,node.nxdims[2]] 
 79          for i in range(node.nxdims[0]): 
 80              for j in range(node.nxdims[1]): 
 81                  value = node.nxslab.get([i,j,0],size) # Read counts 
 82   
 83  The equivalent can be done in Python 2.4 and lower using the context 
 84  functions __enter__ and __exit__:: 
 85   
 86      node.nxslab.__enter__() 
 87      ... do the slab functions ... 
 88      node.nxslab.__exit__() 
 89   
 90  You can traverse the tree by component class instead of component name. 
 91  Since there may be multiple components of the same class in one group 
 92  you will need to specify which one to use.  For example, 
 93  tree.NXentry[0].NXinstrument[0].NXdetector[0].distance references the 
 94  first detector of the first instrument of the first entry.  Unfortunately, 
 95  there is no guarantee regarding the order of the entries, and it may vary 
 96  from call to call, so the above is of limited utility. 
 97   
 98  The nxplot() method for groups uses matplotlib to plot the data.  You 
 99  can replace this with your own plotter by setting nexus.NXgroup._plotter 
100  to your own plotter class.  The plotter class has one method:: 
101   
102      plot(signal, axes, entry, title) 
103   
104  where signal is the field containing the data, axes are the fields 
105  listing the signal sample points, entry is file/path within the file 
106  to the data group and title is the title of the NXentry, if available. 
107   
108  The read() and write() functions are implemented within a specialized 
109  NeXus class which allows all the usual API functions.  You can subclass 
110  this with your own definitions for NXgroup(), NXattr(), SDS() and NXlink() 
111  if you want to change the nature of the tree.  The properties of these 
112  classes are closely coupled to the behaviour of read/write so refer to 
113  the source if you need to do this. 
114  """ 
115  __all__ = ['read', 'write', 'dir'] 
116   
117  from copy import copy, deepcopy 
118  import numpy 
119  import nxs 
120  import nxsunit 
121 122 123 -class NeXus(nxs.NeXus):
124 """ 125 Structure-based interface to the NeXus file API. 126 127 Usage: 128 129 file = NeXus(filename, ['r','rw','w']) 130 root = file.read() 131 - read the structure of the NeXus file. This returns a NeXus tree. 132 file.write(root) 133 - write a NeXus tree to the file. 134 data = file.readpath(path) 135 - read data from a particular path 136 137 138 Example:: 139 140 nx = NeXus('REF_L_1346.nxs','r') 141 tree = nx.read() 142 for entry in tree.NXentry: 143 process(entry) 144 copy = NeXus('modified.nxs','w') 145 copy.write(tree) 146 147 Note that the large datasets are not loaded immediately. Instead, the 148 when the data set is requested, the file is reopened, the data read, and 149 the file closed again. open/close are available for when we want to 150 read/write slabs without the overhead of moving the file cursor each time. 151 The NXdata nodes in the returned tree hold the node values. 152 153 """
154 - def read(self):
155 """ 156 Read the nexus file structure from the file. Reading of large datasets 157 will be postponed. Returns a tree of NXgroup, NXattr, SDS and 158 NXlink nodes. 159 160 Subclasses can provide methods for individual NeXus classes such 161 as NXbeam or NXdata. Brave users can also specialize NXgroup, 162 NXattr, SDS and NXlink methods. 163 """ 164 self.open() 165 self.openpath("/") 166 root = self._readgroup() 167 self.close() 168 root.nxgroup = None 169 170 # Resolve links 171 self._readlinks(root) 172 return root
173
174 - def write(self, tree):
175 """ 176 Write the nexus file structure to the file. The file is assumed to 177 start empty. 178 179 Updating individual nodes can be done using the napi interface, with 180 nx.handle as the nexus file handle. 181 """ 182 self.open() 183 links = [] 184 # Root node is special --- only write its children. 185 # TODO: maybe want to write root node attributes? 186 for entry in tree.nxentries.values(): 187 links += self._writegroup(entry, path="") 188 self._writelinks(links) 189 self.close()
190
191 - def readpath(self, path):
192 """ 193 Read the data on a particular file path. 194 195 Returns a numpy array containing the data, a python scalar, or a 196 string depending on the shape and storage class. 197 """ 198 self.open() 199 self.openpath(path) 200 return self.getdata()
201
202 - def _readattrs(self):
203 """ 204 Return the attributes for the currently open group/data or for 205 the file if no group or data object is open. 206 """ 207 attrs = dict() 208 for i in range(self.getattrinfo()): 209 name,length,nxtype = self.getnextattr() 210 value = self.getattr(name, length, nxtype) 211 pair = self.NXattr(value,nxtype) 212 attrs[name] = pair 213 #print "read attr",name,pair.nxdata, pair.nxtype 214 return attrs
215
216 - def _readdata(self,name,path):
217 """ 218 Read a data node, returning SDS or NXlink depending on the 219 nature of the node. 220 """ 221 # Finally some data, but don't read it if it is big 222 # Instead record the location, type and size 223 path = path+"/"+name 224 self.opendata(name) 225 attrs = self._readattrs() 226 if 'target' in attrs and attrs['target'].nxdata != path: 227 # This is a linked dataset; don't try to load it. 228 #print "read link %s->%s"%(path,attrs['target'].nxdata) 229 data = NXlink(name,nxclass='SDS',attrs=attrs) 230 else: 231 dims,type = self.getinfo() 232 if numpy.prod(dims) < 1000: 233 value = self.getdata() 234 else: 235 value = None 236 data = self.SDS(name,type,dims,attrs=attrs,value=value, 237 file=self,path=path) 238 self.closedata() 239 return data
240
241 - def _readchildren(self,n,path):
242 children = {} 243 for i in range(n): 244 name,nxclass = self.getnextentry() 245 #print "name,class,path",name,nxclass,path 246 if nxclass == 'SDS': 247 children[name] = self._readdata(name,path) 248 elif nxclass.startswith('NX'): 249 self.opengroup(name,nxclass) 250 children[name] = self._readgroup() 251 self.closegroup() 252 else: # Bad entry; flag it but don't do anything 253 children[name] = self.Unknown(name,nxclass=nxclass) 254 return children
255
256 - def _readgroup(self):
257 """ 258 Read the currently open group and all subgroups. 259 """ 260 # TODO: does it make sense to read without recursing? 261 # TODO: can we specify which NXclasses we are interested 262 # in and skip those of different classes? 263 n,_,nxclass = self.getgroupinfo() 264 # getgroupinfo doesn't seem to return the correct path, so reconstruct 265 path = '/' + '/'.join(self.path) 266 name = path.split('/')[-1] 267 attrs = self._readattrs() 268 if 'target' in attrs and attrs['target'].nxdata != path: 269 # This is a linked group; don't try to load it. 270 #print "read group link %s->%s"%(attrs['target'].nxdata,path) 271 group = self.NXlink(name,nxclass=nxclass,attrs=attrs) 272 else: 273 #print "read group",nxclass,"from",path 274 children = self._readchildren(n,path) 275 # If we are subclassed with a handler for the particular 276 # NXentry class name use that constructor for the group 277 # rather than the generic NXgroup class. 278 if hasattr(self,nxclass): 279 factory = getattr(self,nxclass) 280 else: 281 factory = self.NXgroup 282 group = factory(name,nxclass,attrs=attrs,entries=children) 283 # Build chain back structure 284 for node in children.values(): 285 node.nxgroup = group 286 return group
287 298 299 # Allow subclasses to override
300 - def NXattr(self, *args, **kw): return NXattr(*args, **kw)
301 - def SDS(self, *args, **kw): return SDS(*args, **kw)
302 - def NXgroup(self,*args,**kw): return NXgroup(*args, **kw)
304 - def Unknown(self, *args, **kw): return Unknown(*args, **kw)
305
306 - def _writeattrs(self,attrs):
307 """ 308 Return the attributes for the currently open group/data or for 309 the file if no group or data object is open. 310 """ 311 for name,pair in attrs.iteritems(): 312 #print "write attrs",name,pair.nxtype,pair.nxdata 313 self.putattr(name,pair.nxdata,pair.nxtype)
314
315 - def _writedata(self, data, path):
316 """ 317 Write the given data node. 318 319 NXlinks cannot be written until the linked group is created, so 320 this routine returns the set of links that need to be written. 321 Call writelinks on the list. 322 """ 323 324 path = path + "/" + data.nxname 325 #print 'write data',path 326 327 # If the data is linked then 328 if hasattr(data,'_link_target'): 329 return [(path, data._link_target)] 330 331 # Finally some data, but don't read it if it is big 332 # Instead record the location, type and size 333 #print "creating data",child.nxname,child.nxdims,child.nxtype 334 if numpy.prod(data.nxdims) > 10000: 335 # Compress the fastest moving dimension of large datasets 336 slab_dims = numpy.ones(len(data.nxdims),'i') 337 slab_dims[-1] = data.nxdims[-1] 338 self.compmakedata(data.nxname, data.nxtype, data.nxdims, 339 'lzw', slab_dims) 340 else: 341 # Don't use compression for small datasets 342 self.makedata(data.nxname, data.nxtype, data.nxdims) 343 self.opendata(data.nxname) 344 self._writeattrs(data.nxattrs) 345 value = data.nxdata 346 if value is not None: self.putdata(value) 347 self.closedata() 348 return []
349
350 - def _writegroup(self, group, path):
351 """ 352 Write the given group structure, including the data. 353 354 NXlinks cannot be written until the linked group is created, so 355 this routine returns the set of links that need to be written. 356 Call writelinks on the list. 357 """ 358 path = path + "/" + group.nxname 359 #print 'write group',path 360 361 links = [] 362 self.makegroup(group.nxname, group.nxclass) 363 self.opengroup(group.nxname, group.nxclass) 364 self._writeattrs(group.nxattrs) 365 if hasattr(group, '_link_target'): 366 links += [(path, group._link_target)] 367 for child in group.nxentries.values(): 368 if child.nxclass == 'SDS': 369 links += self._writedata(child,path) 370 elif hasattr(child,'_link_target'): 371 links += [(path+"/"+child.nxname,child._link_target)] 372 else: 373 links += self._writegroup(child,path) 374 self.closegroup() 375 return links
376
408
409 -class NXattr(object):
410 """ 411 Attributes need to keep track of nxtype as well as attribute value. 412 """
413 - def __init__(self,value=None,nxtype='char'):
414 self.nxdata,self.nxtype = value,nxtype
415
416 - def __str__(self):
417 return str(self.nxdata)
418
419 - def __repr__(self):
420 if self.nxtype == 'char': 421 return "NXattr('%s','%s')"%(self.nxdata,self.nxtype) 422 else: 423 return "NXattr(%s,'%s')"%(self.nxdata,self.nxtype)
424
425 -class NXnode(object):
426 """ 427 Abstract base class for elements in NeXus files. 428 The individual objects may be either SDS data objects or groups. 429 430 Children should be accessible directly as object attributes. 431 NeXus attributes accessible using .Aname rather than .name. 432 """ 433 nxclass = "unknown" 434 nxname = "unknown" 435 nxgroup = None 436
437 - def __str__(self):
438 return "%s:%s"%(self.nxclass,self.nxname)
439
440 - def __repr__(self):
441 return "NXnode('%s','%s')"%(self.nxclass,self.nxname)
442
443 - def _setattrs(self, attrs):
444 for k,v in attrs.items(): 445 setattr(self, 'A'+k, v)
446 - def _attrs(self):
447 return dict([(k[1:],v) 448 for k,v in self.__dict__.items() 449 if isinstance(v,NXattr)])
450 - def _entries(self):
451 return dict([(k,v) 452 for k,v in self.__dict__.items() 453 if isinstance(v,NXnode) and not k.startswith('nx')])
454 nxattrs = property(_attrs,doc="NeXus attributes for node") 455 nxentries = property(_entries,doc="NeXus nodes within group") 456
457 - def _str_name(self,indent=0):
458 if self.nxclass == 'SDS': 459 return " "*indent+self.nxname+'\n' 460 else: 461 return " "*indent+self.nxname+':'+self.nxclass+'\n'
462
463 - def _str_value(self,indent=0):
464 return ""
465
466 - def _str_attrs(self,indent=0):
467 result = "" 468 attrs = self.nxattrs 469 names = attrs.keys() 470 names.sort() 471 for k in names: 472 result += " "*indent+"@%s = %s\n"%(k,attrs[k].nxdata) 473 return result
474
475 - def _str_tree(self,indent=0,attrs=False,recursive=False):
476 """ 477 Print current node and possibly subnodes. 478 """ 479 # Print node 480 result = self._str_name(indent=indent) 481 if attrs: result += self._str_attrs(indent=indent+2) 482 result += self._str_value(indent=indent+2) 483 # Print children 484 entries = self.nxentries 485 names = entries.keys() 486 names.sort() 487 if recursive: 488 for k in names: 489 result += entries[k]._str_tree(indent=indent+2, 490 attrs=attrs, recursive=True) 491 else: 492 for k in names: 493 result += entries[k]._str_name(indent=indent+2) 494 return result
495
496 - def nxdir(self,attrs=False,recursive=False):
497 """Print directory""" 498 print self._str_tree(attrs=attrs,recursive=recursive)
499 - def nxtree(self,attrs=True):
500 """Print directory tree""" 501 print self._str_tree(attrs=attrs,recursive=True)
502
503 -class NXslab_context(object):
504 """ 505 Context manager for NeXus fields. 506 507 The context manager opens the file and positions the cursor to the 508 correct field. Data can then be read or written using the get() and 509 put() methods. See SDS for details on how to use this class. 510 """ 511
512 - def __init__(self, file, path, unit_converter):
513 self._file = file 514 self._path = path 515 self._converter = unit_converter
516
517 - def __enter__(self):
518 """ 519 Open the datapath for reading slab by slab. 520 """ 521 self._close_on_exit = not self._file.isopen 522 self._file.open() # Force file open even if closed 523 self._file.openpath(self._path) 524 return self
525
526 - def __exit__(self, *args):
527 """ 528 Close the file associated the data after reading. 529 """ 530 if self._close_on_exit: 531 self._file.close()
532
533 - def get(self, offset, size, units=""):
534 """ 535 Get a slab from the data array. 536 537 Offsets are 0-origin. Shape can be inferred from the data. 538 Offset and shape must each have one entry per dimension. 539 540 If units are specified, convert the values to the given units 541 before returning them. 542 543 Raises ValueError if this fails. 544 545 Corresponds to NXgetslab(handle,data,offset,shape) 546 """ 547 value = self._file.getslab(offset,size) 548 return self._converter(value,units)
549
550 - def put(self, data, offset):
551 """ 552 Put a slab into the data array. 553 554 Offsets are 0-origin. Shape can be inferred from the data. 555 Offset and shape must each have one entry per dimension. 556 557 Raises ValueError if this fails. 558 559 Corresponds to NXputslab(handle,data,offset,shape) 560 """ 561 self._file.putslab(data, offset, data.shape)
562
563 -class SDS(NXnode):
564 """ 565 NeXus data node SDS. 566 567 Operations are querying type and dimensions, reading the entire 568 data block or reading a single slab. 569 570 Attributes: 571 572 nxname - dataset name 573 nxclass - SDS (Scientific Data Set) 574 nxattrs - attributes for the data 575 nxgroup - group containing the data 576 nxdims - array dimensions (numpy shape) 577 nxtype - data type (numpy dtype) 578 nxdata - data values 579 nxdata_as('units') - data values in particular units 580 nxslab - context manager for reading/writing slabs 581 582 Example: 583 584 # Read the entire array 585 node.nxdata # The dataset in stored units 586 node.nxdata_as(units="mm") # read the entire dataset as millimeters 587 588 # Read a Ni x Nj x Nk array one vector at a time 589 with node.nxslab: 590 size = [1,1,node.nxdims[2]] 591 for i in range(node.nxdims[0]): 592 for j in range(node.nxdims[1]): 593 value = node.nxslab.get([i,j,0],size) # Read counts 594 595 596 """
597 - def __init__(self,name,dtype='',shape=[],file=None,path=None, 598 attrs=None,value=None,group=None):
599 self._file = file 600 self._path = path 601 self._value = value 602 self.nxclass = "SDS" # Scientific Data Set 603 self.nxname = name 604 self.nxtype = dtype 605 self.nxdims = shape 606 self.nxgroup = group 607 # Convert NeXus attributes to python attributes 608 self._setattrs(attrs) 609 if 'units' in attrs: 610 units = attrs['units'].nxdata 611 else: 612 units = None 613 self._converter = nxsunit.Converter(units) 614 self.nxslab = NXslab_context(file, path, self._converter)
615
616 - def __str__(self):
617 """ 618 If value is loaded, return the value as a string. If value is 619 not loaded, return the empty string. Only the first view values 620 for large arrays will be printed. 621 """ 622 if self._value is not None: 623 return str(self._value) 624 return ""
625
626 - def _str_value(self,indent=0):
627 v = str(self) 628 if '\n' in v: 629 v = '\n'.join([(" "*indent)+s for s in v.split('\n')]) 630 return v+'\n'
631
632 - def _str_tree(self,indent=0,attrs=False,recursive=False):
633 dims = 'x'.join([str(n) for n in self.nxdims]) 634 #return "%s(%s)"%(self.nxtype, dims) 635 v = str(self) 636 if '\n' in v or v == "": 637 v = "%s(%s)"%(self.nxtype, dims) 638 v = " "*indent + "%s = %s"%(self.nxname, v)+'\n' 639 if attrs: v += self._str_attrs(indent=indent+2) 640 return v
641
642 - def nxdata_as(self, units=""):
643 """ 644 Read the data in particular units. 645 """ 646 if self._value is None: 647 self._value = self._file.readpath(self._path) 648 649 return self._converter(self._value,units)
650 651 nxdata = property(nxdata_as,doc="The data in default units; see also read() and slab()")
652 671
672 -def centers(signal, axes):
673 """ 674 Return the centers of the axes regardless if the axes contain 675 bin boundaries or centers. 676 """ 677 def findc(axis, dimlen): 678 if len(axis.nxdata) == dimlen+1: 679 return (axis.nxdata[:-1] + axis.nxdata[-1:])/2 680 else: 681 assert len(axis.nxdata) == dimlen 682 return axis.nxdata
683 return [findc(a,signal.nxdims[i]) for i,a in enumerate(axes)] 684
685 -def label(field):
686 """ 687 Construct a label for a data field suitable for use on a graph axis. 688 """ 689 if hasattr(field,'Along_name'): 690 return field.Along_name.nxdata 691 else: 692 return "%s (%s)"%(field.nxname,field.Aunits.nxdata)
693
694 -def imshow_irregular(x,y,z):
695 import pylab 696 ax = pylab.gca() 697 im = pylab.mpl.image.NonUniformImage(ax, extent=(x[0],x[-1],y[0],y[-1])) 698 im.set_data(x,y,z) 699 ax.images.append(im) 700 ax.set_xlim(x[0],x[-1]) 701 ax.set_ylim(y[0],y[-1]) 702 pylab.gcf().canvas.draw_idle()
703
704 -class PylabPlotter(object):
705 """ 706 Matplotlib plotter object for NeXus data nodes. 707 """
708 - def plot(self, signal, axes, entry, title, **opts):
709 import pylab 710 pylab.cla() 711 712 # Find the centers of the bins for histogrammed data 713 axis_data = centers(signal, axes) 714 title = entry + '\n' + title 715 716 #One-dimensional Plot 717 if len(signal.nxdims) == 1: 718 if signal.Aunits.nxdata == 'counts': 719 myopts=copy(opts) 720 myopts.setdefault('fmt','o') 721 myopts.setdefault('linestyle','None') 722 pylab.errorbar(axis_data[0], signal.nxdata, 723 numpy.sqrt(signal.nxdata), **myopts) 724 else: 725 pylab.scatter(axis_data[0], signal.nxdata, **opts) 726 pylab.xlabel(label(axes[0])) 727 pylab.ylabel(label(signal)) 728 pylab.title(title) 729 730 #Two dimensional plot 731 elif len(signal.nxdims) == 2: 732 #gridplot = pylab.pcolormesh 733 gridplot = imshow_irregular 734 if signal.Aunits.nxdata == 'counts': 735 gridplot(axis_data[1], axis_data[0], 736 numpy.log10(signal.nxdata+1), **opts) 737 else: 738 gridplot(axis_data[1], axis_data[0], 739 signal.nxdata, **opts) 740 pylab.xlabel(label(axes[1])) 741 pylab.ylabel(label(axes[0])) 742 pylab.title(title) 743 744 # No support for higher dimensions yet 745 else: 746 raise RuntimeError, "Cannot plot a dataset of rank 3 or greater."
747 @staticmethod
748 - def show():
749 import pylab 750 pylab.show()
751
752 -class NXgroup(NXnode):
753 """ 754 NeXus group node. NXgroup nodes have no data associated with them, 755 but they do have attributes and children. 756 """ 757 # Plotter to use for nxplot calls 758 _plotter = PylabPlotter() 759
760 - def __init__(self,name, nxclass=NXnode.nxclass, 761 attrs={},entries={}, group=None):
762 self.nxclass = nxclass 763 self.nxname = name 764 self.nxgroup = group 765 # Convert children and NeXus attributes to python attributes 766 self._setattrs(attrs) 767 for k,v in entries.items(): 768 setattr(self, k, v)
769 - def _str_value(self,indent=0):
770 return ""
771
772 - def __getattr__(self, key):
773 """ 774 Provide direct access to nodes via nxclass name. 775 """ 776 if key.startswith('NX'): 777 return self.nxcomponent(key) 778 raise KeyError(key+" not in "+self.nxclass+":"+self.nxname)
779
780 - def nxcomponent(self, nxclass):
781 """ 782 Find all child nodes that have a particular class. 783 """ 784 return [E for name,E in self.nxentries.items() if E.nxclass==nxclass]
785
786 - def nxplot(self, **opts):
787 """ 788 Plot data contained within the group. 789 """ 790 791 # Find a plottable signal 792 for node in self.nxentries.values(): 793 if 'signal' in node.nxattrs: 794 signal = node 795 break 796 else: 797 raise RuntimeError('No plottable signal') 798 799 # Find the associated axes 800 if hasattr(signal,'Aaxes'): 801 axes = [getattr(self,a) for a in signal.Aaxes.nxdata.split(':')] 802 else: 803 raise RuntimeError('Axes attribute missing from signal') 804 805 # Construct title 806 path = [] 807 node = self 808 subtitle = '' 809 while node.nxgroup is not None: 810 if node.nxclass == 'NXentry': 811 subtitle = node.title.nxdata 812 path = [node.nxname] + path 813 node = node.nxgroup 814 path = [node.Afile_name.nxdata] + path 815 entry = "/".join(path) 816 817 # Plot with the available plotter 818 self._plotter.plot(signal, axes, entry, subtitle)
819
820 -class Unknown(NXnode):
821 """ 822 Unknown group type; class does not start with NX or SDS. 823 """
824 - def __init__(self, name, nxclass=NXnode.nxclass):
825 self.nxname = name 826 self.nxclass = nxclass
827 - def __repr__(self):
828 return "Unknown('%s','%s')"%(self.nxname,self.nxclass)
829
830 # File level operations 831 -def read(filename, mode='r'):
832 """ 833 Read a NeXus file, returning a tree of nodes 834 """ 835 file = NeXus(filename,mode) 836 tree = file.read() 837 file.close() 838 return tree
839
840 -def write(filename, tree):
841 """ 842 Write a NeXus file from a tree of nodes 843 """ 844 file = NeXus(filename,'w5') 845 file.write(tree)
846
847 -def dir(file):
848 """ 849 Read and summarize the named nexus file. 850 """ 851 tree = read(file) 852 tree.nxtree()
853
854 -def demo(argv):
855 """ 856 Process command line commands in argv. argv should contain 857 program name, command, arguments, where command is one of 858 the following: 859 copy fromfile.nxs tofile.nxs 860 ls f1.nxs f2.nxs ... 861 """ 862 if len(argv) > 1: 863 op = argv[1] 864 else: 865 op = 'help' 866 if op == 'ls': 867 for f in argv[2:]: dir(f) 868 elif op == 'copy' and len(argv)==4: 869 tree = read(argv[2]) 870 write(argv[3], tree) 871 elif op == 'plot' and len(argv)==4: 872 tree = read(argv[2]) 873 for entry in argv[3].split('.'): 874 tree = getattr(tree,entry) 875 tree.nxplot() 876 tree._plotter.show() 877 878 else: 879 usage = """ 880 usage: %s cmd [args] 881 copy fromfile.nxs tofile.nxs 882 ls *.nxs 883 plot file.nxs entry.data 884 """%(argv[0],) 885 print usage
886 887 if __name__ == "__main__": 888 import sys 889 demo(sys.argv) 890