[yt-svn] commit/yt: 4 new changesets

Bitbucket commits-noreply at bitbucket.org
Fri Dec 16 13:09:39 PST 2011


4 new commits in yt:


https://bitbucket.org/yt_analysis/yt/changeset/22ce5d86a60b/
changeset:   22ce5d86a60b
branch:      yt
user:        brittonsmith
date:        2011-12-15 16:48:56
summary:     Moved functionality to get a sphere for halo profiling to a separate
function to make way for a generalized halo analysis function.
affected #:  2 files

diff -r 40ea2908895ff6b9dd0dbbddd6e8da0346273886 -r 22ce5d86a60b71f9afb9f492a43b60f3463b7bb7 yt/analysis_modules/halo_profiler/api.py
--- a/yt/analysis_modules/halo_profiler/api.py
+++ b/yt/analysis_modules/halo_profiler/api.py
@@ -34,5 +34,5 @@
 from .multi_halo_profiler import \
     HaloProfiler, \
     FakeProfile, \
-    shift_projections, \
+    get_halo_sphere, \
     standard_fields


diff -r 40ea2908895ff6b9dd0dbbddd6e8da0346273886 -r 22ce5d86a60b71f9afb9f492a43b60f3463b7bb7 yt/analysis_modules/halo_profiler/multi_halo_profiler.py
--- a/yt/analysis_modules/halo_profiler/multi_halo_profiler.py
+++ b/yt/analysis_modules/halo_profiler/multi_halo_profiler.py
@@ -510,7 +510,7 @@
 
     def _get_halo_profile(self, halo, filename, virial_filter=True,
             force_write=False):
-        """Profile a single halo and write profile data to a file.
+        r"""Profile a single halo and write profile data to a file.
         If file already exists, read profile data from file.
         Return a dictionary of id, center, and virial quantities if virial_filter is True.
         """
@@ -528,39 +528,8 @@
                 mylog.error("Skipping halo with r_max / r_min = %f." % (halo['r_max']/r_min))
                 return None
 
-            sphere = self.pf.h.sphere(halo['center'], halo['r_max']/self.pf.units['mpc'])
-            if len(sphere._grids) == 0: return None
-            new_sphere = False
-
-            if self.recenter:
-                old = halo['center']
-                if self.recenter in centering_registry:
-                    new_x, new_y, new_z = \
-                        centering_registry[self.recenter](sphere)
-                else:
-                    # user supplied function
-                    new_x, new_y, new_z = self.recenter(sphere)
-                if new_x < self.pf.domain_left_edge[0] or \
-                        new_y < self.pf.domain_left_edge[1] or \
-                        new_z < self.pf.domain_left_edge[2]:
-                    mylog.info("Recentering rejected, skipping halo %d" % \
-                        halo['id'])
-                    return None
-                halo['center'] = [new_x, new_y, new_z]
-                d = self.pf['kpc'] * periodic_dist(old, halo['center'],
-                    self.pf.domain_right_edge - self.pf.domain_left_edge)
-                mylog.info("Recentered halo %d %1.3e kpc away." % (halo['id'], d))
-                # Expand the halo to account for recentering. 
-                halo['r_max'] += d / 1000 # d is in kpc -> want mpc
-                new_sphere = True
-
-            if new_sphere:
-                # Temporary solution to memory leak.
-                for g in self.pf.h.grids:
-                    g.clear_data()
-                sphere.clear_data()
-                del sphere
-                sphere = self.pf.h.sphere(halo['center'], halo['r_max']/self.pf.units['mpc'])
+            # get a sphere object to profile
+            sphere = get_halo_sphere(halo, self.pf, recenter=self.recenter)
 
             if self._need_bulk_velocity:
                 # Set bulk velocity to zero out radial velocity profiles.
@@ -733,7 +702,7 @@
                 # Set x and y limits, shift image if it overlaps domain boundary.
                 if need_per:
                     pw = self.projection_width/self.pf.units[self.projection_width_units]
-                    shift_projections(self.pf, projections, halo['center'], center, w)
+                    _shift_projections(self.pf, projections, halo['center'], center, w)
                     # Projection has now been shifted to center of box.
                     proj_left = [center[x_axis]-0.5*pw, center[y_axis]-0.5*pw]
                     proj_right = [center[x_axis]+0.5*pw, center[y_axis]+0.5*pw]
@@ -1002,7 +971,94 @@
         else:
             os.mkdir(my_output_dir)
 
-def shift_projections(pf, projections, oldCenter, newCenter, axis):
+def get_halo_sphere(halo, pf, recenter=None):
+    r"""Returns a sphere object for a given halo.
+        
+    With a dictionary containing halo properties, such as center 
+    and r_max, this creates a sphere object and optionally 
+    recenters and recreates the sphere using a recentering function.
+    This is to be used primarily to make spheres for a set of halos 
+    loaded by the HaloProfiler.
+    
+    Parameters
+    ----------
+    halo : dict, required
+        The dictionary containing halo properties used to make the sphere.
+        Required entries:
+            center : list with center coordinates.
+            r_max : sphere radius in Mpc.
+    pf : parameter file object, required
+        The parameter file from which the sphere will be made.
+    recenter : {None, string or function}
+        The exact location of the sphere center can significantly affect 
+        radial profiles.  The halo center loaded by the HaloProfiler will 
+        typically be the dark matter center of mass calculated by a halo 
+        finder.  However, this may not be the best location for centering 
+        profiles of baryon quantities.  For example, one may want to center 
+        on the maximum density.
+        If recenter is given as a string, one of the existing recentering 
+        functions will be used:
+            Min_Dark_Matter_Density : location of minimum dark matter density
+            Max_Dark_Matter_Density : location of maximum dark matter density
+            CoM_Dark_Matter_Density : dark matter center of mass
+            Min_Gas_Density : location of minimum gas density
+            Max_Gas_Density : location of maximum gas density
+            CoM_Gas_Density : gas center of mass
+            Min_Total_Density : location of minimum total density
+            Max_Total_Density : location of maximum total density
+            CoM_Total_Density : total center of mass
+            Min_Temperature : location of minimum temperature
+            Max_Temperature : location of maximum temperature
+        Alternately, a function can be supplied for custom recentering.
+        The function should take only one argument, a sphere object.
+            Example function:
+                def my_center_of_mass(data):
+                   my_x, my_y, my_z = data.quantities['CenterOfMass']()
+                   return (my_x, my_y, my_z)
+
+        Examples: this should primarily be used with the halo list of the HaloProfiler.
+        This is an example with an abstract halo asssuming a pre-defined pf.
+        >>> halo = {'center': [0.5, 0.5, 0.5], 'r_max': 1.0}
+        >>> my_sphere = get_halo_sphere(halo, pf, recenter='Max_Gas_Density')
+        >>> # Assuming the above example function has been defined.
+        >>> my_sphere = get_halo_sphere(halo, pf, recenter=my_center_of_mass)
+    """
+        
+    sphere = pf.h.sphere(halo['center'], halo['r_max']/pf.units['mpc'])
+    if len(sphere._grids) == 0: return None
+    new_sphere = False
+
+    if recenter:
+        old = halo['center']
+        if recenter in centering_registry:
+            new_x, new_y, new_z = \
+                centering_registry[recenter](sphere)
+        else:
+            # user supplied function
+            new_x, new_y, new_z = recenter(sphere)
+        if new_x < pf.domain_left_edge[0] or \
+                new_y < pf.domain_left_edge[1] or \
+                new_z < pf.domain_left_edge[2]:
+            mylog.info("Recentering rejected, skipping halo %d" % \
+                halo['id'])
+            return None
+        halo['center'] = [new_x, new_y, new_z]
+        d = pf['kpc'] * periodic_dist(old, halo['center'],
+            pf.domain_right_edge - pf.domain_left_edge)
+        mylog.info("Recentered halo %d %1.3e kpc away." % (halo['id'], d))
+        # Expand the halo to account for recentering. 
+        halo['r_max'] += d / 1000 # d is in kpc -> want mpc
+        new_sphere = True
+
+    if new_sphere:
+        # Temporary solution to memory leak.
+        for g in pf.h.grids:
+            g.clear_data()
+        sphere.clear_data()
+        del sphere
+        sphere = pf.h.sphere(halo['center'], halo['r_max']/pf.units['mpc'])
+
+def _shift_projections(pf, projections, oldCenter, newCenter, axis):
     """
     Shift projection data around.
     This is necessary when projecting a preiodic region.



