[yt-svn] commit/yt: MatthewTurk: Merging from development tip.

Bitbucket commits-noreply at bitbucket.org
Wed Feb 20 08:02:51 PST 2013


1 new commit in yt:

https://bitbucket.org/yt_analysis/yt/commits/3161cb50ce62/
changeset:   3161cb50ce62
branch:      stable
user:        MatthewTurk
date:        2013-02-20 17:02:42
summary:     Merging from development tip.
affected #:  15 files

diff -r 329d11eeff224ed16d054e989680ba6f193318e9 -r 3161cb50ce626380cbf2958e0abacf61677072e3 CREDITS
--- a/CREDITS
+++ b/CREDITS
@@ -1,29 +1,41 @@
 YT is a group effort.
 
-Contributors:                   Matthew Turk (matthewturk at gmail.com)
-                                Britton Smith (brittonsmith at gmail.com)
-                                Jeff Oishi (jsoishi at gmail.com)
-                                Stephen Skory (s at skory.us)
-                                Sam Skillman (samskillman at gmail.com)
-                                Devin Silvia (devin.silvia at gmail.com)
-                                John Wise (jwise at astro.princeton.edu)
-                                David Collins (dcollins at physics.ucsd.edu)
-                                Christopher Moody (cemoody at ucsc.edu)
-                                Oliver Hahn (ohahn at stanford.edu)
-                                John ZuHone (jzuhone at cfa.harvard.edu)
-                                Chris Malone (cmalone at mail.astro.sunysb.edu)
-                                Cameron Hummels (chummels at astro.columbia.edu)
-                                Stefan Klemer (sklemer at phys.uni-goettingen.de)
-                                Tom Abel (tabel at stanford.edu)
-                                Andrew Myers (atmyers at astro.berkeley.edu)
-                                Michael Kuhlen (mqk at astro.berkeley.edu)
-                                Casey Stark (caseywstark at gmail.com)
-                                JC Passy (jcpassy at gmail.com)
-                                Eve Lee (elee at cita.utoronto.ca)
-                                Elizabeth Tasker (tasker at astro1.sci.hokudai.ac.jp)
-                                Kacper Kowalik (xarthisius.kk at gmail.com)
-                                Nathan Goldbaum (goldbaum at ucolick.org)
-                                Anna Rosen (rosen at ucolick.org)
+Contributors:                   Tom Abel (tabel at stanford.edu)
+				David Collins (dcollins at physics.ucsd.edu)
+				Brian Crosby (crosby.bd at gmail.com)
+				Andrew Cunningham (ajcunn at gmail.com)
+				Nathan Goldbaum (goldbaum at ucolick.org)
+				Markus Haider (markus.haider at uibk.ac.at)
+				Cameron Hummels (chummels at gmail.com)
+				Christian Karch (chiffre at posteo.de)
+				Ji-hoon Kim (me at jihoonkim.org)
+				Steffen Klemer (sklemer at phys.uni-goettingen.de)
+				Kacper Kowalik (xarthisius.kk at gmail.com)
+				Michael Kuhlen (mqk at astro.berkeley.edu)
+				Eve Lee (elee at cita.utoronto.ca)
+				Yuan Li (yuan at astro.columbia.edu)
+				Chris Malone (chris.m.malone at gmail.com)
+				Josh Maloney (joshua.moloney at colorado.edu)
+				Chris Moody (cemoody at ucsc.edu)
+				Andrew Myers (atmyers at astro.berkeley.edu)
+				Jeff Oishi (jsoishi at gmail.com)
+				Jean-Claude Passy (jcpassy at uvic.ca)
+				Mark Richardson (Mark.L.Richardson at asu.edu)
+				Thomas Robitaille (thomas.robitaille at gmail.com)
+				Anna Rosen (rosen at ucolick.org)
+				Anthony Scopatz (scopatz at gmail.com)
+				Devin Silvia (devin.silvia at colorado.edu)
+				Sam Skillman (samskillman at gmail.com)
+				Stephen Skory (s at skory.us)
+				Britton Smith (brittonsmith at gmail.com)
+				Geoffrey So (gsiisg at gmail.com)
+				Casey Stark (caseywstark at gmail.com)
+				Elizabeth Tasker (tasker at astro1.sci.hokudai.ac.jp)
+				Stephanie Tonnesen (stonnes at gmail.com)
+				Matthew Turk (matthewturk at gmail.com)
+				Rich Wagner (rwagner at physics.ucsd.edu)
+				John Wise (jwise at physics.gatech.edu)
+				John ZuHone (jzuhone at gmail.com)
 
 We also include the Delaunay Triangulation module written by Robert Kern of
 Enthought, the cmdln.py module by Trent Mick, and the progressbar module by

diff -r 329d11eeff224ed16d054e989680ba6f193318e9 -r 3161cb50ce626380cbf2958e0abacf61677072e3 yt/analysis_modules/halo_finding/rockstar/rockstar.py
--- a/yt/analysis_modules/halo_finding/rockstar/rockstar.py
+++ b/yt/analysis_modules/halo_finding/rockstar/rockstar.py
@@ -164,6 +164,13 @@
         If set to ``True``, it will be assumed that there are only dark
         matter particles present in the simulation. This can save analysis
         time if this is indeed the case. Default: ``False``.
+    hires_dm_mass : float
+        If supplied, use only the highest resolution dark matter
+        particles, with a mass less than (1.1*hires_dm_mass), in units
+        of ParticleMassMsun. This is useful for multi-dm-mass
+        simulations. Note that this will only give sensible results for
+        halos that are not "polluted" by lower resolution
+        particles. Default: ``None``.
         
     Returns
     -------
