Ignore:
Timestamp:
16/01/12 18:06:09 (4 months ago)
Author:
Ray Osborn
Message:

Refs #322: Major revision to the tree API to make it more robust and provide a cleaner syntax.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/bindings/python/nxs/tree.py

    r1571 r1801  
    33 
    44""" 
    5 Tree view for NeXus files. 
    6  
    7 The `nexus.tree` routines provide a natural interface to NeXus datasets. 
    8 Entries in a group are referenced much like fields in a class are 
    9 referenced in python.  Rather than following the directory model of 
    10 the `nexus.napi` interface, users are free to reference separate fields 
    11 in the dataset at the same time.  Large datasets are not read until  
    12 they are needed, and may be read or written one slab at a time. 
    13  
    14 There are a number of functions which operate on files:: 
    15  
    16     import nexus 
    17     nxfile = nexus.load('file.nxs') # loads a structure from a file 
    18     nexus.save('copy.nxs', tree)    # saves a structure to a file 
    19     nexus.tree('copy.nxs')          # display the contents of a file 
    20  
    21 The tree returned from load() has an entry for each group, field and 
    22 attribute.  You can traverse the hierarchy using the names of the 
    23 groups.  For example, tree.entry.instrument.detector.distance is an 
    24 example of a field containing the distance to each pixel in the  
    25 detector. Entries can also be referenced by NXclass name, such as  
    26 tree.NXentry[0].instrument. Since there may be multiple entries of the  
    27 same NXclass, the NXclass attribute returns a possibly empty list. 
    28  
    29 Properties of the entry in the tree are referenced by nx attributes. 
    30 Depending on the node type, different nx attributes may be available. 
    31  
    32 Nodes (class NXnode) have attributes shared by both groups and fields:: 
    33     * nxname     node name 
    34     * nxclass    node class for groups, 'SDS' for fields 
    35     * nxgroup    group containing the entry, or None for the root 
    36     * nxattrs    dictionary of NeXus attributes for the node 
     5NeXus data as Python trees 
     6========================== 
     7The `nexus.tree` modules are designed to accomplish two goals: 
     8 
     9    1. To provide convenient access to existing data contained in NeXus files. 
     10    2. To enable new NeXus data to be created and manipulated interactively. 
     11     
     12These goals are achieved by mapping hierarchical NeXus data structures directly 
     13into python objects, which either represent NeXus groups or NeXus fields. 
     14Entries in a group are referenced much like fields in a class are referenced in 
     15python. The entire data hierarchy can be referenced at any time, whether the 
     16NeXus data has been loaded in from an existing NeXus file or created dynamically 
     17within the python session. This provides a much more natural scripting interface 
     18to NeXus data than the directory model of the `nexus.napi` interface. 
     19 
     20Example 1: Loading a NeXus file 
     21------------------------------- 
     22The following commands loads NeXus data from a file, displays (some of) the 
     23contents as a tree, and then accesses individual data items. 
     24 
     25    >>> from nexpy.api import nexus as nx 
     26    >>> a=nx.load('sns/data/ARCS_7326.nxs') 
     27    >>> a.tree 
     28    root:NXroot 
     29      @HDF5_Version = 1.8.2 
     30      @NeXus_version = 4.2.1 
     31      @file_name = ARCS_7326.nxs 
     32      @file_time = 2010-05-05T01:59:25-05:00 
     33      entry:NXentry 
     34        data:NXdata 
     35          data = float32(631x461x4x825) 
     36            @axes = rotation_angle:tilt_angle:sample_angle:time_of_flight 
     37            @signal = 1 
     38          rotation_angle = float32(632) 
     39            @units = degree 
     40          sample_angle = [ 210.  215.  220.  225.  230.] 
     41            @units = degree 
     42          tilt_angle = float32(462) 
     43            @units = degree 
     44          time_of_flight = float32(826) 
     45            @units = microsecond 
     46        run_number = 7326 
     47        sample:NXsample 
     48          pulse_time = 2854.94747365 
     49            @units = microsecond 
     50    . 
     51    . 
     52    . 
     53    >>> a.entry.run_number 
     54    NXfield(7326) 
     55 
     56So the tree returned from load() has an entry for each group, field and 
     57attribute.  You can traverse the hierarchy using the names of the groups.  For 
     58example, tree.entry.instrument.detector.distance is an example of a field 
     59containing the distance to each pixel in the detector. Entries can also be 
     60referenced by NXclass name, such as tree.NXentry[0].instrument. Since there may 
     61be multiple entries of the same NeXus class, the NXclass attribute returns a 
     62(possibly empty) list. 
     63 
     64The load() and save() functions are implemented using the class 
     65`nexus.tree.NeXusTree`, a subclass of `nexus.napi.NeXus` which allows all the 
     66usual API functions. 
     67 
     68Example 2: Creating a NeXus file dynamically 
     69-------------------------------------------- 
     70The second example shows how to create NeXus data dynamically and saves it to a 
     71file. The data are first created as Numpy arrays 
     72 
     73    >>> import numpy as np 
     74    >>> x=y=np.linspace(0,2*np.pi,101) 
     75    >>> X,Y=np.meshgrid(y,x) 
     76    >>> z=np.sin(X)*np.sin(Y) 
     77     
     78Then a NeXus data groups are created and the data inserted to produce a  
     79NeXus-compliant structure that can be saved to a file. 
     80 
     81    >>> root=nx.NXroot(NXentry()) 
     82    >>> root.tree 
     83    root:NXroot 
     84      entry:NXentry 
     85    >>> root.entry.data=nx.NXdata(z,[x,y]) 
     86 
     87Additional metadata can be inserted before saving the data to a file. 
     88 
     89    >>> root.entry.sample=nx.NXsample() 
     90    >>> root.entry.sample.temperature = 40.0 
     91    >>> root.entry.sample.temperature.units = 'K' 
     92    >>> root.save('example.nxs') 
     93 
     94NeXus Objects 
     95------------- 
     96Properties of the entry in the tree are referenced by attributes that depend 
     97on the object type, different nx attributes may be available. 
     98 
     99Objects (class NXobject) have attributes shared by both groups and fields:: 
     100    * nxname   object name 
     101    * nxclass  object class for groups, 'NXfield' for fields 
     102    * nxgroup  group containing the entry, or None for the root 
     103    * attrs    dictionary of NeXus attributes for the object 
    37104 
    38105Groups (class NXgroup) have attributes for accessing children:: 
    39     * nxentries  dictionary of entries within the group 
    40     * nxcomponent('nxclass')  return group entries of a particular class 
    41     * nxdir()    print the list of entries in the group 
    42     * nxtree()   print the list of entries and subentries in the group 
    43     * nxplot()   plot signal and axes for the group, if available 
    44  
    45 Fields (class SDS) have attributes for accessing data: 
    46     * nxdims     dimensions of data in the field 
    47     * nxtype     data type 
    48     * nxdata     data in the field 
    49     * nxdata_as  data returned in particular units 
    50     * nxslab     slab context for the field 
    51  
    52 Linked fields (class NXlink) have attributes for accessing the link:: 
    53     * nxlink     reference to the linked field 
    54  
    55 Unknown fields (class Unknown) are groups with a name that doesn't 
    56 start with 'NX'.  These groups are not loaded or saved. 
     106    * entries  dictionary of entries within the group 
     107    * component('nxclass')  return group entries of a particular class 
     108    * dir()    print the list of entries in the group 
     109    * tree     print the list of entries and subentries in the group 
     110    * plot()   plot signal and axes for the group, if available 
     111 
     112Fields (class NXfield) have attributes for accessing data: 
     113    * shape    dimensions of data in the field 
     114    * dtype    data type 
     115    * nxdata   data in the field 
     116 
     117Linked fields or groups (class NXlink) have attributes for accessing the link:: 
     118    * nxlink   reference to the linked field or group 
    57119 
    58120NeXus attributes (class NXattr) have a type and a value only:: 
    59     * nxtype     attribute type 
    60     * nxdata     attribute data 
    61  
    62 Data can be stored in the NeXus file in a variety of units, depending 
    63 on which facility is storing the file.  This makes life difficult 
    64 for reduction and analysis programs which must know the units they 
    65 are working with.  Our solution to this problem is to allow the reader 
    66 to retrieve data from the file in particular units.  For example, if 
    67 detector distance is stored in the file using millimeters you can  
    68 retrieve them in meters using:: 
    69     entry.instrument.detector.distance.nxdata_as('m') 
     121    * dtype    attribute type 
     122    * nxdata   attribute data 
     123 
     124There is a subclass of NXgroup for each group class defined by the NeXus standard,  
     125so it is possible to create an NXgroup of NeXus class NXsample directly using: 
     126 
     127    >>> sample = NXsample() 
     128 
     129The default group name will be the class name following the 'NX', so the above  
     130group will have an nxname of 'sample'. However, this is overridden by the  
     131attribute name when it is assigned as a group attribute, e.g., 
     132 
     133    >>> entry.sample1 = NXsample() 
     134    >>> entry.sample1.nxname 
     135    sample1 
     136 
     137You can traverse the tree by component class instead of component name. Since 
     138there may be multiple components of the same class in one group you will need to 
     139specify which one to use.  For example, 
     140 
     141    tree.NXentry[0].NXinstrument[0].NXdetector[0].distance  
     142 
     143references the first detector of the first instrument of the first entry.  
     144Unfortunately, there is no guarantee regarding the order of the entries, and it 
     145may vary from call to call, so this is mainly useful in iterative searches. 
     146 
     147 
     148Unit Conversion 
     149--------------- 
     150Data can be stored in the NeXus file in a variety of units, depending on which 
     151facility is storing the file.  This makes life difficult for reduction and 
     152analysis programs which must know the units they are working with.  Our solution 
     153to this problem is to allow the reader to retrieve data from the file in 
     154particular units.  For example, if detector distance is stored in the file using 
     155millimeters you can retrieve them in meters using:: 
     156 
     157    entry.instrument.detector.distance.convert('m') 
     158 
    70159See `nexus.unit` for more details on the unit formats supported. 
    71160 
    72 The slab interface to field data works by opening the file handle 
    73 and keeping it open as long as the slab interface is needed.  This 
    74 is done in python 2.5 using the with statement.  Once the context 
    75 is entered, nxget() and nxput() methods on the node allow you to read 
    76 and write data a slab at a time.  For example:: 
     161Reading and Writing Slabs 
     162------------------------- 
     163The slab interface to field data works by opening the file handle and keeping it 
     164open as long as the slab interface is needed.  This is done in python 2.5 using 
     165the with statement.  Once the context is entered, get() and put() methods on the 
     166object allow you to read and write data a slab at a time.  For example:: 
    77167 
    78168    # Read a Ni x Nj x Nk array one vector at a time 
    79169    with root.NXentry[0].data.data as slab: 
    80         Ni,Nj,Nk = slab.nxdims 
     170        Ni,Nj,Nk = slab.shape 
    81171        size = [1,1,Nk] 
    82172        for i in range(Ni): 
    83173            for j in range(Nj): 
    84                 value = slab.nxget([i,j,0],size) 
     174                value = slab.get([i,j,0],size) 
    85175 
    86176The equivalent can be done in Python 2.4 and lower using the context 
    87177functions __enter__ and __exit__:: 
    88178 
    89     slab = data.nxslab.__enter__() 
     179    slab = data.slab.__enter__() 
    90180    ... do the slab functions ... 
    91     data.nxslab.__exit__() 
    92  
    93 You can traverse the tree by component class instead of component name.   
    94 Since there may be multiple components of the same class in one group 
    95 you will need to specify which one to use.  For example,  
    96 tree.NXentry[0].NXinstrument[0].NXdetector[0].distance references the  
    97 first detector of the first instrument of the first entry.  Unfortunately,  
    98 there is no guarantee regarding the order of the entries, and it may vary  
    99 from call to call, so the above is of limited utility. 
    100  
    101 The nxplot() method for groups uses matplotlib to plot the data.  You 
    102 can replace this with your own plotter by setting nexus.NXgroup._plotter 
    103 to your own plotter class.  The plotter class has one method:: 
     181    data.slab.__exit__() 
     182 
     183Plotting NeXus data 
     184------------------- 
     185There is a plot() method for groups that automatically looks for 'signal' and 
     186'axes' attributes within the group in order to determine what to plot. These are 
     187defined by the 'nxsignal' and 'nxaxes' properties of the group. This means that 
     188the method will determine whether the plot should be one- or two- dimensional. 
     189For higher than two dimensions, only the top slice is plotted by default. 
     190 
     191The plot() method uses matplotlib by default to plot the data.  You can replace 
     192this with your own plotter by setting nexus.NXgroup._plotter to your own plotter 
     193class.  The plotter class has one method:: 
    104194 
    105195    plot(signal, axes, entry, title) 
    106196 
    107 where signal is the field containing the data, axes are the fields 
    108 listing the signal sample points, entry is file/path within the file 
    109 to the data group and title is the title of the NXentry, if available. 
    110  
    111 The load() and save() functions are implemented using the class 
    112 `nexus.tree.NeXusTree`, a subclass of `nexus.napi.NeXus` which allows  
    113 all the usual API functions.  You can subclass NeXusTree with your 
    114 own version that defines, e.g., a NXmonitor() method to return an 
    115 NXmonitor object when an NXmonitor class is read.  Your NXmonitor 
    116 class should probably be a subclass of NXgroup. 
     197where signal is the field containing the data, axes are the fields listing the 
     198signal sample points, entry is file/path within the file to the data group and 
     199title is the title of the group or the parent NXentry, if available. 
    117200""" 
    118 __all__ = ['NeXusTree', 'SDS', 'NXgroup', 'NXattr',  
    119            'NXroot', 'NXentry', 'NXdata', 'NXmonitor', 'NXsample', 
    120            'NXinstrument', 'NXaperture', 'NXattenuator', 'NXbeam_stop', 
    121            'NXbending_magnet', 'NXcollimator', 'NXcrystal', 'NXdetector', 
    122            'NXdisk_chopper', 'NXfermi_chopper', 'NXfilter', 'NXflipper', 
    123            'NXguide', 'NXinsertion_device', 'NXmirror', 'NXmoderator', 
    124            'NXmonochromator', 'NXpolarizer', 'NXpositioner', 'NXsource', 
    125            'NXvelocity_selector', 'NXevent_data', 'NXuser', 'NXparameter', 
    126            'NXprocess', 'NXcharacterization', 'NXlog', 'NXnote', 'NXbeam', 
    127            'NXgeometry', 'NXtranslation', 'NXshape', 'NXorientation', 
    128            'NXenvironment', 'NXsensor',  
    129            'load', 'save', 'tree', 'centers'] 
    130  
    131  
    132 from copy import copy 
     201 
     202__all__ = ['NeXusTree', 'NXobject', 'NXfield', 'NXgroup', 'NXattr',  
     203           'NX_MEMORY', 'setmemory', 'load', 'save', 'tree', 'centers',  
     204           'NXlink', 'NXlinkfield', 'NXlinkgroup', 'SDS', 'NXlinkdata'] 
     205 
     206from copy import copy, deepcopy 
    133207 
    134208import numpy as np 
    135209import napi 
    136 import unit 
    137210from napi import NeXusError 
    138211 
     212#Memory in MB 
     213NX_MEMORY = 500 
     214 
     215#List of defined base classes (later added to __all__) 
     216_nxclasses = ['NXroot', 'NXentry', 'NXsubentry', 'NXdata', 'NXmonitor', 
     217              'NXlog', 'NXsample', 'NXinstrument', 'NXaperture', 'NXattenuator', 
     218              'NXbeam', 'NXbeam_stop', 'NXbending_magnet', 'NXcharacterization', 
     219              'NXcollection', 'NXcollimator', 'NXcrystal', 'NXdetector', 
     220              'NXdisk_chopper', 'NXenvironment', 'NXevent_data', 
     221              'NXfermi_chopper', 'NXfilter', 'NXflipper', 'NXgeometry', 
     222              'NXguide', 'NXinsertion_device', 'NXmirror', 'NXmoderator', 
     223              'NXmonochromator', 'NXnote', 'NXorientation', 'NXparameter', 
     224              'NXpolarizer', 'NXpositioner', 'NXprocess', 'NXsensor', 'NXshape', 
     225              'NXsource', 'NXtranslation', 'NXuser', 'NXvelocity_selector', 'NXtree'] 
     226 
    139227np.set_printoptions(threshold=5) 
    140228 
    141229class NeXusTree(napi.NeXus): 
     230 
    142231    """ 
    143232    Structure-based interface to the NeXus file API. 
     
    167256    the file closed again.  open/close are available for when we want to 
    168257    read/write slabs without the overhead of moving the file cursor each time. 
    169     The NXdata nodes in the returned tree hold the node values. 
     258    The NXdata objects in the returned tree hold the object values. 
    170259 
    171260    Subclasses can provide methods for individual NeXus classes such 
    172261    as NXbeam or NXdata.  Brave users can also specialize NXgroup,  
    173     NXattr, SDS and NXlink methods. 
    174     """ 
     262    NXattr, NXfield and NXlink methods. 
     263    """ 
     264 
    175265    def readfile(self): 
    176266        """ 
    177         Read the nexus file structure from the file.  Large datasets 
    178         are not read until they are needed.   
    179  
    180         Returns a tree of NXgroup, SDS and NXlink nodes. 
     267        Read the NeXus file structure from the file and return a tree of NXobjects. 
     268 
     269        Large datasets are not read until they are needed.   
    181270        """ 
    182271        self.open() 
     
    184273        root = self._readgroup() 
    185274        self.close() 
    186         root.nxgroup = None         
    187         # Resolve links (not necessary now that nxlink is set as a property) 
     275        root._group = None         
     276        # Resolve links (not necessary now that link is set as a property) 
    188277        #self._readlinks(root, root) 
     278        root._file = self 
    189279        return root 
    190280 
    191281    def writefile(self, tree): 
    192282        """ 
    193         Write the nexus file structure to the file.  The file is assumed to 
    194         start empty. 
    195  
    196         Updating individual nodes can be done using the napi interface, with 
    197         nx.handle as the nexus file handle. 
     283        Write the NeXus file structure to a file. 
     284 
     285        The file is assumed to start empty. Updating individual objects can be  
     286        done using the napi interface, with nx.handle as the nexus file handle. 
    198287        """ 
    199288        self.open() 
    200289        links = [] 
    201         # Root node is special --- only write its children. 
    202         # TODO: maybe want to write root node attributes? 
    203         for entry in tree.nxentries.values(): 
     290        for entry in tree.entries.values(): 
    204291            links += self._writegroup(entry, path="") 
    205292        self._writelinks(links) 
     
    208295    def readpath(self, path): 
    209296        """ 
    210         Read the data on a particular file path. 
     297        Return the data on a particular file path. 
    211298 
    212299        Returns a numpy array containing the data, a python scalar, or a 
     
    222309    def _readdata(self, name): 
    223310        """ 
    224         Read a data node, returning SDS or NXlink depending on the 
    225         nature of the node. 
     311        Read a data object and return it as an NXfield or NXlink. 
    226312        """ 
    227313        # Finally some data, but don't read it if it is big 
    228314        # Instead record the location, type and size 
    229315        self.opendata(name) 
     316        attrs={} 
    230317        attrs = self.getattrs() 
    231         if 'target' in attrs and attrs['target'].nxdata != self.path: 
     318        if 'target' in attrs and attrs['target'] != self.path: 
    232319            # This is a linked dataset; don't try to load it. 
    233320            #print "read link %s->%s"%(path,attrs['target'].nxdata) 
    234             data = NXlinkdata(target=attrs['target'].nxdata, name=name) 
     321            data = NXlinkfield(target=attrs['target'], name=name) 
    235322        else: 
    236323            dims,type = self.getinfo() 
     
    243330            else: 
    244331                value = None 
    245             data = self.SDS(value=value,name=name,dtype=type,shape=dims, 
    246                             attrs=attrs,file=self,path=self.path) 
     332            data = NXfield(value=value,name=name,dtype=type,shape=dims,attrs=attrs) 
    247333        self.closedata() 
    248334        return data 
     
    254340    def _readchildren(self,n): 
    255341        children = {} 
    256         for dummy in range(n): 
     342        for _item in range(n): 
    257343            name,nxclass = self.getnextentry() 
    258             #print "name,class,path",name,nxclass,self.path 
    259344            if nxclass in self._skipgroups: 
    260345                pass # Skip known bogus classes 
    261             elif nxclass == 'SDS': 
     346            elif nxclass == 'SDS': # NXgetnextentry returns 'SDS' as the class for NXfields 
    262347                children[name] = self._readdata(name) 
    263             elif nxclass.startswith('NX'): 
    264                 self.opengroup(name,nxclass) 
    265                 children[name] = self._readgroup() 
    266                 self.closegroup() 
    267             else: # Bad entry; flag it but don't do anything 
    268 #                children[name] = self.Unknown(nxname=name,nxclass=nxclass) 
     348            else: 
    269349                self.opengroup(name,nxclass) 
    270350                children[name] = self._readgroup() 
     
    274354    def _readgroup(self): 
    275355        """ 
    276         Read the currently open group and all subgroups. 
    277         """ 
    278         # TODO: does it make sense to read without recursing? 
    279         # TODO: can we specify which NXclasses we are interested 
    280         # in and skip those of different classes? 
     356        Read the currently open group and return it as an NXgroup. 
     357        """ 
    281358        n,name,nxclass = self.getgroupinfo() 
    282         attrs = self.getattrs() 
    283         if 'target' in attrs and attrs['target'].nxdata != self.path: 
     359        attrs = {} 
     360        attrs = self.getattrs()         
     361        if 'target' in attrs and attrs['target'] != self.path: 
    284362            # This is a linked group; don't try to load it. 
    285             #print "read group link %s->%s"%(attrs['target'].nxdata,self.path) 
    286             group = self.NXlinkgroup(target=attrs['target'].nxdata, name=name) 
    287         else: 
    288             #print "read group",nxclass,"from",self.path 
     363            group = self.NXlinkgroup(target=attrs['target'], name=name) 
     364        else: 
    289365            children = self._readchildren(n) 
    290366            # If we are subclassed with a handler for the particular 
    291367            # NXentry class name use that constructor for the group 
    292368            # rather than the generic NXgroup class. 
    293             if hasattr(self,nxclass): 
    294                 factory = getattr(self,nxclass) 
    295                 #print 'factory', type(self), type(nxclass), type(factory) 
    296             else: 
    297                 factory = self.NXgroup 
    298             group = factory(nxclass=nxclass,nxname=name,attrs=attrs,entries=children) 
     369#            if hasattr(self,nxclass): 
     370#                factory = getattr(self,nxclass) 
     371#            else: 
     372#                factory = self.NXgroup 
     373            group = NXgroup(nxclass=nxclass,name=name,attrs=attrs,entries=children) 
    299374            # Build chain back structure 
    300             for node in children.values(): 
    301                 node.nxgroup = group 
     375            for obj in children.values(): 
     376                obj._group = group 
    302377        return group 
    303378     
    304379    def _readlinks(self, root, group): 
    305380        """ 
    306         Convert linked nodes into direct references. 
    307         """ 
    308         for entry in group.nxentries.values(): 
    309             if isinstance(entry, NXlinkdata) or isinstance(entry, NXlinkgroup): 
     381        Convert linked objects into direct references. 
     382        """ 
     383        for entry in group.entries.values(): 
     384            if isinstance(entry, NXlink): 
    310385                link = root 
    311386                try: 
     
    318393                self._readlinks(root, entry) 
    319394                 
    320     # Allow subclasses to override  
    321     def NXattr(self, *args, **kw): return NXattr(*args, **kw) 
    322     def SDS(self, *args, **kw): return SDS(*args, **kw) 
    323     def NXgroup(self,*args,**kw): return NXgroup(*args, **kw) 
    324     def NXroot(self,*args,**kw): return NXroot(*args, **kw) 
    325     def NXentry(self,*args,**kw): return NXentry(*args, **kw) 
    326     def NXdata(self,*args,**kw): return NXdata(*args, **kw) 
    327     def NXmonitor(self,*args,**kw): return NXmonitor(*args, **kw) 
    328     def NXsample(self,*args,**kw): return NXsample(*args, **kw) 
    329     def NXinstrument(self,*args,**kw): return NXinstrument(*args, **kw) 
    330     def NXaperture(self,*args,**kw): return NXaperture(*args,**kw) 
    331     def NXattenuator(self,*args,**kw): return NXattenuator(*args,**kw) 
    332     def NXbeam_stop(self,*args,**kw): return NXbeam_stop(*args,**kw) 
    333     def NXbending_magnet(self,*args,**kw): return NXbending_magnet(*args,**kw) 
    334     def NXcollimator(self,*args,**kw): return NXcollimator(*args,**kw) 
    335     def NXcrystal(self,*args,**kw): return NXcrystal(*args,**kw) 
    336     def NXdetector(self,*args,**kw): return NXdetector(*args,**kw) 
    337     def NXdisk_chopper(self,*args,**kw): return NXdisk_chopper(*args,**kw) 
    338     def NXfermi_chopper(self,*args,**kw): return NXfermi_chopper(*args,**kw) 
    339     def NXfilter(self,*args,**kw): return NXfilter(*args,**kw) 
    340     def NXflipper(self,*args,**kw): return NXflipper(*args,**kw) 
    341     def NXguide(self,*args,**kw): return NXguide(*args,**kw) 
    342     def NXinsertion_device(self,*args,**kw): return NXinsertion_device(*args,**kw) 
    343     def NXmirror(self,*args,**kw): return NXmirror(*args,**kw) 
    344     def NXmoderator(self,*args,**kw): return NXmoderator(*args,**kw) 
    345     def NXmonochromator(self,*args,**kw): return NXmonochromator(*args,**kw) 
    346     def NXpolarizer(self,*args,**kw): return NXpolarizer(*args,**kw) 
    347     def NXpositioner(self,*args,**kw): return NXpositioner(*args,**kw) 
    348     def NXsource(self,*args,**kw): return NXsource(*args,**kw) 
    349     def NXvelocity_selector(self,*args,**kw): return NXvelocity_selector(*args,**kw) 
    350     def NXevent_data(self,*args,**kw): return NXevent_data(*args,**kw) 
    351     def NXuser(self,*args,**kw): return NXuser(*args,**kw) 
    352     def NXprocess(self,*args,**kw): return NXprocess(*args,**kw) 
    353     def NXparameter(self,*args,**kw): return NXparameter(*args,**kw) 
    354     def NXcharacterization(self,*args,**kw): return NXcharacterization(*args,**kw) 
    355     def NXlog(self,*args,**kw): return NXlog(*args,**kw) 
    356     def NXnote(self,*args,**kw): return NXnote(*args,**kw) 
    357     def NXbeam(self,*args,**kw): return NXbeam(*args,**kw) 
    358     def NXgeometry(self,*args,**kw): return NXgeometry(*args,**kw) 
    359     def NXtranslation(self,*args,**kw): return NXtranslation(*args,**kw) 
    360     def NXshape(self,*args,**kw): return NXshape(*args,**kw) 
    361     def NXorientation(self,*args,**kw): return NXorientation(*args,**kw) 
    362     def NXenvironment(self,*args,**kw): return NXenvironment(*args,**kw) 
    363     def NXsensor(self,*args,**kw): return NXsensor(*args,**kw) 
    364     def NXlink(self, *args, **kw): return NXlink(*args, **kw) 
    365     def NXlinkdata(self, *args, **kw): return NXlinkdata(*args, **kw) 
    366     def NXlinkgroup(self, *args, **kw): return NXlinkgroup(*args, **kw) 
    367     def Unknown(self, *args, **kw): return Unknown(*args, **kw) 
    368  
    369395    def _writeattrs(self, attrs): 
    370396        """ 
    371         Return the attributes for the currently open group/data or for 
    372         the file if no group or data object is open. 
     397        Return the attributes for the currently open group/data. 
     398         
     399        If no group or data object is open, the file attributes are returned. 
    373400        """ 
    374401        for name,pair in attrs.iteritems(): 
    375             #print "write attrs",name,pair.nxtype,pair.nxdata 
    376             self.putattr(name,pair.nxdata,pair.nxtype) 
     402            self.putattr(name,pair.nxdata,pair.dtype) 
    377403 
    378404    def _writedata(self, data, path): 
    379405        """ 
    380         Write the given data node. 
     406        Write the given data to a file. 
    381407 
    382408        NXlinks cannot be written until the linked group is created, so 
     
    386412 
    387413        path = path + "/" + data.nxname 
    388         #print 'write data',path 
     414        shape = data.shape 
     415        if data.shape == (): shape = (1,) 
    389416 
    390417        # If the data is linked then 
     
    394421        # Finally some data, but don't read it if it is big 
    395422        # Instead record the location, type and size 
    396         #print "creating data",child.nxname,child.nxdims,child.nxtype 
    397423        #If the array size is too large, their product needs a long integer 
    398         if np.prod(data.nxdims) > 10000 or np.prod(data.nxdims) < 0: 
     424        if np.prod(shape) > 10000 or np.prod(shape) < 0: 
    399425            # Compress the fastest moving dimension of large datasets 
    400             slab_dims = np.ones(len(data.nxdims),'i') 
    401             slab_dims[-1] = data.nxdims[-1] 
    402             self.compmakedata(data.nxname, data.nxtype, data.nxdims, 
    403                               'lzw', slab_dims) 
     426            slab_dims = np.ones(len(shape),'i') 
     427            if shape[-1] < 100000: 
     428                slab_dims[-1] = shape[-1] 
     429            else:  
     430                slab_dims[-1] = 100000 
     431            self.compmakedata(data.nxname, data.dtype, shape, 'lzw', slab_dims) 
    404432        else: 
    405433            # Don't use compression for small datasets 
    406             self.makedata(data.nxname, data.nxtype, data.nxdims) 
     434            try: 
     435                self.makedata(data.nxname, data.dtype, shape) 
     436            except StandardError,errortype: 
     437                print "Error in tree, makedata: ",errortype 
     438 
    407439        self.opendata(data.nxname) 
    408         self._writeattrs(data.nxattrs) 
     440        self._writeattrs(data.attrs) 
    409441        value = data.nxdata 
    410442        if value is not None: 
    411             self.putdata(data.nxdata)                 
     443            self.putdata(data.nxdata) 
    412444        self.closedata() 
    413445        return [] 
     
    422454        """ 
    423455        path = path + "/" + group.nxname 
    424         #print 'write group',path 
    425456 
    426457        links = [] 
    427458        self.makegroup(group.nxname, group.nxclass) 
    428459        self.opengroup(group.nxname, group.nxclass) 
    429         self._writeattrs(group.nxattrs) 
     460        self._writeattrs(group.attrs) 
    430461        if hasattr(group, '_target'): 
    431462            links += [(path, group._target)] 
    432         for child in group.nxentries.values(): 
    433             if child.nxclass == 'SDS': 
     463        for child in group.entries.values(): 
     464            if child.nxclass == 'NXfield': 
    434465                links += self._writedata(child,path) 
    435466            elif hasattr(child,'_target'): 
     
    442473    def _writelinks(self, links): 
    443474        """ 
    444         Create links within the NeXus file as indicated by the set of pairs 
    445         returned by writegroup. 
     475        Create links within the NeXus file. 
     476         
     477        THese are defined by the set of pairs returned by _writegroup. 
    446478        """ 
    447479        gid = {} 
     
    472504                self.makelink(gid[target]) 
    473505 
    474 _pythontype={type(int()):'int32', type(long()):'int64',  
    475              type(float()):'float32', type(str()):'char'} 
    476  
    477 def _gettype(value): 
    478     if isinstance(value, np.generic) or isinstance(value, np.ndarray): 
    479         nxtype = value.dtype.name 
    480         nxdims = max(list(value.shape),[1]) 
    481     else: 
    482         try:       
    483             nxtype = _pythontype[type(value)] 
    484             #print "In api.tree._gettype(value), _pythontype[type(value)]: " + nxtype 
    485             if nxtype == 'char': 
    486                 nxdims = [len(value)] 
    487             else: 
    488                 nxdims = [1] 
    489         except KeyError: 
    490             nxtype = '' 
    491             nxdims = [1] 
    492     return nxtype, nxdims 
    493  
    494 def _settype(value, nxtype): 
    495     if nxtype == _gettype(value)[0]: 
    496         return value 
    497     elif nxtype == 'char': 
    498         return str(value) 
    499     else: 
    500         return value.astype(nxtype) 
    501      
    502  
    503 def getClass(classname, modulename="tree"): 
    504     return getattr(__import__(modulename, globals(), locals(), [classname]), classname) 
    505  
    506 def getAxes(axes): 
    507     """ 
    508     Parse the 'axes' attribute for the axis names. 
     506 
     507def _readaxes(axes): 
     508    """ 
     509    Return a list of axis names stored in the 'axes' attribute. 
    509510     
    510511    The delimiter separating each axis can be white space, a comma, or a colon. 
     
    512513    import re 
    513514    sep=re.compile('[\[]*(\s*,*:*)+[\]]*') 
    514     return filter(lambda x: len(x)>0, sep.split(axes.nxdata)) 
    515  
    516 def getRoot(node): 
    517     if node.nxgroup == None: 
    518         return None 
    519     elif node.nxgroup.nxclass == "NXroot": 
    520         return node.nxgroup 
    521     else: 
    522         return getRoot(node.nxgroup) 
     515    return filter(lambda x: len(x)>0, sep.split(axes)) 
     516 
     517 
     518class AttrDict(dict): 
     519 
     520    """ 
     521    A dictionary class to assign all attributes to the NXattr class. 
     522    """ 
     523     
     524    def __setitem__(self, key, value): 
     525        if isinstance(value, NXattr): 
     526            dict.__setitem__(self, key, value) 
     527        else: 
     528            dict.__setitem__(self, key, NXattr(value)) 
    523529 
    524530 
    525531class NXattr(object): 
    526     """ 
    527     Attributes need to keep track of nxtype as well as attribute value. 
    528     """ 
     532 
     533    """ 
     534    Class for NeXus attributes of a NXfield or NXgroup object. 
     535     
     536    This class is only used for NeXus attributes that are stored in a  
     537    NeXus file and helps to distinguish them from Python attributes.  
     538    There are two Python attributes for each NeXus attribute. 
     539     
     540    Python Attributes 
     541    ----------------- 
     542    nxdata : string, Numpy scalar, or Numpy ndarray 
     543        The value of the NeXus attribute. 
     544    dtype : string 
     545        The data type of the NeXus attribute. This is set to 'char' for 
     546        a string attribute or the string of the corresponding Numpy data type 
     547        for a numeric attribute. 
     548     
     549    NeXus Attributes 
     550    ---------------- 
     551    NeXus attributes are stored in the 'attrs' dictionary of the parent object, 
     552    NXfield or NXgroup, but can often be referenced or assigned using the 
     553    attribute name as if it were an object attribute.  
     554     
     555    For example, after assigning the NXfield, the following three attribute 
     556    assignments are all equivalent:: 
     557 
     558        >>> entry.sample.temperature = NXfield(40.0) 
     559        >>> entry.sample.temperature.attrs['units'] = 'K' 
     560        >>> entry.sample.temperature.units = NXattr('K') 
     561        >>> entry.sample.temperature.units = 'K' 
     562     
     563    The third version above is only allowed for NXfield attributes and is  
     564    not allowed if the attribute has the same name as one of the following 
     565    internally defined attributes, i.e., 
     566     
     567    ['entries', 'attrs', 'dtype','shape']  
     568     
     569    or if the attribute name begins with 'nx' or '_'. It is only possible to  
     570    reference attributes with one of the proscribed names using the 'attrs' 
     571    dictionary. 
     572 
     573    """ 
     574 
    529575    def __init__(self,value=None,dtype=''): 
    530576        if isinstance(value, NXattr): 
    531             self.nxdata,self.nxtype = value.nxdata,value.nxtype 
     577            self._data,self._dtype = value.nxdata,value.dtype 
    532578        elif dtype: 
    533             self.nxdata,self.nxtype = value,dtype 
    534         else: 
    535             self.nxdata,self.nxtype = value,'char' 
    536             if value: 
    537                 if isinstance(value, NXnode):  
    538                     raise NeXusError, "A data attribute cannot be an SDS or NeXus group" 
    539                 if isinstance(value, np.generic) or isinstance(value, np.ndarray): 
    540                     self.nxtype = value.dtype.name 
     579            if dtype in np.typeDict: 
     580                self._data,self._dtype = np.__dict__[dtype](value),dtype 
     581            elif dtype == 'char': 
     582                self._data,self._dtype = str(value),dtype 
     583            else: 
     584                raise NeXusError, "Invalid data type" 
     585        else: 
     586            if isinstance(value, str): 
     587                self._data,self._dtype = str(value), 'char' 
     588            elif value is not None: 
     589                if isinstance(value, NXobject):  
     590                    raise NeXusError, "A data attribute cannot be a NXfield or NXgroup" 
    541591                else: 
    542                     try:       
    543                         self.nxtype = _pythontype[type(value)] 
    544                     except KeyError: 
    545                         pass 
     592                    self._data = np.array(value) 
     593                self._dtype = self._data.dtype.name 
     594                if self._data.size == 1: 
     595                    self._data = np.__dict__[self._dtype](self._data) 
     596            else: 
     597                self._data,self._dtype = None, 'char' 
    546598 
    547599    def __str__(self): 
     
    549601 
    550602    def __repr__(self): 
    551         if self.nxtype == 'char': 
    552             return "NXattr('%s','%s')"%(self.nxdata,self.nxtype) 
    553         else: 
    554             return "NXattr(%s,'%s')"%(self.nxdata,self.nxtype) 
     603        if str(self.dtype) == 'char': 
     604            return "NXattr('%s')"%self.nxdata 
     605        else: 
     606            return "NXattr(%s)"%self.nxdata 
    555607 
    556608    def __eq__(self, other): 
     
    563615            return self.nxdata == other 
    564616 
    565  
    566 class NXnode(object): 
     617    def _getdata(self): 
     618        """ 
     619        Return the attribute value. 
     620        """ 
     621        return self._data 
     622     
     623    def _getdtype(self): 
     624        return self._dtype 
     625     
     626    nxdata = property(_getdata,doc="The attribute values") 
     627    dtype = property(_getdtype, "Data type of NeXus attribute") 
     628 
     629_npattrs = filter(lambda x: not x.startswith('_'), np.ndarray.__dict__.keys()) 
     630 
     631class NXobject(object): 
     632 
    567633    """ 
    568634    Abstract base class for elements in NeXus files. 
    569635     
    570     The node has a subclass of SDS (Scientific Data Set), NXgroup, or one  
    571     of the NXgroup subclasses. Child nodes should be accessible directly as  
    572     object attributes. Constructors for NXnode objects are defined by either  
    573     the SDS or NXgroup classes. 
    574      
    575     Attributes 
    576     ---------- 
     636    The object has a subclass of NXfield, NXgroup, or one of the NXgroup 
     637    subclasses. Child nodes should be accessible directly as object attributes. 
     638    Constructors for NXobject objects are defined by either the NXfield or 
     639    NXgroup classes. 
     640     
     641    Python Attributes 
     642    ----------------- 
    577643    nxclass : string 
    578         The class of the NXnode. NXnodes can have class SDS, NXgroup,  
    579         or one of the NXgroup subclasses.  
     644        The class of the NXobject. NXobjects can have class NXfield, NXgroup, or 
     645        be one of the NXgroup subclasses. 
    580646    nxname : string 
    581         The name of the NXnode.  
    582     nxgroup : NXnode 
    583         The parent NXgroup, if the node is defined as the attribute of parent 
    584         node. 
    585     nxentries : list 
    586         A list of all the NeXus objects contained within an NXgroup. This list 
    587         excludes all node attributes whose names begin with 'nx' or '_'. 
    588     nxattrs : list 
    589         A list of all the NeXus attributes, i.e., attribute with class NXattr. 
     647        The name of the NXobject. Since it is possible to reference the same  
     648        Python object multiple times, this is not necessarily the same as the 
     649        object name. However, if the object is part of a NeXus tree, this will 
     650        be the attribute name within the tree.  
     651    nxgroup : NXgroup 
     652        The parent group containing this object within a NeXus tree. If the  
     653        object is not part of any NeXus tree, it will be set to None. 
    590654    nxpath : string 
    591         The path to this node with respect to the root of the NeXus tree. For 
     655        The path to this object with respect to the root of the NeXus tree. For 
    592656        NeXus data read from a file, this will be a group of class NXroot, but 
    593657        if the NeXus tree was defined interactively, it can be any valid  
    594658        NXgroup. 
    595     """ 
    596     nxclass = "unknown" 
    597     nxname = "unknown" 
    598     nxgroup = None 
     659    nxroot : NXgroup 
     660        The root object of the NeXus tree containing this object. For 
     661        NeXus data read from a file, this will be a group of class NXroot, but 
     662        if the NeXus tree was defined interactively, it can be any valid  
     663        NXgroup. 
     664    nxfile : NeXusTree 
     665        The file handle of the root object of the NeXus tree containing this  
     666        object. 
     667    filename : string 
     668        The file name of NeXus object's tree file handle. 
     669    attrs : dict 
     670        A dictionary of the NeXus object's attributes. 
     671 
     672    Methods 
     673    ------- 
     674    dir(self, attrs=False, recursive=False): 
     675        Print the group directory. 
     676         
     677        The directory is a list of NeXus objects within this group, either NeXus 
     678        groups or NXfield data. If 'attrs' is True, NXfield attributes are 
     679        displayed. If 'recursive' is True, the contents of child groups are also 
     680        displayed. 
     681 
     682    tree: 
     683        Print the object's tree. 
     684         
     685        It invokes the 'dir' method with both 'attrs' and 'recursive'  
     686        set to True. Note that this method is defined as a property attribute and 
     687        does not require parentheses. 
     688 
     689    save(self, filename, format='w5') 
     690        Save the NeXus group into a file 
     691 
     692        The object is wrapped in an NXroot group (with name 'root') and an  
     693        NXentry group (with name 'entry'), if necessary, in order to produce  
     694        a valid NeXus file. 
     695 
     696    """ 
     697 
     698    _class = "unknown" 
     699    _name = "unknown" 
     700    _group = None 
     701    _file = None 
    599702 
    600703    def __str__(self): 
     
    602705 
    603706    def __repr__(self): 
    604         return "NXnode('%s','%s')"%(self.nxclass,self.nxname) 
    605          
    606     def __setattr__(self, name, value): 
    607         if not name.startswith('nx') and not name.startswith('_'): 
    608             if not isinstance(value, NXnode): 
    609                 value = SDS(value) 
    610             value.nxname=name 
    611             value.nxgroup=self.nxname 
    612         object.__setattr__(self, name, value) 
    613  
     707        return "NXobject('%s','%s')"%(self.nxclass,self.nxname) 
     708         
    614709    def _setattrs(self, attrs): 
    615         for k,v in attrs.items():           
    616             setattr(self, k, v) 
    617  
    618     def _attrs(self): 
    619         return dict([(k,v)  
    620                      for k,v in self.__dict__.items() 
    621                      if isinstance(v,NXattr)]) 
    622  
    623     def _entries(self): 
    624         return dict([(k,v)  
    625                      for k,v in self.__dict__.items() 
    626                      if isinstance(v,NXnode) and not k.startswith('nx') 
    627                         and not k.startswith('_')]) 
    628  
    629     nxattrs = property(_attrs,doc="NeXus attributes for node") 
    630     nxentries = property(_entries,doc="NeXus nodes within group") 
    631  
     710        for k,v in attrs.items(): 
     711            self._attrs[k] = v 
     712     
    632713    def _str_name(self,indent=0): 
    633         if self.nxclass == 'SDS': 
     714        if self.nxclass == 'NXfield': 
    634715            return " "*indent+self.nxname 
    635716        else: 
     
    640721 
    641722    def _str_attrs(self,indent=0): 
    642         attrs = self.nxattrs 
    643         names = attrs.keys() 
     723        names = self.attrs.keys() 
    644724        names.sort() 
    645725        result = [] 
    646726        for k in names: 
    647             result.append(" "*indent+"@%s = %s"%(k,attrs[k].nxdata)) 
     727            result.append(" "*indent+"@%s = %s"%(k,self.attrs[k].nxdata)) 
    648728        return "\n".join(result) 
    649729 
    650730    def _str_tree(self,indent=0,attrs=False,recursive=False): 
    651731        """ 
    652         Print current node and possibly subnodes. 
    653         """ 
    654         # Print node 
     732        Print current object and possibly children. 
     733        """ 
    655734        result = [self._str_name(indent=indent)] 
    656         if attrs and self.nxattrs:  
     735        if attrs and self.attrs:  
    657736            result.append(self._str_attrs(indent=indent+2)) 
    658737        # Print children 
    659         entries = self.nxentries 
     738        entries = self.entries 
    660739        if entries: 
    661740            names = entries.keys() 
     
    670749        result 
    671750        return "\n".join(result) 
     751 
     752    def dir(self,attrs=False,recursive=False): 
     753        """ 
     754        Print the object directory. 
     755         
     756        The directory is a list of NeXus objects within this object, either  
     757        NeXus groups or NXfields. If 'attrs' is True, NXfield attributes are  
     758        displayed. If 'recursive' is True, the contents of child groups are  
     759        also displayed. 
     760        """ 
     761        print self._str_tree(attrs=attrs,recursive=recursive) 
     762 
     763    @property 
     764    def tree(self): 
     765        """ 
     766        Print the directory tree. 
     767         
     768        The tree contains all child objects of this object and their children.  
     769        It invokes the 'dir' method with both 'attrs' and 'recursive' set  
     770        to True. 
     771        """ 
     772        print self._str_tree(attrs=True,recursive=True) 
     773         
     774    def treestr(self, attrs=True): 
     775        """Return directory tree string""" 
     776        return self._str_tree(attrs=attrs,recursive=True) 
     777     
     778    def save(self, filename=None, format='w5'): 
     779        """ 
     780        Save the NeXus object to a data file. 
     781         
     782        An error is raised if the object is an NXroot group from an external file  
     783        that has been opened as readonly and no file name is specified.   
     784         
     785        The object is wrapped in an NXroot group (with name 'root') and an  
     786        NXentry group (with name 'entry'), if necessary, in order to produce  
     787        a valid NeXus file. 
     788        """ 
     789        if self.nxclass == "NXroot": 
     790            tree = self 
     791            if filename is None: 
     792                if self._file: 
     793                    if self._file.mode == napi.ACC_READ: 
     794                        raise NeXusError, "NeXus file is readonly" 
     795                    else: 
     796                        self._file.close() 
     797                        filename = self._file.filename 
     798        elif filename: 
     799            if self.nxclass == "NXentry": 
     800                tree = NXroot(self) 
     801            else: 
     802                tree = NXroot(NXentry(self)) 
     803 
     804        if filename is None: 
     805            raise NeXusError, "No output file specified" 
     806 
     807        file = NeXusTree(filename, format) 
     808        file.writefile(tree) 
     809        file.close() 
     810        tree._file = file 
     811        tree._file.open() 
     812        tree._file.openpath('/') 
     813        tree._setattrs(tree._file.getattrs()) 
     814        tree._file.close() 
     815        return tree 
     816 
     817    def _getclass(self): 
     818        return self._class 
     819     
     820    def _getname(self): 
     821        return self._name 
     822     
     823    def _getgroup(self): 
     824        return self._group 
    672825     
    673826    def _getpath(self): 
    674827        if self.nxgroup is None: 
    675828            return "" 
     829        elif isinstance(self.nxgroup, NXroot): 
     830            return "/" + self.nxname 
    676831        else: 
    677832            return self.nxgroup._getpath()+"/"+self.nxname 
    678833     
    679     def _getheadnode(self): 
     834    def _getroot(self): 
    680835        if self.nxgroup is None: 
    681836            return self 
    682         else: 
    683             return self.nxgroup._getheadnode() 
    684      
    685     nxpath = property(_getpath, "Path to NeXus node") 
    686     nxhead = property(_getheadnode, "Head node of this object's tree") 
    687  
    688     def nxdir(self,attrs=False,recursive=False): 
    689         """ 
    690         Print the node directory. 
    691          
    692         The directory is a list of NeXus objects within this node, either  
    693         NeXus groups or SDS data. If 'attrs' is True, SDS attributes are  
    694         displayed. If 'recursive' is True, the contents of child groups are  
    695         also displayed. 
    696         """ 
    697         print self._str_tree(attrs=attrs,recursive=recursive) 
    698  
    699     def nxtree(self,attrs=True): 
    700         """ 
    701         Print the directory tree. 
    702          
    703         The tree contains all child objects of this node and their children.  
    704         It invokes the 'nxdir' method with both 'attrs' and 'recursive' set  
    705         to True. 
    706         """ 
    707         print self._str_tree(attrs=attrs,recursive=True) 
    708          
    709     def nxtreestr(self, attrs=True): 
    710         """Return directory tree string""" 
    711         return self._str_tree(attrs=attrs,recursive=True) 
    712      
    713     def nxsave(self, filename, format='w5'): 
    714         """ 
    715         Save the NeXus node to a data file. 
    716          
    717         The object is wrapped in an NXroot group (with name 'root') and an  
    718         NXentry group (with name 'entry'), if necessary, in order to produce  
    719         a valid NeXus file. 
    720         """ 
    721         #print "nxsave() call in tree.py NXnode class" 
    722         if self.nxclass == "NXroot": 
    723             tree = self 
    724         elif self.nxclass == "NXentry": 
    725             tree = NXroot(self) 
    726         else: 
    727             tree = NXroot(NXentry(self)) 
    728         file = NeXusTree(filename, format) 
    729         file.writefile(tree) 
    730         file.close() 
    731  
    732  
    733 class SDS(NXnode): 
    734     """ 
    735     A NeXus data node (Scientific Data Set). 
    736      
    737     This is a subclass of NXnode to contain scalar, array, or string data 
     837        elif isinstance(self.nxgroup, NXroot): 
     838            return self.nxgroup 
     839        else: 
     840            return self.nxgroup._getroot() 
     841     
     842    def _getfile(self): 
     843        return self.nxroot._file 
     844     
     845    def _getfilename(self): 
     846        return self.nxroot._file.filename 
     847     
     848    def _getattrs(self): 
     849        return self._attrs 
     850 
     851    nxclass = property(_getclass, doc="Class of NeXus object") 
     852    nxname = property(_getname, doc="Name of NeXus object") 
     853    nxgroup = property(_getgroup, doc="Parent group of NeXus object") 
     854    nxpath = property(_getpath, doc="Path to NeXus object") 
     855    nxroot = property(_getroot, doc="Root group of NeXus object's tree") 
     856    nxfile = property(_getfile, doc="File handle of NeXus object's tree") 
     857    filename = property(_getfilename, doc="File name of NeXus object's tree") 
     858    attrs = property(_getattrs, doc="NeXus attributes for an object") 
     859 
     860 
     861class NXfield(NXobject): 
     862 
     863    """ 
     864    A NeXus data field. 
     865     
     866    This is a subclass of NXobject that contains scalar, array, or string data 
    738867    and associated NeXus attributes.  
    739868     
    740     SDS(value=None, name='unknown', dtype='', shape=[], attrs={}, file=None,  
    741         path=None, group=None, **attr) 
    742  
    743     Parameters 
    744     ---------- 
     869    NXfield(value=None, name='unknown', dtype='', shape=[], attrs={}, file=None,  
     870            path=None, group=None, **attr) 
     871 
     872    Input Parameters 
     873    ---------------- 
    745874    value : scalar value, Numpy array, or string 
    746         The numerical or string value of the SDS, which is directly accessible  
    747         as the SDS attribute 'nxdata'.  
     875        The numerical or string value of the NXfield, which is directly 
     876        accessible as the NXfield attribute 'nxdata'. 
    748877    name : string 
    749         The name of the SDS, which is directly accessible as the SDS  
    750         attribute 'nxname'. If the SDS is initialized as the attribute of  
    751         a parent node, the name is automatically set to the name of this  
     878        The name of the NXfield, which is directly accessible as the NXfield 
     879        attribute 'name'. If the NXfield is initialized as the attribute of a 
     880        parent object, the name is automatically set to the name of this 
    752881        attribute. 
    753882    dtype : string 
    754         The data type of the SDS value, which is directly accessible as the  
    755         SDS attribute 'nxtype'. Valid types correspond to standard Numpy  
    756         data types, using names defined by the NeXus API, 
     883        The data type of the NXfield value, which is directly accessible as the 
     884        NXfield attribute 'dtype'. Valid input types correspond to standard 
     885        Numpy data types, using names defined by the NeXus API, 
    757886        i.e., 'float32' 'float64' 
    758887              'int8' 'int16' 'int32' 'int64' 
     
    762891        by the data type of the 'value' parameter. 
    763892    shape : list of ints 
    764         The dimensions of the SDS data, which is accessible as the SDS 
    765         attribute 'nxdims'. This corresponds to the shape of a Numpy array,  
    766         or the length of a string. The shape is [1] if the value is a scalar. 
     893        The dimensions of the NXfield data, which is accessible as the NXfield 
     894        attribute 'shape'. This corresponds to the shape of the Numpy array.  
     895        Scalars (numeric or string) are stored as Numpy zero-rank arrays, 
     896        for which shape=[]. 
    767897    attrs : dict 
    768         A dictionary containing SDS attributes. The dictionary values should 
     898        A dictionary containing NXfield attributes. The dictionary values should 
    769899        all have class NXattr. 
    770900    file : filename 
    771         The file from which the SDS has been read. 
     901        The file from which the NXfield has been read. 
    772902    path : string 
    773         The path to this node with respect to the root of the NeXus tree,  
     903        The path to this object with respect to the root of the NeXus tree,  
    774904        using the convention for unix file paths. 
    775     group : NXnode (SDS, NXgroup, or subclass of NXgroup) 
    776         The parent NeXus node, which is accessible as the SDS attribute  
    777         'nxgroup'. If the SDS is initialized as the attribute of  
    778         a parent node, the group is set to the parent node. 
    779      
    780     SDS attributes can also be set directly by keyword arguments, which are 
    781     converted to objects of class NXattr, e.g., 
    782      
    783         temperature = SDS(40.0, units='K') 
    784  
    785     Attributes 
    786     ---------- 
    787     nxclass : 'SDS' 
    788         The class of the NXnode. 
     905    group : NXgroup or subclass of NXgroup 
     906        The parent NeXus object. If the NXfield is initialized as the attribute 
     907        of a parent group, this attribute is automatically set to the parent group. 
     908 
     909    Python Attributes 
     910    ----------------- 
     911    nxclass : 'NXfield' 
     912        The class of the NXobject. 
    789913    nxname : string 
    790         The name of the SDS.  
    791     nxtype : string 
    792         The data type of the SDS value. Valid values are given above. 
    793     nxdims : list of ints 
    794         The dimensions of the SDS data. This is equivalent to the shape of a  
    795         Numpy array.  
    796     nxattrs : dict 
    797         A dictionary of the SDS attributes, i.e., those with class NXattr 
     914        The name of the NXfield. Since it is possible to reference the same  
     915        Python object multiple times, this is not necessarily the same as the 
     916        object name. However, if the field is part of a NeXus tree, this will 
     917        be the attribute name within the tree.  
     918    nxgroup : NXgroup 
     919        The parent group containing this field within a NeXus tree. If the  
     920        field is not part of any NeXus tree, it will be set to None. 
     921    dtype : string or Numpy dtype 
     922        The data type of the NXfield value. If the NXfield has been initialized 
     923        but the data values have not been read in or defined, this is a string. 
     924        Otherwise, it is set to the equivalent Numpy dtype.  
     925    shape : list or tuple of ints 
     926        The dimensions of the NXfield data. If the NXfield has been initialized 
     927        but the data values have not been read in or defined, this is a list of 
     928        ints. Otherwise, it is set to the equivalent Numpy shape, which is a 
     929        tuple. Scalars (numeric or string) are stored as Numpy zero-rank arrays, 
     930        for which shape=(). 
     931    attrs : dict 
     932        A dictionary of all the NeXus attributes associated with the field.  
     933        These are objects with class NXattr. 
    798934    nxdata : scalar, Numpy array or string 
    799         The data value of the SDS. This is normally initialized using the  
     935        The data value of the NXfield. This is normally initialized using the  
    800936        'value' parameter (see above). If the NeXus data is contained  
    801         in a file and the size of the SDS array is too large to be stored  
     937        in a file and the size of the NXfield array is too large to be stored  
    802938        in memory, the value is not read in until this attribute is directly 
    803939        accessed. Even then, if there is insufficient memory, a value of None 
    804         will be returned. In this case, the SDS array should be read as a  
    805         series of smaller slabs using 'nxget'.  
     940        will be returned. In this case, the NXfield array should be read as a  
     941        series of smaller slabs using 'get'.  
    806942    nxdata_as('units') : scalar value or Numpy array 
    807         If the SDS 'units' attribute has been set, the data values, stored 
     943        If the NXfield 'units' attribute has been set, the data values, stored 
    808944        in 'nxdata', are returned after conversion to the specified units. 
    809945    nxpath : string 
    810         The path to this node with respect to the root of the NeXus tree. For 
     946        The path to this object with respect to the root of the NeXus tree. For 
    811947        NeXus data read from a file, this will be a group of class NXroot, but 
    812948        if the NeXus tree was defined interactively, it can be any valid  
    813         NXgroup. This is determined by recursively accessing the 'nxgroup' 
    814         attributes of the parent nodes. 
    815  
     949        NXgroup. 
     950    nxroot : NXgroup 
     951        The root object of the NeXus tree containing this object. For 
     952        NeXus data read from a file, this will be a group of class NXroot, but 
     953        if the NeXus tree was defined interactively, it can be any valid  
     954        NXgroup. 
     955 
     956    NeXus Attributes 
     957    ---------------- 
     958    NeXus attributes are stored in the 'attrs' dictionary of the NXfield, but 
     959    can usually be assigned or referenced as if they are Python attributes, as 
     960    long as the attribute name is not the same as one of those listed above. 
     961    This is to simplify typing in an interactive session and should not cause 
     962    any problems because there is no name clash with attributes so far defined 
     963    within the NeXus standard. When writing modules, it is recommended that the 
     964    attributes always be referenced using the 'attrs' dictionary if there is 
     965    any doubt. 
     966     
     967    a) Assigning a NeXus attribute 
     968     
     969    In the example below, after assigning the NXfield, the following three 
     970    NeXus attribute assignments are all equivalent: 
     971 
     972        >>> entry.sample.temperature = NXfield(40.0) 
     973        >>> entry.sample.temperature.attrs['units'] = 'K' 
     974        >>> entry.sample.temperature.units = NXattr('K') 
     975        >>> entry.sample.temperature.units = 'K' 
     976     
     977    b) Referencing a NeXus attribute 
     978     
     979    If the name of the NeXus attribute is not the same as any of the Python 
     980    attributes listed above, or one of the methods listed below, or any of the  
     981    attributes defined for Numpy arrays, they can be referenced as if they were 
     982    a Python attribute of the NXfield. However, it is only possible to reference 
     983    attributes with one of the proscribed names using the 'attrs' dictionary. 
     984     
     985        >>> entry.sample.temperature.tree = 10.0 
     986        >>> entry.sample.temperature.tree 
     987        temperature = 40.0 
     988          @tree = 10.0 
     989          @units = K 
     990        >>> entry.sample.temperature.attrs['tree'] 
     991        NXattr(10.0) 
     992 
     993    Numerical Operations on NXfields 
     994    -------------------------------- 
     995    NXfields usually consist of arrays of numeric data with associated 
     996    meta-data, the NeXus attributes. The exception is when they contain 
     997    character strings. This makes them similar to Numpy arrays, and this module 
     998    allows the use of NXfields in numerical operations in the same way as Numpy 
     999    ndarrays. NXfields are technically not a sub-class of the ndarray class, but 
     1000    most Numpy operations work on NXfields, returning either another NXfield or, 
     1001    in some cases, an ndarray that can easily be converted to an NXfield. 
     1002     
     1003        >>> x = NXfield((1.0,2.0,3.0,4.0)) 
     1004        >>> print x+1 
     1005        [ 2.  3.  4.  5.] 
     1006        >>> print 2*x 
     1007        [ 2.  4.  6.  8.] 
     1008        >>> print x/2 
     1009        [ 0.5  1.   1.5  2. ] 
     1010        >>> print x**2 
     1011        [  1.   4.   9.  16.] 
     1012        >>> print x.reshape((2,2)) 
     1013        [[ 1.  2.] 
     1014         [ 3.  4.]] 
     1015        >>> y = NXfield((0.5,1.5,2.5,3.5)) 
     1016        >>> x+y 
     1017        NXfield(name=x,value=[ 1.5  3.5  5.5  7.5]) 
     1018        >>> x*y 
     1019        NXfield(name=x,value=[  0.5   3.    7.5  14. ]) 
     1020        >>> (x+y).shape 
     1021        (4,) 
     1022        >>> (x+y).dtype 
     1023        dtype('float64') 
     1024 
     1025    All these operations return valid NXfield objects containing the same 
     1026    attributes as the first NXobject in the expression. The 'reshape' and  
     1027    'transpose' methods also return NXfield objects. 
     1028     
     1029    It is possible to use the standard slice syntax. 
     1030 
     1031        >>> x=NXfield(np.linspace(0,10,11)) 
     1032        >>> x 
     1033        NXfield([  0.   1.   2. ...,   8.   9.  10.]) 
     1034        >>> x[2:5] 
     1035        NXfield([ 2.  3.  4.]) 
     1036     
     1037    In addition, it is possible to use floating point numbers as the slice 
     1038    indices. If one of the indices is not integer, both indices are used to 
     1039    extract elements in the array with values between the two index values. 
     1040     
     1041        >>> x=NXfield(np.linspace(0,100.,11)) 
     1042        >>> x 
     1043        NXfield([   0.   10.   20. ...,   80.   90.  100.]) 
     1044        >>> x[20.:50.] 
     1045        NXfield([ 20.  30.  40.  50.]) 
     1046    
     1047    The standard Numpy ndarray attributes and methods will also work with  
     1048    NXfields, but will return scalars or Numpy arrays. 
     1049     
     1050        >>> x.size 
     1051        4 
     1052        >>> x.sum() 
     1053        10.0 
     1054        >>> x.max() 
     1055        4.0 
     1056        >>> x.mean() 
     1057        2.5 
     1058        >>> x.var() 
     1059        1.25 
     1060        >>> x.reshape((2,2)).sum(1) 
     1061        array([ 3.,  7.]) 
     1062     
     1063    Finally, NXfields are cast as ndarrays for operations that require them. 
     1064    The returned value will be the same as for the equivalent ndarray  
     1065    operation, e.g., 
     1066     
     1067    >>> np.sin(x) 
     1068    array([ 0.84147098,  0.90929743,  0.14112001, -0.7568025 ]) 
     1069    >>> np.sqrt(x) 
     1070    array([ 1.        ,  1.41421356,  1.73205081,  2.        ]) 
     1071     
    8161072    Methods 
    8171073    ------- 
    818     nxdir(self, attrs=False): 
    819         Print the SDS specification. 
    820          
    821         This outputs the name, dimensions and data type of the SDS.  
    822         If 'attrs' is True, SDS attributes are displayed. 
    823  
    824     nxtree(self, attrs=True): 
    825         Print the SDS and its attributes. 
    826          
    827         It invokes the 'nxdir' method with 'attrs' set to True. 
    828          
    829     nxsave(filename, format='w5') 
    830         Save the SDS into a file wrapped in a NXroot group and NXentry group 
     1074    dir(self, attrs=False): 
     1075        Print the NXfield specification. 
     1076         
     1077        This outputs the name, dimensions and data type of the NXfield.  
     1078        If 'attrs' is True, NXfield attributes are displayed. 
     1079 
     1080    tree: 
     1081        Print the NXfield's tree. 
     1082         
     1083        It invokes the 'dir' method with both 'attrs' and 'recursive'  
     1084        set to True. Note that this method is defined as a property attribute and 
     1085        does not require parentheses. 
     1086         
     1087         
     1088    save(self, filename, format='w5') 
     1089        Save the NXfield into a file wrapped in a NXroot group and NXentry group 
    8311090        with default names. This is equivalent to  
    8321091         
    833         >>> NXroot(NXentry(SDS(...))).nxsave(filename) 
     1092        >>> NXroot(NXentry(NXfield(...))).save(filename) 
    8341093 
    8351094    Examples 
    8361095    -------- 
    837     >>> x = SDS(np.linspace(0,2*np.pi,101), units='degree') 
     1096    >>> x = NXfield(np.linspace(0,2*np.pi,101), units='degree') 
    8381097    >>> phi = x.nxdata_as(units='radian') 
    839     >>> y = SDS(np.sin(phi)) 
     1098    >>> y = NXfield(np.sin(phi)) 
    8401099     
    8411100    # Read a Ni x Nj x Nk array one vector at a time 
    8421101    >>> with root.NXentry[0].data.data as slab: 
    843             Ni,Nj,Nk = slab.nxdims 
     1102            Ni,Nj,Nk = slab.shape 
    8441103            size = [1,1,Nk] 
    8451104            for i in range(Ni): 
    8461105                for j in range(Nj): 
    847                     value = slab.nxget([i,j,0],size) 
    848  
    849     """ 
    850     def __init__(self, value=None, name='unknown', dtype='', shape=(), attrs={}, 
    851                  file=None, path=None, group=None, **attr): 
     1106                    value = slab.get([i,j,0],size) 
     1107 
     1108    """ 
     1109 
     1110    def __init__(self, value=None, name='unknown', dtype=None, shape=(), attrs={}, group=None,  
     1111                 **attr): 
    8521112        if isinstance(value, list) or isinstance(value, tuple): 
    8531113            value = np.array(value) 
    8541114        self._value = value 
    855         self.nxclass = 'SDS' # Scientific Data Set 
    856         self.nxname = name 
    857         self.nxtype = '' 
    858         self.nxdims = list(shape) 
    859         #print "self.nxtype: " + str(self.nxtype) 
    860         if dtype: 
    861             self.nxtype = dtype 
    862             #print "self.nxtype: " + str(self.nxtype) 
    863         elif value is not None: 
    864             #print "Type declared in SDS.__init__(): " + str(_gettype(value)) 
    865             self.nxtype, self.nxdims = _gettype(value) 
    866         else: 
    867             self.nxtype = '' 
    868         self.nxgroup = group 
     1115        self._class = 'NXfield' 
     1116        self._name = name 
     1117        self._group = group 
     1118        self._dtype = dtype 
     1119        if dtype == 'char': 
     1120            self._dtype = 'char' 
     1121        elif dtype in np.typeDict: 
     1122            self._dtype = np.dtype(dtype) 
     1123        elif dtype: 
     1124            raise NeXusError, "Invalid data type: %s" % dtype 
     1125        self._shape = tuple(shape) 
    8691126        # Append extra keywords to the attribute list 
     1127        self._attrs = AttrDict() 
    8701128        for key in attr.keys(): 
    871             attrs[key] = NXattr(attr[key]) 
     1129            attrs[key] = attr[key] 
    8721130        # Convert NeXus attributes to python attributes 
    8731131        self._setattrs(attrs) 
    8741132        if 'units' in attrs: 
    875             units = attrs['units'].nxdata 
     1133            units = attrs['units'] 
    8761134        else: 
    8771135            units = None 
    878         self._converter = unit.Converter(units) 
    8791136        self._incontext = False 
    880         self._file = file 
    881         self._path = path 
    882         for key in attrs.keys(): 
    883             del attrs[key] 
     1137        del attrs 
     1138        if value is not None and dtype == 'char': value = str(value) 
     1139        self._setdata(value) 
    8841140 
    8851141    def __repr__(self): 
    8861142        if self._value is not None: 
    887             return "SDS(name=%s,value=%s)" % (self.nxname, self._str_value()) 
    888         else: 
    889             return "SDS(name='%s')" % self.nxname 
    890          
     1143            if str(self.dtype) == 'char': 
     1144                return "NXfield('%s')" % str(self) 
     1145            else: 
     1146                return "NXfield(%s)" % self._str_value() 
     1147        else: 
     1148            return "NXfield(dtype=%s,shape=%s)" % (self.dtype,self.shape) 
     1149         
     1150    def __getattr__(self, name): 
     1151        """ 
     1152        Enable standard numpy ndarray attributes if not otherwise defined. 
     1153        """ 
     1154        if name in _npattrs: 
     1155            return self.nxdata.__getattribute__(name) 
     1156        elif name in self.attrs: 
     1157            return self.attrs[name].nxdata 
     1158        raise KeyError(name+" not in "+self.nxname) 
     1159 
    8911160    def __setattr__(self, name, value): 
    892         if name.startswith('nx') or name.startswith('_') or isinstance(value, NXattr): 
     1161        """ 
     1162        Add an attribute to the NXfield 'attrs' dictionary unless the attribute 
     1163        name starts with 'nx' or '_', or unless it is one of the standard Python 
     1164        attributes for the NXfield class. 
     1165        """ 
     1166        if name.startswith('_'): 
    8931167            object.__setattr__(self, name, value) 
    894         else: 
    895             object.__setattr__(self, name, NXattr(value)) 
    896  
    897     def __len__(self): 
    898         """ 
    899         Return the length of the SDS data. 
    900         """ 
    901         return np.prod(self.nxdims) 
    902          
    903     def index(self, value, max=False): 
    904         """ 
    905         Return the index of the SDS nxdata array that is greater than or equal to the value. 
    906          
    907         If max, then return the index that is less than or equal to the value. 
    908         This should only be used on one-dimensional monotonically increasing arrays. 
    909         """ 
    910         if max: 
    911             return len(self.nxdata)-len(self.nxdata[self.nxdata>=value]) 
    912         else: 
    913             return len(self.nxdata[self.nxdata<value]) 
     1168        elif isinstance(value, NXattr): 
     1169            self._attrs[name] = value 
     1170        else: 
     1171            self._attrs[name] = NXattr(value) 
    9141172 
    9151173    def __getitem__(self, index): 
    9161174        """ 
    917         Returns a slice from the SDS. 
    918          
    919         In most cases, the slice values are applied to the SDS nxdata array 
    920         and returned within an SDS object with the same metadata. However, 
     1175        Returns a slice from the NXfield. 
     1176         
     1177        In most cases, the slice values are applied to the NXfield nxdata array 
     1178        and returned within an NXfield object with the same metadata. However, 
    9211179        if the array is one-dimensional and the index start and stop values  
    9221180        are real, the nxdata array is returned with values between those limits. 
     
    9311189            result = self.nxdata.__getitem__(index) 
    9321190        else: 
    933             offset = np.zeros(len(self.nxdims),dtype=int) 
    934             size = np.array(self.nxdims) 
     1191            offset = np.zeros(len(self.shape),dtype=int) 
     1192            size = np.array(self.shape) 
    9351193            if isinstance(index, int): 
    9361194                offset[0] = index 
     
    9481206                    i = i + 1 
    9491207            try: 
    950                 result = self.nxget(offset, size) 
     1208                result = self.get(offset, size) 
    9511209            except ValueError: 
    9521210                result = self.nxdata.__getitem__(index) 
    953         return SDS(result, name=self.nxname, attrs=self.nxattrs) 
     1211        return NXfield(result, name=self.nxname, attrs=self.attrs) 
    9541212                 
    9551213    def __setitem__(self, index, value): 
    9561214        """ 
    957         Assign a slice to the SDS. 
     1215        Assign a slice to the NXfield. 
    9581216        """ 
    9591217        if self._value is not None: 
    9601218            self.nxdata[index] = value 
    9611219        else: 
    962             raise NeXusError, "SDS dataspace not yet allocated" 
     1220            raise NeXusError, "NXfield dataspace not yet allocated" 
     1221                 
     1222    def __deepcopy__(self, memo): 
     1223        dpcpy = self.__class__() 
     1224        memo[id(self)] = dpcpy 
     1225        dpcpy._value = copy(self._value) 
     1226        dpcpy._name = copy(self.nxname) 
     1227        dpcpy._dtype = copy(self.dtype) 
     1228        dpcpy._shape = copy(self.shape) 
     1229        for k, v in self.attrs.items(): 
     1230            dpcpy.attrs[k] = copy(v) 
     1231        return dpcpy 
     1232 
     1233    def __len__(self): 
     1234        """ 
     1235        Return the length of the NXfield data. 
     1236        """ 
     1237        return np.prod(self.shape)         
     1238         
     1239    def index(self, value, max=False): 
     1240        """ 
     1241        Return the index of the NXfield nxdata array that is greater than or equal to the value. 
     1242         
     1243        If max, then return the index that is less than or equal to the value. 
     1244        This should only be used on one-dimensional monotonically increasing arrays. 
     1245        """ 
     1246        if max: 
     1247            return len(self.nxdata)-len(self.nxdata[self.nxdata>=value]) 
     1248        else: 
     1249            return len(self.nxdata[self.nxdata<value]) 
     1250 
     1251    def __array__(self): 
     1252        """ 
     1253        Cast the NXfield as an array when it is expected by numpy 
     1254        """ 
     1255        return self.nxdata 
    9631256                 
    9641257    def __eq__(self, other): 
    9651258        """ 
    966         Return true if the values of the SDS are the same. 
    967         """ 
    968         if isinstance(other, SDS): 
     1259        Return true if the values of the NXfield are the same. 
     1260        """ 
     1261        if isinstance(other, NXfield): 
    9691262            if isinstance(self.nxdata, np.ndarray) and isinstance(other.nxdata, np.ndarray): 
    9701263                return all(self.nxdata == other.nxdata) 
     
    9761269    def __ne__(self, other): 
    9771270        """ 
    978         Return true if the values of the SDS are not the same. 
    979         """ 
    980         if isinstance(other, SDS): 
     1271        Return true if the values of the NXfield are not the same. 
     1272        """ 
     1273        if isinstance(other, NXfield): 
    9811274            if isinstance(self.nxdata, np.ndarray) and isinstance(other.nxdata, np.ndarray): 
    9821275                return any(self.nxdata != other.nxdata) 
     
    9881281    def __add__(self, other): 
    9891282        """ 
    990         Return the sum of the SDS and another SDS or number. 
     1283        Return the sum of the NXfield and another NXfield or number. 
    9911284        """ 
    9921285        try: 
    993             if isinstance(other, SDS): 
    994                 return SDS(value=self.nxdata+other.nxdata, name=self.nxname,  
    995                            attrs=self.nxattrs) 
     1286            if isinstance(other, NXfield): 
     1287                return NXfield(value=self.nxdata+other.nxdata, name=self.nxname,  
     1288                               attrs=self.attrs) 
    9961289            else: 
    997                 return SDS(value=self.nxdata+other, name=self.nxname,  
    998                            attrs=self.nxattrs) 
     1290                return NXfield(value=self.nxdata+other, name=self.nxname,  
     1291                               attrs=self.attrs) 
    9991292        except TypeError, message: 
    10001293            raise NeXusError, message 
     
    10021295    def __radd__(self, other): 
    10031296        """ 
    1004         Return the sum of the SDS and another SDS or number. 
     1297        Return the sum of the NXfield and another NXfield or number. 
    10051298         
    10061299        This variant makes __add__ commutative. 
     
    10101303    def __sub__(self, other): 
    10111304        """ 
    1012         Return the SDS with the subtraction of another SDS or number. 
     1305        Return the NXfield with the subtraction of another NXfield or number. 
    10131306        """ 
    10141307        try: 
    1015             if isinstance(other, SDS): 
    1016                 return SDS(value=self.nxdata-other.nxdata, name=self.nxname,  
    1017                            attrs=self.nxattrs) 
     1308            if isinstance(other, NXfield): 
     1309                return NXfield(value=self.nxdata-other.nxdata, name=self.nxname,  
     1310                               attrs=self.attrs) 
    10181311            else: 
    1019                 return SDS(value=self.nxdata-other, name=self.nxname,  
    1020                            attrs=self.nxattrs) 
     1312                return NXfield(value=self.nxdata-other, name=self.nxname,  
     1313                               attrs=self.attrs) 
    10211314        except TypeError, message: 
    10221315            raise NeXusError, message 
     
    10241317    def __mul__(self, other): 
    10251318        """ 
    1026         Return the product of the SDS and another SDS or number. 
     1319        Return the product of the NXfield and another NXfield or number. 
    10271320        """ 
    10281321        try: 
    1029             if isinstance(other, SDS): 
    1030                 return SDS(value=self.nxdata*other.nxdata, name=self.nxname,  
    1031                            attrs=self.nxattrs) 
     1322            if isinstance(other, NXfield): 
     1323                return NXfield(value=self.nxdata*other.nxdata, name=self.nxname,  
     1324                               attrs=self.attrs) 
    10321325            else: 
    1033                 return SDS(value=self.nxdata*other, name=self.nxname,  
    1034                            attrs=self.nxattrs) 
     1326                return NXfield(value=self.nxdata*other, name=self.nxname,  
     1327                               attrs=self.attrs) 
    10351328        except TypeError, message: 
    10361329            raise NeXusError, message 
     
    10381331    def __rmul__(self, other): 
    10391332        """ 
    1040         Return the product of the SDS and another SDS or number. 
     1333        Return the product of the NXfield and another NXfield or number. 
    10411334         
    10421335        This variant makes __mul__ commutative. 
     
    10461339    def __div__(self, other): 
    10471340        """ 
    1048         Return the SDS divided by another SDS or number. 
     1341        Return the NXfield divided by another NXfield or number. 
    10491342        """ 
    10501343        try: 
    1051             if isinstance(other, SDS): 
    1052                 return SDS(value=self.nxdata/other.nxdata, name=self.nxname,  
    1053                            attrs=self.nxattrs) 
     1344            if isinstance(other, NXfield): 
     1345                return NXfield(value=self.nxdata/other.nxdata, name=self.nxname,  
     1346                               attrs=self.attrs) 
    10541347            else: 
    1055                 return SDS(value=self.nxdata/other, name=self.nxname,  
    1056                            attrs=self.nxattrs) 
     1348                return NXfield(value=self.nxdata/other, name=self.nxname,  
     1349                               attrs=self.attrs) 
    10571350        except TypeError, message: 
    10581351            raise NeXusError, message 
     
    10601353    def __rdiv__(self, other): 
    10611354        """ 
    1062         Return the inverse of the SDS divided by another SDS or number. 
     1355        Return the inverse of the NXfield divided by another NXfield or number. 
    10631356        """ 
    10641357        try: 
    1065             if isinstance(other, SDS): 
    1066                 return SDS(value=other.nxdata/self.nxdata, name=self.nxname,  
    1067                            attrs=self.nxattrs) 
     1358            if isinstance(other, NXfield): 
     1359                return NXfield(value=other.nxdata/self.nxdata, name=self.nxname,  
     1360                               attrs=self.attrs) 
    10681361            else: 
    1069                 return SDS(value=other/self.nxdata, name=self.nxname,  
    1070                            attrs=self.nxattrs) 
     1362                return NXfield(value=other/self.nxdata, name=self.nxname,  
     1363                               attrs=self.attrs) 
    10711364        except TypeError, message: 
    10721365            raise NeXusError, message 
    10731366 
    1074     def nxreshape(self, shape): 
    1075         """ 
    1076         Returns the SDS with the defined shape 
    1077         """ 
    1078         self.nxdims = list(shape) 
     1367    def __pow__(self, power): 
     1368        """ 
     1369        Return the NXfield raised to the specified power. 
     1370        """ 
     1371        try: 
     1372            return NXfield(value=pow(self.nxdata,power), name=self.nxname,  
     1373                           attrs=self.attrs) 
     1374        except TypeError, message: 
     1375            raise NeXusError, message 
     1376 
     1377    def reshape(self, shape): 
     1378        """ 
     1379        Returns an NXfield with shape. 
     1380        """ 
     1381        self.shape = list(shape) 
    10791382        if isinstance(self.nxdata, np.ndarray): self.nxdata.shape = shape 
    10801383        return self 
    10811384 
    1082     def nxsum(self, axis=None): 
    1083         """ 
    1084         Returns a scalar value or Numpy array containing the data summed  
    1085         along one or more axes. The default is to return a sum over the entire 
    1086         array. Equivalent to the Numpy ndarray.sum. 
    1087         """ 
    1088         return self.nxdata.sum(axis) 
    1089  
    1090     def nxtranspose(self,shape=None):#added shape variable here 9/8/2010 
    1091         """ 
    1092         Returns an SDS containing the transpose of the data array. Equivalent  
     1385    def transpose(self,shape=None):#added shape variable here 9/8/2010 
     1386        """ 
     1387        Returns an NXfield containing the transpose of the data array. Equivalent  
    10931388        to the Numpy ndarray.transpose. 
    10941389        """ 
    10951390        # error, no shape variable in this scope 
    1096         return SDS(value=self.nxdata.transpose(shape), name=self.nxname,  
    1097                    attrs=self.nxattrs) 
    1098  
    1099     def nxvar(self, axis=None): 
    1100         return SDS(value=self.nxdata.var(axis), name="variance",  
    1101                    attrs=self.nxattrs) 
    1102  
    1103     def nxcenters(self): 
    1104         """ 
    1105         Returns an SDS with the centers of a single axis  
     1391        return NXfield(value=self.nxdata.transpose(shape), name=self.nxname,  
     1392                       attrs=self.attrs) 
     1393 
     1394    def centers(self): 
     1395        """ 
     1396        Returns an NXfield with the centers of a single axis  
    11061397        assuming it contains bin boundaries. 
    11071398        """ 
    1108         return SDS((self.nxdata[:-1]+self.nxdata[1:])/2, 
    1109                     name=self.nxname,attrs=self.nxattrs) 
     1399        return NXfield((self.nxdata[:-1]+self.nxdata[1:])/2, 
     1400                        name=self.nxname,attrs=self.attrs) 
    11101401 
    11111402    def __enter__(self): 
     
    11201411        # file cursor when in the slab context. 
    11211412        # TODO: if HDF allows multiple cursors, extend napi to support them 
    1122         self._close_on_exit = not self._file.isopen 
    1123         self._file.open() # Force file open even if closed 
    1124         self._file.openpath(self._path) 
     1413        self._close_on_exit = not self.nxroot._file.isopen 
     1414        self.nxroot._file.open() # Force file open even if closed 
     1415        self.nxroot._file.openpath(self.nxpath) 
    11251416        self._incontext = True 
    11261417        return self 
     
    11321423        self._incontext = False 
    11331424        if self._close_on_exit: 
    1134             self._file.close() 
    1135  
    1136     def nxget(self, offset, size, units=""): 
     1425            self.nxroot._file.close() 
     1426 
     1427    def get(self, offset, size): 
    11371428        """ 
    11381429        Return a slab from the data array. 
     
    11411432        Offset and shape must each have one entry per dimension. 
    11421433         
    1143         If units are specified, convert the values to the given units 
    1144         before returning them. 
    1145  
    11461434        This operation should be performed in a "with group.data" 
    11471435        context. 
     
    11511439        Corresponds to NXgetslab(handle,data,offset,shape) 
    11521440        """ 
    1153         self.__enter__() 
    1154         value = self._file.getslab(offset,size) 
    1155         self.__exit__() 
    1156         return self._converter(value,units) 
    1157  
    1158     def nxput(self, data, offset): 
     1441        if self.nxroot._file: 
     1442            self.__enter__() 
     1443            value = self.nxroot._file.getslab(offset,size) 
     1444            self.__exit__() 
     1445            return value 
     1446        else: 
     1447            raise NeXusError, "No input file specified for NXgetslab" 
     1448 
     1449    def put(self, data, offset, refresh=True): 
    11591450        """ 
    11601451        Put a slab into the data array. 
     
    11661457        context. 
    11671458 
    1168         Raises ValueError if this fails.  No error is raised when 
     1459        Raises NeXusError if this fails.  No error is raised when 
    11691460        writing to a file which is open read-only. 
    11701461 
    11711462        Corresponds to NXputslab(handle,data,offset,shape) 
    11721463        """ 
    1173         self.__enter__() 
    1174         self._file.putslab(data, offset, data.shape) 
    1175         self.__exit__() 
    1176          
     1464        if self.nxroot._file: 
     1465            if self.nxroot._file.mode == napi.ACC_READ: 
     1466                raise NeXusError, "NeXus file is readonly" 
     1467            self.__enter__() 
     1468            if isinstance(data, NXfield): 
     1469                self.nxroot._file.putslab(data.nxdata.astype(self.dtype), offset,  
     1470                                        data.shape) 
     1471            else: 
     1472                data = np.array(data) 
     1473                self.nxroot._file.putslab(data.astype(self.dtype), offset,  
     1474                                        data.shape) 
     1475            self.__exit__() 
     1476            if refresh: self.refresh() 
     1477        else: 
     1478            raise NeXusError, "No output file specified for NXputslab" 
     1479         
     1480    def add(self, data, offset): 
     1481        """ 
     1482        Add a slab into the data array. 
     1483 
     1484        Calls get to read in existing data before adding the value 
     1485        and calling put. It assumes that the two sets of data have 
     1486        compatible data types. 
     1487        """ 
     1488        if isinstance(data, NXfield): 
     1489            value = self.get(offset, data.shape)         
     1490            self.put(data.nxdata.astype(self.dtype)+value, offset) 
     1491        else: 
     1492            value = self.get(offset, data.shape) 
     1493            self.put(data.astype(self.dtype)+value, offset) 
     1494         
     1495    def refresh(self): 
     1496        """ 
     1497        Rereads the data from the file. 
     1498 
     1499        Calls net to read in data again. If put has been called, then 
     1500        nxdata is no longer synchronized with the file making a refresh 
     1501        necessary. This will only be performed if nxdata already stores the  
     1502        data. 
     1503        """ 
     1504        if self._value is not None: 
     1505            if self.nxroot._file: 
     1506                self._value = self.nxroot._file.readpath(self.nxpath) 
     1507            else: 
     1508                raise NeXusError, "No file specified for reads" 
     1509         
     1510    def convert(self, units=""): 
     1511        """ 
     1512        Return the data in particular units. 
     1513        """ 
     1514        try: 
     1515            import units 
     1516        except ImportError: 
     1517            raise NeXusError, "No conversion utility available" 
     1518        if self._value is not None: 
     1519            return self._converter(self._value,units) 
     1520        else: 
     1521            return None 
     1522     
    11771523    def __str__(self): 
    11781524        """ 
     
    11921538 
    11931539    def _str_tree(self,indent=0,attrs=False,recursive=False): 
    1194         dims = 'x'.join([str(n) for n in self.nxdims]) 
    1195         #return "%s(%s)"%(self.nxtype, dims) 
     1540        dims = 'x'.join([str(n) for n in self.shape]) 
    11961541        s = str(self) 
    11971542        if '\n' in s or s == "": 
    1198             s = "%s(%s)"%(self.nxtype, dims) 
     1543            s = "%s(%s)"%(self.dtype, dims) 
    11991544        v=[" "*indent + "%s = %s"%(self.nxname, s)] 
    1200         if attrs and self.nxattrs: v.append(self._str_attrs(indent=indent+2)) 
     1545        if attrs and self.attrs: v.append(self._str_attrs(indent=indent+2)) 
    12011546        return "\n".join(v) 
    12021547 
    1203     def nxaxes(self): 
    1204         """ 
    1205         Return a list of SDSs containing axes. 
    1206          
    1207         Only works if the SDS has the 'axes' attribute 
     1548    def _getaxes(self): 
     1549        """ 
     1550        Return a list of NXfields containing axes. 
     1551         
     1552        Only works if the NXfield has the 'axes' attribute 
    12081553        """ 
    12091554        try: 
    1210             return [getattr(self.nxgroup,name) for name in getAxes(self.axes)] 
     1555            return [getattr(self.nxgroup,name) for name in _readaxes(self.axes)] 
    12111556        except KeyError: 
    12121557            return None 
    12131558 
    1214     def nxdata_as(self, units=""): 
    1215         """ 
    1216         Return the data in particular units. 
     1559    def _getdata(self): 
     1560        """ 
     1561        Return the data if it is not larger than NX_MEMORY. 
    12171562        """ 
    12181563        if self._value is None: 
    1219             if self._file: 
    1220                 self._value = self._file.readpath(self._path) 
     1564            if self.nxroot._file: 
     1565                if str(self.dtype) == 'char': 
     1566                    self._value = self.nxroot._file.readpath(self.nxpath) 
     1567                elif np.prod(self.shape) * np.dtype(self.dtype).itemsize <= NX_MEMORY*1024*1024: 
     1568                    self._value = self.nxroot._file.readpath(self.nxpath) 
     1569                else: 
     1570                    raise MemoryError, 'Data size larger than NX_MEMORY=%s MB' % NX_MEMORY 
    12211571            else: 
    12221572                return None 
    1223         if isinstance(self._value, np.ndarray): 
    1224             self._value = self._value.reshape(self.nxdims) 
    1225         return self._converter(self._value,units) 
    1226      
    1227     def nxdata_set(self, value): 
    1228         dummy_nxtype, nxdims = _gettype(value) 
    1229         if nxdims == self.nxdims: 
    1230             self._value = _settype(value, self.nxtype) 
    1231         else: 
    1232             raise NeXusError, "Dimensions do not match SDS" 
    1233  
    1234     nxdata = property(nxdata_as,nxdata_set,doc="The data values in default units") 
     1573                 
     1574        return self._value 
     1575     
     1576    def _setdata(self, value): 
     1577        if value is not None: 
     1578            if str(self._dtype) == 'char' or isinstance(value,str): 
     1579                self._value = str(value) 
     1580                self._shape = (len(self._value),) 
     1581                self._dtype = 'char' 
     1582            else: 
     1583                if self.dtype in np.typeDict: 
     1584                    self._value = np.array(value,self.dtype) 
     1585                else: 
     1586                    self._value = np.array(value) 
     1587                self._shape = self._value.shape 
     1588                self._dtype = self._value.dtype 
     1589 
     1590    def _getdtype(self): 
     1591        return self._dtype 
     1592     
     1593    def _getshape(self): 
     1594        return self._shape 
     1595     
     1596    def _getsize(self): 
     1597        return len(self) 
     1598 
     1599    nxdata = property(_getdata,_setdata,doc="The data values") 
     1600    nxaxes = property(_getaxes,doc="The plotting axes") 
     1601    dtype = property(_getdtype, "Data type of NeXus field") 
     1602    shape = property(_getshape, "Shape of NeXus field") 
     1603    size = property(_getsize, "Size of NeXus field") 
     1604 
     1605SDS = NXfield # For backward compatibility 
     1606 
     1607def _fixaxes(signal, axes): 
     1608    """ 
     1609    Remove length-one dimensions from plottable data 
     1610    """ 
     1611    shape = list(signal.shape) 
     1612    while 1 in shape: shape.remove(1) 
     1613    newaxes = [] 
     1614    for axis in axes: 
     1615        if axis.size > 1: newaxes.append(axis) 
     1616    return signal.nxdata.view().reshape(shape), newaxes 
    12351617 
    12361618class PylabPlotter(object): 
    1237     """ 
    1238     Matplotlib plotter object for NeXus data nodes. 
    1239     """ 
    1240     def plot(self, signal, axes, title, errors=None, **opts): 
     1619 
     1620    """ 
     1621    Matplotlib plotter class for NeXus data. 
     1622    """ 
     1623 
     1624    def plot(self, signal, axes, title, errors, **opts): 
    12411625        """ 
    12421626        Plot the data entry. 
     
    12441628        Raises NeXusError if the data cannot be plotted. 
    12451629        """ 
    1246         import pylab 
     1630        try: 
     1631            import pylab 
     1632        except ImportError: 
     1633            raise NeXusError, "Plotting package not available." 
    12471634        if "over" in opts.keys(): 
    12481635            over = True 
     
    12581645            logplot = False 
    12591646 
     1647        # Provide a new view of the data if there is a dimension of length 1 
     1648        if 1 in signal.shape:  
     1649            data, axes = _fixaxes(signal, axes) 
     1650        else: 
     1651            data = signal.nxdata 
     1652 
    12601653        # Find the centers of the bins for histogrammed data 
    1261         axis_data = centers(signal, axes) 
     1654        axis_data = centers(data, axes) 
    12621655 
    12631656        #One-dimensional Plot 
    1264         if len(signal.nxdims) == 1: 
     1657        if len(data.shape) == 1: 
    12651658            if hasattr(signal, 'units'): 
    1266                 if not errors and signal.units.nxdata == 'counts': 
    1267                     errors = SDS(np.sqrt(signal.nxdata)) 
     1659                if not errors and signal.units == 'counts': 
     1660                    errors = NXfield(np.sqrt(data)) 
    12681661            if logplot: 
    1269                 data = np.log10(np.clip(signal.nxdata,0,1e8)) 
    1270                 if errors: ebars = np.log10(errors.nxdata) 
    1271             else: 
    1272                 data = signal.nxdata 
    1273                 if errors: ebars = errors.nxdata 
     1662                data = np.log10(np.clip(data,0,1e8)) 
     1663                if errors: ebars = np.log10(errors) 
     1664            elif errors:  
     1665                ebars = errors.nxdata 
    12741666            if errors: 
    12751667                myopts=copy(opts) 
    12761668                myopts.setdefault('fmt','o') 
    12771669                myopts.setdefault('linestyle','None') 
    1278                 pylab.scatter(axis_data[0], signal.nxdata, **opts) 
    1279                 pylab.errorbar(axis_data[0], signal.nxdata, ebars, **myopts) 
     1670                pylab.scatter(axis_data[0], data, **opts) 
     1671                pylab.errorbar(axis_data[0], data, ebars, **myopts) 
    12801672            else: 
    1281                 pylab.scatter(axis_data[0], signal.nxdata, **opts) 
     1673                pylab.scatter(axis_data[0], data, **opts) 
    12821674            if not over: 
    12831675                pylab.xlabel(label(axes[0])) 
     
    12871679        #Two dimensional plot 
    12881680        else: 
     1681            if len(data.shape) > 2: 
     1682                slab = [slice(None), slice(None)] 
     1683                for _dim in data.shape[2:]: 
     1684                    slab.append(0) 
     1685                data = data[slab].view().reshape(data.shape[:2]) 
     1686                print "Warning: Only the top 2D slice of the data is plotted" 
    12891687            #from api.nexus import meshgl 
    12901688            #gridplot = meshgl.pcolor_gl 
    12911689            #gridplot = pylab.pcolormesh 
    1292             if len(signal.nxdims) == 2: 
    1293                 data = signal.nxdata 
    1294             else: 
    1295                 slab = [slice(None), slice(None)] 
    1296                 for dummy in signal.nxdims[2:]: 
    1297                     slab.append(0) 
    1298                 data = signal[slab].nxreshape(signal.nxdims[:2]).nxdata 
    1299                 print "Warning: Only the top 2D slice of the data is plotted" 
    13001690            gridplot = imshow_irregular 
    1301 #            if hasattr(signal, 'units') and signal.units == 'counts': 
    13021691            if logplot: 
    13031692                gridplot(axis_data[0], axis_data[1],  
     
    13091698            pylab.title(title) 
    13101699 
    1311         #For higher-dimensional plots, choose limits 
    1312         #else: 
    1313             #raise NeXusError, "Only one and two-dimensional plots currently supported" 
    1314  
    13151700    @staticmethod 
    13161701    def show(): 
     
    13181703        pylab.show()     
    13191704 
    1320 class NXgroup(NXnode): 
    1321     """ 
    1322     A NeXus group node. 
    1323      
    1324     This is a subclass of NXnode and is the base class for the specific 
     1705 
     1706class NXgroup(NXobject): 
     1707 
     1708    """ 
     1709    A NeXus group object. 
     1710     
     1711    This is a subclass of NXobject and is the base class for the specific 
    13251712    NeXus group classes, e.g., NXentry, NXsample, NXdata.  
    13261713     
     
    13321719    arguments. 
    13331720     
    1334     Positional Arguments : These must be valid NeXus nodes, either an SDS  
     1721    Positional Arguments : These must be valid NeXus objects, either an NXfield  
    13351722    or a NeXus group. These are added without modification as children of this  
    13361723    group.  
     
    13381725    Keyword Arguments : Apart from a list of special keywords shown below,  
    13391726    keyword arguments are used to add children to the group using the keywords 
    1340     as attribute names. The values can either be valid SDS data or NXgroups,  
    1341     in which case the 'nxname' attribute is changed to the keyword, or they  
    1342     can be numerical or string data, which are converted to SDS objects. 
     1727    as attribute names. The values can either be valid NXfields or NXgroups,  
     1728    in which case the 'name' attribute is changed to the keyword, or they  
     1729    can be numerical or string data, which are converted to NXfield objects. 
    13431730     
    13441731    Special Keyword Arguments: 
     
    13461733    name : string 
    13471734        The name of the NXgroup, which is directly accessible as the NXgroup  
    1348         attribute 'nxname'. If the NXgroup is initialized as the attribute of  
    1349         a parent node, the name is automatically set to the name of this  
     1735        attribute 'name'. If the NXgroup is initialized as the attribute of  
     1736        a parent group, the name is automatically set to the name of this  
    13501737        attribute. If 'nxclass' is specified and has the usual prefix 'NX', 
    13511738        the default name is the class name without this prefix. 
     
    13571744        arguments. 
    13581745    file : filename 
    1359         The file from which the SDS has been read. 
     1746        The file from which the NXfield has been read. 
    13601747    path : string 
    1361         The path to this node with respect to the root of the NeXus tree,  
     1748        The path to this object with respect to the root of the NeXus tree,  
    13621749        using the convention for unix file paths. 
    1363     group : NXnode (NXgroup or subclass of NXgroup) 
    1364         The parent NeXus node, which is accessible as the group attribute  
    1365         'nxgroup'. If the group is initialized as the attribute of  
    1366         a parent node, this is set to the parent node. 
    1367  
    1368     Attributes 
    1369     ---------- 
     1750    group : NXobject (NXgroup or subclass of NXgroup) 
     1751        The parent NeXus group, which is accessible as the group attribute  
     1752        'group'. If the group is initialized as the attribute of  
     1753        a parent group, this is set to the parent group. 
     1754 
     1755    Python Attributes 
     1756    ----------------- 
    13701757    nxclass : string 
    1371         The class of the NXnode. 
     1758        The class of the NXobject. 
    13721759    nxname : string 
    1373         The name of the SDS.  
    1374     nxentries : dict 
    1375         A dictionary of all the group entries. 
    1376     nxattrs : dict 
    1377         A dictionary of the group attributes.  
     1760        The name of the NXfield.  
     1761    entries : dictionary 
     1762        A dictionary of all the NeXus objects contained within an NXgroup. 
     1763    attrs : dictionary 
     1764        A dictionary of all the NeXus attributes, i.e., attribute with class NXattr. 
     1765    entries : dictionary 
     1766        A dictionary of all the NeXus objects contained within the group. 
     1767    attrs : dictionary 
     1768        A dictionary of all the group's NeXus attributes, which all have the 
     1769        class NXattr. 
    13781770    nxpath : string 
    1379         The path to this node with respect to the root of the NeXus tree. For 
     1771        The path to this object with respect to the root of the NeXus tree. For 
    13801772        NeXus data read from a file, this will be a group of class NXroot, but 
    13811773        if the NeXus tree was defined interactively, it can be any valid  
    1382         NXgroup. This is determined by recursively accessing the 'nxgroup' 
    1383         attributes of the parent nodes. 
     1774        NXgroup. 
     1775    nxroot : NXgroup 
     1776        The root object of the NeXus tree containing this object. For 
     1777        NeXus data read from a file, this will be a group of class NXroot, but 
     1778        if the NeXus tree was defined interactively, it can be any valid  
     1779        NXgroup. 
     1780 
     1781    NeXus Group Entries 
     1782    ------------------- 
     1783    Just as in a NeXus file, NeXus groups can contain either data or other 
     1784    groups, represented by NXfield and NXgroup objects respectively. To 
     1785    distinguish them from regular Python attributes, all NeXus objects are 
     1786    stored in the 'entries' dictionary of the NXgroup. However, they can usually 
     1787    be assigned or referenced as if they are Python attributes, i.e., using the 
     1788    dictionary name directly as the group attribute name, as long as this name 
     1789    is not the same as one of the Python attributes defined above or as one of 
     1790    the NXfield Python attributes.   
     1791     
     1792    a) Assigning a NeXus object to a NeXus group 
     1793     
     1794    In the example below, after assigning the NXgroup, the following three 
     1795    NeXus object assignments to entry.sample are all equivalent: 
     1796 
     1797        >>> entry.sample = NXsample() 
     1798        >>> entry.sample['temperature'] = NXfield(40.0) 
     1799        >>> entry.sample.temperature = NXfield(40.0) 
     1800        >>> entry.sample.temperature = 40.0 
     1801        >>> entry.sample.temperature 
     1802        NXfield(40.0) 
     1803         
     1804    If the assigned value is not a valid NXobject, then it is cast as an NXfield  
     1805    with a type determined from the Python data type. 
     1806     
     1807        >>> entry.sample.temperature = 40.0 
     1808        >>> entry.sample.temperature 
     1809        NXfield(40.0) 
     1810        >>> entry.data.data.x=np.linspace(0,10,11).astype('float32') 
     1811        >>> entry.data.data.x 
     1812        NXfield([  0.   1.   2. ...,   8.   9.  10.]) 
     1813     
     1814    b) Referencing a NeXus object in a NeXus group 
     1815     
     1816    If the name of the NeXus object is not the same as any of the Python 
     1817    attributes listed above, or the methods listed below, they can be referenced 
     1818    as if they were a Python attribute of the NXgroup. However, it is only possible  
     1819    to reference attributes with one of the proscribed names using the group  
     1820    dictionary, i.e., 
     1821 
     1822        >>> entry.sample.tree = 100.0 
     1823        >>> entry.sample.tree 
     1824        sample:NXsample 
     1825          tree = 100.0 
     1826        >>> entry.sample['tree'] 
     1827        NXfield(100.0) 
     1828     
     1829    For this reason, it is recommended to use the group dictionary to reference 
     1830    all group objects within Python scripts. 
     1831 
     1832    NeXus Attributes 
     1833    ---------------- 
     1834    NeXus attributes are not currently used much with NXgroups, except for the 
     1835    root group, which has a number of global attributes to store the file name, 
     1836    file creation time, and NeXus and HDF version numbers. However, the 
     1837    mechanism described for NXfields works here as well. All NeXus attributes 
     1838    are stored in the 'attrs' dictionary of the NXgroup, but can be referenced 
     1839    as if they are Python attributes as long as there is no name clash. 
     1840 
     1841        >>> entry.sample.temperature = 40.0 
     1842        >>> entry.sample.attrs['tree'] = 10.0 
     1843        >>> entry.sample.tree 
     1844        sample:NXsample 
     1845          @tree = 10.0 
     1846          temperature = 40.0 
     1847        >>> entry.sample.attrs['tree'] 
     1848        NXattr(10.0) 
    13841849 
    13851850    Methods 
    13861851    ------- 
    1387     nxdir(self, attrs=False): 
     1852    insert(self, NXobject, name='unknown'): 
     1853        Insert a valid NXobject (NXfield or NXgroup) into the group. 
     1854         
     1855        If NXobject has a 'name' attribute and the 'name' keyword is not given, 
     1856        then the object is inserted with the NXobject name. 
     1857         
     1858    makelink(self, NXobject): 
     1859        Add the NXobject to the group entries as a link (NXlink). 
     1860 
     1861    dir(self, attrs=False, recursive=False): 
    13881862        Print the group directory. 
    13891863         
    1390         The directory is a list of NeXus objects within this group, either  
    1391         NeXus groups or SDS data. If 'attrs' is True, SDS attributes are  
    1392         displayed. If 'recursive' is True, the contents of child groups are  
    1393         also displayed. 
    1394  
    1395     nxtree(self, attrs=True): 
    1396         Print the SDS and its attributes. 
    1397          
    1398         It invokes the 'nxdir' method with both 'attrs' and 'recursive'  
    1399         set to True. 
    1400          
    1401     nxsave(filename, format='w5') 
     1864        The directory is a list of NeXus objects within this group, either NeXus 
     1865        groups or NXfield data. If 'attrs' is True, NXfield attributes are 
     1866        displayed. If 'recursive' is True, the contents of child groups are also 
     1867        displayed. 
     1868 
     1869    tree: 
     1870        Print the group tree. 
     1871         
     1872        It invokes the 'dir' method with both 'attrs' and 'recursive'  
     1873        set to True. Note that this method is defined as a property attribute and 
     1874        does not require parentheses. 
     1875         
     1876    save(self, filename, format='w5') 
    14021877        Save the NeXus group into a file 
    14031878 
     
    14061881        a valid NeXus file. 
    14071882 
    1408     nxsave(filename, format='w5') 
    1409         Save the NeXus group into a file 
    1410  
    1411         The object is wrapped in an NXroot group (with name 'root') and an  
    1412         NXentry group (with name 'entry'), if necessary, in order to produce  
    1413         a valid NeXus file. 
    1414  
    14151883    Examples 
    14161884    -------- 
    1417     >>> x = SDS(np.linspace(0,2*np.pi,101), units='degree') 
     1885    >>> x = NXfield(np.linspace(0,2*np.pi,101), units='degree') 
    14181886    >>> entry = NXgroup(x, name='entry', nxclass='NXentry') 
    1419     >>> entry.sample = NXgroup(temperature=SDS(40.0,units='K'), 
     1887    >>> entry.sample = NXgroup(temperature=NXfield(40.0,units='K'), 
    14201888                               nxclass='NXsample') 
    1421     >>> entry.sample.nxtree() 
     1889    >>> entry.sample.tree 
    14221890    sample:NXsample 
    14231891      temperature = 40.0 
     
    14291897 
    14301898    >>> entry = NXentry(x) 
    1431     >>> entry.sample = NXsample(temperature=SDS(40.0,units='K')) 
     1899    >>> entry.sample = NXsample(temperature=NXfield(40.0,units='K')) 
     1900     
     1901    or 
     1902     
     1903    >>> entry.sample.temperature = 40.0 
     1904    >>> entry.sample.temperature.units='K' 
    14321905      
    14331906    """ 
    1434     # Plotter to use for nxplot calls 
     1907 
     1908    # Plotter to use for plot calls 
    14351909    _plotter = PylabPlotter() 
    14361910 
    14371911    def __init__(self, *items, **opts): 
    14381912        if "name" in opts.keys(): 
    1439             self.nxname = opts["name"] 
     1913            self._name = opts["name"] 
    14401914            del opts["name"] 
     1915        self._entries = {} 
    14411916        if "entries" in opts.keys(): 
    14421917            for k,v in opts["entries"].items(): 
    14431918                setattr(self, k, v) 
    14441919            del opts["entries"] 
     1920        self._attrs = AttrDict() 
    14451921        if "attrs" in opts.keys(): 
    14461922            self._setattrs(opts["attrs"]) 
    14471923            del opts["attrs"] 
     1924        if "nxclass" in opts.keys(): 
     1925            self._class = opts["nxclass"] 
     1926            del opts["nxclass"] 
    14481927        for k,v in opts.items(): 
    14491928            setattr(self, k, v) 
    1450         if self.nxname == "unknown" and self.nxclass.startswith("NX"): 
    1451             self.nxname = self.nxclass[2:] 
     1929        if self.nxclass.startswith("NX"): 
     1930            if self.nxname == "unknown": self._name = self.nxclass[2:] 
     1931            try: # If one exists, set the class to a valid NXgroup subclass 
     1932                self.__class__ = globals()[self.nxclass] 
     1933            except KeyError: 
     1934                pass              
    14521935        for item in items: 
    14531936            try: 
    14541937                setattr(self, item.nxname, item) 
    14551938            except AttributeError: 
    1456                 raise NeXusError, "Non-keyword arguments must be valid NXnodes" 
     1939                raise NeXusError, "Non-keyword arguments must be valid NXobjects" 
    14571940 
    14581941#    def __cmp__(self, other): 
     
    14631946#            return cmp(self.nxname, other.nxname) 
    14641947 
     1948    def __repr__(self): 
     1949        return "%s('%s')" % (self.__class__.__name__,self.nxname) 
     1950         
    14651951    def _str_value(self,indent=0): 
    14661952        return "" 
     
    14681954    def __getattr__(self, key): 
    14691955        """ 
    1470         Provide direct access to nodes via nxclass name. 
     1956        Provide direct access to groups via nxclass name. 
    14711957        """ 
    14721958        if key.startswith('NX'): 
    1473             return self.nxcomponent(key) 
     1959            return self.component(key) 
     1960        elif key in self.entries: 
     1961            return self.entries[key] 
     1962        elif key in self.attrs: 
     1963            return self.attrs[key].nxdata 
    14741964        raise KeyError(key+" not in "+self.nxclass+":"+self.nxname) 
    14751965 
    14761966    def __setattr__(self, name, value): 
    14771967        """ 
    1478         Set a node attribute as a node or regular Python attribute. 
     1968        Set an attribute as an object or regular Python attribute. 
    14791969         
    14801970        It is assumed that attributes starting with 'nx' or '_' are regular  
    1481         Python attributes. All other attributes are converted to valid NXnodes, 
    1482         with class SDS, NXgroup, or a sub-class of NXgroup, depending on the 
     1971        Python attributes. All other attributes are converted to valid NXobjects, 
     1972        with class NXfield, NXgroup, or a sub-class of NXgroup, depending on the 
    14831973        assigned value. 
    14841974         
    1485         The internal value of the attribute name, i.e., 'nxname', is set to the  
     1975        The internal value of the attribute name, i.e., 'name', is set to the  
    14861976        attribute name used in the assignment.  The parent group of the  
    1487         attribute, i.e., 'nxgroup', is set to the parent node of the attribute. 
     1977        attribute, i.e., 'group', is set to the parent group of the attribute. 
    14881978         
    14891979        If the assigned value is a numerical (scalar or array) or string object, 
    1490         it is converted to an object of class SDS, whose attribute, 'nxdata',  
     1980        it is converted to an object of class NXfield, whose attribute, 'nxdata',  
    14911981        is set to the assigned value. 
    14921982        """ 
    1493         if isinstance(value, NXattr) or name.startswith('nx') or name.startswith('_'): 
     1983        if name.startswith('_'): 
    14941984            object.__setattr__(self, name, value) 
    1495         elif isinstance(value, NXnode): 
    1496             value.nxgroup = self 
    1497             value.nxname = name 
    1498             object.__setattr__(self, name, value) 
    1499         else: 
    1500             object.__setattr__(self, name, SDS(value=value, name=name, group=self)) 
     1985        elif isinstance(value, NXattr): 
     1986            self._attrs[name] = value 
     1987        else: 
     1988            self[name] = value  
    15011989 
    15021990    def __getitem__(self, index): 
    15031991        """ 
    15041992        Returns a slice from the NXgroup nxsignal attribute (if it exists) as 
    1505         a new NXdata group. 
    1506          
    1507         In most cases, the slice values are applied to the SDS nxdata array 
    1508         and returned within an SDS object with the same metadata. However, 
     1993        a new NXdata group, if the index is a slice object.  
     1994         
     1995        In most cases, the slice values are applied to the NXfield nxdata array 
     1996        and returned within an NXfield object with the same metadata. However, 
    15091997        if the array is one-dimensional and the index start and stop values  
    15101998        are real, the nxdata array is returned with values between the limits 
     
    15142002        decreasing) one-dimensional arrays. 
    15152003        """ 
     2004        if isinstance(index, str): #i.e., requesting a dictionary value 
     2005            return self._entries[index] 
     2006 
    15162007        if not self.nxsignal:  
    15172008            raise NeXusError, "No plottable signal" 
     
    15522043        for axis in result.nxaxes: 
    15532044            if len(axis) > 1: axes.append(axis) 
    1554         result._setaxes(axes) 
     2045        result.nxsignal.axes = ":".join([axis.nxname for axis in axes]) 
     2046        if self.nxtitle: 
     2047            result.title = self.nxtitle 
    15552048        return result 
    15562049 
    1557     def _setSDS(self, value, name): 
    1558         if isinstance(value, list) or isinstance(value, tuple): 
    1559             setattr(self, name, SDS(value=np.array(value), name=name)) 
    1560             return name 
    1561         elif isinstance(value, np.ndarray): 
    1562             setattr(self, name, SDS(value=value, name=name)) 
    1563             return name 
    1564         elif isinstance(value, SDS): 
    1565             if value.nxname == 'unknown': value.nxname = name 
    1566             setattr(self, value.nxname, value) 
    1567             return value.nxname 
    1568         else: 
    1569             raise NeXusError, "%s: '%s' should be a Numpy array or an SDS" % (type(value), name) 
    1570  
    1571     def _setaxes(self, axes): 
    1572         #print "_setaxes", signal.nxname, id(signal) 
    1573         self.nxsignal.axes = ":".join([axis.nxname for axis in axes]) 
    1574          
    1575     def _fixaxes(self, signal): 
    1576         """ 
    1577         Remove length-one dimensions from plottable data 
    1578         """ 
    1579         if isinstance(signal, NXlinkdata): signal = signal.nxlink 
    1580         while 1 in signal.nxdims: signal.nxdims.remove(1) 
    1581         axes = [] 
    1582         for axisname in getAxes(signal.axes): 
    1583             axis = getattr(self,axisname) 
    1584             if len(axis) > 1: axes.append(axis) 
    1585         signal.axes = ":".join([axis.nxname for axis in axes]) 
    1586          
    1587     def nxinsert(self, node): 
    1588         """ 
    1589         Add a valid NeXus node (SDS or NXgroup) to the group 
    1590         """ 
    1591         if isinstance(node, NXnode): 
    1592             node.nxgroup = self 
    1593             object.__setattr__(self, node.nxname, node) 
    1594  
    1595     def nxsum(self, axis=None): 
     2050    def __setitem__(self, key, value): 
     2051        """ 
     2052        Includes a NeXus group item by adding it to the 'entries' dictionary. 
     2053        """ 
     2054        if isinstance(value, NXlink): 
     2055            self._entries[key] = value 
     2056        elif isinstance(value, NXobject): 
     2057            if value.nxgroup is not None:    
     2058                memo = {} 
     2059                value = deepcopy(value, memo) 
     2060                value._attrs = copy(value._attrs) 
     2061            value._group = self 
     2062            value._name = key 
     2063            self._entries[key] = value 
     2064        else: 
     2065            self._entries[key] = NXfield(value=value, name=key, group=self)  
     2066 
     2067    def __deepcopy__(self, memo): 
     2068        dpcpy = self.__class__() 
     2069        memo[id(self)] = dpcpy 
     2070        for k,v in self.items(): 
     2071            if isinstance(v, NXgroup): 
     2072                dpcpy[k] = deepcopy(v, memo) 
     2073            else: 
     2074                dpcpy[k] = copy(v)                 
     2075        for k, v in self.attrs.items(): 
     2076            dpcpy.attrs[k] = copy(v) 
     2077        return dpcpy 
     2078 
     2079    def keys(self): 
     2080        """ 
     2081        Returns the names of NeXus objects in the group. 
     2082        """ 
     2083        return self._entries.keys() 
     2084 
     2085    def values(self): 
     2086        """ 
     2087        Returns the values of NeXus objects in the group. 
     2088        """ 
     2089        return self._entries.values() 
     2090     
     2091    def items(self): 
     2092        """ 
     2093        Returns a list of the NeXus objects in the group as (key,value) pairs. 
     2094        """ 
     2095        return self._entries.items() 
     2096     
     2097    def has_key(self, name): 
     2098        """ 
     2099        Returns true if the NeXus object with the specified name is in the group. 
     2100        """ 
     2101        return self._entries.has_key(name) 
     2102 
     2103    def insert(self, value, name='unknown'): 
     2104        """ 
     2105        Adds an attribute to the group. 
     2106         
     2107        If it is not a valid NeXus object (NXfield or NXgroup), the attribute  
     2108        is converted to an NXfield.         
     2109        """ 
     2110        if isinstance(value, NXobject): 
     2111            if name == 'unknown': name = value.nxname 
     2112            if name in self._entries: 
     2113                raise NeXusError, "'%s' already exists in group" % name 
     2114            value._group = self 
     2115            self._entries[name] = value 
     2116        else: 
     2117            self._entries[name] = NXfield(value=value, name=name, group=self) 
     2118 
     2119    def makelink(self, target): 
     2120        """ 
     2121        Creates a linked NXobject within the group. 
     2122         
     2123        All attributes are inherited from the parent object including the name 
     2124        """ 
     2125        if isinstance(target, NXobject): 
     2126            self.entries[target.nxname] = NXlink(target=target, name=target.nxname, group=self) 
     2127        else: 
     2128            raise NeXusError, "Link target must be an NXobject" 
     2129 
     2130         
     2131    def sum(self, axis=None): 
    15962132        """ 
    15972133        Return the sum of the NXdata group using the Numpy sum method 
     
    16062142            raise NeXusError, "Summing not allowed for groups of unknown class" 
    16072143        if axis is None: 
    1608             return self.nxsignal.nxsum() 
    1609         else: 
    1610             signal = SDS(self.nxsignal.nxsum(axis), name=self.nxsignal.nxname) 
     2144            return self.nxsignal.sum() 
     2145        else: 
     2146            signal = NXfield(self.nxsignal.sum(axis), name=self.nxsignal.nxname) 
    16112147            axes = self.nxaxes 
    16122148            summedaxis = axes.pop(axis) 
     
    16152151            signal.long_name = "Integral from %s to %s %s" % \ 
    16162152                               (summedaxis[0], summedaxis[-1], units) 
    1617             average = SDS(0.5*(summedaxis.nxdata[0]+summedaxis.nxdata[-1]), name=summedaxis.nxname) 
     2153            average = NXfield(0.5*(summedaxis.nxdata[0]+summedaxis.nxdata[-1]), name=summedaxis.nxname) 
    16182154            if units: average.units = units 
    16192155            result = NXdata(signal, axes, average) 
    16202156            if self.nxerrors: 
    16212157                errors = np.sqrt((self.nxerrors.nxdata**2).sum(axis)) 
    1622                 result.errors = SDS(errors, name="errors") 
     2158                result.errors = NXfield(errors, name="errors") 
     2159            if self.nxtitle: 
     2160                result.title = self.nxtitle 
    16232161            return result         
    16242162 
    1625     def nxmoment(self, order=1): 
    1626         """ 
    1627         Return an SDS containing the moments of the NXdata group  
     2163    def moment(self, order=1): 
     2164        """ 
     2165        Return an NXfield containing the moments of the NXdata group  
    16282166        assuming the signal is one-dimensional. 
    16292167         
     
    16332171        if not self.nxsignal:  
    16342172            raise NeXusError, "No signal to calculate" 
    1635         elif len(self.nxsignal.nxdims) > 1: 
     2173        elif len(self.nxsignal.shape) > 1: 
    16362174            raise NeXusError, "Operation only possible on one-dimensional signals" 
    16372175        elif order > 1: 
     
    16392177        if not hasattr(self,"nxclass"): 
    16402178            raise NeXusError, "Operation not allowed for groups of unknown class" 
    1641         return (centers(self.nxsignal,self.nxaxes)*self.nxsignal).nxsum() \ 
    1642                 /self.nxsignal.nxsum() 
    1643  
    1644     def nxcomponent(self, nxclass): 
    1645         """ 
    1646         Find all child nodes that have a particular class. 
    1647         """ 
    1648         return [E for dummy_name,E in self.nxentries.items() if E.nxclass==nxclass] 
    1649  
    1650     def nxsignals(self): 
    1651         """ 
    1652         Return a dictionary of SDS's containing signal data. 
     2179        return (centers(self.nxsignal,self.nxaxes)*self.nxsignal).sum() \ 
     2180                /self.nxsignal.sum() 
     2181 
     2182    def component(self, nxclass): 
     2183        """ 
     2184        Find all child objects that have a particular class. 
     2185        """ 
     2186        return [E for _name,E in self.entries.items() if E.nxclass==nxclass] 
     2187 
     2188    def signals(self): 
     2189        """ 
     2190        Return a dictionary of NXfield's containing signal data. 
    16532191         
    16542192        The key is the value of the signal attribute. 
    16552193        """ 
    16562194        signals = {} 
    1657         for node in self.nxentries.values(): 
    1658             if 'signal' in node.nxattrs: 
    1659                 signals[node.signal.nxdata] = node 
     2195        for obj in self.entries.values(): 
     2196            if 'signal' in obj.attrs: 
     2197                signals[obj.nxsignal.nxdata] = obj 
    16602198        return signals                 
    16612199 
    16622200    def _signal(self): 
    16632201        """ 
    1664         Return the SDS containing the signal data. 
    1665         """ 
    1666         for node in self.nxentries.values(): 
    1667             if 'signal' in node.nxattrs and str(node.signal.nxdata) == '1': 
    1668                 if 1 in node.nxdims: self._fixaxes(node) 
    1669                 return self.__dict__[node.nxname] 
     2202        Return the NXfield containing the signal data. 
     2203        """ 
     2204        for obj in self.entries.values(): 
     2205            if 'signal' in obj.attrs and str(obj.signal) == '1': 
     2206#                if isinstance(self[obj.nxname],NXlink): 
     2207#                    return self[obj.nxname].nxlink 
     2208#                else: 
     2209                return self[obj.nxname] 
    16702210        return None 
    16712211 
    16722212    def _axes(self): 
    16732213        """ 
    1674         Return a list of SDSs containing the axes. 
     2214        Return a list of NXfields containing the axes. 
    16752215        """ 
    16762216        try: 
    1677             return [getattr(self,name) for name in getAxes(self.nxsignal.axes)] 
     2217            return [getattr(self,name) for name in _readaxes(self.nxsignal.axes)] 
    16782218        except KeyError: 
    16792219            axes = {} 
    1680             for node in self.nxentries: 
    1681                 if 'axis' in getattr(self,node).nxattrs: 
    1682                     axes[getattr(self,node).axis.nxdata] = getattr(self,node) 
     2220            for obj in self.entries: 
     2221                if 'axis' in getattr(self,obj).attrs: 
     2222                    axes[getattr(self,obj).axis] = getattr(self,obj) 
    16832223            return [axes[key] for key in sorted(axes.keys())] 
    16842224 
    16852225    def _errors(self): 
    16862226        """ 
    1687         Return the SDS containing the signal errors. 
     2227        Return the NXfield containing the signal errors. 
    16882228        """ 
    16892229        try: 
    1690             return self.nxentries['errors'] 
     2230            return self.entries['errors'] 
    16912231        except KeyError: 
    16922232            return None 
    16932233 
    1694     nxsignal = property(_signal, "Signal SDS within group") 
     2234    def _title(self): 
     2235        """ 
     2236        Return the title as a string. 
     2237         
     2238        If there is no title attribute in the string, the parent  
     2239        NXentry group in the group's path is searched. 
     2240        """ 
     2241        title = self.nxpath 
     2242        if 'title' in self.entries: 
     2243            return str(self.title) 
     2244        else: 
     2245            obj = self 
     2246            while obj.nxgroup is not None: 
     2247                try: 
     2248                    if obj.nxclass == 'NXentry': return str(obj.title) 
     2249                except KeyError: 
     2250                    pass 
     2251                obj = obj.nxgroup 
     2252            return self.nxpath         
     2253 
     2254    def _getentries(self): 
     2255        return self._entries 
     2256 
     2257    nxsignal = property(_signal, "Signal NXfield within group") 
    16952258    nxaxes = property(_axes, "List of axes within group") 
    1696     nxerrors = property(_errors, "Errors SDS within group") 
    1697      
    1698     def nxplot(self, signal=None, **opts): 
     2259    nxerrors = property(_errors, "Errors NXfield within group") 
     2260    nxtitle = property(_title, "Title for group plot") 
     2261    entries = property(_getentries,doc="NeXus objects within group")     
     2262 
     2263     
     2264    def plot(self, **opts): 
    16992265        """ 
    17002266        Plot data contained within the group. 
     
    17072273            group = group.NXentry[0] 
    17082274        if group.nxclass == "NXentry": 
    1709             group = group.NXdata[0] 
     2275            try: 
     2276                group = group.NXdata[0] 
     2277            except IndexError: 
     2278                raise NeXusError('No NXdata group found') 
    17102279         
    17112280        # Find a plottable signal 
    1712         if signal is None: 
    1713             signal = group.nxsignal 
    1714             if not signal: 
    1715                 raise NeXusError('No plottable signal defined') 
     2281        signal = group.nxsignal 
     2282        if not signal: 
     2283            raise NeXusError('No plottable signal defined') 
    17162284 
    17172285        # Find errors 
     
    17202288        # Find the associated axes 
    17212289        axes = group.nxaxes 
    1722  
     2290         
    17232291        # Construct title 
    1724         path = [] 
    1725         node = group 
    1726         title = '' 
    1727         while node.nxgroup is not None: 
    1728             try: 
    1729                 if node.nxclass == 'NXentry': title = node.title.nxdata 
    1730             except KeyError: 
    1731                 pass 
    1732             path = [node.nxname] + path 
    1733             node = node.nxgroup 
     2292        title = group.nxtitle 
    17342293  
    17352294        # Plot with the available plotter 
     
    17372296 
    17382297 
    1739 class NXroot(NXgroup): 
    1740     """ 
    1741     NXroot node. This is a subclass of the NXgroup class. 
    1742      
    1743     See the NXgroup documentation for more details. 
    1744     """ 
    1745     def __init__(self, *items, **opts): 
    1746         self.nxclass = "NXroot" 
    1747         NXgroup.__init__(self, *items, **opts) 
    1748      
     2298class NXlink(NXobject): 
     2299 
     2300    """ 
     2301    Class for NeXus linked objects. 
     2302     
     2303    The real object will be accessible by following the link attribute. 
     2304    """ 
     2305 
     2306    _class = "NXlink" 
     2307 
     2308    def __init__(self, target=None, name='link', group=None): 
     2309        self._group = group 
     2310        self._name = name 
     2311        if isinstance(target, NXobject): 
     2312            self._target = target.nxpath 
     2313            if target.nxclass == "NXlink": 
     2314                raise NeXusError, "Cannot link to another NXlink object" 
     2315            elif target.nxclass == "NXfield": 
     2316                self.__class__ = NXlinkfield 
     2317            else: 
     2318                self.__class__ = NXlinkgroup 
     2319        else: 
     2320            self._target = target 
     2321 
     2322    def __getattr__(self, key): 
     2323        try: 
     2324            try: 
     2325                return self.nxlink.__dict__[key] 
     2326            except KeyError: 
     2327                return self.nxlink.__getattr__(key) 
     2328        except KeyError: 
     2329            raise KeyError, (key+" not in %s" % self._target) 
     2330 
     2331    def __repr__(self): 
     2332        return "NXlink('%s')"%(self._target) 
     2333 
     2334    def __str__(self): 
     2335        return "NXlink('%s')"%(self._target) 
     2336 
     2337    def _str_tree(self, indent=0, attrs=False, recursive=False): 
     2338        if self.nxlink: 
     2339            return self.nxlink._str_tree(indent, attrs, recursive) 
     2340        else: 
     2341            return " "*indent+self.nxname+' -> '+self._target 
     2342 
     2343    def _getlink(self): 
     2344        link = self.nxroot 
     2345        if link: 
     2346            try: 
     2347                for level in self._target[1:].split('/'): 
     2348                    link = link.entries[level] 
     2349                return link 
     2350            except AttributeError: 
     2351                return None 
     2352        else: 
     2353            return None 
     2354 
     2355    def _getattrs(self): 
     2356        return self.nxlink.attrs 
     2357 
     2358    def _getentries(self): 
     2359        return self.nxlink.entries 
     2360 
     2361    nxlink = property(_getlink, "Linked object") 
     2362    attrs = property(_getattrs,doc="NeXus attributes for object") 
     2363    entries = property(_getentries,doc="NeXus objects within group")     
     2364 
     2365 
     2366class NXlinkfield(NXlink, NXfield): 
     2367 
     2368    """ 
     2369    Class for a NeXus linked field. 
     2370     
     2371    The real field will be accessible by following the link attribute. 
     2372    """ 
     2373 
     2374    def __setattr__(self, name, value): 
     2375        if name.startswith('_'): 
     2376            object.__setattr__(self, name, value) 
     2377        elif name == "name" or name == "nxclass" or name == "group": 
     2378            NXfield.__setattr__(self, name, value) 
     2379        elif self.nxlink: 
     2380            self.nxlink.__setattr__(name, value) 
     2381 
     2382    def get(self, offset, size): 
     2383        """ 
     2384        Get a slab from the data array. 
     2385 
     2386        Offsets are 0-origin.  Shape can be inferred from the data. 
     2387        Offset and shape must each have one entry per dimension. 
     2388         
     2389        This operation should be performed in a "with group.data" 
     2390        conext. 
     2391 
     2392        Raises ValueError cannot convert units. 
     2393 
     2394        Corresponds to NXgetslab(handle,data,offset,shape) 
     2395        """ 
     2396        if self.nxroot._file: 
     2397            self.nxlink.__enter__() 
     2398            value = self.nxlink.nxroot._file.getslab(offset,size) 
     2399            self.nxlink.__exit__() 
     2400        else: 
     2401            raise NeXusError, "No input file specified for NXgetslab" 
     2402     
     2403NXlinkdata = NXlinkfield # For backward compatibility 
     2404 
     2405class NXlinkgroup(NXlink, NXgroup): 
     2406 
     2407    """ 
     2408    Class for a NeXus linked group. 
     2409     
     2410    The real group will be accessible by following the link attribute. 
     2411    """ 
     2412 
     2413    def __setattr__(self, name, value): 
     2414        if name.startswith('_'): 
     2415            object.__setattr__(self, name, value) 
     2416        elif name == "name" or name == "nxclass" or name == "group": 
     2417            NXgroup.__setattr__(self, name, value) 
     2418        elif self.nxlink: 
     2419            if not isinstance(value, NXobject): value = NXfield(value=value, name=name) 
     2420            value._group = self 
     2421            value._name = name 
     2422            object.__setattr__(self, name, value) 
     2423            setattr(self.nxlink, name, value) 
     2424 
     2425 
    17492426class NXentry(NXgroup): 
    1750     """ 
    1751     NXentry node. This is a subclass of the NXgroup class. 
     2427 
     2428    """ 
     2429    NXentry group. This is a subclass of the NXgroup class. 
    17522430 
    17532431    Each NXdata and NXmonitor object of the same name will be added 
     
    17602438    See the NXgroup documentation for more details. 
    17612439    """ 
     2440 
    17622441    def __init__(self, *items, **opts): 
    1763         self.nxclass = "NXentry" 
     2442        self._class = "NXentry" 
    17642443        NXgroup.__init__(self, *items, **opts) 
    17652444 
     
    17682447        Add two NXentry objects        
    17692448        """ 
    1770         result = NXentry(entries=self.nxentries, attrs=self.nxattrs) 
     2449        result = NXentry(entries=self.entries, attrs=self.attrs) 
    17712450        try: 
    1772             names = [group.nxname for group in self.nxcomponent("NXdata")] 
     2451            names = [group.nxname for group in self.component("NXdata")] 
    17732452            for name in names: 
    1774                 if isinstance(other.__dict__[name], NXdata): 
    1775                     result.__dict__[name] = self.__dict__[name] + other.__dict__[name] 
     2453                if isinstance(other.entries[name], NXdata): 
     2454                    result.entries[name] = self.entries[name] + other.entries[name] 
    17762455                else: 
    17772456                    raise KeyError 
    1778             names = [group.nxname for group in self.nxcomponent("NXmonitor")] 
     2457            names = [group.nxname for group in self.component("NXmonitor")] 
    17792458            for name in names: 
    1780                 if isinstance(other.__dict__[name], NXmonitor): 
    1781                     result.__dict__[name] = self.__dict__[name] + other.__dict__[name] 
     2459                if isinstance(other.entries[name], NXmonitor): 
     2460                    result.entries[name] = self.entries[name] + other.entries[name] 
    17822461                else: 
    17832462                    raise KeyError 
     
    17902469        Subtract two NXentry objects 
    17912470        """ 
    1792         result = NXentry(entries=self.nxentries, attrs=self.nxattrs) 
     2471        result = NXentry(entries=self.entries, attrs=self.attrs) 
    17932472        try: 
    1794             names = [group.nxname for group in self.nxcomponent("NXdata")] 
     2473            names = [group.nxname for group in self.component("NXdata")] 
    17952474            for name in names: 
    1796                 if isinstance(other.__dict__[name], NXdata): 
    1797                     result.__dict__[name] = self.__dict__[name] - other.__dict__[name] 
     2475                if isinstance(other.entries[name], NXdata): 
     2476                    result.entries[name] = self.entries[name] - other.entries[name] 
    17982477                else: 
    17992478                    raise KeyError 
    1800             names = [group.nxname for group in self.nxcomponent("NXmonitor")] 
     2479            names = [group.nxname for group in self.component("NXmonitor")] 
    18012480            for name in names: 
    1802                 if isinstance(other.__dict__[name], NXmonitor): 
    1803                     result.__dict__[name] = self.__dict__[name] - other.__dict__[name] 
     2481                if isinstance(other.entries[name], NXmonitor): 
     2482                    result.entries[name] = self.entries[name] - other.entries[name] 
    18042483                else: 
    18052484                    raise KeyError 
     
    18092488 
    18102489 
     2490class NXsubentry(NXentry): 
     2491 
     2492    """ 
     2493    NXsubentry group. This is a subclass of the NXsubentry class. 
     2494 
     2495    See the NXgroup documentation for more details. 
     2496    """ 
     2497 
     2498    def __init__(self, *items, **opts): 
     2499        self._class = "NXsubentry" 
     2500        NXgroup.__init__(self, *items, **opts) 
     2501 
     2502 
    18112503class NXdata(NXgroup): 
    1812     """ 
    1813     NXdata node. This is a subclass of the NXgroup class. 
     2504 
     2505    """ 
     2506    NXdata group. This is a subclass of the NXgroup class. 
    18142507     
    18152508    The constructor assumes that the first argument contains the signal and 
    18162509    the second contains either the axis, for one-dimensional data, or a list 
    1817     of axes, for multidimenional data. These arguments can either be SDS  
    1818     objects or Numpy arrays, which are converted to SDS objects with default  
     2510    of axes, for multidimensional data. These arguments can either be NXfield  
     2511    objects or Numpy arrays, which are converted to NXfield objects with default  
    18192512    names. 
    18202513     
     
    18272520    Attributes 
    18282521    ---------- 
    1829     nxsignal : The SDS containing the attribute 'signal' with value 1 
    1830     nxaxes   : A list of SDSs containing the signal axes 
    1831     nxerrors : The SDS containing the errors 
     2522    nxsignal : The NXfield containing the attribute 'signal' with value 1 
     2523    nxaxes   : A list of NXfields containing the signal axes 
     2524    nxerrors : The NXfield containing the errors 
    18322525 
    18332526    Methods 
    18342527    ------- 
    1835     nxplot(self, over=False, log=False, **opts) 
     2528    plot(self, over=False, log=False, **opts) 
    18362529        Plot the NXdata group using the defined signal and axes. Valid 
    18372530        Matplotlib parameters, specifying markers, colors, etc, can be  
    18382531        specified using the 'opts' dictionary.     
    18392532     
    1840     nxmoment(self, order=1) 
     2533    moment(self, order=1) 
    18412534        Calculate moments of the NXdata group. This assumes that the  
    18422535        signal is one-dimenional. Currently, only the first moment is  
     
    18532546      x = float64(101) 
    18542547    >>> X, Y = np.meshgrid(x, x) 
    1855     >>> z = SDS(sin(X) * sin(Y), name='intensity') 
     2548    >>> z = NXfield(sin(X) * sin(Y), name='intensity') 
    18562549    >>> entry = NXentry() 
    18572550    >>> entry.grid = NXdata(z, (x, x)) 
    1858     >>> grid.nxtree() 
     2551    >>> grid.tree() 
    18592552    entry:NXentry 
    18602553      grid:NXdata 
     
    18672560    See the NXgroup documentation for more details. 
    18682561    """ 
    1869     def __init__(self, signal=None, axes=(), *items, **opts): 
    1870         self.nxclass = "NXdata" 
     2562 
     2563    def __init__(self, signal=None, axes=None, *items, **opts): 
     2564        self._class = "NXdata" 
    18712565        NXgroup.__init__(self, *items, **opts) 
    18722566        if signal is not None: 
    1873             signalname = self._setSDS(signal, "signal") 
    1874             self.__dict__[signalname].signal = 1 
     2567            if isinstance(signal,NXfield): 
     2568                if signal.nxname == "unknown": signal.nxname = "signal" 
     2569                if "signal" not in signal.attrs: signal.signal = 1 
     2570                self[signal.nxname] = signal 
     2571                signalname = signal.nxname 
     2572            else: 
     2573                self["signal"] = signal 
     2574                self["signal"].signal = 1 
     2575                signalname = "signal" 
    18752576            if axes is not None: 
    1876                 if isinstance(axes,tuple) or isinstance(axes,list): 
    1877                     axisname = {} 
    1878                     i = 0 
    1879                     for axis in axes: 
    1880                         i = i + 1 
    1881                         axisname[i] = self._setSDS(axis, "axis%s" % i) 
    1882                     self.__dict__[signalname].axes = ":".join(axisname.values()) 
    1883                 else: 
    1884                     axisname = self._setSDS(axes, 'x') 
    1885                     self.__dict__[signalname].axes = axisname 
     2577                if not isinstance(axes,tuple) and not isinstance(axes,list): 
     2578                    axes = [axes] 
     2579                axisnames = {} 
     2580                i = 0 
     2581                for axis in axes: 
     2582                    i = i + 1 
     2583                    if isinstance(axis,NXfield): 
     2584                        if axis._name == "unknown": axis._name = "axis%s" % i 
     2585                        self[axis.nxname] = axis 
     2586                        axisnames[i] = axis.nxname 
     2587                    else: 
     2588                        axisname = "axis%s" % i 
     2589                        self[axisname] = axis 
     2590                        axisnames[i] = axisname 
     2591                self[signalname].axes = ":".join(axisnames.values())                 
    18862592 
    18872593    def __add__(self, other): 
     
    18922598        The result contains a copy of all the metadata contained in 
    18932599        the first NXdata group. The module checks that the dimensions are  
    1894         compatible, but does not check that the SDS names or values are 
     2600        compatible, but does not check that the NXfield names or values are 
    18952601        identical. This is so that spelling variations or rounding errors 
    18962602        do not make the operation fail. However, it is up to the user to  
    18972603        ensure that the results make sense. 
    18982604        """ 
    1899         result = NXdata(entries=self.nxentries, attrs=self.nxattrs) 
     2605        result = NXdata(entries=self.entries, attrs=self.attrs) 
    19002606        if isinstance(other, NXdata): 
    1901             if self.nxsignal and self.nxsignal.nxdims == other.nxsignal.nxdims: 
    1902                 result.__dict__[self.nxsignal.nxname] = self.nxsignal + other.nxsignal 
     2607            if self.nxsignal and self.nxsignal.shape == other.nxsignal.shape: 
     2608                result.entries[self.nxsignal.nxname] = self.nxsignal + other.nxsignal 
    19032609                if self.nxerrors: 
    19042610                    if other.nxerrors: 
     
    19102616            raise NeXusError, "Cannot add two arbitrary groups" 
    19112617        else: 
    1912             result.__dict__[self.nxsignal.nxname] = self.nxsignal + other 
    1913             result.__dict__[self.nxsignal.nxname].nxname = self.nxsignal.nxname 
     2618            result.entries[self.nxsignal.nxname] = self.nxsignal + other 
     2619            result.entries[self.nxsignal.nxname].nxname = self.nxsignal.nxname 
    19142620            return result 
    19152621         
     
    19212627        The result contains a copy of all the metadata contained in 
    19222628        the first NXdata group. The module checks that the dimensions are  
    1923         compatible, but does not check that the SDS names or values are 
     2629        compatible, but does not check that the NXfield names or values are 
    19242630        identical. This is so that spelling variations or rounding errors 
    19252631        do not make the operation fail. However, it is up to the user to  
    19262632        ensure that the results make sense. 
    19272633        """ 
    1928         result = NXdata(entries=self.nxentries, attrs=self.nxattrs) 
     2634        result = NXdata(entries=self.entries, attrs=self.attrs) 
    19292635        if isinstance(other, NXdata): 
    1930             if self.nxsignal and self.nxsignal.nxdims == other.nxsignal.nxdims: 
    1931                 result.__dict__[self.nxsignal.nxname] = self.nxsignal - other.nxsignal 
     2636            if self.nxsignal and self.nxsignal.shape == other.nxsignal.shape: 
     2637                result.entries[self.nxsignal.nxname] = self.nxsignal - other.nxsignal 
    19322638                if self.nxerrors: 
    19332639                    if other.nxerrors: 
     
    19392645            raise NeXusError, "Cannot subtract two arbitrary groups" 
    19402646        else: 
    1941             result.__dict__[self.nxsignal.nxname] = self.nxsignal - other 
    1942             result.__dict__[self.nxsignal.nxname].nxname = self.nxsignal.nxname 
     2647            result.entries[self.nxsignal.nxname] = self.nxsignal - other 
     2648            result.entries[self.nxsignal.nxname].nxname = self.nxsignal.nxname 
    19432649            return result 
    19442650         
     
    19502656        The result contains a copy of all the metadata contained in 
    19512657        the first NXdata group. The module checks that the dimensions are  
    1952         compatible, but does not check that the SDS names or values are 
     2658        compatible, but does not check that the NXfield names or values are 
    19532659        identical. This is so that spelling variations or rounding errors 
    19542660        do not make the operation fail. However, it is up to the user to  
    19552661        ensure that the results make sense. 
    19562662        """ 
    1957         result = NXdata(entries=self.nxentries, attrs=self.nxattrs) 
     2663        result = NXdata(entries=self.entries, attrs=self.attrs) 
    19582664        if isinstance(other, NXdata): 
    19592665 
    19602666            # error here signal not defined in this scope 
    1961             #if self.nxsignal and signal.nxdims == other.nxsignal.nxdims: 
    1962             if self.nxsignal and self.nxsignal.nxdims == other.nxsignal.nxdims: 
    1963                 result.__dict__[self.nxsignal.nxname] = self.nxsignal * other.nxsignal 
     2667            #if self.nxsignal and signal.shape == other.nxsignal.shape: 
     2668            if self.nxsignal and self.nxsignal.shape == other.nxsignal.shape: 
     2669                result.entries[self.nxsignal.nxname] = self.nxsignal * other.nxsignal 
    19642670                if self.nxerrors: 
    19652671                    if other.nxerrors: 
     
    19722678            raise NeXusError, "Cannot multiply two arbitrary groups" 
    19732679        else: 
    1974             result.__dict__[self.nxsignal.nxname] = self.nxsignal * other 
    1975             result.__dict__[self.nxsignal.nxname].nxname = self.nxsignal.nxname 
     2680            result.entries[self.nxsignal.nxname] = self.nxsignal * other 
     2681            result.entries[self.nxsignal.nxname].nxname = self.nxsignal.nxname 
    19762682            if self.nxerrors: 
    19772683                result.errors = self.errors * other 
     
    19932699        The result contains a copy of all the metadata contained in 
    19942700        the first NXdata group. The module checks that the dimensions are  
    1995         compatible, but does not check that the SDS names or values are 
     2701        compatible, but does not check that the NXfield names or values are 
    19962702        identical. This is so that spelling variations or rounding errors 
    19972703        do not make the operation fail. However, it is up to the user to  
    19982704        ensure that the results make sense. 
    19992705        """ 
    2000         result = NXdata(entries=self.nxentries, attrs=self.nxattrs) 
     2706        result = NXdata(entries=self.entries, attrs=self.attrs) 
    20012707        if isinstance(other, NXdata): 
    2002             if self.nxsignal and self.nxsignal.nxdims == other.nxsignal.nxdims: 
     2708            if self.nxsignal and self.nxsignal.shape == other.nxsignal.shape: 
    20032709                # error here, signal and othersignal not defined here 
    2004                 #result.__dict__[self.nxsignal.nxname] = signal / othersignal 
    2005                 result.__dict__[self.nxsignal.nxname] = self.nxsignal / other.nxsignal 
    2006                 resultvalues = result.__dict__[self.nxsignal.nxname].nxdata 
     2710                #result.entries[self.nxsignal.nxname] = signal / othersignal 
     2711                result.entries[self.nxsignal.nxname] = self.nxsignal / other.nxsignal 
     2712                resultvalues = result.entries[self.nxsignal.nxname].nxdata 
    20072713                if self.nxerrors: 
    20082714                    if other.nxerrors: 
     
    20162722            raise NeXusError, "Cannot divide two arbitrary groups" 
    20172723        else: 
    2018             result.__dict__[self.nxsignal.nxname] = self.nxsignal / other 
    2019             result.__dict__[self.nxsignal.nxname].nxname = self.nxsignal.nxname 
     2724            result.entries[self.nxsignal.nxname] = self.nxsignal / other 
     2725            result.entries[self.nxsignal.nxname].nxname = self.nxsignal.nxname 
    20202726            if self.nxerrors: result.errors = self.errors / other 
    20212727            return result 
     
    20232729 
    20242730class NXmonitor(NXdata): 
    2025     """ 
    2026     NXmonitor node. This is a subclass of the NXdata class. 
     2731 
     2732    """ 
     2733    NXmonitor group. This is a subclass of the NXdata class. 
    20272734     
    20282735    See the NXdata and NXgroup documentation for more details. 
    20292736    """ 
     2737 
    20302738    def __init__(self, signal=None, axes=(), *items, **opts): 
    20312739        NXdata.__init__(self, signal=signal, axes=axes, *items, **opts) 
    2032         self.nxclass = "NXmonitor" 
    2033         if "nxname" not in opts.keys(): 
    2034             self.nxname = "monitor" 
    2035  
    2036  
    2037 class NXsample(NXgroup): 
    2038     """ 
    2039     NXsample node. This is a subclass of the NXgroup class. 
    2040      
    2041     See the NXgroup documentation for more details. 
    2042     """ 
    2043     def __init__(self, *items, **opts): 
    2044         self.nxclass = "NXsample" 
    2045         NXgroup.__init__(self, *items, **opts) 
    2046  
    2047  
    2048 class NXinstrument(NXgroup): 
    2049     """ 
    2050     NXinstrument node. This is a subclass of the NXgroup class. 
    2051      
    2052     See the NXgroup documentation for more details. 
    2053     """ 
    2054     def __init__(self, *items, **opts): 
    2055         self.nxclass = "NXinstrument" 
    2056         NXgroup.__init__(self, *items, **opts) 
    2057  
    2058  
    2059 class NXaperture(NXgroup): 
    2060     """ 
    2061     NXaperture node. This is a subclass of the NXgroup class. 
    2062      
    2063     See the NXgroup documentation for more details. 
    2064     """ 
    2065     def __init__(self, *items, **opts): 
    2066         self.nxclass = "NXaperture" 
    2067         NXgroup.__init__(self, *items, **opts) 
    2068  
    2069  
    2070  
    2071 class NXattenuator(NXgroup): 
    2072     """ 
    2073     NXattenuator node. This is a subclass of the NXgroup class. 
    2074      
    2075     See the NXgroup documentation for more details. 
    2076     """ 
    2077     def __init__(self, *items, **opts): 
    2078         self.nxclass = "NXattenuator" 
    2079         NXgroup.__init__(self, *items, **opts) 
    2080  
    2081  
    2082  
    2083 class NXbeam_stop(NXgroup): 
    2084     """ 
    2085     NXbeam_stop node. This is a subclass of the NXgroup class. 
    2086      
    2087     See the NXgroup documentation for more details. 
    2088     """ 
    2089     def __init__(self, *items, **opts): 
    2090         self.nxclass = "NXbeam_stop" 
    2091         NXgroup.__init__(self, *items, **opts) 
    2092  
    2093  
    2094  
    2095 class NXbending_magnet(NXgroup): 
    2096     """ 
    2097     NXbending_magnet node. This is a subclass of the NXgroup class. 
    2098      
    2099     See the NXgroup documentation for more details. 
    2100     """ 
    2101     def __init__(self, *items, **opts): 
    2102         self.nxclass = "NXbending_magnet" 
    2103         NXgroup.__init__(self, *items, **opts) 
    2104  
    2105  
    2106  
    2107 class NXcollimator(NXgroup): 
    2108     """ 
    2109     NXcollimator node. This is a subclass of the NXgroup class. 
    2110      
    2111     See the NXgroup documentation for more details. 
    2112     """ 
    2113     def __init__(self, *items, **opts): 
    2114         self.nxclass = "NXcollimator" 
    2115         NXgroup.__init__(self, *items, **opts) 
    2116  
    2117  
    2118  
    2119 class NXcrystal(NXgroup): 
    2120     """ 
    2121     NXcrystal node. This is a subclass of the NXgroup class. 
    2122      
    2123     See the NXgroup documentation for more details. 
    2124     """ 
    2125     def __init__(self, *items, **opts): 
    2126         self.nxclass = "NXcrystal" 
    2127         NXgroup.__init__(self, *items, **opts) 
    2128  
    2129  
    2130 class NXdetector(NXgroup): 
    2131     """ 
    2132     NXdetector node. This is a subclass of the NXgroup class. 
    2133      
    2134     See the NXgroup documentation for more details. 
    2135     """ 
    2136     def __init__(self, *items, **opts): 
    2137         self.nxclass = "NXdetector" 
    2138         NXgroup.__init__(self, *items, **opts) 
    2139  
    2140  
    2141 class NXdisk_chopper(NXgroup): 
    2142     """ 
    2143     NXdisk_chopper node. This is a subclass of the NXgroup class. 
    2144      
    2145     See the NXgroup documentation for more details. 
    2146     """ 
    2147     def __init__(self, *items, **opts): 
    2148         self.nxclass = "NXdisk_chopper" 
    2149         NXgroup.__init__(self, *items, **opts) 
    2150  
    2151  
    2152 class NXfermi_chopper(NXgroup): 
    2153     """ 
    2154     NXfermi_chopper node. This is a subclass of the NXgroup class. 
    2155      
    2156     See the NXgroup documentation for more details. 
    2157     """ 
    2158     def __init__(self, *items, **opts): 
    2159         self.nxclass = "NXfermi_chopper" 
    2160         NXgroup.__init__(self, *items, **opts) 
    2161  
    2162  
    2163 class NXfilter(NXgroup): 
    2164     """ 
    2165     NXfilter node. This is a subclass of the NXgroup class. 
    2166      
    2167     See the NXgroup documentation for more details. 
    2168     """ 
    2169     def __init__(self, *items, **opts): 
    2170         self.nxclass = "NXfilter" 
    2171         NXgroup.__init__(self, *items, **opts) 
    2172  
    2173  
    2174 class NXflipper(NXgroup): 
    2175     """ 
    2176     NXflipper node. This is a subclass of the NXgroup class. 
    2177      
    2178     See the NXgroup documentation for more details. 
    2179     """ 
    2180     def __init__(self, *items, **opts): 
    2181         self.nxclass = "NXflipper" 
    2182         NXgroup.__init__(self, *items, **opts) 
    2183  
    2184  
    2185 class NXguide(NXgroup): 
    2186     """ 
    2187     NXguide node. This is a subclass of the NXgroup class. 
    2188      
    2189     See the NXgroup documentation for more details. 
    2190     """ 
    2191     def __init__(self, *items, **opts): 
    2192         self.nxclass = "NXguide" 
    2193         NXgroup.__init__(self, *items, **opts) 
    2194  
    2195  
    2196 class NXinsertion_device(NXgroup): 
    2197     """ 
    2198     NXinsertion_device node. This is a subclass of the NXgroup class. 
    2199      
    2200     See the NXgroup documentation for more details. 
    2201     """ 
    2202     def __init__(self, *items, **opts): 
    2203         self.nxclass = "NXinsertion_device" 
    2204         NXgroup.__init__(self, *items, **opts) 
    2205  
    2206  
    2207 class NXmirror(NXgroup): 
    2208     """ 
    2209     NXmirror node. This is a subclass of the NXgroup class. 
    2210      
    2211     See the NXgroup documentation for more details. 
    2212     """ 
    2213     def __init__(self, *items, **opts): 
    2214         self.nxclass = "NXmirror" 
    2215         NXgroup.__init__(self, *items, **opts) 
    2216  
    2217  
    2218 class NXmoderator(NXgroup): 
    2219     """ 
    2220     NXmoderator node. This is a subclass of the NXgroup class. 
    2221      
    2222     See the NXgroup documentation for more details. 
    2223     """ 
    2224     def __init__(self, *items, **opts): 
    2225         self.nxclass = "NXmoderator" 
    2226         NXgroup.__init__(self, *items, **opts) 
    2227  
    2228  
    2229 class NXmonochromator(NXgroup): 
    2230     """ 
    2231     NXmonochromator node. This is a subclass of the NXgroup class. 
    2232      
    2233     See the NXgroup documentation for more details. 
    2234     """ 
    2235     def __init__(self, *items, **opts): 
    2236         self.nxclass = "NXmonochromator" 
    2237         NXgroup.__init__(self, *items, **opts) 
    2238  
    2239  
    2240 class NXpolarizer(NXgroup): 
    2241     """ 
    2242     NXpolarizer node. This is a subclass of the NXgroup class. 
    2243      
    2244     See the NXgroup documentation for more details. 
    2245     """ 
    2246     def __init__(self, *items, **opts): 
    2247         self.nxclass = "NXpolarizer" 
    2248         NXgroup.__init__(self, *items, **opts) 
    2249  
    2250  
    2251 class NXpositioner(NXgroup): 
    2252     """ 
    2253     NXpositioner node. This is a subclass of the NXgroup class. 
    2254      
    2255     See the NXgroup documentation for more details. 
    2256     """ 
    2257     def __init__(self, *items, **opts): 
    2258         self.nxclass = "NXpositioner" 
    2259         NXgroup.__init__(self, *items, **opts) 
    2260  
    2261  
    2262 class NXsource(NXgroup): 
    2263     """ 
    2264     NXsource node. This is a subclass of the NXgroup class. 
    2265      
    2266     See the NXgroup documentation for more details. 
    2267     """ 
    2268     def __init__(self, *items, **opts): 
    2269         self.nxclass = "NXsource" 
    2270         NXgroup.__init__(self, *items, **opts) 
    2271  
    2272  
    2273 class NXvelocity_selector(NXgroup): 
    2274     """ 
    2275     NXvelocity_selector node. This is a subclass of the NXgroup class. 
    2276      
    2277     See the NXgroup documentation for more details. 
    2278     """ 
    2279     def __init__(self, *items, **opts): 
    2280         self.nxclass = "NXvelocity_selector" 
    2281         NXgroup.__init__(self, *items, **opts) 
    2282  
    2283  
    2284 class NXevent_data(NXgroup): 
    2285     """ 
    2286     NXevent_data node. This is a subclass of the NXgroup class. 
    2287      
    2288     See the NXgroup documentation for more details. 
    2289     """ 
    2290     def __init__(self, *items, **opts): 
    2291         self.nxclass = "NXevent_data" 
    2292         NXgroup.__init__(self, *items, **opts) 
    2293  
    2294  
    2295 class NXuser(NXgroup): 
    2296     """ 
    2297     NXuser node. This is a subclass of the NXgroup class. 
    2298      
    2299     See the NXgroup documentation for more details. 
    2300     """ 
    2301     def __init__(self, *items, **opts): 
    2302         self.nxclass = "NXuser" 
    2303         NXgroup.__init__(self, *items, **opts) 
    2304  
    2305  
    2306 class NXparameter(NXgroup): 
    2307     """ 
    2308     NXparameter node. This is a subclass of the NXgroup class. 
    2309      
    2310     See the NXgroup documentation for more details. 
    2311     """ 
    2312     def __init__(self, *items, **opts): 
    2313         self.nxclass = "NXparameter" 
    2314         NXgroup.__init__(self, *items, **opts) 
    2315  
    2316  
    2317 class NXprocess(NXgroup): 
    2318     """ 
    2319     NXprocess node. This is a subclass of the NXgroup class. 
    2320      
    2321     See the NXgroup documentation for more details. 
    2322     """ 
    2323     def __init__(self, *items, **opts): 
    2324         self.nxclass = "NXprocess" 
    2325         NXgroup.__init__(self, *items, **opts) 
    2326  
    2327  
    2328 class NXcharacterization(NXgroup): 
    2329     """ 
    2330     NXcharacterization node. This is a subclass of the NXgroup class. 
    2331      
    2332     See the NXgroup documentation for more details. 
    2333     """ 
    2334     def __init__(self, *items, **opts): 
    2335         self.nxclass = "NXcharacterization" 
    2336         NXgroup.__init__(self, *items, **opts) 
     2740        self._class = "NXmonitor" 
     2741        if "name" not in opts.keys(): 
     2742            self._name = "monitor" 
    23372743 
    23382744 
    23392745class NXlog(NXgroup): 
    2340     """ 
    2341     NXlog node. This is a subclass of the NXgroup class. 
     2746 
     2747    """ 
     2748    NXlog group. This is a subclass of the NXgroup class. 
    23422749     
    23432750    Methods 
    23442751    ------- 
    2345     nxplot(self, **opts) 
     2752    plot(self, **opts) 
    23462753        Plot the logged values against the elapsed time. Valid 
    23472754        Matplotlib parameters, specifying markers, colors, etc, can be  
     
    23502757    See the NXgroup documentation for more details. 
    23512758    """ 
     2759 
    23522760    def __init__(self, *items, **opts): 
    2353         self.nxclass = "NXlog" 
     2761        self._class = "NXlog" 
    23542762        NXgroup.__init__(self, *items, **opts) 
    23552763     
    2356     def nxplot(self, **opts): 
     2764    def plot(self, **opts): 
    23572765        axis = [self.time] 
    23582766        title = "%s Log" % self.value.nxname.upper() 
     
    23602768 
    23612769 
    2362  
    2363 class NXnote(NXgroup): 
    2364     """ 
    2365     NXnote node. This is a subclass of the NXgroup class. 
    2366      
    2367     See the NXgroup documentation for more details. 
    2368     """ 
    2369     def __init__(self, *items, **opts): 
    2370         self.nxclass = "NXnote" 
    2371         NXgroup.__init__(self, *items, **opts) 
    2372  
    2373  
    2374 class NXbeam(NXgroup): 
    2375     """ 
    2376     NXbeam node. This is a subclass of the NXgroup class. 
    2377      
    2378     See the NXgroup documentation for more details. 
    2379     """ 
    2380     def __init__(self, *items, **opts): 
    2381         self.nxclass = "NXbeam" 
    2382         NXgroup.__init__(self, *items, **opts) 
    2383  
    2384  
    2385 class NXgeometry(NXgroup): 
    2386     """ 
    2387     NXgeometry node. This is a subclass of the NXgroup class. 
    2388      
    2389     See the NXgroup documentation for more details. 
    2390     """ 
    2391     def __init__(self, *items, **opts): 
    2392         self.nxclass = "NXgeometry" 
    2393         NXgroup.__init__(self, *items, **opts) 
    2394  
    2395  
    2396 class NXtranslation(NXgroup): 
    2397     """ 
    2398     NXtranslation node. This is a subclass of the NXgroup class. 
    2399      
    2400     See the NXgroup documentation for more details. 
    2401     """ 
    2402     def __init__(self, *items, **opts): 
    2403         self.nxclass = "NXtranslation" 
    2404         NXgroup.__init__(self, *items, **opts) 
    2405  
    2406  
    2407 class NXshape(NXgroup): 
    2408     """ 
    2409     NXshape node. This is a subclass of the NXgroup class. 
    2410      
    2411     See the NXgroup documentation for more details. 
    2412     """ 
    2413     def __init__(self, *items, **opts): 
    2414         self.nxclass = "NXshape" 
    2415         NXgroup.__init__(self, *items, **opts) 
    2416  
    2417  
    2418 class NXorientation(NXgroup): 
    2419     """ 
    2420     NXorientation node. This is a subclass of the NXgroup class. 
    2421      
    2422     See the NXgroup documentation for more details. 
    2423     """ 
    2424     def __init__(self, *items, **opts): 
    2425         self.nxclass = "NXorientation" 
    2426         NXgroup.__init__(self, *items, **opts) 
    2427  
    2428  
    2429 class NXenvironment(NXgroup): 
    2430     """ 
    2431     NXenvironment node. This is a subclass of the NXgroup class. 
    2432      
    2433     See the NXgroup documentation for more details. 
    2434     """ 
    2435     def __init__(self, *items, **opts): 
    2436         self.nxclass = "NXenvironment" 
    2437         NXgroup.__init__(self, *items, **opts) 
    2438  
    2439  
    2440 class NXsensor(NXgroup): 
    2441     """ 
    2442     NXsensor node. This is a subclass of the NXgroup class. 
    2443      
    2444     See the NXgroup documentation for more details. 
    2445     """ 
    2446     def __init__(self, *items, **opts): 
    2447         self.nxclass = "NXsensor" 
    2448         NXgroup.__init__(self, *items, **opts) 
    2449  
    2450  
    2451 class NXlink(NXnode): 
    2452     """ 
    2453     NeXus linked node. 
    2454      
    2455     The real node will be accessible by following the nxlink attribute. 
    2456     """ 
    2457     nxclass = "NXlink" 
    2458  
    2459     def __init__(self, target=None, name="link"): 
    2460         self.nxname = name 
    2461         self.nxgroup = None 
    2462         if isinstance(target, NXnode): 
    2463             self._target = target.nxpath 
    2464             if target.nxclass == "NXlink": 
    2465                 raise NeXusError, "Cannot link to another NXlink object" 
    2466             elif target.nxclass == "SDS": 
    2467                 self.__class__ = NXlinkdata                 
    2468             else: 
    2469                 self.__class__ = NXlinkgroup 
    2470         else: 
    2471             self._target = target 
    2472  
    2473     def __getattr__(self, key): 
    2474         try: 
    2475             if key == "nxdata": return self.nxlink.nxdata 
    2476             return self.nxlink.__dict__[key] 
    2477         except KeyError: 
    2478             raise KeyError, (key+" not in %s" % self._target) 
    2479  
    2480     def __repr__(self): 
    2481         return "NXlink('%s')"%(self._target) 
    2482  
    2483     def __str__(self): 
    2484         return "NXlink('%s')"%(self._target) 
    2485  
    2486     def _str_tree(self, indent=0, attrs=False, recursive=False): 
    2487         if self.nxlink: 
    2488             return self.nxlink._str_tree(indent, attrs, recursive) 
    2489         else: 
    2490             return " "*indent+self.nxname+' -> '+self._target 
    2491  
    2492     def _link(self): 
    2493         link = getRoot(self) 
    2494         if link: 
    2495             try: 
    2496                 for level in self._target[1:].split('/'): 
    2497                     link = getattr(link, level) 
    2498                 return link 
    2499             except AttributeError: 
    2500                 return None 
    2501         else: 
    2502             return None 
    2503  
    2504     def _attrs(self): 
    2505         return dict([(k,v)  
    2506                      for k,v in self.nxlink.__dict__.items() 
    2507                      if isinstance(v,NXattr)]) 
    2508  
    2509     def _entries(self): 
    2510         return dict([(k,v)  
    2511                      for k,v in self.nxlink.__dict__.items() 
    2512                      if isinstance(v,NXnode) and not k.startswith('nx') 
    2513                         and not k.startswith('_')]) 
    2514  
    2515     nxlink = property(_link, "Linked object") 
    2516     nxattrs = property(_attrs,doc="NeXus attributes for node") 
    2517     nxentries = property(_entries,doc="NeXus nodes within group")     
    2518  
    2519  
    2520 class NXlinkdata(NXlink, SDS): 
    2521     """ 
    2522     NeXus linked SDS. 
    2523      
    2524     The real node will be accessible by following the nxlink attribute. 
    2525     """ 
    2526     def __setattr__(self, name, value): 
    2527         if name.startswith('_'): 
    2528             object.__setattr__(self, name, value) 
    2529         elif name == "nxname" or name == "nxclass" or name == "nxgroup": 
    2530             SDS.__setattr__(self, name, value) 
    2531         elif self.nxlink: 
    2532             self.nxlink.__setattr__(name, value) 
    2533  
    2534     def nxget(self, offset, size, units=""): 
    2535         """ 
    2536         Get a slab from the data array. 
    2537  
    2538         Offsets are 0-origin.  Shape can be inferred from the data. 
    2539         Offset and shape must each have one entry per dimension. 
    2540          
    2541         If units are specified, convert the values to the given units 
    2542         before returning them. 
    2543  
    2544         This operation should be performed in a "with group.data" 
    2545         conext. 
    2546  
    2547         Raises ValueError cannot convert units. 
    2548  
    2549         Corresponds to NXgetslab(handle,data,offset,shape) 
    2550         """ 
    2551         self.nxlink.__enter__() 
    2552         value = self.nxlink._file.getslab(offset,size) 
    2553         return self.nxlink._converter(value,units) 
    2554         self.nxlink.__exit__() 
    2555      
    2556 class NXlinkgroup(NXlink, NXgroup): 
    2557     """ 
    2558     NeXus linked group. 
    2559      
    2560     The real node will be accessible by following the nxlink attribute. 
    2561     """ 
    2562     def __setattr__(self, name, value): 
    2563         if name.startswith('_'): 
    2564             object.__setattr__(self, name, value) 
    2565         elif name == "nxname" or name == "nxclass" or name == "nxgroup": 
    2566             NXgroup.__setattr__(self, name, value) 
    2567         elif self.nxlink: 
    2568             if not isinstance(value, NXnode): value = SDS(value=value, name=name) 
    2569             value.nxgroup = self 
    2570             value.nxname = name 
    2571             object.__setattr__(self, name, value) 
    2572             setattr(self.nxlink, name, value) 
    2573  
    2574  
    2575 class Unknown(NXnode): 
    2576     """ 
    2577     Unknown group type; class does not start with NX or SDS. 
    2578     """ 
    2579     def __init__(self, nxname="unknown", nxclass="unknown"): 
    2580         self.nxname = nxname 
    2581         self.nxclass = nxclass 
    2582  
    2583     def __repr__(self): 
    2584         return "Unknown('%s','%s')"%(self.nxname,self.nxclass) 
     2770#------------------------------------------------------------------------- 
     2771#Add remaining base classes as subclasses of NXgroup and append to __all__ 
     2772 
     2773for _class in _nxclasses: 
     2774    if _class not in globals(): 
     2775        docstring = """ 
     2776                    %s group. This is a subclass of the NXgroup class. 
     2777     
     2778                    See the NXgroup documentation for more details. 
     2779                    """ % _class 
     2780        globals()[_class]=type(_class, (NXgroup,), 
     2781                               {'_class':_class,'__doc__':docstring}) 
     2782    __all__.append(_class) 
     2783 
     2784#------------------------------------------------------------------------- 
    25852785 
    25862786 
    25872787def centers(signal, axes): 
    25882788    """ 
    2589     Return the centers of the axes regardless if the axes contain 
    2590     bin boundaries or centers. 
     2789    Return the centers of the axes. 
     2790     
     2791    This works regardless if the axes contain bin boundaries or centers. 
    25912792    """ 
    25922793    def findc(axis, dimlen): 
    2593         if axis.nxdims[0] == dimlen+1: 
     2794        if axis.shape[0] == dimlen+1: 
    25942795            return (axis.nxdata[:-1] + axis.nxdata[1:])/2 
    25952796        else: 
    2596             assert axis.nxdims[0] == dimlen 
     2797            assert axis.shape[0] == dimlen 
    25972798            return axis.nxdata 
    2598     return [findc(a,signal.nxdims[i]) for i,a in enumerate(axes)] 
     2799    return [findc(a,signal.shape[i]) for i,a in enumerate(axes)] 
     2800 
     2801def setmemory(value): 
     2802    """ 
     2803    Set the memory limit for data arrays (in MB). 
     2804    """ 
     2805    global NX_MEMORY 
     2806    NX_MEMORY = value 
    25992807 
    26002808def label(field): 
    26012809    """ 
    2602     Construct a label for a data field suitable for use on a graph axis. 
     2810    Return a label for a data field suitable for use on a graph axis. 
    26032811    """ 
    26042812    if hasattr(field,'long_name'): 
    2605         return field.long_name.nxdata 
     2813        return field.long_name 
    26062814    elif hasattr(field,'units'): 
    2607         return "%s (%s)"%(field.nxname,field.units.nxdata) 
     2815        return "%s (%s)"%(field.nxname,field.units) 
    26082816    else: 
    26092817        return field.nxname 
     
    26112819def imshow_irregular(x,y,z): 
    26122820    import pylab 
    2613     #from matplotlib.ticker import FormatStrFormatter 
     2821#    from matplotlib.ticker import LogFormatter 
    26142822    ax = pylab.gca() 
    26152823    im = pylab.mpl.image.NonUniformImage(ax, extent=(x[0],x[-1],y[0],y[-1]), origin=None) 
     
    26182826    ax.set_xlim(x[0],x[-1]) 
    26192827    ax.set_ylim(y[0],y[-1]) 
    2620     pylab.colorbar(im) #format=FormatStrFormatter('$10^{%d}$') 
     2828    pylab.colorbar(im)#, format=LogFormatter()) 
    26212829    pylab.gcf().canvas.draw_idle() 
    26222830 
     
    26242832def load(filename, mode='r'): 
    26252833    """ 
    2626     Read a NeXus file, returning a tree of nodes 
     2834    Read a NeXus file returning a tree of objects. 
     2835     
     2836    This is aliased to 'read' because of potential name clashes with Numpy 
    26272837    """ 
    26282838    file = NeXusTree(filename,mode) 
     
    26312841    return tree 
    26322842 
    2633 def save(filename, node, format='w5'): 
    2634     """ 
    2635     Write a NeXus file from a tree of nodes 
    2636     """ 
    2637     if node.nxclass == "NXroot": 
    2638         tree = node 
    2639     elif node.nxclass == "NXentry": 
    2640         tree = NXroot(node) 
     2843#Definition for when there are name clashes with Numpy 
     2844read = load 
     2845__all__.append('read') 
     2846 
     2847def save(filename, group, format='w5'): 
     2848    """ 
     2849    Write a NeXus file from a tree of objects. 
     2850    """ 
     2851    if group.nxclass == "NXroot": 
     2852        tree = group 
     2853    elif group.nxclass == "NXentry": 
     2854        tree = NXroot(group) 
    26412855    else: 
    2642         tree = NXroot(NXentry(node)) 
     2856        tree = NXroot(NXentry(group)) 
    26432857    file = NeXusTree(filename, format) 
    26442858    file.writefile(tree) 
     
    26472861def tree(file): 
    26482862    """ 
    2649     Read and summarize the named nexus file. 
     2863    Read and summarize the named NeXus file. 
    26502864    """ 
    26512865    nxfile = load(file) 
    2652     nxfile.nxtree() 
     2866    nxfile.tree 
    26532867 
    26542868def demo(argv): 
    26552869    """ 
    2656     Process command line commands in argv.  argv should contain 
    2657     program name, command, arguments, where command is one of 
    2658     the following: 
     2870    Process a list of command line commands.   
     2871     
     2872    'argv' should contain program name, command, arguments, where command is one 
     2873    of the following: 
    26592874        copy fromfile.nxs tofile.nxs 
    26602875        ls f1.nxs f2.nxs ... 
     
    26732888        for entry in argv[3].split('.'): 
    26742889            tree = getattr(tree,entry) 
    2675         tree.nxplot() 
     2890        tree.plot() 
    26762891        tree._plotter.show() 
    26772892             
     
    26842899        """%(argv[0],) 
    26852900        print usage 
     2901         
    26862902 
    26872903if __name__ == "__main__": 
Note: See TracChangeset for help on using the changeset viewer.