https://bitbucket.org/yt_analysis/yt/changeset/97f7357fd0ce/
changeset:   97f7357fd0ce
branch:      yt
user:        brittonsmith
date:        2011-12-15 18:00:30
summary:     Added analyze_halo_sphere method to HaloProfiler to perform custom
analysis on every halo on a halo list.
affected #:  1 file

diff -r 22ce5d86a60b71f9afb9f492a43b60f3463b7bb7 -r 97f7357fd0ce361c39414aa20b350a96487ac20d yt/analysis_modules/halo_profiler/multi_halo_profiler.py
--- a/yt/analysis_modules/halo_profiler/multi_halo_profiler.py
+++ b/yt/analysis_modules/halo_profiler/multi_halo_profiler.py
@@ -66,7 +66,7 @@
                  recenter = None,
                  profile_output_dir='radial_profiles', projection_output_dir='projections',
                  projection_width=8.0, projection_width_units='mpc', project_at_level='max',
-                 velocity_center=['bulk', 'halo'], filter_quantities=['id','center'], 
+                 velocity_center=['bulk', 'halo'], filter_quantities=['id', 'center', 'r_max'], 
                  use_critical_density=False):
         r"""Initialize a Halo Profiler object.
         
@@ -530,6 +530,7 @@
 
             # get a sphere object to profile
             sphere = get_halo_sphere(halo, self.pf, recenter=self.recenter)
+            if sphere is None: return None
 
             if self._need_bulk_velocity:
                 # Set bulk velocity to zero out radial velocity profiles.
@@ -742,6 +743,76 @@
 
             del region
 
+    @parallel_blocking_call
+    def analyze_halo_spheres(self, analysis_function, halo_list='filtered',
+                             analysis_output_dir=None):
+        r"""Perform custom analysis on all halos.
+        
+        This will loop through all halo on the HaloProfiler's list, 
+        creating a sphere object for each halo and passing that sphere 
+        to the provided analysis function.
+        
+        Parameters
+        ---------
+        analysis_function : function
+            A function taking two arguments, the halo dictionary, and a 
+            sphere object.
+            Example function to calculate total mass of halo:
+                def my_analysis(halo, sphere):
+                    total_mass = sphere.quantities['TotalMass']()
+                    print total_mass
+        halo_list : {'filtered', 'all'}
+            Which set of halos to make profiles of, either ones passed by the
+            halo filters (if enabled/added), or all halos.
+            Default='filtered'.
+        analysis_output_dir : string, optional
+            If specified, this directory will be created within the dataset to 
+            contain any output from the analysis function.  Default: None.
+
+        Examples
+        --------
+        >>> hp.analyze_halo_spheres(my_analysis, halo_list="filtered",
+                                    analysis_output_dir='special_analysis')
+        
+        """
+
+        # Get list of halos for projecting.
+        if halo_list == 'filtered':
+            self._halo_analysis_list = self.filtered_halos
+        elif halo_list == 'all':
+            self._halo_analysis_list = self.all_halos
+        elif isinstance(halo_list, types.StringType):
+            self._halo_analysis_list = self._read_halo_list(halo_list)
+        elif isinstance(halo_list, types.ListType):
+            self._halo_analysis_list = halo_list
+        else:
+            mylog.error("Keyword, halo_list', must be 'filtered', 'all', a filename, or an actual list.")
+            return
+
+        if len(self._halo_analysis_list) == 0:
+            mylog.error("Halo list for analysis is empty.")
+            return
+
+        # Create output directory.
+        if analysis_output_dir is not None:
+            if self.output_dir is not None:
+                self.__check_directory("%s/%s" % (self.output_dir, self.pf.directory))
+                my_output_dir = "%s/%s/%s" % (self.output_dir, self.pf.directory, 
+                                              analysis_output_dir)
+            else:
+                my_output_dir = "%s/%s" % (self.pf.fullpath, analysis_output_dir)
+            self.__check_directory(my_output_dir)
+
+        for halo in self._get_objs('_halo_analysis_list', round_robin=True):
+            if halo is None: continue
+
+            # Get a sphere object to analze.
+            sphere = get_halo_sphere(halo, self.pf, recenter=self.recenter)
+            if sphere is None: continue
+
+            # Call the given analysis function.
+            analysis_function(halo, sphere)
+
     def _add_actual_overdensity(self, profile):
         "Calculate overdensity from TotalMassMsun and CellVolume fields."
 
@@ -1057,6 +1128,7 @@
         sphere.clear_data()
         del sphere
         sphere = pf.h.sphere(halo['center'], halo['r_max']/pf.units['mpc'])
+    return sphere
 
 def _shift_projections(pf, projections, oldCenter, newCenter, axis):
     """