@@ -187,7 +194,8 @@
     """
     def __init__(self, ts, num_readers = 1, num_writers = None,
             outbase="rockstar_halos", dm_type=1, 
-            force_res=None, total_particles=None, dm_only=False):
+            force_res=None, total_particles=None, dm_only=False,
+            hires_dm_mass=None):
         mylog.warning("The citation for the Rockstar halo finder can be found at")
         mylog.warning("http://adsabs.harvard.edu/abs/2013ApJ...762..109B")
         ParallelAnalysisInterface.__init__(self)
@@ -217,6 +225,7 @@
             self.force_res = force_res
         self.total_particles = total_particles
         self.dm_only = dm_only
+        self.hires_dm_mass = hires_dm_mass
         # Setup pool and workgroups.
         self.pool, self.workgroup = self.runner.setup_pool()
         p = self._setup_parameters(ts)
@@ -227,28 +236,51 @@
     def _setup_parameters(self, ts):
         if self.workgroup.name != "readers": return None
         tpf = ts[0]
+
         def _particle_count(field, data):
-            if self.dm_only:
-                return np.prod(data["particle_position_x"].shape)
             try:
-                return (data["particle_type"]==self.dm_type).sum()
+                data["particle_type"]
+                has_particle_type=True
             except KeyError:
-                return np.prod(data["particle_position_x"].shape)
+                has_particle_type=False
+                
+            if (self.dm_only or (not has_particle_type)):
+                if self.hires_dm_mass is None:
+                    return np.prod(data["particle_position_x"].shape)
+                else:
+                    return (data['ParticleMassMsun'] < self.hires_dm_mass*1.1).sum()
+            elif has_particle_type:
+                if self.hires_dm_mass is None:
+                    return (data["particle_type"]==self.dm_type).sum()
+                else:
+                    return ( (data["particle_type"]==self.dm_type) & 
+                             (data['ParticleMassMsun'] < self.hires_dm_mass*1.1) ).sum()
+            else:                
+                raise RuntimeError() # should never get here
+
         add_field("particle_count", function=_particle_count,
                   not_in_all=True, particle_type=True)
         dd = tpf.h.all_data()
         # Get DM particle mass.
         all_fields = set(tpf.h.derived_field_list + tpf.h.field_list)
-        for g in tpf.h._get_objs("grids"):
-            if g.NumberOfParticles == 0: continue
-            if self.dm_only:
-                iddm = Ellipsis
-            elif "particle_type" in all_fields:
-                iddm = g["particle_type"] == self.dm_type
-            else:
-                iddm = Ellipsis
-            particle_mass = g['ParticleMassMsun'][iddm][0] / tpf.hubble_constant
-            break
+        has_particle_type = ("particle_type" in all_fields)
+
+        if self.hires_dm_mass is None:
+            for g in tpf.h._get_objs("grids"):
+                if g.NumberOfParticles == 0: continue
+
+                if (self.dm_only or (not has_particle_type)):
+                    iddm = Ellipsis
+                elif has_particle_type:
+                    iddm = g["particle_type"] == self.dm_type
+                else:                    
+                    iddm = Ellipsis # should never get here
+
+                particle_mass = g['ParticleMassMsun'][iddm][0] / tpf.hubble_constant
+                break
+        else:
+            particle_mass = self.hires_dm_mass / tpf.hubble_constant
+
         p = {}
         if self.total_particles is None:
             # Get total_particles in parallel.
@@ -302,6 +334,7 @@
                     force_res = self.force_res,
                     particle_mass = float(self.particle_mass),
                     dm_only = int(self.dm_only),
+                    hires_only = (self.hires_dm_mass is not None),
                     **kwargs)
         # Make the directory to store the halo lists in.
         if self.comm.rank == 0:

diff -r 329d11eeff224ed16d054e989680ba6f193318e9 -r 3161cb50ce626380cbf2958e0abacf61677072e3 yt/analysis_modules/halo_finding/rockstar/rockstar_interface.pyx
--- a/yt/analysis_modules/halo_finding/rockstar/rockstar_interface.pyx
+++ b/yt/analysis_modules/halo_finding/rockstar/rockstar_interface.pyx
@@ -163,6 +163,7 @@
     SCALE_NOW = 1.0/(pf.current_redshift+1.0)
     # Now we want to grab data from only a subset of the grids for each reader.
     all_fields = set(pf.h.derived_field_list + pf.h.field_list)
+    has_particle_type = ("particle_type" in all_fields)
 
     # First we need to find out how many this reader is going to read in
     # if the number of readers > 1.
@@ -170,12 +171,19 @@
         local_parts = 0
         for g in pf.h._get_objs("grids"):
             if g.NumberOfParticles == 0: continue
-            if rh.dm_only:
-                iddm = Ellipsis
-            elif "particle_type" in all_fields:
-                iddm = g["particle_type"] == rh.dm_type
+            if (rh.dm_only or (not has_particle_type)):
+                if rh.hires_only:
+                    iddm = (g['ParticleMassMsun'] < PARTICLE_MASS*1.1)
+                else:
+                    iddm = Ellipsis
+            elif has_particle_type:
+                if rh.hires_only:
+                    iddm = ( (g["particle_type"]==rh.dm_type) &
+                             (g['ParticleMassMsun'] < PARTICLE_MASS*1.1) )                    
+                else:
+                    iddm = g["particle_type"] == rh.dm_type
             else:
-                iddm = Ellipsis
+                iddm = Ellipsis # should never get here
             arri = g["particle_index"].astype("int64")
             arri = arri[iddm] #pick only DM
             local_parts += arri.size
@@ -195,12 +203,19 @@
     pi = 0
     for g in pf.h._get_objs("grids"):
         if g.NumberOfParticles == 0: continue
-        if rh.dm_only:
-            iddm = Ellipsis
-        elif "particle_type" in all_fields:
-            iddm = g["particle_type"] == rh.dm_type
-        else:
-            iddm = Ellipsis
+        if (rh.dm_only or (not has_particle_type)):
+            if rh.hires_only:
+                iddm = (g['ParticleMassMsun'] < PARTICLE_MASS*1.1)
+            else:
+                iddm = Ellipsis
+        elif has_particle_type:
+            if rh.hires_only:
+                iddm = ( (g["particle_type"]==rh.dm_type) &
+                         (g['ParticleMassMsun'] < PARTICLE_MASS*1.1) )                    
+            else:
+                iddm = g["particle_type"] == rh.dm_type
+        else:            
+            iddm = Ellipsis # should never get here
         arri = g["particle_index"].astype("int64")
         arri = arri[iddm] #pick only DM
         npart = arri.size
@@ -230,6 +245,7 @@
     cdef public int dm_type
     cdef public int total_particles
     cdef public int dm_only
+    cdef public int hires_only
 
     def __cinit__(self, ts):
         self.ts = ts
@@ -244,7 +260,7 @@
                        int writing_port = -1, int block_ratio = 1,
                        int periodic = 1, force_res=None,
                        int min_halo_size = 25, outbase = "None",
-                       int dm_only = 0):
+                       int dm_only = 0, int hires_only = False):
         global PARALLEL_IO, PARALLEL_IO_SERVER_ADDRESS, PARALLEL_IO_SERVER_PORT
         global FILENAME, FILE_FORMAT, NUM_SNAPS, STARTING_SNAP, h0, Ol, Om
         global BOX_SIZE, PERIODIC, PARTICLE_MASS, NUM_BLOCKS, NUM_READERS
@@ -276,6 +292,7 @@
         TOTAL_PARTICLES = total_particles
         self.block_ratio = block_ratio
         self.dm_only = dm_only
+        self.hires_only = hires_only
         
         tpf = self.ts[0]
         h0 = tpf.hubble_constant

diff -r 329d11eeff224ed16d054e989680ba6f193318e9 -r 3161cb50ce626380cbf2958e0abacf61677072e3 yt/analysis_modules/halo_merger_tree/enzofof_merger_tree.py
--- a/yt/analysis_modules/halo_merger_tree/enzofof_merger_tree.py
+++ b/yt/analysis_modules/halo_merger_tree/enzofof_merger_tree.py
@@ -810,6 +810,6 @@
         ax.set_xscale("log")
     if y_log:
         ax.set_yscale("log")
-    ofn = "%s_%s_%s.png" % (basename, fields[0], fields[1])
+    ofn = "%s/%s_%s_%s.png" % (FOF_directory, basename, fields[0], fields[1])
     plt.savefig(ofn)
     plt.clf()

diff -r 329d11eeff224ed16d054e989680ba6f193318e9 -r 3161cb50ce626380cbf2958e0abacf61677072e3 yt/data_objects/image_array.py
--- a/yt/data_objects/image_array.py
+++ b/yt/data_objects/image_array.py
@@ -71,12 +71,12 @@
 
     >>> im = np.zeros([64,128,3])
     >>> for i in xrange(im.shape[0]):
-    >>>     for k in xrange(im.shape[2]):
-    >>>         im[i,:,k] = np.linspace(0.,0.3*k, im.shape[1])
+    ...     for k in xrange(im.shape[2]):
+    ...         im[i,:,k] = np.linspace(0.,0.3*k, im.shape[1])
 
     >>> myinfo = {'field':'dinosaurs', 'east_vector':np.array([1.,0.,0.]), 
-    >>>     'north_vector':np.array([0.,0.,1.]), 'normal_vector':np.array([0.,1.,0.]),  
-    >>>     'width':0.245, 'units':'cm', 'type':'rendering'}
+    ...     'north_vector':np.array([0.,0.,1.]), 'normal_vector':np.array([0.,1.,0.]),  
+    ...     'width':0.245, 'units':'cm', 'type':'rendering'}
 
     >>> im_arr = ImageArray(im, info=myinfo)
     >>> im_arr.save('test_ImageArray')
@@ -112,12 +112,12 @@
         -------- 
         >>> im = np.zeros([64,128,3])
         >>> for i in xrange(im.shape[0]):
-        >>>     for k in xrange(im.shape[2]):
-        >>>         im[i,:,k] = np.linspace(0.,0.3*k, im.shape[1])
+        ...     for k in xrange(im.shape[2]):
+        ...         im[i,:,k] = np.linspace(0.,0.3*k, im.shape[1])
 
         >>> myinfo = {'field':'dinosaurs', 'east_vector':np.array([1.,0.,0.]), 
-        >>>     'north_vector':np.array([0.,0.,1.]), 'normal_vector':np.array([0.,1.,0.]),  
-        >>>     'width':0.245, 'units':'cm', 'type':'rendering'}
+        ...     'north_vector':np.array([0.,0.,1.]), 'normal_vector':np.array([0.,1.,0.]),  
+        ...     'width':0.245, 'units':'cm', 'type':'rendering'}
 
         >>> im_arr = ImageArray(im, info=myinfo)
         >>> im_arr.write_hdf5('test_ImageArray.h5')
@@ -133,38 +133,191 @@
             d.attrs.create(k, v)
         f.close()
 
-    def write_png(self, filename, clip_ratio=None):
+    def add_background_color(self, background='black', inline=True):
+        r"""Adds a background color to a 4-channel ImageArray
+
+        This adds a background color to a 4-channel ImageArray, by default
+        doing so inline.  The ImageArray must already be normalized to the
+        [0,1] range.
+
+        Parameters
+        ----------
+        background: 
+            This can be used to set a background color for the image, and can
+            take several types of values:
+
+               * ``white``: white background, opaque
+               * ``black``: black background, opaque
+               * ``None``: transparent background
+               * 4-element array [r,g,b,a]: arbitrary rgba setting.
+
+            Default: 'black'
+        inline: boolean, optional
+            If True, original ImageArray is modified. If False, a copy is first
+            created, then modified. Default: True
+
+        Returns
+        -------
+        out: ImageArray
+            The modified ImageArray with a background color added.
+       
+        Examples
+        --------
+        >>> im = np.zeros([64,128,4])
+        >>> for i in xrange(im.shape[0]):
+        ...     for k in xrange(im.shape[2]):
+        ...         im[i,:,k] = np.linspace(0.,10.*k, im.shape[1])
+
+        >>> im_arr = ImageArray(im)
+        >>> im_arr.rescale()
+        >>> new_im = im_arr.add_background_color([1.,0.,0.,1.], inline=False)
+        >>> new_im.write_png('red_bg.png')
+        >>> im_arr.add_background_color('black')
+        >>> im_arr.write_png('black_bg.png')
+        """
+        assert(self.shape[-1] == 4)
+        
+        if background == None:
+            background = (0., 0., 0., 0.)
+        elif background == 'white':
+            background = (1., 1., 1., 1.)
+        elif background == 'black':
+            background = (0., 0., 0., 1.)
+
+        # Alpha blending to background
+        if inline:
+            out = self
+        else:
+            out = self.copy()
+
+        for i in range(3):
+            out[:,:,i] = self[:,:,i]*self[:,:,3] + \
+                    background[i]*background[3]*(1.0-self[:,:,3])
+        out[:,:,3] = self[:,:,3] + background[3]*(1.0-self[:,:,3]) 
+        return out 
+
+
+    def rescale(self, cmax=None, amax=None, inline=True):
+        r"""Rescales the image to be in [0,1] range.
+
+        Parameters
+        ----------
+        cmax: float, optional
+            Normalization value to use for rgb channels. Defaults to None,
+            corresponding to using the maximum value in the rgb channels.
+        amax: float, optional
+            Normalization value to use for alpha channel. Defaults to None,
+            corresponding to using the maximum value in the alpha channel.
+        inline: boolean, optional
+            Specifies whether or not the rescaling is done inline. If false,
+            a new copy of the ImageArray will be created, returned. 
+            Default:True.
+
+        Returns
+        -------
+        out: ImageArray
+            The rescaled ImageArray, clipped to the [0,1] range.
+
+        Notes
+        -----
+        This requires that the shape of the ImageArray to have a length of 3,
+        and for the third dimension to be >= 3.  If the third dimension has
+        a shape of 4, the alpha channel will also be rescaled.
+       
+        Examples
+        -------- 
+        >>> im = np.zeros([64,128,4])
+        >>> for i in xrange(im.shape[0]):
+        ...     for k in xrange(im.shape[2]):
+        ...         im[i,:,k] = np.linspace(0.,0.3*k, im.shape[1])
+
+        >>> im_arr.write_png('original.png')
+        >>> im_arr.rescale()
+        >>> im_arr.write_png('normalized.png')
+
+        """
+        assert(len(self.shape) == 3)
+        assert(self.shape[2] >= 3)
+        if inline:
+            out = self
+        else:
+            out = self.copy()
+        if cmax is None: 
+            cmax = self[:,:,:3].sum(axis=2).max()
+
+        np.multiply(self[:,:,:3], 1./cmax, out[:,:,:3])
+
+        if self.shape[2] == 4:
+            if amax is None:
+                amax = self[:,:,3].max()
+            if amax > 0.0:
+                np.multiply(self[:,:,3], 1./amax, out[:,:,3])
+        
+        np.clip(out, 0.0, 1.0, out)
+        return out
+
+    def write_png(self, filename, clip_ratio=None, background='black',
+                 rescale=True):
         r"""Writes ImageArray to png file.
 
         Parameters
         ----------
         filename: string
             Note filename not be modified.
+        clip_ratio: float, optional
+            Image will be clipped before saving to the standard deviation
+            of the image multiplied by this value.  Useful for enhancing 
+            images. Default: None
+        background: 
+            This can be used to set a background color for the image, and can
+            take several types of values:
+
+               * ``white``: white background, opaque
+               * ``black``: black background, opaque
+               * ``None``: transparent background
+               * 4-element array [r,g,b,a]: arbitrary rgba setting.
+
+            Default: 'black'
+        rescale: boolean, optional
+            If True, will write out a rescaled image (without modifying the
+            original image). Default: True
        
         Examples
         --------
-        
-        >>> im = np.zeros([64,128,3])
+        >>> im = np.zeros([64,128,4])
         >>> for i in xrange(im.shape[0]):
-        >>>     for k in xrange(im.shape[2]):
-        >>>         im[i,:,k] = np.linspace(0.,0.3*k, im.shape[1])
+        ...     for k in xrange(im.shape[2]):
+        ...         im[i,:,k] = np.linspace(0.,10.*k, im.shape[1])
 
-        >>> myinfo = {'field':'dinosaurs', 'east_vector':np.array([1.,0.,0.]), 
-        >>>     'north_vector':np.array([0.,0.,1.]), 'normal_vector':np.array([0.,1.,0.]),  
-        >>>     'width':0.245, 'units':'cm', 'type':'rendering'}
-
-        >>> im_arr = ImageArray(im, info=myinfo)
-        >>> im_arr.write_png('test_ImageArray.png')
+        >>> im_arr = ImageArray(im)
+        >>> im_arr.write_png('standard.png')
+        >>> im_arr.write_png('non-scaled.png', rescale=False)
+        >>> im_arr.write_png('black_bg.png', background='black')
+        >>> im_arr.write_png('white_bg.png', background='white')
+        >>> im_arr.write_png('green_bg.png', background=[0,1,0,1])
+        >>> im_arr.write_png('transparent_bg.png', background=None)
 
         """
+        if rescale:
+            scaled = self.rescale(inline=False)
+        else:
+            scaled = self
+
+        if self.shape[-1] == 4:
+            out = scaled.add_background_color(background, inline=False)
+        else:
+            out = scaled
+
         if filename[-4:] != '.png': 
             filename += '.png'
 
         if clip_ratio is not None:
-            return write_bitmap(self.swapaxes(0, 1), filename,
-                                clip_ratio * self.std())
+            nz = out[:,:,:3][out[:,:,:3].nonzero()]
+            return write_bitmap(out.swapaxes(0, 1), filename,
+                                nz.mean() + \
+                                clip_ratio * nz.std())
         else:
-            return write_bitmap(self.swapaxes(0, 1), filename)
+            return write_bitmap(out.swapaxes(0, 1), filename)
 
     def write_image(self, filename, color_bounds=None, channel=None,  cmap_name="algae", func=lambda x: x):
         r"""Writes a single channel of the ImageArray to a png file.