https://bitbucket.org/yt_analysis/yt/changeset/bd9ef841c0aa/
changeset:   bd9ef841c0aa
branch:      yt
user:        brittonsmith
date:        2011-12-15 22:30:30
summary:     Merged.
affected #:  43 files

diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 setup.py
--- a/setup.py
+++ b/setup.py
@@ -76,7 +76,7 @@
 
 import setuptools
 
-VERSION = "2.3dev"
+VERSION = "2.4dev"
 
 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
 


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/analysis_modules/halo_merger_tree/merger_tree.py
--- a/yt/analysis_modules/halo_merger_tree/merger_tree.py
+++ b/yt/analysis_modules/halo_merger_tree/merger_tree.py
@@ -86,6 +86,9 @@
 "ChildHaloID3", "ChildHaloFrac3",
 "ChildHaloID4", "ChildHaloFrac4"]
 
+NumNeighbors = 15
+NumDB = 5
+
 class DatabaseFunctions(object):
     # Common database functions so it doesn't have to be repeated.
     def _open_database(self):
@@ -366,9 +369,9 @@
         child_points = na.array(child_points)
         fKD.pos = na.asfortranarray(child_points.T)
         fKD.qv = na.empty(3, dtype='float64')
-        fKD.dist = na.empty(5, dtype='float64')
-        fKD.tags = na.empty(5, dtype='int64')
-        fKD.nn = 5
+        fKD.dist = na.empty(NumNeighbors, dtype='float64')
+        fKD.tags = na.empty(NumNeighbors, dtype='int64')
+        fKD.nn = NumNeighbors
         fKD.sort = True
         fKD.rearrange = True
         create_tree(0)
@@ -395,7 +398,7 @@
                 nIDs.append(n)
             # We need to fill in fake halos if there aren't enough halos,
             # which can happen at high redshifts.
-            while len(nIDs) < 5:
+            while len(nIDs) < NumNeighbors:
                 nIDs.append(-1)
             candidates[row[0]] = nIDs
         
@@ -405,12 +408,12 @@
         self.candidates = candidates
         
         # This stores the masses contributed to each child candidate.
-        self.child_mass_arr = na.zeros(len(candidates)*5, dtype='float64')
+        self.child_mass_arr = na.zeros(len(candidates)*NumNeighbors, dtype='float64')
         # Records where to put the entries in the above array.
         self.child_mass_loc = defaultdict(dict)
         for i,halo in enumerate(sorted(candidates)):
             for j, child in enumerate(candidates[halo]):
-                self.child_mass_loc[halo][child] = i*5 + j
+                self.child_mass_loc[halo][child] = i*NumNeighbors + j
 
     def _build_h5_refs(self, filename):
         # For this snapshot, add lists of file names that contain the
@@ -618,8 +621,8 @@
         result = self.cursor.fetchone()
         while result:
             mass = result[0]
-            self.child_mass_arr[mark:mark+5] /= mass
-            mark += 5
+            self.child_mass_arr[mark:mark+NumNeighbors] /= mass
+            mark += NumNeighbors
             result = self.cursor.fetchone()
         
         # Get the global ID for the SnapHaloID=0 from the child, this will
@@ -642,14 +645,15 @@
                 # We need to get the GlobalHaloID for this child.
                 child_globalID = baseChildID + child
                 child_indexes.append(child_globalID)
-                child_per.append(self.child_mass_arr[i*5 + j])
+                child_per.append(self.child_mass_arr[i*NumNeighbors + j])
             # Sort by percentages, desending.
             child_per, child_indexes = zip(*sorted(zip(child_per, child_indexes), reverse=True))
             values = []
-            for pair in zip(child_indexes, child_per):
+            for pair_count, pair in enumerate(zip(child_indexes, child_per)):
+                if pair_count == NumDB: break
                 values.extend([int(pair[0]), float(pair[1])])
             #values.extend([parent_currt, parent_halo])
-            # This has the child ID, child percent listed five times, followed
+            # This has the child ID, child percent listed NumDB times, followed
             # by the currt and this parent halo ID (SnapHaloID).
             #values = tuple(values)
             self.write_values.append(values)
@@ -841,7 +845,7 @@
          [1609, 0.0]]
         """
         parents = []
-        for i in range(5):
+        for i in range(NumDB):
             string = "SELECT GlobalHaloID, ChildHaloFrac%d FROM Halos\
             WHERE ChildHaloID%d=%d;" % (i, i, GlobalHaloID)
             self.cursor.execute(string)


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/analysis_modules/star_analysis/sfr_spectrum.py
--- a/yt/analysis_modules/star_analysis/sfr_spectrum.py
+++ b/yt/analysis_modules/star_analysis/sfr_spectrum.py
@@ -96,6 +96,8 @@
             self._pf.current_redshift) # seconds
         # Build the distribution.
         self.build_dist()
+        # Attach some convenience arrays.
+        self.attach_arrays()
 
     def build_dist(self):
         """
@@ -127,6 +129,47 @@
         # We will want the time taken between bins.
         self.time_bins_dt = self.time_bins[1:] - self.time_bins[:-1]
     
+    def attach_arrays(self):
+        """
+        Attach convenience arrays to the class for easy access.
+        """
+        if self.mode == 'data_source':
+            try:
+                vol = self._data_source.volume('mpc')
+            except AttributeError:
+                # If we're here, this is probably a HOPHalo object, and we
+                # can get the volume this way.
+                ds = self._data_source.get_sphere()
+                vol = ds.volume('mpc')
+        elif self.mode == 'provided':
+            vol = self.volume
+        tc = self._pf["Time"]
+        self.time = []
+        self.lookback_time = []
+        self.redshift = []
+        self.Msol_yr = []
+        self.Msol_yr_vol = []
+        self.Msol = []
+        self.Msol_cumulative = []
+        # Use the center of the time_bin, not the left edge.
+        for i, time in enumerate((self.time_bins[1:] + self.time_bins[:-1])/2.):
+            self.time.append(time * tc / YEAR)
+            self.lookback_time.append((self.time_now - time * tc)/YEAR)
+            self.redshift.append(self.cosm.ComputeRedshiftFromTime(time * tc))
+            self.Msol_yr.append(self.mass_bins[i] / \
+                (self.time_bins_dt[i] * tc / YEAR))
+            self.Msol_yr_vol.append(self.mass_bins[i] / \
+                (self.time_bins_dt[i] * tc / YEAR) / vol)
+            self.Msol.append(self.mass_bins[i])
+            self.Msol_cumulative.append(self.cum_mass_bins[i])
+        self.time = na.array(self.time)
+        self.lookback_time = na.array(self.lookback_time)
+        self.redshift = na.array(self.redshift)
+        self.Msol_yr = na.array(self.Msol_yr)
+        self.Msol_yr_vol = na.array(self.Msol_yr_vol)
+        self.Msol = na.array(self.Msol)
+        self.Msol_cumulative = na.array(self.Msol_cumulative)
+    
     def write_out(self, name="StarFormationRate.out"):
         r"""Write out the star analysis to a text file *name*. The columns are in
         order.
@@ -150,31 +193,21 @@
         >>> sfr.write_out("stars-SFR.out")
         """
         fp = open(name, "w")
-        if self.mode == 'data_source':
-            try:
-                vol = self._data_source.volume('mpc')
-            except AttributeError:
-                # If we're here, this is probably a HOPHalo object, and we
-                # can get the volume this way.
-                ds = self._data_source.get_sphere()
-                vol = ds.volume('mpc')
-        elif self.mode == 'provided':
-            vol = self.volume
-        tc = self._pf["Time"]
-        # Use the center of the time_bin, not the left edge.
         fp.write("#time\tlookback\tredshift\tMsol/yr\tMsol/yr/Mpc3\tMsol\tcumMsol\t\n")
-        for i, time in enumerate((self.time_bins[1:] + self.time_bins[:-1])/2.):
+        for i, time in enumerate(self.time):
             line = "%1.5e %1.5e %1.5e %1.5e %1.5e %1.5e %1.5e\n" % \
-            (time * tc / YEAR, # Time
-            (self.time_now - time * tc)/YEAR, # Lookback time
-            self.cosm.ComputeRedshiftFromTime(time * tc), # Redshift
-            self.mass_bins[i] / (self.time_bins_dt[i] * tc / YEAR), # Msol/yr
-            self.mass_bins[i] / (self.time_bins_dt[i] * tc / YEAR) / vol, # Msol/yr/vol
-            self.mass_bins[i], # Msol in bin
-            self.cum_mass_bins[i]) # cumulative
+            (time, # Time
+            self.lookback_time[i], # Lookback time
+            self.redshift[i], # Redshift
+            self.Msol_yr[i], # Msol/yr
+            self.Msol_yr_vol[i], # Msol/yr/vol
+            self.Msol[i], # Msol in bin
+            self.Msol_cumulative[i]) # cumulative
             fp.write(line)
         fp.close()
 
+### Begin Synthetic Spectrum Stuff. ####
+
 CHABRIER = {
 "Z0001" : "bc2003_hr_m22_chab_ssp.ised.h5", #/* 0.5% */
 "Z0004" : "bc2003_hr_m32_chab_ssp.ised.h5", #/* 2% */


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/data_objects/data_containers.py
--- a/yt/data_objects/data_containers.py
+++ b/yt/data_objects/data_containers.py
@@ -88,6 +88,20 @@
         return tr
     return save_state
 
+def restore_field_information_state(func):
+    """
+    A decorator that takes a function with the API of (self, grid, field)
+    and ensures that after the function is called, the field_parameters will
+    be returned to normal.
+    """
+    def save_state(self, grid, field=None, *args, **kwargs):
+        old_params = grid.field_parameters
+        grid.field_parameters = self.field_parameters
+        tr = func(self, grid, field, *args, **kwargs)
+        grid.field_parameters = old_params
+        return tr
+    return save_state
+
 def cache_mask(func):
     """
     For computationally intensive indexing operations, we can cache
@@ -833,6 +847,38 @@
             self[field] = temp_data[field]
 
     def to_frb(self, width, resolution, center = None):
+        r"""This function returns a FixedResolutionBuffer generated from this
+        object.
+
+        A FixedResolutionBuffer is an object that accepts a variable-resolution
+        2D object and transforms it into an NxM bitmap that can be plotted,
+        examined or processed.  This is a convenience function to return an FRB
+        directly from an existing 2D data object.
+
+        Parameters
+        ----------
+        width : width specifier
+            This can either be a floating point value, in the native domain
+            units of the simulation, or a tuple of the (value, unit) style.
+            This will be the width of the FRB.
+        resolution : int or tuple of ints
+            The number of pixels on a side of the final FRB.
+        center : array-like of floats, optional
+            The center of the FRB.  If not specified, defaults to the center of
+            the current object.
+
+        Returns
+        -------
+        frb : :class:`~yt.visualization.fixed_resolution.FixedResolutionBuffer`
+            A fixed resolution buffer, which can be queried for fields.
+
+        Examples
+        --------
+
+        >>> proj = pf.h.proj(0, "Density")
+        >>> frb = proj.to_frb( (100.0, 'kpc'), 1024)
+        >>> write_image(na.log10(frb["Density"]), 'density_100kpc.png')
+        """
         if center is None:
             center = self.get_field_parameter("center")
             if center is None:
@@ -1258,6 +1304,52 @@
         return "%s/c%s_L%s" % \
             (self._top_node, cen_name, L_name)
 
+    def to_frb(self, width, resolution):
+        r"""This function returns an ObliqueFixedResolutionBuffer generated
+        from this object.
+
+        An ObliqueFixedResolutionBuffer is an object that accepts a
+        variable-resolution 2D object and transforms it into an NxM bitmap that
+        can be plotted, examined or processed.  This is a convenience function
+        to return an FRB directly from an existing 2D data object.  Unlike the
+        corresponding to_frb function for other AMR2DData objects, this does
+        not accept a 'center' parameter as it is assumed to be centered at the
+        center of the cutting plane.
+
+        Parameters
+        ----------
+        width : width specifier
+            This can either be a floating point value, in the native domain
+            units of the simulation, or a tuple of the (value, unit) style.
+            This will be the width of the FRB.
+        resolution : int or tuple of ints
+            The number of pixels on a side of the final FRB.
+
+        Returns
+        -------
+        frb : :class:`~yt.visualization.fixed_resolution.ObliqueFixedResolutionBuffer`
+            A fixed resolution buffer, which can be queried for fields.
+
+        Examples
+        --------
+
+        >>> v, c = pf.h.find_max("Density")
+        >>> sp = pf.h.sphere(c, (100.0, 'au'))
+        >>> L = sp.quantities["AngularMomentumVector"]()
+        >>> cutting = pf.h.cutting(L, c)
+        >>> frb = cutting.to_frb( (1.0, 'pc'), 1024)
+        >>> write_image(na.log10(frb["Density"]), 'density_1pc.png')
+        """
+        if iterable(width):
+            w, u = width
+            width = w/self.pf[u]
+        if not iterable(resolution):
+            resolution = (resolution, resolution)
+        from yt.visualization.fixed_resolution import ObliqueFixedResolutionBuffer
+        bounds = (-width/2.0, width/2.0, -width/2.0, width/2.0)
+        frb = ObliqueFixedResolutionBuffer(self, bounds, resolution)
+        return frb
+
 class AMRFixedResCuttingPlaneBase(AMR2DData):
     """
     AMRFixedResCuttingPlaneBase is an oblique plane through the data,
@@ -3467,7 +3559,7 @@
                                    output_field, output_left)
             self.field_data[field] = output_field
 