@@ -197,11 +350,11 @@
         
         >>> im = np.zeros([64,128])
         >>> for i in xrange(im.shape[0]):
-        >>>     im[i,:] = np.linspace(0.,0.3*k, im.shape[1])
+        ...     im[i,:] = np.linspace(0.,0.3*k, im.shape[1])
 
         >>> myinfo = {'field':'dinosaurs', 'east_vector':np.array([1.,0.,0.]), 
-        >>>     'north_vector':np.array([0.,0.,1.]), 'normal_vector':np.array([0.,1.,0.]),  
-        >>>     'width':0.245, 'units':'cm', 'type':'rendering'}
+        ...     'north_vector':np.array([0.,0.,1.]), 'normal_vector':np.array([0.,1.,0.]),  
+        ...     'width':0.245, 'units':'cm', 'type':'rendering'}
 
         >>> im_arr = ImageArray(im, info=myinfo)
         >>> im_arr.write_image('test_ImageArray.png')
@@ -245,27 +398,3 @@
 
     __doc__ += np.ndarray.__doc__
 
-if __name__ == "__main__":
-    im = np.zeros([64,128,3])
-    for i in xrange(im.shape[0]):
-        for k in xrange(im.shape[2]):
-            im[i,:,k] = np.linspace(0.,0.3*k, im.shape[1])
-
-    myinfo = {'field':'dinosaurs', 'east_vector':np.array([1.,0.,0.]), 
-        'north_vector':np.array([0.,0.,1.]), 'normal_vector':np.array([0.,1.,0.]),  
-        'width':0.245, 'units':'cm', 'type':'rendering'}
-
-    im_arr = ImageArray(im, info=myinfo)
-    im_arr.save('test_3d_ImageArray')
-
-    im = np.zeros([64,128])
-    for i in xrange(im.shape[0]):
-        im[i,:] = np.linspace(0.,0.3*k, im.shape[1])
-
-    myinfo = {'field':'dinosaurs', 'east_vector':np.array([1.,0.,0.]), 
-        'north_vector':np.array([0.,0.,1.]), 'normal_vector':np.array([0.,1.,0.]),  
-        'width':0.245, 'units':'cm', 'type':'rendering'}
-
-    im_arr = ImageArray(im, info=myinfo)
-    im_arr.save('test_2d_ImageArray')
-