-    @restore_grid_state
+    @restore_field_information_state
     def _get_data_from_grid(self, grid, fields):
         fields = ensure_list(fields)
         g_fields = [grid[field].astype("float64") for field in fields]


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/frontends/flash/data_structures.py
--- a/yt/frontends/flash/data_structures.py
+++ b/yt/frontends/flash/data_structures.py
@@ -316,6 +316,13 @@
             self.current_time = \
                 float(self._find_parameter("real", "time", scalar=True))
 
+        if self._flash_version == 7:
+            self.parameters['timestep'] = float(
+                self._handle["simulation parameters"]["timestep"])
+        else:
+            self.parameters['timestep'] = \
+                float(self._find_parameter("real", "dt", scalar=True))
+
         try:
             use_cosmo = self._find_parameter("logical", "usecosmology") 
         except:


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/3d.png
Binary file yt/gui/reason/html/images/3d.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/3d_tab.png
Binary file yt/gui/reason/html/images/3d_tab.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/binary.png
Binary file yt/gui/reason/html/images/binary.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/blockdevice.png
Binary file yt/gui/reason/html/images/blockdevice.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/blockdevice_tab.png
Binary file yt/gui/reason/html/images/blockdevice_tab.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/console.png
Binary file yt/gui/reason/html/images/console.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/double_down.png
Binary file yt/gui/reason/html/images/double_down.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/double_down_sm.png
Binary file yt/gui/reason/html/images/double_down_sm.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/double_left.png
Binary file yt/gui/reason/html/images/double_left.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/double_left_sm.png
Binary file yt/gui/reason/html/images/double_left_sm.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/double_right.png
Binary file yt/gui/reason/html/images/double_right.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/double_right_sm.png
Binary file yt/gui/reason/html/images/double_right_sm.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/double_up.png
Binary file yt/gui/reason/html/images/double_up.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/double_up_sm.png
Binary file yt/gui/reason/html/images/double_up_sm.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/graph.png
Binary file yt/gui/reason/html/images/graph.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/kivio_flw.png
Binary file yt/gui/reason/html/images/kivio_flw.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/single_down.png
Binary file yt/gui/reason/html/images/single_down.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/single_down_sm.png
Binary file yt/gui/reason/html/images/single_down_sm.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/single_left.png
Binary file yt/gui/reason/html/images/single_left.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/single_left_sm.png
Binary file yt/gui/reason/html/images/single_left_sm.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/single_right.png
Binary file yt/gui/reason/html/images/single_right.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/single_right_sm.png
Binary file yt/gui/reason/html/images/single_right_sm.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/single_up.png
Binary file yt/gui/reason/html/images/single_up.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/single_up_sm.png
Binary file yt/gui/reason/html/images/single_up_sm.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/images/upload.png
Binary file yt/gui/reason/html/images/upload.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/leaflet/images/marker-shadow.png
Binary file yt/gui/reason/html/leaflet/images/marker-shadow.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/leaflet/images/marker.png
Binary file yt/gui/reason/html/leaflet/images/marker.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/leaflet/images/popup-close.png
Binary file yt/gui/reason/html/leaflet/images/popup-close.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/leaflet/images/zoom-in.png
Binary file yt/gui/reason/html/leaflet/images/zoom-in.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/gui/reason/html/leaflet/images/zoom-out.png
Binary file yt/gui/reason/html/leaflet/images/zoom-out.png has changed


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/mods.py
--- a/yt/mods.py
+++ b/yt/mods.py
@@ -108,7 +108,7 @@
     PlotCollection, PlotCollectionInteractive, \
     get_multi_plot, FixedResolutionBuffer, ObliqueFixedResolutionBuffer, \
     callback_registry, write_bitmap, write_image, annotate_image, \
-    apply_colormap, scale_image
+    apply_colormap, scale_image, write_projection
 
 from yt.visualization.volume_rendering.api import \
     ColorTransferFunction, PlanckTransferFunction, ProjectionTransferFunction, \
@@ -122,6 +122,10 @@
 
 from yt.convenience import all_pfs, max_spheres, load, projload
 
+# Import some helpful math utilities
+from yt.utilities.math_utils import \
+    ortho_find, quartiles
+
 
 # We load plugins.  Keep in mind, this can be fairly dangerous -
 # the primary purpose is to allow people to have a set of functions


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/utilities/_amr_utils/VolumeIntegrator.pyx
--- a/yt/utilities/_amr_utils/VolumeIntegrator.pyx
+++ b/yt/utilities/_amr_utils/VolumeIntegrator.pyx
@@ -65,6 +65,9 @@
     double log2(double x)
     long int lrint(double x)
     double fabs(double x)
+    double cos(double x)
+    double sin(double x)
+    double asin(double x)
 
 cdef struct Triangle:
     Triangle *next
@@ -238,6 +241,33 @@
         tr[i] = ipnest
     return tr
 
+def arr_fisheye_vectors(int resolution, np.float64_t fov):
+    # We now follow figures 4-7 of:
+    # http://paulbourke.net/miscellaneous/domefisheye/fisheye/
+    # ...but all in Cython.
+    cdef np.ndarray[np.float64_t, ndim=3] vp
+    cdef int i, j, k
+    cdef np.float64_t r, phi, theta, px, py
+    cdef np.float64_t pi = 3.1415926
+    cdef np.float64_t fov_rad = fov * pi / 180.0
+    vp = np.zeros((resolution, resolution, 3), dtype="float64")
+    for i in range(resolution):
+        px = 2.0 * i / (resolution) - 1.0
+        for j in range(resolution):
+            py = 2.0 * j / (resolution) - 1.0
+            r = (px*px + py*py)**0.5
+            if r == 0.0:
+                phi = 0.0
+            elif px < 0:
+                phi = pi - asin(py / r)
+            else:
+                phi = asin(py / r)
+            theta = r * fov_rad / 2.0
+            vp[i,j,0] = sin(theta) * cos(phi)
+            vp[i,j,1] = sin(theta) * sin(phi)
+            vp[i,j,2] = cos(theta)
+    return vp
+
 cdef class star_kdtree_container:
     cdef kdtree_utils.kdtree *tree
     cdef public np.float64_t sigma


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/utilities/math_utils.py
--- a/yt/utilities/math_utils.py
+++ b/yt/utilities/math_utils.py
@@ -515,3 +515,107 @@
     vec2 /= norm2
     vec3 = na.cross(vec1, vec2)
     return vec1, vec2, vec3
+
+def quartiles(a, axis=None, out=None, overwrite_input=False):
+    """
+    Compute the quartile values (25% and 75%) along the specified axis
+    in the same way that the numpy.median calculates the median (50%) value
+    alone a specified axis.  Check numpy.median for details, as it is
+    virtually the same algorithm.
+
+    Returns an array of the quartiles of the array elements [lower quartile, 
+    upper quartile].
+
+    Parameters
+    ----------
+    a : array_like
+        Input array or object that can be converted to an array.
+    axis : {None, int}, optional
+        Axis along which the quartiles are computed. The default (axis=None)
+        is to compute the quartiles along a flattened version of the array.
+    out : ndarray, optional
+        Alternative output array in which to place the result. It must
+        have the same shape and buffer length as the expected output,
+        but the type (of the output) will be cast if necessary.
+    overwrite_input : {False, True}, optional
+       If True, then allow use of memory of input array (a) for
+       calculations. The input array will be modified by the call to
+       quartiles. This will save memory when you do not need to preserve
+       the contents of the input array. Treat the input as undefined,
+       but it will probably be fully or partially sorted. Default is
+       False. Note that, if `overwrite_input` is True and the input
+       is not already an ndarray, an error will be raised.
+
+    Returns
+    -------
+    quartiles : ndarray
+        A new 2D array holding the result (unless `out` is specified, in
+        which case that array is returned instead).  If the input contains
+        integers, or floats of smaller precision than 64, then the output
+        data-type is float64.  Otherwise, the output data-type is the same
+        as that of the input.
+
+    See Also
+    --------
+    numpy.median, numpy.mean, numpy.percentile
+
+    Notes
+    -----
+    Given a vector V of length N, the quartiles of V are the 25% and 75% values 
+    of a sorted copy of V, ``V_sorted`` - i.e., ``V_sorted[(N-1)/4]`` and 
+    ``3*V_sorted[(N-1)/4]``, when N is odd.  When N is even, it is the average 
+    of the two values bounding these values of ``V_sorted``.
+
+    Examples
+    --------
+    >>> a = na.arange(100).reshape(10,10)
+    >>> a
+    array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
+           [10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
+           [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
+           [30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
+           [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
+           [50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
+           [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
+           [70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
+           [80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
+           [90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])
+    >>> mu.quartiles(a)
+    array([ 24.5,  74.5])
+    >>> mu.quartiles(a,axis=0)
+    array([[ 15.,  16.,  17.,  18.,  19.,  20.,  21.,  22.,  23.,  24.],
+           [ 65.,  66.,  67.,  68.,  69.,  70.,  71.,  72.,  73.,  74.]])
+    >>> mu.quartiles(a,axis=1)
+    array([[  1.5,  11.5,  21.5,  31.5,  41.5,  51.5,  61.5,  71.5,  81.5,
+             91.5],
+           [  6.5,  16.5,  26.5,  36.5,  46.5,  56.5,  66.5,  76.5,  86.5,
+             96.5]])
+    """
+    if overwrite_input:
+        if axis is None:
+            sorted = a.ravel()
+            sorted.sort()
+        else:
+            a.sort(axis=axis)
+            sorted = a
+    else:
+        sorted = na.sort(a, axis=axis)
+    if axis is None:
+        axis = 0
+    indexer = [slice(None)] * sorted.ndim
+    indices = [int(sorted.shape[axis]/4), int(sorted.shape[axis]*.75)]
+    result = []
+    for index in indices:
+        if sorted.shape[axis] % 2 == 1:
+            # index with slice to allow mean (below) to work
+            indexer[axis] = slice(index, index+1)
+        else:
+            indexer[axis] = slice(index-1, index+1)
+        # special cases for small arrays
+        if sorted.shape[axis] == 2:
+            # index with slice to allow mean (below) to work
+            indexer[axis] = slice(index, index+1)
+        # Use mean in odd and even case to coerce data type
+        # and check, use out array.
+        result.append(na.mean(sorted[indexer], axis=axis, out=out))
+    return na.array(result)


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/visualization/api.py
--- a/yt/visualization/api.py
+++ b/yt/visualization/api.py
@@ -49,7 +49,8 @@
     splat_points, \
     annotate_image, \
     apply_colormap, \
-    scale_image
+    scale_image, \
+    write_projection
 
 from plot_modifications import \
     PlotCallback, \


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/visualization/image_writer.py
--- a/yt/visualization/image_writer.py
+++ b/yt/visualization/image_writer.py
@@ -327,3 +327,90 @@
     im = image.copy()
     au.add_points_to_image(im, points_x, points_y, val)
     return im