diff -r 329d11eeff224ed16d054e989680ba6f193318e9 -r 3161cb50ce626380cbf2958e0abacf61677072e3 yt/data_objects/tests/test_image_array.py
--- /dev/null
+++ b/yt/data_objects/tests/test_image_array.py
@@ -0,0 +1,130 @@
+from yt.testing import *
+from yt.data_objects.image_array import ImageArray
+import numpy as np
+import os
+import tempfile
+import shutil
+
+def setup():
+    from yt.config import ytcfg
+    ytcfg["yt","__withintesting"] = "True"
+    np.seterr(all = 'ignore')
+
+def test_rgba_rescale():
+    im = np.zeros([64,128,4])
+    for i in xrange(im.shape[0]):
+        for k in xrange(im.shape[2]):
+            im[i,:,k] = np.linspace(0.,10.*k, im.shape[1])
+    im_arr = ImageArray(im)
+
+    new_im = im_arr.rescale(inline=False)
+    yield assert_equal, im_arr[:,:,:3].max(), 2*10.
+    yield assert_equal, im_arr[:,:,3].max(), 3*10.
+    yield assert_equal, new_im[:,:,:3].sum(axis=2).max(), 1.0 
+    yield assert_equal, new_im[:,:,3].max(), 1.0
+
+    im_arr.rescale()
+    yield assert_equal, im_arr[:,:,:3].sum(axis=2).max(), 1.0
+    yield assert_equal, im_arr[:,:,3].max(), 1.0
+
+def test_image_array_hdf5():
+    # Perform I/O in safe place instead of yt main dir
+    tmpdir = tempfile.mkdtemp()
+    curdir = os.getcwd()
+    os.chdir(tmpdir)
+
+    im = np.zeros([64,128,3])
+    for i in xrange(im.shape[0]):
+        for k in xrange(im.shape[2]):
+            im[i,:,k] = np.linspace(0.,0.3*k, im.shape[1])
+
+    myinfo = {'field':'dinosaurs', 'east_vector':np.array([1.,0.,0.]), 
+        'north_vector':np.array([0.,0.,1.]), 'normal_vector':np.array([0.,1.,0.]),  
+        'width':0.245, 'units':'cm', 'type':'rendering'}
+
+    im_arr = ImageArray(im, info=myinfo)
+    im_arr.save('test_3d_ImageArray')
+
+    im = np.zeros([64,128])
+    for i in xrange(im.shape[0]):
+        im[i,:] = np.linspace(0.,0.3*k, im.shape[1])
+
+    myinfo = {'field':'dinosaurs', 'east_vector':np.array([1.,0.,0.]), 
+        'north_vector':np.array([0.,0.,1.]), 'normal_vector':np.array([0.,1.,0.]),  
+        'width':0.245, 'units':'cm', 'type':'rendering'}
+
+    im_arr = ImageArray(im, info=myinfo)
+    im_arr.save('test_2d_ImageArray')
+
+    os.chdir(curdir)
+    # clean up
+    shutil.rmtree(tmpdir)
+
+def test_image_array_rgb_png():
+    # Perform I/O in safe place instead of yt main dir
+    tmpdir = tempfile.mkdtemp()
+    curdir = os.getcwd()
+    os.chdir(tmpdir)
+
+    im = np.zeros([64,128,3])
+    for i in xrange(im.shape[0]):
+        for k in xrange(im.shape[2]):
+            im[i,:,k] = np.linspace(0.,10.*k, im.shape[1])
+
+    im_arr = ImageArray(im)
+    im_arr.write_png('standard.png')
+
+def test_image_array_rgba_png():
+    # Perform I/O in safe place instead of yt main dir
+    tmpdir = tempfile.mkdtemp()
+    curdir = os.getcwd()
+    os.chdir(tmpdir)
+
+    im = np.zeros([64,128,4])
+    for i in xrange(im.shape[0]):
+        for k in xrange(im.shape[2]):
+            im[i,:,k] = np.linspace(0.,10.*k, im.shape[1])
+
+    im_arr = ImageArray(im)
+    im_arr.write_png('standard.png')
+    im_arr.write_png('non-scaled.png', rescale=False)
+    im_arr.write_png('black_bg.png', background='black')
+    im_arr.write_png('white_bg.png', background='white')
+    im_arr.write_png('green_bg.png', background=[0.,1.,0.,1.])
+    im_arr.write_png('transparent_bg.png', background=None)
+
+
+def test_image_array_background():
+    # Perform I/O in safe place instead of yt main dir
+    tmpdir = tempfile.mkdtemp()
+    curdir = os.getcwd()
+    os.chdir(tmpdir)
+
+    im = np.zeros([64,128,4])
+    for i in xrange(im.shape[0]):
+        for k in xrange(im.shape[2]):
+            im[i,:,k] = np.linspace(0.,10.*k, im.shape[1])
+
+    im_arr = ImageArray(im)
+    im_arr.rescale()
+    new_im = im_arr.add_background_color([1.,0.,0.,1.], inline=False)
+    new_im.write_png('red_bg.png')
+    im_arr.add_background_color('black')
+    im_arr.write_png('black_bg2.png')
+ 
+    os.chdir(curdir)
+    # clean up
+    shutil.rmtree(tmpdir)
+
+
+
+
+
+
+
+
+
+
+
+
+

diff -r 329d11eeff224ed16d054e989680ba6f193318e9 -r 3161cb50ce626380cbf2958e0abacf61677072e3 yt/utilities/amr_kdtree/amr_kdtree.py
--- a/yt/utilities/amr_kdtree/amr_kdtree.py
+++ b/yt/utilities/amr_kdtree/amr_kdtree.py
@@ -35,12 +35,6 @@
 from yt.utilities.lib.grid_traversal import PartitionedGrid
 from yt.utilities.math_utils import periodic_position
 