+
+def write_projection(data, filename, colorbar=True, colorbar_label=None, 
+                    title=None, limits=None, take_log=True, var_fig_size=False):
+    r"""Write a projection or volume rendering to disk with a variety of 
+    pretty parameters such as limits, title, colorbar, etc.  write_projection
+    uses the standard matplotlib interface to create the figure.  N.B. This code
+    only works *after* you have created the projection using the standard 
+    framework (i.e. the Camera interface or off_axis_projection).
+
+    Accepts an NxM sized array representing the projection itself as well
+    as the filename to which you will save this figure.  
+
+    Parameters
+    ----------
+    data : array_like 
+        image array as output by off_axis_projection or camera.snapshot()
+    filename : string 
+        the filename where the data will be saved
+    colorbar : boolean
+        do you want a colorbar generated to the right of the image?
+    colorbar_label : string
+        the label associated with your colorbar
+    title : string
+        the label at the top of the figure
+    limits : 2-element array_like
+        the lower limit and the upper limit to be plotted in the figure 
+        of the data array
+    take_log : boolean
+        plot the log of the data array (and take the log of the limits if set)?
+    var_fig_size : boolean
+        If we want the resolution (and size) of the output image to scale 
+        with the resolution of the image array.  
+
+    Examples
+    --------
+
+    >>> image = off_axis_projection(pf, c, L, W, N, "Density", no_ghost=False)
+    >>> write_projection(image, 'test.png', 
+                         colorbar_label="Column Density (cm$^{-2}$)", 
+                         title="Offaxis Projection", limits=(1e-3,1e-5), 
+                         take_log=True)
+    """
+    import pylab as pl
+
+    # If there are limits, then clip the data before plotting
+    if limits is not None:
+        data = na.clip(data, limits[0], limits[1])
+
+    # If this is rendered as log, then apply now.
+    if take_log:
+        data = na.log10(data)
+        if limits is not None:
+            limits = na.log10(limits)
+
+
+    # Create the figure and paint the data on
+    fig = pl.figure()
+    ax = fig.add_subplot(111)
+    cax = ax.imshow(data)
+
+    # If there are limits, apply them to the colormap
+    if limits is not None:
+        cax.set_clim=limits
+    if title:
+        ax.set_title(title)
+
+    # Suppress the x and y pixel counts
+    ax.set_xticks(())
+    ax.set_yticks(())
+
+    # Add a color bar and label if requested
+    if colorbar:
+        cbar = fig.colorbar(cax)
+        if colorbar_label:
+            cbar.ax.set_ylabel(colorbar_label)
+
+    # If we want the resolution of the image to scale with the resolution
+    # of the image array. we increase the dpi value accordingly
+    if var_fig_size:
+        N = data.shape[0]
+        mag_factor = N/480.
+        pl.savefig(filename, dpi=100*mag_factor)
+    else:
+        pl.savefig(filename)
+
+    pl.clf()
+    pl.close()


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/visualization/plot_collection.py
--- a/yt/visualization/plot_collection.py
+++ b/yt/visualization/plot_collection.py
@@ -211,9 +211,9 @@
         Only ONE of the following options can be specified. If all 3 are
         specified, they will be used in the following precedence order:
 
-        * `ticks` - a list of floating point numbers at which to put ticks
-        * `minmaxtick` - display DEFAULT ticks with min & max also displayed
-        * `nticks` - if ticks not specified, can automatically determine a
+        * ``ticks`` - a list of floating point numbers at which to put ticks
+        * ``minmaxtick`` - display DEFAULT ticks with min & max also displayed
+        * ``nticks`` - if ticks not specified, can automatically determine a
           number of ticks to be evenly spaced in log space
         """
         for plot in self.plots:
@@ -1713,9 +1713,9 @@
     r"""Construct a multiple axes plot object, with or without a colorbar, into
     which multiple plots may be inserted.
 
-    This will create a set of `matplotlib.axes.Axes`, all lined up into a grid,
-    which are then returned to the user and which can be used to plot multiple
-    plots on a single figure.
+    This will create a set of :class:`matplotlib.axes.Axes`, all lined up into
+    a grid, which are then returned to the user and which can be used to plot
+    multiple plots on a single figure.
 
     Parameters
     ----------
@@ -1733,12 +1733,12 @@
 
     Returns
     -------