-import pdb
-
-def my_break():
-    my_debug = False 
-    if my_debug: pdb.set_trace()
-
 steps = np.array([[-1, -1, -1], [-1, -1,  0], [-1, -1,  1],
                   [-1,  0, -1], [-1,  0,  0], [-1,  0,  1],
                   [-1,  1, -1], [-1,  1,  0], [-1,  1,  1],
@@ -81,7 +75,6 @@
         self.build(grids)
 
     def add_grids(self, grids):
-        my_break() 
         lvl_range = range(self.min_level, self.max_level+1)
         if grids is None:
             level_iter = self.pf.hierarchy.get_levels()
@@ -95,7 +88,6 @@
                 gles =  np.array([g.LeftEdge for g in grids])[gmask]
                 gres =  np.array([g.RightEdge for g in grids])[gmask]
                 gids = np.array([g.id for g in grids])[gmask]
-                my_break()
                 add_grids(self.trunk, gles, gres, gids, self.comm_rank, self.comm_size)
                 del gles, gres, gids, grids
         else:

diff -r 329d11eeff224ed16d054e989680ba6f193318e9 -r 3161cb50ce626380cbf2958e0abacf61677072e3 yt/utilities/lib/misc_utilities.pyx
--- a/yt/utilities/lib/misc_utilities.pyx
+++ b/yt/utilities/lib/misc_utilities.pyx
@@ -126,19 +126,24 @@
     cdef int nx = image.shape[0]
     cdef int ny = image.shape[1]
     cdef int nl = xs.shape[0]
-    cdef np.float64_t alpha[3], nalpha 
+    cdef np.float64_t alpha[4]
     cdef int i, j
     cdef int dx, dy, sx, sy, e2, err
     cdef np.int64_t x0, x1, y0, y1
+    cdef int has_alpha = (image.shape[-1] == 4)
     for j in range(0, nl, 2):
         # From wikipedia http://en.wikipedia.org/wiki/Bresenham's_line_algorithm
         x0 = xs[j]; y0 = ys[j]; x1 = xs[j+1]; y1 = ys[j+1]
         dx = abs(x1-x0)
         dy = abs(y1-y0)
         err = dx - dy
-        for i in range(3):
-            alpha[i] = colors[j/points_per_color,3]*colors[j/points_per_color,i]
-        nalpha = 1.0-colors[j/points_per_color,3]
+        if has_alpha:
+            for i in range(4):
+                alpha[i] = colors[j/points_per_color,i]
+        else:
+            for i in range(3):
+                alpha[i] = colors[j/points_per_color,3]*\
+                        colors[j/points_per_color,i]
         if x0 < x1: 
             sx = 1
         else:
@@ -153,8 +158,13 @@
             elif (y0 < 0 and sy == -1): break
             elif (y0 >= nx and sy == 1): break
             if (x0 >=0 and x0 < nx and y0 >= 0 and y0 < ny):
-                for i in range(3):
-                    image[x0,y0,i] = (1.-alpha[i])*image[x0,y0,i] + alpha[i]
+                if has_alpha:
+                    for i in range(4):
+                        image[x0,y0,i] = (1.-alpha[i])*image[x0,y0,i] + alpha[i]
+                else:
+                    for i in range(3):
+                        image[x0,y0,i] = (1.-alpha[i])*image[x0,y0,i] + alpha[i]
+
             if (x0 == x1 and y0 == y1):
                 break
             e2 = 2*err

diff -r 329d11eeff224ed16d054e989680ba6f193318e9 -r 3161cb50ce626380cbf2958e0abacf61677072e3 yt/utilities/particle_generator.py
--- a/yt/utilities/particle_generator.py
+++ b/yt/utilities/particle_generator.py
@@ -7,9 +7,8 @@
 
     default_fields = ["particle_position_x",
                       "particle_position_y",
-                      "particle_position_z",
-                      "particle_index"]
-
+                      "particle_position_z"]
+    
     def __init__(self, pf, num_particles, field_list) :
         """
         Base class for generating particle fields which may be applied to
@@ -21,20 +20,21 @@
         self.pf = pf
         self.num_particles = num_particles
         self.field_list = field_list
-            
+        self.field_list.append("particle_index")
+        
         try :
             self.posx_index = self.field_list.index(self.default_fields[0])
             self.posy_index = self.field_list.index(self.default_fields[1])
             self.posz_index = self.field_list.index(self.default_fields[2])
-            self.index_index = self.field_list.index(self.default_fields[3])
         except :
             raise KeyError("Field list must contain the following fields: " +
                            "\'particle_position_x\', \'particle_position_y\'" +
-                           ", \'particle_position_z\', \'particle_index\' ")
-
+                           ", \'particle_position_z\' ")
+        self.index_index = self.field_list.index("particle_index")
+        
         self.num_grids = self.pf.h.num_grids
         self.NumberOfParticles = np.zeros((self.num_grids), dtype='int64')
-        self.ParticleIndices = np.zeros(self.num_grids + 1, dtype='int64')
+        self.ParticleGridIndices = np.zeros(self.num_grids + 1, dtype='int64')
         
         self.num_fields = len(self.field_list)
         
@@ -78,8 +78,8 @@
         Return a dict containing all of the particle fields in the specified grid.
         """
         ind = grid.id-grid._id_offset
-        start = self.ParticleIndices[ind]
-        end = self.ParticleIndices[ind+1]
+        start = self.ParticleGridIndices[ind]
+        end = self.ParticleGridIndices[ind+1]
         return dict([(field, self.particles[start:end,self.field_list.index(field)])
                      for field in self.field_list])
     
@@ -97,9 +97,9 @@
                                              minlength=self.num_grids)
         if self.num_grids > 1 :
             np.add.accumulate(self.NumberOfParticles.squeeze(),
-                              out=self.ParticleIndices[1:])
+                              out=self.ParticleGridIndices[1:])
         else :
-            self.ParticleIndices[1] = self.NumberOfParticles.squeeze()
+            self.ParticleGridIndices[1] = self.NumberOfParticles.squeeze()
         if setup_fields is not None:
             for key, value in setup_fields.items():
                 if key not in self.default_fields:
@@ -130,8 +130,8 @@
         for i, grid in enumerate(self.pf.h.grids) :
             pbar.update(i)
             if self.NumberOfParticles[i] > 0:
-                start = self.ParticleIndices[i]
-                end = self.ParticleIndices[i+1]
+                start = self.ParticleGridIndices[i]
+                end = self.ParticleGridIndices[i+1]
                 # Note we add one ghost zone to the grid!
                 cube = grid.retrieve_ghost_zones(1, mapping_dict.keys())
                 le = np.array(grid.LeftEdge).astype(np.float64)
@@ -257,7 +257,7 @@
         >>> le = np.array([0.25,0.25,0.25])
         >>> re = np.array([0.75,0.75,0.75])
         >>> fields = ["particle_position_x","particle_position_y",
-        >>>           "particle_position_z","particle_index",
+        >>>           "particle_position_z",
         >>>           "particle_density","particle_temperature"]
         >>> particles = LatticeParticleGenerator(pf, dims, le, re, fields)
         """
@@ -321,7 +321,7 @@
         >>> sphere = pf.h.sphere(pf.domain_center, 0.5)
         >>> num_p = 100000
         >>> fields = ["particle_position_x","particle_position_y",
-        >>>           "particle_position_z","particle_index",
+        >>>           "particle_position_z",
         >>>           "particle_density","particle_temperature"]
         >>> particles = WithDensityParticleGenerator(pf, sphere, num_particles,
         >>>                                          fields, density_field='Dark_Matter_Density')

diff -r 329d11eeff224ed16d054e989680ba6f193318e9 -r 3161cb50ce626380cbf2958e0abacf61677072e3 yt/visualization/image_writer.py
--- a/yt/visualization/image_writer.py
+++ b/yt/visualization/image_writer.py
@@ -28,6 +28,7 @@
 from yt.funcs import *
 import _colormap_data as cmd
 import yt.utilities.lib as au
+import __builtin__
 
 def scale_image(image, mi=None, ma=None):
     r"""Scale an image ([NxNxM] where M = 1-4) to be uint8 and values scaled 