-    fig : `matplotlib.figure.Figure
+    fig : :class:`matplotlib.figure.Figure`
         The figure created inside which the axes reside
-    tr : list of list of `matplotlib.axes.Axes` objects
+    tr : list of list of :class:`matplotlib.axes.Axes` objects
         This is a list, where the inner list is along the x-axis and the outer
         is along the y-axis
-    cbars : list of `matplotlib.axes.Axes` objects
+    cbars : list of :class:`matplotlib.axes.Axes` objects
         Each of these is an axes onto which a colorbar can be placed.
 
     Notes


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/visualization/plot_types.py
--- a/yt/visualization/plot_types.py
+++ b/yt/visualization/plot_types.py
@@ -170,10 +170,11 @@
 
         Only ONE of the following options can be specified. If all 3 are
         specified, they will be used in the following precedence order:
-            ticks - a list of floating point numbers at which to put ticks
-            minmaxtick - display DEFAULT ticks with min & max also displayed
-            nticks - if ticks not specified, can automatically determine a
-               number of ticks to be evenly spaced in log space
+
+        * ``ticks`` - a list of floating point numbers at which to put ticks
+        * ``minmaxtick`` - display DEFAULT ticks with min & max also displayed
+        * ``nticks`` - if ticks not specified, can automatically determine a
+          number of ticks to be evenly spaced in log space
         """
         # This next call fixes some things, but is slower...
         self._redraw_image()


diff -r 97f7357fd0ce361c39414aa20b350a96487ac20d -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 yt/visualization/volume_rendering/camera.py
--- a/yt/visualization/volume_rendering/camera.py
+++ b/yt/visualization/volume_rendering/camera.py
@@ -32,7 +32,7 @@
 
 from yt.utilities.amr_utils import TransferFunctionProxy, VectorPlane, \
     arr_vec2pix_nest, arr_pix2vec_nest, AdaptiveRaySource, \
-    arr_ang2pix_nest
+    arr_ang2pix_nest, arr_fisheye_vectors
 from yt.visualization.image_writer import write_bitmap
 from yt.data_objects.data_containers import data_object_registry
 from yt.utilities.parallel_tools.parallel_analysis_interface import \
@@ -703,7 +703,7 @@
             ax = fig.add_subplot(1,1,1,projection='mollweide')
             implot = ax.imshow(img, extent=(-pi,pi,-pi/2,pi/2), clip_on=False, aspect=0.5)
             cb = fig.colorbar(implot, orientation='horizontal')
-            cb.set_label(r"$\mathrm{Column}\/\mathrm{Density}\/[\mathrm{g}/\mathrm{cm}^2]$")
+            cb.set_label(r"$\mathrm{log}\/\mathrm{Column}\/\mathrm{Density}\/[\mathrm{g}/\mathrm{cm}^2]$")
             ax.xaxis.set_ticks(())
             ax.yaxis.set_ticks(())
             canvas = matplotlib.backends.backend_agg.FigureCanvasAgg(fig)
@@ -793,6 +793,59 @@
                              oc.sub_samples, oc.pf)
         return (left_camera, right_camera)
 
+class FisheyeCamera(Camera):
+    def __init__(self, center, radius, fov, resolution,
+                 transfer_function = None, fields = None,
+                 sub_samples = 5, log_fields = None, volume = None,
+                 pf = None, no_ghost=False):
+        ParallelAnalysisInterface.__init__(self)
+        if pf is not None: self.pf = pf
+        self.center = na.array(center, dtype='float64')
+        self.radius = radius
+        self.fov = fov
+        if iterable(resolution):
+            raise RuntimeError("Resolution must be a single int")
+        self.resolution = resolution
+        if transfer_function is None:
+            transfer_function = ProjectionTransferFunction()
+        self.transfer_function = transfer_function
+        if fields is None: fields = ["Density"]
+        self.fields = fields
+        self.sub_samples = sub_samples
+        self.log_fields = log_fields
+        if volume is None:
+            volume = AMRKDTree(self.pf, fields=self.fields, no_ghost=no_ghost,
+                               log_fields=log_fields)
+        self.volume = volume
+
+    def snapshot(self):
+        image = na.zeros((self.resolution**2,1,3), dtype='float64', order='C')
+        # We now follow figures 4-7 of:
+        # http://paulbourke.net/miscellaneous/domefisheye/fisheye/
+        # ...but all in Cython.
+        vp = arr_fisheye_vectors(self.resolution, self.fov)
+        vp.shape = (self.resolution**2,1,3)
+        uv = na.ones(3, dtype='float64')
+        positions = na.ones((self.resolution**2, 1, 3), dtype='float64') * self.center
+        vector_plane = VectorPlane(positions, vp, self.center,
+                        (0.0, 1.0, 0.0, 1.0), image, uv, uv)
+        tfp = TransferFunctionProxy(self.transfer_function)
+        tfp.ns = self.sub_samples
+        self.volume.initialize_source()
+        mylog.info("Rendering fisheye of %s^2", self.resolution)
+        pbar = get_pbar("Ray casting",
+                        (self.volume.brick_dimensions + 1).prod(axis=-1).sum())
+
+        total_cells = 0
+        for brick in self.volume.traverse(None, self.center, image):
+            brick.cast_plane(tfp, vector_plane)
+            total_cells += na.prod(brick.my_data[0].shape)
+            pbar.update(total_cells)
+        pbar.finish()
+        image.shape = (self.resolution, self.resolution, 3)
+        return image
+
+
 def off_axis_projection(pf, center, normal_vector, width, resolution,
                         field, weight = None, volume = None, no_ghost = True):
     r"""Project through a parameter file, off-axis, and return the image plane.



https://bitbucket.org/yt_analysis/yt/changeset/3d6032495f52/
changeset:   3d6032495f52
branch:      yt
user:        brittonsmith
date:        2011-12-16 21:59:46
summary:     Switched the splitting of halos in the HaloProfiler from _get_objs
to parallel_objects.  This fixes some issues with processors getting
stuck at barriers when analysing different halos.  Thanks to Matt
for the tip.
affected #:  1 file

diff -r bd9ef841c0aacf5feb6479ce3d19153c8d821876 -r 3d6032495f52c73e7962fa112b60ce1fe6ad6185 yt/analysis_modules/halo_profiler/multi_halo_profiler.py
--- a/yt/analysis_modules/halo_profiler/multi_halo_profiler.py
+++ b/yt/analysis_modules/halo_profiler/multi_halo_profiler.py
@@ -46,7 +46,8 @@
 from yt.utilities.parallel_tools.parallel_analysis_interface import \
     ParallelAnalysisInterface, \
     parallel_blocking_call, \
-    parallel_root_only
+    parallel_root_only, \
+    parallel_objects
 from yt.visualization.fixed_resolution import \
     FixedResolutionBuffer
 from yt.visualization.image_writer import write_image
@@ -184,7 +185,6 @@
         self._halo_filters = []
         self.all_halos = []
         self.filtered_halos = []
-        self._projection_halo_list = []
 
         # Create output directory if specified
         if self.output_dir is not None:
@@ -454,7 +454,7 @@
 
         # Profile all halos.
         updated_halos = []
-        for halo in self._get_objs('all_halos', round_robin=True):
+        for halo in parallel_objects(self.all_halos, -1):
             # Apply prefilters to avoid profiling unwanted halos.
             filter_result = True
             haloQuantities = {}
@@ -618,18 +618,18 @@
 
         # Get list of halos for projecting.
         if halo_list == 'filtered':
-            self._halo_projection_list = self.filtered_halos
+            halo_projection_list = self.filtered_halos
         elif halo_list == 'all':
-            self._halo_projection_list = self.all_halos
+            halo_projection_list = self.all_halos
         elif isinstance(halo_list, types.StringType):
-            self._halo_projection_list = self._read_halo_list(halo_list)
+            halo_projection_list = self._read_halo_list(halo_list)
         elif isinstance(halo_list, types.ListType):
-            self._halo_projection_list = halo_list
+            halo_projection_list = halo_list
         else:
             mylog.error("Keyword, halo_list', must be 'filtered', 'all', a filename, or an actual list.")
             return
 
-        if len(self._halo_projection_list) == 0:
+        if len(halo_projection_list) == 0:
             mylog.error("Halo list for projections is empty.")
             return
 
@@ -656,7 +656,7 @@
                          self.pf.parameters['DomainRightEdge'][w])
                   for w in range(self.pf.parameters['TopGridRank'])]
 
-        for halo in self._get_objs('_halo_projection_list', round_robin=True):
+        for halo in parallel_objects(halo_projection_list, -1):
             if halo is None:
                 continue
             # Check if region will overlap domain edge.
@@ -778,18 +778,18 @@
 
         # Get list of halos for projecting.
         if halo_list == 'filtered':
-            self._halo_analysis_list = self.filtered_halos
+            halo_analysis_list = self.filtered_halos
         elif halo_list == 'all':
-            self._halo_analysis_list = self.all_halos
+            halo_analysis_list = self.all_halos
         elif isinstance(halo_list, types.StringType):
-            self._halo_analysis_list = self._read_halo_list(halo_list)
+            halo_analysis_list = self._read_halo_list(halo_list)
         elif isinstance(halo_list, types.ListType):
-            self._halo_analysis_list = halo_list
+            halo_analysis_list = halo_list
         else:
             mylog.error("Keyword, halo_list', must be 'filtered', 'all', a filename, or an actual list.")
             return
 
-        if len(self._halo_analysis_list) == 0:
+        if len(halo_analysis_list) == 0:
             mylog.error("Halo list for analysis is empty.")
             return
 
@@ -803,7 +803,7 @@
                 my_output_dir = "%s/%s" % (self.pf.fullpath, analysis_output_dir)
             self.__check_directory(my_output_dir)
 
-        for halo in self._get_objs('_halo_analysis_list', round_robin=True):
+        for halo in parallel_objects(halo_analysis_list, -1):
             if halo is None: continue
 
             # Get a sphere object to analze.

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