@@ -120,14 +121,16 @@
     r"""Write out a bitmapped image directly to a PNG file.
 
     This accepts a three- or four-channel `bitmap_array`.  If the image is not
-    already uint8, it will be scaled and converted.  If it is not four channel, a
-    fourth alpha channel will be added and set to fully opaque.  The resultant
-    image will be directly written to `filename` as a PNG with no colormap
-    applied.  `max_val` is a value used if the array is passed in as anything
-    other than uint8; it will be the value used for scaling and clipping when the
-    array is converted.  Additionally, the minimum is assumed to be zero; this
-    makes it primarily suited for the results of volume rendered images, rather
-    than misaligned projections.
+    already uint8, it will be scaled and converted.  If it is four channel,
+    only the first three channels will be scaled, while the fourth channel is
+    assumed to be in the range of [0,1]. If it is not four channel, a fourth
+    alpha channel will be added and set to fully opaque.  The resultant image
+    will be directly written to `filename` as a PNG with no colormap applied.
+    `max_val` is a value used if the array is passed in as anything other than
+    uint8; it will be the value used for scaling and clipping in the first
+    three channels when the array is converted.  Additionally, the minimum is
+    assumed to be zero; this makes it primarily suited for the results of
+    volume rendered images, rather than misaligned projections.
 
     Parameters
     ----------
@@ -141,16 +144,19 @@
         The upper limit to clip values to in the output, if converting to uint8.
         If `bitmap_array` is already uint8, this will be ignore.
     """
-    if bitmap_array.dtype != np.uint8:
-        if max_val is None: max_val = bitmap_array.max()
-        bitmap_array = np.clip(bitmap_array / max_val, 0.0, 1.0) * 255
-        bitmap_array = bitmap_array.astype("uint8")
     if len(bitmap_array.shape) != 3 or bitmap_array.shape[-1] not in (3,4):
         raise RuntimeError
-    if bitmap_array.shape[-1] == 3:
+    if bitmap_array.dtype != np.uint8:
         s1, s2 = bitmap_array.shape[:2]
-        alpha_channel = 255*np.ones((s1,s2,1), dtype='uint8')
-        bitmap_array = np.concatenate([bitmap_array, alpha_channel], axis=-1)
+        if bitmap_array.shape[-1] == 3:
+            alpha_channel = 255*np.ones((s1,s2,1), dtype='uint8')
+        else:
+            alpha_channel = (255*bitmap_array[:,:,3]).astype('uint8')
+            alpha_channel.shape = s1, s2, 1
+        if max_val is None: max_val = bitmap_array[:,:,:3].max()
+        bitmap_array = np.clip(bitmap_array[:,:,:3] / max_val, 0.0, 1.0) * 255
+        bitmap_array = np.concatenate([bitmap_array.astype('uint8'),
+                                       alpha_channel], axis=-1)
     if transpose:
         bitmap_array = bitmap_array.swapaxes(0,1)
     if filename is not None:
@@ -433,7 +439,6 @@
 
 
 def write_fits(image, filename_prefix, clobber=True, coords=None, gzip_file=False) :
-
     """
     This will export a FITS image of a floating point array. The output filename is
     *filename_prefix*. If clobber is set to True, this will overwrite any existing
@@ -497,4 +502,32 @@
         clob = ""
         if (clobber) : clob="-f"
         system("gzip "+clob+" %s.fits" % (filename_prefix))
+
+def display_in_notebook(image, max_val=None):
+    """
+    A helper function to display images in an IPython notebook
     
+    Must be run from within an IPython notebook, or else it will raise
+    a YTNotInsideNotebook exception.
+        
+    Parameters
+    ----------
+    image : array_like
+        This is an (unscaled) array of floating point values, shape (N,N,3) or
+        (N,N,4) to display in the notebook. The first three channels will be
+        scaled automatically.  
+    max_val : float, optional
+        The upper limit to clip values of the image.  Only applies to the first
+        three channels.
+    """
+ 
+    if "__IPYTHON__" in dir(__builtin__):
+        from IPython.core.displaypub import publish_display_data
+        data = write_bitmap(image, None, max_val=max_val)
+        publish_display_data(
+            'yt.visualization.image_writer.display_in_notebook',
+            {'image/png' : data}
+        )
+    else:
+        raise YTNotInsideNotebook
+

diff -r 329d11eeff224ed16d054e989680ba6f193318e9 -r 3161cb50ce626380cbf2958e0abacf61677072e3 yt/visualization/plot_collection.py
--- a/yt/visualization/plot_collection.py
+++ b/yt/visualization/plot_collection.py
@@ -74,9 +74,7 @@
         self.images.append((os.path.basename(fn), np.fromfile(fn, dtype='c')))
 
 class PlotCollection(object):
-    r"""The primary interface for creating plots.
-
-    The PlotCollection object was created to ease the creation of multiple
+    r"""The PlotCollection object was created to ease the creation of multiple
     slices, projections and so forth made from a single parameter file.
     The concept is that when the width on one image changes, it should
     change on all the others.  The PlotCollection can create all plot types

diff -r 329d11eeff224ed16d054e989680ba6f193318e9 -r 3161cb50ce626380cbf2958e0abacf61677072e3 yt/visualization/volume_rendering/blenders.py
--- a/yt/visualization/volume_rendering/blenders.py
+++ b/yt/visualization/volume_rendering/blenders.py
@@ -11,3 +11,18 @@
             del nz
     np.clip(im, 0.0, 1.0, im)
 
+def enhance_rgba(im, stdval=6.0):
+    nzc = im[:,:,:3][im[:,:,:3]>0.0]
+    cmax = nzc.mean()+stdval*nzc.std()
+
+
+    nza = im[:,:,3][im[:,:,3]>0.0]
+    if len(nza) == 0:
+        im[:,:,3]=1.0
+        amax = 1.0
+    else:
+        amax = nza.mean()+stdval*nza.std()
+
+    im.rescale(amax=amax, cmax=cmax, inline=True)
+    np.clip(im, 0.0, 1.0, im)
+

diff -r 329d11eeff224ed16d054e989680ba6f193318e9 -r 3161cb50ce626380cbf2958e0abacf61677072e3 yt/visualization/volume_rendering/camera.py
--- a/yt/visualization/volume_rendering/camera.py
+++ b/yt/visualization/volume_rendering/camera.py
@@ -49,7 +49,7 @@
 from yt.utilities.parallel_tools.parallel_analysis_interface import \
     ParallelAnalysisInterface, ProcessorPool, parallel_objects
 from yt.utilities.amr_kdtree.api import AMRKDTree
-from .blenders import  enhance
+from .blenders import  enhance_rgba
 from numpy import pi
 
 def get_corners(le, re):
@@ -267,7 +267,8 @@
         px = (res[1]*(dy/self.width[1])).astype('int')
         return px, py, dz
 
-    def draw_grids(self, im, alpha=0.3, cmap='algae'):
+    def draw_grids(self, im, alpha=0.3, cmap='algae', min_level=None, 
+                   max_level=None):
         r"""Draws Grids on an existing volume rendering.
 
         By mapping grid level to a color, drawes edges of grids on 
@@ -284,6 +285,9 @@
             Default : 0.3
         cmap : string, optional
             Colormap to be used mapping grid levels to colors.
+        min_level, max_level : int, optional
+            Optional parameters to specify the min and max level grid boxes 
+            to overplot on the image.  
         
         Returns
         -------
@@ -298,6 +302,16 @@
         """
         corners = self.pf.h.grid_corners
         levels = self.pf.h.grid_levels[:,0]
+
+        if max_level is not None:
+            subset = levels <= max_level
+            levels = levels[subset]
+            corners = corners[:,:,subset]
+        if min_level is not None:
+            subset = levels >= min_level
+            levels = levels[subset]
+            corners = corners[:,:,subset]
+            
         colors = apply_colormap(levels*1.0,
                                 color_bounds=[0,self.pf.h.max_level],
                                 cmap_name=cmap)[0,:,:]*1.0/255.
@@ -315,11 +329,12 @@
         px, py, dz = self.project_to_plane(vertices, res=im.shape[:2])
         
         # Must normalize the image
-        ma = im.max()
-        if ma > 0.0: 
-            enhance(im)
+        nim = im.rescale(inline=False)
+        enhance_rgba(nim)
+        nim.add_background_color('black', inline=True)
        
-        lines(im, px, py, colors, 24)
+        lines(nim, px, py, colors, 24)
+        return nim
 
     def draw_line(self, im, x0, x1, color=None):
         r"""Draws a line on an existing volume rendering.
@@ -388,12 +403,14 @@
         >>> write_bitmap(im, 'render_with_domain_boundary.png')
 
         """
-
-        ma = im.max()
-        if ma > 0.0: 
-            enhance(im)
-        self.draw_box(im, self.pf.domain_left_edge, self.pf.domain_right_edge,
+        # Must normalize the image
+        nim = im.rescale(inline=False)
+        enhance_rgba(nim)
+        nim.add_background_color('black', inline=True)
+ 
+        self.draw_box(nim, self.pf.domain_left_edge, self.pf.domain_right_edge,
                         color=np.array([1.0,1.0,1.0,alpha]))
+        return nim
 
     def draw_box(self, im, le, re, color=None):
         r"""Draws a box on an existing volume rendering.
@@ -529,6 +546,8 @@
     def finalize_image(self, image):
         view_pos = self.front_center + self.orienter.unit_vectors[2] * 1.0e6 * self.width[2]
         image = self.volume.reduce_tree_images(image, view_pos)
+        if self.transfer_function.grey_opacity is False:
+            image[:,:,3]=1.0
         return image
 
     def _render(self, double_check, num_threads, image, sampler):
@@ -598,12 +617,14 @@
         self.annotate(ax.axes, enhance)
         self._pylab.savefig(fn, bbox_inches='tight', facecolor='black', dpi=dpi)
         
-    def save_image(self, fn, clip_ratio, image, transparent=False):
-        if self.comm.rank is 0 and fn is not None:
+    def save_image(self, image, fn=None, clip_ratio=None, transparent=False):
+        if self.comm.rank == 0 and fn is not None:
             if transparent:
-                image.write_png(fn, clip_ratio=clip_ratio)
+                image.write_png(fn, clip_ratio=clip_ratio, rescale=True,
+                                background=None)
             else:
-                image[:,:,:3].write_png(fn, clip_ratio=clip_ratio)
+                image.write_png(fn, clip_ratio=clip_ratio, rescale=True,
+                                background='black')
 
     def initialize_source(self):
         return self.volume.initialize_source()
@@ -619,7 +640,7 @@
         return info_dict
 
     def snapshot(self, fn = None, clip_ratio = None, double_check = False,
-                 num_threads = 0):
+                 num_threads = 0, transparent=False):
         r"""Ray-cast the camera.
 
         This method instructs the camera to take a snapshot -- i.e., call the ray
@@ -640,6 +661,9 @@
             If supplied, will use 'num_threads' number of OpenMP threads during
             the rendering.  Defaults to 0, which uses the environment variable
             OMP_NUM_THREADS.
+        transparent: bool, optional
+            Optionally saves out the 4-channel rgba image, which can appear 
+            empty if the alpha channel is low everywhere. Default: False
 
         Returns
         -------
@@ -655,7 +679,8 @@
         image = ImageArray(self._render(double_check, num_threads, 
                                         image, sampler),
                            info=self.get_information())
-        self.save_image(fn, clip_ratio, image)
+        self.save_image(image, fn=fn, clip_ratio=clip_ratio, 
+                       transparent=transparent)
         return image
 
     def show(self, clip_ratio = None):
@@ -1191,11 +1216,11 @@
         image = ImageArray(self._render(double_check, num_threads, 
                                         image, sampler),
                            info=self.get_information())
-        self.save_image(fn, clim, image, label = label)
+        self.save_image(image, fn=fn, clim=clim, label = label)
         return image
 
-    def save_image(self, fn, clim, image, label = None):
-        if self.comm.rank is 0 and fn is not None:
+    def save_image(self, image, fn=None, clim=None, label = None):
+        if self.comm.rank == 0 and fn is not None:
             # This assumes Density; this is a relatively safe assumption.
             import matplotlib.figure
             import matplotlib.backends.backend_agg
@@ -1509,7 +1534,7 @@
             sto.id = self.imj*self.nimx + self.imi
             sto.result = image
         image = self.reduce_images(my_storage)
-        self.save_image(fn, clip_ratio, image)
+        self.save_image(image, fn=fn, clip_ratio=clip_ratio)
         return image
 
     def reduce_images(self,im_dict):
@@ -2165,12 +2190,12 @@
         image = self.finalize_image(sampler.aimage)
         return image
 
-    def save_image(self, fn, clip_ratio, image):
+    def save_image(self, image, fn=None, clip_ratio=None):
         if self.pf.field_info[self.field].take_log:
             im = np.log10(image)
         else:
             im = image
-        if self.comm.rank is 0 and fn is not None:
+        if self.comm.rank == 0 and fn is not None:
             if clip_ratio is not None:
                 write_image(im, fn)
             else:
@@ -2197,7 +2222,7 @@
                                         image, sampler),
                            info=self.get_information())
 
-        self.save_image(fn, clip_ratio, image)
+        self.save_image(image, fn=fn, clip_ratio=clip_ratio)
 
         return image
     snapshot.__doc__ = Camera.snapshot.__doc__

Repository URL: https://bitbucket.org/yt_analysis/yt/

--

This is a commit notification from bitbucket.org. You are receiving
this because you have the service enabled, addressing the recipient of
this email.



More information about the yt-svn mailing list