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

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Wed Jul 12 10:59:11 PDT 2017


51 new commits in yt:

https://bitbucket.org/yt_analysis/yt/commits/eb8a68473134/
Changeset:   eb8a68473134
Branch:      yt
User:        al007
Date:        2017-02-17 00:40:45+00:00
Summary:     Get started on line plot.
Affected #:  2 files

diff -r 662fdbe5331ad344f09868c8115f697e5a3bae62 -r eb8a6847313443e439e5981daa86c129d1cfc2a9 yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -65,6 +65,7 @@
 
     def pixelize(self, dimension, data_source, field, bounds, size,
                  antialias = True, periodic = True):
+        import pdb; pdb.set_trace()
         index = data_source.ds.index
         if (hasattr(index, 'meshes') and
            not isinstance(index.meshes[0], SemiStructuredMesh)):

diff -r 662fdbe5331ad344f09868c8115f697e5a3bae62 -r eb8a6847313443e439e5981daa86c129d1cfc2a9 yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -862,3 +862,73 @@
     free(vertices)
     free(field_vals)
     return img
+
+def element_mesh_line_plot(np.ndarray[np.float64_t, ndim=2] coords,
+                           np.ndarray[np.int64_t, ndim=2] conn,
+                           np.ndarray[np.float64_t, ndim=1] start_point,
+                           np.ndarray[np.float64_t, ndim=1] end_point,
+                           resolution
+                           np.ndarray[np.float64_t, ndim=2] field,
+                           int index_offset = 0):
+
+    cdef np.float64_t *vertices
+    cdef np.float64_t *field_vals
+    cdef int nvertices = conn.shape[1]
+    cdef int ndim = coords.shape[1]
+    cdef int num_field_vals = field.shape[1]
+    cdef double[4] mapped_coord
+    cdef ElementSampler sampler
+    cdef np.float64_t lin_vec[3]
+    cdef np.float64_t lin_inc[3]
+    cdef np.ndarray[np.float64_t, ndim=2] lin_sample_points
+    lin_sample_points = np.zeros((resolution + 1, 3), dtype="float64")
+    cdef np.int64_t i, n
+    cdef np.float64_t arc_length[resolution + 1]
+    cdef np.float64_t lin_length
+
+    # Pick the right sampler and allocate storage for the mapped coordinate
+    if ndim == 3 and nvertices == 4:
+        sampler = P1Sampler3D()
+    elif ndim == 3 and nvertices == 6:
+        sampler = W1Sampler3D()
+    elif ndim == 3 and nvertices == 8:
+        sampler = Q1Sampler3D()
+    elif ndim == 3 and nvertices == 20:
+        sampler = S2Sampler3D()
+    elif ndim == 2 and nvertices == 3:
+        sampler = P1Sampler2D()
+    elif ndim == 1 and nvertices == 2:
+        sampler = P1Sampler1D()
+    elif ndim == 2 and nvertices == 4:
+        sampler = Q1Sampler2D()
+    elif ndim == 2 and nvertices == 6:
+        sampler = T2Sampler2D()
+    elif ndim == 3 and nvertices == 10:
+        sampler = Tet2Sampler3D()
+    else:
+        raise YTElementTypeNotRecognized(ndim, nvertices)
+
+    # allocate temporary storage
+    vertices = <np.float64_t *> malloc(ndim * sizeof(np.float64_t) * nvertices)
+    field_vals = <np.float64_t *> malloc(sizeof(np.float64_t) * num_field_vals)
+
+    for ci in range(conn.shape[0]):
+
+        for n in range(num_field_vals):
+            field_vals[n] = field[ci, n]
+
+        # Fill the vertices
+        for n in range(nvertices):
+            cj = conn[ci, n] - index_offset
+            for i in range(ndim):
+                vertices[ndim*n + i] = coords[cj, i]
+
+    lin_vec = end_point - start_point
+    lin_length = np.linalg.norm(lin_vec)
+    lin_inc = lin_vec / resolution
+    inc_length = np.linalg.norm(lin_inc)
+    lin_sample_points[0] = start_point
+    arc_length[0] = 0
+    for i in range(1, resolution + 1):
+        lin_sample_points[i] = lin_sample_points[i-1] + lin_inc
+        arc_length[i] = arc_length[i-1] + inc_length


https://bitbucket.org/yt_analysis/yt/commits/f061532a8b56/
Changeset:   f061532a8b56
Branch:      yt
User:        al007
Date:        2017-02-17 19:25:02+00:00
Summary:     Ready to try compiling.
Affected #:  2 files

diff -r eb8a6847313443e439e5981daa86c129d1cfc2a9 -r f061532a8b566df8e2a4c8660debdcc30d15afa1 yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -120,6 +120,39 @@
             return self._oblique_pixelize(data_source, field, bounds, size,
                                           antialias)
 
+    def line_plot(self, data_source, field, start_point, end_point, resolution):
+        import pdb; pdb.set_trace()
+        index = data_source.ds.index
+        if (hasattr(index, 'meshes') and
+           not isinstance(index.meshes[0], SemiStructuredMesh)):
+            ftype, fname = field
+            if ftype == "all":
+                mesh_id = 0
+                indices = np.concatenate([mesh.connectivity_indices for mesh in index.mesh_union])
+            else:
+                mesh_id = int(ftype[-1]) - 1
+                indices = index.meshes[mesh_id].connectivity_indices
+
+            coords = index.meshes[mesh_id].connectivity_coords
+            offset = index.meshes[mesh_id]._index_offset
+            ad = data_source.ds.all_data()
+            field_data = ad[field]
+            if field_data.shape[1] == 27:
+                # hexahedral
+                mylog.warning("High order elements not yet supported, " +
+                              "dropping to 1st order.")
+                field_data = field_data[:, 0:8]
+                indices = indices[:, 0:8]
+
+            arc_length, plot_values = element_mesh_line_plot(coords, indices,
+                                                             start_point,
+                                                             end_point,
+                                                             resolution, field,
+                                                             index_offset=offset)
+
+            return arc_length, plot_values
+
+
     def _ortho_pixelize(self, data_source, field, bounds, size, antialias,
                         dim, periodic):
         # We should be using fcoords

diff -r eb8a6847313443e439e5981daa86c129d1cfc2a9 -r f061532a8b566df8e2a4c8660debdcc30d15afa1 yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -119,7 +119,7 @@
     # (lr) and then iterate up to "right column" (rc) and "uppeR row" (rr),
     # depositing into them the data value.  Overlap computes the relative
     # overlap of a data value with a pixel.
-    # 
+    #
     # NOTE ON ROWS AND COLUMNS:
     #
     #   The way that images are plotting in matplotlib is somewhat different
@@ -497,7 +497,7 @@
     cdef np.float64_t r_i, theta_i, dr_i, dtheta_i, dthetamin
     cdef np.float64_t costheta, sintheta
     cdef int i, pi, pj
-    
+
     cdef int imax = np.asarray(radius).argmax()
     rmax = radius[imax] + dradius[imax]
 
@@ -867,7 +867,7 @@
                            np.ndarray[np.int64_t, ndim=2] conn,
                            np.ndarray[np.float64_t, ndim=1] start_point,
                            np.ndarray[np.float64_t, ndim=1] end_point,
-                           resolution
+                           resolution,
                            np.ndarray[np.float64_t, ndim=2] field,
                            int index_offset = 0):
 
@@ -876,15 +876,20 @@
     cdef int nvertices = conn.shape[1]
     cdef int ndim = coords.shape[1]
     cdef int num_field_vals = field.shape[1]
+    cdef int num_plot_nodes = resolution + 1
     cdef double[4] mapped_coord
     cdef ElementSampler sampler
     cdef np.float64_t lin_vec[3]
     cdef np.float64_t lin_inc[3]
     cdef np.ndarray[np.float64_t, ndim=2] lin_sample_points
-    lin_sample_points = np.zeros((resolution + 1, 3), dtype="float64")
+    lin_sample_points = np.zeros((num_plot_nodes, 3), dtype="float64")
     cdef np.int64_t i, n
-    cdef np.float64_t arc_length[resolution + 1]
+    cdef np.ndarray[np.float64_t, ndim=1] arc_length
+    arc_length = np.zeros(num_plot_nodes, dtype="float64")
     cdef np.float64_t lin_length
+    cdef np.ndarray[np.float64_t, ndim=1] plot_values
+    plot_values = np.zeros(num_plot_nodes, dtype="foat64")
+    cdef np.float64_t sample_point[3]
 
     # Pick the right sampler and allocate storage for the mapped coordinate
     if ndim == 3 and nvertices == 4:
@@ -912,8 +917,17 @@
     vertices = <np.float64_t *> malloc(ndim * sizeof(np.float64_t) * nvertices)
     field_vals = <np.float64_t *> malloc(sizeof(np.float64_t) * num_field_vals)
 
+    lin_vec = end_point - start_point
+    lin_length = np.linalg.norm(lin_vec)
+    lin_inc = lin_vec / resolution
+    inc_length = np.linalg.norm(lin_inc)
+    lin_sample_points[0] = start_point
+    arc_length[0] = 0
+    for i in range(1, resolution + 1):
+        lin_sample_points[i] = lin_sample_points[i-1] + lin_inc
+        arc_length[i] = arc_length[i-1] + inc_length
+
     for ci in range(conn.shape[0]):
-
         for n in range(num_field_vals):
             field_vals[n] = field[ci, n]
 
@@ -923,12 +937,13 @@
             for i in range(ndim):
                 vertices[ndim*n + i] = coords[cj, i]
 
-    lin_vec = end_point - start_point
-    lin_length = np.linalg.norm(lin_vec)
-    lin_inc = lin_vec / resolution
-    inc_length = np.linalg.norm(lin_inc)
-    lin_sample_points[0] = start_point
-    arc_length[0] = 0
-    for i in range(1, resolution + 1):
-        lin_sample_points[i] = lin_sample_points[i-1] + lin_inc
-        arc_length[i] = arc_length[i-1] + inc_length
+        for i in range(resolution + 1):
+            sample_point = lin_sample_points[i]
+            sampler.map_real_to_unit(mapped_coord, vertices, sample_point)
+            if not sampler.check_inside(mapped_coord):
+                continue
+            plot_values = sampler.sample_at_unit_point(mapped_coord, field_vals)
+
+    free(vertices)
+    free(field_vals)
+    return arc_length, plot_values


https://bitbucket.org/yt_analysis/yt/commits/59e73dee28b5/
Changeset:   59e73dee28b5
Branch:      yt
User:        al007
Date:        2017-02-17 22:19:57+00:00
Summary:     Bad ordering of for loops in pixelization_routines
Affected #:  2 files

diff -r f061532a8b566df8e2a4c8660debdcc30d15afa1 -r 59e73dee28b5eec194176aae9e3ae1094c828447 yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -24,7 +24,8 @@
 from yt.funcs import mylog
 from yt.utilities.lib.pixelization_routines import \
     pixelize_element_mesh, pixelize_off_axis_cartesian, \
-    pixelize_cartesian, pixelize_cartesian_nodal
+    pixelize_cartesian, pixelize_cartesian_nodal,
+    element_mesh_line_plot
 from yt.data_objects.unstructured_mesh import SemiStructuredMesh
 from yt.utilities.nodal_data_utils import get_nodal_data
 
@@ -120,12 +121,11 @@
             return self._oblique_pixelize(data_source, field, bounds, size,
                                           antialias)
 
-    def line_plot(self, data_source, field, start_point, end_point, resolution):
-        import pdb; pdb.set_trace()
-        index = data_source.ds.index
+    def line_plot(self, field, start_point, end_point, resolution):
+        index = self.ds.index
         if (hasattr(index, 'meshes') and
            not isinstance(index.meshes[0], SemiStructuredMesh)):
-            ftype, fname = field
+            ftype, fname = field[0]
             if ftype == "all":
                 mesh_id = 0
                 indices = np.concatenate([mesh.connectivity_indices for mesh in index.mesh_union])
@@ -134,9 +134,14 @@
                 indices = index.meshes[mesh_id].connectivity_indices
 
             coords = index.meshes[mesh_id].connectivity_coords
+            if coords.shape[1] != end_point.size != start_point.size:
+                raise ValueError("The coordinate dimension doesn't match the "
+                                 "start and end point dimensions.")
+
+
             offset = index.meshes[mesh_id]._index_offset
-            ad = data_source.ds.all_data()
-            field_data = ad[field]
+            ad = self.ds.all_data()
+            field_data = ad[field[0]]
             if field_data.shape[1] == 27:
                 # hexahedral
                 mylog.warning("High order elements not yet supported, " +
@@ -147,7 +152,7 @@
             arc_length, plot_values = element_mesh_line_plot(coords, indices,
                                                              start_point,
                                                              end_point,
-                                                             resolution, field,
+                                                             resolution, field_data,
                                                              index_offset=offset)
 
             return arc_length, plot_values

diff -r f061532a8b566df8e2a4c8660debdcc30d15afa1 -r 59e73dee28b5eec194176aae9e3ae1094c828447 yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -879,18 +879,22 @@
     cdef int num_plot_nodes = resolution + 1
     cdef double[4] mapped_coord
     cdef ElementSampler sampler
-    cdef np.float64_t lin_vec[3]
-    cdef np.float64_t lin_inc[3]
+    cdef np.ndarray[np.float64_t, ndim=1] lin_vec
+    cdef np.ndarray[np.float64_t, ndim=1] lin_inc
     cdef np.ndarray[np.float64_t, ndim=2] lin_sample_points
-    lin_sample_points = np.zeros((num_plot_nodes, 3), dtype="float64")
-    cdef np.int64_t i, n
+    cdef np.int64_t i, n, j
     cdef np.ndarray[np.float64_t, ndim=1] arc_length
-    arc_length = np.zeros(num_plot_nodes, dtype="float64")
-    cdef np.float64_t lin_length
+    cdef np.float64_t lin_length, inc_length
     cdef np.ndarray[np.float64_t, ndim=1] plot_values
-    plot_values = np.zeros(num_plot_nodes, dtype="foat64")
     cdef np.float64_t sample_point[3]
 
+    lin_vec = np.zeros(ndim, dtype="float64")
+    lin_inc = np.zeros(ndim, dtype="float64")
+
+    lin_sample_points = np.zeros((num_plot_nodes, ndim), dtype="float64")
+    arc_length = np.zeros(num_plot_nodes, dtype="float64")
+    plot_values = np.zeros(num_plot_nodes, dtype="float64")
+
     # Pick the right sampler and allocate storage for the mapped coordinate
     if ndim == 3 and nvertices == 4:
         sampler = P1Sampler3D()
@@ -920,29 +924,39 @@
     lin_vec = end_point - start_point
     lin_length = np.linalg.norm(lin_vec)
     lin_inc = lin_vec / resolution
-    inc_length = np.linalg.norm(lin_inc)
-    lin_sample_points[0] = start_point
+    inc_length = lin_length / resolution
+    for j in range(ndim):
+        lin_sample_points[0, j] = start_point[j]
     arc_length[0] = 0
     for i in range(1, resolution + 1):
-        lin_sample_points[i] = lin_sample_points[i-1] + lin_inc
-        arc_length[i] = arc_length[i-1] + inc_length
+        for j in range(ndim):
+            lin_sample_points[i, j] = lin_sample_points[i-1, j] + lin_inc[j]
+            arc_length[i] = arc_length[i-1] + inc_length
 
-    for ci in range(conn.shape[0]):
-        for n in range(num_field_vals):
-            field_vals[n] = field[ci, n]
+    for i in range(resolution + 1):
+        for j in range(3):
+            if j < ndim:
+                sample_point[j] = lin_sample_points[i][j]
+            else:
+                sample_point[j] = 0
+        for ci in range(conn.shape[0]):
+            for n in range(num_field_vals):
+                field_vals[n] = field[ci, n]
 
-        # Fill the vertices
-        for n in range(nvertices):
-            cj = conn[ci, n] - index_offset
-            for i in range(ndim):
-                vertices[ndim*n + i] = coords[cj, i]
+            # Fill the vertices
+            for n in range(nvertices):
+                cj = conn[ci, n] - index_offset
+                for i in range(ndim):
+                    vertices[ndim*n + i] = coords[cj, i]
 
-        for i in range(resolution + 1):
-            sample_point = lin_sample_points[i]
             sampler.map_real_to_unit(mapped_coord, vertices, sample_point)
-            if not sampler.check_inside(mapped_coord):
+            if not sampler.check_inside(mapped_coord) and ci != conn.shape[0] - 1:
                 continue
-            plot_values = sampler.sample_at_unit_point(mapped_coord, field_vals)
+            elif not sampler.check_inside(mapped_coord):
+                raise RuntimeError("It's impossible that the line point doesn't "
+                                   "fall within any of the elements.")
+            plot_values[i] = sampler.sample_at_unit_point(mapped_coord, field_vals)
+            break
 
     free(vertices)
     free(field_vals)


https://bitbucket.org/yt_analysis/yt/commits/ce965bc94703/
Changeset:   ce965bc94703
Branch:      yt
User:        al007
Date:        2017-02-18 00:18:18+00:00
Summary:     New code doesn't show any spikes. Nice!
Affected #:  1 file

diff -r 59e73dee28b5eec194176aae9e3ae1094c828447 -r ce965bc94703ab0bbc2fa1d4fd2fd402c6db3238 yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -882,7 +882,7 @@
     cdef np.ndarray[np.float64_t, ndim=1] lin_vec
     cdef np.ndarray[np.float64_t, ndim=1] lin_inc
     cdef np.ndarray[np.float64_t, ndim=2] lin_sample_points
-    cdef np.int64_t i, n, j
+    cdef np.int64_t i, n, j, k
     cdef np.ndarray[np.float64_t, ndim=1] arc_length
     cdef np.float64_t lin_length, inc_length
     cdef np.ndarray[np.float64_t, ndim=1] plot_values
@@ -946,8 +946,8 @@
             # Fill the vertices
             for n in range(nvertices):
                 cj = conn[ci, n] - index_offset
-                for i in range(ndim):
-                    vertices[ndim*n + i] = coords[cj, i]
+                for k in range(ndim):
+                    vertices[ndim*n + k] = coords[cj, k]
 
             sampler.map_real_to_unit(mapped_coord, vertices, sample_point)
             if not sampler.check_inside(mapped_coord) and ci != conn.shape[0] - 1:


https://bitbucket.org/yt_analysis/yt/commits/48059f104435/
Changeset:   48059f104435
Branch:      yt
User:        al007
Date:        2017-02-20 14:56:10+00:00
Summary:     Remove pdb set trace
Affected #:  1 file

diff -r ce965bc94703ab0bbc2fa1d4fd2fd402c6db3238 -r 48059f1044355dee30eddb937ae3fb4cbd783041 yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -66,7 +66,6 @@
 
     def pixelize(self, dimension, data_source, field, bounds, size,
                  antialias = True, periodic = True):
-        import pdb; pdb.set_trace()
         index = data_source.ds.index
         if (hasattr(index, 'meshes') and
            not isinstance(index.meshes[0], SemiStructuredMesh)):
@@ -125,7 +124,7 @@
         index = self.ds.index
         if (hasattr(index, 'meshes') and
            not isinstance(index.meshes[0], SemiStructuredMesh)):
-            ftype, fname = field[0]
+            ftype, fname = field
             if ftype == "all":
                 mesh_id = 0
                 indices = np.concatenate([mesh.connectivity_indices for mesh in index.mesh_union])
@@ -141,7 +140,7 @@
 
             offset = index.meshes[mesh_id]._index_offset
             ad = self.ds.all_data()
-            field_data = ad[field[0]]
+            field_data = ad[field]
             if field_data.shape[1] == 27:
                 # hexahedral
                 mylog.warning("High order elements not yet supported, " +


https://bitbucket.org/yt_analysis/yt/commits/8595f7887f5f/
Changeset:   8595f7887f5f
Branch:      yt
User:        al007
Date:        2017-03-17 23:17:43+00:00
Summary:     Demonstrate quad9 support with line plot.
Affected #:  1 file

diff -r 48059f1044355dee30eddb937ae3fb4cbd783041 -r 8595f7887f5fb5064ce41c571734621b7473ff0d yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -910,6 +910,8 @@
         sampler = P1Sampler1D()
     elif ndim == 2 and nvertices == 4:
         sampler = Q1Sampler2D()
+    elif ndim == 2 and nvertices == 9:
+        sampler = Q2Sampler2D()
     elif ndim == 2 and nvertices == 6:
         sampler = T2Sampler2D()
     elif ndim == 3 and nvertices == 10:


https://bitbucket.org/yt_analysis/yt/commits/d4b51a2d6fcc/
Changeset:   d4b51a2d6fcc
Branch:      yt
User:        al007
Date:        2017-02-17 00:40:45+00:00
Summary:     Get started on line plot.
Affected #:  2 files

diff -r fb62830c163dff615a41314214a81e4e784155b1 -r d4b51a2d6fcc6de3e4ee7872b850b59a353e3172 yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -64,6 +64,7 @@
 
     def pixelize(self, dimension, data_source, field, bounds, size,
                  antialias = True, periodic = True):
+        import pdb; pdb.set_trace()
         index = data_source.ds.index
         if (hasattr(index, 'meshes') and
            not isinstance(index.meshes[0], SemiStructuredMesh)):

diff -r fb62830c163dff615a41314214a81e4e784155b1 -r d4b51a2d6fcc6de3e4ee7872b850b59a353e3172 yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -694,3 +694,73 @@
     free(vertices)
     free(field_vals)
     return img
+
+def element_mesh_line_plot(np.ndarray[np.float64_t, ndim=2] coords,
+                           np.ndarray[np.int64_t, ndim=2] conn,
+                           np.ndarray[np.float64_t, ndim=1] start_point,
+                           np.ndarray[np.float64_t, ndim=1] end_point,
+                           resolution
+                           np.ndarray[np.float64_t, ndim=2] field,
+                           int index_offset = 0):
+
+    cdef np.float64_t *vertices
+    cdef np.float64_t *field_vals
+    cdef int nvertices = conn.shape[1]
+    cdef int ndim = coords.shape[1]
+    cdef int num_field_vals = field.shape[1]
+    cdef double[4] mapped_coord
+    cdef ElementSampler sampler
+    cdef np.float64_t lin_vec[3]
+    cdef np.float64_t lin_inc[3]
+    cdef np.ndarray[np.float64_t, ndim=2] lin_sample_points
+    lin_sample_points = np.zeros((resolution + 1, 3), dtype="float64")
+    cdef np.int64_t i, n
+    cdef np.float64_t arc_length[resolution + 1]
+    cdef np.float64_t lin_length
+
+    # Pick the right sampler and allocate storage for the mapped coordinate
+    if ndim == 3 and nvertices == 4:
+        sampler = P1Sampler3D()
+    elif ndim == 3 and nvertices == 6:
+        sampler = W1Sampler3D()
+    elif ndim == 3 and nvertices == 8:
+        sampler = Q1Sampler3D()
+    elif ndim == 3 and nvertices == 20:
+        sampler = S2Sampler3D()
+    elif ndim == 2 and nvertices == 3:
+        sampler = P1Sampler2D()
+    elif ndim == 1 and nvertices == 2:
+        sampler = P1Sampler1D()
+    elif ndim == 2 and nvertices == 4:
+        sampler = Q1Sampler2D()
+    elif ndim == 2 and nvertices == 6:
+        sampler = T2Sampler2D()
+    elif ndim == 3 and nvertices == 10:
+        sampler = Tet2Sampler3D()
+    else:
+        raise YTElementTypeNotRecognized(ndim, nvertices)
+
+    # allocate temporary storage
+    vertices = <np.float64_t *> malloc(ndim * sizeof(np.float64_t) * nvertices)
+    field_vals = <np.float64_t *> malloc(sizeof(np.float64_t) * num_field_vals)
+
+    for ci in range(conn.shape[0]):
+
+        for n in range(num_field_vals):
+            field_vals[n] = field[ci, n]
+
+        # Fill the vertices
+        for n in range(nvertices):
+            cj = conn[ci, n] - index_offset
+            for i in range(ndim):
+                vertices[ndim*n + i] = coords[cj, i]
+
+    lin_vec = end_point - start_point
+    lin_length = np.linalg.norm(lin_vec)
+    lin_inc = lin_vec / resolution
+    inc_length = np.linalg.norm(lin_inc)
+    lin_sample_points[0] = start_point
+    arc_length[0] = 0
+    for i in range(1, resolution + 1):
+        lin_sample_points[i] = lin_sample_points[i-1] + lin_inc
+        arc_length[i] = arc_length[i-1] + inc_length


https://bitbucket.org/yt_analysis/yt/commits/95d92056c4fd/
Changeset:   95d92056c4fd
Branch:      yt
User:        al007
Date:        2017-02-17 19:25:02+00:00
Summary:     Ready to try compiling.
Affected #:  2 files

diff -r d4b51a2d6fcc6de3e4ee7872b850b59a353e3172 -r 95d92056c4fd0e9bf638a7e411a308ed2feda04d yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -125,6 +125,39 @@
             return self._oblique_pixelize(data_source, field, bounds, size,
                                           antialias)
 
+    def line_plot(self, data_source, field, start_point, end_point, resolution):
+        import pdb; pdb.set_trace()
+        index = data_source.ds.index
+        if (hasattr(index, 'meshes') and
+           not isinstance(index.meshes[0], SemiStructuredMesh)):
+            ftype, fname = field
+            if ftype == "all":
+                mesh_id = 0
+                indices = np.concatenate([mesh.connectivity_indices for mesh in index.mesh_union])
+            else:
+                mesh_id = int(ftype[-1]) - 1
+                indices = index.meshes[mesh_id].connectivity_indices
+
+            coords = index.meshes[mesh_id].connectivity_coords
+            offset = index.meshes[mesh_id]._index_offset
+            ad = data_source.ds.all_data()
+            field_data = ad[field]
+            if field_data.shape[1] == 27:
+                # hexahedral
+                mylog.warning("High order elements not yet supported, " +
+                              "dropping to 1st order.")
+                field_data = field_data[:, 0:8]
+                indices = indices[:, 0:8]
+
+            arc_length, plot_values = element_mesh_line_plot(coords, indices,
+                                                             start_point,
+                                                             end_point,
+                                                             resolution, field,
+                                                             index_offset=offset)
+
+            return arc_length, plot_values
+
+
     def _ortho_pixelize(self, data_source, field, bounds, size, antialias,
                         dim, periodic):
         # We should be using fcoords

diff -r d4b51a2d6fcc6de3e4ee7872b850b59a353e3172 -r 95d92056c4fd0e9bf638a7e411a308ed2feda04d yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -118,7 +118,7 @@
     # (lr) and then iterate up to "right column" (rc) and "uppeR row" (rr),
     # depositing into them the data value.  Overlap computes the relative
     # overlap of a data value with a pixel.
-    # 
+    #
     # NOTE ON ROWS AND COLUMNS:
     #
     #   The way that images are plotting in matplotlib is somewhat different
@@ -331,7 +331,7 @@
     cdef np.float64_t r_i, theta_i, dr_i, dtheta_i, dthetamin
     cdef np.float64_t costheta, sintheta
     cdef int i, pi, pj
-    
+
     cdef int imax = np.asarray(radius).argmax()
     rmax = radius[imax] + dradius[imax]
 
@@ -699,7 +699,7 @@
                            np.ndarray[np.int64_t, ndim=2] conn,
                            np.ndarray[np.float64_t, ndim=1] start_point,
                            np.ndarray[np.float64_t, ndim=1] end_point,
-                           resolution
+                           resolution,
                            np.ndarray[np.float64_t, ndim=2] field,
                            int index_offset = 0):
 
@@ -708,15 +708,20 @@
     cdef int nvertices = conn.shape[1]
     cdef int ndim = coords.shape[1]
     cdef int num_field_vals = field.shape[1]
+    cdef int num_plot_nodes = resolution + 1
     cdef double[4] mapped_coord
     cdef ElementSampler sampler
     cdef np.float64_t lin_vec[3]
     cdef np.float64_t lin_inc[3]
     cdef np.ndarray[np.float64_t, ndim=2] lin_sample_points
-    lin_sample_points = np.zeros((resolution + 1, 3), dtype="float64")
+    lin_sample_points = np.zeros((num_plot_nodes, 3), dtype="float64")
     cdef np.int64_t i, n
-    cdef np.float64_t arc_length[resolution + 1]
+    cdef np.ndarray[np.float64_t, ndim=1] arc_length
+    arc_length = np.zeros(num_plot_nodes, dtype="float64")
     cdef np.float64_t lin_length
+    cdef np.ndarray[np.float64_t, ndim=1] plot_values
+    plot_values = np.zeros(num_plot_nodes, dtype="foat64")
+    cdef np.float64_t sample_point[3]
 
     # Pick the right sampler and allocate storage for the mapped coordinate
     if ndim == 3 and nvertices == 4:
@@ -744,8 +749,17 @@
     vertices = <np.float64_t *> malloc(ndim * sizeof(np.float64_t) * nvertices)
     field_vals = <np.float64_t *> malloc(sizeof(np.float64_t) * num_field_vals)
 
+    lin_vec = end_point - start_point
+    lin_length = np.linalg.norm(lin_vec)
+    lin_inc = lin_vec / resolution
+    inc_length = np.linalg.norm(lin_inc)
+    lin_sample_points[0] = start_point
+    arc_length[0] = 0
+    for i in range(1, resolution + 1):
+        lin_sample_points[i] = lin_sample_points[i-1] + lin_inc
+        arc_length[i] = arc_length[i-1] + inc_length
+
     for ci in range(conn.shape[0]):
-
         for n in range(num_field_vals):
             field_vals[n] = field[ci, n]
 
@@ -755,12 +769,13 @@
             for i in range(ndim):
                 vertices[ndim*n + i] = coords[cj, i]
 
-    lin_vec = end_point - start_point
-    lin_length = np.linalg.norm(lin_vec)
-    lin_inc = lin_vec / resolution
-    inc_length = np.linalg.norm(lin_inc)
-    lin_sample_points[0] = start_point
-    arc_length[0] = 0
-    for i in range(1, resolution + 1):
-        lin_sample_points[i] = lin_sample_points[i-1] + lin_inc
-        arc_length[i] = arc_length[i-1] + inc_length
+        for i in range(resolution + 1):
+            sample_point = lin_sample_points[i]
+            sampler.map_real_to_unit(mapped_coord, vertices, sample_point)
+            if not sampler.check_inside(mapped_coord):
+                continue
+            plot_values = sampler.sample_at_unit_point(mapped_coord, field_vals)
+
+    free(vertices)
+    free(field_vals)
+    return arc_length, plot_values


https://bitbucket.org/yt_analysis/yt/commits/6415ed2c5dbb/
Changeset:   6415ed2c5dbb
Branch:      yt
User:        al007
Date:        2017-02-17 22:19:57+00:00
Summary:     Bad ordering of for loops in pixelization_routines
Affected #:  2 files

diff -r 95d92056c4fd0e9bf638a7e411a308ed2feda04d -r 6415ed2c5dbb93ff0f09860b2e4a7f0cb6fb5698 yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -24,7 +24,7 @@
 from yt.funcs import mylog
 from yt.utilities.lib.pixelization_routines import \
     pixelize_element_mesh, pixelize_off_axis_cartesian, \
-    pixelize_cartesian
+    pixelize_cartesian, element_mesh_line_plot
 from yt.data_objects.unstructured_mesh import SemiStructuredMesh
 
 
@@ -125,12 +125,11 @@
             return self._oblique_pixelize(data_source, field, bounds, size,
                                           antialias)
 
-    def line_plot(self, data_source, field, start_point, end_point, resolution):
-        import pdb; pdb.set_trace()
-        index = data_source.ds.index
+    def line_plot(self, field, start_point, end_point, resolution):
+        index = self.ds.index
         if (hasattr(index, 'meshes') and
            not isinstance(index.meshes[0], SemiStructuredMesh)):
-            ftype, fname = field
+            ftype, fname = field[0]
             if ftype == "all":
                 mesh_id = 0
                 indices = np.concatenate([mesh.connectivity_indices for mesh in index.mesh_union])
@@ -139,9 +138,14 @@
                 indices = index.meshes[mesh_id].connectivity_indices
 
             coords = index.meshes[mesh_id].connectivity_coords
+            if coords.shape[1] != end_point.size != start_point.size:
+                raise ValueError("The coordinate dimension doesn't match the "
+                                 "start and end point dimensions.")
+
+
             offset = index.meshes[mesh_id]._index_offset
-            ad = data_source.ds.all_data()
-            field_data = ad[field]
+            ad = self.ds.all_data()
+            field_data = ad[field[0]]
             if field_data.shape[1] == 27:
                 # hexahedral
                 mylog.warning("High order elements not yet supported, " +
@@ -152,7 +156,7 @@
             arc_length, plot_values = element_mesh_line_plot(coords, indices,
                                                              start_point,
                                                              end_point,
-                                                             resolution, field,
+                                                             resolution, field_data,
                                                              index_offset=offset)
 
             return arc_length, plot_values

diff -r 95d92056c4fd0e9bf638a7e411a308ed2feda04d -r 6415ed2c5dbb93ff0f09860b2e4a7f0cb6fb5698 yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -711,18 +711,22 @@
     cdef int num_plot_nodes = resolution + 1
     cdef double[4] mapped_coord
     cdef ElementSampler sampler
-    cdef np.float64_t lin_vec[3]
-    cdef np.float64_t lin_inc[3]
+    cdef np.ndarray[np.float64_t, ndim=1] lin_vec
+    cdef np.ndarray[np.float64_t, ndim=1] lin_inc
     cdef np.ndarray[np.float64_t, ndim=2] lin_sample_points
-    lin_sample_points = np.zeros((num_plot_nodes, 3), dtype="float64")
-    cdef np.int64_t i, n
+    cdef np.int64_t i, n, j
     cdef np.ndarray[np.float64_t, ndim=1] arc_length
-    arc_length = np.zeros(num_plot_nodes, dtype="float64")
-    cdef np.float64_t lin_length
+    cdef np.float64_t lin_length, inc_length
     cdef np.ndarray[np.float64_t, ndim=1] plot_values
-    plot_values = np.zeros(num_plot_nodes, dtype="foat64")
     cdef np.float64_t sample_point[3]
 
+    lin_vec = np.zeros(ndim, dtype="float64")
+    lin_inc = np.zeros(ndim, dtype="float64")
+
+    lin_sample_points = np.zeros((num_plot_nodes, ndim), dtype="float64")
+    arc_length = np.zeros(num_plot_nodes, dtype="float64")
+    plot_values = np.zeros(num_plot_nodes, dtype="float64")
+
     # Pick the right sampler and allocate storage for the mapped coordinate
     if ndim == 3 and nvertices == 4:
         sampler = P1Sampler3D()
@@ -752,29 +756,39 @@
     lin_vec = end_point - start_point
     lin_length = np.linalg.norm(lin_vec)
     lin_inc = lin_vec / resolution
-    inc_length = np.linalg.norm(lin_inc)
-    lin_sample_points[0] = start_point
+    inc_length = lin_length / resolution
+    for j in range(ndim):
+        lin_sample_points[0, j] = start_point[j]
     arc_length[0] = 0
     for i in range(1, resolution + 1):
-        lin_sample_points[i] = lin_sample_points[i-1] + lin_inc
-        arc_length[i] = arc_length[i-1] + inc_length
+        for j in range(ndim):
+            lin_sample_points[i, j] = lin_sample_points[i-1, j] + lin_inc[j]
+            arc_length[i] = arc_length[i-1] + inc_length
 
-    for ci in range(conn.shape[0]):
-        for n in range(num_field_vals):
-            field_vals[n] = field[ci, n]
+    for i in range(resolution + 1):
+        for j in range(3):
+            if j < ndim:
+                sample_point[j] = lin_sample_points[i][j]
+            else:
+                sample_point[j] = 0
+        for ci in range(conn.shape[0]):
+            for n in range(num_field_vals):
+                field_vals[n] = field[ci, n]
 
-        # Fill the vertices
-        for n in range(nvertices):
-            cj = conn[ci, n] - index_offset
-            for i in range(ndim):
-                vertices[ndim*n + i] = coords[cj, i]
+            # Fill the vertices
+            for n in range(nvertices):
+                cj = conn[ci, n] - index_offset
+                for i in range(ndim):
+                    vertices[ndim*n + i] = coords[cj, i]
 
-        for i in range(resolution + 1):
-            sample_point = lin_sample_points[i]
             sampler.map_real_to_unit(mapped_coord, vertices, sample_point)
-            if not sampler.check_inside(mapped_coord):
+            if not sampler.check_inside(mapped_coord) and ci != conn.shape[0] - 1:
                 continue
-            plot_values = sampler.sample_at_unit_point(mapped_coord, field_vals)
+            elif not sampler.check_inside(mapped_coord):
+                raise RuntimeError("It's impossible that the line point doesn't "
+                                   "fall within any of the elements.")
+            plot_values[i] = sampler.sample_at_unit_point(mapped_coord, field_vals)
+            break
 
     free(vertices)
     free(field_vals)


https://bitbucket.org/yt_analysis/yt/commits/72b76ebf266e/
Changeset:   72b76ebf266e
Branch:      yt
User:        al007
Date:        2017-02-18 00:18:18+00:00
Summary:     New code doesn't show any spikes. Nice!
Affected #:  1 file

diff -r 6415ed2c5dbb93ff0f09860b2e4a7f0cb6fb5698 -r 72b76ebf266e65869b4531cc7af248e48745463f yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -714,7 +714,7 @@
     cdef np.ndarray[np.float64_t, ndim=1] lin_vec
     cdef np.ndarray[np.float64_t, ndim=1] lin_inc
     cdef np.ndarray[np.float64_t, ndim=2] lin_sample_points
-    cdef np.int64_t i, n, j
+    cdef np.int64_t i, n, j, k
     cdef np.ndarray[np.float64_t, ndim=1] arc_length
     cdef np.float64_t lin_length, inc_length
     cdef np.ndarray[np.float64_t, ndim=1] plot_values
@@ -778,8 +778,8 @@
             # Fill the vertices
             for n in range(nvertices):
                 cj = conn[ci, n] - index_offset
-                for i in range(ndim):
-                    vertices[ndim*n + i] = coords[cj, i]
+                for k in range(ndim):
+                    vertices[ndim*n + k] = coords[cj, k]
 
             sampler.map_real_to_unit(mapped_coord, vertices, sample_point)
             if not sampler.check_inside(mapped_coord) and ci != conn.shape[0] - 1:


https://bitbucket.org/yt_analysis/yt/commits/153dfc60f6e2/
Changeset:   153dfc60f6e2
Branch:      yt
User:        al007
Date:        2017-02-20 14:56:10+00:00
Summary:     Remove pdb set trace
Affected #:  1 file

diff -r 72b76ebf266e65869b4531cc7af248e48745463f -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -64,7 +64,6 @@
 
     def pixelize(self, dimension, data_source, field, bounds, size,
                  antialias = True, periodic = True):
-        import pdb; pdb.set_trace()
         index = data_source.ds.index
         if (hasattr(index, 'meshes') and
            not isinstance(index.meshes[0], SemiStructuredMesh)):
@@ -129,7 +128,7 @@
         index = self.ds.index
         if (hasattr(index, 'meshes') and
            not isinstance(index.meshes[0], SemiStructuredMesh)):
-            ftype, fname = field[0]
+            ftype, fname = field
             if ftype == "all":
                 mesh_id = 0
                 indices = np.concatenate([mesh.connectivity_indices for mesh in index.mesh_union])
@@ -145,7 +144,7 @@
 
             offset = index.meshes[mesh_id]._index_offset
             ad = self.ds.all_data()
-            field_data = ad[field[0]]
+            field_data = ad[field]
             if field_data.shape[1] == 27:
                 # hexahedral
                 mylog.warning("High order elements not yet supported, " +


https://bitbucket.org/yt_analysis/yt/commits/83eae35d29c6/
Changeset:   83eae35d29c6
Branch:      yt
User:        al007
Date:        2017-03-17 21:04:42+00:00
Summary:     Merge quad9 bookmark into line_plotting bookmark.
Affected #:  72 files

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 doc/Makefile
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -47,6 +47,9 @@
 	-rm -rf _temp/*.done source/cookbook/_static/*
 
 html:
+	sphinx-apidoc -o source/reference/api/ -e ../yt ../yt/extern/* \
+		$(shell find ../yt -name "*tests*" -type d) ../yt/utilities/voropp*
+	sed -e '/show-inheritance/ a\ \ \ \ :inherited-members:' -i source/reference/api/yt*.rst
 	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
 	@echo
 	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 doc/extensions/pythonscript_sphinxext.py
--- a/doc/extensions/pythonscript_sphinxext.py
+++ b/doc/extensions/pythonscript_sphinxext.py
@@ -1,4 +1,5 @@
 import tempfile
+import time
 import os
 import glob
 import shutil
@@ -37,12 +38,16 @@
             f.write(content)
 
         # Use sphinx logger?
+        uid = uuid.uuid4().hex[:8]
         print("")
+        print(">> Contents of the script: %s" % uid)
         print(content)
         print("")
 
+        start = time.time()
         subprocess.call(['python', 'temp.py'])
-
+        print(">> The execution of the script %s took %f s" %
+              (uid, time.time() - start))
         text = ''
         for im in sorted(glob.glob("*.png")):
             text += get_image_tag(im, image_dir, image_rel_dir)

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 doc/source/analyzing/Particle_Trajectories.ipynb
--- a/doc/source/analyzing/Particle_Trajectories.ipynb
+++ b/doc/source/analyzing/Particle_Trajectories.ipynb
@@ -279,9 +279,9 @@
    "source": [
     "fig = plt.figure(figsize=(8.0, 8.0))\n",
     "ax = fig.add_subplot(111, projection='3d')\n",
-    "ax.plot(trajs[\"particle_position_x\"][100], trajs[\"particle_position_z\"][100], trajs[\"particle_position_z\"][100])\n",
-    "ax.plot(trajs[\"particle_position_x\"][8], trajs[\"particle_position_z\"][8], trajs[\"particle_position_z\"][8])\n",
-    "ax.plot(trajs[\"particle_position_x\"][25], trajs[\"particle_position_z\"][25], trajs[\"particle_position_z\"][25])"
+    "ax.plot(trajs[\"particle_position_x\"][100], trajs[\"particle_position_y\"][100], trajs[\"particle_position_z\"][100])\n",
+    "ax.plot(trajs[\"particle_position_x\"][8], trajs[\"particle_position_y\"][8], trajs[\"particle_position_z\"][8])\n",
+    "ax.plot(trajs[\"particle_position_x\"][25], trajs[\"particle_position_y\"][25], trajs[\"particle_position_z\"][25])"
    ]
   },
   {

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 doc/source/analyzing/analysis_modules/halo_catalogs.rst
--- a/doc/source/analyzing/analysis_modules/halo_catalogs.rst
+++ b/doc/source/analyzing/analysis_modules/halo_catalogs.rst
@@ -481,7 +481,9 @@
 
 A :class:`~yt.analysis_modules.halo_analysis.halo_catalog.HaloCatalog`
 saved to disk can be reloaded as a yt dataset with the
-standard call to ``yt.load``. Any side data, such as profiles, can be reloaded
+standard call to ``yt.load``.  See :ref:`halocatalog` for a demonstration
+of loading and working only with the catalog.
+Any side data, such as profiles, can be reloaded
 with a ``load_profiles`` callback and a call to
 :func:`~yt.analysis_modules.halo_analysis.halo_catalog.HaloCatalog.load`.
 

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 doc/source/analyzing/analysis_modules/xray_emission_fields.rst
--- a/doc/source/analyzing/analysis_modules/xray_emission_fields.rst
+++ b/doc/source/analyzing/analysis_modules/xray_emission_fields.rst
@@ -1,3 +1,6 @@
 .. _xray_emission_fields:
 
+X-ray Emission Fields
+=====================
+
 .. notebook:: XrayEmissionFields.ipynb

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 doc/source/examining/loading_data.rst
--- a/doc/source/examining/loading_data.rst
+++ b/doc/source/examining/loading_data.rst
@@ -151,9 +151,9 @@
 
    import yt
 
-   units_override = {"length_unit":(1.0,"Mpc"),
-                     "time_unit"(1.0,"Myr"),
-                     "mass_unit":(1.0e14,"Msun")}
+   units_override = {"length_unit": (1.0, "Mpc"),
+                     "time_unit": (1.0, "Myr"),
+                     "mass_unit": (1.0e14, "Msun")}
 
    ds = yt.load("id0/cluster_merger.0250.vtk", units_override=units_override)
 
@@ -1458,7 +1458,8 @@
 
 If you have access to both the halo catalog and the simulation snapshot from
 the same redshift, additional analysis can be performed for each halo using
-:ref:`halo_catalog`.
+:ref:`halo_catalog`.  The resulting product can be reloaded in a similar manner
+to the other halo catalogs shown here.
 
 .. _rockstar:
 
@@ -1600,6 +1601,39 @@
    # The halo mass
    print(ad["FOF", "particle_mass"])
 
+.. _halocatalog:
+
+HaloCatalog
+^^^^^^^^^^^
+
+These are catalogs produced by the analysis discussed in :ref:`halo_catalog`.
+In the case where multiple files were produced, one need only provide the path
+to a single one of them.  The field type for all fields is "halos".  The fields
+available here are similar to other catalogs.  Any addition
+:ref:`halo_catalog_quantities` will also be accessible as fields.
+
++-------------------+---------------------------+
+| HaloCatalog field | yt field name             |
++===================+===========================+
+| halo id           | particle_identifier       |
++-------------------+---------------------------+
+| virial mass       | particle_mass             |
++-------------------+---------------------------+
+| virial radius     | virial_radius             |
++-------------------+---------------------------+
+| halo position     | particle_position_(x,y,z) |
++-------------------+---------------------------+
+| halo velocity     | particle_velocity_(x,y,z) |
++-------------------+---------------------------+
+
+.. code-block:: python
+
+   import yt
+   ds = yt.load("catalogs/catalog.0.h5")
+   ad = ds.all_data()
+   # The halo mass
+   print(ad["halos", "particle_mass"])
+
 .. _loading-openpmd-data:
 
 openPMD Data

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 doc/source/reference/api/api.rst
--- a/doc/source/reference/api/api.rst
+++ b/doc/source/reference/api/api.rst
@@ -10,7 +10,6 @@
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.visualization.plot_window.SlicePlot
    ~yt.visualization.plot_window.AxisAlignedSlicePlot
@@ -24,7 +23,6 @@
 ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.visualization.profile_plotter.ProfilePlot
    ~yt.visualization.profile_plotter.PhasePlot
@@ -34,7 +32,6 @@
 ^^^^^^^^^^^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.visualization.particle_plots.ParticleProjectionPlot
    ~yt.visualization.particle_plots.ParticlePhasePlot
@@ -44,7 +41,6 @@
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.visualization.fixed_resolution.FixedResolutionBuffer
    ~yt.visualization.fixed_resolution.ParticleImageBuffer
@@ -69,7 +65,6 @@
 These will almost never need to be instantiated on their own.
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.data_objects.data_containers.YTDataContainer
    ~yt.data_objects.data_containers.YTSelectionContainer
@@ -85,7 +80,6 @@
 geometric.
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.data_objects.selection_data_containers.YTPoint
    ~yt.data_objects.selection_data_containers.YTOrthoRay
@@ -108,7 +102,6 @@
 expensive set of intermediate data.
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.data_objects.construction_data_containers.YTStreamline
    ~yt.data_objects.construction_data_containers.YTQuadTreeProj
@@ -124,7 +117,6 @@
 datasets.
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.data_objects.time_series.DatasetSeries
    ~yt.data_objects.time_series.DatasetSeriesObject
@@ -138,7 +130,6 @@
 These objects generate an "index" into multiresolution data.
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.geometry.geometry_handler.Index
    ~yt.geometry.grid_geometry_handler.GridIndex
@@ -152,7 +143,6 @@
 These classes and functions enable yt's symbolic unit handling system.
 
 .. autosummary::
-   :toctree: generated/
 
    yt.data_objects.static_output.Dataset.arr
    yt.data_objects.static_output.Dataset.quan
@@ -173,13 +163,11 @@
 ---------
 
 .. autosummary::
-   :toctree: generated/
 
 ARTIO
 ^^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.frontends.artio.data_structures.ARTIOIndex
    ~yt.frontends.artio.data_structures.ARTIOOctreeSubset
@@ -194,7 +182,6 @@
 ^^^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.frontends.athena.data_structures.AthenaGrid
    ~yt.frontends.athena.data_structures.AthenaHierarchy
@@ -206,7 +193,6 @@
 ^^^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.frontends.boxlib.data_structures.BoxlibGrid
    ~yt.frontends.boxlib.data_structures.BoxlibHierarchy
@@ -225,7 +211,6 @@
 ^^^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.frontends.chombo.data_structures.ChomboGrid
    ~yt.frontends.chombo.data_structures.ChomboHierarchy
@@ -239,7 +224,6 @@
 ^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.frontends.enzo.answer_testing_support.ShockTubeTest
    ~yt.frontends.enzo.data_structures.EnzoGrid
@@ -264,7 +248,6 @@
 ^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.frontends.fits.data_structures.FITSGrid
    ~yt.frontends.fits.data_structures.FITSHierarchy
@@ -276,7 +259,6 @@
 ^^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.frontends.flash.data_structures.FLASHGrid
    ~yt.frontends.flash.data_structures.FLASHHierarchy
@@ -288,7 +270,6 @@
 ^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.frontends.gdf.data_structures.GDFGrid
    ~yt.frontends.gdf.data_structures.GDFHierarchy
@@ -299,7 +280,6 @@
 ^^^^^^^^^^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.frontends.halo_catalog.data_structures.HaloCatalogHDF5File
    ~yt.frontends.halo_catalog.data_structures.HaloCatalogDataset
@@ -319,7 +299,6 @@
 ^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.frontends.moab.data_structures.MoabHex8Hierarchy
    ~yt.frontends.moab.data_structures.MoabHex8Mesh
@@ -334,7 +313,6 @@
 ^^^^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.frontends.open_pmd.data_structures.OpenPMDGrid
    ~yt.frontends.open_pmd.data_structures.OpenPMDHierarchy
@@ -349,7 +327,6 @@
 ^^^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.frontends.ramses.data_structures.RAMSESDomainFile
    ~yt.frontends.ramses.data_structures.RAMSESDomainSubset
@@ -362,7 +339,6 @@
 ^^^^^^^^^^^^^^^^^^^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.frontends.gadget.data_structures.GadgetBinaryFile
    ~yt.frontends.gadget.data_structures.GadgetHDF5Dataset
@@ -384,7 +360,6 @@
 ^^^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.frontends.stream.data_structures.StreamDictFieldHandler
    ~yt.frontends.stream.data_structures.StreamGrid
@@ -410,7 +385,6 @@
 ^^^^^^
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.frontends.ytdata.data_structures.YTDataContainerDataset
    ~yt.frontends.ytdata.data_structures.YTSpatialPlotDataset
@@ -434,7 +408,6 @@
 ------------
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.convenience.load
    ~yt.convenience.simulation
@@ -457,7 +430,6 @@
 
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.data_objects.profiles.ProfileND
    ~yt.data_objects.profiles.Profile1D
@@ -475,7 +447,6 @@
 of topologically disconnected structures, i.e., clump finding.
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.analysis_modules.level_sets.clump_handling.Clump
    ~yt.analysis_modules.level_sets.clump_handling.Clump.add_info_item
@@ -495,7 +466,6 @@
 on cosmological halos.  It is also the primary interface for halo finding.
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.analysis_modules.halo_analysis.halo_catalog.HaloCatalog
    ~yt.analysis_modules.halo_analysis.halo_finding_methods.HaloFindingMethod
@@ -526,7 +496,6 @@
 to use the ``HaloCatalog``.
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.analysis_modules.halo_finding.halo_objects.FOFHaloFinder
    ~yt.analysis_modules.halo_finding.halo_objects.HOPHaloFinder
@@ -541,7 +510,6 @@
 
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.analysis_modules.two_point_functions.two_point_functions.TwoPointFunctions
    ~yt.analysis_modules.two_point_functions.two_point_functions.FcnSet
@@ -550,7 +518,6 @@
 -----------
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.fields.field_info_container.FieldInfoContainer
    ~yt.fields.derived_field.DerivedField
@@ -564,7 +531,6 @@
 ---------------
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.fields.field_info_container.FieldInfoContainer.add_field
    ~yt.data_objects.static_output.Dataset.add_field
@@ -574,7 +540,6 @@
 ----------------
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.data_objects.particle_filters.add_particle_filter
    ~yt.data_objects.particle_filters.particle_filter
@@ -587,7 +552,6 @@
 writing to bitmaps.
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.data_objects.image_array.ImageArray
 
@@ -601,7 +565,6 @@
 
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.analysis_modules.star_analysis.sfr_spectrum.StarFormationRate
    ~yt.analysis_modules.star_analysis.sfr_spectrum.SpectrumBuilder
@@ -611,7 +574,6 @@
 
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.analysis_modules.cosmological_observation.light_cone.light_cone.LightCone
    ~yt.analysis_modules.cosmological_observation.light_ray.light_ray.LightRay
@@ -619,7 +581,6 @@
 Absorption and X-ray spectra and spectral lines:
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.analysis_modules.absorption_spectrum.absorption_spectrum.AbsorptionSpectrum
    ~yt.fields.xray_emission_fields.XrayEmissivityIntegrator
@@ -628,14 +589,12 @@
 Absorption spectra fitting:
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.analysis_modules.absorption_spectrum.absorption_spectrum_fit.generate_total_fit
 
 Sunrise exporting:
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.analysis_modules.sunrise_export.sunrise_exporter.export_to_sunrise
    ~yt.analysis_modules.sunrise_export.sunrise_exporter.export_to_sunrise_from_halolist
@@ -643,7 +602,6 @@
 RADMC-3D exporting:
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.analysis_modules.radmc3d_export.RadMC3DInterface.RadMC3DLayer
    ~yt.analysis_modules.radmc3d_export.RadMC3DInterface.RadMC3DWriter
@@ -657,7 +615,6 @@
 Scene infrastructure:
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.visualization.volume_rendering.volume_rendering.volume_render
    ~yt.visualization.volume_rendering.volume_rendering.create_scene
@@ -669,7 +626,6 @@
 The different kinds of sources:
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.visualization.volume_rendering.render_source.RenderSource
    ~yt.visualization.volume_rendering.render_source.VolumeSource
@@ -683,7 +639,6 @@
 The different kinds of transfer functions:
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.visualization.volume_rendering.transfer_functions.TransferFunction
    ~yt.visualization.volume_rendering.transfer_functions.ColorTransferFunction
@@ -695,7 +650,6 @@
 The different kinds of lenses:
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.visualization.volume_rendering.lens.Lens
    ~yt.visualization.volume_rendering.lens.PlaneParallelLens
@@ -712,7 +666,6 @@
 
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.visualization.streamlines.Streamlines
 
@@ -725,7 +678,6 @@
 
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.visualization.image_writer.multi_image_composite
    ~yt.visualization.image_writer.write_bitmap
@@ -740,7 +692,6 @@
 particularly with complicated layouts.
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.visualization.eps_writer.DualEPS
    ~yt.visualization.eps_writer.single_plot
@@ -757,7 +708,6 @@
 
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.data_objects.derived_quantities.DerivedQuantity
    ~yt.data_objects.derived_quantities.DerivedQuantityCollection
@@ -783,7 +733,6 @@
 See also :ref:`callbacks`.
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.visualization.plot_window.PWViewerMPL.annotate_clear
    ~yt.visualization.plot_modifications.ArrowCallback
@@ -817,7 +766,6 @@
 See also :ref:`colormaps`.
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.visualization.color_maps.add_cmap
    ~yt.visualization.color_maps.make_colormap
@@ -828,7 +776,6 @@
 
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.convenience.load
    ~yt.frontends.ytdata.utilities.save_as_dataset
@@ -864,7 +811,6 @@
 
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.utilities.math_utils.periodic_position
    ~yt.utilities.math_utils.periodic_dist
@@ -899,7 +845,6 @@
 
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.config.YTConfigParser
    ~yt.utilities.parameter_file_storage.ParameterFileStore
@@ -913,7 +858,6 @@
 --------------------
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.utilities.cosmology.Cosmology
    ~yt.utilities.cosmology.Cosmology.hubble_distance
@@ -937,7 +881,6 @@
 The first set of functions are all provided by NumPy.
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.testing.assert_array_equal
    ~yt.testing.assert_almost_equal
@@ -953,7 +896,6 @@
 These are yt-provided functions:
 
 .. autosummary::
-   :toctree: generated/
 
    ~yt.testing.assert_rel_equal
    ~yt.testing.amrspace

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 doc/source/visualizing/callbacks.rst
--- a/doc/source/visualizing/callbacks.rst
+++ b/doc/source/visualizing/callbacks.rst
@@ -433,7 +433,7 @@
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 .. function:: annotate_particles(self, width, p_size=1.0, col='k', marker='o',\
-                                 stride=1.0, ptype=None, minimum_mass=None, \
+                                 stride=1, ptype='all', minimum_mass=None, \
                                  alpha=1.0)
 
    (This is a proxy for

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 scripts/pr_backport.py
--- a/scripts/pr_backport.py
+++ b/scripts/pr_backport.py
@@ -40,19 +40,19 @@
     """
     with hglib.open(repo_path) as client:
         tags = client.log("reverse(tag())")
-        tags = sorted([LooseVersion(t[2]) for t in tags])
+        tags = [t[2].decode('utf8') for t in tags]
+        tags = sorted([t for t in tags if t[:2] == 'yt'])
         for t in tags[::-1]:
-            if t.version[0:2] != ['yt', '-']:
-                continue
-            if len(t.version) == 4 or t.version[4] == 0:
+            ver = LooseVersion(t)
+            if len(ver.version) == 4 or ver.version[4] == 0:
                 last_major_tag = t
                 break
         last_before_release = client.log(
             "last(ancestors(%s) and branch(yt))" % str(last_major_tag))
+        rev = last_before_release[0][1].decode()
         first_after_release = client.log(
-            "first(descendants(%s) and branch(yt) and not %s)"
-            % (last_before_release[0][1], last_before_release[0][1]))
-    return str(first_after_release[0][1][:12])
+            "first(descendants(%s) and branch(yt) and not %s)" % (rev, rev))
+    return first_after_release[0][1][:12].decode('utf8')
 
 
 def get_branch_tip(repo_path, branch, exclude=None):
@@ -65,7 +65,7 @@
                 revset += "and not %s" % exclude
             except hglib.error.CommandError:
                 pass
-        change = client.log(revset)[0][1][:12]
+        change = client.log(revset)[0][1][:12].decode('utf8')
     return change
 
 
@@ -153,7 +153,7 @@
     """
     for pr in prs[::-1]:
         if pr['merge_commit'] is not None:
-            if pr['merge_commit']['hash'] == needle[1][:12]:
+            if pr['merge_commit']['hash'] == needle[1][:12].decode('utf8'):
                 return pr
     return None
 
@@ -167,8 +167,8 @@
     my_prs = list(prs)
     commit_data = cache_commit_data(my_prs)
     for commit in lineage:
-        cset_hash = commit[1]
-        message = commit[5]
+        cset_hash = commit[1].decode('utf8')
+        message = commit[5].decode('utf8')
         if message.startswith('Merged in') and '(pull request #' in message:
             pr = find_merge_commit_in_prs(commit, my_prs)
             if pr is None:
@@ -186,7 +186,7 @@
 def invert_commits_to_prs_mapping(commits_to_prs):
     """invert the mapping from individual commits to pull requests"""
     inv_map = {}
-    for k, v in commits_to_prs.iteritems():
+    for k, v in commits_to_prs.items():
         # can't save v itself in inv_map since it's an unhashable dictionary
         if v is not None:
             created_date = v['created_on'].split('.')[0]
@@ -210,14 +210,18 @@
 def screen_already_backported(repo_path, inv_map):
     with hglib.open(repo_path) as client:
         tags = client.log("reverse(tag())")
-        major_tags = [t for t in tags if t[2].endswith('.0')]
-        most_recent_major_tag_name = major_tags[0][2]
+        tags = [t[2].decode('utf8') for t in tags]
+        tags = [LooseVersion(t) for t in tags if t.startswith('yt')]
+        major_tags = [
+            t for t in tags if len(t.version) == 4 or t.version[-1] == 0]
+        most_recent_major_tag_name = major_tags[0].vstring
         lineage = client.log(
             "descendants(%s) and branch(stable)" % most_recent_major_tag_name)
         prs_to_screen = []
         for pr in inv_map:
             for commit in lineage:
-                if commit[5].startswith('Backporting PR #%s' % pr[0]):
+                desc = commit[5].decode('utf8')
+                if desc.startswith('Backporting PR #%s' % pr[0]):
                     prs_to_screen.append(pr)
         for pr in prs_to_screen:
             del inv_map[pr]
@@ -227,6 +231,7 @@
     with hglib.open(repo_path) as client:
         commit_info = client.log(commit)[0]
         most_recent_tag_name = client.log("reverse(tag())")[0][2]
+        most_recent_tag_name = most_recent_tag_name.decode('utf8')
         lineage = client.log(
             "descendants(%s) and branch(stable)" % most_recent_tag_name)
         # if there is a stable commit with the same commit message,
@@ -263,7 +268,7 @@
             revset = '"%s"' % revset
             message = "Backporting PR #%s %s" % \
                 (pr['id'], pr['links']['html']['href'])
-            dest = get_last_descendant(repo_path, last_stable)
+            dest = get_last_descendant(repo_path, last_stable).decode('utf8')
             message = \
                 "hg rebase -r %s --keep --collapse -m \"%s\" -d %s\n" % \
                 (revset, message, dest)

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/analysis_modules/halo_analysis/halo_catalog.py
--- a/yt/analysis_modules/halo_analysis/halo_catalog.py
+++ b/yt/analysis_modules/halo_analysis/halo_catalog.py
@@ -72,11 +72,11 @@
     --------
 
     >>> # create profiles or overdensity vs. radius for each halo and save to disk
-    >>> from yt.mods import *
+    >>> import yt
     >>> from yt.analysis_modules.halo_analysis.api import *
-    >>> data_ds = load("DD0064/DD0064")
-    >>> halos_ds = load("rockstar_halos/halos_64.0.bin",
-    ...                 output_dir="halo_catalogs/catalog_0064")
+    >>> data_ds = yt.load("DD0064/DD0064")
+    >>> halos_ds = yt.load("rockstar_halos/halos_64.0.bin",
+    ...                    output_dir="halo_catalogs/catalog_0064")
     >>> hc = HaloCatalog(data_ds=data_ds, halos_ds=halos_ds)
     >>> # filter out halos with mass < 1e13 Msun
     >>> hc.add_filter("quantity_value", "particle_mass", ">", 1e13, "Msun")
@@ -91,7 +91,7 @@
     >>> hc.create()
 
     >>> # load in the saved halo catalog and all the profile data
-    >>> halos_ds = load("halo_catalogs/catalog_0064/catalog_0064.0.h5")
+    >>> halos_ds = yt.load("halo_catalogs/catalog_0064/catalog_0064.0.h5")
     >>> hc = HaloCatalog(halos_ds=halos_ds,
                          output_dir="halo_catalogs/catalog_0064")
     >>> hc.add_callback("load_profiles", output_dir="profiles")

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/analysis_modules/halo_finding/halo_objects.py
--- a/yt/analysis_modules/halo_finding/halo_objects.py
+++ b/yt/analysis_modules/halo_finding/halo_objects.py
@@ -708,12 +708,12 @@
         -------
         tuple : (cm, mag_A, mag_B, mag_C, e0_vector, tilt)
             The 6-tuple has in order:
-              #. The center of mass as an array.
-              #. mag_A as a float.
-              #. mag_B as a float.
-              #. mag_C as a float.
-              #. e0_vector as an array.
-              #. tilt as a float.
+            #. The center of mass as an array.
+            #. mag_A as a float.
+            #. mag_B as a float.
+            #. mag_C as a float.
+            #. e0_vector as an array.
+            #. tilt as a float.
 
         Examples
         --------

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/analysis_modules/halo_finding/hop/EnzoHop.c
--- a/yt/analysis_modules/halo_finding/hop/EnzoHop.c
+++ b/yt/analysis_modules/halo_finding/hop/EnzoHop.c
@@ -27,6 +27,9 @@
     #define Py_TYPE(ob) (((PyObject*)(ob))->ob_type)
 #endif
 
+void PrepareKD(KD kd);
+int kdMedianJst(KD kd, int d, int l, int u);
+void kdUpPass(KD kd, int iCell);
 void initgrouplist(Grouplist *g);
 void hop_main(KD kd, HC *my_comm, float densthres);
 void regroup_main(float dens_outer, HC *my_comm);

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/analysis_modules/halo_finding/rockstar/rockstar_groupies.pyx
--- a/yt/analysis_modules/halo_finding/rockstar/rockstar_groupies.pyx
+++ b/yt/analysis_modules/halo_finding/rockstar/rockstar_groupies.pyx
@@ -2,14 +2,11 @@
 import os, sys
 cimport numpy as np
 cimport cython
+from cython cimport floating
 #from cpython.mem cimport PyMem_Malloc
 from libc.stdlib cimport malloc, free
 import sys
 
-ctypedef fused anyfloat:
-    np.float32_t
-    np.float64_t
-
 # Importing relevant rockstar data types particle, fof halo, halo
 
 cdef import from "particle.h":
@@ -366,8 +363,8 @@
     @cython.wraparound(False)
     def make_rockstar_fof(self, np.ndarray[np.int64_t, ndim=1] pind,
                                 np.ndarray[np.int64_t, ndim=1] fof_tags,
-                                np.ndarray[anyfloat, ndim=2] pos,
-                                np.ndarray[anyfloat, ndim=2] vel):
+                                np.ndarray[floating, ndim=2] pos,
+                                np.ndarray[floating, ndim=2] vel):
 
         verbose = False
         # Define fof object

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/analysis_modules/halo_mass_function/halo_mass_function.py
--- a/yt/analysis_modules/halo_mass_function/halo_mass_function.py
+++ b/yt/analysis_modules/halo_mass_function/halo_mass_function.py
@@ -327,22 +327,22 @@
 
     def sigmaM(self):
         """
-         Written by BWO, 2006 (updated 25 January 2007).
-         Converted to Python by Stephen Skory December 2009.
+        Written by BWO, 2006 (updated 25 January 2007).
+        Converted to Python by Stephen Skory December 2009.
 
-         This routine takes in cosmological parameters and creates a file (array) with
-         sigma(M) in it, which is necessary for various press-schechter type
-         stuff.  In principle one can calculate it ahead of time, but it's far,
-         far faster in the long run to calculate your sigma(M) ahead of time.
+        This routine takes in cosmological parameters and creates a file (array) with
+        sigma(M) in it, which is necessary for various press-schechter type
+        stuff.  In principle one can calculate it ahead of time, but it's far,
+        far faster in the long run to calculate your sigma(M) ahead of time.
         
-         Inputs: cosmology, user must set parameters
+        Inputs: cosmology, user must set parameters
         
-         Outputs: four columns of data containing the following information:
+        Outputs: four columns of data containing the following information:
 
-         1) mass (Msolar/h)
-         2) sigma (normalized) using Msun/h as the input
-         
-         The arrays output are used later.
+        1) mass (Msolar/h)
+        2) sigma (normalized) using Msun/h as the input
+        
+        The arrays output are used later.
         """
         
         # Set up the transfer function object.
@@ -446,13 +446,12 @@
 
     def sigma_squared_of_R(self, R):
         """
-        /* calculates sigma^2(R).  This is the routine where the magic happens (or
-           whatever it is that we do here).  Integrates the sigma_squared_integrand
-           parameter from R to infinity.  Calls GSL (gnu scientific library) to do
-           the actual integration.  
+        calculates sigma^2(R).  This is the routine where the magic happens (or
+        whatever it is that we do here).  Integrates the sigma_squared_integrand
+        parameter from R to infinity.  Calls GSL (gnu scientific library) to do
+        the actual integration.  
         
-           Note that R is in h^-1 Mpc (comoving)
-        */
+        Note that R is in h^-1 Mpc (comoving)
         """
         self.R = R
         result = integrate_inf(self.sigma_squared_integrand)
@@ -463,7 +462,7 @@
 
     def sigma_squared_integrand(self, k):
         """
-        /* integrand for integral to get sigma^2(R). */
+        integrand for integral to get sigma^2(R).
         """
 
         Rcom = self.R;  # this is R in comoving Mpc/h
@@ -474,7 +473,7 @@
 
     def PofK(self, k):
         """
-        /* returns power spectrum as a function of wavenumber k */
+        returns power spectrum as a function of wavenumber k
         """
 
         thisPofK = np.power(k, self.primordial_index) * np.power( self.TofK(k), 2.0);
@@ -483,7 +482,7 @@
 
     def TofK(self, k):
         """
-        /* returns transfer function as a function of wavenumber k. */
+        returns transfer function as a function of wavenumber k.
         """
         
         thisTofK = self.TF.TFmdm_onek_hmpc(k);
@@ -503,9 +502,9 @@
 
     def multiplicityfunction(self, sigma):
         """
-        /* Multiplicity function - this is where the various fitting functions/analytic 
+        Multiplicity function - this is where the various fitting functions/analytic 
         theories are different.  The various places where I found these fitting functions
-        are listed below.  */
+        are listed below.
         """
         
         nu = self.delta_c0 / sigma;
@@ -552,7 +551,7 @@
 
     def sigmaof_M_z(self, sigmabin, redshift):
         """
-        /* sigma(M, z) */
+        sigma(M, z)
         """
         
         thissigma = self.Dofz(redshift) * self.sigmaarray[sigmabin];
@@ -561,7 +560,7 @@
 
     def Dofz(self, redshift):
         """
-        /* Growth function */
+        Growth function
         """
 
         thisDofz = self.gofz(redshift) / self.gofz(0.0) / (1.0+redshift);
@@ -571,7 +570,7 @@
 
     def gofz(self, redshift):
         """
-        /* g(z) - I don't think this has any other name*/
+        g(z) - I don't think this has any other name
         """
 
         thisgofz = 2.5 * self.omega_matter_of_z(redshift) / \
@@ -585,7 +584,7 @@
 
     def omega_matter_of_z(self,redshift):
         """
-        /* Omega matter as a function of redshift */
+        Omega matter as a function of redshift
         """
         
         thisomofz = self.omega_matter0 * math.pow( 1.0+redshift, 3.0) / \
@@ -595,7 +594,7 @@
 
     def omega_lambda_of_z(self,redshift):
         """
-        /* Omega lambda as a function of redshift */
+        Omega lambda as a function of redshift
         """
 
         thisolofz = self.omega_lambda0 / math.pow( self.Eofz(redshift), 2.0 )
@@ -604,7 +603,7 @@
 
     def Eofz(self, redshift):
         """
-        /* E(z) - I don't think this has any other name */
+        E(z) - I don't think this has any other name
         """
         thiseofz = math.sqrt( self.omega_lambda0 \
             + (1.0 - self.omega_lambda0 - self.omega_matter0)*math.pow( 1.0+redshift, 2.0) \
@@ -614,15 +613,15 @@
 
 
 """ 
-/* Fitting Formulae for CDM + Baryon + Massive Neutrino (MDM) cosmologies. */
-/* Daniel J. Eisenstein & Wayne Hu, Institute for Advanced Study */
+Fitting Formulae for CDM + Baryon + Massive Neutrino (MDM) cosmologies.
+Daniel J. Eisenstein & Wayne Hu, Institute for Advanced Study
 
-/* There are two primary routines here, one to set the cosmology, the
+There are two primary routines here, one to set the cosmology, the
 other to construct the transfer function for a single wavenumber k. 
 You should call the former once (per cosmology) and the latter as 
-many times as you want. */
+many times as you want. 
 
-/* TFmdm_set_cosm() -- User passes all the cosmological parameters as
+   TFmdm_set_cosm() -- User passes all the cosmological parameters as
    arguments; the routine sets up all of the scalar quantites needed 
    computation of the fitting formula.  The input parameters are: 
    1) omega_matter -- Density of CDM, baryons, and massive neutrinos,
@@ -634,7 +633,7 @@
    6) hubble       -- Hubble constant, in units of 100 km/s/Mpc 
    7) redshift     -- The redshift at which to evaluate */
 
-/* TFmdm_onek_mpc() -- User passes a single wavenumber, in units of Mpc^-1.
+   TFmdm_onek_mpc() -- User passes a single wavenumber, in units of Mpc^-1.
    Routine returns the transfer function from the Eisenstein & Hu
    fitting formula, based on the cosmology currently held in the 
    internal variables.  The routine returns T_cb (the CDM+Baryon
@@ -642,29 +641,40 @@
    Baryon+Neutrino density-weighted transfer function) is stored
    in the global variable tf_cbnu. */
 
-/* We also supply TFmdm_onek_hmpc(), which is identical to the previous
-   routine, but takes the wavenumber in units of h Mpc^-1. */
+We also supply TFmdm_onek_hmpc(), which is identical to the previous
+routine, but takes the wavenumber in units of h Mpc^-1.
 
-/* We hold the internal scalar quantities in global variables, so that
-the user may access them in an external program, via "extern" declarations. */
+We hold the internal scalar quantities in global variables, so that
+the user may access them in an external program, via "extern" declarations.
 
-/* Please note that all internal length scales are in Mpc, not h^-1 Mpc! */
+Please note that all internal length scales are in Mpc, not h^-1 Mpc!
 """
 
 class TransferFunction(object):
     """
-    /* This routine takes cosmological parameters and a redshift and sets up
-    all the internal scalar quantities needed to compute the transfer function. */
-    /* INPUT: omega_matter -- Density of CDM, baryons, and massive neutrinos,
-                    in units of the critical density. */
-    /* 	  omega_baryon -- Density of baryons, in units of critical. */
-    /* 	  omega_hdm    -- Density of massive neutrinos, in units of critical */
-    /* 	  degen_hdm    -- (Int) Number of degenerate massive neutrino species */
-    /*        omega_lambda -- Cosmological constant */
-    /* 	  hubble       -- Hubble constant, in units of 100 km/s/Mpc */
-    /*        redshift     -- The redshift at which to evaluate */
-    /* OUTPUT: Returns 0 if all is well, 1 if a warning was issued.  Otherwise,
-        sets many global variables for use in TFmdm_onek_mpc() */
+    This routine takes cosmological parameters and a redshift and sets up
+    all the internal scalar quantities needed to compute the transfer function.
+
+    Parameters
+    ----------
+    omega_matter : float
+        Density of CDM, baryons, and massive neutrinos, in units 
+        of the critical density.
+    omega_baryon : float
+        Density of baryons, in units of critical.
+    omega_hdm : float
+        Density of massive neutrinos, in units of critical
+    degen_hdm : integer
+        Number of degenerate massive neutrino species
+    omega_lambda : float
+        Cosmological constant
+    hubble : float
+        Hubble constant, in units of 100 km/s/Mpc
+    redshift : float
+        The redshift at which to evaluate
+
+    Returns 0 if all is well, 1 if a warning was issued. Otherwise,
+    sets many global variables for use in TFmdm_onek_mpc()
     """
     def __init__(self, omega_matter, omega_baryon, omega_hdm,
                  degen_hdm, omega_lambda, hubble, redshift):
@@ -751,15 +761,23 @@
 
     def TFmdm_onek_mpc(self,  kk):
         """
-        /* Given a wavenumber in Mpc^-1, return the transfer function for the
-        cosmology held in the global variables. */
-        /* Input: kk -- Wavenumber in Mpc^-1 */
-        /* Output: The following are set as global variables:
-            growth_cb -- the transfer function for density-weighted
-                    CDM + Baryon perturbations. 
-            growth_cbnu -- the transfer function for density-weighted
-                    CDM + Baryon + Massive Neutrino perturbations. */
-        /* The function returns growth_cb */
+        Given a wavenumber in Mpc^-1, return the transfer function for the
+        cosmology held in the global variables.
+
+        Parameters
+        ----------
+        kk : float
+            Wavenumber in Mpc^-1
+
+        Returns
+        -------
+        growth_cb : float
+            the transfer function for density-weighted
+            CDM + Baryon perturbations. (returned and set as a global var)
+        growth_cbnu : float
+            the transfer function for density-weighted
+            CDM + Baryon + Massive Neutrino perturbations.
+            (set as a global var)
         """
     
         self.qq = kk/self.omhh*SQR(self.theta_cmb);
@@ -794,15 +812,22 @@
 
     def TFmdm_onek_hmpc(self, kk):
         """
-        /* Given a wavenumber in h Mpc^-1, return the transfer function for the
-        cosmology held in the global variables. */
-        /* Input: kk -- Wavenumber in h Mpc^-1 */
-        /* Output: The following are set as global variables:
-            growth_cb -- the transfer function for density-weighted
-                    CDM + Baryon perturbations. 
-            growth_cbnu -- the transfer function for density-weighted
-                    CDM + Baryon + Massive Neutrino perturbations. */
-        /* The function returns growth_cb */
+        Given a wavenumber in h Mpc^-1, return the transfer function for the
+        cosmology held in the global variables.
+
+        Parameters
+        ----------
+        kk : float
+            Wavenumber in h Mpc^-1
+
+        Returns
+        -------
+        growth_cb : float
+            the transfer function for density-weighted
+            CDM + Baryon perturbations. (return and set as a global var) 
+        growth_cbnu : float
+            the transfer function for density-weighted
+            CDM + Baryon + Massive Neutrino perturbations.
         """
         return self.TFmdm_onek_mpc(kk*self.hhubble);
 

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/analysis_modules/sunyaev_zeldovich/projection.py
--- a/yt/analysis_modules/sunyaev_zeldovich/projection.py
+++ b/yt/analysis_modules/sunyaev_zeldovich/projection.py
@@ -78,7 +78,7 @@
 
     Parameters
     ----------
-    ds : Dataset
+    ds : ~yt.data_objects.static_output.Dataset 
         The dataset
     freqs : array_like
         The frequencies (in GHz) at which to compute the SZ spectral distortion.

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/data_objects/data_containers.py
--- a/yt/data_objects/data_containers.py
+++ b/yt/data_objects/data_containers.py
@@ -1957,7 +1957,7 @@
     multiple data objects.
 
     This object is not designed to be created directly; it is designed to be
-    created implicitly by using one of the bitwise operations (&, |, ^, ~) on
+    created implicitly by using one of the bitwise operations (&, \|, ^, \~) on
     one or two other data objects.  These correspond to the appropriate boolean
     operations, and the resultant object can be nested.
 

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/data_objects/static_output.py
--- a/yt/data_objects/static_output.py
+++ b/yt/data_objects/static_output.py
@@ -194,6 +194,8 @@
         self.data = weakref.WeakKeyDictionary()
 
     def __get__(self, instance, owner):
+        if not instance:
+            return None
         ret = self.data.get(instance, None)
         try:
             ret = ret.copy()
@@ -1056,7 +1058,7 @@
         Parameters
         ----------
 
-        input_array : iterable
+        input_array : Iterable
             A tuple, list, or array to attach units to
         input_units : String unit specification, unit symbol or astropy object
             The units of the array. Powers must be specified using python syntax

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/fields/species_fields.py
--- a/yt/fields/species_fields.py
+++ b/yt/fields/species_fields.py
@@ -126,8 +126,8 @@
     """
     This takes a field registry, a fluid type, and two species names.  
     The first species name is one you wish to alias to an existing species
-    name.  For instance you might alias all "H_p0" fields to "H_" fields
-    to indicate that "H_" fields are really just neutral hydrogen fields.
+    name.  For instance you might alias all "H_p0" fields to "H\_" fields
+    to indicate that "H\_" fields are really just neutral hydrogen fields.
     This function registers field aliases for the density, number_density,
     mass, and fraction fields between the two species given in the arguments.
     """

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/fields/xray_emission_fields.py
--- a/yt/fields/xray_emission_fields.py
+++ b/yt/fields/xray_emission_fields.py
@@ -72,7 +72,7 @@
 
     Parameters
     ----------
-    table_type: string
+    table_type : string
         The type of data to use when computing the emissivity values. If "cloudy",
         a file called "cloudy_emissivity.h5" is used, for photoionized
         plasmas. If, "apec", a file called "apec_emissivity.h5" is used for 

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/frontends/boxlib/data_structures.py
--- a/yt/frontends/boxlib/data_structures.py
+++ b/yt/frontends/boxlib/data_structures.py
@@ -405,8 +405,8 @@
     periodicity = (True, True, True)
 
     def __init__(self, output_dir,
-                 cparam_filename="inputs",
-                 fparam_filename="probin",
+                 cparam_filename=None,
+                 fparam_filename=None,
                  dataset_type='boxlib_native',
                  storage_filename=None,
                  units_override=None,
@@ -459,6 +459,8 @@
             return False
         args = inspect.getcallargs(cls.__init__, args, kwargs)
         # This might need to be localized somehow
+        if args['cparam_filename'] is None:
+            return True  # Treat as generic boxlib data
         inputs_filename = os.path.join(
             os.path.dirname(os.path.abspath(output_dir)),
             args['cparam_filename'])
@@ -820,7 +822,7 @@
         if any(("castro." in line for line in lines)): return False
         if any(("nyx." in line for line in lines)): return False
         if any(("maestro" in line.lower() for line in lines)): return False
-        if any(("geometry.prob_lo" in line for line in lines)): return True
+        if any(("hyp." in line for line in lines)): return True
         return False
 
 
@@ -848,6 +850,22 @@
     _index_class = CastroHierarchy
     _field_info_class = CastroFieldInfo
 
+    def __init__(self, output_dir,
+                 cparam_filename='inputs',
+                 fparam_filename='probin',
+                 dataset_type='boxlib_native',
+                 storage_filename=None,
+                 units_override=None,
+                 unit_system="cgs"):
+
+        super(CastroDataset, self).__init__(output_dir,
+                                            cparam_filename,
+                                            fparam_filename,
+                                            dataset_type,
+                                            storage_filename,
+                                            units_override,
+                                            unit_system)
+
     @classmethod
     def _is_valid(cls, *args, **kwargs):
         # fill our args
@@ -916,6 +934,22 @@
 
     _field_info_class = MaestroFieldInfo
 
+    def __init__(self, output_dir,
+                 cparam_filename='inputs',
+                 fparam_filename='probin',
+                 dataset_type='boxlib_native',
+                 storage_filename=None,
+                 units_override=None,
+                 unit_system="cgs"):
+        
+        super(MaestroDataset, self).__init__(output_dir,
+                                             cparam_filename,
+                                             fparam_filename,
+                                             dataset_type,
+                                             storage_filename,
+                                             units_override,
+                                             unit_system)
+
     @classmethod
     def _is_valid(cls, *args, **kwargs):
         # fill our args
@@ -993,6 +1027,22 @@
     _index_class = NyxHierarchy
     _field_info_class = NyxFieldInfo
 
+    def __init__(self, output_dir,
+                 cparam_filename='inputs',
+                 fparam_filename='probin',
+                 dataset_type='boxlib_native',
+                 storage_filename=None,
+                 units_override=None,
+                 unit_system="cgs"):
+
+        super(NyxDataset, self).__init__(output_dir,
+                                         cparam_filename,
+                                         fparam_filename,
+                                         dataset_type,
+                                         storage_filename,
+                                         units_override,
+                                         unit_system)
+
     @classmethod
     def _is_valid(cls, *args, **kwargs):
         # fill our args

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/frontends/boxlib/io.py
--- a/yt/frontends/boxlib/io.py
+++ b/yt/frontends/boxlib/io.py
@@ -126,7 +126,11 @@
                 rdata = np.fromfile(f, pheader.real_type, pheader.num_real * npart)
                 x = np.asarray(rdata[0::pheader.num_real], dtype=np.float64)
                 y = np.asarray(rdata[1::pheader.num_real], dtype=np.float64)
-                z = np.asarray(rdata[2::pheader.num_real], dtype=np.float64)
+                if (grid.ds.dimensionality == 2):
+                    z = np.ones_like(y)
+                    z *= 0.5*(grid.LeftEdge[2] + grid.RightEdge[2])
+                else:
+                    z = np.asarray(rdata[2::pheader.num_real], dtype=np.float64)
                 mask = selector.select_points(x, y, z, 0.0)
 
                 if mask is None:
@@ -150,7 +154,11 @@
                 rdata = np.fromfile(f, pheader.real_type, pheader.num_real * npart)
                 x = np.asarray(rdata[0::pheader.num_real], dtype=np.float64)
                 y = np.asarray(rdata[1::pheader.num_real], dtype=np.float64)
-                z = np.asarray(rdata[2::pheader.num_real], dtype=np.float64)
+                if (grid.ds.dimensionality == 2):
+                    z = np.ones_like(y)
+                    z *= 0.5*(grid.LeftEdge[2] + grid.RightEdge[2])
+                else:
+                    z = np.asarray(rdata[2::pheader.num_real], dtype=np.float64)
                 mask = selector.select_points(x, y, z, 0.0)
 
                 if mask is None:

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/frontends/chombo/io.py
--- a/yt/frontends/chombo/io.py
+++ b/yt/frontends/chombo/io.py
@@ -199,11 +199,9 @@
 
 def parse_orion_sinks(fn):
     '''
-
     Orion sink particles are stored in text files. This function
     is for figuring what particle fields are present based on the
-    number of entries per line in the *.sink file.
-
+    number of entries per line in the \*.sink file.
     '''
 
     # Figure out the format of the particle file

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/frontends/exodus_ii/data_structures.py
--- a/yt/frontends/exodus_ii/data_structures.py
+++ b/yt/frontends/exodus_ii/data_structures.py
@@ -180,18 +180,18 @@
 
     def _parse_parameter_file(self):
         self._handle = NetCDF4FileHandler(self.parameter_filename)
-        self._vars = self._handle.dataset.variables
-        self._read_glo_var()
-        self.dimensionality = self._vars['coor_names'].shape[0]
-        self.parameters['info_records'] = self._load_info_records()
-        self.unique_identifier = self._get_unique_identifier()
-        self.num_steps = len(self._vars['time_whole'])
-        self.current_time = self._get_current_time()
-        self.parameters['num_meshes'] = self._vars['eb_status'].shape[0]
-        self.parameters['elem_names'] = self._get_elem_names()
-        self.parameters['nod_names'] = self._get_nod_names()
-        self.domain_left_edge, self.domain_right_edge = self._load_domain_edge()
-        self.periodicity = (False, False, False)
+        with self._handle.open_ds() as ds:
+            self._read_glo_var()
+            self.dimensionality = ds.variables['coor_names'].shape[0]
+            self.parameters['info_records'] = self._load_info_records()
+            self.unique_identifier = self._get_unique_identifier()
+            self.num_steps = len(ds.variables['time_whole'])
+            self.current_time = self._get_current_time()
+            self.parameters['num_meshes'] = ds.variables['eb_status'].shape[0]
+            self.parameters['elem_names'] = self._get_elem_names()
+            self.parameters['nod_names'] = self._get_nod_names()
+            self.domain_left_edge, self.domain_right_edge = self._load_domain_edge()
+            self.periodicity = (False, False, False)
 
         # These attributes don't really make sense for unstructured
         # mesh data, but yt warns if they are not present, so we set
@@ -205,18 +205,18 @@
         self.refine_by = 0
 
     def _get_fluid_types(self):
-        handle = NetCDF4FileHandler(self.parameter_filename).dataset
-        fluid_types = ()
-        i = 1
-        while True:
-            ftype = 'connect%d' % i
-            if ftype in handle.variables:
-                fluid_types += (ftype,)
-                i += 1
-            else:
-                break
-        fluid_types += ('all',)
-        return fluid_types
+        with NetCDF4FileHandler(self.parameter_filename).open_ds() as ds:
+            fluid_types = ()
+            i = 1
+            while True:
+                ftype = 'connect%d' % i
+                if ftype in ds.variables:
+                    fluid_types += (ftype,)
+                    i += 1
+                else:
+                    break
+            fluid_types += ('all',)
+            return fluid_types
 
     def _read_glo_var(self):
         """
@@ -226,31 +226,34 @@
         names = self._get_glo_names()
         if not names:
             return
-        values = self._vars['vals_glo_var'][:].transpose()
-        for name, value in zip(names, values):
-            self.parameters[name] = value
+        with self._handle.open_ds() as ds:
+            values = ds.variables['vals_glo_var'][:].transpose()
+            for name, value in zip(names, values):
+                self.parameters[name] = value
 
     def _load_info_records(self):
         """
         Returns parsed version of the info_records.
         """
-        try:
-            return load_info_records(self._vars['info_records'])
-        except (KeyError, TypeError):
-            mylog.warning("No info_records found")
-            return []
+        with self._handle.open_ds() as ds:
+            try:
+                return load_info_records(ds.variables['info_records'])
+            except (KeyError, TypeError):
+                mylog.warning("No info_records found")
+                return []
 
     def _get_unique_identifier(self):
         return self.parameter_filename
 
     def _get_current_time(self):
-        try:
-            return self._vars['time_whole'][self.step]
-        except IndexError:
-            raise RuntimeError("Invalid step number, max is %d" \
-                               % (self.num_steps - 1))
-        except (KeyError, TypeError):
-            return 0.0
+        with self._handle.open_ds() as ds:
+            try:
+                return ds.variables['time_whole'][self.step]
+            except IndexError:
+                raise RuntimeError("Invalid step number, max is %d" \
+                                   % (self.num_steps - 1))
+            except (KeyError, TypeError):
+                return 0.0
 
     def _get_glo_names(self):
         """
@@ -259,12 +262,13 @@
 
         """
 
-        if "name_glo_var" not in self._vars:
-            mylog.warning("name_glo_var not found")
-            return []
-        else:
-            return [sanitize_string(v.tostring()) for v in
-                    self._vars["name_glo_var"]]
+        with self._handle.open_ds() as ds:
+            if "name_glo_var" not in ds.variables:
+                mylog.warning("name_glo_var not found")
+                return []
+            else:
+                return [sanitize_string(v.tostring()) for v in
+                        ds.variables["name_glo_var"]]
 
     def _get_elem_names(self):
         """
@@ -273,12 +277,13 @@
 
         """
 
-        if "name_elem_var" not in self._vars:
-            mylog.warning("name_elem_var not found")
-            return []
-        else:
-            return [sanitize_string(v.tostring()) for v in
-                    self._vars["name_elem_var"]]
+        with self._handle.open_ds() as ds:
+            if "name_elem_var" not in ds.variables:
+                mylog.warning("name_elem_var not found")
+                return []
+            else:
+                return [sanitize_string(v.tostring()) for v in
+                        ds.variables["name_elem_var"]]
 
     def _get_nod_names(self):
         """
@@ -287,12 +292,13 @@
 
         """
 
-        if "name_nod_var" not in self._vars:
-            mylog.warning("name_nod_var not found")
-            return []
-        else:
-            return [sanitize_string(v.tostring()) for v in
-                    self._vars["name_nod_var"]]
+        with self._handle.open_ds() as ds:
+            if "name_nod_var" not in ds.variables:
+                mylog.warning("name_nod_var not found")
+                return []
+            else:
+                return [sanitize_string(v.tostring()) for v in
+                        ds.variables["name_nod_var"]]
 
     def _read_coordinates(self):
         """
@@ -304,13 +310,14 @@
         coord_axes = 'xyz'[:self.dimensionality]
 
         mylog.info("Loading coordinates")
-        if "coord" not in self._vars:
-            coords = np.array([self._vars["coord%s" % ax][:]
-                               for ax in coord_axes]).transpose().copy()
-        else:
-            coords = np.array([coord for coord in
-                               self._vars["coord"][:]]).transpose().copy()
-        return coords
+        with self._handle.open_ds() as ds:
+            if "coord" not in ds.variables:
+                coords = np.array([ds.variables["coord%s" % ax][:]
+                                   for ax in coord_axes]).transpose().copy()
+            else:
+                coords = np.array([coord for coord in
+                                   ds.variables["coord"][:]]).transpose().copy()
+            return coords
 
     def _apply_displacement(self, coords, mesh_id):
 
@@ -324,13 +331,14 @@
         offset = self.displacements[mesh_name][1]
 
         coord_axes = 'xyz'[:self.dimensionality]
-        for i, ax in enumerate(coord_axes):
-            if "disp_%s" % ax in self.parameters['nod_names']:
-                ind = self.parameters['nod_names'].index("disp_%s" % ax)
-                disp = self._vars['vals_nod_var%d' % (ind + 1)][self.step]
-                new_coords[:, i] = coords[:, i] + fac*disp + offset[i]
+        with self._handle.open_ds() as ds:
+            for i, ax in enumerate(coord_axes):
+                if "disp_%s" % ax in self.parameters['nod_names']:
+                    ind = self.parameters['nod_names'].index("disp_%s" % ax)
+                    disp = ds.variables['vals_nod_var%d' % (ind + 1)][self.step]
+                    new_coords[:, i] = coords[:, i] + fac*disp + offset[i]
 
-        return new_coords
+            return new_coords
 
     def _read_connectivity(self):
         """
@@ -338,9 +346,10 @@
         """
         mylog.info("Loading connectivity")
         connectivity = []
-        for i in range(self.parameters['num_meshes']):
-            connectivity.append(self._vars["connect%d" % (i+1)][:].astype("i8"))
-        return connectivity
+        with self._handle.open_ds() as ds:
+            for i in range(self.parameters['num_meshes']):
+                connectivity.append(ds.variables["connect%d" % (i+1)][:].astype("i8"))
+            return connectivity
 
     def _load_domain_edge(self):
         """
@@ -373,7 +382,7 @@
         for i in range(self.dimensionality, 3):
             mi[i] = 0.0
             ma[i] = 1.0
-        
+
         return mi, ma
 
     @classmethod

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/frontends/exodus_ii/io.py
--- a/yt/frontends/exodus_ii/io.py
+++ b/yt/frontends/exodus_ii/io.py
@@ -28,7 +28,7 @@
     def __init__(self, ds):
         self.filename = ds.index_filename
         exodus_ii_handler = NetCDF4FileHandler(self.filename)
-        self.handler = exodus_ii_handler.dataset
+        self.handler = exodus_ii_handler
         super(IOHandlerExodusII, self).__init__(ds)
         self.node_fields = ds._get_nod_names()
         self.elem_fields = ds._get_elem_names()
@@ -46,46 +46,47 @@
         # dict gets returned at the end and it should be flat, with selected
         # data.  Note that if you're reading grid data, you might need to
         # special-case a grid selector object.
-        chunks = list(chunks)
-        rv = {}
-        for field in fields:
-            ftype, fname = field
-            if ftype == "all":
-                ci = np.concatenate([mesh.connectivity_indices - self._INDEX_OFFSET \
-                                     for mesh in self.ds.index.mesh_union])
-            else:
-                ci = self.handler.variables[ftype][:] - self._INDEX_OFFSET
-            num_elem = ci.shape[0]
-            if fname in self.node_fields:
-                nodes_per_element = ci.shape[1]
-                rv[field] = np.zeros((num_elem, nodes_per_element), dtype="float64")
-            elif fname in self.elem_fields:
-                rv[field] = np.zeros(num_elem, dtype="float64")
-        for field in fields:
-            ind = 0
-            ftype, fname = field
-            if ftype == "all":
-                mesh_ids = [mesh.mesh_id + 1 for mesh in self.ds.index.mesh_union]
-                objs = [mesh for mesh in self.ds.index.mesh_union]
-            else:
-                mesh_ids = [int(ftype[-1])]
-                chunk = chunks[mesh_ids[0] - 1]
-                objs = chunk.objs
-            if fname in self.node_fields:
-                field_ind = self.node_fields.index(fname)
-                fdata = self.handler.variables['vals_nod_var%d' % (field_ind + 1)]
-                for g in objs:
-                    ci = g.connectivity_indices - self._INDEX_OFFSET
-                    data = fdata[self.ds.step][ci]
-                    ind += g.select(selector, data, rv[field], ind)  # caches
-            if fname in self.elem_fields:
-                field_ind = self.elem_fields.index(fname)
-                for g, mesh_id in zip(objs, mesh_ids):
-                    fdata = self.handler.variables['vals_elem_var%deb%s' %
-                                                   (field_ind + 1, mesh_id)][:]
-                    data = fdata[self.ds.step, :]
-                    ind += g.select(selector, data, rv[field], ind)  # caches
-        return rv
+        with self.handler.open_ds() as ds:
+            chunks = list(chunks)
+            rv = {}
+            for field in fields:
+                ftype, fname = field
+                if ftype == "all":
+                    ci = np.concatenate([mesh.connectivity_indices - self._INDEX_OFFSET \
+                                         for mesh in self.ds.index.mesh_union])
+                else:
+                    ci = ds.variables[ftype][:] - self._INDEX_OFFSET
+                num_elem = ci.shape[0]
+                if fname in self.node_fields:
+                    nodes_per_element = ci.shape[1]
+                    rv[field] = np.zeros((num_elem, nodes_per_element), dtype="float64")
+                elif fname in self.elem_fields:
+                    rv[field] = np.zeros(num_elem, dtype="float64")
+            for field in fields:
+                ind = 0
+                ftype, fname = field
+                if ftype == "all":
+                    mesh_ids = [mesh.mesh_id + 1 for mesh in self.ds.index.mesh_union]
+                    objs = [mesh for mesh in self.ds.index.mesh_union]
+                else:
+                    mesh_ids = [int(ftype[-1])]
+                    chunk = chunks[mesh_ids[0] - 1]
+                    objs = chunk.objs
+                if fname in self.node_fields:
+                    field_ind = self.node_fields.index(fname)
+                    fdata = ds.variables['vals_nod_var%d' % (field_ind + 1)]
+                    for g in objs:
+                        ci = g.connectivity_indices - self._INDEX_OFFSET
+                        data = fdata[self.ds.step][ci]
+                        ind += g.select(selector, data, rv[field], ind)  # caches
+                if fname in self.elem_fields:
+                    field_ind = self.elem_fields.index(fname)
+                    for g, mesh_id in zip(objs, mesh_ids):
+                        fdata = ds.variables['vals_elem_var%deb%s' %
+                                                       (field_ind + 1, mesh_id)][:]
+                        data = fdata[self.ds.step, :]
+                        ind += g.select(selector, data, rv[field], ind)  # caches
+            return rv
 
     def _read_chunk_data(self, chunk, fields):
         # This reads the data from a single chunk, and is only used for

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/frontends/exodus_ii/simulation_handling.py
--- a/yt/frontends/exodus_ii/simulation_handling.py
+++ b/yt/frontends/exodus_ii/simulation_handling.py
@@ -14,7 +14,6 @@
 from yt.data_objects.time_series import \
     DatasetSeries, \
     RegisteredSimulationTimeSeries
-from yt.frontends.exodus_ii.api import ExodusIIDataset
 
 
 @add_metaclass(RegisteredSimulationTimeSeries)
@@ -45,6 +44,25 @@
         self.all_outputs = self._check_for_outputs(potential_outputs)
         self.all_outputs.sort(key=lambda obj: obj["filename"])
 
+    def __iter__(self):
+        for o in self._pre_outputs:
+            fn, step = o
+            ds = load(fn, step=step)
+            self._setup_function(ds)
+            yield ds
+
+    def __getitem__(self, key):
+        if isinstance(key, slice):
+            if isinstance(key.start, float):
+                return self.get_range(key.start, key.stop)
+            # This will return a sliced up object!
+            return DatasetSeries(self._pre_outputs[key], self.parallel)
+        o = self._pre_outputs[key]
+        fn, step = o
+        o = load(fn, step=step)
+        self._setup_function(o)
+        return o
+
     def get_time_series(self, parallel=False, setup_function=None):
         r"""
         Instantiate a DatasetSeries object for a set of outputs.
@@ -55,15 +73,14 @@
         Fine-level filtering is currently not implemented.
         
         """
-        
+
         all_outputs = self.all_outputs
         ds_list = []
         for output in all_outputs:
             num_steps = output['num_steps']
             fn = output['filename']
             for step in range(num_steps):
-                ds = ExodusIIDataset(fn, step=step)
-                ds_list.append(ds)
+                ds_list.append((fn, step))
         super(ExodusIISimulation, self).__init__(ds_list, 
                                                  parallel=parallel, 
                                                  setup_function=setup_function)

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/frontends/fits/misc.py
--- a/yt/frontends/fits/misc.py
+++ b/yt/frontends/fits/misc.py
@@ -49,7 +49,7 @@
 
     Parameters
     ----------
-    ds : Dataset
+    ds : `~yt.data_objects.static_output.Dataset`
         The FITS events file dataset to add the counts fields to.
     ebounds : list of tuples
         A list of tuples, one for each field, with (emin, emax) as the

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/frontends/gadget/fields.py
--- a/yt/frontends/gadget/fields.py
+++ b/yt/frontends/gadget/fields.py
@@ -83,8 +83,11 @@
                 # Assume cosmic abundances
                 x_H = 0.76
                 gamma = 5.0/3.0
-                # Assume zero ionization
-                mu = 4.0 / (3.0 * x_H + 1.0)
+                if data.has_field_parameter("mean_molecular_weight"):
+                    mu = data.get_field_parameter("mean_molecular_weight")
+                else:
+                    # Assume zero ionization
+                    mu = 4.0 / (3.0 * x_H + 1.0)
                 ret = data[ptype, "InternalEnergy"]*(gamma-1)*mu*mp/kb
                 return ret.in_units(self.ds.unit_system["temperature"])
 

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/frontends/gamer/data_structures.py
--- a/yt/frontends/gamer/data_structures.py
+++ b/yt/frontends/gamer/data_structures.py
@@ -126,12 +126,12 @@
         son_list = self._handle["Tree/Son"].value
 
         for gid in range(self.num_grids):
-            grid     = self.grids.flat[gid]
+            grid     = self.grids[gid]
             son_gid0 = son_list[gid]
 
             # set up the parent-children relationship
             if son_gid0 >= 0:
-                grid.Children = [ self.grids.flat[son_gid0+s] for s in range(8) ]
+                grid.Children = [ self.grids[son_gid0+s] for s in range(8) ]
 
             for son_grid in grid.Children: son_grid.Parent = grid
 

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/frontends/sdf/data_structures.py
--- a/yt/frontends/sdf/data_structures.py
+++ b/yt/frontends/sdf/data_structures.py
@@ -78,7 +78,7 @@
                  unit_system="cgs"):
         if bounding_box is not None:
             self._subspace = True
-            bbox = np.array(bounding_box, dtype="float32")
+            bbox = np.array(bounding_box, dtype="float64")
             if bbox.shape == (2, 3):
                 bbox = bbox.transpose()
             self.domain_left_edge = bbox[:,0]
@@ -121,8 +121,7 @@
         except:
             self.unique_identifier = time.time()
 
-
-        if None in (self.domain_left_edge, self.domain_right_edge):
+        if self.domain_left_edge is None or self.domain_right_edge is None:
             R0 = self.parameters['R0']
             if 'offset_center' in self.parameters and self.parameters['offset_center']:
                 self.domain_left_edge = np.array([0, 0, 0], dtype=np.float64)

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/frontends/stream/data_structures.py
--- a/yt/frontends/stream/data_structures.py
+++ b/yt/frontends/stream/data_structures.py
@@ -903,7 +903,7 @@
 
     Parameters
     ----------
-    base_ds : Dataset
+    base_ds : `~yt.data_objects.static_output.Dataset`
         This is any static output.  It can also be a stream static output, for
         instance as returned by load_uniform_data.
     refinement_critera : list of :class:`~yt.utilities.flagging_methods.FlaggingMethod`
@@ -1167,7 +1167,7 @@
     r"""Define the cell coordinates and cell neighbors of a hexahedral mesh
     for a semistructured grid. Used to specify the connectivity and
     coordinates parameters used in
-    :function:`~yt.frontends.stream.data_structures.load_hexahedral_mesh`.
+    :func:`~yt.frontends.stream.data_structures.load_hexahedral_mesh`.
 
     Parameters
     ----------

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/frontends/ytdata/data_structures.py
--- a/yt/frontends/ytdata/data_structures.py
+++ b/yt/frontends/ytdata/data_structures.py
@@ -275,7 +275,7 @@
             # since this is now particle-like data.
             data_type = self.parameters.get("data_type")
             container_type = self.parameters.get("container_type")
-            ex_container_type = ["cutting", "proj", "ray", "slice"]
+            ex_container_type = ["cutting", "proj", "ray", "slice", "cut_region"]
             if data_type == "yt_light_ray" or container_type in ex_container_type:
                 mylog.info("Returning an all_data data container.")
                 return self.all_data()

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/frontends/ytdata/tests/test_outputs.py
--- a/yt/frontends/ytdata/tests/test_outputs.py
+++ b/yt/frontends/ytdata/tests/test_outputs.py
@@ -100,6 +100,12 @@
     assert isinstance(sphere_ds, YTDataContainerDataset)
     yield YTDataFieldTest(full_fn, ("grid", "density"))
     yield YTDataFieldTest(full_fn, ("all", "particle_mass"))
+    cr = ds.cut_region(sphere, ['obj["temperature"] > 1e4'])
+    fn = cr.save_as_dataset(fields=["temperature"])
+    full_fn = os.path.join(tmpdir, fn)
+    cr_ds = load(full_fn)
+    assert isinstance(cr_ds, YTDataContainerDataset)
+    assert (cr["temperature"] == cr_ds.data["temperature"]).all()
     os.chdir(curdir)
     shutil.rmtree(tmpdir)
 

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/frontends/ytdata/utilities.py
--- a/yt/frontends/ytdata/utilities.py
+++ b/yt/frontends/ytdata/utilities.py
@@ -239,4 +239,9 @@
         val = np.array(val)
         if val.dtype.kind == 'U':
             val = val.astype('|S')
-    fh.attrs[str(attr)] = val
+    try:
+        fh.attrs[str(attr)] = val
+    # This is raised if no HDF5 equivalent exists.
+    # In that case, save its string representation.
+    except TypeError:
+        fh.attrs[str(attr)] = str(val)

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/funcs.py
--- a/yt/funcs.py
+++ b/yt/funcs.py
@@ -400,7 +400,8 @@
     maxval = max(maxval, 1)
     from yt.config import ytcfg
     if ytcfg.getboolean("yt", "suppressStreamLogging") or \
-       ytcfg.getboolean("yt", "__withintesting"):
+       ytcfg.getboolean("yt", "__withintesting") or \
+       maxval == 1: \
         return DummyProgressBar()
     elif ytcfg.getboolean("yt", "__parallel"):
         # If parallel is True, update progress on root only.
@@ -746,6 +747,7 @@
 
     With a name provided by the user, this will decide how to 
     appropriately name the output file by the following rules:
+
     1. if name is None, the filename will be the keyword plus 
        the suffix.
     2. if name ends with "/", assume name is a directory and 

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/geometry/oct_container.pyx
--- a/yt/geometry/oct_container.pyx
+++ b/yt/geometry/oct_container.pyx
@@ -744,7 +744,7 @@
         self.visit_all_octs(selector, visitor)
         assert ((visitor.global_index+1)*visitor.nz == visitor.index)
 
-cdef int root_node_compare(void *a, void *b) nogil:
+cdef int root_node_compare(const void *a, const void *b) nogil:
     cdef OctKey *ao
     cdef OctKey *bo
     ao = <OctKey *>a

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/geometry/particle_oct_container.pyx
--- a/yt/geometry/particle_oct_container.pyx
+++ b/yt/geometry/particle_oct_container.pyx
@@ -23,6 +23,7 @@
 import numpy as np
 from selection_routines cimport SelectorObject
 cimport cython
+from cython cimport floating
 
 cdef class ParticleOctreeContainer(OctreeContainer):
     cdef Oct** oct_list
@@ -261,10 +262,6 @@
                         self.visit(o.children[cind(i,j,k)], counts, level + 1)
         return
 
-ctypedef fused anyfloat:
-    np.float32_t
-    np.float64_t
-
 cdef np.uint64_t ONEBIT=1
 
 cdef class ParticleRegions:
@@ -299,7 +296,7 @@
     @cython.boundscheck(False)
     @cython.wraparound(False)
     @cython.cdivision(True)
-    cdef void _mask_positions(self, np.ndarray[anyfloat, ndim=2] pos,
+    cdef void _mask_positions(self, np.ndarray[floating, ndim=2] pos,
                               np.uint64_t file_id, int filter):
         # TODO: Replace with the bitarray
         cdef np.int64_t no = pos.shape[0]

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/geometry/selection_routines.pxd
--- a/yt/geometry/selection_routines.pxd
+++ b/yt/geometry/selection_routines.pxd
@@ -19,10 +19,6 @@
 from grid_visitors cimport GridTreeNode, GridVisitorData, \
     grid_visitor_function, check_child_masked
 
-ctypedef fused anyfloat:
-    np.float32_t
-    np.float64_t
-
 cdef inline _ensure_code(arr):
     if hasattr(arr, "units"):
         if "code_length" == str(arr.units):

diff -r 153dfc60f6e2dbd910865b3d5034df037fd8da65 -r 83eae35d29c676e9380ccf1429a2c85542333387 yt/geometry/selection_routines.pyx
--- a/yt/geometry/selection_routines.pyx
+++ b/yt/geometry/selection_routines.pyx
@@ -17,6 +17,7 @@
 import numpy as np
 cimport numpy as np
 cimport cython
+from cython cimport floating
 from libc.stdlib cimport malloc, free
 from yt.utilities.lib.fp_utils cimport fclip, iclip, fmax, fmin, imin, imax
 from .oct_container cimport OctreeContainer, Oct
@@ -96,7 +97,7 @@
 cdef _mask_fill(np.ndarray[np.float64_t, ndim=1] out,
                 np.int64_t offset,
                 np.ndarray[np.uint8_t, ndim=3, cast=True] mask,
-                np.ndarray[anyfloat, ndim=3] vals):
+                np.ndarray[floating, ndim=3] vals):
     cdef np.int64_t count = 0
     cdef int i, j, k
     for i in range(mask.shape[0]):
@@ -550,9 +551,9 @@
     @cython.boundscheck(False)
     @cython.wraparound(False)
     @cython.cdivision(True)
-    def count_points(self, np.ndarray[anyfloat, ndim=1] x,
-                           np.ndarray[anyfloat, ndim=1] y,
-                           np.ndarray[anyfloat, ndim=1] z,
+    def count_points(self, np.ndarray[floating, ndim=1] x,
+                           np.ndarray[floating, ndim=1] y,
+                           np.ndarray[floating, ndim=1] z,
                            np.float64_t radius):
         cdef int count = 0
         cdef int i
@@ -578,9 +579,9 @@
     @cython.boundscheck(False)
     @cython.wraparound(False)
     @cython.cdivision(True)
-    def select_points(self, np.ndarray[anyfloat, ndim=1] x,
-                            np.ndarray[anyfloat, ndim=1] y,
-                            np.ndarray[anyfloat, ndim=1] z,
+    def select_points(self, np.ndarray[floating, ndim=1] x,
+                            np.ndarray[floating, ndim=1] y,
+                            np.ndarray[floating, ndim=1] z,
                             np.float64_t radius):
         cdef int count = 0
         cdef int i

This diff is so big that we needed to truncate the remainder.

https://bitbucket.org/yt_analysis/yt/commits/add68b583bb1/
Changeset:   add68b583bb1
Branch:      yt
User:        al007
Date:        2017-03-17 23:17:43+00:00
Summary:     Demonstrate quad9 support with line plot.
Affected #:  1 file

diff -r 83eae35d29c676e9380ccf1429a2c85542333387 -r add68b583bb1f40f49f724237fe658ee0769a978 yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -745,6 +745,8 @@
         sampler = P1Sampler1D()
     elif ndim == 2 and nvertices == 4:
         sampler = Q1Sampler2D()
+    elif ndim == 2 and nvertices == 9:
+        sampler = Q2Sampler2D()
     elif ndim == 2 and nvertices == 6:
         sampler = T2Sampler2D()
     elif ndim == 3 and nvertices == 10:


https://bitbucket.org/yt_analysis/yt/commits/8ee92ca7654f/
Changeset:   8ee92ca7654f
Branch:      yt
User:        al007
Date:        2017-04-04 16:59:15+00:00
Summary:     Merge in yt-tip.
Affected #:  38 files

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a doc/Makefile
--- a/doc/Makefile
+++ b/doc/Makefile
@@ -33,23 +33,24 @@
 	@echo "  linkcheck   to check all external links for integrity"
 	@echo "  doctest     to run all doctests embedded in the documentation (if enabled)"
 	@echo "  clean 	     to remove the build directory"
-	@echo "  fullclean   to remove the build directory and autogenerated api docs"
 	@echo "  recipeclean to remove files produced by running the cookbook scripts"
 
 clean:
 	-rm -rf $(BUILDDIR)/*
+	-rm -rf source/reference/api/yt.*
+	-rm -rf source/reference/api/modules.rst
 
-fullclean:
-	-rm -rf $(BUILDDIR)/*
-	-rm -rf source/reference/api/generated
+fullclean: clean
 
 recipeclean:
 	-rm -rf _temp/*.done source/cookbook/_static/*
 
 html:
-	sphinx-apidoc -o source/reference/api/ -e ../yt ../yt/extern/* \
-		$(shell find ../yt -name "*tests*" -type d) ../yt/utilities/voropp*
-	sed -e '/show-inheritance/ a\ \ \ \ :inherited-members:' -i source/reference/api/yt*.rst
+ifneq ($(READTHEDOCS),True)
+	SPHINX_APIDOC_OPTIONS=members,undoc-members,inherited-members,show-inheritance sphinx-apidoc \
+        -o source/reference/api/ \
+        -e ../yt ../yt/extern/* $(shell find ../yt -name "*tests*" -type d) ../yt/utilities/voropp*
+endif
 	$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
 	@echo
 	@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a doc/source/analyzing/analysis_modules/star_analysis.rst
--- a/doc/source/analyzing/analysis_modules/star_analysis.rst
+++ b/doc/source/analyzing/analysis_modules/star_analysis.rst
@@ -209,8 +209,8 @@
 There are two ways to write out the data once the spectrum has been calculated.
 The command ``write_out`` outputs two columns of data:
 
-  1. Wavelength :math:`(\text{\AA})`
-  2. Flux (Luminosity per unit wavelength :math:`(\mathrm{\rm{L}_\odot} / \text{\AA})` , where
+  1. Wavelength (:math:`\text{Angstroms}`)
+  2. Flux (Luminosity per unit wavelength :math:`(\mathrm{\rm{L}_\odot} / \text{Angstrom})` , where
        :math:`\mathrm{\rm{L}_\odot} = 3.826 \cdot 10^{33}\, \mathrm{ergs / s}` ).
 
 and can be called simply, specifying the output file:
@@ -225,7 +225,7 @@
 distribution to. The default is 5200 Angstroms. This command outputs the data
 in two columns:
 
-  1. Wavelength :math:`(\text{\AA})`
+  1. Wavelength :math:`(\text{Angstroms})`
   2. Relative flux normalized to the flux at *flux_norm*.
 
 .. code-block:: python

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a doc/source/conf.py
--- a/doc/source/conf.py
+++ b/doc/source/conf.py
@@ -11,9 +11,9 @@
 # All configuration values have a default; values that are commented out
 # serve to show the default.
 
-import sys, os, glob, re
-from sphinx.search import WordCollector
-from docutils.nodes import comment, title, Text, SkipNode
+import sys
+import os
+import glob
 
 on_rtd = os.environ.get("READTHEDOCS", None) == "True"
 
@@ -30,7 +30,7 @@
 # Add any Sphinx extension module names here, as strings. They can be extensions
 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
 extensions = ['sphinx.ext.autodoc', 'sphinx.ext.intersphinx',
-              'sphinx.ext.pngmath', 'sphinx.ext.viewcode',
+              'sphinx.ext.mathjax', 'sphinx.ext.viewcode',
               'sphinx.ext.napoleon', 'yt_cookbook', 'yt_colormaps',
               'config_help']
 
@@ -228,13 +228,6 @@
 # If true, show URL addresses after external links.
 #latex_show_urls = False
 
-# Additional stuff for the LaTeX preamble.
-latex_preamble = r"""
-\renewcommand{\AA}{\text{\r{A}}} % Allow \AA in math mode
-\usepackage[utf8]{inputenc}      % Allow unicode symbols in text
-\DeclareUnicodeCharacter {212B} {\AA}                  % Angstrom
-"""
-
 # Documents to append as an appendix to all manuals.
 #latex_appendices = []
 

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a doc/source/help/index.rst
--- a/doc/source/help/index.rst
+++ b/doc/source/help/index.rst
@@ -72,8 +72,8 @@
 
   $ yt --help
 
-If you continue to see errors, you should try contacting us via IRC or email
-but you may have to reinstall yt (see :ref:`getting-and-installing-yt`).
+If you continue to see errors, you should try contacting us via Slack, IRC or
+email but you may have to reinstall yt (see :ref:`getting-and-installing-yt`).
 
 .. _search-the-documentation:
 
@@ -170,17 +170,24 @@
 
 .. _irc:
 
-Go on IRC to ask a question
----------------------------
+Go on Slack or IRC to ask a question
+------------------------------------
+
+If you want a fast, interactive experience, you could try jumping into our Slack
+or IRC channels to get your questions answered in a chatroom style environment.
 
-If you want a fast, interactive experience, you could try jumping into our IRC
-channel to get your questions answered in a chatroom style environment.  You
-don't even need to have any special IRC client in order to join.  We are the
-#yt channel on irc.freenode.net, but you can also connect using your web
-browser by going to http://yt-project.org/irc.html .  There are usually 2-8
-members of the user base and development team online, so you'll probably get
-your answers quickly.  Remember to bring the information from the
-:ref:`last step <isolate_and_document>`.
+To join our slack channel you will need to request an invite by going to
+http://yt-project.org/development.html, click the "Join as @ Slack!" button, and
+fill out the form. You will get an invite as soon as an administrator approves
+your request.
+
+Alternatively you can go to our IRC channel, which does not require an
+invite. You don't even need to have any special IRC client in order to join the
+IRC channel.  We are the #yt channel on irc.freenode.net, but you can also
+connect using your web browser by going to http://yt-project.org/irc.html .
+There are usually 2-8 members of the user base and development team online, so
+you'll probably get your answers quickly.  Remember to bring the information
+from the :ref:`last step <isolate_and_document>`.
 
 .. _mailing-list:
 

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a doc/source/installing.rst
--- a/doc/source/installing.rst
+++ b/doc/source/installing.rst
@@ -270,7 +270,7 @@
 
 .. code-block:: bash
 
-  $ conda install yt
+  $ conda install -c conda-forge yt
 
 which will install stable branch of yt along with all of its dependencies.
 
@@ -284,7 +284,7 @@
 
 .. code-block:: bash
 
-  $ conda install -c http://use.yt/with_conda/ yt
+  $ conda install -c http://use.yt/with_conda/ -c conda-forge yt
 
 New packages for development branch are built after every pull request is
 merged. In order to make sure you are running latest version, it's recommended
@@ -292,7 +292,12 @@
 
 .. code-block:: bash
 
-  $ conda update -c http://use.yt/with_conda/ yt
+  $ conda update -c http://use.yt/with_conda/ -c conda-forge yt
+
+We recommend trying to install dependencies from conda-forge as indicated above
+since focused individual communities stand a better chance of successfully
+maintaining build recipes. However, if you wish to use the default anaconda
+packages, simply remove ``-c conda-forge`` during conda installation.
 
 Location of our channel can be added to ``.condarc`` to avoid retyping it during
 each *conda* invocation. Please refer to `Conda Manual
@@ -309,7 +314,7 @@
 
 .. code-block:: bash
 
-  $ conda install cython mercurial sympy ipython matplotlib
+  $ conda install -c conda-forge cython mercurial sympy ipython matplotlib netCDF4
 
 In addition, you will need a C compiler installed.
 

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a setup.cfg
--- a/setup.cfg
+++ b/setup.cfg
@@ -15,4 +15,4 @@
 #      vendored libraries
 exclude = doc,benchmarks,*/api.py,*/__init__.py,*/__config__.py,yt/visualization/_mpl_imports.py,yt/utilities/lodgeit.py,yt/utilities/lru_cache.py,yt/utilities/poster/*,yt/extern/*,yt/mods.py,yt/utilities/fits_image.py
 max-line-length=999
-ignore = E111,E121,E122,E123,E124,E125,E126,E127,E128,E129,E131,E201,E202,E211,E221,E222,E227,E228,E241,E301,E203,E225,E226,E231,E251,E261,E262,E265,E266,E302,E303,E402,E502,E701,E703,E731,W291,W292,W293,W391,W503
\ No newline at end of file
+ignore = E111,E121,E122,E123,E124,E125,E126,E127,E128,E129,E131,E201,E202,E211,E221,E222,E227,E228,E241,E301,E203,E225,E226,E231,E251,E261,E262,E265,E266,E302,E303,E305,E306,E402,E502,E701,E703,E731,W291,W292,W293,W391,W503
\ No newline at end of file

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a tests/nose_runner.py
--- a/tests/nose_runner.py
+++ b/tests/nose_runner.py
@@ -28,12 +28,17 @@
             result = next_task()
             self.task_queue.task_done()
             self.result_queue.put(result)
+            if next_task.exclusive:
+                print("%s: Exiting (exclusive)" % proc_name)
+                break
         return
 
 class NoseTask(object):
-    def __init__(self, argv):
+    def __init__(self, job):
+        argv, exclusive = job
         self.argv = argv
         self.name = argv[0]
+        self.exclusive = exclusive
 
     def __call__(self):
         old_stderr = sys.stderr
@@ -69,12 +74,12 @@
                       if DROP_TAG not in line])
     tests = yaml.load(data)
 
-    base_argv = ['--local-dir=%s' % answers_dir,
+    base_argv = ['--local-dir=%s' % answers_dir, '-s', '--nologcapture',
                  '--with-answer-testing', '--answer-big-data', '--local']
     args = []
 
     for test in list(tests["other_tests"].keys()):
-        args.append([test] + tests["other_tests"][test])
+        args.append(([test] + tests["other_tests"][test], True))
     for answer in list(tests["answer_tests"].keys()):
         if tests["answer_tests"][answer] is None:
             continue
@@ -82,10 +87,10 @@
         argv += base_argv
         argv.append('--answer-name=%s' % argv[0])
         argv += tests["answer_tests"][answer]
-        args.append(argv)
+        args.append((argv, False))
 
-    args = [item + ['-s', '--nologcapture', '--xunit-file=%s.xml' % item[0]]
-            for item in args]
+    args = [(item + ['--xunit-file=%s.xml' % item[0]], exclusive)
+            for item, exclusive in args]
     return args
 
 if __name__ == "__main__":
@@ -93,13 +98,15 @@
     tasks = multiprocessing.JoinableQueue()
     results = multiprocessing.Queue()
 
-    num_consumers = 6  # TODO 
+    num_consumers = int(os.environ.get('NUM_WORKERS', 6))
     consumers = [NoseWorker(tasks, results) for i in range(num_consumers)]
     for w in consumers:
         w.start()
 
     num_jobs = 0
     for job in generate_tasks_input():
+        if job[1]:
+            num_consumers -= 1  # take into account exclusive jobs
         tasks.put(NoseTask(job))
         num_jobs += 1
 
@@ -110,5 +117,4 @@
 
     while num_jobs:
         result = results.get()
-        print(result)
         num_jobs -= 1

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a tests/tests.yaml
--- a/tests/tests.yaml
+++ b/tests/tests.yaml
@@ -42,7 +42,7 @@
   local_owls_001:
     - yt/frontends/owls/tests/test_outputs.py
 
-  local_pw_012:
+  local_pw_013:
     - yt/visualization/tests/test_plotwindow.py:test_attributes
     - yt/visualization/tests/test_plotwindow.py:test_attributes_wt
     - yt/visualization/tests/test_profile_plots.py:test_phase_plot_attributes
@@ -63,9 +63,10 @@
     - yt/analysis_modules/photon_simulator/tests/test_spectra.py
     - yt/analysis_modules/photon_simulator/tests/test_sloshing.py
 
-  local_unstructured_002:
+  local_unstructured_005:
     - yt/visualization/volume_rendering/tests/test_mesh_render.py
     - yt/visualization/tests/test_mesh_slices.py:test_tri2
+    - yt/visualization/tests/test_mesh_slices.py:test_quad2
     - yt/visualization/tests/test_mesh_slices.py:test_multi_region
 
   local_boxlib_003:
@@ -77,7 +78,7 @@
     - yt/frontends/boxlib/tests/test_outputs.py:test_RT_particles
     - yt/frontends/boxlib/tests/test_outputs.py:test_units_override
 
-  local_boxlib_particles_002:
+  local_boxlib_particles_003:
     - yt/frontends/boxlib/tests/test_outputs.py:test_LyA
     - yt/frontends/boxlib/tests/test_outputs.py:test_nyx_particle_io
     - yt/frontends/boxlib/tests/test_outputs.py:test_castro_particle_io

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/data_objects/profiles.py
--- a/yt/data_objects/profiles.py
+++ b/yt/data_objects/profiles.py
@@ -708,7 +708,7 @@
                  self.GridDimensions,
                  cell_size)
 
-            locs = storage.values[:, :, fi] > 0.0
+            locs = storage.values[:, :, fi] != 0.0
             storage.used[locs] = True
 
             if self.weight_field is not None:

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/data_objects/static_output.py
--- a/yt/data_objects/static_output.py
+++ b/yt/data_objects/static_output.py
@@ -567,6 +567,7 @@
         self.field_dependencies.update(deps)
         self.fields = FieldTypeContainer(self)
         self.index.field_list = sorted(self.field_list)
+        self._last_freq = (None, None)
 
     def setup_deprecated_fields(self):
         from yt.fields.field_aliases import _field_name_aliases

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/data_objects/tests/test_covering_grid.py
--- a/yt/data_objects/tests/test_covering_grid.py
+++ b/yt/data_objects/tests/test_covering_grid.py
@@ -111,7 +111,18 @@
             obj = ds.arbitrary_grid(LE, RE, dims)
             deposited_mass = obj["deposit", "all_density"].sum() * volume
 
-            yield assert_equal, deposited_mass, ds.quan(1.0, 'g')
+            assert_equal(deposited_mass, ds.quan(1.0, 'g'))
+
+            LE = np.array([0.00, 0.00, 0.00])
+            RE = np.array([0.05, 0.05, 0.05])
+            dims = np.array([ncells, ncells, ncells])
+
+            obj = ds.arbitrary_grid(LE, RE, dims)
+
+            deposited_mass = obj["deposit", "all_density"].sum()
+
+            assert_equal(deposited_mass, 0)
+
 
     # Test that we get identical results to the covering grid for unigrid data.
     # Testing AMR data is much harder.

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/data_objects/tests/test_profiles.py
--- a/yt/data_objects/tests/test_profiles.py
+++ b/yt/data_objects/tests/test_profiles.py
@@ -1,3 +1,4 @@
+import yt
 import numpy as np
 
 from yt.data_objects.profiles import \
@@ -195,3 +196,28 @@
     assert_raises(
         YTIllDefinedProfile, PhasePlot, ad, 'particle_radius', 'particle_mass',
         'particle_ones')
+
+def test_particle_profile_negative_field():
+    # see Issue #1340
+    n_particles = int(1e4)
+
+    ppx, ppy, ppz = np.random.normal(size=[3, n_particles])
+    pvx, pvy, pvz = - np.ones((3, n_particles))
+
+    data = {'particle_position_x': ppx,
+            'particle_position_y': ppy,
+            'particle_position_z': ppz,
+            'particle_velocity_x': pvx,
+            'particle_velocity_y': pvy,
+            'particle_velocity_z': pvz}
+
+    bbox = 1.1*np.array([[min(ppx), max(ppx)], [min(ppy), max(ppy)], [min(ppz), max(ppz)]])
+    ds = yt.load_particles(data, bbox=bbox)
+    ad = ds.all_data()
+
+    profile = yt.create_profile(
+        ad,
+        ["particle_position_x", "particle_position_y"],
+        "particle_velocity_x",
+        weight_field=None)
+    assert profile['particle_velocity_x'].min() < 0

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/fields/tests/test_fields.py
--- a/yt/fields/tests/test_fields.py
+++ b/yt/fields/tests/test_fields.py
@@ -317,3 +317,11 @@
     a1 = np.argsort(mi)
     a2 = np.argsort(mi2)
     assert_array_equal(a1, a2)
+
+
+def test_field_inference():
+    ds = fake_random_ds(16)
+    ds.index
+    # If this is not true this means the result of field inference depends
+    # on the order we did field detection, which is random in Python3
+    assert_equal(ds._last_freq, (None, None))

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/frontends/boxlib/data_structures.py
--- a/yt/frontends/boxlib/data_structures.py
+++ b/yt/frontends/boxlib/data_structures.py
@@ -139,7 +139,193 @@
              startIndex[2]:endIndex[2]] = tofill
 
 
+class BoxLibParticleHeader(object):
+
+    def __init__(self, ds, directory_name, is_checkpoint, 
+                 extra_field_names=None):
+
+        self.particle_type = directory_name
+        header_filename =  ds.output_dir + "/" + directory_name + "/Header"
+        with open(header_filename, "r") as f:
+            self.version_string = f.readline().strip()
+
+            particle_real_type = self.version_string.split('_')[-1]
+            particle_real_type = self.version_string.split('_')[-1]
+            if particle_real_type == 'double':
+                self.real_type = np.float64
+            elif particle_real_type == 'single':
+                self.real_type = np.float32
+            else:
+                raise RuntimeError("yt did not recognize particle real type.")
+            self.int_type = np.int32
+
+            self.dim = int(f.readline().strip())
+            self.num_int_base = 2 + self.dim
+            self.num_real_base = self.dim
+            self.num_int_extra = 0  # this should be written by Boxlib, but isn't
+            self.num_real_extra = int(f.readline().strip())
+            self.num_int = self.num_int_base + self.num_int_extra
+            self.num_real = self.num_real_base + self.num_real_extra            
+            self.num_particles = int(f.readline().strip())
+            self.max_next_id = int(f.readline().strip())
+            self.finest_level = int(f.readline().strip())
+            self.num_levels = self.finest_level + 1
+
+            # Boxlib particles can be written in checkpoint or plotfile mode
+            # The base integer fields are only there for checkpoints, but some
+            # codes use the checkpoint format for plotting
+            if not is_checkpoint:
+                self.num_int_base = 0
+                self.num_int_extra = 0
+                self.num_int = 0
+
+            self.grids_per_level = np.zeros(self.num_levels, dtype='int64')
+            self.data_map = {}
+            for level_num in range(self.num_levels):
+                self.grids_per_level[level_num] = int(f.readline().strip())
+                self.data_map[level_num] = {}
+            
+            pfd = namedtuple("ParticleFileDescriptor",
+                             ["file_number", "num_particles", "offset"])
+
+            for level_num in range(self.num_levels):
+                for grid_num in range(self.grids_per_level[level_num]):
+                    entry = [int(val) for val in f.readline().strip().split()]
+                    self.data_map[level_num][grid_num] = pfd(*entry)
+
+        self._generate_particle_fields(extra_field_names)
+
+    def _generate_particle_fields(self, extra_field_names):
+
+        # these are the 'base' integer fields
+        self.known_int_fields = [(self.particle_type, "particle_id"),
+                                 (self.particle_type, "particle_cpu"),
+                                 (self.particle_type, "particle_cell_x"),
+                                 (self.particle_type, "particle_cell_y"),
+                                 (self.particle_type, "particle_cell_z")]
+        self.known_int_fields = self.known_int_fields[0:self.num_int_base]
+
+        # these are extra integer fields
+        extra_int_fields = ["particle_int_comp%d" % i 
+                            for i in range(self.num_int_extra)]
+        self.known_int_fields.extend([(self.particle_type, field) 
+                                      for field in extra_int_fields])
+
+        # these are the base real fields
+        self.known_real_fields = [(self.particle_type, "particle_position_x"),
+                                  (self.particle_type, "particle_position_y"),
+                                  (self.particle_type, "particle_position_z")]
+        self.known_real_fields = self.known_real_fields[0:self.num_real_base]
+
+        # these are the extras
+        if extra_field_names is not None:
+            assert(len(extra_field_names) == self.num_real_extra)
+        else:
+            extra_field_names = ["particle_real_comp%d" % i 
+                                 for i in range(self.num_real_extra)]
+
+        self.known_real_fields.extend([(self.particle_type, field) 
+                                       for field in extra_field_names])
+
+        self.known_fields = self.known_int_fields + self.known_real_fields
+
+        self.particle_int_dtype = np.dtype([(t[1], self.int_type) 
+                                            for t in self.known_int_fields])
+
+        self.particle_real_dtype = np.dtype([(t[1], self.real_type) 
+                                            for t in self.known_real_fields])
+
+
+class AMReXParticleHeader(object):
+
+    def __init__(self, ds, directory_name, is_checkpoint, 
+                 extra_field_names=None):
+
+        self.particle_type = directory_name
+        header_filename =  ds.output_dir + "/" + directory_name + "/Header"
+        self.real_component_names = []
+        self.int_component_names = []
+        with open(header_filename, "r") as f:
+            self.version_string = f.readline().strip()
+
+            particle_real_type = self.version_string.split('_')[-1]
+            particle_real_type = self.version_string.split('_')[-1]
+            if particle_real_type == 'double':
+                self.real_type = np.float64
+            elif particle_real_type == 'single':
+                self.real_type = np.float32
+            else:
+                raise RuntimeError("yt did not recognize particle real type.")
+            self.int_type = np.int32
+
+            self.dim = int(f.readline().strip())
+            self.num_int_base = 2
+            self.num_real_base = self.dim
+            self.num_real_extra = int(f.readline().strip())
+            for i in range(self.num_real_extra):
+                self.real_component_names.append(f.readline().strip())
+            self.num_int_extra = int(f.readline().strip())
+            for i in range(self.num_int_extra):
+                self.int_component_names.append(f.readline().strip())
+            self.num_int = self.num_int_base + self.num_int_extra
+            self.num_real = self.num_real_base + self.num_real_extra
+            self.is_checkpoint = bool(int(f.readline().strip()))
+            self.num_particles = int(f.readline().strip())
+            self.max_next_id = int(f.readline().strip())
+            self.finest_level = int(f.readline().strip())
+            self.num_levels = self.finest_level + 1
+
+            if not self.is_checkpoint:
+                self.num_int_base = 0
+                self.num_int_extra = 0
+                self.num_int = 0
+
+            self.grids_per_level = np.zeros(self.num_levels, dtype='int64')
+            self.data_map = {}
+            for level_num in range(self.num_levels):
+                self.grids_per_level[level_num] = int(f.readline().strip())
+                self.data_map[level_num] = {}
+            
+            pfd = namedtuple("ParticleFileDescriptor",
+                             ["file_number", "num_particles", "offset"])
+
+            for level_num in range(self.num_levels):
+                for grid_num in range(self.grids_per_level[level_num]):
+                    entry = [int(val) for val in f.readline().strip().split()]
+                    self.data_map[level_num][grid_num] = pfd(*entry)
+
+        self._generate_particle_fields()
+
+    def _generate_particle_fields(self):
+
+        # these are the 'base' integer fields
+        self.known_int_fields = [(self.particle_type, "particle_id"),
+                                 (self.particle_type, "particle_cpu")]
+        self.known_int_fields = self.known_int_fields[0:self.num_int_base]
+
+        self.known_int_fields.extend([(self.particle_type, "particle_" + field) 
+                                       for field in self.int_component_names])
+
+        # these are the base real fields
+        self.known_real_fields = [(self.particle_type, "particle_position_x"),
+                                  (self.particle_type, "particle_position_y"),
+                                  (self.particle_type, "particle_position_z")]
+        self.known_real_fields = self.known_real_fields[0:self.num_real_base]
+
+        self.known_real_fields.extend([(self.particle_type, "particle_" + field) 
+                                       for field in self.real_component_names])
+
+        self.known_fields = self.known_int_fields + self.known_real_fields
+
+        self.particle_int_dtype = np.dtype([(t[1], self.int_type) 
+                                            for t in self.known_int_fields])
+
+        self.particle_real_dtype = np.dtype([(t[1], self.real_type) 
+                                            for t in self.known_real_fields])
+
+
 class BoxlibHierarchy(GridIndex):
+
     grid = BoxlibGrid
 
     def __init__(self, ds, dataset_type='boxlib_native'):
@@ -365,12 +551,21 @@
     def _setup_data_io(self):
         self.io = io_registry[self.dataset_type](self.dataset)
 
-    def _read_particles(self, directory_name, is_checkpoint, extra_field_names=None):
+    def _determine_particle_output_type(self, directory_name):
+        header_filename =  self.ds.output_dir + "/" + directory_name + "/Header"
+        with open(header_filename, "r") as f:
+            version_string = f.readline().strip()
+            if version_string.startswith("Version_Two"):
+                return AMReXParticleHeader
+            else:
+                return BoxLibParticleHeader
 
-        self.particle_headers[directory_name] = BoxLibParticleHeader(self.ds,
-                                                                     directory_name,
-                                                                     is_checkpoint,
-                                                                     extra_field_names)
+    def _read_particles(self, directory_name, is_checkpoint, extra_field_names=None):
+        pheader = self._determine_particle_output_type(directory_name)
+        self.particle_headers[directory_name] = pheader(self.ds,
+                                                        directory_name,
+                                                        is_checkpoint,
+                                                        extra_field_names)
 
         base_particle_fn = self.ds.output_dir + '/' + directory_name + "/Level_%d/DATA_%.4d"
 
@@ -1137,118 +1332,14 @@
     return vals
 
 
-class BoxLibParticleHeader(object):
-
-    def __init__(self, ds, directory_name, is_checkpoint, extra_field_names=None):
-
-        self.species_name = directory_name
-        header_filename =  ds.output_dir + "/" + directory_name + "/Header"
-        with open(header_filename, "r") as f:
-            self.version_string = f.readline().strip()
-
-            particle_real_type = self.version_string.split('_')[-1]
-            particle_real_type = self.version_string.split('_')[-1]
-            if particle_real_type == 'double':
-                self.real_type = np.float64
-            elif particle_real_type == 'single':
-                self.real_type = np.float32
-            else:
-                raise RuntimeError("yt did not recognize particle real type.")
-            self.int_type = np.int32
-
-            self.dim = int(f.readline().strip())
-            self.num_int_base = 2 + self.dim
-            self.num_real_base = self.dim
-            self.num_int_extra = 0  # this should be written by Boxlib, but isn't
-            self.num_real_extra = int(f.readline().strip())
-            self.num_int = self.num_int_base + self.num_int_extra
-            self.num_real = self.num_real_base + self.num_real_extra            
-            self.num_particles = int(f.readline().strip())
-            self.max_next_id = int(f.readline().strip())
-            self.finest_level = int(f.readline().strip())
-            self.num_levels = self.finest_level + 1
-
-            # Boxlib particles can be written in checkpoint or plotfile mode
-            # The base integer fields are only there for checkpoints, but some
-            # codes use the checkpoint format for plotting
-            if not is_checkpoint:
-                self.num_int_base = 0
-                self.num_int_extra = 0
-                self.num_int = 0
-
-            self.grids_per_level = np.zeros(self.num_levels, dtype='int64')
-            self.data_map = {}
-            for level_num in range(self.num_levels):
-                self.grids_per_level[level_num] = int(f.readline().strip())
-                self.data_map[level_num] = {}
-            
-            pfd = namedtuple("ParticleFileDescriptor",
-                             ["file_number", "num_particles", "offset"])
-
-            for level_num in range(self.num_levels):
-                for grid_num in range(self.grids_per_level[level_num]):
-                    entry = [int(val) for val in f.readline().strip().split()]
-                    self.data_map[level_num][grid_num] = pfd(*entry)
-
-        self._generate_particle_fields(extra_field_names)
-
-    def _generate_particle_fields(self, extra_field_names):
-
-        # these are the 'base' integer fields
-        self.known_int_fields = [(self.species_name, "particle_id"),
-                                 (self.species_name, "particle_cpu"),
-                                 (self.species_name, "particle_cell_x"),
-                                 (self.species_name, "particle_cell_y"),
-                                 (self.species_name, "particle_cell_z")]
-        self.known_int_fields = self.known_int_fields[0:self.num_int_base]
-
-        # these are extra integer fields
-        extra_int_fields = ["particle_int_comp%d" % i 
-                            for i in range(self.num_int_extra)]
-        self.known_int_fields.extend([(self.species_name, field) 
-                                      for field in extra_int_fields])
-
-        # these are the base real fields
-        self.known_real_fields = [(self.species_name, "particle_position_x"),
-                                  (self.species_name, "particle_position_y"),
-                                  (self.species_name, "particle_position_z")]
-        self.known_real_fields = self.known_real_fields[0:self.num_real_base]
-
-        # these are the extras
-        if extra_field_names is not None:
-            assert(len(extra_field_names) == self.num_real_extra)
-        else:
-            extra_field_names = ["particle_real_comp%d" % i 
-                                 for i in range(self.num_real_extra)]
-
-        self.known_real_fields.extend([(self.species_name, field) 
-                                       for field in extra_field_names])
-
-        self.known_fields = self.known_int_fields + self.known_real_fields
-
-        self.particle_int_dtype = np.dtype([(t[1], self.int_type) 
-                                            for t in self.known_int_fields])
-
-        self.particle_real_dtype = np.dtype([(t[1], self.real_type) 
-                                            for t in self.known_real_fields])
-
-
 class WarpXHierarchy(BoxlibHierarchy):
 
     def __init__(self, ds, dataset_type="boxlib_native"):
         super(WarpXHierarchy, self).__init__(ds, dataset_type)
 
-        # extra beyond the base real fields that all Boxlib
-        # particles have, i.e. the xyz positions
-        warpx_extra_real_fields = ['particle_weight',
-                                   'particle_velocity_x',
-                                   'particle_velocity_y',
-                                   'particle_velocity_z']
-
-        is_checkpoint = False
-
+        is_checkpoint = True
         for ptype in self.ds.particle_types:
-            self._read_particles(ptype, is_checkpoint, warpx_extra_real_fields)
+            self._read_particles(ptype, is_checkpoint)
         
         # Additional WarpX particle information (used to set up species)
         with open(self.ds.output_dir + "/WarpXHeader", 'r') as f:
@@ -1351,3 +1442,48 @@
         setdefaultattr(self, 'mass_unit', self.quan(1.0, "kg"))
         setdefaultattr(self, 'time_unit', self.quan(1.0, "s"))
         setdefaultattr(self, 'velocity_unit', self.quan(1.0, "m/s"))
+
+
+class AMReXHierarchy(BoxlibHierarchy):
+
+    def __init__(self, ds, dataset_type="boxlib_native"):
+        super(AMReXHierarchy, self).__init__(ds, dataset_type)
+        
+        if ("particles" in self.ds.parameters):
+            is_checkpoint = True
+            for ptype in self.ds.particle_types:
+                self._read_particles(ptype, is_checkpoint)
+
+
+class AMReXDataset(BoxlibDataset):
+    
+    _index_class = AMReXHierarchy
+
+    def __init__(self, output_dir,
+                 cparam_filename=None,
+                 fparam_filename=None,
+                 dataset_type='boxlib_native',
+                 storage_filename=None,
+                 units_override=None,
+                 unit_system="cgs"):
+
+        super(AMReXDataset, self).__init__(output_dir,
+                                           cparam_filename,
+                                           fparam_filename,
+                                           dataset_type,
+                                           storage_filename,
+                                           units_override,
+                                           unit_system)
+
+    def _parse_parameter_file(self):
+        super(AMReXDataset, self)._parse_parameter_file()
+        particle_types = glob.glob(self.output_dir + "/*/Header")
+        particle_types = [cpt.split("/")[-2] for cpt in particle_types]
+        if len(particle_types) > 0:
+            self.parameters["particles"] = 1
+            self.particle_types = tuple(particle_types)
+            self.particle_types_raw = self.particle_types
+
+    @classmethod
+    def _is_valid(cls, *args, **kwargs):
+        return False

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/frontends/boxlib/tests/test_outputs.py
--- a/yt/frontends/boxlib/tests/test_outputs.py
+++ b/yt/frontends/boxlib/tests/test_outputs.py
@@ -144,22 +144,22 @@
     assert(np.all(np.logical_and(reg['particle_position_y'] <= right_edge[1], 
                                  reg['particle_position_y'] >= left_edge[1])))
 
-langmuir = "LangmuirWave/plt00020"
+langmuir = "LangmuirWave/plt00020_v2"
 @requires_ds(langmuir)
 def test_langmuir():
     ds = data_dir_load(langmuir)
-    yield assert_equal, str(ds), "plt00020"
+    yield assert_equal, str(ds), "plt00020_v2"
     for test in small_patch_amr(ds, _warpx_fields, 
                                 input_center="c",
                                 input_weight="Ex"):
         test_langmuir.__name__ = test.description
         yield test
 
-plasma = "PlasmaAcceleration/plt00030"
+plasma = "PlasmaAcceleration/plt00030_v2"
 @requires_ds(plasma)
 def test_plasma():
     ds = data_dir_load(plasma)
-    yield assert_equal, str(ds), "plt00030"
+    yield assert_equal, str(ds), "plt00030_v2"
     for test in small_patch_amr(ds, _warpx_fields,
                                 input_center="c",
                                 input_weight="Ex"):

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/frontends/chombo/data_structures.py
--- a/yt/frontends/chombo/data_structures.py
+++ b/yt/frontends/chombo/data_structures.py
@@ -34,7 +34,8 @@
 from yt.data_objects.static_output import \
     Dataset
 from yt.utilities.file_handler import \
-    HDF5FileHandler
+    HDF5FileHandler, \
+    warn_h5py
 from yt.utilities.parallel_tools.parallel_analysis_interface import \
     parallel_root_only
 from yt.utilities.lib.misc_utilities import \
@@ -730,6 +731,8 @@
     @classmethod
     def _is_valid(self, *args, **kwargs):
 
+        warn_h5py(args[0])
+
         if not is_chombo_hdf5(args[0]):
             return False
 

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/frontends/exodus_ii/data_structures.py
--- a/yt/frontends/exodus_ii/data_structures.py
+++ b/yt/frontends/exodus_ii/data_structures.py
@@ -23,8 +23,9 @@
 from yt.data_objects.static_output import \
     Dataset
 from yt.data_objects.unions import MeshUnion
-from .io import \
-    NetCDF4FileHandler
+from yt.utilities.file_handler import \
+    NetCDF4FileHandler, \
+    warn_netcdf
 from yt.utilities.logger import ytLogger as mylog
 from .fields import \
     ExodusIIFieldInfo
@@ -387,6 +388,7 @@
 
     @classmethod
     def _is_valid(self, *args, **kwargs):
+        warn_netcdf(args[0])
         try:
             from netCDF4 import Dataset
             filename = args[0]

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/frontends/flash/data_structures.py
--- a/yt/frontends/flash/data_structures.py
+++ b/yt/frontends/flash/data_structures.py
@@ -30,7 +30,8 @@
 from yt.geometry.particle_geometry_handler import \
     ParticleIndex
 from yt.utilities.file_handler import \
-    HDF5FileHandler
+    HDF5FileHandler, \
+    warn_h5py
 from yt.utilities.physical_ratios import cm_per_mpc
 from .fields import FLASHFieldInfo
 
@@ -439,7 +440,7 @@
             fileh = HDF5FileHandler(args[0])
             if "bounding box" in fileh["/"].keys():
                 return True
-        except:
+        except (IOError, OSError, ImportError):
             pass
         return False
 
@@ -489,12 +490,13 @@
 
     @classmethod
     def _is_valid(self, *args, **kwargs):
+        warn_h5py(args[0])
         try:
             fileh = HDF5FileHandler(args[0])
             if "bounding box" not in fileh["/"].keys() \
                 and "localnp" in fileh["/"].keys():
                 return True
-        except IOError:
+        except (IOError, OSError, ImportError):
             pass
         return False
 

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/frontends/open_pmd/data_structures.py
--- a/yt/frontends/open_pmd/data_structures.py
+++ b/yt/frontends/open_pmd/data_structures.py
@@ -31,7 +31,8 @@
     get_component
 from yt.funcs import setdefaultattr
 from yt.geometry.grid_geometry_handler import GridIndex
-from yt.utilities.file_handler import HDF5FileHandler
+from yt.utilities.file_handler import HDF5FileHandler, \
+    warn_h5py
 from yt.utilities.logger import ytLogger as mylog
 from yt.utilities.on_demand_imports import _h5py as h5
 
@@ -495,9 +496,10 @@
     def _is_valid(self, *args, **kwargs):
         """Checks whether the supplied file can be read by this frontend.
         """
+        warn_h5py(args[0])
         try:
             f = h5.File(args[0], "r")
-        except (IOError, OSError):
+        except (IOError, OSError, ImportError):
             return False
 
         requirements = ["openPMD", "basePath", "meshesPath", "particlesPath"]

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -102,12 +102,6 @@
                               "dropping to 1st order.")
                 field_data = field_data[:, 0:8]
                 indices = indices[:, 0:8]
-            elif field_data.shape[1] == 10:
-                # tetrahedral
-                mylog.warning("High order elements not yet supported, " +
-                              "dropping to 1st order.")
-                field_data = field_data[:,0:4]
-                indices = indices[:, 0:4]
 
             img = pixelize_element_mesh(coords,
                                         indices,

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/geometry/grid_container.pyx
--- a/yt/geometry/grid_container.pyx
+++ b/yt/geometry/grid_container.pyx
@@ -63,9 +63,9 @@
                   np.ndarray[np.int64_t, ndim=1] num_children):
 
         cdef int i, j, k
-        cdef np.ndarray[np.int64_t, ndim=1] child_ptr
+        cdef np.ndarray[np.int_t, ndim=1] child_ptr
 
-        child_ptr = np.zeros(num_grids, dtype='int64')
+        child_ptr = np.zeros(num_grids, dtype='int')
 
         self.num_grids = num_grids
         self.num_root_grids = 0

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/geometry/grid_visitors.pxd
--- a/yt/geometry/grid_visitors.pxd
+++ b/yt/geometry/grid_visitors.pxd
@@ -37,7 +37,7 @@
     np.float64_t right_edge_x
     np.float64_t right_edge_y
     np.float64_t right_edge_z
-    np.int64_t children_pointers
+    np.int_t children_pointers
     np.int64_t start_index_x
     np.int64_t start_index_y
     np.int64_t start_index_z

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/geometry/particle_deposit.pxd
--- a/yt/geometry/particle_deposit.pxd
+++ b/yt/geometry/particle_deposit.pxd
@@ -136,7 +136,7 @@
     cdef kernel_func sph_kernel
     cdef public object nvals
     cdef public int update_values
-    cdef void process(self, int dim[3], np.float64_t left_edge[3],
-                      np.float64_t dds[3], np.int64_t offset,
-                      np.float64_t ppos[3], np.float64_t[:] fields,
-                      np.int64_t domain_ind)
+    cdef int process(self, int dim[3], np.float64_t left_edge[3],
+                     np.float64_t dds[3], np.int64_t offset,
+                     np.float64_t ppos[3], np.float64_t[:] fields,
+                     np.int64_t domain_ind) except -1

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/geometry/particle_deposit.pyx
--- a/yt/geometry/particle_deposit.pyx
+++ b/yt/geometry/particle_deposit.pyx
@@ -122,10 +122,12 @@
         cdef np.int64_t gid = getattr(gobj, "id", -1)
         cdef np.float64_t dds[3]
         cdef np.float64_t left_edge[3]
+        cdef np.float64_t right_edge[3]
         cdef int dims[3]
         for i in range(3):
             dds[i] = gobj.dds[i]
             left_edge[i] = gobj.LeftEdge[i]
+            right_edge[i] = gobj.RightEdge[i]
             dims[i] = gobj.ActiveDimensions[i]
         for i in range(positions.shape[0]):
             # Now we process
@@ -133,15 +135,21 @@
                 field_vals[j] = field_pointers[j,i]
             for j in range(3):
                 pos[j] = positions[i, j]
+            continue_loop = False
+            for j in range(3):
+                if pos[j] < left_edge[j] or pos[j] > right_edge[j]:
+                    continue_loop = True
+            if continue_loop:
+                continue
             self.process(dims, left_edge, dds, 0, pos, field_vals, gid)
             if self.update_values == 1:
                 for j in range(nf):
                     field_pointers[j][i] = field_vals[j]
 
-    cdef void process(self, int dim[3], np.float64_t left_edge[3],
-                      np.float64_t dds[3], np.int64_t offset,
-                      np.float64_t ppos[3], np.float64_t[:] fields,
-                      np.int64_t domain_ind):
+    cdef int process(self, int dim[3], np.float64_t left_edge[3],
+                     np.float64_t dds[3], np.int64_t offset,
+                     np.float64_t ppos[3], np.float64_t[:] fields,
+                     np.int64_t domain_ind) except -1:
         raise NotImplementedError
 
 cdef class CountParticles(ParticleDepositOperation):
@@ -152,20 +160,21 @@
             np.zeros(self.nvals, dtype="int64", order='F'), 4)
 
     @cython.cdivision(True)
-    cdef void process(self, int dim[3],
-                      np.float64_t left_edge[3],
-                      np.float64_t dds[3],
-                      np.int64_t offset, # offset into IO field
-                      np.float64_t ppos[3], # this particle's position
-                      np.float64_t[:] fields,
-                      np.int64_t domain_ind
-                      ):
+    cdef int process(self, int dim[3],
+                     np.float64_t left_edge[3],
+                     np.float64_t dds[3],
+                     np.int64_t offset, # offset into IO field
+                     np.float64_t ppos[3], # this particle's position
+                     np.float64_t[:] fields,
+                     np.int64_t domain_ind
+                     ) except -1:
         # here we do our thing; this is the kernel
         cdef int ii[3]
         cdef int i
         for i in range(3):
             ii[i] = <int>((ppos[i] - left_edge[i])/dds[i])
         self.count[ii[2], ii[1], ii[0], offset] += 1
+        return 0
 
     def finalize(self):
         arr = np.asarray(self.count)
@@ -188,14 +197,14 @@
             np.zeros(self.nvals, dtype="float64", order='F'), 4)
 
     @cython.cdivision(True)
-    cdef void process(self, int dim[3],
-                      np.float64_t left_edge[3],
-                      np.float64_t dds[3],
-                      np.int64_t offset,
-                      np.float64_t ppos[3],
-                      np.float64_t[:] fields,
-                      np.int64_t domain_ind
-                      ):
+    cdef int process(self, int dim[3],
+                     np.float64_t left_edge[3],
+                     np.float64_t dds[3],
+                     np.int64_t offset,
+                     np.float64_t ppos[3],
+                     np.float64_t[:] fields,
+                     np.int64_t domain_ind
+                     ) except -1:
         cdef int ii[3]
         cdef int ib0[3]
         cdef int ib1[3]
@@ -210,7 +219,7 @@
             ib0[i] = ii[i] - half_len
             ib1[i] = ii[i] + half_len
             if ib0[i] >= dim[i] or ib1[i] <0:
-                return
+                return 0
             ib0[i] = iclip(ib0[i], 0, dim[i] - 1)
             ib1[i] = iclip(ib1[i], 0, dim[i] - 1)
         for i from ib0[0] <= i <= ib1[0]:
@@ -233,6 +242,7 @@
                 for k from ib0[2] <= k <= ib1[2]:
                     dist = self.temp[k,j,i,offset] / kernel_sum
                     self.data[k,j,i,offset] += fields[1] * dist
+        return 0
 
     def finalize(self):
         return self.odata
@@ -246,20 +256,20 @@
             np.zeros(self.nvals, dtype="float64", order='F'), 4)
 
     @cython.cdivision(True)
-    cdef void process(self, int dim[3],
-                      np.float64_t left_edge[3],
-                      np.float64_t dds[3],
-                      np.int64_t offset,
-                      np.float64_t ppos[3],
-                      np.float64_t[:] fields,
-                      np.int64_t domain_ind
-                      ):
+    cdef int process(self, int dim[3],
+                     np.float64_t left_edge[3],
+                     np.float64_t dds[3],
+                     np.int64_t offset,
+                     np.float64_t ppos[3],
+                     np.float64_t[:] fields,
+                     np.int64_t domain_ind
+                     ) except -1:
         cdef int ii[3]
         cdef int i
         for i in range(3):
             ii[i] = <int>((ppos[i] - left_edge[i]) / dds[i])
         self.sum[ii[2], ii[1], ii[0], offset] += fields[0]
-        return
+        return 0
 
     def finalize(self):
         sum = np.asarray(self.sum)
@@ -288,14 +298,14 @@
             np.zeros(self.nvals, dtype="float64", order='F'), 4)
 
     @cython.cdivision(True)
-    cdef void process(self, int dim[3],
-                      np.float64_t left_edge[3],
-                      np.float64_t dds[3],
-                      np.int64_t offset,
-                      np.float64_t ppos[3],
-                      np.float64_t[:] fields,
-                      np.int64_t domain_ind
-                      ):
+    cdef int process(self, int dim[3],
+                     np.float64_t left_edge[3],
+                     np.float64_t dds[3],
+                     np.int64_t offset,
+                     np.float64_t ppos[3],
+                     np.float64_t[:] fields,
+                     np.int64_t domain_ind
+                     ) except -1:
         cdef int ii[3]
         cdef int i, cell_index
         cdef float k, mk, qk
@@ -313,6 +323,7 @@
             self.qk[ii[2], ii[1], ii[0], offset] = \
                 qk + (k - 1.0) * (fields[0] - mk) * (fields[0] - mk) / k
         self.i[ii[2], ii[1], ii[0], offset] += 1
+        return 0
 
     def finalize(self):
         # This is the standard variance
@@ -338,14 +349,14 @@
             np.zeros(self.nvals, dtype="float64", order='F'), 4)
 
     @cython.cdivision(True)
-    cdef void process(self, int dim[3],
-                      np.float64_t left_edge[3],
-                      np.float64_t dds[3],
-                      np.int64_t offset, # offset into IO field
-                      np.float64_t ppos[3], # this particle's position
-                      np.float64_t[:] fields,
-                      np.int64_t domain_ind
-                      ):
+    cdef int process(self, int dim[3],
+                     np.float64_t left_edge[3],
+                     np.float64_t dds[3],
+                     np.int64_t offset, # offset into IO field
+                     np.float64_t ppos[3], # this particle's position
+                     np.float64_t[:] fields,
+                     np.int64_t domain_ind
+                     ) except -1:
 
         cdef int i, j, k
         cdef np.uint64_t ii
@@ -371,6 +382,8 @@
                     self.field[ind[2] - k, ind[1] - j, ind[0] - i, offset] += \
                         fields[0]*rdds[0][i]*rdds[1][j]*rdds[2][k]
 
+        return 0
+
     def finalize(self):
         rv = np.asarray(self.field)
         rv.shape = self.nvals
@@ -390,20 +403,21 @@
             np.zeros(self.nvals, dtype='float64', order='F'), 4)
 
     @cython.cdivision(True)
-    cdef void process(self, int dim[3],
-                      np.float64_t left_edge[3],
-                      np.float64_t dds[3],
-                      np.int64_t offset,
-                      np.float64_t ppos[3],
-                      np.float64_t[:] fields,
-                      np.int64_t domain_ind
-                      ):
+    cdef int process(self, int dim[3],
+                     np.float64_t left_edge[3],
+                     np.float64_t dds[3],
+                     np.int64_t offset,
+                     np.float64_t ppos[3],
+                     np.float64_t[:] fields,
+                     np.int64_t domain_ind
+                     ) except -1:
         cdef int ii[3]
         cdef int i
         for i in range(3):
             ii[i] = <int>((ppos[i] - left_edge[i]) / dds[i])
         self.w[ii[2], ii[1], ii[0], offset] += fields[1]
         self.wf[ii[2], ii[1], ii[0], offset] += fields[0] * fields[1]
+        return 0
 
     def finalize(self):
         wf = np.asarray(self.wf)
@@ -422,15 +436,16 @@
         self.update_values = 1
 
     @cython.cdivision(True)
-    cdef void process(self, int dim[3],
+    cdef int process(self, int dim[3],
                       np.float64_t left_edge[3],
                       np.float64_t dds[3],
                       np.int64_t offset,
                       np.float64_t ppos[3],
                       np.float64_t[:] fields,
                       np.int64_t domain_ind
-                      ):
+                      ) except -1:
         fields[0] = domain_ind
+        return 0
 
     def finalize(self):
         return
@@ -448,14 +463,14 @@
         self.distfield[:] = np.inf
 
     @cython.cdivision(True)
-    cdef void process(self, int dim[3],
-                      np.float64_t left_edge[3],
-                      np.float64_t dds[3],
-                      np.int64_t offset,
-                      np.float64_t ppos[3],
-                      np.float64_t[:] fields,
-                      np.int64_t domain_ind
-                      ):
+    cdef int process(self, int dim[3],
+                     np.float64_t left_edge[3],
+                     np.float64_t dds[3],
+                     np.int64_t offset,
+                     np.float64_t ppos[3],
+                     np.float64_t[:] fields,
+                     np.int64_t domain_ind
+                     ) except -1:
         # This one is a bit slow.  Every grid cell is going to be iterated
         # over, and we're going to deposit particles in it.
         cdef int i, j, k
@@ -477,7 +492,7 @@
                     gpos[2] += dds[2]
                 gpos[1] += dds[1]
             gpos[0] += dds[0]
-        return
+        return 0
 
     def finalize(self):
         nn = np.asarray(self.nnfield)

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/geometry/particle_smooth.pyx
--- a/yt/geometry/particle_smooth.pyx
+++ b/yt/geometry/particle_smooth.pyx
@@ -314,8 +314,7 @@
         # Note that what we will be providing to our processing functions will
         # actually be indirectly-sorted fields.  This preserves memory at the
         # expense of additional pointer lookups.
-        pind = np.argsort(pdoms)
-        pind = np.asarray(pind, dtype='int64', order='C')
+        pind = np.asarray(np.argsort(pdoms), dtype='int64', order='C')
         # So what this means is that we now have all the oct-0 particle indices
         # in order, then the oct-1, etc etc.
         # This now gives us the indices to the particles for each domain.

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/utilities/exodusII_reader.py
--- a/yt/utilities/exodusII_reader.py
+++ /dev/null
@@ -1,51 +0,0 @@
-import string
-from itertools import takewhile
-from netCDF4 import Dataset
-import numpy as np
-from yt.config import ytcfg
-import os
-import warnings
-
-
-def sanitize_string(s):
-    s = "".join(_ for _ in takewhile(lambda a: a in string.printable, s))
-    return s
-
-
-def get_data(fn):
-    warnings.warn("The yt.utilities.exodusII_reader module is deprecated "
-                  "and will be removed in a future release. "
-                  "Please use the normal yt.load() command to access "
-                  "your data instead.")
-    try:
-        f = Dataset(fn)
-    except RuntimeError:
-        f = Dataset(os.path.join(ytcfg.get("yt", "test_data_dir"), fn))
-    fvars = f.variables
-    # Is this correct?
-    etypes = fvars["eb_status"][:]
-    nelem = etypes.shape[0]
-    varnames = [sanitize_string(v.tostring()) for v in
-                fvars["name_elem_var"][:]]
-    nodnames = [sanitize_string(v.tostring()) for v in
-                fvars["name_nod_var"][:]]
-    coord = np.array([fvars["coord%s" % ax][:]
-                     for ax in 'xyz']).transpose().copy()
-    coords = []
-    connects = []
-    data = []
-    for i in range(nelem):
-        connects.append(fvars["connect%s" % (i+1)][:].astype("i8"))
-        ci = connects[-1]
-        coords.append(coord)  # Same for all
-        vals = {}
-        for j, v in enumerate(varnames):
-            values = fvars["vals_elem_var%seb%s" % (j+1, i+1)][:]
-            vals['gas', v] = values.astype("f8")[-1, :]
-        for j, v in enumerate(nodnames):
-            # We want just for this set of nodes all the node variables
-            # Use (ci - 1) to get these values
-            values = fvars["vals_nod_var%s" % (j+1)][:]
-            vals['gas', v] = values.astype("f8")[-1, ci - 1, ...]
-        data.append(vals)
-    return coords, connects, data

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/utilities/file_handler.py
--- a/yt/utilities/file_handler.py
+++ b/yt/utilities/file_handler.py
@@ -14,8 +14,26 @@
 #-----------------------------------------------------------------------------
 
 from yt.utilities.on_demand_imports import _h5py as h5py
+from yt.utilities.on_demand_imports import NotAModule
 from contextlib import contextmanager
 
+def valid_hdf5_signature(fn):
+    signature = b'\x89HDF\r\n\x1a\n'
+    try:
+        with open(fn, 'rb') as f:
+            header = f.read(8)
+            return header == signature
+    except:
+        return False
+
+
+def warn_h5py(fn):
+    needs_h5py = valid_hdf5_signature(fn)
+    if needs_h5py and isinstance(h5py.File, NotAModule):
+        raise RuntimeError("This appears to be an HDF5 file, "
+                           "but h5py is not installed.")
+
+
 class HDF5FileHandler(object):
     handle = None
 
@@ -68,13 +86,33 @@
     def close(self):
         self.handle.close()
 
-class NetCDF4FileHandler():
+
+def valid_netcdf_classic_signature(filename):
+    signature_v1 = b'CDF\x01'
+    signature_v2 = b'CDF\x02'
+    try:
+        with open(filename, 'rb') as f:
+            header = f.read(4)
+            return (header == signature_v1 or header == signature_v2)
+    except:
+        return False
+
+
+def warn_netcdf(fn):
+    needs_netcdf = valid_netcdf_classic_signature(fn)
+    from yt.utilities.on_demand_imports import _netCDF4 as netCDF4
+    if needs_netcdf and isinstance(netCDF4.Dataset, NotAModule):
+        raise RuntimeError("This appears to be a netCDF file, but the "
+                           "python bindings for netCDF4 are not installed.")
+
+
+class NetCDF4FileHandler(object):
     def __init__(self, filename):
         self.filename = filename
 
     @contextmanager
     def open_ds(self):
-        from netCDF4 import Dataset
-        ds = Dataset(self.filename)
+        from yt.utilities.on_demand_imports import _netCDF4 as netCDF4
+        ds = netCDF4.Dataset(self.filename)
         yield ds
         ds.close()

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/utilities/lib/alt_ray_tracers.pyx
--- a/yt/utilities/lib/alt_ray_tracers.pyx
+++ b/yt/utilities/lib/alt_ray_tracers.pyx
@@ -101,7 +101,7 @@
                                           rleft, rright, zleft, zright, \
                                           cleft, cright, thetaleft, thetaright, \
                                           tmleft, tpleft, tmright, tpright, tsect
-    cdef np.ndarray[np.int64_t, ndim=1, cast=True] inds, tinds, sinds
+    cdef np.ndarray[np.int_t, ndim=1, cast=True] inds, tinds, sinds
     cdef np.ndarray[np.float64_t, ndim=2] xyz, rztheta, ptemp, b1, b2, dsect
 
     # set up  points

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/utilities/lib/element_mappings.pyx
--- a/yt/utilities/lib/element_mappings.pyx
+++ b/yt/utilities/lib/element_mappings.pyx
@@ -344,6 +344,10 @@
                                double* physical_x) nogil:
         '''
 
+        A thorough description of Newton's method and modifications for global
+        convergence can be found in Dennis's text "Numerical Methods for
+        Unconstrained Optimization and Nonlinear Equations."
+
         x: solution vector; holds unit/mapped coordinates
         xk: temporary vector for holding solution of current iteration
         f: residual vector
@@ -352,11 +356,16 @@
         d: Jacobian determinant
         s_n: Newton step vector
         lam: fraction of Newton step by which to change x
-        alpha: constant proportional to how much residual required to decrease
+        alpha: constant proportional to how much residual required to decrease.
+               1e-4 is value of alpha recommended by Dennis
         err_c: Error of current iteration
         err_plus: Error of next iteration
+        min_lam: minimum fraction of Newton step that the line search is allowed
+                 to take. General experience suggests that lambda values smaller
+                 than 1e-3 will not significantly reduce the residual, but we
+                 set to 1e-6 just to be safe
+        '''
         
-        '''
         cdef int i
         cdef double d, lam
         cdef double[3] f
@@ -367,6 +376,7 @@
         cdef int iterations = 0
         cdef double err_c, err_plus
         cdef double alpha = 1e-4
+        cdef double min_lam = 1e-6
 
         # initial guess
         for i in range(3):
@@ -391,7 +401,7 @@
             err_plus = maxnorm(f, 3)
             
             lam = 1
-            while err_plus > err_c * (1. - alpha * lam) and lam > 1e-6:
+            while err_plus > err_c * (1. - alpha * lam) and lam > min_lam:
                 lam = lam / 2
                 xk[0] = x[0] + lam * s_n[0]
                 xk[1] = x[1] + lam * s_n[1]
@@ -791,6 +801,10 @@
                                double* physical_x) nogil:
         '''
 
+        A thorough description of Newton's method and modifications for global
+        convergence can be found in Dennis's text "Numerical Methods for
+        Unconstrained Optimization and Nonlinear Equations."
+
         x: solution vector; holds unit/mapped coordinates
         xk: temporary vector for holding solution of current iteration
         f: residual vector
@@ -798,11 +812,16 @@
         d: Jacobian determinant
         s_n: Newton step vector
         lam: fraction of Newton step by which to change x
-        alpha: constant proportional to how much residual required to decrease
+        alpha: constant proportional to how much residual required to decrease.
+               1e-4 is value of alpha recommended by Dennis
         err_c: Error of current iteration
         err_plus: Error of next iteration
+        min_lam: minimum fraction of Newton step that the line search is allowed
+                 to take. General experience suggests that lambda values smaller
+                 than 1e-3 will not significantly reduce the residual, but we
+                 set to 1e-6 just to be safe
+        '''
         
-        '''
         cdef int i
         cdef double d, lam
         cdef double[2] f
@@ -811,6 +830,7 @@
         cdef int iterations = 0
         cdef double err_c, err_plus
         cdef double alpha = 1e-4
+        cdef double min_lam = 1e-6
 
         # initial guess
         for i in range(2):
@@ -833,7 +853,7 @@
             err_plus = maxnorm(f, 2)
             
             lam = 1
-            while err_plus > err_c * (1. - alpha * lam) and lam > 1e-6:
+            while err_plus > err_c * (1. - alpha * lam) and lam > min_lam:
                 lam = lam / 2
                 xk[0] = x[0] + lam * s_n[0]
                 xk[1] = x[1] + lam * s_n[1]

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/utilities/lib/image_utilities.pyx
--- a/yt/utilities/lib/image_utilities.pyx
+++ b/yt/utilities/lib/image_utilities.pyx
@@ -17,6 +17,7 @@
 
 def add_points_to_greyscale_image(
         np.ndarray[np.float64_t, ndim=2] buffer,
+        np.ndarray[np.int_t,     ndim=2] buffer_mask,
         np.ndarray[np.float64_t, ndim=1] px,
         np.ndarray[np.float64_t, ndim=1] py,
         np.ndarray[np.float64_t, ndim=1] pv):
@@ -28,6 +29,7 @@
         j = <int> (xs * px[pi])
         i = <int> (ys * py[pi])
         buffer[i, j] += pv[pi]
+        buffer_mask[i, j] = 1
     return
 
 def add_points_to_image(

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -245,7 +245,7 @@
                        np.float64_t[:] pdz,
                        np.float64_t[:] center,
                        np.float64_t[:,:] inv_mat,
-                       np.int64_t[:] indices,
+                       np.int_t[:] indices,
                        np.float64_t[:] data,
                        bounds):
     cdef np.float64_t x_min, x_max, y_min, y_max

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/utilities/on_demand_imports.py
--- a/yt/utilities/on_demand_imports.py
+++ b/yt/utilities/on_demand_imports.py
@@ -30,6 +30,23 @@
     def __call__(self, *args, **kwargs):
         raise self.error
 
+class netCDF4_imports(object):
+    _name = "netCDF4"
+    _Dataset = None
+    @property
+    def Dataset(self):
+        if self._Dataset is None:
+            try:
+                from netCDF4 import Dataset
+            except ImportError:
+                Dataset = NotAModule(self._name)
+            self._Dataset = Dataset
+        return self._Dataset
+
+
+_netCDF4 = netCDF4_imports()
+
+
 class astropy_imports(object):
     _name = "astropy"
     _pyfits = None

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/visualization/fixed_resolution.py
--- a/yt/visualization/fixed_resolution.py
+++ b/yt/visualization/fixed_resolution.py
@@ -648,24 +648,31 @@
 
         # splat particles
         buff = np.zeros(self.buff_size)
+        buff_mask = np.zeros(self.buff_size).astype('int')
         add_points_to_greyscale_image(buff,
+                                      buff_mask,
                                       px[mask],
                                       py[mask],
                                       splat_vals)
+        # remove values in no-particle region
+        buff[buff_mask==0] = np.nan
         ia = ImageArray(buff, input_units=data.units,
                         info=self._get_info(item))
 
         # divide by the weight_field, if needed
         if weight_field is not None:
             weight_buff = np.zeros(self.buff_size)
+            weight_buff_mask = np.zeros(self.buff_size).astype('int')
             add_points_to_greyscale_image(weight_buff,
+                                          weight_buff_mask,
                                           px[mask],
                                           py[mask],
                                           weight_data[mask])
             weight_array = ImageArray(weight_buff,
                                       input_units=weight_data.units,
                                       info=self._get_info(item))
-
+            # remove values in no-particle region
+            weight_buff[weight_buff_mask==0] = np.nan
             locs = np.where(weight_array > 0)
             ia[locs] /= weight_array[locs]
 

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/visualization/plot_modifications.py
--- a/yt/visualization/plot_modifications.py
+++ b/yt/visualization/plot_modifications.py
@@ -2033,10 +2033,6 @@
 
         # Callback only works for plots with axis ratios of 1
         xsize = plot.xlim[1] - plot.xlim[0]
-        if plot.aspect != 1.0:
-            raise NotImplementedError(
-                "Scale callback has only been implemented for plots with no "
-                "aspect ratio scaling. (aspect = {%s})".format(plot._aspect))
 
         # Setting pos overrides corner argument
         if self.pos is None:
@@ -2076,8 +2072,8 @@
         text = "{scale} {units}".format(scale=int(self.coeff), units=self.unit)
         image_scale = (plot.frb.convert_distance_x(self.scale) /
                        plot.frb.convert_distance_x(xsize)).v
-
-        size_vertical = self.size_bar_args.pop('size_vertical', .005)
+        size_vertical = self.size_bar_args.pop(
+            'size_vertical', .005 * plot.aspect)
         fontproperties = self.size_bar_args.pop(
             'fontproperties', plot.font_properties.copy())
         frameon = self.size_bar_args.pop('frameon', self.draw_inset_box)
@@ -2336,12 +2332,12 @@
                                                  plot.data,
                                                  self.field_x,
                                                  bounds,
-                                                 (nx,ny))
+                                                 (ny,nx))
         pixY = plot.data.ds.coordinates.pixelize(plot.data.axis,
                                                  plot.data,
                                                  self.field_y,
                                                  bounds,
-                                                 (nx,ny))
+                                                 (ny,nx))
 
         vectors = np.concatenate((pixX[...,np.newaxis],
                                   pixY[...,np.newaxis]),axis=2)

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/visualization/profile_plotter.py
--- a/yt/visualization/profile_plotter.py
+++ b/yt/visualization/profile_plotter.py
@@ -84,6 +84,7 @@
     def __init__(self, plots):
         self.plots = plots
         self.ylim = {}
+        self.xlim = (None, None)
         super(AxesContainer, self).__init__()
 
     def __missing__(self, key):
@@ -357,6 +358,7 @@
             axes.set_xlabel(xtitle)
             axes.set_ylabel(ytitle)
             axes.set_ylim(*self.axes.ylim[fname])
+            axes.set_xlim(*self.axes.xlim)
             if any(self.label):
                 axes.legend(loc="best")
         self._set_font_properties()
@@ -539,6 +541,7 @@
         >>> pp.save()
 
         """
+        self.axes.xlim = (xmin, xmax)
         for i, p in enumerate(self.profiles):
             if xmin is None:
                 xmi = p.x_bins.min()

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/visualization/tests/test_callbacks.py
--- a/yt/visualization/tests/test_callbacks.py
+++ b/yt/visualization/tests/test_callbacks.py
@@ -107,32 +107,38 @@
         ds = fake_amr_ds(fields = ("density",))
         p = ProjectionPlot(ds, ax, "density")
         p.annotate_scale()
-        yield assert_fname, p.save(prefix)[0]
+        assert_fname(p.save(prefix)[0])
+        p = ProjectionPlot(ds, ax, "density", width=(0.5, 1.0))
+        p.annotate_scale()
+        assert_fname(p.save(prefix)[0])
+        p = ProjectionPlot(ds, ax, "density", width=(1.0, 1.5))
+        p.annotate_scale()
+        assert_fname(p.save(prefix)[0])
         p = SlicePlot(ds, ax, "density")
         p.annotate_scale()
-        yield assert_fname, p.save(prefix)[0]
+        assert_fname(p.save(prefix)[0])
         p = OffAxisSlicePlot(ds, vector, "density")
         p.annotate_scale()
-        yield assert_fname, p.save(prefix)[0]
+        assert_fname(p.save(prefix)[0])
         # Now we'll check a few additional minor things
         p = SlicePlot(ds, "x", "density")
         p.annotate_scale(corner='upper_right', coeff=10., unit='kpc')
-        yield assert_fname, p.save(prefix)[0]
+        assert_fname(p.save(prefix)[0])
         p = SlicePlot(ds, "x", "density")
         p.annotate_scale(text_args={"size": 24})
-        yield assert_fname, p.save(prefix)[0]
+        assert_fname(p.save(prefix)[0])
         p = SlicePlot(ds, "x", "density")
         p.annotate_scale(text_args={"font": 24})
-        yield assert_raises, YTPlotCallbackError
+        assert_raises(YTPlotCallbackError)
 
     with _cleanup_fname() as prefix:
         ds = fake_amr_ds(fields = ("density",), geometry="spherical")
         p = ProjectionPlot(ds, "r", "density")
         p.annotate_scale()
-        yield assert_raises, YTDataTypeUnsupported, p.save, prefix
+        assert_raises(YTDataTypeUnsupported, p.save, prefix)
         p = ProjectionPlot(ds, "r", "density")
         p.annotate_scale(coord_system="axis")
-        yield assert_raises, YTDataTypeUnsupported, p.save, prefix
+        assert_raises(YTDataTypeUnsupported, p.save, prefix)
 
 def test_line_callback():
     with _cleanup_fname() as prefix:

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/visualization/tests/test_mesh_slices.py
--- a/yt/visualization/tests/test_mesh_slices.py
+++ b/yt/visualization/tests/test_mesh_slices.py
@@ -49,6 +49,15 @@
     for field in ds.field_list:
         yield compare(ds, field, "answers_tri2_%s_%s" % (field[0], field[1]))
 
+quad2 = "SecondOrderQuads/lid_driven_out.e"
+
+ at requires_ds(quad2)
+def test_quad2():
+    ds = data_dir_load(quad2, kwargs={'step':-1})
+    field_list = [('all', 'T'), ('all', 'vel_x'), ('all', 'vel_y')]
+    for field in field_list:
+        yield compare(ds, field, "answers_quad2_%s_%s" % (field[0], field[1]))
+
 multi_region = "MultiRegion/two_region_example_out.e"
 
 @requires_ds(multi_region)

diff -r add68b583bb1f40f49f724237fe658ee0769a978 -r 8ee92ca7654fcd5c12a0c703aa9d48c89fa7435a yt/visualization/volume_rendering/scene.py
--- a/yt/visualization/volume_rendering/scene.py
+++ b/yt/visualization/volume_rendering/scene.py
@@ -393,8 +393,6 @@
         rs = rensources[0]
         tf = rs.transfer_function
         label = rs.data_source.ds._get_field_info(rs.field).get_label()
-        if rs.log_field:
-            label = r'$\rm{log}\ $' + label
 
         ax = self._show_mpl(self._last_render.swapaxes(0, 1),
                             sigma_clip=sigma_clip, dpi=dpi)


https://bitbucket.org/yt_analysis/yt/commits/8873fcbb5c4c/
Changeset:   8873fcbb5c4c
User:        Alex Lindsay
Date:        2017-06-03 19:59:50+00:00
Summary:     Merge branch 'line_plot_plus_quad9' of ssh://github.com/lindsayad/yt into line_plot_plus_quad9
Affected #:  2 files



https://bitbucket.org/yt_analysis/yt/commits/f2b2d87a28ed/
Changeset:   f2b2d87a28ed
User:        Alex Lindsay
Date:        2017-06-03 22:06:40+00:00
Summary:     First interesting error
Affected #:  4 files

diff -r 8873fcbb5c4c2514043a0b11243a83a59023fd69 -r f2b2d87a28ed6d646a65f44d8fa23d1ce4cfe078 yt/__init__.py
--- a/yt/__init__.py
+++ b/yt/__init__.py
@@ -103,7 +103,7 @@
     FixedResolutionBuffer, ObliqueFixedResolutionBuffer, \
     write_bitmap, write_image, \
     apply_colormap, scale_image, write_projection, \
-    SlicePlot, AxisAlignedSlicePlot, OffAxisSlicePlot, \
+    SlicePlot, AxisAlignedSlicePlot, OffAxisSlicePlot, LinePlot, \
     ProjectionPlot, OffAxisProjectionPlot, \
     show_colormaps, add_cmap, make_colormap, \
     ProfilePlot, PhasePlot, ParticlePhasePlot, \

diff -r 8873fcbb5c4c2514043a0b11243a83a59023fd69 -r f2b2d87a28ed6d646a65f44d8fa23d1ce4cfe078 yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -24,7 +24,7 @@
 from yt.funcs import mylog
 from yt.utilities.lib.pixelization_routines import \
     pixelize_element_mesh, pixelize_off_axis_cartesian, \
-    pixelize_cartesian, pixelize_cartesian_nodal,
+    pixelize_cartesian, pixelize_cartesian_nodal, \
     element_mesh_line_plot
 from yt.data_objects.unstructured_mesh import SemiStructuredMesh
 from yt.utilities.nodal_data_utils import get_nodal_data
@@ -66,6 +66,7 @@
 
     def pixelize(self, dimension, data_source, field, bounds, size,
                  antialias = True, periodic = True):
+        import pdb; pdb.set_trace()
         index = data_source.ds.index
         if (hasattr(index, 'meshes') and
            not isinstance(index.meshes[0], SemiStructuredMesh)):

diff -r 8873fcbb5c4c2514043a0b11243a83a59023fd69 -r f2b2d87a28ed6d646a65f44d8fa23d1ce4cfe078 yt/visualization/api.py
--- a/yt/visualization/api.py
+++ b/yt/visualization/api.py
@@ -51,7 +51,8 @@
     AxisAlignedSlicePlot, \
     OffAxisSlicePlot, \
     ProjectionPlot, \
-    OffAxisProjectionPlot
+    OffAxisProjectionPlot, \
+    LinePlot
 
 from .profile_plotter import \
     ProfilePlot, \

diff -r 8873fcbb5c4c2514043a0b11243a83a59023fd69 -r f2b2d87a28ed6d646a65f44d8fa23d1ce4cfe078 yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -24,7 +24,7 @@
 from numbers import Number
 
 from .base_plot_types import \
-    ImagePlotMPL
+    ImagePlotMPL, PlotMPL
 from .fixed_resolution import \
     FixedResolutionBuffer, \
     OffAxisProjectionFixedResolutionBuffer
@@ -67,6 +67,7 @@
     YTPlotCallbackError, \
     YTDataTypeUnsupported, \
     YTInvalidFieldType
+from yt.geometry.coordinates.cartesian_coordinates import CartesianCoordinateHandler
 
 MPL_VERSION = LooseVersion(matplotlib.__version__)
 
@@ -1118,7 +1119,8 @@
         ----------
 
         field : string, field tuple, or list of strings or field tuples (optional)
-            The name of the field(s) that we want to show the colorbar.
+            The name of the field(s) that we want to show
+ the colorbar.
         """
         if field is None:
             field = self.fields
@@ -2014,3 +2016,44 @@
             del kwargs['north_vector']
 
         return AxisAlignedSlicePlot(ds, normal, fields, *args, **kwargs)
+
+class LinePlot(PlotMPL):
+    r"""
+    A factory function for
+    :class:`yt.visualization.plot_window.AxisAlignedSlicePlot`
+    and :class:`yt.visualization.plot_window.OffAxisSlicePlot` objects.  This
+    essentially allows for a single entry point to both types of slice plots,
+    the distinction being determined by the specified normal vector to the
+    slice.
+
+    The returned plot object can be updated using one of the many helper
+    functions defined in PlotWindow.
+
+    Parameters
+    ----------
+
+    ds : :class:`yt.data_objects.static_output.Dataset`
+        This is the dataset object corresponding to the
+        simulation output to be plotted.
+    fields : string
+        The name of the field(s) to be plotted.
+    point1: tuple
+        Contains the coordinates of the first point for constructing the line
+    point2: tuple
+        Contains the coordinates of the second point for constructing the line
+    resolution: int
+        How many points to sample between point1 and point2 for constructing 
+        the line plot
+    """
+
+    def __init__(self, ds, fields, point1, point2, resolution):
+        self.ds = ds
+        self.handler = CartesianCoordinateHandler(self.ds)
+        # super(LinePlot, self).__init__(fsize, axrect, None, None)
+        super(LinePlot, self).__init__(None, None, None, None)
+        for field in fields:
+            x, y = self.handler.line_plot(field, np.asarray(point1, dtype='float64'),
+                                          np.asarray(point2, dtype='float64'), resolution)
+            self.ax.plot(x, y)
+        
+    


https://bitbucket.org/yt_analysis/yt/commits/8aa3a4319b7c/
Changeset:   8aa3a4319b7c
User:        Alex Lindsay
Date:        2017-06-05 18:52:24+00:00
Summary:     First test working
Affected #:  3 files

diff -r f2b2d87a28ed6d646a65f44d8fa23d1ce4cfe078 -r 8aa3a4319b7c047a70f1eb0bc8d80fd645df0b14 yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -66,7 +66,7 @@
 
     def pixelize(self, dimension, data_source, field, bounds, size,
                  antialias = True, periodic = True):
-        import pdb; pdb.set_trace()
+        # import pdb; pdb.set_trace()
         index = data_source.ds.index
         if (hasattr(index, 'meshes') and
            not isinstance(index.meshes[0], SemiStructuredMesh)):
@@ -122,6 +122,7 @@
                                           antialias)
 
     def line_plot(self, field, start_point, end_point, resolution):
+        import pdb; pdb.set_trace()
         index = self.ds.index
         if (hasattr(index, 'meshes') and
            not isinstance(index.meshes[0], SemiStructuredMesh)):
@@ -168,13 +169,13 @@
             period = period.in_units("code_length").d
 
         buff = np.zeros((size[1], size[0]), dtype="f8")
-        
+
         finfo = self.ds._get_field_info(field)
         nodal_flag = finfo.nodal_flag
         if np.any(nodal_flag):
             nodal_data = get_nodal_data(data_source, field)
             coord = data_source.coord.d
-            pixelize_cartesian_nodal(buff, 
+            pixelize_cartesian_nodal(buff,
                                      data_source['px'], data_source['py'], data_source['pz'],
                                      data_source['pdx'], data_source['pdy'], data_source['pdz'],
                                      nodal_data, coord, bounds, int(antialias),
@@ -186,7 +187,7 @@
                                bounds, int(antialias),
                                period, int(periodic))
         return buff
-            
+
     def _oblique_pixelize(self, data_source, field, bounds, size, antialias):
         indices = np.argsort(data_source['pdx'])[::-1].astype(np.int_)
         buff = np.zeros((size[1], size[0]), dtype="f8")

diff -r f2b2d87a28ed6d646a65f44d8fa23d1ce4cfe078 -r 8aa3a4319b7c047a70f1eb0bc8d80fd645df0b14 yt/visualization/base_plot_types.py
--- a/yt/visualization/base_plot_types.py
+++ b/yt/visualization/base_plot_types.py
@@ -50,7 +50,7 @@
 
 
 class CallbackWrapper(object):
-    def __init__(self, viewer, window_plot, frb, field, font_properties, 
+    def __init__(self, viewer, window_plot, frb, field, font_properties,
                  font_color):
         self.frb = frb
         self.data = frb.data_source
@@ -83,6 +83,7 @@
 
     def __init__(self, fsize, axrect, figure, axes):
         """Initialize PlotMPL class"""
+        import pdb; pdb.set_trace()
         import matplotlib.figure
         self._plot_valid = True
         if figure is None:
@@ -279,10 +280,10 @@
         x_frac_widths = xbins/size[0]
         y_frac_widths = ybins/size[1]
 
-        # axrect is the rectangle defining the area of the 
-        # axis object of the plot.  Its range goes from 0 to 1 in 
-        # x and y directions.  The first two values are the x,y 
-        # start values of the axis object (lower left corner), and the 
+        # axrect is the rectangle defining the area of the
+        # axis object of the plot.  Its range goes from 0 to 1 in
+        # x and y directions.  The first two values are the x,y
+        # start values of the axis object (lower left corner), and the
         # second two values are the size of the axis object.  To get
         # the upper right corner, add the first x,y to the second x,y.
         axrect = (
@@ -452,5 +453,3 @@
             ax.clear()
             cbars.append(ax)
     return fig, tr, cbars
-
-

diff -r f2b2d87a28ed6d646a65f44d8fa23d1ce4cfe078 -r 8aa3a4319b7c047a70f1eb0bc8d80fd645df0b14 yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -661,6 +661,7 @@
     _data_valid = False
 
     def __init__(self, *args, **kwargs):
+        import pdb; pdb.set_trace()
         if self._frb_generator is None:
             self._frb_generator = kwargs.pop("frb_generator")
         if self._plot_type is None:
@@ -1037,7 +1038,7 @@
     @invalidate_plot
     def annotate_clear(self, index=None):
         """
-        Clear callbacks from the plot.  If index is not set, clear all 
+        Clear callbacks from the plot.  If index is not set, clear all
         callbacks.  If index is set, clear that index (ie 0 is the first one
         created, 1 is the 2nd one created, -1 is the last one created, etc.)
         """
@@ -1052,7 +1053,7 @@
         for f in self.fields:
             keys = self.frb.keys()
             for name, (args, kwargs) in self._callbacks:
-                cbw = CallbackWrapper(self, self.plots[f], self.frb, f, 
+                cbw = CallbackWrapper(self, self.plots[f], self.frb, f,
                                       self._font_properties, self._font_color)
                 CallbackMaker = callback_registry[name]
                 callback = CallbackMaker(*args[1:], **kwargs)
@@ -1070,8 +1071,8 @@
 
     def hide_colorbar(self, field=None):
         """
-        Hides the colorbar for a plot and updates the size of the 
-        plot accordingly.  Defaults to operating on all fields for a 
+        Hides the colorbar for a plot and updates the size of the
+        plot accordingly.  Defaults to operating on all fields for a
         PlotWindow object.
 
         Parameters
@@ -1111,8 +1112,8 @@
 
     def show_colorbar(self, field=None):
         """
-        Shows the colorbar for a plot and updates the size of the 
-        plot accordingly.  Defaults to operating on all fields for a 
+        Shows the colorbar for a plot and updates the size of the
+        plot accordingly.  Defaults to operating on all fields for a
         PlotWindow object.  See hide_colorbar().
 
         Parameters
@@ -1131,8 +1132,8 @@
 
     def hide_axes(self, field=None):
         """
-        Hides the axes for a plot and updates the size of the 
-        plot accordingly.  Defaults to operating on all fields for a 
+        Hides the axes for a plot and updates the size of the
+        plot accordingly.  Defaults to operating on all fields for a
         PlotWindow object.
 
         Parameters
@@ -1170,8 +1171,8 @@
 
     def show_axes(self, field=None):
         """
-        Shows the axes for a plot and updates the size of the 
-        plot accordingly.  Defaults to operating on all fields for a 
+        Shows the axes for a plot and updates the size of the
+        plot accordingly.  Defaults to operating on all fields for a
         PlotWindow object.  See hide_axes().
 
         Parameters
@@ -1285,7 +1286,7 @@
          A dictionary of field parameters than can be accessed by derived
          fields.
     data_source: YTSelectionContainer object
-         Object to be used for data selection. Defaults to ds.all_data(), a 
+         Object to be used for data selection. Defaults to ds.all_data(), a
          region covering the full domain
 
     Examples
@@ -1440,7 +1441,7 @@
     fontsize : integer
          The size of the fonts for the axis, colorbar, and tick labels.
     method : string
-         The method of projection.  Valid methods are: 
+         The method of projection.  Valid methods are:
 
          "integrate" with no weight_field specified : integrate the requested
          field along the line of sight.
@@ -1466,7 +1467,7 @@
          A dictionary of field parameters than can be accessed by derived
          fields.
     data_source: YTSelectionContainer object
-         Object to be used for data selection. Defaults to ds.all_data(), a 
+         Object to be used for data selection. Defaults to ds.all_data(), a
          region covering the full domain
 
     Examples
@@ -1492,7 +1493,7 @@
         self.ts = ts
         ds = self.ds = ts[0]
         axis = fix_axis(axis, ds)
-        # proj_style is deprecated, but if someone specifies then it trumps 
+        # proj_style is deprecated, but if someone specifies then it trumps
         # method.
         if proj_style is not None:
             method = proj_style
@@ -1521,7 +1522,7 @@
                            field_parameters=field_parameters, method=method,
                            max_level=max_level)
         PWViewerMPL.__init__(self, proj, bounds, fields=fields, origin=origin,
-                             right_handed=right_handed, fontsize=fontsize, window_size=window_size, 
+                             right_handed=right_handed, fontsize=fontsize, window_size=window_size,
                              aspect=aspect)
         if axes_unit is None:
             axes_unit = get_axes_unit(width, ds)
@@ -1595,7 +1596,7 @@
          A dictionary of field parameters than can be accessed by derived
          fields.
     data_source : YTSelectionContainer Object
-         Object to be used for data selection.  Defaults ds.all_data(), a 
+         Object to be used for data selection.  Defaults ds.all_data(), a
          region covering the full domain.
     """
 
@@ -1750,7 +1751,7 @@
          This should only be used for uniform resolution grid datasets, as other
          datasets may result in unphysical images.
     data_source: YTSelectionContainer object
-         Object to be used for data selection. Defaults to ds.all_data(), a 
+         Object to be used for data selection. Defaults to ds.all_data(), a
          region covering the full domain
     """
     _plot_type = 'OffAxisProjection'
@@ -2042,18 +2043,57 @@
     point2: tuple
         Contains the coordinates of the second point for constructing the line
     resolution: int
-        How many points to sample between point1 and point2 for constructing 
+        How many points to sample between point1 and point2 for constructing
         the line plot
     """
 
-    def __init__(self, ds, fields, point1, point2, resolution):
-        self.ds = ds
-        self.handler = CartesianCoordinateHandler(self.ds)
+    def __init__(self, ds, fields, point1, point2, resolution,
+                 figure_size=5., aspect=None, fontsize=18.):
+        handler = CartesianCoordinateHandler(ds)
+        if not isinstance(fields, list):
+            fields = [fields]
+        if aspect is None:
+            aspect = 1.
+        fontscale = fontsize / 18.
+        ax_text_size = [1.2*fontscale, 0.9*fontscale]
+        top_buff_size = 0.3*fontscale
+
+        if iterable(figure_size):
+            x_fig_size = figure_size[0]
+            y_fig_size = figure_size[1]
+        else:
+            x_fig_size = figure_size
+            y_fig_size = figure_size/aspect
+
+        x_axis_size = ax_text_size[0]
+        y_axis_size = ax_text_size[1]
+
+        top_buff_size = top_buff_size
+
+        xbins = np.array([x_axis_size, x_fig_size, x_axis_size])
+        ybins = np.array([y_axis_size, y_fig_size, top_buff_size])
+
+        size = [xbins.sum(), ybins.sum()]
+
+        x_frac_widths = xbins/size[0]
+        y_frac_widths = ybins/size[1]
+
+        # axrect is the rectangle defining the area of the
+        # axis object of the plot.  Its range goes from 0 to 1 in
+        # x and y directions.  The first two values are the x,y
+        # start values of the axis object (lower left corner), and the
+        # second two values are the size of the axis object.  To get
+        # the upper right corner, add the first x,y to the second x,y.
+        axrect = (
+            x_frac_widths[0],
+            y_frac_widths[0],
+            x_frac_widths[1],
+            y_frac_widths[1],
+        )
+
         # super(LinePlot, self).__init__(fsize, axrect, None, None)
-        super(LinePlot, self).__init__(None, None, None, None)
+        super(LinePlot, self).__init__(size, axrect, None, None)
         for field in fields:
-            x, y = self.handler.line_plot(field, np.asarray(point1, dtype='float64'),
+            x, y = handler.line_plot(field, np.asarray(point1, dtype='float64'),
                                           np.asarray(point2, dtype='float64'), resolution)
-            self.ax.plot(x, y)
-        
-    
+            self.axes.plot(x, y)


https://bitbucket.org/yt_analysis/yt/commits/4ca701abb425/
Changeset:   4ca701abb425
User:        Alex Lindsay
Date:        2017-06-05 19:22:53+00:00
Summary:     Add some documentation
Affected #:  4 files

diff -r 8aa3a4319b7c047a70f1eb0bc8d80fd645df0b14 -r 4ca701abb425577ab00ecd0e899d3a63e4c7418b yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -66,7 +66,11 @@
 
     def pixelize(self, dimension, data_source, field, bounds, size,
                  antialias = True, periodic = True):
-        # import pdb; pdb.set_trace()
+        """
+        Method for pixelizing grid data sets in preparation for
+        two-dimensional image plots. Relies on several sampling
+        routines written in cython
+        """
         index = data_source.ds.index
         if (hasattr(index, 'meshes') and
            not isinstance(index.meshes[0], SemiStructuredMesh)):
@@ -121,8 +125,14 @@
             return self._oblique_pixelize(data_source, field, bounds, size,
                                           antialias)
 
+
     def line_plot(self, field, start_point, end_point, resolution):
-        import pdb; pdb.set_trace()
+        """
+        Method for sampling grid data sets along a line in preparation for
+        one-dimensional line plots. For UnstructuredMesh, relies on a
+        sampling routine written in cython
+        """
+
         index = self.ds.index
         if (hasattr(index, 'meshes') and
            not isinstance(index.meshes[0], SemiStructuredMesh)):
@@ -158,6 +168,10 @@
 
             return arc_length, plot_values
 
+        else:
+            raise NotImplementedError("Currently line plotting routines have only "
+                                      "been implemented for unstructured meshes.")
+
 
     def _ortho_pixelize(self, data_source, field, bounds, size, antialias,
                         dim, periodic):

diff -r 8aa3a4319b7c047a70f1eb0bc8d80fd645df0b14 -r 4ca701abb425577ab00ecd0e899d3a63e4c7418b yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -254,7 +254,7 @@
     cdef int lc, lr, rc, rr
     cdef np.float64_t lypx, rypx, lxpx, rxpx, overlap1, overlap2
     # These are the temp vars we get from the arrays
-    cdef np.float64_t oxsp, oysp, ozsp 
+    cdef np.float64_t oxsp, oysp, ozsp
     cdef np.float64_t xsp, ysp, zsp
     cdef np.float64_t dxsp, dysp, dzsp
     # Some periodicity helpers
@@ -303,7 +303,7 @@
     # (lr) and then iterate up to "right column" (rc) and "uppeR row" (rr),
     # depositing into them the data value.  Overlap computes the relative
     # overlap of a data value with a pixel.
-    # 
+    #
     # NOTE ON ROWS AND COLUMNS:
     #
     #   The way that images are plotting in matplotlib is somewhat different
@@ -871,6 +871,8 @@
                            np.ndarray[np.float64_t, ndim=2] field,
                            int index_offset = 0):
 
+    # This routine chooses the correct element sampler to interpolate field
+    # values at evenly spaced points along a sampling line
     cdef np.float64_t *vertices
     cdef np.float64_t *field_vals
     cdef int nvertices = conn.shape[1]
@@ -955,8 +957,8 @@
             if not sampler.check_inside(mapped_coord) and ci != conn.shape[0] - 1:
                 continue
             elif not sampler.check_inside(mapped_coord):
-                raise RuntimeError("It's impossible that the line point doesn't "
-                                   "fall within any of the elements.")
+                raise ValueError("Check to see that both starting and ending line points "
+                                 "are within the domain of the mesh.")
             plot_values[i] = sampler.sample_at_unit_point(mapped_coord, field_vals)
             break
 

diff -r 8aa3a4319b7c047a70f1eb0bc8d80fd645df0b14 -r 4ca701abb425577ab00ecd0e899d3a63e4c7418b yt/visualization/base_plot_types.py
--- a/yt/visualization/base_plot_types.py
+++ b/yt/visualization/base_plot_types.py
@@ -83,7 +83,6 @@
 
     def __init__(self, fsize, axrect, figure, axes):
         """Initialize PlotMPL class"""
-        import pdb; pdb.set_trace()
         import matplotlib.figure
         self._plot_valid = True
         if figure is None:

diff -r 8aa3a4319b7c047a70f1eb0bc8d80fd645df0b14 -r 4ca701abb425577ab00ecd0e899d3a63e4c7418b yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -661,7 +661,6 @@
     _data_valid = False
 
     def __init__(self, *args, **kwargs):
-        import pdb; pdb.set_trace()
         if self._frb_generator is None:
             self._frb_generator = kwargs.pop("frb_generator")
         if self._plot_type is None:
@@ -2020,15 +2019,7 @@
 
 class LinePlot(PlotMPL):
     r"""
-    A factory function for
-    :class:`yt.visualization.plot_window.AxisAlignedSlicePlot`
-    and :class:`yt.visualization.plot_window.OffAxisSlicePlot` objects.  This
-    essentially allows for a single entry point to both types of slice plots,
-    the distinction being determined by the specified normal vector to the
-    slice.
-
-    The returned plot object can be updated using one of the many helper
-    functions defined in PlotWindow.
+    A class for constructing line plots
 
     Parameters
     ----------
@@ -2037,7 +2028,7 @@
         This is the dataset object corresponding to the
         simulation output to be plotted.
     fields : string
-        The name of the field(s) to be plotted.
+        The name(s) of the field(s) to be plotted.
     point1: tuple
         Contains the coordinates of the first point for constructing the line
     point2: tuple


https://bitbucket.org/yt_analysis/yt/commits/41338deb4c96/
Changeset:   41338deb4c96
User:        Alex Lindsay
Date:        2017-06-05 23:20:39+00:00
Summary:     Add show method, documentation, and answer test.
Affected #:  5 files

diff -r 4ca701abb425577ab00ecd0e899d3a63e4c7418b -r 41338deb4c9629c22d03b4f4fe6d6196617c09ff doc/source/cookbook/simple_plots.rst
--- a/doc/source/cookbook/simple_plots.rst
+++ b/doc/source/cookbook/simple_plots.rst
@@ -237,3 +237,11 @@
 When creating movies of multiple outputs from the same simulation (see :ref:`time-series-analysis`), it can be helpful to include a timestamp and the physical scale of each individual output.  This is simply achieved using the :ref:`annotate_timestamp() <annotate-timestamp>` and :ref:`annotate_scale() <annotate-scale>` callbacks on your plots.  For more information about similar plot modifications using other callbacks, see the section on :ref:`Plot Modifications <callbacks>`.
 
 .. yt_cookbook:: annotate_timestamp_and_scale.py
+
+Simple 1D Unstructured Mesh Line Plotting
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This script shows how to make a `LinePlot` through an `UnstructuredMesh`
+data-set. See :ref:`how-to-1d-unstructured-mesh` for more information.
+
+.. yt_cookbook:: simple_unstructured_1d.py

diff -r 4ca701abb425577ab00ecd0e899d3a63e4c7418b -r 41338deb4c9629c22d03b4f4fe6d6196617c09ff doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -30,7 +30,7 @@
 in other environments as well:
 
 .. code-block:: python
- 
+
    %matplotlib notebook
    import yt
    yt.toggle_interactivity()
@@ -943,6 +943,7 @@
    # Save the image.
    plot.save()
 
+
 Customizing axis limits
 ~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -1054,6 +1055,45 @@
     # change only the first line
     plot.set_line_property("linestyle", "--", 0)
 
+.. _how-to-1d-unstructured-mesh:
+
+1D Sampling on Unstructured Meshes
+----------------------------------
+
+YT has the ability to sample unstructured mesh data-sets along arbitrary lines
+and plot the result. You must supply five arguments to the `LinePlot`
+class. They are enumerated below:
+
+1. Data-set
+2. A list of fields or a single field you wish to plot
+3. The starting point of the sampling line. This should be a tuple of three
+   floats corresponding to the coordinates of the starting point
+4. The ending point of the sampling line. This should also be a tuple of three
+   floats
+5. The resolution of the sampling line. This is the number of sampling points
+   along the line, e.g. if 1000 is specified, then data will be sampled at
+   1000 points evenly spaced between the starting and ending points.
+
+The below code snippet illustrates how this is done:
+
+.. python-script::
+
+   ds = yt.load(home + "/yt_data/SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1)
+   ln = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], (0, 0, 0), (0, 1, 0), 1000)
+   ln.save("first_test.png")
+
+You can also add plots to existing `LinePlot` instances with `add_plot` as shown
+below:
+
+.. python-script::
+
+   ln.add_plot(('all', 'p'), (0, 0, 0), (1, 1, 0), 1000)
+   ln.save("added_plot.png")
+
+Note that the beginning and end-points of multiple plots can be different. If
+working in an IPython Notebook, `LinePlot` also has the `show()` method.
+
+
 .. _how-to-make-2d-profiles:
 
 2D Phase Plots

diff -r 4ca701abb425577ab00ecd0e899d3a63e4c7418b -r 41338deb4c9629c22d03b4f4fe6d6196617c09ff tests/tests.yaml
--- a/tests/tests.yaml
+++ b/tests/tests.yaml
@@ -69,6 +69,7 @@
     - yt/visualization/tests/test_mesh_slices.py:test_tri2
     - yt/visualization/tests/test_mesh_slices.py:test_quad2
     - yt/visualization/tests/test_mesh_slices.py:test_multi_region
+    - yt/visualization/tests/test_line_plots.py:test_line_plot
 
   local_boxlib_004:
     - yt/frontends/boxlib/tests/test_outputs.py:test_radadvect

diff -r 4ca701abb425577ab00ecd0e899d3a63e4c7418b -r 41338deb4c9629c22d03b4f4fe6d6196617c09ff yt/visualization/plot_container.py
--- a/yt/visualization/plot_container.py
+++ b/yt/visualization/plot_container.py
@@ -230,10 +230,10 @@
         for field in self.data_source._determine_fields(fields):
             if log:
                 if linthresh is not None:
-                    if not linthresh > 0.: 
+                    if not linthresh > 0.:
                         raise ValueError('\"linthresh\" must be positive')
                     self._field_transform[field] = symlog_transform
-                    self._field_transform[field].func = linthresh 
+                    self._field_transform[field].func = linthresh
                 else:
                     self._field_transform[field] = log_transform
             else:
@@ -306,7 +306,7 @@
             if field == 'all', applies to all plots.
         color : string or RGBA tuple (optional)
             if set, set the background color to this color
-            if unset, background color is set to the bottom value of 
+            if unset, background color is set to the bottom value of
             the color map
 
         """
@@ -402,7 +402,7 @@
     def set_cbar_minorticks(self, field, state):
         """turn colorbar minor ticks on or off in the current plot
 
-        Displaying minor ticks reduces performance; turn them off 
+        Displaying minor ticks reduces performance; turn them off
         using set_cbar_minorticks('all', 'off') if drawing speed is a problem.
 
         Parameters
@@ -471,7 +471,7 @@
         ----------
 
         font_dict : dict
-            A dict of keyword parameters to be passed to 
+            A dict of keyword parameters to be passed to
             :class:`matplotlib.font_manager.FontProperties`.
 
             Possible keys include:

diff -r 4ca701abb425577ab00ecd0e899d3a63e4c7418b -r 41338deb4c9629c22d03b4f4fe6d6196617c09ff yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -67,7 +67,7 @@
     YTPlotCallbackError, \
     YTDataTypeUnsupported, \
     YTInvalidFieldType
-from yt.geometry.coordinates.cartesian_coordinates import CartesianCoordinateHandler
+from yt.extern.six.moves import builtins
 
 MPL_VERSION = LooseVersion(matplotlib.__version__)
 
@@ -1119,8 +1119,7 @@
         ----------
 
         field : string, field tuple, or list of strings or field tuples (optional)
-            The name of the field(s) that we want to show
- the colorbar.
+            The name of the field(s) that we want to show the colorbar.
         """
         if field is None:
             field = self.fields
@@ -2040,9 +2039,10 @@
 
     def __init__(self, ds, fields, point1, point2, resolution,
                  figure_size=5., aspect=None, fontsize=18.):
-        handler = CartesianCoordinateHandler(ds)
-        if not isinstance(fields, list):
-            fields = [fields]
+        """
+        Sets up figure and axes
+        """
+        self.handler = ds.coordinates
         if aspect is None:
             aspect = 1.
         fontscale = fontsize / 18.
@@ -2082,9 +2082,30 @@
             y_frac_widths[1],
         )
 
-        # super(LinePlot, self).__init__(fsize, axrect, None, None)
         super(LinePlot, self).__init__(size, axrect, None, None)
+        self.add_plot(fields, point1, point2, resolution)
+
+    def add_plot(self, fields, point1, point2, resolution):
+        r"""
+        Used to add plots to the figure
+        """
+        if not isinstance(fields, list):
+            fields = [fields]
         for field in fields:
-            x, y = handler.line_plot(field, np.asarray(point1, dtype='float64'),
+            x, y = self.handler.line_plot(field, np.asarray(point1, dtype='float64'),
                                           np.asarray(point2, dtype='float64'), resolution)
             self.axes.plot(x, y)
+
+    def show(self):
+        r"""This will send any existing plots to the IPython notebook.
+
+        If yt is being run from within an IPython session, and it is able to
+        determine this, this function will send any existing plots to the
+        notebook for display.
+
+        If yt can't determine if it's inside an IPython session, it will raise
+        YTNotInsideNotebook.
+        """
+        if "__IPYTHON__" in dir(builtins):
+            from IPython.display import display
+            display(self)


https://bitbucket.org/yt_analysis/yt/commits/1138d932bf4b/
Changeset:   1138d932bf4b
User:        Alex Lindsay
Date:        2017-06-06 13:54:35+00:00
Summary:     Add YTNotInsideNotebook exceptions
Affected #:  2 files

diff -r 41338deb4c9629c22d03b4f4fe6d6196617c09ff -r 1138d932bf4b92e02475b76b9eaa3249b0a3bd65 yt/visualization/plot_container.py
--- a/yt/visualization/plot_container.py
+++ b/yt/visualization/plot_container.py
@@ -657,6 +657,8 @@
             if "__IPYTHON__" in dir(builtins):
                 from IPython.display import display
                 display(self)
+            else:
+                raise YTNotInsideNotebook
 
     @validate_plot
     def display(self, name=None, mpl_kwargs=None):

diff -r 41338deb4c9629c22d03b4f4fe6d6196617c09ff -r 1138d932bf4b92e02475b76b9eaa3249b0a3bd65 yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -66,7 +66,8 @@
     YTUnitConversionError, \
     YTPlotCallbackError, \
     YTDataTypeUnsupported, \
-    YTInvalidFieldType
+    YTInvalidFieldType, \
+    YTNotInsideNotebook
 from yt.extern.six.moves import builtins
 
 MPL_VERSION = LooseVersion(matplotlib.__version__)
@@ -2109,3 +2110,5 @@
         if "__IPYTHON__" in dir(builtins):
             from IPython.display import display
             display(self)
+        else:
+            raise YTNotInsideNotebook


https://bitbucket.org/yt_analysis/yt/commits/07ed5d34c300/
Changeset:   07ed5d34c300
User:        Alex Lindsay
Date:        2017-06-06 15:02:46+00:00
Summary:     Add legend and label methods
Affected #:  3 files

diff -r 1138d932bf4b92e02475b76b9eaa3249b0a3bd65 -r 07ed5d34c30032208c27d129cce8ff1d61f8da8c doc/source/cookbook/simple_plots.rst
--- a/doc/source/cookbook/simple_plots.rst
+++ b/doc/source/cookbook/simple_plots.rst
@@ -241,7 +241,7 @@
 Simple 1D Unstructured Mesh Line Plotting
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-This script shows how to make a `LinePlot` through an `UnstructuredMesh`
+This script shows how to make a ``LinePlot`` through an ``UnstructuredMesh``
 data-set. See :ref:`how-to-1d-unstructured-mesh` for more information.
 
 .. yt_cookbook:: simple_unstructured_1d.py

diff -r 1138d932bf4b92e02475b76b9eaa3249b0a3bd65 -r 07ed5d34c30032208c27d129cce8ff1d61f8da8c doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -1061,7 +1061,7 @@
 ----------------------------------
 
 YT has the ability to sample unstructured mesh data-sets along arbitrary lines
-and plot the result. You must supply five arguments to the `LinePlot`
+and plot the result. You must supply five arguments to the ``LinePlot``
 class. They are enumerated below:
 
 1. Data-set
@@ -1082,7 +1082,7 @@
    ln = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], (0, 0, 0), (0, 1, 0), 1000)
    ln.save("first_test.png")
 
-You can also add plots to existing `LinePlot` instances with `add_plot` as shown
+You can also add plots to existing ``LinePlot`` instances with ``add_plot`` as shown
 below:
 
 .. python-script::
@@ -1091,7 +1091,30 @@
    ln.save("added_plot.png")
 
 Note that the beginning and end-points of multiple plots can be different. If
-working in an IPython Notebook, `LinePlot` also has the `show()` method.
+working in an IPython Notebook, ``LinePlot`` also has the ``show()`` method.
+
+You can also create legends and add x and y axes labels to these 1D sampling
+plots. The legend process takes two steps:
+
+1. When instantiating the ``LinePlot`` or invoking ``add_plot`` pass a dictionary of
+   labels with keys corresponding to the field names
+2. Call the ``LinePlot`` ``add_legend`` method
+
+X- and Y- axis labels are set simply with ``set_xlabel`` and ``set_ylabel``
+methods. The below code snippet combines all the features we've discussed:
+
+.. python-script::
+
+   import yt
+   ds = yt.load("SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1)
+   ln = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], (0, 0, 0), (0, 1, 0), 1000,
+                    labels={('all', 'u') : r"u$_s$", ('all', 'v') : r"v$_s$"})
+   ln.add_plot([('all', 'v'), ('all', 'u')], (0, 0, 0), (1, 1, 0), 1000,
+               labels={('all', 'u') : r"u$_l$", ('all', 'v') : r"v$_l$"})
+   ln.add_legend()
+   ln.set_xlabel("Arc Length (cm)")
+   ln.set_ylabel(r"Velocity (m s$^{-1}$)")
+   ln.save("line_plot.eps")
 
 
 .. _how-to-make-2d-profiles:

diff -r 1138d932bf4b92e02475b76b9eaa3249b0a3bd65 -r 07ed5d34c30032208c27d129cce8ff1d61f8da8c yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -2039,7 +2039,7 @@
     """
 
     def __init__(self, ds, fields, point1, point2, resolution,
-                 figure_size=5., aspect=None, fontsize=18.):
+                 figure_size=5., aspect=None, fontsize=18., labels={}):
         """
         Sets up figure and axes
         """
@@ -2084,18 +2084,35 @@
         )
 
         super(LinePlot, self).__init__(size, axrect, None, None)
-        self.add_plot(fields, point1, point2, resolution)
+        self.add_plot(fields, point1, point2, resolution, labels=labels)
+        self._xlabel = ("Arc Length [Arb. Units]", 14.)
+        self._ylabel = ("Field Value [Arb. Units]", 14.)
 
-    def add_plot(self, fields, point1, point2, resolution):
+    def add_plot(self, fields, point1, point2, resolution, labels={}):
         r"""
         Used to add plots to the figure
         """
         if not isinstance(fields, list):
             fields = [fields]
         for field in fields:
+            if field not in labels:
+                labels[field] = field[1]
             x, y = self.handler.line_plot(field, np.asarray(point1, dtype='float64'),
                                           np.asarray(point2, dtype='float64'), resolution)
-            self.axes.plot(x, y)
+            self.axes.plot(x, y, label=labels[field])
+
+    def add_legend(self):
+        self.axes.legend()
+
+    def set_xlabel(self, label, fontsize=14):
+        self._xlabel = (label, fontsize)
+
+    def set_ylabel(self, label, fontsize=14):
+        self._ylabel = (label, fontsize)
+
+    def _setup_plot(self):
+        self.axes.set_xlabel(self._xlabel[0], fontsize=self._xlabel[1])
+        self.axes.set_ylabel(self._ylabel[0], fontsize=self._ylabel[1])
 
     def show(self):
         r"""This will send any existing plots to the IPython notebook.
@@ -2107,8 +2124,13 @@
         If yt can't determine if it's inside an IPython session, it will raise
         YTNotInsideNotebook.
         """
+        self._setup_plot()
         if "__IPYTHON__" in dir(builtins):
             from IPython.display import display
             display(self)
         else:
             raise YTNotInsideNotebook
+
+    def save(self, name):
+        self._setup_plot()
+        super(LinePlot, self).save(name)


https://bitbucket.org/yt_analysis/yt/commits/cdc638ea224d/
Changeset:   cdc638ea224d
User:        Alex Lindsay
Date:        2017-06-06 15:06:36+00:00
Summary:     Add new files
Affected #:  2 files

diff -r 07ed5d34c30032208c27d129cce8ff1d61f8da8c -r cdc638ea224dbb4b404a358c83d10a151f6a7954 doc/source/cookbook/simple_unstructured_1d.py
--- /dev/null
+++ b/doc/source/cookbook/simple_unstructured_1d.py
@@ -0,0 +1,24 @@
+import yt
+
+# Load the dataset
+ds = yt.load("SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1)
+
+# Create a line plot of the variables 'u' and 'v' with 1000 sampling points evenly spaced
+# between the coordinates (0, 0, 0) and (0, 1, 0)
+ln = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], (0, 0, 0), (0, 1, 0), 1000)
+
+# Add a plot to the LinePlot instance of the variable 'p' with 100 sampling points evenly
+# spaced between (0.5, 0, 0) and (1, 1, 0). Also create a label for p
+ln.add_plot(('all', 'p'), (0.5, 0, 0), (1, 1, 0), 100, labels={('all', 'p') : 'p'})
+
+# Add a legend
+ln.add_legend()
+
+# Set xlabel
+ln.set_xlabel("Arc Length (cm)")
+
+# Set ylabel
+ln.set_ylabel("Field Values [Arb. Units]")
+
+# Save the line plot. Note that the save string is a required argument
+ln.save("line_plot.png")

diff -r 07ed5d34c30032208c27d129cce8ff1d61f8da8c -r cdc638ea224dbb4b404a358c83d10a151f6a7954 yt/visualization/tests/test_line_plots.py
--- /dev/null
+++ b/yt/visualization/tests/test_line_plots.py
@@ -0,0 +1,41 @@
+"""
+Tests for making line plots
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2017, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+import yt
+from yt.utilities.answer_testing.framework import \
+    requires_ds, \
+    data_dir_load, \
+    GenericImageTest
+
+def setup():
+    """Test specific setup."""
+    from yt.config import ytcfg
+    ytcfg["yt", "__withintesting"] = "True"
+
+def compare(ds, fields, point1, point2, resolution, test_prefix, decimals=12):
+    def line_plot(filename_prefix):
+        ln = yt.LinePlot(ds, fields, point1, point2, resolution)
+        image_file = ln.save(filename_prefix)
+        return image_file
+
+    line_plot.__name__ = "line_{}".format(test_prefix)
+    test = GenericImageTest(ds, line_plot, decimals)
+    test.prefix = test_prefix
+    return test
+
+tri2 = "SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e"
+
+ at requires_ds(tri2)
+def test_line_plot():
+    ds = data_dir_load(tri2, kwargs={'step':-1})
+    fields = [field for field in ds.field_list if field[0] == 'all']
+    yield compare(ds, fields, (0, 0, 0), (1, 1, 0), 1000, "answers_line_plot")


https://bitbucket.org/yt_analysis/yt/commits/583f58085559/
Changeset:   583f58085559
User:        Alex Lindsay
Date:        2017-06-08 20:17:00+00:00
Summary:     No other failures. Increment unstructured counter.
Affected #:  1 file

diff -r cdc638ea224dbb4b404a358c83d10a151f6a7954 -r 583f5808555944565a6de1ad073ac5141cc98ef2 tests/tests.yaml
--- a/tests/tests.yaml
+++ b/tests/tests.yaml
@@ -64,7 +64,7 @@
     - yt/analysis_modules/photon_simulator/tests/test_spectra.py
     - yt/analysis_modules/photon_simulator/tests/test_sloshing.py
 
-  local_unstructured_005:
+  local_unstructured_006:
     - yt/visualization/volume_rendering/tests/test_mesh_render.py
     - yt/visualization/tests/test_mesh_slices.py:test_tri2
     - yt/visualization/tests/test_mesh_slices.py:test_quad2


https://bitbucket.org/yt_analysis/yt/commits/07c8c38b97de/
Changeset:   07c8c38b97de
User:        ngoldbaum
Date:        2017-06-26 17:18:42+00:00
Summary:     make LinePlot work with cartesian AMR data as well
Affected #:  3 files

diff -r 583f5808555944565a6de1ad073ac5141cc98ef2 -r 07c8c38b97deee212c083c3e96cd18fd15be2d0a yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -22,6 +22,7 @@
     cartesian_to_cylindrical, \
     cylindrical_to_cartesian
 from yt.funcs import mylog
+from yt.units.yt_array import uvstack
 from yt.utilities.lib.pixelization_routines import \
     pixelize_element_mesh, pixelize_off_axis_cartesian, \
     pixelize_cartesian, pixelize_cartesian_nodal, \
@@ -29,6 +30,26 @@
 from yt.data_objects.unstructured_mesh import SemiStructuredMesh
 from yt.utilities.nodal_data_utils import get_nodal_data
 
+def _sample_ray(ray, resolution, field):
+    start_point = ray.start_point
+    end_point = ray.end_point
+    sample_dr = (end_point - start_point)/(resolution-1)
+    sample_points = [np.arange(resolution)*sample_dr[i] for i in
+                     range(ray.ds.dimensionality)]
+    sample_points = uvstack(sample_points).T
+    ray_coordinates = uvstack([ray[d] for d in 'xyz']).T
+    ray_dds = uvstack([ray['d'+d] for d in 'xyz']).T
+    field_values = np.zeros(resolution)
+    ray_field = ray[field]
+    for i, sample_point in enumerate(sample_points):
+        ray_contains = ((sample_point >= (ray_coordinates - ray_dds/2)) &
+                        (sample_point <= (ray_coordinates + ray_dds/2)))
+        ray_contains = ray_contains.all(axis=-1)
+        wh = np.where(ray_contains)[0]
+        if wh.shape != (1,):
+            raise RuntimeError
+        field_values[i] = ray_field[wh]
+    return sample_points, field_values
 
 class CartesianCoordinateHandler(CoordinateHandler):
     name = "cartesian"
@@ -165,12 +186,11 @@
                                                              end_point,
                                                              resolution, field_data,
                                                              index_offset=offset)
-
-            return arc_length, plot_values
+        else:
+            ray = self.ds.ray(start_point, end_point)
+            arc_length, plot_values = _sample_ray(ray, resolution, field)
+        return arc_length, plot_values
 
-        else:
-            raise NotImplementedError("Currently line plotting routines have only "
-                                      "been implemented for unstructured meshes.")
 
 
     def _ortho_pixelize(self, data_source, field, bounds, size, antialias,

diff -r 583f5808555944565a6de1ad073ac5141cc98ef2 -r 07c8c38b97deee212c083c3e96cd18fd15be2d0a yt/utilities/lib/fixed_interpolator.pxd
--- a/yt/utilities/lib/fixed_interpolator.pxd
+++ b/yt/utilities/lib/fixed_interpolator.pxd
@@ -30,4 +30,3 @@
                        np.float64_t vl[3], np.float64_t dds[3],
                        np.float64_t x, np.float64_t y, np.float64_t z,
                        int vind1, int vind2) nogil
-

diff -r 583f5808555944565a6de1ad073ac5141cc98ef2 -r 07c8c38b97deee212c083c3e96cd18fd15be2d0a yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -14,6 +14,7 @@
 # The full license is in the file COPYING.txt, distributed with this software.
 #-----------------------------------------------------------------------------
 import numpy as np
+import os
 import matplotlib
 import types
 import six
@@ -45,7 +46,8 @@
     YTSpatialPlotDataset
 from yt.funcs import \
     mylog, iterable, ensure_list, \
-    fix_axis, fix_unitary
+    fix_axis, fix_unitary, ensure_dir, \
+    get_image_suffix
 from yt.units.unit_object import \
     Unit
 from yt.units.unit_registry import \
@@ -2017,6 +2019,13 @@
 
         return AxisAlignedSlicePlot(ds, normal, fields, *args, **kwargs)
 
+def _validate_point(point, ds):
+    if not iterable(point):
+        raise RuntimeError
+    if not isinstance(point, YTArray):
+        point = ds.arr(point, 'code_length')
+    return point
+
 class LinePlot(PlotMPL):
     r"""
     A class for constructing line plots
@@ -2029,21 +2038,24 @@
         simulation output to be plotted.
     fields : string
         The name(s) of the field(s) to be plotted.
-    point1: tuple
-        Contains the coordinates of the first point for constructing the line
-    point2: tuple
-        Contains the coordinates of the second point for constructing the line
+    start_point: n-element list, tuple, ndarray, or YTArray
+        Contains the coordinates of the first point for constructing the line.
+        Must contain n elements where n is the dimensionality of the dataset.
+    end_point: n-element list, tuple, ndarray, or YTArray
+        Contains the coordinates of the first point for constructing the line.
+        Must contain n elements where n is the dimensionality of the dataset.
     resolution: int
-        How many points to sample between point1 and point2 for constructing
+        How many points to sample between start_point and end_point for constructing
         the line plot
     """
 
-    def __init__(self, ds, fields, point1, point2, resolution,
+    def __init__(self, ds, fields, start_point, end_point, resolution,
                  figure_size=5., aspect=None, fontsize=18., labels={}):
         """
         Sets up figure and axes
         """
         self.handler = ds.coordinates
+        self.ds = ds
         if aspect is None:
             aspect = 1.
         fontscale = fontsize / 18.
@@ -2083,12 +2095,16 @@
             y_frac_widths[1],
         )
 
+        start_point = _validate_point(start_point, ds)
+        end_point = _validate_point(end_point, ds)
+
         super(LinePlot, self).__init__(size, axrect, None, None)
-        self.add_plot(fields, point1, point2, resolution, labels=labels)
+
+        self.add_plot(fields, start_point, end_point, resolution, labels=labels)
         self._xlabel = ("Arc Length [Arb. Units]", 14.)
         self._ylabel = ("Field Value [Arb. Units]", 14.)
 
-    def add_plot(self, fields, point1, point2, resolution, labels={}):
+    def add_plot(self, fields, start_point, end_point, resolution, labels={}):
         r"""
         Used to add plots to the figure
         """
@@ -2097,8 +2113,8 @@
         for field in fields:
             if field not in labels:
                 labels[field] = field[1]
-            x, y = self.handler.line_plot(field, np.asarray(point1, dtype='float64'),
-                                          np.asarray(point2, dtype='float64'), resolution)
+            x, y = self.handler.line_plot(
+                field, start_point, end_point, resolution)
             self.axes.plot(x, y, label=labels[field])
 
     def add_legend(self):
@@ -2131,6 +2147,16 @@
         else:
             raise YTNotInsideNotebook
 
-    def save(self, name):
+    def save(self, name=None, mpl_kwargs=None):
         self._setup_plot()
+        if name is None:
+            name = str(self.ds)
+        name = os.path.expanduser(name)
+        if name[-1] == os.sep and not os.path.isdir(name):
+            ensure_dir(name)
+        if os.path.isdir(name) and name != str(self.ds):
+            name = name + (os.sep if name[-1] != os.sep else '') + str(self.ds)
+        suffix = get_image_suffix(name)
+        if suffix == '':
+            name = name + '_LinePlot.png'
         super(LinePlot, self).save(name)


https://bitbucket.org/yt_analysis/yt/commits/db0f96ce2051/
Changeset:   db0f96ce2051
User:        Alex Lindsay
Date:        2017-06-26 17:31:14+00:00
Summary:     Address components of Nathan review.

- Add additional documentation
- Change python-script to code-block python
- Remove empty dictionary as default argument
- Add test for class methods
Affected #:  4 files

diff -r 583f5808555944565a6de1ad073ac5141cc98ef2 -r db0f96ce205128988d5c67e860a4c88ff96de237 doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -14,8 +14,8 @@
 
 The :class:`~yt.visualization.plot_window.PlotWindow` interface is useful for
 taking a quick look at simulation outputs.  Simple mechanisms exist for making
-plots of slices, projections, 1D profiles, and 2D profiles (phase plots), all of
-which are described below.
+plots of slices, projections, 1D spatial line plots, 1D profiles, and 2D
+profiles (phase plots), all of which are described below.
 
 .. _viewing-plots:
 
@@ -1064,34 +1064,36 @@
 and plot the result. You must supply five arguments to the ``LinePlot``
 class. They are enumerated below:
 
-1. Data-set
+1. Dataset
 2. A list of fields or a single field you wish to plot
-3. The starting point of the sampling line. This should be a tuple of three
-   floats corresponding to the coordinates of the starting point
-4. The ending point of the sampling line. This should also be a tuple of three
-   floats
+3. The starting point of the sampling line. This should be an n-element list, tuple,
+   ndarray, or YTArray with the elements corresponding to the coordinates of the
+   starting point. (n should equal the dimension of the dataset)
+4. The ending point of the sampling line. This should also be an n-element list, tuple,
+   ndarray, or YTArray with the elements corresponding to the coordinates of the
+   ending point.
 5. The resolution of the sampling line. This is the number of sampling points
    along the line, e.g. if 1000 is specified, then data will be sampled at
    1000 points evenly spaced between the starting and ending points.
 
 The below code snippet illustrates how this is done:
 
-.. python-script::
+.. code-block:: python
 
-   ds = yt.load(home + "/yt_data/SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1)
+   ds = yt.load("SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1)
    ln = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], (0, 0, 0), (0, 1, 0), 1000)
    ln.save("first_test.png")
 
 You can also add plots to existing ``LinePlot`` instances with ``add_plot`` as shown
 below:
 
-.. python-script::
+.. code-block:: python
 
    ln.add_plot(('all', 'p'), (0, 0, 0), (1, 1, 0), 1000)
    ln.save("added_plot.png")
 
 Note that the beginning and end-points of multiple plots can be different. If
-working in an IPython Notebook, ``LinePlot`` also has the ``show()`` method.
+working in an Jupyter Notebook, ``LinePlot`` also has the ``show()`` method.
 
 You can also create legends and add x and y axes labels to these 1D sampling
 plots. The legend process takes two steps:
@@ -1114,7 +1116,7 @@
    ln.add_legend()
    ln.set_xlabel("Arc Length (cm)")
    ln.set_ylabel(r"Velocity (m s$^{-1}$)")
-   ln.save("line_plot.eps")
+   ln.save("line_plot.png")
 
 
 .. _how-to-make-2d-profiles:

diff -r 583f5808555944565a6de1ad073ac5141cc98ef2 -r db0f96ce205128988d5c67e860a4c88ff96de237 yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -67,7 +67,7 @@
     def pixelize(self, dimension, data_source, field, bounds, size,
                  antialias = True, periodic = True):
         """
-        Method for pixelizing grid data sets in preparation for
+        Method for pixelizing datasets in preparation for
         two-dimensional image plots. Relies on several sampling
         routines written in cython
         """
@@ -128,7 +128,7 @@
 
     def line_plot(self, field, start_point, end_point, resolution):
         """
-        Method for sampling grid data sets along a line in preparation for
+        Method for sampling datasets along a line in preparation for
         one-dimensional line plots. For UnstructuredMesh, relies on a
         sampling routine written in cython
         """

diff -r 583f5808555944565a6de1ad073ac5141cc98ef2 -r db0f96ce205128988d5c67e860a4c88ff96de237 yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -2039,7 +2039,7 @@
     """
 
     def __init__(self, ds, fields, point1, point2, resolution,
-                 figure_size=5., aspect=None, fontsize=18., labels={}):
+                 figure_size=5., aspect=None, fontsize=18., labels=None):
         """
         Sets up figure and axes
         """
@@ -2088,10 +2088,29 @@
         self._xlabel = ("Arc Length [Arb. Units]", 14.)
         self._ylabel = ("Field Value [Arb. Units]", 14.)
 
-    def add_plot(self, fields, point1, point2, resolution, labels={}):
+    def add_plot(self, fields, point1, point2, resolution, labels=None):
         r"""
         Used to add plots to the figure
+
+        parameters
+        ----------
+
+        fields : A single field tuple or a list of field tuples
+            The fields to plot
+        start_point : list, tuple, ndarray, or YTArray
+            The coordinates of the start point of the line plot. Length of
+            iterable should be equal to the number of dimensions of the
+            dataset
+        end_point : list, tuple, ndarray, or YTArray
+            The coordinates of the end point of the line plot. Length of
+            iterable should be equal to the number of dimensions of the
+            dataset
+        resolution : integer
+            How many points to sample between start_point and end_point for
+            constructing the line plot
         """
+        if labels is None:
+            labels = {}
         if not isinstance(fields, list):
             fields = [fields]
         for field in fields:
@@ -2102,15 +2121,38 @@
             self.axes.plot(x, y, label=labels[field])
 
     def add_legend(self):
+        r"""
+        Adds a legend to the `LinePlot` instance
+        """
         self.axes.legend()
 
     def set_xlabel(self, label, fontsize=14):
+        r"""
+        Method for setting the x-label
+
+        parameters
+        ----------
+        label : string
+            The label for the x-axis
+        """
         self._xlabel = (label, fontsize)
 
     def set_ylabel(self, label, fontsize=14):
+        r"""
+        Method for setting the y-label
+
+        parameters
+        ----------
+        label : string
+            The label for the y-axis
+        """
         self._ylabel = (label, fontsize)
 
     def _setup_plot(self):
+        r"""
+        Private method called from either `show` or `save`. Sets x and
+        y labels
+        """
         self.axes.set_xlabel(self._xlabel[0], fontsize=self._xlabel[1])
         self.axes.set_ylabel(self._ylabel[0], fontsize=self._ylabel[1])
 
@@ -2132,5 +2174,14 @@
             raise YTNotInsideNotebook
 
     def save(self, name):
+        r"""
+        Method for saving line plots. No default so user should specify
+        extension
+
+        parameters
+        ----------
+        name : string
+            Name to save file to
+        """
         self._setup_plot()
         super(LinePlot, self).save(name)

diff -r 583f5808555944565a6de1ad073ac5141cc98ef2 -r db0f96ce205128988d5c67e860a4c88ff96de237 yt/visualization/tests/test_line_plots.py
--- a/yt/visualization/tests/test_line_plots.py
+++ b/yt/visualization/tests/test_line_plots.py
@@ -39,3 +39,23 @@
     ds = data_dir_load(tri2, kwargs={'step':-1})
     fields = [field for field in ds.field_list if field[0] == 'all']
     yield compare(ds, fields, (0, 0, 0), (1, 1, 0), 1000, "answers_line_plot")
+
+def test_line_plot_methods():
+    # Perform I/O in safe place instead of yt main dir
+    tmpdir = tempfile.mkdtemp()
+    curdir = os.getcwd()
+    os.chdir(tmpdir)
+
+    # hexahedral ds
+    ds = fake_hexahedral_ds()
+
+    ln = yt.LinePlot(ds, ds.field_list, (0, 0, -.25), (0, 0, .25), 100)
+    ln.add_plot(ds.field_list, (0, 0, -.25), (0, 0, .25), 100)
+    ln.add_legend()
+    ln.set_xlabel("Test x label")
+    ln.set_ylabel("Test y label")
+    ln.save()
+
+    os.chdir(curdir)
+    # clean up
+    shutil.rmtree(tmpdir)


https://bitbucket.org/yt_analysis/yt/commits/71a0b2eea9a2/
Changeset:   71a0b2eea9a2
User:        ngoldbaum
Date:        2017-06-26 19:10:52+00:00
Summary:     Merge commit 'refs/pull/1440/head' of https://github.com/yt-project/yt into line_plot_plus_quad9
Affected #:  4 files

diff -r 07c8c38b97deee212c083c3e96cd18fd15be2d0a -r 71a0b2eea9a242e99ea74b55fc2d8badd254c720 doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -14,8 +14,8 @@
 
 The :class:`~yt.visualization.plot_window.PlotWindow` interface is useful for
 taking a quick look at simulation outputs.  Simple mechanisms exist for making
-plots of slices, projections, 1D profiles, and 2D profiles (phase plots), all of
-which are described below.
+plots of slices, projections, 1D spatial line plots, 1D profiles, and 2D
+profiles (phase plots), all of which are described below.
 
 .. _viewing-plots:
 
@@ -1064,34 +1064,36 @@
 and plot the result. You must supply five arguments to the ``LinePlot``
 class. They are enumerated below:
 
-1. Data-set
+1. Dataset
 2. A list of fields or a single field you wish to plot
-3. The starting point of the sampling line. This should be a tuple of three
-   floats corresponding to the coordinates of the starting point
-4. The ending point of the sampling line. This should also be a tuple of three
-   floats
+3. The starting point of the sampling line. This should be an n-element list, tuple,
+   ndarray, or YTArray with the elements corresponding to the coordinates of the
+   starting point. (n should equal the dimension of the dataset)
+4. The ending point of the sampling line. This should also be an n-element list, tuple,
+   ndarray, or YTArray with the elements corresponding to the coordinates of the
+   ending point.
 5. The resolution of the sampling line. This is the number of sampling points
    along the line, e.g. if 1000 is specified, then data will be sampled at
    1000 points evenly spaced between the starting and ending points.
 
 The below code snippet illustrates how this is done:
 
-.. python-script::
+.. code-block:: python
 
-   ds = yt.load(home + "/yt_data/SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1)
+   ds = yt.load("SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1)
    ln = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], (0, 0, 0), (0, 1, 0), 1000)
    ln.save("first_test.png")
 
 You can also add plots to existing ``LinePlot`` instances with ``add_plot`` as shown
 below:
 
-.. python-script::
+.. code-block:: python
 
    ln.add_plot(('all', 'p'), (0, 0, 0), (1, 1, 0), 1000)
    ln.save("added_plot.png")
 
 Note that the beginning and end-points of multiple plots can be different. If
-working in an IPython Notebook, ``LinePlot`` also has the ``show()`` method.
+working in an Jupyter Notebook, ``LinePlot`` also has the ``show()`` method.
 
 You can also create legends and add x and y axes labels to these 1D sampling
 plots. The legend process takes two steps:
@@ -1114,7 +1116,7 @@
    ln.add_legend()
    ln.set_xlabel("Arc Length (cm)")
    ln.set_ylabel(r"Velocity (m s$^{-1}$)")
-   ln.save("line_plot.eps")
+   ln.save("line_plot.png")
 
 
 .. _how-to-make-2d-profiles:

diff -r 07c8c38b97deee212c083c3e96cd18fd15be2d0a -r 71a0b2eea9a242e99ea74b55fc2d8badd254c720 yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -88,7 +88,7 @@
     def pixelize(self, dimension, data_source, field, bounds, size,
                  antialias = True, periodic = True):
         """
-        Method for pixelizing grid data sets in preparation for
+        Method for pixelizing datasets in preparation for
         two-dimensional image plots. Relies on several sampling
         routines written in cython
         """
@@ -149,7 +149,7 @@
 
     def line_plot(self, field, start_point, end_point, resolution):
         """
-        Method for sampling grid data sets along a line in preparation for
+        Method for sampling datasets along a line in preparation for
         one-dimensional line plots. For UnstructuredMesh, relies on a
         sampling routine written in cython
         """

diff -r 07c8c38b97deee212c083c3e96cd18fd15be2d0a -r 71a0b2eea9a242e99ea74b55fc2d8badd254c720 yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -2050,7 +2050,7 @@
     """
 
     def __init__(self, ds, fields, start_point, end_point, resolution,
-                 figure_size=5., aspect=None, fontsize=18., labels={}):
+                 figure_size=5., aspect=None, fontsize=18., labels=None):
         """
         Sets up figure and axes
         """
@@ -2104,10 +2104,29 @@
         self._xlabel = ("Arc Length [Arb. Units]", 14.)
         self._ylabel = ("Field Value [Arb. Units]", 14.)
 
-    def add_plot(self, fields, start_point, end_point, resolution, labels={}):
+    def add_plot(self, fields, start_point, end_point, resolution, labels=None):
         r"""
         Used to add plots to the figure
+
+        parameters
+        ----------
+
+        fields : A single field tuple or a list of field tuples
+            The fields to plot
+        start_point : list, tuple, ndarray, or YTArray
+            The coordinates of the start point of the line plot. Length of
+            iterable should be equal to the number of dimensions of the
+            dataset
+        end_point : list, tuple, ndarray, or YTArray
+            The coordinates of the end point of the line plot. Length of
+            iterable should be equal to the number of dimensions of the
+            dataset
+        resolution : integer
+            How many points to sample between start_point and end_point for
+            constructing the line plot
         """
+        if labels is None:
+            labels = {}
         if not isinstance(fields, list):
             fields = [fields]
         for field in fields:
@@ -2118,15 +2137,38 @@
             self.axes.plot(x, y, label=labels[field])
 
     def add_legend(self):
+        r"""
+        Adds a legend to the `LinePlot` instance
+        """
         self.axes.legend()
 
     def set_xlabel(self, label, fontsize=14):
+        r"""
+        Method for setting the x-label
+
+        parameters
+        ----------
+        label : string
+            The label for the x-axis
+        """
         self._xlabel = (label, fontsize)
 
     def set_ylabel(self, label, fontsize=14):
+        r"""
+        Method for setting the y-label
+
+        parameters
+        ----------
+        label : string
+            The label for the y-axis
+        """
         self._ylabel = (label, fontsize)
 
     def _setup_plot(self):
+        r"""
+        Private method called from either `show` or `save`. Sets x and
+        y labels
+        """
         self.axes.set_xlabel(self._xlabel[0], fontsize=self._xlabel[1])
         self.axes.set_ylabel(self._ylabel[0], fontsize=self._ylabel[1])
 
@@ -2148,6 +2190,17 @@
             raise YTNotInsideNotebook
 
     def save(self, name=None, mpl_kwargs=None):
+        r"""
+        Method for saving line plots. No default so user should specify
+        extension
+
+        parameters
+        ----------
+        name : string
+            Name to save file to
+        mpl_kwargs: dict
+            A dictionary of keywoard arguments to pass to matplotlib
+        """
         self._setup_plot()
         if name is None:
             name = str(self.ds)

diff -r 07c8c38b97deee212c083c3e96cd18fd15be2d0a -r 71a0b2eea9a242e99ea74b55fc2d8badd254c720 yt/visualization/tests/test_line_plots.py
--- a/yt/visualization/tests/test_line_plots.py
+++ b/yt/visualization/tests/test_line_plots.py
@@ -39,3 +39,23 @@
     ds = data_dir_load(tri2, kwargs={'step':-1})
     fields = [field for field in ds.field_list if field[0] == 'all']
     yield compare(ds, fields, (0, 0, 0), (1, 1, 0), 1000, "answers_line_plot")
+
+def test_line_plot_methods():
+    # Perform I/O in safe place instead of yt main dir
+    tmpdir = tempfile.mkdtemp()
+    curdir = os.getcwd()
+    os.chdir(tmpdir)
+
+    # hexahedral ds
+    ds = fake_hexahedral_ds()
+
+    ln = yt.LinePlot(ds, ds.field_list, (0, 0, -.25), (0, 0, .25), 100)
+    ln.add_plot(ds.field_list, (0, 0, -.25), (0, 0, .25), 100)
+    ln.add_legend()
+    ln.set_xlabel("Test x label")
+    ln.set_ylabel("Test y label")
+    ln.save()
+
+    os.chdir(curdir)
+    # clean up
+    shutil.rmtree(tmpdir)


https://bitbucket.org/yt_analysis/yt/commits/b5e69528f853/
Changeset:   b5e69528f853
User:        ngoldbaum
Date:        2017-06-26 22:26:06+00:00
Summary:     Add a get_latex_display_name function to DerivedField for generating nice labels for fields
Affected #:  2 files

diff -r 71a0b2eea9a242e99ea74b55fc2d8badd254c720 -r b5e69528f85359e97533fc4b51bf5f7cd6c381ce yt/fields/derived_field.py
--- a/yt/fields/derived_field.py
+++ b/yt/fields/derived_field.py
@@ -13,6 +13,7 @@
 
 import contextlib
 import inspect
+import re
 import warnings
 
 from yt.extern.six import string_types, PY2
@@ -292,6 +293,51 @@
         s += ")"
         return s
 
+    def _is_ion(self):
+        p = re.compile("_p[0-9]+_")
+        result = False
+        if p.search(self.name[1]) is not None:
+            result = True
+        return result
+
+    def _ion_to_label(self):
+        pnum2rom = {
+            "0":"I", "1":"II", "2":"III", "3":"IV", "4":"V",
+            "5":"VI", "6":"VII", "7":"VIII", "8":"IX", "9":"X",
+            "10":"XI", "11":"XII", "12":"XIII", "13":"XIV", "14":"XV",
+            "15":"XVI", "16":"XVII", "17":"XVIII", "18":"XIX", "19":"XX"}
+
+        p = re.compile("_p[0-9]+_")
+        m = p.search(self.name[1])
+        if m is not None:
+            pstr = m.string[m.start()+1:m.end()-1]
+            segments = self.name[1].split("_")
+            for i,s in enumerate(segments):
+                segments[i] = s.capitalize()
+                if s == pstr:
+                    ipstr = i
+            element = segments[ipstr-1]
+            roman = pnum2rom[pstr[1:]]
+            label = element + '\ ' + roman + '\ ' + \
+                '\ '.join(segments[ipstr+1:])
+        else:
+            label = self.name[1]
+        return label
+
+    def get_latex_display_name(self):
+        label = self.display_name
+        if label is None:
+            if self._is_ion():
+                fname = self._ion_to_label()
+                label = r'$\rm{'+fname.replace('_','\ ')+r'}$'
+            else:
+                label = r'$\rm{'+self.name[1].replace('_','\ ').title()+r'}$'
+        elif label.find('$') == -1:
+            label = label.replace(' ','\ ')
+            label = r'$\rm{'+label+r'}$'
+        return label
+
+
 class FieldValidator(object):
     pass
 
@@ -361,6 +407,7 @@
         FieldValidator.__init__(self)
         self.ghost_zones = ghost_zones
         self.fields = fields
+
     def __call__(self, data):
         # When we say spatial information, we really mean
         # that it has a three-dimensional data structure

diff -r 71a0b2eea9a242e99ea74b55fc2d8badd254c720 -r b5e69528f85359e97533fc4b51bf5f7cd6c381ce yt/visualization/fixed_resolution.py
--- a/yt/visualization/fixed_resolution.py
+++ b/yt/visualization/fixed_resolution.py
@@ -30,7 +30,6 @@
 
 import numpy as np
 import weakref
-import re
 import types
 
 class FixedResolutionBuffer(object):
@@ -155,38 +154,6 @@
             if f not in exclude and f[0] not in self.data_source.ds.particle_types:
                 self[f]
 
-    def _is_ion( self, fname ):
-        p = re.compile("_p[0-9]+_")
-        result = False
-        if p.search( fname ) is not None:
-            result = True
-        return result
-
-    def _ion_to_label( self, fname ):
-        pnum2rom = {
-            "0":"I", "1":"II", "2":"III", "3":"IV", "4":"V",
-            "5":"VI", "6":"VII", "7":"VIII", "8":"IX", "9":"X",
-            "10":"XI", "11":"XII", "12":"XIII", "13":"XIV", "14":"XV",
-            "15":"XVI", "16":"XVII", "17":"XVIII", "18":"XIX", "19":"XX"}
-
-        p = re.compile("_p[0-9]+_")
-        m = p.search( fname )
-        if m is not None:
-            pstr = m.string[m.start()+1:m.end()-1]
-            segments = fname.split("_")
-            for i,s in enumerate(segments):
-                segments[i] = s.capitalize()
-                if s == pstr:
-                    ipstr = i
-            element = segments[ipstr-1]
-            roman = pnum2rom[pstr[1:]]
-            label = element + '\ ' + roman + '\ ' + \
-                '\ '.join(segments[ipstr+1:])
-        else:
-            label = fname
-        return label
-
-
     def _get_info(self, item):
         info = {}
         ftype, fname = field = self.data_source._determine_fields(item)[0]
@@ -210,18 +177,7 @@
         except AttributeError:
             pass
 
-        info['label'] = finfo.display_name
-        if info['label'] is None:
-            if self._is_ion( fname ):
-                fname = self._ion_to_label( fname )
-                info['label'] = r'$\rm{'+fname+r'}$'
-                info['label'] = r'$\rm{'+fname.replace('_','\ ')+r'}$'
-            else:
-                info['label'] = r'$\rm{'+fname+r'}$'
-                info['label'] = r'$\rm{'+fname.replace('_','\ ').title()+r'}$'
-        elif info['label'].find('$') == -1:
-            info['label'] = info['label'].replace(' ','\ ')
-            info['label'] = r'$\rm{'+info['label']+r'}$'
+        info['label'] = finfo.get_latex_display_name()
 
         return info
 


https://bitbucket.org/yt_analysis/yt/commits/457f673b7c51/
Changeset:   457f673b7c51
User:        ngoldbaum
Date:        2017-06-26 22:26:49+00:00
Summary:     Refactor to make LinePlot a sublcass of PlotContainer
Affected #:  6 files

diff -r b5e69528f85359e97533fc4b51bf5f7cd6c381ce -r 457f673b7c51106719338ec6914a026685e1fac1 yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -39,8 +39,8 @@
     sample_points = uvstack(sample_points).T
     ray_coordinates = uvstack([ray[d] for d in 'xyz']).T
     ray_dds = uvstack([ray['d'+d] for d in 'xyz']).T
-    field_values = np.zeros(resolution)
     ray_field = ray[field]
+    field_values = ray.ds.arr(np.zeros(resolution), ray_field.units)
     for i, sample_point in enumerate(sample_points):
         ray_contains = ((sample_point >= (ray_coordinates - ray_dds/2)) &
                         (sample_point <= (ray_coordinates + ray_dds/2)))

diff -r b5e69528f85359e97533fc4b51bf5f7cd6c381ce -r 457f673b7c51106719338ec6914a026685e1fac1 yt/visualization/api.py
--- a/yt/visualization/api.py
+++ b/yt/visualization/api.py
@@ -51,7 +51,9 @@
     AxisAlignedSlicePlot, \
     OffAxisSlicePlot, \
     ProjectionPlot, \
-    OffAxisProjectionPlot, \
+    OffAxisProjectionPlot
+
+from .line_plot import \
     LinePlot
 
 from .profile_plotter import \

diff -r b5e69528f85359e97533fc4b51bf5f7cd6c381ce -r 457f673b7c51106719338ec6914a026685e1fac1 yt/visualization/line_plot.py
--- /dev/null
+++ b/yt/visualization/line_plot.py
@@ -0,0 +1,245 @@
+"""
+A mechanism for plotting field values along a line through a dataset
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2013, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+import numpy as np
+
+from yt.funcs import \
+    iterable
+from yt.units.unit_object import \
+    Unit
+from yt.units.yt_array import \
+    YTArray
+from yt.visualization.base_plot_types import \
+    PlotMPL
+from yt.visualization.plot_container import \
+    PlotContainer, \
+    PlotDictionary, \
+    log_transform, \
+    linear_transform, \
+    invalidate_plot
+
+class LinePlotDictionary(PlotDictionary):
+    def __init__(self, data_source):
+        super(LinePlotDictionary, self).__init__(data_source)
+        self.known_dimensions = {}
+    
+    def _sanitize_dimensions(self, item):
+        field = self.data_source._determine_fields(item)[0]
+        finfo = self.data_source.ds.field_info[field]
+        dimensions = Unit(
+            finfo.units, registry=self.data_source.ds.unit_registry).dimensions
+        if dimensions not in self.known_dimensions:
+            self.known_dimensions[dimensions] = item
+            ret_item = item
+        else:
+            ret_item = self.known_dimensions[dimensions]
+        return ret_item
+    
+    def __getitem__(self, item):
+        ret_item = self._sanitize_dimensions(item)
+        return super(LinePlotDictionary, self).__getitem__(ret_item)
+
+    def __setitem__(self, item, value):
+        ret_item = self._sanitize_dimensions(item)
+        super(LinePlotDictionary, self).__setitem__(ret_item, value)
+
+    def __contains__(self, item):
+        ret_item = self._sanitize_dimensions(item)
+        return super(LinePlotDictionary, self).__contains__(ret_item)
+
+class LinePlot(PlotContainer):
+    r"""
+    A class for constructing line plots
+
+    Parameters
+    ----------
+
+    ds: :class:`yt.data_objects.static_output.Dataset`
+        This is the dataset object corresponding to the
+        simulation output to be plotted.
+    fields: string
+        The name(s) of the field(s) to be plotted.
+    start_point: n-element list, tuple, ndarray, or YTArray
+        Contains the coordinates of the first point for constructing the line.
+        Must contain n elements where n is the dimensionality of the dataset.
+    end_point: n-element list, tuple, ndarray, or YTArray
+        Contains the coordinates of the first point for constructing the line.
+        Must contain n elements where n is the dimensionality of the dataset.
+    figure_size: integer or two-element 
+    resolution: int
+        How many points to sample between start_point and end_point for 
+        constructing the line plot
+    """
+    _plot_type = 'line_plot'
+
+    def __init__(self, ds, fields, start_point, end_point, resolution,
+                 figure_size=5., fontsize=14., labels=None):
+        """
+        Sets up figure and axes
+        """
+        self.start_point = _validate_point(start_point, ds)
+        self.end_point = _validate_point(end_point, ds)
+        self.resolution = resolution
+        self._x_unit = None
+        self._y_units = {}
+        self._titles = {}
+
+        data_source = ds.all_data()
+
+        self.fields = data_source._determine_fields(fields)
+        self.plots = LinePlotDictionary(data_source)
+        if labels is None:
+            self.labels = {}
+                
+        super(LinePlot, self).__init__(data_source, figure_size, fontsize)
+
+        for f in self.fields:
+            if f not in self.labels:
+                self.labels[f] = f[1]
+            finfo = self.data_source.ds._get_field_info(*f)
+            if finfo.take_log:
+                self._field_transform[f] = log_transform
+            else:
+                self._field_transform[f] = linear_transform
+
+        self._setup_plots()
+        
+    def add_legend(self):
+        """Adds a legend to the `LinePlot` instance"""
+        self.axes.legend()
+
+    def _setup_plots(self):
+        if self._plot_valid is True:
+            return
+        for field in self.fields:
+            fontscale = self._font_properties._size / 14.
+            top_buff_size = 0.3*fontscale
+
+            x_axis_size = 1.2*fontscale
+            y_axis_size = 0.9*fontscale
+            right_buff_size = 0.2*fontscale
+
+            xbins = np.array([x_axis_size, self.figure_size[0],
+                              right_buff_size])
+            ybins = np.array([y_axis_size, self.figure_size[1], top_buff_size])
+
+            size = [xbins.sum(), ybins.sum()]
+
+            x_frac_widths = xbins/size[0]
+            y_frac_widths = ybins/size[1]
+
+            axrect = (
+                x_frac_widths[0],
+                y_frac_widths[0],
+                x_frac_widths[1],
+                y_frac_widths[1],
+            )
+
+            try:
+                plot_used = True
+                plot = self.plots[field]
+            except KeyError:
+                plot_used = False
+                plot = PlotMPL(self.figure_size, axrect, None, None)
+                self.plots[field] = plot
+                
+            plot._set_font_properties(self._font_properties, None)
+
+            x, y = self.ds.coordinates.line_plot(
+                field, self.start_point, self.end_point, self.resolution)
+            
+            if self._x_unit is None:
+                unit_x = x.units
+            else:
+                unit_x = self._x_unit
+
+            if field in self._y_units:
+                unit_y = self._y_units[field]
+            else:
+                unit_y = y.units
+
+            x = x.to(unit_x)
+            y = y.to(unit_y)
+
+            plot.axes.plot(x, y, label=self.labels[field])
+            
+            if self._field_transform[field] != linear_transform:
+                if (y < 0).any():
+                    plot.axes.set_yscale('symlog')
+                else:
+                    plot.axes.set_yscale('log')
+
+            axes_unit_labels = self._get_axes_unit_labels(unit_x, unit_y)
+
+            finfo = self.ds.field_info[field]
+            
+            x_label = r'$\rm{Path\ Length' + axes_unit_labels[0]+'}$'
+            if plot_used:
+                y_label = (r'$\rm{Multiple\ Fields}$' + r'$\rm{' +
+                           axes_unit_labels[1]+'}$')
+            else:
+                y_label = (finfo.get_latex_display_name() + r'$\rm{' +
+                           axes_unit_labels[1]+'}$')
+
+            plot.axes.set_xlabel(x_label)
+            plot.axes.set_ylabel(y_label)
+
+            if field in self._titles:
+                plot.axes.set_title(self._titles[field])
+
+    @invalidate_plot
+    def set_x_unit(self, unit_name):
+        """Set the unit to use along the x-axis
+
+        Parameters
+        ----------
+        unit_name: str
+          The name of the unit to use for the x-axis unit
+        """
+        self._x_unit = unit_name
+
+    @invalidate_plot
+    def set_unit(self, field, unit_name):
+        """Set the unit used to plot the field
+
+        Parameters
+        ----------
+        field: str or field tuple
+           The name of the field to set the units for
+        unit_name: str
+           The name of the unit to use for this field
+        """
+        self._y_units[self.data_source._determine_fields(field)[0]] = unit_name
+
+    @invalidate_plot
+    def annotate_title(self, field, title):
+        """Set the unit used to plot the field
+
+        Parameters
+        ----------
+        field: str or field tuple
+           The name of the field to set the units for
+        title: str
+           The title to use for the plot
+        """
+        self._titles[self.data_source._determine_fields(field)[0]] = title
+
+def _validate_point(point, ds):
+    if not iterable(point):
+        raise RuntimeError
+    if not isinstance(point, YTArray):
+        point = ds.arr(point, 'code_length')
+    return point
+

diff -r b5e69528f85359e97533fc4b51bf5f7cd6c381ce -r 457f673b7c51106719338ec6914a026685e1fac1 yt/visualization/plot_container.py
--- a/yt/visualization/plot_container.py
+++ b/yt/visualization/plot_container.py
@@ -30,11 +30,19 @@
 
 from yt.config import \
     ytcfg
+from yt.data_objects.time_series import \
+    DatasetSeries
 from yt.funcs import \
     get_image_suffix, \
     iterable, \
     ensure_dir, \
     ensure_list
+from yt.units.unit_lookup_table import \
+    prefixable_units, latex_prefixes
+from yt.units.unit_object import \
+    Unit
+from yt.utilities.definitions import \
+    formatted_length_unit_names
 from yt.utilities.exceptions import \
     YTNotInsideNotebook
 from yt.visualization.color_maps import \
@@ -178,36 +186,27 @@
         self.data_source = data_source
         return defaultdict.__init__(self, default_factory)
 
-class ImagePlotContainer(object):
-    """A container for plots with colorbars.
-
-    """
+class PlotContainer(object):
+    """A container for generic plots"""
     _plot_type = None
     _plot_valid = False
-    _colorbar_valid = False
 
     def __init__(self, data_source, figure_size, fontsize):
         from matplotlib.font_manager import FontProperties
-
         self.data_source = data_source
+        self.ds = data_source.ds
+        self.ts = self._initialize_dataset(self.ds)
         if iterable(figure_size):
             self.figure_size = float(figure_size[0]), float(figure_size[1])
         else:
-            self.figure_size = float(figure_size)
-        self.plots = PlotDictionary(data_source)
-        self._callbacks = []
-        self._field_transform = {}
-        self._colormaps = defaultdict(
-            lambda: ytcfg.get("yt", "default_colormap"))
+            self.figure_size = float(figure_size), float(figure_size)
         font_path = matplotlib.get_data_path() + '/fonts/ttf/STIXGeneral.ttf'
         self._font_properties = FontProperties(size=fontsize, fname=font_path)
         self._font_color = None
         self._xlabel = None
         self._ylabel = None
         self._minorticks = {}
-        self._cbar_minorticks = {}
-        self._colorbar_label = PlotDictionary(
-            self.data_source, lambda: None)
+        self._field_transform = {}
 
     @invalidate_plot
     def set_log(self, field, log, linthresh=None):
@@ -270,109 +269,6 @@
         return self
 
     @invalidate_plot
-    def set_cmap(self, field, cmap):
-        """set the colormap for one of the fields
-
-        Parameters
-        ----------
-        field : string
-            the field to set the colormap
-            if field == 'all', applies to all plots.
-        cmap : string or tuple
-            If a string, will be interpreted as name of the colormap.
-            If a tuple, it is assumed to be of the form (name, type, number)
-            to be used for palettable functionality. (name, type, number, bool)
-            can be used to specify if a reverse colormap is to be used.
-
-        """
-
-        if field == 'all':
-            fields = list(self.plots.keys())
-        else:
-            fields = [field]
-        for field in self.data_source._determine_fields(fields):
-            self._colorbar_valid = False
-            self._colormaps[field] = cmap
-        return self
-
-    @invalidate_plot
-    def set_background_color(self, field, color=None):
-        """set the background color to match provided color
-
-        Parameters
-        ----------
-        field : string
-            the field to set the colormap
-            if field == 'all', applies to all plots.
-        color : string or RGBA tuple (optional)
-            if set, set the background color to this color
-            if unset, background color is set to the bottom value of
-            the color map
-
-        """
-        actual_field = self.data_source._determine_fields(field)[0]
-        if color is None:
-            cmap = self._colormaps[actual_field]
-            if isinstance(cmap, string_types):
-                try:
-                    cmap = yt_colormaps[cmap]
-                except KeyError:
-                    cmap = getattr(matplotlib.cm, cmap)
-            color = cmap(0)
-        if LooseVersion(matplotlib.__version__) < LooseVersion("2.0.0"):
-            self.plots[actual_field].axes.set_axis_bgcolor(color)
-        else:
-            self.plots[actual_field].axes.set_facecolor(color)
-        return self
-
-    @invalidate_plot
-    def set_zlim(self, field, zmin, zmax, dynamic_range=None):
-        """set the scale of the colormap
-
-        Parameters
-        ----------
-        field : string
-            the field to set a colormap scale
-            if field == 'all', applies to all plots.
-        zmin : float
-            the new minimum of the colormap scale. If 'min', will
-            set to the minimum value in the current view.
-        zmax : float
-            the new maximum of the colormap scale. If 'max', will
-            set to the maximum value in the current view.
-
-        Other Parameters
-        ----------------
-        dynamic_range : float (default: None)
-            The dynamic range of the image.
-            If zmin == None, will set zmin = zmax / dynamic_range
-            If zmax == None, will set zmax = zmin * dynamic_range
-            When dynamic_range is specified, defaults to setting
-            zmin = zmax / dynamic_range.
-
-        """
-        if field is 'all':
-            fields = list(self.plots.keys())
-        else:
-            fields = ensure_list(field)
-        for field in self.data_source._determine_fields(fields):
-            myzmin = zmin
-            myzmax = zmax
-            if zmin == 'min':
-                myzmin = self.plots[field].image._A.min()
-            if zmax == 'max':
-                myzmax = self.plots[field].image._A.max()
-            if dynamic_range is not None:
-                if zmax is None:
-                    myzmax = myzmin * dynamic_range
-                else:
-                    myzmin = myzmax / dynamic_range
-
-            self.plots[field].zmin = myzmin
-            self.plots[field].zmax = myzmax
-        return self
-
-    @invalidate_plot
     def set_minorticks(self, field, state):
         """turn minor ticks on or off in the current plot
 
@@ -398,36 +294,16 @@
                 self._minorticks[field] = False
         return self
 
-    @invalidate_plot
-    def set_cbar_minorticks(self, field, state):
-        """turn colorbar minor ticks on or off in the current plot
-
-        Displaying minor ticks reduces performance; turn them off
-        using set_cbar_minorticks('all', 'off') if drawing speed is a problem.
-
-        Parameters
-        ----------
-        field : string
-            the field to remove colorbar minorticks
-        state : string
-            the state indicating 'on' or 'off'
-
-        """
-        if field == 'all':
-            fields = list(self.plots.keys())
-        else:
-            fields = [field]
-        for field in self.data_source._determine_fields(fields):
-            if state == 'on':
-                self._cbar_minorticks[field] = True
-            else:
-                self._cbar_minorticks[field] = False
-        return self
-
     def _setup_plots(self):
         # Left blank to be overriden in subclasses
         pass
 
+    def _initialize_dataset(self, ts):
+        if not isinstance(ts, DatasetSeries):
+            if not iterable(ts): ts = [ts]
+            ts = DatasetSeries(ts)
+        return ts
+
     def _switch_ds(self, new_ds, data_source=None):
         old_object = self.data_source
         name = old_object._type_name
@@ -585,8 +461,11 @@
                 for k, v in iteritems(self.plots):
                     names.append(v.save(name, mpl_kwargs))
                 return names
-        axis = self.ds.coordinates.axis_name.get(
-            self.data_source.axis, '')
+        if hasattr(self.data_source, 'axis'):
+            axis = self.ds.coordinates.axis_name.get(
+                self.data_source.axis, '')
+        else:
+            axis = None
         weight = None
         type = self._plot_type
         if type in ['Projection', 'OffAxisProjection']:
@@ -616,21 +495,6 @@
         return self
 
     @validate_plot
-    def _send_zmq(self):
-        from ._mpl_imports import FigureCanvasAgg
-        try:
-            # pre-IPython v1.0
-            from IPython.zmq.pylab.backend_inline import send_figure as display
-        except ImportError:
-            # IPython v1.0+
-            from IPython.core.display import display
-        for k, v in sorted(iteritems(self.plots)):
-            # Due to a quirk in the matplotlib API, we need to create
-            # a dummy canvas variable here that is never used.
-            canvas = FigureCanvasAgg(v.figure)  # NOQA
-            display(v.figure)
-
-    @validate_plot
     def show(self):
         r"""This will send any existing plots to the IPython notebook.
 
@@ -715,6 +579,211 @@
         self._ylabel = label
         return self
 
+    def _get_axes_unit_labels(self, unit_x, unit_y):
+        axes_unit_labels = ['', '']
+        comoving = False
+        hinv = False
+        for i, un in enumerate((unit_x, unit_y)):
+            unn = None
+            if hasattr(self.data_source, 'axis'):
+                if hasattr(self.ds.coordinates, "image_units"):
+                    # This *forces* an override
+                    unn = self.ds.coordinates.image_units[
+                        self.data_source.axis][i]
+                elif hasattr(self.ds.coordinates, "default_unit_label"):
+                    axax = getattr(self.ds.coordinates,
+                                   "%s_axis" % ("xy"[i]))[self.data_source.axis]
+                    unn = self.ds.coordinates.default_unit_label.get(
+                        axax, None)
+            if unn is not None:
+                axes_unit_labels[i] = r'\ \ \left('+unn+r'\right)'
+                continue
+            # Use sympy to factor h out of the unit.  In this context 'un'
+            # is a string, so we call the Unit constructor.
+            expr = Unit(un, registry=self.ds.unit_registry).expr
+            h_expr = Unit('h', registry=self.ds.unit_registry).expr
+            # See http://docs.sympy.org/latest/modules/core.html#sympy.core.expr.Expr
+            h_power = expr.as_coeff_exponent(h_expr)[1]
+            # un is now the original unit, but with h factored out.
+            un = str(expr*h_expr**(-1*h_power))
+            un_unit = Unit(un, registry=self.ds.unit_registry)
+            cm = Unit('cm').expr
+            if str(un).endswith('cm') and cm not in un_unit.expr.atoms():
+                comoving = True
+                un = un[:-2]
+            # no length units besides code_length end in h so this is safe
+            if h_power == -1:
+                hinv = True
+            elif h_power != 0:
+                # It doesn't make sense to scale a position by anything
+                # other than h**-1
+                raise RuntimeError
+            if un not in ['1', 'u', 'unitary']:
+                if un in formatted_length_unit_names:
+                    un = formatted_length_unit_names[un]
+                else:
+                    un = Unit(un, registry=self.ds.unit_registry)
+                    un = un.latex_representation()
+                    if hinv:
+                        un = un + '\,h^{-1}'
+                    if comoving:
+                        un = un + '\,(1+z)^{-1}'
+                    pp = un[0]
+                    if pp in latex_prefixes:
+                        symbol_wo_prefix = un[1:]
+                        if symbol_wo_prefix in prefixable_units:
+                            un = un.replace(
+                                pp, "{"+latex_prefixes[pp]+"}", 1)
+                axes_unit_labels[i] = '\ \ ('+un+')'
+        return axes_unit_labels
+
+
+class ImagePlotContainer(PlotContainer):
+    """A container for plots with colorbars.
+
+    """
+    _colorbar_valid = False
+
+    def __init__(self, data_source, figure_size, fontsize):
+        super(ImagePlotContainer, self).__init__(
+            data_source, figure_size, fontsize)
+        self.plots = PlotDictionary(data_source)
+        self._callbacks = []
+        self._colormaps = defaultdict(
+            lambda: ytcfg.get("yt", "default_colormap"))
+        self._cbar_minorticks = {}
+        self._colorbar_label = PlotDictionary(
+            self.data_source, lambda: None)
+
+    @invalidate_plot
+    def set_cmap(self, field, cmap):
+        """set the colormap for one of the fields
+
+        Parameters
+        ----------
+        field : string
+            the field to set the colormap
+            if field == 'all', applies to all plots.
+        cmap : string or tuple
+            If a string, will be interpreted as name of the colormap.
+            If a tuple, it is assumed to be of the form (name, type, number)
+            to be used for palettable functionality. (name, type, number, bool)
+            can be used to specify if a reverse colormap is to be used.
+
+        """
+
+        if field == 'all':
+            fields = list(self.plots.keys())
+        else:
+            fields = [field]
+        for field in self.data_source._determine_fields(fields):
+            self._colorbar_valid = False
+            self._colormaps[field] = cmap
+        return self
+
+    @invalidate_plot
+    def set_background_color(self, field, color=None):
+        """set the background color to match provided color
+
+        Parameters
+        ----------
+        field : string
+            the field to set the colormap
+            if field == 'all', applies to all plots.
+        color : string or RGBA tuple (optional)
+            if set, set the background color to this color
+            if unset, background color is set to the bottom value of
+            the color map
+
+        """
+        actual_field = self.data_source._determine_fields(field)[0]
+        if color is None:
+            cmap = self._colormaps[actual_field]
+            if isinstance(cmap, string_types):
+                try:
+                    cmap = yt_colormaps[cmap]
+                except KeyError:
+                    cmap = getattr(matplotlib.cm, cmap)
+            color = cmap(0)
+        if LooseVersion(matplotlib.__version__) < LooseVersion("2.0.0"):
+            self.plots[actual_field].axes.set_axis_bgcolor(color)
+        else:
+            self.plots[actual_field].axes.set_facecolor(color)
+        return self
+
+    @invalidate_plot
+    def set_zlim(self, field, zmin, zmax, dynamic_range=None):
+        """set the scale of the colormap
+
+        Parameters
+        ----------
+        field : string
+            the field to set a colormap scale
+            if field == 'all', applies to all plots.
+        zmin : float
+            the new minimum of the colormap scale. If 'min', will
+            set to the minimum value in the current view.
+        zmax : float
+            the new maximum of the colormap scale. If 'max', will
+            set to the maximum value in the current view.
+
+        Other Parameters
+        ----------------
+        dynamic_range : float (default: None)
+            The dynamic range of the image.
+            If zmin == None, will set zmin = zmax / dynamic_range
+            If zmax == None, will set zmax = zmin * dynamic_range
+            When dynamic_range is specified, defaults to setting
+            zmin = zmax / dynamic_range.
+
+        """
+        if field is 'all':
+            fields = list(self.plots.keys())
+        else:
+            fields = ensure_list(field)
+        for field in self.data_source._determine_fields(fields):
+            myzmin = zmin
+            myzmax = zmax
+            if zmin == 'min':
+                myzmin = self.plots[field].image._A.min()
+            if zmax == 'max':
+                myzmax = self.plots[field].image._A.max()
+            if dynamic_range is not None:
+                if zmax is None:
+                    myzmax = myzmin * dynamic_range
+                else:
+                    myzmin = myzmax / dynamic_range
+
+            self.plots[field].zmin = myzmin
+            self.plots[field].zmax = myzmax
+        return self
+
+    @invalidate_plot
+    def set_cbar_minorticks(self, field, state):
+        """turn colorbar minor ticks on or off in the current plot
+
+        Displaying minor ticks reduces performance; turn them off
+        using set_cbar_minorticks('all', 'off') if drawing speed is a problem.
+
+        Parameters
+        ----------
+        field : string
+            the field to remove colorbar minorticks
+        state : string
+            the state indicating 'on' or 'off'
+
+        """
+        if field == 'all':
+            fields = list(self.plots.keys())
+        else:
+            fields = [field]
+        for field in self.data_source._determine_fields(fields):
+            if state == 'on':
+                self._cbar_minorticks[field] = True
+            else:
+                self._cbar_minorticks[field] = False
+        return self
+
     @invalidate_plot
     def set_colorbar_label(self, field, label):
         r"""

diff -r b5e69528f85359e97533fc4b51bf5f7cd6c381ce -r 457f673b7c51106719338ec6914a026685e1fac1 yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -14,7 +14,6 @@
 # The full license is in the file COPYING.txt, distributed with this software.
 #-----------------------------------------------------------------------------
 import numpy as np
-import os
 import matplotlib
 import types
 import six
@@ -25,7 +24,7 @@
 from numbers import Number
 
 from .base_plot_types import \
-    ImagePlotMPL, PlotMPL
+    ImagePlotMPL
 from .fixed_resolution import \
     FixedResolutionBuffer, \
     OffAxisProjectionFixedResolutionBuffer
@@ -37,8 +36,6 @@
     invalidate_data, invalidate_plot, apply_callback
 from .base_plot_types import CallbackWrapper
 
-from yt.data_objects.time_series import \
-    DatasetSeries
 from yt.data_objects.image_array import \
     ImageArray
 from yt.extern.six import string_types
@@ -46,31 +43,24 @@
     YTSpatialPlotDataset
 from yt.funcs import \
     mylog, iterable, ensure_list, \
-    fix_axis, fix_unitary, ensure_dir, \
-    get_image_suffix
+    fix_axis, fix_unitary
 from yt.units.unit_object import \
     Unit
 from yt.units.unit_registry import \
     UnitParseError
-from yt.units.unit_lookup_table import \
-    prefixable_units, latex_prefixes
 from yt.units.yt_array import \
     YTArray, YTQuantity
-from yt.utilities.definitions import \
-    formatted_length_unit_names
 from yt.utilities.math_utils import \
     ortho_find
 from yt.utilities.orientation import \
     Orientation
 from yt.utilities.exceptions import \
-    YTUnitNotRecognized, \
     YTCannotParseUnitDisplayName, \
-    YTUnitConversionError, \
     YTPlotCallbackError, \
     YTDataTypeUnsupported, \
     YTInvalidFieldType, \
-    YTNotInsideNotebook
-from yt.extern.six.moves import builtins
+    YTUnitNotRecognized, \
+    YTUnitConversionError
 
 MPL_VERSION = LooseVersion(matplotlib.__version__)
 
@@ -183,11 +173,6 @@
                  periodic=True, origin='center-window', oblique=False, right_handed=True,
                  window_size=8.0, fields=None, fontsize=18, aspect=None,
                  setup=False):
-        if not hasattr(self, "ds"):
-            self.ds = data_source.ds
-            ts = self._initialize_dataset(self.ds)
-            self.ts = ts
-        self._axes_unit_names = None
         self.center = None
         self._periodic = periodic
         self.oblique = oblique
@@ -195,6 +180,7 @@
         self._equivalencies = defaultdict(lambda: (None, {}))
         self.buff_size = buff_size
         self.antialias = antialias
+        self._axes_unit_names = None
 
         self.aspect = aspect
         skip = list(FixedResolutionBuffer._exclude_fields) + data_source._key_fields
@@ -224,12 +210,6 @@
         self.setup_callbacks()
         self._setup_plots()
 
-    def _initialize_dataset(self, ts):
-        if not isinstance(ts, DatasetSeries):
-            if not iterable(ts): ts = [ts]
-            ts = DatasetSeries(ts)
-        return ts
-
     def __iter__(self):
         for ds in self.ts:
             mylog.warning("Switching to %s", ds)
@@ -860,59 +840,7 @@
                 ax = self.plots[f].axes
                 ax.invert_xaxis()
 
-            axes_unit_labels = ['', '']
-            comoving = False
-            hinv = False
-            for i, un in enumerate((unit_x, unit_y)):
-                unn = None
-                if hasattr(self.ds.coordinates, "image_units"):
-                    # This *forces* an override
-                    unn = self.ds.coordinates.image_units[axis_index][i]
-                elif hasattr(self.ds.coordinates, "default_unit_label"):
-                    axax = getattr(self.ds.coordinates,
-                                   "%s_axis" % ("xy"[i]))[axis_index]
-                    unn = self.ds.coordinates.default_unit_label.get(axax,
-                        None)
-                if unn is not None:
-                    axes_unit_labels[i] = r'\ \ \left('+unn+r'\right)'
-                    continue
-                # Use sympy to factor h out of the unit.  In this context 'un'
-                # is a string, so we call the Unit constructor.
-                expr = Unit(un, registry=self.ds.unit_registry).expr
-                h_expr = Unit('h', registry=self.ds.unit_registry).expr
-                # See http://docs.sympy.org/latest/modules/core.html#sympy.core.expr.Expr
-                h_power = expr.as_coeff_exponent(h_expr)[1]
-                # un is now the original unit, but with h factored out.
-                un = str(expr*h_expr**(-1*h_power))
-                un_unit = Unit(un, registry=self.ds.unit_registry)
-                cm = Unit('cm').expr
-                if str(un).endswith('cm') and cm not in un_unit.expr.atoms():
-                    comoving = True
-                    un = un[:-2]
-                # no length units besides code_length end in h so this is safe
-                if h_power == -1:
-                    hinv = True
-                elif h_power != 0:
-                    # It doesn't make sense to scale a position by anything
-                    # other than h**-1
-                    raise RuntimeError
-                if un not in ['1', 'u', 'unitary']:
-                    if un in formatted_length_unit_names:
-                        un = formatted_length_unit_names[un]
-                    else:
-                        un = Unit(un, registry=self.ds.unit_registry)
-                        un = un.latex_representation()
-                        if hinv:
-                            un = un + '\,h^{-1}'
-                        if comoving:
-                            un = un + '\,(1+z)^{-1}'
-                        pp = un[0]
-                        if pp in latex_prefixes:
-                            symbol_wo_prefix = un[1:]
-                            if symbol_wo_prefix in prefixable_units:
-                                un = un.replace(
-                                    pp, "{"+latex_prefixes[pp]+"}", 1)
-                    axes_unit_labels[i] = '\ \ ('+un+')'
+            axes_unit_labels = self._get_axes_unit_labels(unit_x, unit_y)
 
             if self.oblique:
                 labels = [r'$\rm{Image\ x'+axes_unit_labels[0]+'}$',
@@ -1308,9 +1236,6 @@
                  origin='center-window', right_handed=True, fontsize=18, field_parameters=None,
                  window_size=8.0, aspect=None, data_source=None):
         # this will handle time series data and controllers
-        ts = self._initialize_dataset(ds)
-        self.ts = ts
-        ds = self.ds = ts[0]
         axis = fix_axis(axis, ds)
         (bounds, center, display_center) = \
             get_window_parameters(axis, center, width, ds)
@@ -1490,9 +1415,6 @@
                  right_handed=True, fontsize=18, field_parameters=None, data_source=None,
                  method = "integrate", proj_style = None, window_size=8.0,
                  aspect=None):
-        ts = self._initialize_dataset(ds)
-        self.ts = ts
-        ds = self.ds = ts[0]
         axis = fix_axis(axis, ds)
         # proj_style is deprecated, but if someone specifies then it trumps
         # method.
@@ -2018,198 +1940,3 @@
             del kwargs['north_vector']
 
         return AxisAlignedSlicePlot(ds, normal, fields, *args, **kwargs)
-
-def _validate_point(point, ds):
-    if not iterable(point):
-        raise RuntimeError
-    if not isinstance(point, YTArray):
-        point = ds.arr(point, 'code_length')
-    return point
-
-class LinePlot(PlotMPL):
-    r"""
-    A class for constructing line plots
-
-    Parameters
-    ----------
-
-    ds : :class:`yt.data_objects.static_output.Dataset`
-        This is the dataset object corresponding to the
-        simulation output to be plotted.
-    fields : string
-        The name(s) of the field(s) to be plotted.
-    start_point: n-element list, tuple, ndarray, or YTArray
-        Contains the coordinates of the first point for constructing the line.
-        Must contain n elements where n is the dimensionality of the dataset.
-    end_point: n-element list, tuple, ndarray, or YTArray
-        Contains the coordinates of the first point for constructing the line.
-        Must contain n elements where n is the dimensionality of the dataset.
-    resolution: int
-        How many points to sample between start_point and end_point for constructing
-        the line plot
-    """
-
-    def __init__(self, ds, fields, start_point, end_point, resolution,
-                 figure_size=5., aspect=None, fontsize=18., labels=None):
-        """
-        Sets up figure and axes
-        """
-        self.handler = ds.coordinates
-        self.ds = ds
-        if aspect is None:
-            aspect = 1.
-        fontscale = fontsize / 18.
-        ax_text_size = [1.2*fontscale, 0.9*fontscale]
-        top_buff_size = 0.3*fontscale
-
-        if iterable(figure_size):
-            x_fig_size = figure_size[0]
-            y_fig_size = figure_size[1]
-        else:
-            x_fig_size = figure_size
-            y_fig_size = figure_size/aspect
-
-        x_axis_size = ax_text_size[0]
-        y_axis_size = ax_text_size[1]
-
-        top_buff_size = top_buff_size
-
-        xbins = np.array([x_axis_size, x_fig_size, x_axis_size])
-        ybins = np.array([y_axis_size, y_fig_size, top_buff_size])
-
-        size = [xbins.sum(), ybins.sum()]
-
-        x_frac_widths = xbins/size[0]
-        y_frac_widths = ybins/size[1]
-
-        # axrect is the rectangle defining the area of the
-        # axis object of the plot.  Its range goes from 0 to 1 in
-        # x and y directions.  The first two values are the x,y
-        # start values of the axis object (lower left corner), and the
-        # second two values are the size of the axis object.  To get
-        # the upper right corner, add the first x,y to the second x,y.
-        axrect = (
-            x_frac_widths[0],
-            y_frac_widths[0],
-            x_frac_widths[1],
-            y_frac_widths[1],
-        )
-
-        start_point = _validate_point(start_point, ds)
-        end_point = _validate_point(end_point, ds)
-
-        super(LinePlot, self).__init__(size, axrect, None, None)
-
-        self.add_plot(fields, start_point, end_point, resolution, labels=labels)
-        self._xlabel = ("Arc Length [Arb. Units]", 14.)
-        self._ylabel = ("Field Value [Arb. Units]", 14.)
-
-    def add_plot(self, fields, start_point, end_point, resolution, labels=None):
-        r"""
-        Used to add plots to the figure
-
-        parameters
-        ----------
-
-        fields : A single field tuple or a list of field tuples
-            The fields to plot
-        start_point : list, tuple, ndarray, or YTArray
-            The coordinates of the start point of the line plot. Length of
-            iterable should be equal to the number of dimensions of the
-            dataset
-        end_point : list, tuple, ndarray, or YTArray
-            The coordinates of the end point of the line plot. Length of
-            iterable should be equal to the number of dimensions of the
-            dataset
-        resolution : integer
-            How many points to sample between start_point and end_point for
-            constructing the line plot
-        """
-        if labels is None:
-            labels = {}
-        if not isinstance(fields, list):
-            fields = [fields]
-        for field in fields:
-            if field not in labels:
-                labels[field] = field[1]
-            x, y = self.handler.line_plot(
-                field, start_point, end_point, resolution)
-            self.axes.plot(x, y, label=labels[field])
-
-    def add_legend(self):
-        r"""
-        Adds a legend to the `LinePlot` instance
-        """
-        self.axes.legend()
-
-    def set_xlabel(self, label, fontsize=14):
-        r"""
-        Method for setting the x-label
-
-        parameters
-        ----------
-        label : string
-            The label for the x-axis
-        """
-        self._xlabel = (label, fontsize)
-
-    def set_ylabel(self, label, fontsize=14):
-        r"""
-        Method for setting the y-label
-
-        parameters
-        ----------
-        label : string
-            The label for the y-axis
-        """
-        self._ylabel = (label, fontsize)
-
-    def _setup_plot(self):
-        r"""
-        Private method called from either `show` or `save`. Sets x and
-        y labels
-        """
-        self.axes.set_xlabel(self._xlabel[0], fontsize=self._xlabel[1])
-        self.axes.set_ylabel(self._ylabel[0], fontsize=self._ylabel[1])
-
-    def show(self):
-        r"""This will send any existing plots to the IPython notebook.
-
-        If yt is being run from within an IPython session, and it is able to
-        determine this, this function will send any existing plots to the
-        notebook for display.
-
-        If yt can't determine if it's inside an IPython session, it will raise
-        YTNotInsideNotebook.
-        """
-        self._setup_plot()
-        if "__IPYTHON__" in dir(builtins):
-            from IPython.display import display
-            display(self)
-        else:
-            raise YTNotInsideNotebook
-
-    def save(self, name=None, mpl_kwargs=None):
-        r"""
-        Method for saving line plots. No default so user should specify
-        extension
-
-        parameters
-        ----------
-        name : string
-            Name to save file to
-        mpl_kwargs: dict
-            A dictionary of keywoard arguments to pass to matplotlib
-        """
-        self._setup_plot()
-        if name is None:
-            name = str(self.ds)
-        name = os.path.expanduser(name)
-        if name[-1] == os.sep and not os.path.isdir(name):
-            ensure_dir(name)
-        if os.path.isdir(name) and name != str(self.ds):
-            name = name + (os.sep if name[-1] != os.sep else '') + str(self.ds)
-        suffix = get_image_suffix(name)
-        if suffix == '':
-            name = name + '_LinePlot.png'
-        super(LinePlot, self).save(name)

diff -r b5e69528f85359e97533fc4b51bf5f7cd6c381ce -r 457f673b7c51106719338ec6914a026685e1fac1 yt/visualization/profile_plotter.py
--- a/yt/visualization/profile_plotter.py
+++ b/yt/visualization/profile_plotter.py
@@ -60,7 +60,7 @@
         canvas_cls = mpl.FigureCanvasAgg
     return canvas_cls
 
-class PlotContainer(OrderedDict):
+class PlotContainerDict(OrderedDict):
     def __missing__(self, key):
         plot = PlotMPL((10, 8), [0.1, 0.1, 0.8, 0.8], None, None)
         self[key] = plot
@@ -378,7 +378,7 @@
         if plot_specs is None:
             plot_specs = [dict() for p in obj.profiles]
         obj.plot_spec = plot_specs
-        obj.plots = PlotContainer()
+        obj.plots = PlotContainerDict()
         obj.figures = FigureContainer(obj.plots)
         obj.axes = AxesContainer(obj.plots)
         obj._setup_plots()
@@ -1370,7 +1370,7 @@
         if fontscale < 1.0:
             fontscale = np.sqrt(fontscale)
 
-        self._cb_size = 0.0375*figure_size
+        self._cb_size = 0.0375*figure_size[0]
         self._ax_text_size = [1.1*fontscale, 0.9*fontscale]
         self._top_buff_size = 0.30*fontscale
         self._aspect = 1.0


https://bitbucket.org/yt_analysis/yt/commits/12295ae6bc0a/
Changeset:   12295ae6bc0a
User:        ngoldbaum
Date:        2017-06-26 22:31:19+00:00
Summary:     add NotImplementedError for LinePlot on non-cartesian data
Affected #:  1 file

diff -r 457f673b7c51106719338ec6914a026685e1fac1 -r 12295ae6bc0a6e2cbb63c3de3d691725f0f11ea5 yt/geometry/coordinates/coordinate_handler.py
--- a/yt/geometry/coordinates/coordinate_handler.py
+++ b/yt/geometry/coordinates/coordinate_handler.py
@@ -86,6 +86,9 @@
         # pixelizer
         raise NotImplementedError
 
+    def line_plot(self, field, start_point, end_point, resolution):
+        raise NotImplementedError
+
     def distance(self, start, end):
         p1 = self.convert_to_cartesian(start)
         p2 = self.convert_to_cartesian(end)


https://bitbucket.org/yt_analysis/yt/commits/68faebd9e02c/
Changeset:   68faebd9e02c
User:        Alex Lindsay
Date:        2017-06-27 20:46:35+00:00
Summary:     Make y_label work for single fields. Make add_legend work. Account for lack of Exodus units in handler.
Affected #:  3 files

diff -r 12295ae6bc0a6e2cbb63c3de3d691725f0f11ea5 -r 68faebd9e02c58205342c0bfddba542020743195 yt/frontends/exodus_ii/fields.py
--- a/yt/frontends/exodus_ii/fields.py
+++ b/yt/frontends/exodus_ii/fields.py
@@ -33,6 +33,8 @@
 
     def __init__(self, ds, field_list):
         super(ExodusIIFieldInfo, self).__init__(ds, field_list)
+        for name in self:
+            self[name].take_log = False
         # If you want, you can check self.field_list
 
     def setup_fluid_fields(self):

diff -r 12295ae6bc0a6e2cbb63c3de3d691725f0f11ea5 -r 68faebd9e02c58205342c0bfddba542020743195 yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -21,6 +21,7 @@
     _get_vert_fields, \
     cartesian_to_cylindrical, \
     cylindrical_to_cartesian
+from yt import YTArray
 from yt.funcs import mylog
 from yt.units.yt_array import uvstack
 from yt.utilities.lib.pixelization_routines import \
@@ -186,6 +187,8 @@
                                                              end_point,
                                                              resolution, field_data,
                                                              index_offset=offset)
+            arc_length = YTArray(arc_length, start_point.units)
+            plot_values = YTArray(plot_values, field_data.units)
         else:
             ray = self.ds.ray(start_point, end_point)
             arc_length, plot_values = _sample_ray(ray, resolution, field)

diff -r 12295ae6bc0a6e2cbb63c3de3d691725f0f11ea5 -r 68faebd9e02c58205342c0bfddba542020743195 yt/visualization/line_plot.py
--- a/yt/visualization/line_plot.py
+++ b/yt/visualization/line_plot.py
@@ -15,6 +15,7 @@
 
 import numpy as np
 
+from collections import defaultdict
 from yt.funcs import \
     iterable
 from yt.units.unit_object import \
@@ -34,7 +35,7 @@
     def __init__(self, data_source):
         super(LinePlotDictionary, self).__init__(data_source)
         self.known_dimensions = {}
-    
+
     def _sanitize_dimensions(self, item):
         field = self.data_source._determine_fields(item)[0]
         finfo = self.data_source.ds.field_info[field]
@@ -46,7 +47,7 @@
         else:
             ret_item = self.known_dimensions[dimensions]
         return ret_item
-    
+
     def __getitem__(self, item):
         ret_item = self._sanitize_dimensions(item)
         return super(LinePlotDictionary, self).__getitem__(ret_item)
@@ -77,9 +78,9 @@
     end_point: n-element list, tuple, ndarray, or YTArray
         Contains the coordinates of the first point for constructing the line.
         Must contain n elements where n is the dimensionality of the dataset.
-    figure_size: integer or two-element 
+    figure_size: integer or two-element
     resolution: int
-        How many points to sample between start_point and end_point for 
+        How many points to sample between start_point and end_point for
         constructing the line plot
     """
     _plot_type = 'line_plot'
@@ -102,7 +103,9 @@
         self.plots = LinePlotDictionary(data_source)
         if labels is None:
             self.labels = {}
-                
+        else:
+            self.labels = labels
+
         super(LinePlot, self).__init__(data_source, figure_size, fontsize)
 
         for f in self.fields:
@@ -115,14 +118,16 @@
                 self._field_transform[f] = linear_transform
 
         self._setup_plots()
-        
-    def add_legend(self):
+
+    def add_legend(self, field):
         """Adds a legend to the `LinePlot` instance"""
-        self.axes.legend()
+        plot = self.plots[field]
+        plot.axes.legend()
 
     def _setup_plots(self):
         if self._plot_valid is True:
             return
+        dimensions_counter = defaultdict(int)
         for field in self.fields:
             fontscale = self._font_properties._size / 14.
             top_buff_size = 0.3*fontscale
@@ -148,18 +153,16 @@
             )
 
             try:
-                plot_used = True
                 plot = self.plots[field]
             except KeyError:
-                plot_used = False
                 plot = PlotMPL(self.figure_size, axrect, None, None)
                 self.plots[field] = plot
-                
+
             plot._set_font_properties(self._font_properties, None)
 
             x, y = self.ds.coordinates.line_plot(
                 field, self.start_point, self.end_point, self.resolution)
-            
+
             if self._x_unit is None:
                 unit_x = x.units
             else:
@@ -174,7 +177,7 @@
             y = y.to(unit_y)
 
             plot.axes.plot(x, y, label=self.labels[field])
-            
+
             if self._field_transform[field] != linear_transform:
                 if (y < 0).any():
                     plot.axes.set_yscale('symlog')
@@ -184,9 +187,13 @@
             axes_unit_labels = self._get_axes_unit_labels(unit_x, unit_y)
 
             finfo = self.ds.field_info[field]
-            
+
             x_label = r'$\rm{Path\ Length' + axes_unit_labels[0]+'}$'
-            if plot_used:
+
+            finfo = self.ds.field_info[field]
+            dimensions = Unit(finfo.units, registry=self.ds.unit_registry).dimensions
+            dimensions_counter[dimensions] += 1
+            if dimensions_counter[dimensions] > 1:
                 y_label = (r'$\rm{Multiple\ Fields}$' + r'$\rm{' +
                            axes_unit_labels[1]+'}$')
             else:
@@ -242,4 +249,3 @@
     if not isinstance(point, YTArray):
         point = ds.arr(point, 'code_length')
     return point
-


https://bitbucket.org/yt_analysis/yt/commits/aa76b5ff39cc/
Changeset:   aa76b5ff39cc
User:        Alex Lindsay
Date:        2017-06-27 22:02:42+00:00
Summary:     Prevent redrawing of same plot
Affected #:  1 file

diff -r 68faebd9e02c58205342c0bfddba542020743195 -r aa76b5ff39ccf28f52648f8542a9982577613c8e yt/visualization/line_plot.py
--- a/yt/visualization/line_plot.py
+++ b/yt/visualization/line_plot.py
@@ -101,6 +101,7 @@
 
         self.fields = data_source._determine_fields(fields)
         self.plots = LinePlotDictionary(data_source)
+        self.include_legend = defaultdict(bool)
         if labels is None:
             self.labels = {}
         else:
@@ -119,14 +120,16 @@
 
         self._setup_plots()
 
+    @invalidate_plot
     def add_legend(self, field):
         """Adds a legend to the `LinePlot` instance"""
-        plot = self.plots[field]
-        plot.axes.legend()
+        self.include_legend[field] = True
 
     def _setup_plots(self):
         if self._plot_valid is True:
             return
+        for plot in self.plots.values():
+            plot.axes.cla()
         dimensions_counter = defaultdict(int)
         for field in self.fields:
             fontscale = self._font_properties._size / 14.
@@ -206,6 +209,9 @@
             if field in self._titles:
                 plot.axes.set_title(self._titles[field])
 
+            if self.include_legend[field]:
+                plot.axes.legend()
+
     @invalidate_plot
     def set_x_unit(self, unit_name):
         """Set the unit to use along the x-axis


https://bitbucket.org/yt_analysis/yt/commits/2735e293c2b3/
Changeset:   2735e293c2b3
User:        Alex Lindsay
Date:        2017-06-27 22:31:28+00:00
Summary:     Update rst documentation
Affected #:  4 files

diff -r aa76b5ff39ccf28f52648f8542a9982577613c8e -r 2735e293c2b3dba69336d04ba9b8858532b9e7cf doc/source/cookbook/simple_1d_line_plot.py
--- /dev/null
+++ b/doc/source/cookbook/simple_1d_line_plot.py
@@ -0,0 +1,14 @@
+import yt
+
+# Load the dataset
+ds = yt.load("SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1)
+
+# Create a line plot of the variables 'u' and 'v' with 1000 sampling points evenly spaced
+# between the coordinates (0, 0, 0) and (0, 1, 0)
+ln = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], (0, 0, 0), (0, 1, 0), 1000)
+
+# Add a legend
+ln.add_legend()
+
+# Save the line plot
+ln.save()

diff -r aa76b5ff39ccf28f52648f8542a9982577613c8e -r 2735e293c2b3dba69336d04ba9b8858532b9e7cf doc/source/cookbook/simple_plots.rst
--- a/doc/source/cookbook/simple_plots.rst
+++ b/doc/source/cookbook/simple_plots.rst
@@ -43,6 +43,14 @@
 
 .. yt_cookbook:: simple_phase.py
 
+Simple 1D Line Plotting
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This script shows how to make a ``LinePlot`` through a dataset.
+See :ref:`how-to-1d-line-plot` for more information.
+
+.. yt_cookbook:: simple_1d_line_plot.py
+
 Simple Probability Distribution Functions
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -237,11 +245,3 @@
 When creating movies of multiple outputs from the same simulation (see :ref:`time-series-analysis`), it can be helpful to include a timestamp and the physical scale of each individual output.  This is simply achieved using the :ref:`annotate_timestamp() <annotate-timestamp>` and :ref:`annotate_scale() <annotate-scale>` callbacks on your plots.  For more information about similar plot modifications using other callbacks, see the section on :ref:`Plot Modifications <callbacks>`.
 
 .. yt_cookbook:: annotate_timestamp_and_scale.py
-
-Simple 1D Unstructured Mesh Line Plotting
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-This script shows how to make a ``LinePlot`` through an ``UnstructuredMesh``
-data-set. See :ref:`how-to-1d-unstructured-mesh` for more information.
-
-.. yt_cookbook:: simple_unstructured_1d.py

diff -r aa76b5ff39ccf28f52648f8542a9982577613c8e -r 2735e293c2b3dba69336d04ba9b8858532b9e7cf doc/source/cookbook/simple_unstructured_1d.py
--- a/doc/source/cookbook/simple_unstructured_1d.py
+++ /dev/null
@@ -1,24 +0,0 @@
-import yt
-
-# Load the dataset
-ds = yt.load("SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1)
-
-# Create a line plot of the variables 'u' and 'v' with 1000 sampling points evenly spaced
-# between the coordinates (0, 0, 0) and (0, 1, 0)
-ln = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], (0, 0, 0), (0, 1, 0), 1000)
-
-# Add a plot to the LinePlot instance of the variable 'p' with 100 sampling points evenly
-# spaced between (0.5, 0, 0) and (1, 1, 0). Also create a label for p
-ln.add_plot(('all', 'p'), (0.5, 0, 0), (1, 1, 0), 100, labels={('all', 'p') : 'p'})
-
-# Add a legend
-ln.add_legend()
-
-# Set xlabel
-ln.set_xlabel("Arc Length (cm)")
-
-# Set ylabel
-ln.set_ylabel("Field Values [Arb. Units]")
-
-# Save the line plot. Note that the save string is a required argument
-ln.save("line_plot.png")

diff -r aa76b5ff39ccf28f52648f8542a9982577613c8e -r 2735e293c2b3dba69336d04ba9b8858532b9e7cf doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -1057,10 +1057,10 @@
 
 .. _how-to-1d-unstructured-mesh:
 
-1D Sampling on Unstructured Meshes
-----------------------------------
+1D Line Sampling
+----------------
 
-YT has the ability to sample unstructured mesh data-sets along arbitrary lines
+YT has the ability to sample datasets along arbitrary lines
 and plot the result. You must supply five arguments to the ``LinePlot``
 class. They are enumerated below:
 
@@ -1082,41 +1082,30 @@
 
    ds = yt.load("SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1)
    ln = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], (0, 0, 0), (0, 1, 0), 1000)
-   ln.save("first_test.png")
+   ln.save()
 
-You can also add plots to existing ``LinePlot`` instances with ``add_plot`` as shown
-below:
-
-.. code-block:: python
+If working in a Jupyter Notebook, ``LinePlot`` also has the ``show()`` method.
 
-   ln.add_plot(('all', 'p'), (0, 0, 0), (1, 1, 0), 1000)
-   ln.save("added_plot.png")
+You can can add a legend to a 1D sampling plot. The legend process takes two steps:
 
-Note that the beginning and end-points of multiple plots can be different. If
-working in an Jupyter Notebook, ``LinePlot`` also has the ``show()`` method.
-
-You can also create legends and add x and y axes labels to these 1D sampling
-plots. The legend process takes two steps:
-
-1. When instantiating the ``LinePlot`` or invoking ``add_plot`` pass a dictionary of
+1. When instantiating the ``LinePlot``, pass a dictionary of
    labels with keys corresponding to the field names
 2. Call the ``LinePlot`` ``add_legend`` method
 
-X- and Y- axis labels are set simply with ``set_xlabel`` and ``set_ylabel``
-methods. The below code snippet combines all the features we've discussed:
+X- and Y- axis units can be set with ``set_x_unit`` and ``set_unit`` methods
+respectively. The below code snippet combines all the features we've discussed:
 
 .. python-script::
 
    import yt
-   ds = yt.load("SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1)
-   ln = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], (0, 0, 0), (0, 1, 0), 1000,
-                    labels={('all', 'u') : r"u$_s$", ('all', 'v') : r"v$_s$"})
-   ln.add_plot([('all', 'v'), ('all', 'u')], (0, 0, 0), (1, 1, 0), 1000,
-               labels={('all', 'u') : r"u$_l$", ('all', 'v') : r"v$_l$"})
-   ln.add_legend()
-   ln.set_xlabel("Arc Length (cm)")
-   ln.set_ylabel(r"Velocity (m s$^{-1}$)")
-   ln.save("line_plot.png")
+
+   ds = yt.load('IsolatedGalaxy/galaxy0030/galaxy0030')
+
+   plot = yt.LinePlot(ds, 'density', [0, 0, 0], [1, 1, 1], 512)
+   plot.add_legend('density')
+   plot.set_x_unit('cm')
+   plot.set_unit('density', 'kg/cm**3')
+   plot.save()
 
 
 .. _how-to-make-2d-profiles:


https://bitbucket.org/yt_analysis/yt/commits/1387c8c5c429/
Changeset:   1387c8c5c429
User:        Alex Lindsay
Date:        2017-06-27 22:46:53+00:00
Summary:     Update tests
Affected #:  2 files

diff -r 2735e293c2b3dba69336d04ba9b8858532b9e7cf -r 1387c8c5c429eabbbc320d0ca4d70bf51dfd88f6 doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -1107,6 +1107,15 @@
    plot.set_unit('density', 'kg/cm**3')
    plot.save()
 
+If a list of fields is passed to ``LinePlot``, yt will create a number of
+individual figures equal to the number of different dimensional
+quantities. E.g. if ``LinePlot`` receives two fields with units of "length/time"
+and a field with units of "temperature", two different figures will be created,
+one with plots of the "length/time" fields and another with the plot of the
+"temperature" field. It is only necessary to call ``add_legend``
+for one field of a multi-field plot to produce a legend containing all the
+labels passed in the initial construction of the ``LinePlot`` instance.
+
 
 .. _how-to-make-2d-profiles:
 

diff -r 2735e293c2b3dba69336d04ba9b8858532b9e7cf -r 1387c8c5c429eabbbc320d0ca4d70bf51dfd88f6 yt/visualization/tests/test_line_plots.py
--- a/yt/visualization/tests/test_line_plots.py
+++ b/yt/visualization/tests/test_line_plots.py
@@ -33,6 +33,7 @@
     return test
 
 tri2 = "SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e"
+iso_galaxy = "IsolatedGalaxy/galaxy0030/galaxy0030"
 
 @requires_ds(tri2)
 def test_line_plot():
@@ -40,21 +41,20 @@
     fields = [field for field in ds.field_list if field[0] == 'all']
     yield compare(ds, fields, (0, 0, 0), (1, 1, 0), 1000, "answers_line_plot")
 
+ at requires_ds(iso_galaxy)
 def test_line_plot_methods():
     # Perform I/O in safe place instead of yt main dir
     tmpdir = tempfile.mkdtemp()
     curdir = os.getcwd()
     os.chdir(tmpdir)
 
-    # hexahedral ds
-    ds = fake_hexahedral_ds()
+    ds = data_dir_load(iso_galaxy)
 
-    ln = yt.LinePlot(ds, ds.field_list, (0, 0, -.25), (0, 0, .25), 100)
-    ln.add_plot(ds.field_list, (0, 0, -.25), (0, 0, .25), 100)
-    ln.add_legend()
-    ln.set_xlabel("Test x label")
-    ln.set_ylabel("Test y label")
-    ln.save()
+    plot = yt.LinePlot(ds, 'density', [0, 0, 0], [1, 1, 1], 512)
+    plot.add_legend('density')
+    plot.set_x_unit('cm')
+    plot.set_unit('density', 'kg/cm**3')
+    plot.save()
 
     os.chdir(curdir)
     # clean up


https://bitbucket.org/yt_analysis/yt/commits/3249ecb9ce42/
Changeset:   3249ecb9ce42
User:        Alex Lindsay
Date:        2017-06-28 13:53:39+00:00
Summary:     Address some failed tests and change ln to plot.
Affected #:  4 files

diff -r 1387c8c5c429eabbbc320d0ca4d70bf51dfd88f6 -r 3249ecb9ce42777551963f05fd3d4ed9207ffb76 doc/source/cookbook/simple_1d_line_plot.py
--- a/doc/source/cookbook/simple_1d_line_plot.py
+++ b/doc/source/cookbook/simple_1d_line_plot.py
@@ -5,10 +5,10 @@
 
 # Create a line plot of the variables 'u' and 'v' with 1000 sampling points evenly spaced
 # between the coordinates (0, 0, 0) and (0, 1, 0)
-ln = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], (0, 0, 0), (0, 1, 0), 1000)
+plot = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], (0, 0, 0), (0, 1, 0), 1000)
 
 # Add a legend
-ln.add_legend()
+plot.add_legend(('all', 'v'))
 
 # Save the line plot
-ln.save()
+plot.save()

diff -r 1387c8c5c429eabbbc320d0ca4d70bf51dfd88f6 -r 3249ecb9ce42777551963f05fd3d4ed9207ffb76 doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -1081,8 +1081,8 @@
 .. code-block:: python
 
    ds = yt.load("SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1)
-   ln = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], (0, 0, 0), (0, 1, 0), 1000)
-   ln.save()
+   plot = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], (0, 0, 0), (0, 1, 0), 1000)
+   plot.save()
 
 If working in a Jupyter Notebook, ``LinePlot`` also has the ``show()`` method.
 

diff -r 1387c8c5c429eabbbc320d0ca4d70bf51dfd88f6 -r 3249ecb9ce42777551963f05fd3d4ed9207ffb76 tests/tests.yaml
--- a/tests/tests.yaml
+++ b/tests/tests.yaml
@@ -64,7 +64,7 @@
     - yt/analysis_modules/photon_simulator/tests/test_spectra.py
     - yt/analysis_modules/photon_simulator/tests/test_sloshing.py
 
-  local_unstructured_006:
+  local_unstructured_007:
     - yt/visualization/volume_rendering/tests/test_mesh_render.py
     - yt/visualization/tests/test_mesh_slices.py:test_tri2
     - yt/visualization/tests/test_mesh_slices.py:test_quad2

diff -r 1387c8c5c429eabbbc320d0ca4d70bf51dfd88f6 -r 3249ecb9ce42777551963f05fd3d4ed9207ffb76 yt/visualization/tests/test_line_plots.py
--- a/yt/visualization/tests/test_line_plots.py
+++ b/yt/visualization/tests/test_line_plots.py
@@ -15,6 +15,9 @@
     requires_ds, \
     data_dir_load, \
     GenericImageTest
+import os
+import tempfile
+import shutil
 
 def setup():
     """Test specific setup."""


https://bitbucket.org/yt_analysis/yt/commits/31f1a4ccea04/
Changeset:   31f1a4ccea04
User:        Alex Lindsay
Date:        2017-06-28 14:11:33+00:00
Summary:     Merge branch 'master' into line_plot_plus_quad9
Affected #:  70 files

diff -r 3249ecb9ce42777551963f05fd3d4ed9207ffb76 -r 31f1a4ccea0427e60c645abd49a11345168de4fe appveyor.yml
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -8,6 +8,7 @@
 
   matrix:
       - PYTHON_VERSION: "3.6"
+      - PYTHON_VERSION: "2.7"
 
 platform:
     -x64
@@ -27,7 +28,7 @@
     - "python --version"
 
     # Install specified version of numpy and dependencies
-    - "conda install -q --yes -c conda-forge numpy scipy nose setuptools ipython Cython sympy fastcache h5py matplotlib flake8 "
+    - "conda install -q --yes -c conda-forge numpy scipy nose setuptools ipython Cython sympy fastcache h5py matplotlib flake8 mock"
     - "pip install -e ."
 
 # Not a .NET project

diff -r 3249ecb9ce42777551963f05fd3d4ed9207ffb76 -r 31f1a4ccea0427e60c645abd49a11345168de4fe doc/install_script.sh
--- a/doc/install_script.sh
+++ b/doc/install_script.sh
@@ -13,62 +13,23 @@
 # If you do not have a working compiler environment, use the following 
 # configuration:
 
-INST_CONDA=1       # Should yt's dependencies be installed using miniconda?
 INST_YT_SOURCE=0   # Should yt itself be installed from source?
 
-# If you want to install yt's dependencies using conda but want to build yt
-# itself from source, use the following configuration:
-
-# INST_CONDA=1
-# INST_YT_SOURCE=1
-
-# If you would like to build yt and all dependencies from source, then
-# use the following configuration by uncommenting the lines below.
-# NOTE: Building yt's dependencies from source will cause the install script
-# to require substantially more time to finish.
-
-# INST_CONDA=0
-# INST_YT_SOURCE=1
-
-BRANCH="master" # This is the branch we will install from for source installs
-
 # What follows are some other options that you may or may not need to change.
 
-# Here's where you put the HDF5 path if you like; otherwise it'll download it
-# and install it on its own
-#HDF5_DIR=
-
-# If you've got yt some other place, set this to point to it. The script will
-# already check the current directory and the one above it in the tree.
+# If you've got a clone of the yt repository some other place, set this to
+# point to it. The script will already check the current directory and the one
+# above it in the tree.
 YT_DIR=""
 
 # These options can be set to customize the installation.
 
-INST_PY3=1          # Install Python 3 instead of Python 2. If this is turned
-                    # on, all Python packages (including yt) will be installed
-                    # in Python 3.
-INST_GIT=1          # Install git or not?  If git is not already installed, yt
-                    # cannot be installed from source. Ignored if INST_CONDA=0
-INST_HG=0           # Install Mercurial or not? Ignored if INST_CONDA=0.
-INST_EMBREE=0       # Install dependencies needed for Embree-accelerated 
-                    # ray tracing
-
-# These options control whether low-level system libraries are installed
-# they are necessary for building yt's dependencies from source and are 
-# ignored when INST_CONDA=1
-
-INST_ZLIB=1     # On some systems (Kraken) matplotlib has issues with
-                # the system zlib, which is compiled statically.
-                # If need be, you can turn this off. 
-INST_BZLIB=1    # On some systems, libbzip2 is missing.  This can
-                # lead to broken mercurial installations.
-INST_PNG=1      # Install a local libpng?  Same things apply as with zlib.
-INST_FTYPE=1    # Install FreeType2 locally?
-INST_SQLITE3=1  # Install a local version of SQLite3?
-INST_0MQ=1      # Install 0mq (for IPython) and affiliated bindings?
-
-# These variables control whether optional dependencies are installed
-
+INST_PY3=1      # Install Python 3 instead of Python 2. If this is turned on,
+                # all Python packages (including yt) will be installed
+                # in Python 3.
+INST_GIT=1      # Install git or not?  If git is not already installed, yt
+                # cannot be installed from source.
+INST_EMBREE=0   # Install dependencies needed for Embree-accelerated ray tracing
 INST_PYX=0      # Install PyX?  Sometimes PyX can be problematic without a
                 # working TeX installation.
 INST_ROCKSTAR=0 # Install the Rockstar halo finder?
@@ -77,83 +38,47 @@
 INST_ASTROPY=0  # Install astropy?
 INST_NOSE=1     # Install nose?
 INST_NETCDF4=1  # Install netcdf4 and its python bindings?
-
-# These options allow you to customize the builds of yt dependencies.
-# They are only used if INST_CONDA=0.
-
-# If you need to pass anything to the matplotlib build, do so here.
-MPL_SUPP_LDFLAGS=""
-MPL_SUPP_CFLAGS=""
-MPL_SUPP_CXXFLAGS=""
+INST_HG=0       # Install Mercurial or not?
 
-# If you need to supply arguments to the NumPy or SciPy build, supply them here
-# This one turns on gfortran manually:
-#NUMPY_ARGS="--fcompiler=gnu95"
-# If you absolutely can't get the fortran to work, try this:
-#NUMPY_ARGS="--fcompiler=fake"
+# This is the branch we will install from for INST_YT_SOURCE=1
+BRANCH="master"
 
-# If you want to spawn multiple Make jobs, here's the place to set the
-# arguments.  For instance, "-j4"
-MAKE_PROCS=""
-
-# These variables control which miniconda version and yt recipe are used
-# when INST_CONDA=1.
+# These variables control which miniconda version is used
 
 MINICONDA_URLBASE="http://repo.continuum.io/miniconda"
 MINICONDA_VERSION="latest"
 
-if [ ${REINST_YT} ] && [ ${REINST_YT} -eq 1 ] && [ -n ${YT_DEST} ]
+if [ ! -z "${CONDA_DEFAULT_ENV}" ]
 then
-    DEST_DIR=${YT_DEST}
-    INST_CONDA=0
+    echo "Aborting the yt installation because you appear to already"
+    echo "have a conda environment activated. Either deactivate it with:"
+    echo
+    echo "    $ source deactivate"
+    echo
+    echo "or install yt into your current environment with:"
+    echo
+    echo "    $ conda install -c conda-forge yt"
+    echo
+    exit 1
 fi
-
-if [ $INST_CONDA -ne 0 ]
+DEST_SUFFIX="yt-conda"
+if [ -n "${PYTHONPATH}" ]
 then
-    if [ ! -z "${CONDA_DEFAULT_ENV}" ]
-    then
-        echo "Aborting the yt installation because you appear to already"
-        echo "have a conda environment activated. Either deactivate it with:"
-        echo
-        echo "    $ source deactivate"
-        echo
-        echo "or install yt into your current environment with:"
-        echo
-        echo "    $ conda install -c conda-forge yt"
-        echo
-        exit 1
-    fi
-    DEST_SUFFIX="yt-conda"
-    if [ -n "${PYTHONPATH}" ]
-    then
-        echo "WARNING WARNING WARNING WARNING WARNING WARNING WARNING"
-        echo "*******************************************************"
-        echo
-        echo "The PYTHONPATH environment variable is set to:"
-        echo
-        echo "    $PYTHONPATH"
-        echo
-        echo "If dependencies of yt (numpy, scipy, matplotlib) are installed"
-        echo "to this path, this may cause issues. Exit the install script"
-        echo "with Ctrl-C and unset PYTHONPATH if you are unsure."
-        echo "Hit enter to continue."
-        echo
-        echo "WARNING WARNING WARNING WARNING WARNING WARNING WARNING"
-        echo "*******************************************************"
-        read -p "[hit enter]"
-    fi
-else
-    if [ $INST_YT_SOURCE -eq 0 ]
-    then
-        echo "yt must be compiled from source if INST_CONDA is set to 0"
-        echo "Please set INST_YT_SOURCE to 1 and re-run."
-        exit 1
-    fi
-    if [ $INST_GIT -eq 1 ]
-    then
-        INST_GIT=0
-    fi
-    DEST_SUFFIX="yt-`uname -m`"
+    echo "WARNING WARNING WARNING WARNING WARNING WARNING WARNING"
+    echo "*******************************************************"
+    echo
+    echo "The PYTHONPATH environment variable is set to:"
+    echo
+    echo "    $PYTHONPATH"
+    echo
+    echo "If dependencies of yt (numpy, scipy, matplotlib) are installed"
+    echo "to this path, this may cause issues. Exit the install script"
+    echo "with Ctrl-C and unset PYTHONPATH if you are unsure."
+    echo "Hit enter to continue."
+    echo
+    echo "WARNING WARNING WARNING WARNING WARNING WARNING WARNING"
+    echo "*******************************************************"
+    read -p "[hit enter]"
 fi
 
 if [ -z "${DEST_DIR}" ]
@@ -204,31 +129,18 @@
 function write_config
 {
     CONFIG_FILE=${DEST_DIR}/.yt_config
-
-    echo INST_GIT=${INST_GIT} > ${CONFIG_FILE}
-    echo INST_ZLIB=${INST_ZLIB} >> ${CONFIG_FILE}
-    echo INST_BZLIB=${INST_BZLIB} >> ${CONFIG_FILE}
-    echo INST_PNG=${INST_PNG} >> ${CONFIG_FILE}
-    echo INST_FTYPE=${INST_FTYPE} >> ${CONFIG_FILE}
-    echo INST_SQLITE3=${INST_SQLITE3} >> ${CONFIG_FILE}
+    echo INST_YT_SOURCE=${INST_YT_SOURCE} > ${CONFIG_FILE}
+    echo INST_GIT=${INST_GIT} >> ${CONFIG_FILE}
     echo INST_PYX=${INST_PYX} >> ${CONFIG_FILE}
-    echo INST_0MQ=${INST_0MQ} >> ${CONFIG_FILE}
     echo INST_PY3=${INST_PY3} >> ${CONFIG_FILE}
     echo INST_ROCKSTAR=${INST_ROCKSTAR} >> ${CONFIG_FILE}
     echo INST_SCIPY=${INST_SCIPY} >> ${CONFIG_FILE}
+    echo INST_EMBREE=${INST_EMBREE} >> ${CONFIG_FILE}
+    echo INST_H5PY=${INST_H5PY} >> ${CONFIG_FILE}
+    echo INST_ASTROPY=${INST_ASTROPY} >> ${CONFIG_FILE}
+    echo INST_NOSE=${INST_NOSE} >> ${CONFIG_FILE}
+
     echo YT_DIR=${YT_DIR} >> ${CONFIG_FILE}
-    echo MPL_SUPP_LDFLAGS=${MPL_SUPP_LDFLAGS} >> ${CONFIG_FILE}
-    echo MPL_SUPP_CFLAGS=${MPL_SUPP_CFLAGS} >> ${CONFIG_FILE}
-    echo MPL_SUPP_CXXFLAGS=${MPL_SUPP_CXXFLAGS} >> ${CONFIG_FILE}
-    echo MAKE_PROCS=${MAKE_PROCS} >> ${CONFIG_FILE}
-    if [ ${HDF5_DIR} ]
-    then
-        echo ${HDF5_DIR} >> ${CONFIG_FILE}
-    fi
-    if [ ${NUMPY_ARGS} ]
-    then
-        echo ${NUMPY_ARGS} >> ${CONFIG_FILE}
-    fi
 }
 
 function get_willwont
@@ -246,147 +158,22 @@
     MYHOST=`hostname -s`  # just give the short one, not FQDN
     MYHOSTLONG=`hostname` # FQDN, for Ranger
     MYOS=`uname -s`       # A guess at the OS
-    if [ "${MYHOST##kraken}" != "${MYHOST}" ]
-    then
-        echo "Looks like you're on Kraken."
-        echo
-        echo " ******************************************"
-        echo " * It may be better to use the yt module! *"
-        echo " *                                        *"
-        echo " *   $ module load yt                     *"
-        echo " *                                        *"
-        echo " ******************************************"
-        echo
-        echo "IF YOU CHOOSE TO PROCEED:"
-        echo "YOU MUST BE IN THE GNU PROGRAMMING ENVIRONMENT"
-        echo "   $ module swap PrgEnv-pgi PrgEnv-gnu"
-        echo
-        return
-    fi
-    if [ "${MYHOST##nautilus}" != "${MYHOST}" ]
-    then
-        echo "Looks like you're on Nautilus."
-        echo
-        echo " ******************************************"
-        echo " * It may be better to use the yt module! *"
-        echo " *                                        *"
-        echo " *   $ module load yt                     *"
-        echo " *                                        *"
-        echo " ******************************************"
-        echo
-        echo "NOTE: YOU MUST BE IN THE GNU PROGRAMMING ENVIRONMENT"
-        echo "   $ module swap PE-intel PE-gnu"
-        echo
-        echo "Additionally, note that by default, yt will OVERWRITE"
-        echo "any existing installations from Kraken!  You might want"
-        echo "to adjust the variable DEST_SUFFIX in the install script."
-        echo
-        return
-    fi
-    if [ "${MYHOST##verne}" != "${MYHOST}" ]
-    then
-        echo "Looks like you're on Verne."
-        echo
-        echo "NOTE: YOU MUST BE IN THE GNU PROGRAMMING ENVIRONMENT"
-        echo "This command will take care of that for you:"
-        echo
-        echo "   $ module swap PE-pgi PE-gnu"
-        echo
-    fi
-    if [ "${MYHOST##steele}" != "${MYHOST}" ]
-    then
-        echo "Looks like you're on Steele."
-        echo
-        echo "NOTE: YOU MUST BE IN THE GNU PROGRAMMING ENVIRONMENT"
-        echo "These commands should take care of that for you:"
-        echo
-        echo "   $ module purge"
-        echo "   $ module load gcc"
-        echo
-    fi
-    if [ "${MYHOST##midway}" != "${MYHOST}" ]
-    then
-        echo "Looks like you're on Midway."
-        echo
-        echo " ******************************************"
-        echo " * It may be better to use the yt module! *"
-        echo " *                                        *"
-        echo " *   $ module load yt                     *"
-        echo " *                                        *"
-        echo " ******************************************"
-        echo
-        return
-    fi
     if [ "${MYOS##Darwin}" != "${MYOS}" ]
     then
-        echo "Looks like you're running on Mac OSX."
+        echo "Looks like you're running on MacOS."
         echo
         echo "NOTE: you must have the Xcode command line tools installed."
         echo
-        echo "The instructions for obtaining these tools varies according"
-        echo "to your exact OS version.  On older versions of OS X, you"
-        echo "must register for an account on the apple developer tools"
-        echo "website: https://developer.apple.com/downloads to obtain the"
-        echo "download link."
-        echo
-        echo "We have gathered some additional instructions for each"
-        echo "version of OS X below. If you have trouble installing yt"
-        echo "after following these instructions, don't hesitate to contact"
-        echo "the yt user's e-mail list."
-        echo
-        echo "You can see which version of OSX you are running by clicking"
-        echo "'About This Mac' in the apple menu on the left hand side of"
-        echo "menu bar.  We're assuming that you've installed all operating"
-        echo "system updates; if you have an older version, we suggest"
-        echo "running software update and installing all available updates."
-        echo
-        echo "OS X 10.5.8: search for and download Xcode 3.1.4 from the"
-        echo "Apple developer tools website."
-        echo
-        echo "OS X 10.6.8: search for and download Xcode 3.2 from the Apple"
-        echo "developer tools website.  You can either download the"
-        echo "Xcode 3.2.2 Developer Tools package (744 MB) and then use"
-        echo "Software Update to update to XCode 3.2.6 or"
-        echo "alternatively, you can download the Xcode 3.2.6/iOS SDK"
-        echo "bundle (4.1 GB)."
-        echo
-        echo "OS X 10.7.5: download Xcode 4.2 from the mac app store"
-        echo "(search for Xcode)."
-        echo "Alternatively, download the Xcode command line tools from"
-        echo "the Apple developer tools website."
-        echo
-        echo "OS X 10.8.4, 10.9, 10.10, and 10.11:"
-        echo "download the appropriate version of Xcode from the"
-        echo "mac app store (search for Xcode)."
+        echo "Download the appropriate version of Xcode from the"
+        echo "Mac App Store (search for Xcode)."
         echo
         echo "Additionally, you will have to manually install the Xcode"
         echo "command line tools."
         echo
-        echo "For OS X 10.8, see:"
-        echo "http://stackoverflow.com/questions/9353444"
-        echo
-        echo "For OS X 10.9 and newer the command line tools can be installed"
+        echo "For MacOS 10.10 and newer the command line tools can be installed"
         echo "with the following command:"
         echo "    xcode-select --install"
         echo
-        if [ $INST_CONDA -eq 0 ]
-        then
-            echo "For OS X 10.11, you will additionally need to install the"
-            echo "OpenSSL library using a package manager like homebrew or"
-            echo "macports."
-            echo
-            echo "If your install fails with a message like"
-            echo "    ImportError: cannot import HTTPSHandler"
-            echo "then you do not have the OpenSSL headers available in a"
-            echo "location visible to your C compiler. Consider setting"
-            echo "INST_CONDA=1 instead, as conda's python bundles OpenSSL."
-        fi
-        OSX_VERSION=`sw_vers -productVersion`
-        if [ "${OSX_VERSION##10.8}" != "${OSX_VERSION}" ]
-        then
-            MPL_SUPP_CFLAGS="${MPL_SUPP_CFLAGS} -mmacosx-version-min=10.7"
-            MPL_SUPP_CXXFLAGS="${MPL_SUPP_CXXFLAGS} -mmacosx-version-min=10.7"
-        fi
     fi
     if [ -f /etc/redhat-release ]
     then
@@ -462,31 +249,6 @@
         echo " to avoid conflicts with other command-line programs "
         echo " (like eog and evince, for example)."
     fi
-    if [ $INST_SCIPY -eq 1 ]
-    then
-    echo
-    echo "Looks like you've requested that the install script build SciPy."
-    echo
-    echo "If the SciPy build fails, please uncomment one of the the lines"
-    echo "at the top of the install script that sets NUMPY_ARGS, delete"
-    echo "any broken installation tree, and re-run the install script"
-    echo "verbatim."
-    echo
-    echo "If that doesn't work, don't hesitate to ask for help on the yt"
-    echo "user's mailing list."
-    echo
-    fi
-    if [ ! -z "${CFLAGS}" ]
-    then
-        echo "******************************************"
-        echo "******************************************"
-        echo "**                                      **"
-        echo "**    Your CFLAGS is not empty.         **"
-        echo "**    This can break h5py compilation.  **"
-        echo "**                                      **"
-        echo "******************************************"
-        echo "******************************************"
-    fi
 }
 
 function log_cmd
@@ -506,11 +268,6 @@
         echo "Please set INST_YT_SOURCE to 1 and re-run the install script."
         exit 1
     fi
-    if [ $INST_CONDA -eq 0 ]
-    then
-        echo "Embree support has not yet been implemented for INST_CONDA=0."
-        exit 1
-    fi
     if [ `uname` = "Darwin" ]
     then
         EMBREE="embree-2.8.0.x86_64.macosx"
@@ -538,17 +295,6 @@
     fi
 fi
 
-if [ $INST_NETCDF4 -ne 0 ]
-then
-    if [ $INST_CONDA -eq 0 ]
-    then
-        echo "This script can only install netcdf4 through conda."
-        echo "Please set INST_CONDA to 1 to install netcdf4"
-        echo "Setting INST_NETCDF4=0"
-        INST_NETCDF4=0
-    fi
-fi
-
 echo
 echo
 echo "========================================================================"
@@ -562,10 +308,6 @@
 echo "the script if you aren't such a fan."
 echo
 
-printf "%-18s = %s so I " "INST_CONDA" "${INST_CONDA}"
-get_willwont ${INST_CONDA}
-echo "be installing a conda-based python environment"
-
 printf "%-18s = %s so I " "INST_YT_SOURCE" "${INST_YT_SOURCE}"
 get_willwont ${INST_YT_SOURCE}
 echo "be compiling yt from source"
@@ -582,29 +324,6 @@
 get_willwont ${INST_EMBREE}
 echo "be installing Embree"
 
-if [ $INST_CONDA -eq 0 ]
-then
-    printf "%-18s = %s so I " "INST_ZLIB" "${INST_ZLIB}"
-    get_willwont ${INST_ZLIB}
-    echo "be installing zlib"
-
-    printf "%-18s = %s so I " "INST_BZLIB" "${INST_BZLIB}"
-    get_willwont ${INST_BZLIB}
-    echo "be installing bzlib"
-
-    printf "%-18s = %s so I " "INST_PNG" "${INST_PNG}"
-    get_willwont ${INST_PNG}
-    echo "be installing libpng"
-
-    printf "%-18s = %s so I " "INST_FTYPE" "${INST_FTYPE}"
-    get_willwont ${INST_FTYPE}
-    echo "be installing freetype2"
-
-    printf "%-18s = %s so I " "INST_SQLITE3" "${INST_SQLITE3}"
-    get_willwont ${INST_SQLITE3}
-    echo "be installing SQLite3"
-fi
-
 printf "%-18s = %s so I " "INST_PYX" "${INST_PYX}"
 get_willwont ${INST_PYX}
 echo "be installing PyX"
@@ -627,16 +346,6 @@
 
 echo
 
-if [ $INST_CONDA -eq 0 ]
-then
-    if [ -z "$HDF5_DIR" ]
-    then
-        echo "HDF5_DIR is not set, so I will be installing HDF5"
-    else
-        echo "HDF5_DIR=${HDF5_DIR} , so I will not be installing HDF5"
-    fi
-fi
-
 echo
 echo "Installation will be to"
 echo "  ${DEST_DIR}"
@@ -654,21 +363,13 @@
    host_specific
 fi
 
-if [ $INST_CONDA -eq 0 ]
-then
-    if [ ${USED_CONFIG} ]
-    then
-        echo "Settings were loaded from ${CONFIG_FILE}."
-        echo "Remove this file if you wish to return to the default settings."
-        echo
-    fi
-fi
-echo "========================================================================"
 echo
+
 if [[ $1 != "--yes" ]]
 then
     read -p "[hit enter] "
 fi
+
 echo
 echo "Awesome!  Here we go."
 echo
@@ -694,49 +395,6 @@
      PYTHON_EXEC='python2.7'
 fi
 
-function do_setup_py
-{
-    [ -e $1/done ] && return
-    LIB=$1
-    shift
-    if [ -z "$@" ]
-    then
-        echo "Installing $LIB"
-    else
-        echo "Installing $LIB (arguments: '$@')"
-    fi
-    [ ! -e $LIB/extracted ] && tar xfz $LIB.tar.gz
-    touch $LIB/extracted
-    BUILD_ARGS=""
-    PYEXE=${PYTHON_EXEC}
-    case $LIB in
-        *h5py*)
-            pushd $LIB &> /dev/null
-            ( ${DEST_DIR}/bin/${PYTHON_EXEC} setup.py configure --hdf5=${HDF5_DIR} 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            popd &> /dev/null
-            ;;
-        *numpy*)
-            if [ -e ${DEST_DIR}/lib/${PYTHON_EXEC}/site-packages/numpy/__init__.py ]
-            then
-                VER=$(${DEST_DIR}/bin/${PYTHON_EXEC} -c 'from distutils.version import StrictVersion as SV; \
-                                                 import numpy; print SV(numpy.__version__) < SV("1.8.0")')
-                if [ $VER == "True" ]
-                then
-                    echo "Removing previous NumPy instance (see issue #889)"
-                    rm -rf ${DEST_DIR}/lib/${PYTHON_EXEC}/site-packages/{numpy*,*.pth}
-                fi
-            fi
-            ;;
-        *)
-            ;;
-    esac
-    cd $LIB
-    ( ${DEST_DIR}/bin/${PYEXE} setup.py build ${BUILD_ARGS} $* 2>&1 ) 1>> ${LOG_FILE} || do_exit
-    ( ${DEST_DIR}/bin/${PYEXE} setup.py install    2>&1 ) 1>> ${LOG_FILE} || do_exit
-    touch done
-    cd ..
-}
-
 if type -P curl &>/dev/null
 then
     echo "Using curl"
@@ -746,40 +404,6 @@
     export GETFILE="wget -nv"
 fi
 
-if type -P sha512sum &> /dev/null
-then
-    echo "Using sha512sum"
-    export SHASUM="sha512sum"
-elif type -P shasum &> /dev/null
-then
-    echo "Using shasum -a 512"
-    export SHASUM="shasum -a 512"
-else
-    echo
-    echo "I am unable to locate any shasum-like utility."
-    echo "ALL FILE INTEGRITY IS NOT VERIFIABLE."
-    echo "THIS IS PROBABLY A BIG DEAL."
-    echo
-    echo "(I'll hang out for a minute for you to consider this.)"
-    sleep 60
-fi
-
-function get_ytproject
-{
-    [ -e $1 ] && return
-    echo "Downloading $1 from yt-project.org"
-    ${GETFILE} "http://yt-project.org/dependencies/$1" || do_exit
-    ( ${SHASUM} -c $1.sha512 2>&1 ) 1>> ${LOG_FILE} || do_exit
-}
-
-function get_ytdata
-{
-    echo "Downloading $1 from yt-project.org"
-    [ -e $1 ] && return
-    ${GETFILE} "http://yt-project.org/data/$1" || do_exit
-    ( ${SHASUM} -c $1.sha512 2>&1 ) 1>> ${LOG_FILE} || do_exit
-}
-
 function test_install
 {
     echo "Testing that yt can be imported"
@@ -788,298 +412,206 @@
 
 ORIG_PWD=`pwd`
 
-if [ -z "${DEST_DIR}" ]
+MYARCH=`uname -m`
+MYOS=`uname -s`
+
+if [ $MYOS = "Darwin" ]
+then
+    MINICONDA_OS="MacOSX"
+    MINICONDA_ARCH="x86_64"
+elif [ $MYOS = "Linux" ]
 then
-    echo "Edit this script, set the DEST_DIR parameter and re-run."
+    MINICONDA_OS="Linux"
+    if [ $MYARCH = "i386" ]
+    then
+        MINICONDA_ARCH="x86"
+    elif [ $MYARCH = "i686"  ]
+    then
+        MINICONDA_ARCH="x86"
+    elif [ $MYARCH = "x86_64"  ]
+    then
+        MINICONDA_ARCH="x86_64"
+    else
+        echo "Not sure which architecture you are running."
+        echo "Going with x86_64 architecture."
+        MINICONDA_OS="Linux-x86_64"
+    fi
+else
+    echo "The yt install script is not supported on the ${MYOS}"
+    echo "operating system."
     exit 1
 fi
 
-# Set paths to what they should be when yt is activated.
-if [ $INST_CONDA -eq 0 ]
+if [ $INST_PY3 -eq 1 ]
 then
-    export PATH=${DEST_DIR}/bin:$PATH
-    export LD_LIBRARY_PATH=${DEST_DIR}/lib:$LD_LIBRARY_PATH
-    export PYTHONPATH=${DEST_DIR}/lib/${PYTHON_EXEC}/site-packages
+    PY_VERSION='3'
+else
+    PY_VERSION='2'
+fi
+
+MINICONDA_PKG="Miniconda${PY_VERSION}-${MINICONDA_VERSION}-${MINICONDA_OS}-${MINICONDA_ARCH}.sh"
 
-    # Write config settings to file.
-    CONFIG_FILE=${DEST_DIR}/.yt_config
-    mkdir -p ${DEST_DIR}
-    if [ -z ${REINST_YT} ] || [ ${REINST_YT} -neq 1 ]
-    then
-        write_config
-    elif [ ${REINST_YT} ] && [ ${REINST_YT} -eq 1 ] && [ -f ${CONFIG_FILE} ]
-    then
-        USED_CONFIG=1
-        source ${CONFIG_FILE}
-    fi
-    
-    # Get supplemental data.
+echo
+echo "Downloading ${MINICONDA_URLBASE}/${MINICONDA_PKG}"
+echo
 
-    mkdir -p ${DEST_DIR}/data
-    cd ${DEST_DIR}/data
-    echo 'de6d8c6ea849f0206d219303329a0276b3cce7c051eec34377d42aacbe0a4f47ac5145eb08966a338ecddd2b83c8f787ca9956508ad5c39ee2088ad875166410  cloudy_emissivity.h5' > cloudy_emissivity.h5.sha512
-    [ ! -e cloudy_emissivity.h5 ] && get_ytdata cloudy_emissivity.h5
-    echo '0f714ae2eace0141b1381abf1160dc8f8a521335e886f99919caf3beb31df1fe271d67c7b2a804b1467949eb16b0ef87a3d53abad0e8160fccac1e90d8d9e85f  apec_emissivity.h5' > apec_emissivity.h5.sha512
-    [ ! -e apec_emissivity.h5 ] && get_ytdata apec_emissivity.h5
-    
-    mkdir -p ${DEST_DIR}/src
-    cd ${DEST_DIR}/src
+if [ -f ${MINICONDA_PKG} ]
+then
+    rm $MINICONDA_PKG
+fi
 
-    PYTHON2='Python-2.7.11'
-    PYTHON3='Python-3.5.1'
-    CYTHON='Cython-0.23.5'
-    if [ $INST_PY3 -eq 0 ]
-    then
-        PYX='PyX-0.12.1'
-    else
-        PYX='PyX-0.14.1'
-    fi
-    BZLIB='bzip2-1.0.6'
-    FREETYPE_VER='freetype-2.4.12' 
-    H5PY='h5py-2.5.0'
-    HDF5='hdf5-1.8.14' 
-    LAPACK='lapack-3.4.2'
-    PNG='libpng-1.6.3'
-    MATPLOTLIB='matplotlib-1.5.1'
-    NOSE='nose-1.3.7'
-    NUMPY='numpy-1.11.0'
-    GITPYTHON='GitPython-2.1.3'
-    ROCKSTAR='rockstar-0.99.6'
-    SCIPY='scipy-0.17.0'
-    SQLITE='sqlite-autoconf-3071700'
-    SYMPY='sympy-1.0'
-    ZLIB='zlib-1.2.8'
-    SETUPTOOLS='setuptools-20.6.7'
-    ASTROPY='astropy-1.1.2'
-    
-    # Now we dump all our SHA512 files out.
-    echo '9052d74bbd0c93757fd916939cc3c39eb1aba6c9692b48887ae577256bec64b39b1fd25b6c751e6c8fe723de4c0ddf9a1a207de39f75b0839500dfcdde69f925  Cython-0.23.5.tar.gz' > Cython-0.23.5.tar.gz.sha512
-    if [ $INST_PY3 -eq 0 ]
-    then
-        echo '4941f5aa21aff3743546495fb073c10d2657ff42b2aff401903498638093d0e31e344cce778980f28a7170c6d29eab72ac074277b9d4088376e8692dc71e55c1  PyX-0.12.1.tar.gz' > PyX-0.12.1.tar.gz.sha512
-    else
-        echo '16265bbdcaf28ce194189a2987b32952f296c850b829454bcccce0abd23838bfca0276c3e9c8e96b8cbfaf1473bf14669f9b7f2032ee039b61ae59ea3aa45a20  PyX-0.14.1.tar.gz' > PyX-0.14.1.tar.gz.sha512
-    fi
-    echo 'f21df53da87e9e3c14599a34388976e7dd09b951dff3c4b978fe224beeff07e749c0059ffd94f68ca9b75ecaef142b285d579b8dfaad4eab85aca33957114937  Python-2.7.11.tgz' > Python-2.7.11.tgz.sha512
-    echo '73f1477f3d3f5bd978c4ea1d1b679467b45e9fd2f443287b88c5c107a9ced580c56e0e8f33acea84e06b11a252e2a4e733120b721a9b6e1bb3d34493a3353bfb  Python-3.5.1.tgz' > Python-3.5.1.tgz.sha512
-    echo 'b83c4a1415a3eb8c016507705d0d2f22971e4da937bb97953eec08f8f856933d8fa76ce8c536122235b19e7879b16add2e20fd2fee3e488f9b2b4bf1b9f4dbdb  astropy-1.1.2.tar.gz' > astropy-1.1.2.tar.gz.sha512
-    echo '276bd9c061ec9a27d478b33078a86f93164ee2da72210e12e2c9da71dcffeb64767e4460b93f257302b09328eda8655e93c4b9ae85e74472869afbeae35ca71e  blas.tar.gz' > blas.tar.gz.sha512
-    echo '00ace5438cfa0c577e5f578d8a808613187eff5217c35164ffe044fbafdfec9e98f4192c02a7d67e01e5a5ccced630583ad1003c37697219b0f147343a3fdd12  bzip2-1.0.6.tar.gz' > bzip2-1.0.6.tar.gz.sha512
-    echo '609a68a3675087e0cc95268574f31e104549daa48efe15a25a33b8e269a93b4bd160f4c3e8178dca9c950ef5ca514b039d6fd1b45db6af57f25342464d0429ce  freetype-2.4.12.tar.gz' > freetype-2.4.12.tar.gz.sha512
-    echo '4a83f9ae1855a7fad90133b327d426201c8ccfd2e7fbe9f39b2d61a2eee2f3ebe2ea02cf80f3d4e1ad659f8e790c173df8cc99b87d0b7ce63d34aa88cfdc7939  h5py-2.5.0.tar.gz' > h5py-2.5.0.tar.gz.sha512
-    echo '4073fba510ccadaba41db0939f909613c9cb52ba8fb6c1062fc9118edc601394c75e102310be1af4077d07c9b327e6bbb1a6359939a7268dc140382d0c1e0199  hdf5-1.8.14.tar.gz' > hdf5-1.8.14.tar.gz.sha512
-    echo '8770214491e31f0a7a3efaade90eee7b0eb20a8a6ab635c5f854d78263f59a1849133c14ef5123d01023f0110cbb9fc6f818da053c01277914ae81473430a952  lapack-3.4.2.tar.gz' > lapack-3.4.2.tar.gz.sha512
-    echo '887582e5a22e4cde338aa8fec7a89f6dd31f2f02b8842735f00f970f64582333fa03401cea6d01704083403c7e8b7ebc26655468ce930165673b33efa4bcd586  libpng-1.6.3.tar.gz' > libpng-1.6.3.tar.gz.sha512
-    echo 'a0e78b5027a3a49cf8e77dc0d26f5f380dcd80f7b309b6121199acd5e1d94f48482864a9eee3bd397f7ac6f07fe1d3c21bf517217df3c72e8e3d105b7c2ae58e  matplotlib-1.5.1.tar.gz' > matplotlib-1.5.1.tar.gz.sha512
-    echo 'e65c914f621f8da06b9ab11a0ff2763d6e29b82ce2aaed56da0e3773dc899d9deb1f20015789d44c65a5dad7214520f5b659b3f8d7695fb207ad3f78e5cf1b62  nose-1.3.7.tar.gz' > nose-1.3.7.tar.gz.sha512
-    echo '92c1889397ad013e25da3a0657fc01e787d528fc19c29cc2acd286c3f07d41b984252583457b1b9259fc303afbe9694565cdcf5752eb4ecb950cc7a99ec1ad8b  numpy-1.11.0.tar.gz' > numpy-1.11.0.tar.gz.sha512
-    echo '918ff1765a85a818619165c2bcbb0d417f35c979c2f42f1bb7e41636696c0cb4d6837725f3655fbdfebea966476d1255ee18adabe9ed5536455b63336a1f399d  GitPython-2.1.3.tar.gz' > GitPython-2.1.3.tar.gz.sha512
-    echo 'de6409d75a3ff3cf1e5391d3b09126f0bc7e1a40a15f9bee244195638fe2f8481fca032896d8534623e6122ff59aaf669664e27ff89cf1b094a5ce7312f220b7  scipy-0.17.0.tar.gz' > scipy-0.17.0.tar.gz.sha512
-    echo '96f3e51b46741450bc6b63779c10ebb4a7066860fe544385d64d1eda52592e376a589ef282ace2e1df73df61c10eab1a0d793abbdaf770e60289494d4bf3bcb4  sqlite-autoconf-3071700.tar.gz' > sqlite-autoconf-3071700.tar.gz.sha512
-    echo '977db6e9bc6a5918cceb255981a57e85e7060c0922aefd2968b004d25d704e25a5cb5bbe09eb387e8695581e23e2825d9c40310068fe25ece7e9c23037a21f39  sympy-1.0.tar.gz' > sympy-1.0.tar.gz.sha512
-    echo 'ece209d4c7ec0cb58ede791444dc754e0d10811cbbdebe3df61c0fd9f9f9867c1c3ccd5f1827f847c005e24eef34fb5bf87b5d3f894d75da04f1797538290e4a  zlib-1.2.8.tar.gz' > zlib-1.2.8.tar.gz.sha512
-    echo '91a212b5007f9fdfacb4341e06dc0355c5c29897eb8ea407dd4864091f845ba1417bb0d33b5ed6897869d0233e2d0ec6548898d3dbe9eda23f751829bd51a104  setuptools-20.6.7.tar.gz' > setuptools-20.6.7.tar.gz.sha512
-    # Individual processes
-    [ -z "$HDF5_DIR" ] && get_ytproject $HDF5.tar.gz
-    [ $INST_ZLIB -eq 1 ] && get_ytproject $ZLIB.tar.gz
-    [ $INST_BZLIB -eq 1 ] && get_ytproject $BZLIB.tar.gz
-    [ $INST_PNG -eq 1 ] && get_ytproject $PNG.tar.gz
-    [ $INST_FTYPE -eq 1 ] && get_ytproject $FREETYPE_VER.tar.gz
-    [ $INST_SQLITE3 -eq 1 ] && get_ytproject $SQLITE.tar.gz
-    [ $INST_PYX -eq 1 ] && get_ytproject $PYX.tar.gz
-    [ $INST_SCIPY -eq 1 ] && get_ytproject $SCIPY.tar.gz
-    [ $INST_SCIPY -eq 1 ] && get_ytproject blas.tar.gz
-    [ $INST_SCIPY -eq 1 ] && get_ytproject $LAPACK.tar.gz
-    if [ $INST_PY3 -eq 1 ]
-    then
-        get_ytproject $PYTHON3.tgz
-    else
-        get_ytproject $PYTHON2.tgz
-    fi
-    [ $INST_H5PY -eq 1 ] && get_ytproject $H5PY.tar.gz
-    [ $INST_NOSE -eq 1 ] && get_ytproject $NOSE.tar.gz
-    [ $INST_ASTROPY -eq 1 ] && get_ytproject $ASTROPY.tar.gz
-    get_ytproject $NUMPY.tar.gz
-    get_ytproject $MATPLOTLIB.tar.gz
-    get_ytproject $CYTHON.tar.gz
-    get_ytproject $GITPYTHON.tar.gz
-    get_ytproject $SYMPY.tar.gz
-    get_ytproject $SETUPTOOLS.tar.gz
+echo "Installing the Miniconda python environment."
+
+if [ -e ${DEST_DIR} ]
+then
+    rm -rf $DEST_DIR/*
+else
+    mkdir $DEST_DIR
+fi
 
-    if [ $INST_BZLIB -eq 1 ]
-    then
-        if [ ! -e $BZLIB/done ]
-        then
-            [ ! -e $BZLIB ] && tar xfz $BZLIB.tar.gz
-            echo "Installing BZLIB"
-            cd $BZLIB
-            if [ `uname` = "Darwin" ]
-            then
-                if [ -z "${CC}" ]
-                then
-                    sed -i.bak 's/soname/install_name/' Makefile-libbz2_so
-                else
-                    sed -i.bak -e 's/soname/install_name/' -e "s|CC=gcc|CC=${CC}|" Makefile-libbz2_so
-                fi
-            fi
-            ( make install CFLAGS=-fPIC LDFLAGS=-fPIC PREFIX=${DEST_DIR} 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( make -f Makefile-libbz2_so CFLAGS=-fPIC LDFLAGS=-fPIC PREFIX=${DEST_DIR} 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( cp -v libbz2.so.1.0.6 ${DEST_DIR}/lib 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            touch done
-            cd ..
-        fi
-        BZLIB_DIR=${DEST_DIR}
-        export LDFLAGS="${LDFLAGS} -L${BZLIB_DIR}/lib/ -L${BZLIB_DIR}/lib64/"
-        LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${BZLIB_DIR}/lib/"
-    fi
+log_cmd ${GETFILE} ${MINICONDA_URLBASE}/${MINICONDA_PKG} || do_exit
+
+log_cmd bash ./${MINICONDA_PKG} -b -p $DEST_DIR -f
+
+# Need to set PATH so we use miniconda's python environment
+export PATH=${DEST_DIR}/bin:$PATH
+
+echo "Installing the necessary packages for yt."
+echo "This may take a while, but don't worry.  yt loves you."
 
-    if [ $INST_ZLIB -eq 1 ]
-    then
-        if [ ! -e $ZLIB/done ]
-        then
-            [ ! -e $ZLIB ] && tar xfz $ZLIB.tar.gz
-            echo "Installing ZLIB"
-            cd $ZLIB
-            ( ./configure --shared --prefix=${DEST_DIR}/ 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( make install 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( make clean 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            touch done
-            cd ..
-        fi
-        ZLIB_DIR=${DEST_DIR}
-        export LDFLAGS="${LDFLAGS} -L${ZLIB_DIR}/lib/ -L${ZLIB_DIR}/lib64/"
-        LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${ZLIB_DIR}/lib/"
-    fi
-    
-    if [ $INST_PNG -eq 1 ]
-    then
-        if [ ! -e $PNG/done ]
-        then
-            [ ! -e $PNG ] && tar xfz $PNG.tar.gz
-            echo "Installing PNG"
-            cd $PNG
-            ( ./configure CPPFLAGS=-I${DEST_DIR}/include CFLAGS=-I${DEST_DIR}/include --prefix=${DEST_DIR}/ 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( make install 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( make clean 2>&1) 1>> ${LOG_FILE} || do_exit
-            touch done
-            cd ..
-        fi
-        PNG_DIR=${DEST_DIR}
-        export LDFLAGS="${LDFLAGS} -L${PNG_DIR}/lib/ -L${PNG_DIR}/lib64/"
-        LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${PNG_DIR}/lib/"
-    fi
-
-    if [ $INST_FTYPE -eq 1 ]
-    then
-        if [ ! -e $FREETYPE_VER/done ]
-        then
-            [ ! -e $FREETYPE_VER ] && tar xfz $FREETYPE_VER.tar.gz
-            echo "Installing FreeType2"
-            cd $FREETYPE_VER
-            ( ./configure CFLAGS=-I${DEST_DIR}/include --prefix=${DEST_DIR}/ 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( make 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( make install 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( make clean 2>&1) 1>> ${LOG_FILE} || do_exit
-            touch done
-            cd ..
-        fi
-        FTYPE_DIR=${DEST_DIR}
-        export LDFLAGS="${LDFLAGS} -L${FTYPE_DIR}/lib/ -L${FTYPE_DIR}/lib64/"
-        LD_LIBRARY_PATH="${LD_LIBRARY_PATH}:${FTYPE_DIR}/lib/"
-    fi
+declare -a YT_DEPS
+YT_DEPS+=('python')
+YT_DEPS+=('setuptools')
+YT_DEPS+=('numpy')
+YT_DEPS+=('jupyter')
+YT_DEPS+=('ipython')
+YT_DEPS+=('sphinx')
+if [ ${INST_GIT} -eq 1 ]
+then
+    YT_DEPS+=('git')
+    YT_DEPS+=('gitpython')
+fi
+if [ $INST_H5PY -ne 0 ]
+then
+    YT_DEPS+=('h5py')
+fi
+YT_DEPS+=('matplotlib')
+YT_DEPS+=('cython')
+if [ $INST_NOSE -ne 0 ]
+then
+    YT_DEPS+=('nose')
+fi
+if [ $INST_SCIPY -ne 0 ]
+then
+    YT_DEPS+=('scipy')
+fi
+if [ $INST_ASTROPY -ne 0 ]
+then
+    YT_DEPS+=('astropy')
+fi
+YT_DEPS+=('conda-build')
+if [ $INST_PY3 -eq 0 ] && [ $INST_HG -eq 1 ]
+then
+    YT_DEPS+=('mercurial')
+fi
+YT_DEPS+=('sympy')
 
-    if [ -z "$HDF5_DIR" ]
-    then
-        if [ ! -e $HDF5/done ]
-        then
-            [ ! -e $HDF5 ] && tar xfz $HDF5.tar.gz
-            echo "Installing HDF5"
-            cd $HDF5
-            ( ./configure --prefix=${DEST_DIR}/ --enable-shared 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( make ${MAKE_PROCS} install 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( make clean 2>&1) 1>> ${LOG_FILE} || do_exit
-            touch done
-            cd ..
-        fi
-        export HDF5_DIR=${DEST_DIR}
-    else
-        export HDF5_DIR=${HDF5_DIR}
-    fi
-    export HDF5_API=16
-
-    if [ $INST_SQLITE3 -eq 1 ]
-    then
-        if [ ! -e $SQLITE/done ]
-        then
-            [ ! -e $SQLITE ] && tar xfz $SQLITE.tar.gz
-            echo "Installing SQLite3"
-            cd $SQLITE
-            ( ./configure --prefix=${DEST_DIR}/ 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( make ${MAKE_PROCS} install 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( make clean 2>&1) 1>> ${LOG_FILE} || do_exit
-            touch done
-            cd ..
-        fi
-    fi
+if [ $INST_NETCDF4 -eq 1 ]
+then
+    YT_DEPS+=('netcdf4')
+fi
 
-    if [ $INST_PY3 -eq 1 ]
-    then
-        if [ ! -e $PYTHON3/done ]
-        then
-            echo "Installing Python 3"
-            [ ! -e $PYTHON3 ] && tar xfz $PYTHON3.tgz
-            cd $PYTHON3
-            ( ./configure --prefix=${DEST_DIR}/ ${PYCONF_ARGS} 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            
-            ( make ${MAKE_PROCS} 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( make install 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( ln -sf ${DEST_DIR}/bin/python3.5 ${DEST_DIR}/bin/pyyt 2>&1 ) 1>> ${LOG_FILE}
-            ( ln -sf ${DEST_DIR}/bin/python3.5 ${DEST_DIR}/bin/python 2>&1 ) 1>> ${LOG_FILE}
-            ( ln -sf ${DEST_DIR}/bin/python3-config ${DEST_DIR}/bin/python-config 2>&1 ) 1>> ${LOG_FILE}
-            ( make clean 2>&1) 1>> ${LOG_FILE} || do_exit
-            touch done
-            cd ..
-        fi
-    else
-        if [ ! -e $PYTHON2/done ]
-        then
-            echo "Installing Python 2. This may take a while, but don't worry. yt loves you."
-            [ ! -e $PYTHON2 ] && tar xfz $PYTHON2.tgz
-            cd $PYTHON2
-            ( ./configure --prefix=${DEST_DIR}/ ${PYCONF_ARGS} 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( make ${MAKE_PROCS} 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( make install 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            ( ln -sf ${DEST_DIR}/bin/python2.7 ${DEST_DIR}/bin/pyyt 2>&1 ) 1>> ${LOG_FILE}
-            ( make clean 2>&1) 1>> ${LOG_FILE} || do_exit
-            touch done
-            cd ..
-        fi
-    fi
+log_cmd ${DEST_DIR}/bin/conda update --yes conda
 
-    ( ${DEST_DIR}/bin/python -c "import _ssl" 2>&1 ) 1>> ${LOG_FILE}
-    RESULT=$?
-    if  [ $RESULT -ne 0 ]
-    then
-        echo "Unable to import the python SSL bindings."
-        echo "This means that OpenSSL is not installed or your system's OpenSSL"
-        echo "installation is out of date."
-        echo "Please install OpenSSL or set INST_CONDA=1"
-        do_exit
-    fi
-
-    export PYTHONPATH=${DEST_DIR}/lib/${PYTHON_EXEC}/site-packages/
-
-    # Install setuptools
-    do_setup_py $SETUPTOOLS
-
+if [ $INST_GIT -eq 1 ]
+then
+    GIT_EXE=${DEST_DIR}/bin/git
+else
     if type -P git &>/dev/null
     then
         GIT_EXE="git"
     else
-        echo "Cannot find git. Please install git."
-        do_exit
+        if [ $INST_YT_SOURCE -eq 1 ]
+        then
+            echo "Cannot find git. Please install git or set INST_GIT=1."
+            do_exit
+        fi
+    fi
+fi
+
+log_cmd echo "DEPENDENCIES" ${YT_DEPS[@]}
+for YT_DEP in "${YT_DEPS[@]}"; do
+    echo "Installing $YT_DEP"
+    log_cmd ${DEST_DIR}/bin/conda install -c conda-forge --yes ${YT_DEP}
+done
+
+if [ $INST_PY3 -eq 1 ] && [ $INST_HG -eq 1 ]
+then
+    echo "Installing mercurial"
+    log_cmd ${DEST_DIR}/bin/conda create -y -n py27 python=2.7 mercurial
+    log_cmd ln -s ${DEST_DIR}/envs/py27/bin/hg ${DEST_DIR}/bin
+fi
+
+if [ $INST_YT_SOURCE -eq 1 ]
+then
+    log_cmd ${GIT_EXE} clone https://github.com/yt-project/yt_conda ${DEST_DIR}/src/yt_conda
+fi
+    
+if [ $INST_EMBREE -eq 1 ]
+then
+    echo "Installing Embree"
+    if [ ! -d ${DEST_DIR}/src ]
+    then
+        mkdir ${DEST_DIR}/src
     fi
+    cd ${DEST_DIR}/src
+    ( ${GETFILE} "$EMBREE_URL" 2>&1 ) 1>> ${LOG_FILE} || do_exit
+    log_cmd tar xfz ${EMBREE}.tar.gz
+    log_cmd mv ${DEST_DIR}/src/${EMBREE}/include/embree2 ${DEST_DIR}/include
+    log_cmd mv ${DEST_DIR}/src/${EMBREE}/lib/lib*.* ${DEST_DIR}/lib
+    if [ `uname` = "Darwin" ]
+    then
+        ln -s ${DEST_DIR}/lib/libembree.2.dylib ${DEST_DIR}/lib/libembree.dylib
+        install_name_tool -id ${DEST_DIR}/lib/libembree.2.dylib ${DEST_DIR}/lib/libembree.2.dylib
+    else
+        ln -s ${DEST_DIR}/lib/libembree.so.2 ${DEST_DIR}/lib/libembree.so
+    fi
+    
+    echo "Installing pyembree from source"
+    ( ${GETFILE} "$PYEMBREE_URL" 2>&1 ) 1>> ${LOG_FILE} || do_exit
+    log_cmd unzip ${DEST_DIR}/src/master.zip
+    pushd ${DEST_DIR}/src/pyembree-master &> /dev/null
+    log_cmd ${DEST_DIR}/bin/${PYTHON_EXEC} setup.py install build_ext -I${DEST_DIR}/include -L${DEST_DIR}/lib
+    popd &> /dev/null
+fi
 
+if [ $INST_ROCKSTAR -eq 1 ]
+then
+    echo "Building Rockstar"
+    ( ${GIT_EXE} clone https://github.com/yt-project/rockstar ${DEST_DIR}/src/rockstar/ 2>&1 ) 1>> ${LOG_FILE}
+    ROCKSTAR_PACKAGE=$(${DEST_DIR}/bin/conda build ${DEST_DIR}/src/yt_conda/rockstar --output)
+    log_cmd ${DEST_DIR}/bin/conda build ${DEST_DIR}/src/yt_conda/rockstar
+    log_cmd ${DEST_DIR}/bin/conda install $ROCKSTAR_PACKAGE
+    ROCKSTAR_DIR=${DEST_DIR}/src/rockstar
+fi
+
+# conda doesn't package pyx, so we install manually with pip
+if [ $INST_PYX -eq 1 ]
+then
+    if [ $INST_PY3 -eq 1 ]
+    then
+        log_cmd ${DEST_DIR}/bin/pip install pyx
+    else
+        log_cmd ${DEST_DIR}/bin/pip install pyx==0.12.1
+    fi
+fi
+
+if [ $INST_YT_SOURCE -eq 0 ]
+then
+    echo "Installing yt"
+    log_cmd ${DEST_DIR}/bin/conda install -c conda-forge --yes yt
+else
+    echo "Building yt from source"
     if [ -z "$YT_DIR" ]
     then
         if [ -e $ORIG_PWD/yt/mods.py ]
@@ -1088,512 +620,86 @@
         elif [ -e $ORIG_PWD/../yt/mods.py ]
         then
             YT_DIR=$(dirname $ORIG_PWD)
-        elif [ ! -e yt-git ]
-        then
-            echo "Cloning yt"
-            YT_DIR="$PWD/yt-git/"
-            ( ${GIT_EXE} clone https://github.com/yt-project/yt/ ${YT_DIR} 2>&1 ) 1>> ${LOG_FILE}
-        elif [ -e yt-git ]
-        then
-            YT_DIR="$PWD/yt-git/"
+        else
+            YT_DIR="${DEST_DIR}/src/yt-git"
+            log_cmd ${GIT_EXE} clone https://github.com/yt-project/yt ${YT_DIR}
+            log_cmd ${GIT_EXE} -C ${YT_DIR} checkout ${BRANCH}
         fi
         echo Setting YT_DIR=${YT_DIR}
-    fi
-    
-    # This fixes problems with gfortran linking.
-    unset LDFLAGS
-
-    echo "Installing pip"
-    ( ${GETFILE} https://bootstrap.pypa.io/get-pip.py 2>&1 ) 1>> ${LOG_FILE} || do_exit
-    ( ${DEST_DIR}/bin/${PYTHON_EXEC} get-pip.py 2>&1 ) 1>> ${LOG_FILE} || do_exit
-
-    if [ $INST_SCIPY -eq 0 ]
-    then
-        do_setup_py $NUMPY ${NUMPY_ARGS}
     else
-        if [ ! -e $SCIPY/done ]
-        then
-            if [ ! -e BLAS/done ]
-            then
-                tar xfz blas.tar.gz
-                echo "Building BLAS"
-                cd BLAS
-                gfortran -O2 -fPIC -fno-second-underscore -c *.f
-                ( ar r libfblas.a *.o 2>&1 ) 1>> ${LOG_FILE}
-                ( ranlib libfblas.a 2>&1 ) 1>> ${LOG_FILE}
-                rm -rf *.o
-                touch done
-                cd ..
-            fi
-            if [ ! -e $LAPACK/done ]
-            then
-                tar xfz $LAPACK.tar.gz
-                echo "Building LAPACK"
-                cd $LAPACK/
-                cp INSTALL/make.inc.gfortran make.inc
-                ( make lapacklib OPTS="-fPIC -O2" NOOPT="-fPIC -O0" CFLAGS=-fPIC LDFLAGS=-fPIC 2>&1 ) 1>> ${LOG_FILE} || do_exit
-                touch done
-                cd ..
-            fi
-        fi
-        export BLAS=$PWD/BLAS/libfblas.a
-        export LAPACK=$PWD/$LAPACK/liblapack.a
-        do_setup_py $NUMPY ${NUMPY_ARGS}
-        do_setup_py $SCIPY ${NUMPY_ARGS}
-    fi
-    
-    if [ -n "${MPL_SUPP_LDFLAGS}" ]
-    then
-        OLD_LDFLAGS=${LDFLAGS}
-        export LDFLAGS="${MPL_SUPP_LDFLAGS}"
-        echo "Setting LDFLAGS ${LDFLAGS}"
-    fi
-    if [ -n "${MPL_SUPP_CXXFLAGS}" ]
-    then
-        OLD_CXXFLAGS=${CXXFLAGS}
-        export CXXFLAGS="${MPL_SUPP_CXXFLAGS}"
-        echo "Setting CXXFLAGS ${CXXFLAGS}"
-    fi
-    if [ -n "${MPL_SUPP_CFLAGS}" ]
-    then
-        OLD_CFLAGS=${CFLAGS}
-        export CFLAGS="${MPL_SUPP_CFLAGS}"
-        echo "Setting CFLAGS ${CFLAGS}"
-    fi
-    # Now we set up the basedir for matplotlib:
-    mkdir -p ${DEST_DIR}/src/$MATPLOTLIB
-    echo "[directories]" >> ${DEST_DIR}/src/$MATPLOTLIB/setup.cfg
-    echo "basedirlist = ${DEST_DIR}" >> ${DEST_DIR}/src/$MATPLOTLIB/setup.cfg
-    if [ `uname` = "Darwin" ]
-    then
-        echo "[gui_support]" >> ${DEST_DIR}/src/$MATPLOTLIB/setup.cfg
-        echo "macosx = False" >> ${DEST_DIR}/src/$MATPLOTLIB/setup.cfg
-    fi
-    
-    _user_DISPLAY=$DISPLAY
-    unset DISPLAY   # see (yt-user link missing: "Installation failure" 01/29/15)
-    do_setup_py $MATPLOTLIB
-    export DISPLAY=${_user_DISPLAY}
-    if [ -n "${OLD_LDFLAGS}" ]
-    then
-        export LDFLAG=${OLD_LDFLAGS}
-    fi
-    [ -n "${OLD_LDFLAGS}" ] && export LDFLAGS=${OLD_LDFLAGS}
-    [ -n "${OLD_CXXFLAGS}" ] && export CXXFLAGS=${OLD_CXXFLAGS}
-    [ -n "${OLD_CFLAGS}" ] && export CFLAGS=${OLD_CFLAGS}
-    
-    echo "Installing Jupyter"
-    ( ${DEST_DIR}/bin/pip install "jupyter<2.0.0" 2>&1 ) 1>> ${LOG_FILE}
-    
-    do_setup_py $CYTHON
-    if [ $INST_H5PY -eq 1 ]
-    then
-        do_setup_py $H5PY
-    fi
-    if [ $INST_NOSE -eq 1 ]
-    then
-        do_setup_py $NOSE
-    fi
-    if [ $INST_ASTROPY -eq 1 ]
-    then
-        do_setup_py $ASTROPY
-    fi
-    do_setup_py $GITPYTHON
-    do_setup_py $SYMPY
-    [ $INST_PYX -eq 1 ] && do_setup_py $PYX
-
-    ( ${DEST_DIR}/bin/pip install jinja2 2>&1 ) 1>> ${LOG_FILE}
-    
-    # Now we build Rockstar and set its environment variable.
-    if [ $INST_ROCKSTAR -eq 1 ]
-    then
-        if [ ! -e rockstar/done ]
-        then
-            echo "Building Rockstar"
-            if [ ! -e rockstar ]
-            then
-                ( ${GIT_EXE} clone https://github.com/yt-project/rockstar 2>&1 ) 1>> ${LOG_FILE}
-            fi
-            cd rockstar
-            ( make lib 2>&1 ) 1>> ${LOG_FILE} || do_exit
-            cp librockstar.so ${DEST_DIR}/lib
-            ROCKSTAR_DIR=${DEST_DIR}/src/rockstar
-            echo $ROCKSTAR_DIR > ${YT_DIR}/rockstar.cfg
-            touch done
-            cd ..
-        fi
-    fi
-    
-    MY_PWD=`pwd`
-    cd $YT_DIR
-    ( ${GIT_EXE} pull 2>1 && ${GIT_EXE} checkout ${BRANCH} 2>&1 ) 1>> ${LOG_FILE}
-
-    echo "Installing yt"
-    [ $INST_PNG -eq 1 ] && echo $PNG_DIR > png.cfg
-    ( export PATH=$DEST_DIR/bin:$PATH ; ${DEST_DIR}/bin/${PYTHON_EXEC} setup.py develop 2>&1 ) 1>> ${LOG_FILE} || do_exit
-    touch done
-    cd $MY_PWD
-
-    if !( ( ${DEST_DIR}/bin/${PYTHON_EXEC} -c "import readline" 2>&1 )>> ${LOG_FILE}) || \
-            [[ "${MYOS##Darwin}" != "${MYOS}" && $INST_PY3 -eq 1 ]] 
-    then
-        if !( ( ${DEST_DIR}/bin/${PYTHON_EXEC} -c "import gnureadline" 2>&1 )>> ${LOG_FILE})
+        if [ ! -e $YT_DIR/.git ]
         then
-            echo "Installing pure-python readline"
-            ( ${DEST_DIR}/bin/pip install gnureadline 2>&1 ) 1>> ${LOG_FILE}
-        fi
-    fi
-
-    if [ -e $HOME/.matplotlib/fontList.cache ] && \
-           ( grep -q python2.6 $HOME/.matplotlib/fontList.cache )
-    then
-        echo "WARNING WARNING WARNING WARNING WARNING WARNING WARNING"
-        echo "*******************************************************"
-        echo
-        echo "  You likely need to remove your old fontList.cache!"
-        echo "  You can do this with this command:"
-        echo ""
-        echo "  rm $HOME/.matplotlib/fontList.cache"
-        echo
-        echo "*******************************************************"
-    fi
-    
-    # Add the environment scripts
-    ( cp ${YT_DIR}/doc/activate ${DEST_DIR}/bin/activate 2>&1 ) 1>> ${LOG_FILE}
-    sed -i.bak -e "s,__YT_DIR__,${DEST_DIR}," ${DEST_DIR}/bin/activate
-    ( cp ${YT_DIR}/doc/activate.csh ${DEST_DIR}/bin/activate.csh 2>&1 ) 1>> ${LOG_FILE}
-    sed -i.bak -e "s,__YT_DIR__,${DEST_DIR}," ${DEST_DIR}/bin/activate.csh
-
-    test_install
-
-    function print_afterword
-    {
-        echo
-        echo
-        echo "========================================================================"
-        echo
-        echo "yt is now installed in $DEST_DIR ."
-        echo
-        echo "To run from this new installation, use the activate script for this "
-        echo "environment."
-        echo
-        echo "    $ source $DEST_DIR/bin/activate"
-        echo
-        echo "This modifies the environment variables YT_DEST, PATH, PYTHONPATH, and"
-        echo "LD_LIBRARY_PATH to match your new yt install.  If you use csh, just"
-        echo "append .csh to the above."
-        echo
-        echo "To get started with yt, check out the orientation:"
-        echo
-        echo "    http://yt-project.org/doc/quickstart/"
-        echo
-        echo "The source for yt is located at:"
-        echo "    $YT_DIR"
-        echo
-        echo "For support, see the website and join the mailing list:"
-        echo
-        echo "    http://yt-project.org/"
-        echo "    http://yt-project.org/data/      (Sample data)"
-        echo "    http://yt-project.org/doc/       (Docs)"
-        echo
-        echo "    http://lists.spacepope.org/listinfo.cgi/yt-users-spacepope.org"
-        echo
-        echo "========================================================================"
-        echo
-        echo "Oh, look at me, still talking when there's science to do!"
-        echo "Good luck, and email the user list if you run into any problems."
-    }
-
-    print_afterword
-    print_afterword >> ${LOG_FILE}
-
-    echo "yt dependencies were last updated on" > ${DEST_DIR}/.yt_update
-    date >> ${DEST_DIR}/.yt_update
-else # INST_CONDA -eq 1
-    MYARCH=`uname -m`       # A guess at the OS
-    MYOS=`uname -s`
-
-    if [ $MYOS = "Darwin" ]
-    then
-        MINICONDA_OS="MacOSX"
-        MINICONDA_ARCH="x86_64"
-    elif [ $MYOS = "Linux" ]
-    then
-        MINICONDA_OS="Linux"
-        if [ $MYARCH = "i386" ]
-        then
-            MINICONDA_ARCH="x86"
-        elif [ $MYARCH = "i686"  ]
-        then
-            MINICONDA_ARCH="x86"
-        elif [ $MYARCH = "x86_64"  ]
-        then
-            MINICONDA_ARCH="x86_64"
-        else
-            echo "Not sure which architecture you are running."
-            echo "Going with x86_64 architecture."
-            MINICONDA_OS="Linux-x86_64"
-        fi
-    else
-        echo "The yt install script is not supported on the ${MYOS}"
-        echo "operating system."
-        exit 1
-    fi
-
-    if [ $INST_PY3 -eq 1 ]
-    then
-        PY_VERSION='3'
-    else
-        PY_VERSION='2'
-    fi
-
-    MINICONDA_PKG="Miniconda${PY_VERSION}-${MINICONDA_VERSION}-${MINICONDA_OS}-${MINICONDA_ARCH}.sh"
-
-    echo
-    echo "Downloading ${MINICONDA_URLBASE}/${MINICONDA_PKG}"
-    echo
-
-    if [ -f ${MINICONDA_PKG} ]
-    then
-        rm $MINICONDA_PKG
-    fi
-
-    echo "Installing the Miniconda python environment."
-
-    if [ -e ${DEST_DIR} ]
-    then
-        rm -rf $DEST_DIR/*
-    else
-        mkdir $DEST_DIR
-    fi
-
-    log_cmd ${GETFILE} ${MINICONDA_URLBASE}/${MINICONDA_PKG} || do_exit
-
-    log_cmd bash ./${MINICONDA_PKG} -b -p $DEST_DIR -f
-
-    # Need to set PATH so we use miniconda's python environment
-    export PATH=${DEST_DIR}/bin:$PATH
-
-    echo "Installing the necessary packages for yt."
-    echo "This may take a while, but don't worry.  yt loves you."
-
-    declare -a YT_DEPS
-    YT_DEPS+=('python')
-    YT_DEPS+=('setuptools')
-    YT_DEPS+=('numpy')
-    YT_DEPS+=('jupyter')
-    YT_DEPS+=('ipython')
-    YT_DEPS+=('sphinx')
-    if [ ${INST_GIT} -eq 1 ]
-    then
-        YT_DEPS+=('git')
-        YT_DEPS+=('gitpython')
-    fi
-    if [ $INST_H5PY -ne 0 ]
-    then
-        YT_DEPS+=('h5py')
-    fi
-    YT_DEPS+=('matplotlib')
-    YT_DEPS+=('cython')
-    if [ $INST_NOSE -ne 0 ]
-    then
-        YT_DEPS+=('nose')
-    fi
-    if [ $INST_SCIPY -ne 0 ]
-    then
-        YT_DEPS+=('scipy')
-    fi
-    if [ $INST_ASTROPY -ne 0 ]
-    then
-        YT_DEPS+=('astropy')
-    fi
-    YT_DEPS+=('conda-build')
-    if [ $INST_PY3 -eq 0 ] && [ $INST_HG -eq 1 ]
-    then
-       YT_DEPS+=('mercurial')
-    fi
-    YT_DEPS+=('sympy')
-
-    if [ $INST_NETCDF4 -eq 1 ]
-    then
-        YT_DEPS+=('netcdf4')   
-    fi
-    
-    log_cmd ${DEST_DIR}/bin/conda update --yes conda
-
-    if [ $INST_GIT -eq 1 ]
-    then
-        GIT_EXE=${DEST_DIR}/bin/git
-    else
-        if type -P git &>/dev/null
-        then
-            GIT_EXE="git"
-        else
-            echo "Cannot find git. Please install git or set INST_GIT=1."
+            echo "$YT_DIR is not a clone of the yt git repository, exiting"
             do_exit
         fi
     fi
-
-    log_cmd echo "DEPENDENCIES" ${YT_DEPS[@]}
-    for YT_DEP in "${YT_DEPS[@]}"; do
-        echo "Installing $YT_DEP"
-        log_cmd ${DEST_DIR}/bin/conda install -c conda-forge --yes ${YT_DEP}
-    done
-
-    if [ $INST_PY3 -eq 1 ] && [ $INST_HG -eq 1 ]
-    then
-        echo "Installing mercurial"
-        log_cmd ${DEST_DIR}/bin/conda create -y -n py27 python=2.7 mercurial
-        log_cmd ln -s ${DEST_DIR}/envs/py27/bin/hg ${DEST_DIR}/bin
-    fi
-
-    log_cmd ${GIT_EXE} clone https://github.com/yt-project/yt_conda ${DEST_DIR}/src/yt_conda
-    
     if [ $INST_EMBREE -eq 1 ]
     then
-        
-        echo "Installing Embree"
-        if [ ! -d ${DEST_DIR}/src ]
-        then
-            mkdir ${DEST_DIR}/src
-        fi
-        cd ${DEST_DIR}/src
-        ( ${GETFILE} "$EMBREE_URL" 2>&1 ) 1>> ${LOG_FILE} || do_exit
-        log_cmd tar xfz ${EMBREE}.tar.gz
-        log_cmd mv ${DEST_DIR}/src/${EMBREE}/include/embree2 ${DEST_DIR}/include
-        log_cmd mv ${DEST_DIR}/src/${EMBREE}/lib/lib*.* ${DEST_DIR}/lib
-        if [ `uname` = "Darwin" ]
-        then
-            ln -s ${DEST_DIR}/lib/libembree.2.dylib ${DEST_DIR}/lib/libembree.dylib
-            install_name_tool -id ${DEST_DIR}/lib/libembree.2.dylib ${DEST_DIR}/lib/libembree.2.dylib
-        else
-            ln -s ${DEST_DIR}/lib/libembree.so.2 ${DEST_DIR}/lib/libembree.so
-        fi
-        
-        echo "Installing pyembree from source"
-        ( ${GETFILE} "$PYEMBREE_URL" 2>&1 ) 1>> ${LOG_FILE} || do_exit
-        log_cmd unzip ${DEST_DIR}/src/master.zip
-        pushd ${DEST_DIR}/src/pyembree-master &> /dev/null
-        log_cmd ${DEST_DIR}/bin/${PYTHON_EXEC} setup.py install build_ext -I${DEST_DIR}/include -L${DEST_DIR}/lib
-        popd &> /dev/null
+        echo $DEST_DIR > ${YT_DIR}/embree.cfg
     fi
-
     if [ $INST_ROCKSTAR -eq 1 ]
     then
-        echo "Building Rockstar"
-        ( ${GIT_EXE} clone https://github.com/yt-project/rockstar ${DEST_DIR}/src/rockstar/ 2>&1 ) 1>> ${LOG_FILE}
-        ROCKSTAR_PACKAGE=$(${DEST_DIR}/bin/conda build ${DEST_DIR}/src/yt_conda/rockstar --output)
-        log_cmd ${DEST_DIR}/bin/conda build ${DEST_DIR}/src/yt_conda/rockstar
-        log_cmd ${DEST_DIR}/bin/conda install $ROCKSTAR_PACKAGE
-        ROCKSTAR_DIR=${DEST_DIR}/src/rockstar
-    fi
-
-    # conda doesn't package pyx, so we install manually with pip
-    if [ $INST_PYX -eq 1 ]
-    then
-        if [ $INST_PY3 -eq 1 ]
-        then
-            log_cmd ${DEST_DIR}/bin/pip install pyx
-        else
-            log_cmd ${DEST_DIR}/bin/pip install pyx==0.12.1
-        fi
-    fi
-
-    if [ $INST_YT_SOURCE -eq 0 ]
-    then
-        echo "Installing yt"
-        log_cmd ${DEST_DIR}/bin/conda install -c conda-forge --yes yt
-    else
-        echo "Building yt from source"
-        if [ -z "$YT_DIR" ]
-        then
-            if [ -e $ORIG_PWD/yt/mods.py ]
-            then
-                YT_DIR="$ORIG_PWD"
-            elif [ -e $ORIG_PWD/../yt/mods.py ]
-            then
-                YT_DIR=$(dirname $ORIG_PWD)
-            else
-                YT_DIR="${DEST_DIR}/src/yt-git"
-                log_cmd ${GIT_EXE} clone https://github.com/yt-project/yt ${YT_DIR}
-                log_cmd ${GIT_EXE} -C ${YT_DIR} checkout ${BRANCH}
-            fi
-            echo Setting YT_DIR=${YT_DIR}
-        else
-            if [ ! -e $YT_DIR/.git ]
-            then
-                echo "$YT_DIR is not a clone of the yt git repository, exiting"
-                do_exit
-            fi
-        fi
-        if [ $INST_EMBREE -eq 1 ]
-        then
-            echo $DEST_DIR > ${YT_DIR}/embree.cfg
-        fi
-        if [ $INST_ROCKSTAR -eq 1 ]
-        then
-            echo $ROCKSTAR_DIR > ${YT_DIR}/rockstar.cfg
-            ROCKSTAR_LIBRARY_PATH=${DEST_DIR}/lib
-        fi
-        pushd ${YT_DIR} &> /dev/null
-        ( LIBRARY_PATH=$ROCKSTAR_LIBRARY_PATH ${DEST_DIR}/bin/${PYTHON_EXEC} setup.py develop 2>&1) 1>> ${LOG_FILE} || do_exit
-        popd &> /dev/null
+        echo $ROCKSTAR_DIR > ${YT_DIR}/rockstar.cfg
+        ROCKSTAR_LIBRARY_PATH=${DEST_DIR}/lib
     fi
+    pushd ${YT_DIR} &> /dev/null
+    ( LIBRARY_PATH=$ROCKSTAR_LIBRARY_PATH ${DEST_DIR}/bin/${PYTHON_EXEC} setup.py develop 2>&1) 1>> ${LOG_FILE} || do_exit
+    popd &> /dev/null
+fi
 
-    test_install
+test_install
 
-    echo
-    echo
-    echo "========================================================================"
-    echo
-    echo "yt and the Conda system are now installed in $DEST_DIR"
-    echo
-    echo "To get started with yt, check out the orientation:"
-    echo
-    echo "    http://yt-project.org/doc/orientation/"
-    echo
-    echo "For support, see the website and join the mailing list:"
-    echo
-    echo "    http://yt-project.org/"
-    echo "    http://yt-project.org/data/      (Sample data)"
-    echo "    http://yt-project.org/doc/       (Docs)"
-    echo
-    echo "    http://lists.spacepope.org/listinfo.cgi/yt-users-spacepope.org"
-    echo
-    echo "You must now prepend the following folder to your PATH environment variable:"
-    echo 
-    echo "    $DEST_DIR/bin"
-    echo
-    echo "On Bash-style shells you can copy/paste the following command to "
-    echo "temporarily activate the yt installation:"
+echo
+echo
+echo "========================================================================"
+echo
+echo "yt and the Conda system are now installed in $DEST_DIR"
+echo
+echo "To get started with yt, check out the orientation:"
+echo
+echo "    http://yt-project.org/doc/orientation/"
+echo
+echo "For support, see the website and join the mailing list:"
+echo
+echo "    http://yt-project.org/"
+echo "    http://yt-project.org/data/      (Sample data)"
+echo "    http://yt-project.org/doc/       (Docs)"
+echo
+echo "    http://lists.spacepope.org/listinfo.cgi/yt-users-spacepope.org"
+echo
+echo "You must now prepend the following folder to your PATH environment variable:"
+echo 
+echo "    $DEST_DIR/bin"
+echo
+echo "On Bash-style shells you can copy/paste the following command to "
+echo "temporarily activate the yt installation:"
+echo
+echo "    export PATH=$DEST_DIR/bin:\$PATH"
+echo
+echo "and on csh-style shells:"
+echo
+echo "    setenv PATH $DEST_DIR/bin:\$PATH"
+echo
+echo "You can also update the init file appropriate for your shell"
+echo "(e.g. .bashrc, .bash_profile, .cshrc, or .zshrc) to include"
+echo "the same command."
+echo
+if [ $INST_ROCKSTAR -eq 1 ]
+then
+    if [ $MYOS = "Darwin" ]
+    then
+        LD_NAME="DYLD_LIBRARY_PATH"
+    else
+        LD_NAME="LD_LIBRARY_PATH"
+    fi
     echo
-    echo "    export PATH=$DEST_DIR/bin:\$PATH"
-    echo
-    echo "and on csh-style shells:"
+    echo "For rockstar to work, you must also set $LD_NAME:"
     echo
-    echo "    setenv PATH $DEST_DIR/bin:\$PATH"
-    echo
-    echo "You can also update the init file appropriate for your shell"
-    echo "(e.g. .bashrc, .bash_profile, .cshrc, or .zshrc) to include"
-    echo "the same command."
+    echo "    export $LD_NAME=$DEST_DIR/lib:\$$LD_NAME"
     echo
-    if [ $INST_ROCKSTAR -eq 1 ]
-    then
-        if [ $MYOS = "Darwin" ]
-        then
-            LD_NAME="DYLD_LIBRARY_PATH"
-        else
-            LD_NAME="LD_LIBRARY_PATH"
-        fi
-        echo
-        echo "For rockstar to work, you must also set $LD_NAME:"
-        echo
-        echo "    export $LD_NAME=$DEST_DIR/lib:\$$LD_NAME"
-        echo
-        echo "or whichever invocation is appropriate for your shell."
-    fi
-    echo "========================================================================"
-    echo
-    echo "Oh, look at me, still talking when there's science to do!"
-    echo "Good luck, and email the mailing list if you run into any problems."
+    echo "or whichever invocation is appropriate for your shell."
 fi
+echo "========================================================================"
+echo
+echo "Oh, look at me, still talking when there's science to do!"
+echo "Good luck, and email the mailing list if you run into any problems."

diff -r 3249ecb9ce42777551963f05fd3d4ed9207ffb76 -r 31f1a4ccea0427e60c645abd49a11345168de4fe doc/source/analyzing/objects.rst
--- a/doc/source/analyzing/objects.rst
+++ b/doc/source/analyzing/objects.rst
@@ -197,7 +197,6 @@
              (900.1, 'm')]
    sl.plot()
 
-.. _available-objects:
 
 Making Image Buffers
 ^^^^^^^^^^^^^^^^^^^^
@@ -214,6 +213,41 @@
 This `frb` object then can be queried like a normal fixed resolution buffer,
 and it will return arrays of shape (1024, 1024).
 
+Making Rays
+^^^^^^^^^^^
+
+The slicing syntax can also be used select 1D rays of points, whether along 
+an axis or off-axis. To create a ray along an axis:::
+
+    ortho_ray = ds.r[(500.0, "kpc"), (200, "kpc"):(300.0, "kpc"), (-2.0, "Mpc")]
+
+To create a ray off-axis, use a single slice between the start and end points
+of the ray:::
+
+    start = [0.1, 0.2, 0.3] # interpreted in code_length
+    end = [0.4, 0.5, 0.6] # interpreted in code_length
+    ray = ds.r[start:end]
+
+As for the other slicing options, combinations of unitful quantities with even
+different units can be used. Here's a somewhat convoluted (yet working) example:::
+
+    start = ((500.0, "kpc"), (0.2, "Mpc"), (100.0, "kpc"))
+    end = ((1.0, "Mpc"), (300.0, "kpc"), (0.0, "kpc"))
+    ray = ds.r[start:end]
+
+Selecting Points
+^^^^^^^^^^^^^^^^
+
+Finally, you can quickly select a single point within the domain by providing
+a single coordinate for every axis:::
+
+    pt = ds.r[(10.0, 'km'), (200, 'm'), (1.0,'km')]
+
+Querying this object for fields will give you the value of the field at that
+point.
+
+.. _available-objects:
+
 Available Objects
 -----------------
 

diff -r 3249ecb9ce42777551963f05fd3d4ed9207ffb76 -r 31f1a4ccea0427e60c645abd49a11345168de4fe doc/source/examining/loading_data.rst
--- a/doc/source/examining/loading_data.rst
+++ b/doc/source/examining/loading_data.rst
@@ -482,6 +482,36 @@
 fields. Projections, volume rendering, and many of the analysis modules will not
 work.
 
+.. _loading-enzop-data:
+
+Enzo-P Data
+-----------
+
+Enzo-P outputs have three types of files.
+
+.. code-block:: none
+
+   hello-0200/
+   hello-0200/hello-0200.block_list
+   hello-0200/hello-0200.file_list
+   hello-0200/hello-0200.hello-c0020-p0000.h5
+
+To load Enzo-P data into yt, provide the block list file:
+
+.. code-block:: python
+
+   import yt
+   ds = yt.load("hello-0200/hello-0200.block_list")
+
+Mesh fields are fully supported for 1, 2, and 3D datasets.
+
+.. rubric:: Caveats
+
+   * The Enzo-P output format is still evolving somewhat as the code is being
+     actively developed. This frontend will be updated as development continues.
+   * Units are currently assumed to be in CGS.
+   * Particles are not yet supported.
+
 .. _loading-exodusii-data:
 
 Exodus II Data

diff -r 3249ecb9ce42777551963f05fd3d4ed9207ffb76 -r 31f1a4ccea0427e60c645abd49a11345168de4fe doc/source/faq/index.rst
--- a/doc/source/faq/index.rst
+++ b/doc/source/faq/index.rst
@@ -28,25 +28,16 @@
     $ yt version
 
     yt module located at:
-        /Users/username/src/yt-x86_64/src/yt-hg
-    The supplemental repositories are located at:
-        /Users/username/src/yt-x86_64/src/yt-supplemental
+        /Users/username/src/yt-conda/src/yt-git
 
-    The current version and changeset for the code is:
+    The current version of yt is:
 
     ---
-    Version = 2.7-dev
-    Changeset = 6bffc737a67a
+    Version = 3.4-dev
+    Changeset = 94033fca00e5
     ---
 
     This installation CAN be automatically updated.
-    yt dependencies were last updated on
-    Wed Dec  4 15:47:40 MST 2013
-
-    To update all dependencies, run "yt update --all".
-
-If the changeset is displayed followed by a "+", it means you have made
-modifications to the code since the last changeset.
 
 For more information on this topic, see :ref:`updating-yt`.
 

diff -r 3249ecb9ce42777551963f05fd3d4ed9207ffb76 -r 31f1a4ccea0427e60c645abd49a11345168de4fe doc/source/help/index.rst
--- a/doc/source/help/index.rst
+++ b/doc/source/help/index.rst
@@ -36,19 +36,24 @@
 Sometimes the pace of development is pretty fast on yt, particularly in the
 development branch, so a fix to your problem may have already been developed
 by the time you encounter it.  Many users' problems can simply be corrected
-by updating to the latest version of the code and/or its dependencies.  You
-can update yt's source by running:
+by updating to the latest version of the code and/or its dependencies. If you
+have installed the latest stable release of yt then you should update yt using
+the package manager appropriate for your python installation. For example, with
+``pip``::
+
+  $ pip install -U yt
+
+Or with conda::
+
+  $ conda update yt
+
+If you installed yt from a checkout of the git repository, you can update to
+the latest development version by issuing the following command:
 
 .. code-block:: bash
 
   $ yt update
 
-or you could update yt's source as well as any software dependencies by running:
-
-.. code-block:: bash
-
-  $ yt update --all
-
 .. _update-errors:
 
 Update errors

This diff is so big that we needed to truncate the remainder.

https://bitbucket.org/yt_analysis/yt/commits/7f62ed5535f1/
Changeset:   7f62ed5535f1
User:        ngoldbaum
Date:        2017-06-28 20:05:38+00:00
Summary:     Fix incorrect x values for AMR data
Affected #:  1 file

diff -r 31f1a4ccea0427e60c645abd49a11345168de4fe -r 7f62ed5535f16f2007b844ba32687026cdfd81eb yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -50,7 +50,9 @@
         if wh.shape != (1,):
             raise RuntimeError
         field_values[i] = ray_field[wh]
-    return sample_points, field_values
+    dr = np.sqrt((sample_dr**2).sum())
+    x = np.arange(resolution)/(resolution-1)*(dr*resolution)
+    return x, field_values
 
 class CartesianCoordinateHandler(CoordinateHandler):
     name = "cartesian"


https://bitbucket.org/yt_analysis/yt/commits/a64a7e297534/
Changeset:   a64a7e297534
User:        ngoldbaum
Date:        2017-06-28 20:06:27+00:00
Summary:     Ensure LinePlot works for reduced dimensionality data
Affected #:  2 files

diff -r 7f62ed5535f16f2007b844ba32687026cdfd81eb -r a64a7e2975347eeac175f59db769c7e68ad33c1a yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -35,8 +35,7 @@
     start_point = ray.start_point
     end_point = ray.end_point
     sample_dr = (end_point - start_point)/(resolution-1)
-    sample_points = [np.arange(resolution)*sample_dr[i] for i in
-                     range(ray.ds.dimensionality)]
+    sample_points = [np.arange(resolution)*sample_dr[i] for i in range(3)]
     sample_points = uvstack(sample_points).T
     ray_coordinates = uvstack([ray[d] for d in 'xyz']).T
     ray_dds = uvstack([ray['d'+d] for d in 'xyz']).T

diff -r 7f62ed5535f16f2007b844ba32687026cdfd81eb -r a64a7e2975347eeac175f59db769c7e68ad33c1a yt/visualization/line_plot.py
--- a/yt/visualization/line_plot.py
+++ b/yt/visualization/line_plot.py
@@ -90,7 +90,7 @@
         """
         Sets up figure and axes
         """
-        self.start_point = _validate_point(start_point, ds)
+        self.start_point = _validate_point(start_point, ds, start=True)
         self.end_point = _validate_point(end_point, ds)
         self.resolution = resolution
         self._x_unit = None
@@ -249,9 +249,26 @@
         """
         self._titles[self.data_source._determine_fields(field)[0]] = title
 
-def _validate_point(point, ds):
+def _validate_point(point, ds, start=False):
     if not iterable(point):
-        raise RuntimeError
+        raise RuntimeError(
+            "Input point must be array-like"
+        )
     if not isinstance(point, YTArray):
         point = ds.arr(point, 'code_length')
+    if len(point.shape) != 1:
+        raise RuntimeError(
+            "Input point must be a 1D array"
+        )
+    if point.shape[0] < ds.dimensionality:
+        raise RuntimeError(
+            "Input point must have an element for each dimension"
+        )
+    # need to pad to 3D elements to avoid issues later
+    if point.shape[0] < 3:
+        if start:
+            val = 0
+        else:
+            val = 1
+        point = np.append(point.d, [val]*(3-ds.dimensionality))*point.uq
     return point


https://bitbucket.org/yt_analysis/yt/commits/ea2c00f016aa/
Changeset:   ea2c00f016aa
User:        ngoldbaum
Date:        2017-06-28 20:07:12+00:00
Summary:     Fix issue with missized figures
Affected #:  3 files

diff -r a64a7e2975347eeac175f59db769c7e68ad33c1a -r ea2c00f016aa2bc9b9a0d1d18226110900b4d70d yt/visualization/base_plot_types.py
--- a/yt/visualization/base_plot_types.py
+++ b/yt/visualization/base_plot_types.py
@@ -86,6 +86,8 @@
         import matplotlib.figure
         self._plot_valid = True
         if figure is None:
+            if not iterable(fsize):
+                fsize = (fsize, fsize)
             self.figure = matplotlib.figure.Figure(figsize=fsize, frameon=True)
         else:
             figure.set_size_inches(fsize)

diff -r a64a7e2975347eeac175f59db769c7e68ad33c1a -r ea2c00f016aa2bc9b9a0d1d18226110900b4d70d yt/visualization/line_plot.py
--- a/yt/visualization/line_plot.py
+++ b/yt/visualization/line_plot.py
@@ -139,9 +139,14 @@
             y_axis_size = 0.9*fontscale
             right_buff_size = 0.2*fontscale
 
-            xbins = np.array([x_axis_size, self.figure_size[0],
+            if iterable(self.figure_size):
+                figure_size = self.figure_size
+            else:
+                figure_size = (self.figure_size, self.figure_size)
+            
+            xbins = np.array([x_axis_size, figure_size[0],
                               right_buff_size])
-            ybins = np.array([y_axis_size, self.figure_size[1], top_buff_size])
+            ybins = np.array([y_axis_size, figure_size[1], top_buff_size])
 
             size = [xbins.sum(), ybins.sum()]
 

diff -r a64a7e2975347eeac175f59db769c7e68ad33c1a -r ea2c00f016aa2bc9b9a0d1d18226110900b4d70d yt/visualization/plot_container.py
--- a/yt/visualization/plot_container.py
+++ b/yt/visualization/plot_container.py
@@ -199,7 +199,7 @@
         if iterable(figure_size):
             self.figure_size = float(figure_size[0]), float(figure_size[1])
         else:
-            self.figure_size = float(figure_size), float(figure_size)
+            self.figure_size = float(figure_size)
         font_path = matplotlib.get_data_path() + '/fonts/ttf/STIXGeneral.ttf'
         self._font_properties = FontProperties(size=fontsize, fname=font_path)
         self._font_color = None


https://bitbucket.org/yt_analysis/yt/commits/d48318cf04da/
Changeset:   d48318cf04da
User:        ngoldbaum
Date:        2017-06-28 20:07:33+00:00
Summary:     Fix issue with invalid weakrefs
Affected #:  1 file

diff -r ea2c00f016aa2bc9b9a0d1d18226110900b4d70d -r d48318cf04dadff8e61b4ea52d0f8bfd7d837940 yt/visualization/plot_container.py
--- a/yt/visualization/plot_container.py
+++ b/yt/visualization/plot_container.py
@@ -325,6 +325,8 @@
                 lim = getattr(self, lim_name)
                 lim = tuple(new_ds.quan(l.value, str(l.units)) for l in lim)
                 setattr(self, lim_name, lim)
+        self.plots.data_source = new_object
+        self._colorbar_label.data_source = new_object
         self._setup_plots()
 
     @validate_plot


https://bitbucket.org/yt_analysis/yt/commits/03c82180a7d1/
Changeset:   03c82180a7d1
User:        ngoldbaum
Date:        2017-06-28 20:07:50+00:00
Summary:     Fix issue with incorrect fonts on minor tick labels
Affected #:  1 file

diff -r d48318cf04dadff8e61b4ea52d0f8bfd7d837940 -r 03c82180a7d183039d969f66b3fc8b5ac6e0af50 yt/visualization/base_plot_types.py
--- a/yt/visualization/base_plot_types.py
+++ b/yt/visualization/base_plot_types.py
@@ -166,6 +166,8 @@
     def _get_labels(self):
         ax = self.axes
         labels = ax.xaxis.get_ticklabels() + ax.yaxis.get_ticklabels()
+        labels += ax.xaxis.get_minorticklabels()
+        labels += ax.yaxis.get_minorticklabels()
         labels += [ax.title, ax.xaxis.label, ax.yaxis.label,
                    ax.xaxis.get_offset_text(), ax.yaxis.get_offset_text()]
         return labels


https://bitbucket.org/yt_analysis/yt/commits/a65f1cca6a12/
Changeset:   a65f1cca6a12
User:        ngoldbaum
Date:        2017-06-28 20:08:33+00:00
Summary:     Tweak figure positioning
Affected #:  1 file

diff -r 03c82180a7d183039d969f66b3fc8b5ac6e0af50 -r a65f1cca6a12d659cdacd618c558b27a821e7064 yt/visualization/line_plot.py
--- a/yt/visualization/line_plot.py
+++ b/yt/visualization/line_plot.py
@@ -133,10 +133,10 @@
         dimensions_counter = defaultdict(int)
         for field in self.fields:
             fontscale = self._font_properties._size / 14.
-            top_buff_size = 0.3*fontscale
+            top_buff_size = 0.35*fontscale
 
-            x_axis_size = 1.2*fontscale
-            y_axis_size = 0.9*fontscale
+            x_axis_size = 1.35*fontscale
+            y_axis_size = 0.7*fontscale
             right_buff_size = 0.2*fontscale
 
             if iterable(self.figure_size):
@@ -166,8 +166,6 @@
                 plot = PlotMPL(self.figure_size, axrect, None, None)
                 self.plots[field] = plot
 
-            plot._set_font_properties(self._font_properties, None)
-
             x, y = self.ds.coordinates.line_plot(
                 field, self.start_point, self.end_point, self.resolution)
 
@@ -192,6 +190,8 @@
                 else:
                     plot.axes.set_yscale('log')
 
+            plot._set_font_properties(self._font_properties, None)
+
             axes_unit_labels = self._get_axes_unit_labels(unit_x, unit_y)
 
             finfo = self.ds.field_info[field]
@@ -199,7 +199,8 @@
             x_label = r'$\rm{Path\ Length' + axes_unit_labels[0]+'}$'
 
             finfo = self.ds.field_info[field]
-            dimensions = Unit(finfo.units, registry=self.ds.unit_registry).dimensions
+            dimensions = Unit(finfo.units,
+                              registry=self.ds.unit_registry).dimensions
             dimensions_counter[dimensions] += 1
             if dimensions_counter[dimensions] > 1:
                 y_label = (r'$\rm{Multiple\ Fields}$' + r'$\rm{' +


https://bitbucket.org/yt_analysis/yt/commits/9b435dde7125/
Changeset:   9b435dde7125
User:        ngoldbaum
Date:        2017-06-28 21:33:03+00:00
Summary:     fix issues with saving PhasePlots
Affected #:  1 file

diff -r a65f1cca6a12d659cdacd618c558b27a821e7064 -r 9b435dde7125ec7f66e8a1fbb132400018dec39c yt/visualization/profile_plotter.py
--- a/yt/visualization/profile_plotter.py
+++ b/yt/visualization/profile_plotter.py
@@ -41,7 +41,8 @@
 from yt.funcs import \
     ensure_list, \
     get_image_suffix, \
-    matplotlib_style_context
+    matplotlib_style_context, \
+    iterable
 
 def get_canvas(name):
     from . import _mpl_imports as mpl
@@ -1370,7 +1371,10 @@
         if fontscale < 1.0:
             fontscale = np.sqrt(fontscale)
 
-        self._cb_size = 0.0375*figure_size[0]
+        if iterable(figure_size):
+            self._cb_size = 0.0375*figure_size[0]
+        else:
+            self._cb_size = 0.0375*figure_size
         self._ax_text_size = [1.1*fontscale, 0.9*fontscale]
         self._top_buff_size = 0.30*fontscale
         self._aspect = 1.0


https://bitbucket.org/yt_analysis/yt/commits/407271c1ee64/
Changeset:   407271c1ee64
User:        ngoldbaum
Date:        2017-06-29 16:20:53+00:00
Summary:     Use a fake in-memory dataset for the line_plot tests
Affected #:  1 file

diff -r 9b435dde7125ec7f66e8a1fbb132400018dec39c -r 407271c1ee64e310a8ef6f82338a1d60dacdb50f yt/visualization/tests/test_line_plots.py
--- a/yt/visualization/tests/test_line_plots.py
+++ b/yt/visualization/tests/test_line_plots.py
@@ -15,6 +15,7 @@
     requires_ds, \
     data_dir_load, \
     GenericImageTest
+from yt.testing import fake_random_ds
 import os
 import tempfile
 import shutil
@@ -36,7 +37,6 @@
     return test
 
 tri2 = "SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e"
-iso_galaxy = "IsolatedGalaxy/galaxy0030/galaxy0030"
 
 @requires_ds(tri2)
 def test_line_plot():
@@ -44,14 +44,13 @@
     fields = [field for field in ds.field_list if field[0] == 'all']
     yield compare(ds, fields, (0, 0, 0), (1, 1, 0), 1000, "answers_line_plot")
 
- at requires_ds(iso_galaxy)
 def test_line_plot_methods():
     # Perform I/O in safe place instead of yt main dir
     tmpdir = tempfile.mkdtemp()
     curdir = os.getcwd()
     os.chdir(tmpdir)
 
-    ds = data_dir_load(iso_galaxy)
+    ds = fake_random_ds(32)
 
     plot = yt.LinePlot(ds, 'density', [0, 0, 0], [1, 1, 1], 512)
     plot.add_legend('density')


https://bitbucket.org/yt_analysis/yt/commits/9a5120b67fbb/
Changeset:   9a5120b67fbb
User:        Alex Lindsay
Date:        2017-07-05 21:15:42+00:00
Summary:     Address Britton Smith review.
Affected #:  4 files

diff -r 407271c1ee64e310a8ef6f82338a1d60dacdb50f -r 9a5120b67fbbe8408a3c77745c9083dff72a5f2b doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -1072,9 +1072,9 @@
 4. The ending point of the sampling line. This should also be an n-element list, tuple,
    ndarray, or YTArray with the elements corresponding to the coordinates of the
    ending point.
-5. The resolution of the sampling line. This is the number of sampling points
-   along the line, e.g. if 1000 is specified, then data will be sampled at
-   1000 points evenly spaced between the starting and ending points.
+5. The number of sampling points along the line, e.g. if 1000 is specified, then
+   data will be sampled at 1000 points evenly spaced between the starting and
+   ending points.
 
 The below code snippet illustrates how this is done:
 
@@ -1114,7 +1114,17 @@
 one with plots of the "length/time" fields and another with the plot of the
 "temperature" field. It is only necessary to call ``add_legend``
 for one field of a multi-field plot to produce a legend containing all the
-labels passed in the initial construction of the ``LinePlot`` instance.
+labels passed in the initial construction of the ``LinePlot`` instance. Example:
+
+.. python-script::
+
+   import yt
+
+   ds = yt.load("SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1)
+   plot = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], [0, 0, 0], [0, 1, 0],
+                      100, labels={('all', 'u') : r"v$_x$", ('all', 'v') : r"v$_y$"})
+   plot.add_legend(('all', 'u'))
+   plot.save()
 
 
 .. _how-to-make-2d-profiles:

diff -r 407271c1ee64e310a8ef6f82338a1d60dacdb50f -r 9a5120b67fbbe8408a3c77745c9083dff72a5f2b yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -21,9 +21,8 @@
     _get_vert_fields, \
     cartesian_to_cylindrical, \
     cylindrical_to_cartesian
-from yt import YTArray
 from yt.funcs import mylog
-from yt.units.yt_array import uvstack
+from yt.units.yt_array import uvstack, YTArray
 from yt.utilities.lib.pixelization_routines import \
     pixelize_element_mesh, pixelize_off_axis_cartesian, \
     pixelize_cartesian, pixelize_cartesian_nodal, \
@@ -31,16 +30,29 @@
 from yt.data_objects.unstructured_mesh import SemiStructuredMesh
 from yt.utilities.nodal_data_utils import get_nodal_data
 
-def _sample_ray(ray, resolution, field):
+def _sample_ray(ray, npoints, field):
+    """
+    Private function that uses a ray object for calculating the field values
+    that will be the y-axis values in a LinePlot object.
+
+    Parameters
+    ----------
+    ray : YTOrthoRay, YTRay, or LightRay
+        Ray object from which to sample field values
+    npoints : int
+        The number of points to sample
+    field : str or field tuple
+        The name of the field to sample
+    """
     start_point = ray.start_point
     end_point = ray.end_point
-    sample_dr = (end_point - start_point)/(resolution-1)
-    sample_points = [np.arange(resolution)*sample_dr[i] for i in range(3)]
+    sample_dr = (end_point - start_point)/(npoints-1)
+    sample_points = [np.arange(npoints)*sample_dr[i] for i in range(3)]
     sample_points = uvstack(sample_points).T
     ray_coordinates = uvstack([ray[d] for d in 'xyz']).T
     ray_dds = uvstack([ray['d'+d] for d in 'xyz']).T
     ray_field = ray[field]
-    field_values = ray.ds.arr(np.zeros(resolution), ray_field.units)
+    field_values = ray.ds.arr(np.zeros(npoints), ray_field.units)
     for i, sample_point in enumerate(sample_points):
         ray_contains = ((sample_point >= (ray_coordinates - ray_dds/2)) &
                         (sample_point <= (ray_coordinates + ray_dds/2)))
@@ -50,7 +62,7 @@
             raise RuntimeError
         field_values[i] = ray_field[wh]
     dr = np.sqrt((sample_dr**2).sum())
-    x = np.arange(resolution)/(resolution-1)*(dr*resolution)
+    x = np.arange(npoints)/(npoints-1)*(dr*npoints)
     return x, field_values
 
 class CartesianCoordinateHandler(CoordinateHandler):
@@ -149,13 +161,15 @@
                                           antialias)
 
 
-    def line_plot(self, field, start_point, end_point, resolution):
+    def line_plot(self, field, start_point, end_point, npoints):
         """
         Method for sampling datasets along a line in preparation for
         one-dimensional line plots. For UnstructuredMesh, relies on a
         sampling routine written in cython
         """
-
+        if npoints < 2:
+            raise ValueError("Must have at least two sample points in order "
+                             "to draw a line plot.")
         index = self.ds.index
         if (hasattr(index, 'meshes') and
            not isinstance(index.meshes[0], SemiStructuredMesh)):
@@ -186,13 +200,13 @@
             arc_length, plot_values = element_mesh_line_plot(coords, indices,
                                                              start_point,
                                                              end_point,
-                                                             resolution, field_data,
+                                                             npoints, field_data,
                                                              index_offset=offset)
             arc_length = YTArray(arc_length, start_point.units)
             plot_values = YTArray(plot_values, field_data.units)
         else:
             ray = self.ds.ray(start_point, end_point)
-            arc_length, plot_values = _sample_ray(ray, resolution, field)
+            arc_length, plot_values = _sample_ray(ray, npoints, field)
         return arc_length, plot_values
 
 

diff -r 407271c1ee64e310a8ef6f82338a1d60dacdb50f -r 9a5120b67fbbe8408a3c77745c9083dff72a5f2b yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -867,7 +867,7 @@
                            np.ndarray[np.int64_t, ndim=2] conn,
                            np.ndarray[np.float64_t, ndim=1] start_point,
                            np.ndarray[np.float64_t, ndim=1] end_point,
-                           resolution,
+                           npoints,
                            np.ndarray[np.float64_t, ndim=2] field,
                            int index_offset = 0):
 
@@ -878,7 +878,8 @@
     cdef int nvertices = conn.shape[1]
     cdef int ndim = coords.shape[1]
     cdef int num_field_vals = field.shape[1]
-    cdef int num_plot_nodes = resolution + 1
+    cdef int num_plot_nodes = npoints
+    cdef int num_intervals = npoints - 1
     cdef double[4] mapped_coord
     cdef ElementSampler sampler
     cdef np.ndarray[np.float64_t, ndim=1] lin_vec
@@ -927,17 +928,17 @@
 
     lin_vec = end_point - start_point
     lin_length = np.linalg.norm(lin_vec)
-    lin_inc = lin_vec / resolution
-    inc_length = lin_length / resolution
+    lin_inc = lin_vec / num_intervals
+    inc_length = lin_length / num_intervals
     for j in range(ndim):
         lin_sample_points[0, j] = start_point[j]
     arc_length[0] = 0
-    for i in range(1, resolution + 1):
+    for i in range(1, num_intervals + 1):
         for j in range(ndim):
             lin_sample_points[i, j] = lin_sample_points[i-1, j] + lin_inc[j]
             arc_length[i] = arc_length[i-1] + inc_length
 
-    for i in range(resolution + 1):
+    for i in range(num_intervals + 1):
         for j in range(3):
             if j < ndim:
                 sample_point[j] = lin_sample_points[i][j]

diff -r 407271c1ee64e310a8ef6f82338a1d60dacdb50f -r 9a5120b67fbbe8408a3c77745c9083dff72a5f2b yt/visualization/line_plot.py
--- a/yt/visualization/line_plot.py
+++ b/yt/visualization/line_plot.py
@@ -6,7 +6,7 @@
 """
 
 #-----------------------------------------------------------------------------
-# Copyright (c) 2013, yt Development Team.
+# Copyright (c) 2017, yt Development Team.
 #
 # Distributed under the terms of the Modified BSD License.
 #
@@ -79,20 +79,34 @@
         Contains the coordinates of the first point for constructing the line.
         Must contain n elements where n is the dimensionality of the dataset.
     figure_size: integer or two-element
-    resolution: int
+    npoints: int
         How many points to sample between start_point and end_point for
         constructing the line plot
+
+    Example
+    -------
+
+    >>> import yt
+    >>>
+    >>> ds = yt.load('IsolatedGalaxy/galaxy0030/galaxy0030')
+    >>>
+    >>> plot = yt.LinePlot(ds, 'density', [0, 0, 0], [1, 1, 1], 512)
+    >>> plot.add_legend('density')
+    >>> plot.set_x_unit('cm')
+    >>> plot.set_unit('density', 'kg/cm**3')
+    >>> plot.save()
+
     """
     _plot_type = 'line_plot'
 
-    def __init__(self, ds, fields, start_point, end_point, resolution,
+    def __init__(self, ds, fields, start_point, end_point, npoints,
                  figure_size=5., fontsize=14., labels=None):
         """
         Sets up figure and axes
         """
         self.start_point = _validate_point(start_point, ds, start=True)
         self.end_point = _validate_point(end_point, ds)
-        self.resolution = resolution
+        self.npoints = npoints
         self._x_unit = None
         self._y_units = {}
         self._titles = {}
@@ -143,7 +157,7 @@
                 figure_size = self.figure_size
             else:
                 figure_size = (self.figure_size, self.figure_size)
-            
+
             xbins = np.array([x_axis_size, figure_size[0],
                               right_buff_size])
             ybins = np.array([y_axis_size, figure_size[1], top_buff_size])
@@ -167,7 +181,7 @@
                 self.plots[field] = plot
 
             x, y = self.ds.coordinates.line_plot(
-                field, self.start_point, self.end_point, self.resolution)
+                field, self.start_point, self.end_point, self.npoints)
 
             if self._x_unit is None:
                 unit_x = x.units


https://bitbucket.org/yt_analysis/yt/commits/281cf5be7c9f/
Changeset:   281cf5be7c9f
User:        Alex Lindsay
Date:        2017-07-06 01:57:15+00:00
Summary:     Address remaining Britton review.
Affected #:  2 files

diff -r 9a5120b67fbbe8408a3c77745c9083dff72a5f2b -r 281cf5be7c9f796e4d60914cd972d0361481c0f7 doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -1126,6 +1126,15 @@
    plot.add_legend(('all', 'u'))
    plot.save()
 
+``LinePlot`` is a bit different from yt ray objects which are data
+containers. ``LinePlot`` is a plotting class that may use yt ray objects to
+supply field plotting information. However, perhaps the most important
+difference to highlight between rays and ``LinePlot`` is that rays return data
+elements that intersect with the ray and make no guarantee about the spacing
+between data elements. ``LinePlot`` sampling points are guaranteed to be evenly
+spaced. In the case of cell data where multiple points fall within the same
+cell, the ``LinePlot`` object will show the same field value for each sampling
+point that falls within the same cell.
 
 .. _how-to-make-2d-profiles:
 

diff -r 9a5120b67fbbe8408a3c77745c9083dff72a5f2b -r 281cf5be7c9f796e4d60914cd972d0361481c0f7 yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -57,10 +57,7 @@
         ray_contains = ((sample_point >= (ray_coordinates - ray_dds/2)) &
                         (sample_point <= (ray_coordinates + ray_dds/2)))
         ray_contains = ray_contains.all(axis=-1)
-        wh = np.where(ray_contains)[0]
-        if wh.shape != (1,):
-            raise RuntimeError
-        field_values[i] = ray_field[wh]
+        field_values[i] = ray_field[ray_contains]
     dr = np.sqrt((sample_dr**2).sum())
     x = np.arange(npoints)/(npoints-1)*(dr*npoints)
     return x, field_values


https://bitbucket.org/yt_analysis/yt/commits/df0dfe8d1cd3/
Changeset:   df0dfe8d1cd3
User:        Alex Lindsay
Date:        2017-07-06 14:52:13+00:00
Summary:     Increment answer test counter since argument changed to n_nodes from n_elements for unstructured mesh plotting.
Affected #:  1 file

diff -r 281cf5be7c9f796e4d60914cd972d0361481c0f7 -r df0dfe8d1cd3f94a21a5fdc0c38fe6840d5933f2 tests/tests.yaml
--- a/tests/tests.yaml
+++ b/tests/tests.yaml
@@ -67,7 +67,7 @@
     - yt/analysis_modules/photon_simulator/tests/test_spectra.py
     - yt/analysis_modules/photon_simulator/tests/test_sloshing.py
 
-  local_unstructured_007:
+  local_unstructured_008:
     - yt/visualization/volume_rendering/tests/test_mesh_render.py
     - yt/visualization/tests/test_mesh_slices.py:test_tri2
     - yt/visualization/tests/test_mesh_slices.py:test_quad2


https://bitbucket.org/yt_analysis/yt/commits/7ebf1dd9b9a8/
Changeset:   7ebf1dd9b9a8
User:        Alex Lindsay
Date:        2017-07-06 18:47:07+00:00
Summary:     Finish line plot docstring
Affected #:  1 file

diff -r df0dfe8d1cd3f94a21a5fdc0c38fe6840d5933f2 -r 7ebf1dd9b9a8e74fae7866f907391d066705e7aa yt/visualization/line_plot.py
--- a/yt/visualization/line_plot.py
+++ b/yt/visualization/line_plot.py
@@ -67,21 +67,31 @@
     Parameters
     ----------
 
-    ds: :class:`yt.data_objects.static_output.Dataset`
+    ds : :class:`yt.data_objects.static_output.Dataset`
         This is the dataset object corresponding to the
         simulation output to be plotted.
-    fields: string
+    fields : string / tuple, or list of strings / tuples
         The name(s) of the field(s) to be plotted.
-    start_point: n-element list, tuple, ndarray, or YTArray
+    start_point : n-element list, tuple, ndarray, or YTArray
+        Contains the coordinates of the first point for constructing the line.
+        Must contain n elements where n is the dimensionality of the dataset.
+    end_point : n-element list, tuple, ndarray, or YTArray
         Contains the coordinates of the first point for constructing the line.
         Must contain n elements where n is the dimensionality of the dataset.
-    end_point: n-element list, tuple, ndarray, or YTArray
-        Contains the coordinates of the first point for constructing the line.
-        Must contain n elements where n is the dimensionality of the dataset.
-    figure_size: integer or two-element
-    npoints: int
+    npoints : int
         How many points to sample between start_point and end_point for
         constructing the line plot
+    figure_size : int or two-element iterable of ints
+        Size in inches of the image.
+        Default: 5 (5x5)
+    fontsize : int
+        Font size for all text in the plot.
+        Default: 14
+    labels : dictionary
+        Keys should be the field names. Values should be latex-formattable
+        strings used in the LinePlot legend
+        Default: None
+
 
     Example
     -------


https://bitbucket.org/yt_analysis/yt/commits/8ff2f719cd9d/
Changeset:   8ff2f719cd9d
User:        Alex Lindsay
Date:        2017-07-06 19:05:45+00:00
Summary:     Rename line plotting utilities to 'pixelize_'
Affected #:  4 files

diff -r 7ebf1dd9b9a8e74fae7866f907391d066705e7aa -r 8ff2f719cd9d489941dc3eae3bba7408f813efef yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -26,7 +26,7 @@
 from yt.utilities.lib.pixelization_routines import \
     pixelize_element_mesh, pixelize_off_axis_cartesian, \
     pixelize_cartesian, pixelize_cartesian_nodal, \
-    element_mesh_line_plot
+    pixelize_element_mesh_line
 from yt.data_objects.unstructured_mesh import SemiStructuredMesh
 from yt.utilities.nodal_data_utils import get_nodal_data
 
@@ -158,7 +158,7 @@
                                           antialias)
 
 
-    def line_plot(self, field, start_point, end_point, npoints):
+    def pixelize_line(self, field, start_point, end_point, npoints):
         """
         Method for sampling datasets along a line in preparation for
         one-dimensional line plots. For UnstructuredMesh, relies on a
@@ -194,11 +194,11 @@
                 field_data = field_data[:, 0:8]
                 indices = indices[:, 0:8]
 
-            arc_length, plot_values = element_mesh_line_plot(coords, indices,
-                                                             start_point,
-                                                             end_point,
-                                                             npoints, field_data,
-                                                             index_offset=offset)
+            arc_length, plot_values = pixelize_element_mesh_line(coords, indices,
+                                                                 start_point,
+                                                                 end_point,
+                                                                 npoints, field_data,
+                                                                 index_offset=offset)
             arc_length = YTArray(arc_length, start_point.units)
             plot_values = YTArray(plot_values, field_data.units)
         else:

diff -r 7ebf1dd9b9a8e74fae7866f907391d066705e7aa -r 8ff2f719cd9d489941dc3eae3bba7408f813efef yt/geometry/coordinates/coordinate_handler.py
--- a/yt/geometry/coordinates/coordinate_handler.py
+++ b/yt/geometry/coordinates/coordinate_handler.py
@@ -72,7 +72,7 @@
 
 class CoordinateHandler(object):
     name = None
-    
+
     def __init__(self, ds, ordering):
         self.ds = weakref.proxy(ds)
         self.axis_order = ordering
@@ -86,7 +86,7 @@
         # pixelizer
         raise NotImplementedError
 
-    def line_plot(self, field, start_point, end_point, resolution):
+    def pixelize_line(self, field, start_point, end_point, npoints):
         raise NotImplementedError
 
     def distance(self, start, end):
@@ -268,4 +268,3 @@
     c2[...,1] = np.sin(coord[...,0]) * coord[...,1] + center[1]
     c2[...,2] = coord[...,2]
     return c2
-

diff -r 7ebf1dd9b9a8e74fae7866f907391d066705e7aa -r 8ff2f719cd9d489941dc3eae3bba7408f813efef yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -863,13 +863,13 @@
     free(field_vals)
     return img
 
-def element_mesh_line_plot(np.ndarray[np.float64_t, ndim=2] coords,
-                           np.ndarray[np.int64_t, ndim=2] conn,
-                           np.ndarray[np.float64_t, ndim=1] start_point,
-                           np.ndarray[np.float64_t, ndim=1] end_point,
-                           npoints,
-                           np.ndarray[np.float64_t, ndim=2] field,
-                           int index_offset = 0):
+def pixelize_element_mesh_line(np.ndarray[np.float64_t, ndim=2] coords,
+                               np.ndarray[np.int64_t, ndim=2] conn,
+                               np.ndarray[np.float64_t, ndim=1] start_point,
+                               np.ndarray[np.float64_t, ndim=1] end_point,
+                               npoints,
+                               np.ndarray[np.float64_t, ndim=2] field,
+                               int index_offset = 0):
 
     # This routine chooses the correct element sampler to interpolate field
     # values at evenly spaced points along a sampling line

diff -r 7ebf1dd9b9a8e74fae7866f907391d066705e7aa -r 8ff2f719cd9d489941dc3eae3bba7408f813efef yt/visualization/line_plot.py
--- a/yt/visualization/line_plot.py
+++ b/yt/visualization/line_plot.py
@@ -190,7 +190,7 @@
                 plot = PlotMPL(self.figure_size, axrect, None, None)
                 self.plots[field] = plot
 
-            x, y = self.ds.coordinates.line_plot(
+            x, y = self.ds.coordinates.pixelize_line(
                 field, self.start_point, self.end_point, self.npoints)
 
             if self._x_unit is None:


https://bitbucket.org/yt_analysis/yt/commits/d326aefaa00a/
Changeset:   d326aefaa00a
User:        ngoldbaum
Date:        2017-07-06 19:28:49+00:00
Summary:     Use argmax to disambiguate when we select multiple cells for a sampling point.
Affected #:  1 file

diff -r 8ff2f719cd9d489941dc3eae3bba7408f813efef -r d326aefaa00a8e9c1f4fb466fd496b685612740e yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -57,7 +57,10 @@
         ray_contains = ((sample_point >= (ray_coordinates - ray_dds/2)) &
                         (sample_point <= (ray_coordinates + ray_dds/2)))
         ray_contains = ray_contains.all(axis=-1)
-        field_values[i] = ray_field[ray_contains]
+        # use argmax to find the first nonzero index, sometimes there
+        # are two indices if the sampling point happens to fall exactly at
+        # a cell boundary
+        field_values[i] = ray_field[np.argmax(ray_contains)]
     dr = np.sqrt((sample_dr**2).sum())
     x = np.arange(npoints)/(npoints-1)*(dr*npoints)
     return x, field_values


https://bitbucket.org/yt_analysis/yt/commits/eef1091aa468/
Changeset:   eef1091aa468
User:        ngoldbaum
Date:        2017-07-06 21:29:54+00:00
Summary:     fix issue with incorrect start_point for ray sampling
Affected #:  1 file

diff -r d326aefaa00a8e9c1f4fb466fd496b685612740e -r eef1091aa46826d2c7c55a49938da4302ee1180a yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -48,7 +48,7 @@
     end_point = ray.end_point
     sample_dr = (end_point - start_point)/(npoints-1)
     sample_points = [np.arange(npoints)*sample_dr[i] for i in range(3)]
-    sample_points = uvstack(sample_points).T
+    sample_points = uvstack(sample_points).T + start_point
     ray_coordinates = uvstack([ray[d] for d in 'xyz']).T
     ray_dds = uvstack([ray['d'+d] for d in 'xyz']).T
     ray_field = ray[field]


https://bitbucket.org/yt_analysis/yt/commits/41f22e5d10cb/
Changeset:   41f22e5d10cb
User:        jzuhone
Date:        2017-07-12 17:58:37+00:00
Summary:     Merge pull request #1440 from lindsayad/line_plot_plus_quad9

Initial line plotting
Affected #:  19 files

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 doc/source/cookbook/simple_1d_line_plot.py
--- /dev/null
+++ b/doc/source/cookbook/simple_1d_line_plot.py
@@ -0,0 +1,14 @@
+import yt
+
+# Load the dataset
+ds = yt.load("SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1)
+
+# Create a line plot of the variables 'u' and 'v' with 1000 sampling points evenly spaced
+# between the coordinates (0, 0, 0) and (0, 1, 0)
+plot = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], (0, 0, 0), (0, 1, 0), 1000)
+
+# Add a legend
+plot.add_legend(('all', 'v'))
+
+# Save the line plot
+plot.save()

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 doc/source/cookbook/simple_plots.rst
--- a/doc/source/cookbook/simple_plots.rst
+++ b/doc/source/cookbook/simple_plots.rst
@@ -43,6 +43,14 @@
 
 .. yt_cookbook:: simple_phase.py
 
+Simple 1D Line Plotting
+~~~~~~~~~~~~~~~~~~~~~~~
+
+This script shows how to make a ``LinePlot`` through a dataset.
+See :ref:`how-to-1d-line-plot` for more information.
+
+.. yt_cookbook:: simple_1d_line_plot.py
+
 Simple Probability Distribution Functions
 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -14,8 +14,8 @@
 
 The :class:`~yt.visualization.plot_window.PlotWindow` interface is useful for
 taking a quick look at simulation outputs.  Simple mechanisms exist for making
-plots of slices, projections, 1D profiles, and 2D profiles (phase plots), all of
-which are described below.
+plots of slices, projections, 1D spatial line plots, 1D profiles, and 2D
+profiles (phase plots), all of which are described below.
 
 .. _viewing-plots:
 
@@ -30,7 +30,7 @@
 in other environments as well:
 
 .. code-block:: python
- 
+
    %matplotlib notebook
    import yt
    yt.toggle_interactivity()
@@ -943,6 +943,7 @@
    # Save the image.
    plot.save()
 
+
 Customizing axis limits
 ~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -1054,6 +1055,87 @@
     # change only the first line
     plot.set_line_property("linestyle", "--", 0)
 
+.. _how-to-1d-unstructured-mesh:
+
+1D Line Sampling
+----------------
+
+YT has the ability to sample datasets along arbitrary lines
+and plot the result. You must supply five arguments to the ``LinePlot``
+class. They are enumerated below:
+
+1. Dataset
+2. A list of fields or a single field you wish to plot
+3. The starting point of the sampling line. This should be an n-element list, tuple,
+   ndarray, or YTArray with the elements corresponding to the coordinates of the
+   starting point. (n should equal the dimension of the dataset)
+4. The ending point of the sampling line. This should also be an n-element list, tuple,
+   ndarray, or YTArray with the elements corresponding to the coordinates of the
+   ending point.
+5. The number of sampling points along the line, e.g. if 1000 is specified, then
+   data will be sampled at 1000 points evenly spaced between the starting and
+   ending points.
+
+The below code snippet illustrates how this is done:
+
+.. code-block:: python
+
+   ds = yt.load("SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1)
+   plot = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], (0, 0, 0), (0, 1, 0), 1000)
+   plot.save()
+
+If working in a Jupyter Notebook, ``LinePlot`` also has the ``show()`` method.
+
+You can can add a legend to a 1D sampling plot. The legend process takes two steps:
+
+1. When instantiating the ``LinePlot``, pass a dictionary of
+   labels with keys corresponding to the field names
+2. Call the ``LinePlot`` ``add_legend`` method
+
+X- and Y- axis units can be set with ``set_x_unit`` and ``set_unit`` methods
+respectively. The below code snippet combines all the features we've discussed:
+
+.. python-script::
+
+   import yt
+
+   ds = yt.load('IsolatedGalaxy/galaxy0030/galaxy0030')
+
+   plot = yt.LinePlot(ds, 'density', [0, 0, 0], [1, 1, 1], 512)
+   plot.add_legend('density')
+   plot.set_x_unit('cm')
+   plot.set_unit('density', 'kg/cm**3')
+   plot.save()
+
+If a list of fields is passed to ``LinePlot``, yt will create a number of
+individual figures equal to the number of different dimensional
+quantities. E.g. if ``LinePlot`` receives two fields with units of "length/time"
+and a field with units of "temperature", two different figures will be created,
+one with plots of the "length/time" fields and another with the plot of the
+"temperature" field. It is only necessary to call ``add_legend``
+for one field of a multi-field plot to produce a legend containing all the
+labels passed in the initial construction of the ``LinePlot`` instance. Example:
+
+.. python-script::
+
+   import yt
+
+   ds = yt.load("SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e", step=-1)
+   plot = yt.LinePlot(ds, [('all', 'v'), ('all', 'u')], [0, 0, 0], [0, 1, 0],
+                      100, labels={('all', 'u') : r"v$_x$", ('all', 'v') : r"v$_y$"})
+   plot.add_legend(('all', 'u'))
+   plot.save()
+
+``LinePlot`` is a bit different from yt ray objects which are data
+containers. ``LinePlot`` is a plotting class that may use yt ray objects to
+supply field plotting information. However, perhaps the most important
+difference to highlight between rays and ``LinePlot`` is that rays return data
+elements that intersect with the ray and make no guarantee about the spacing
+between data elements. ``LinePlot`` sampling points are guaranteed to be evenly
+spaced. In the case of cell data where multiple points fall within the same
+cell, the ``LinePlot`` object will show the same field value for each sampling
+point that falls within the same cell.
+
 .. _how-to-make-2d-profiles:
 
 2D Phase Plots

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 tests/tests.yaml
--- a/tests/tests.yaml
+++ b/tests/tests.yaml
@@ -67,11 +67,12 @@
     - yt/analysis_modules/photon_simulator/tests/test_spectra.py
     - yt/analysis_modules/photon_simulator/tests/test_sloshing.py
 
-  local_unstructured_006:
+  local_unstructured_008:
     - yt/visualization/volume_rendering/tests/test_mesh_render.py
     - yt/visualization/tests/test_mesh_slices.py:test_tri2
     - yt/visualization/tests/test_mesh_slices.py:test_quad2
     - yt/visualization/tests/test_mesh_slices.py:test_multi_region
+    - yt/visualization/tests/test_line_plots.py:test_line_plot
 
   local_boxlib_004:
     - yt/frontends/boxlib/tests/test_outputs.py:test_radadvect

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 yt/__init__.py
--- a/yt/__init__.py
+++ b/yt/__init__.py
@@ -103,7 +103,7 @@
     FixedResolutionBuffer, ObliqueFixedResolutionBuffer, \
     write_bitmap, write_image, \
     apply_colormap, scale_image, write_projection, \
-    SlicePlot, AxisAlignedSlicePlot, OffAxisSlicePlot, \
+    SlicePlot, AxisAlignedSlicePlot, OffAxisSlicePlot, LinePlot, \
     ProjectionPlot, OffAxisProjectionPlot, \
     show_colormaps, add_cmap, make_colormap, \
     ProfilePlot, PhasePlot, ParticlePhasePlot, \

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 yt/fields/derived_field.py
--- a/yt/fields/derived_field.py
+++ b/yt/fields/derived_field.py
@@ -13,6 +13,7 @@
 
 import contextlib
 import inspect
+import re
 import warnings
 
 from yt.extern.six import string_types, PY2
@@ -292,6 +293,51 @@
         s += ")"
         return s
 
+    def _is_ion(self):
+        p = re.compile("_p[0-9]+_")
+        result = False
+        if p.search(self.name[1]) is not None:
+            result = True
+        return result
+
+    def _ion_to_label(self):
+        pnum2rom = {
+            "0":"I", "1":"II", "2":"III", "3":"IV", "4":"V",
+            "5":"VI", "6":"VII", "7":"VIII", "8":"IX", "9":"X",
+            "10":"XI", "11":"XII", "12":"XIII", "13":"XIV", "14":"XV",
+            "15":"XVI", "16":"XVII", "17":"XVIII", "18":"XIX", "19":"XX"}
+
+        p = re.compile("_p[0-9]+_")
+        m = p.search(self.name[1])
+        if m is not None:
+            pstr = m.string[m.start()+1:m.end()-1]
+            segments = self.name[1].split("_")
+            for i,s in enumerate(segments):
+                segments[i] = s.capitalize()
+                if s == pstr:
+                    ipstr = i
+            element = segments[ipstr-1]
+            roman = pnum2rom[pstr[1:]]
+            label = element + '\ ' + roman + '\ ' + \
+                '\ '.join(segments[ipstr+1:])
+        else:
+            label = self.name[1]
+        return label
+
+    def get_latex_display_name(self):
+        label = self.display_name
+        if label is None:
+            if self._is_ion():
+                fname = self._ion_to_label()
+                label = r'$\rm{'+fname.replace('_','\ ')+r'}$'
+            else:
+                label = r'$\rm{'+self.name[1].replace('_','\ ').title()+r'}$'
+        elif label.find('$') == -1:
+            label = label.replace(' ','\ ')
+            label = r'$\rm{'+label+r'}$'
+        return label
+
+
 class FieldValidator(object):
     pass
 
@@ -361,6 +407,7 @@
         FieldValidator.__init__(self)
         self.ghost_zones = ghost_zones
         self.fields = fields
+
     def __call__(self, data):
         # When we say spatial information, we really mean
         # that it has a three-dimensional data structure

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 yt/frontends/exodus_ii/fields.py
--- a/yt/frontends/exodus_ii/fields.py
+++ b/yt/frontends/exodus_ii/fields.py
@@ -33,6 +33,8 @@
 
     def __init__(self, ds, field_list):
         super(ExodusIIFieldInfo, self).__init__(ds, field_list)
+        for name in self:
+            self[name].take_log = False
         # If you want, you can check self.field_list
 
     def setup_fluid_fields(self):

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -22,12 +22,48 @@
     cartesian_to_cylindrical, \
     cylindrical_to_cartesian
 from yt.funcs import mylog
+from yt.units.yt_array import uvstack, YTArray
 from yt.utilities.lib.pixelization_routines import \
     pixelize_element_mesh, pixelize_off_axis_cartesian, \
-    pixelize_cartesian, pixelize_cartesian_nodal
+    pixelize_cartesian, pixelize_cartesian_nodal, \
+    pixelize_element_mesh_line
 from yt.data_objects.unstructured_mesh import SemiStructuredMesh
 from yt.utilities.nodal_data_utils import get_nodal_data
 
+def _sample_ray(ray, npoints, field):
+    """
+    Private function that uses a ray object for calculating the field values
+    that will be the y-axis values in a LinePlot object.
+
+    Parameters
+    ----------
+    ray : YTOrthoRay, YTRay, or LightRay
+        Ray object from which to sample field values
+    npoints : int
+        The number of points to sample
+    field : str or field tuple
+        The name of the field to sample
+    """
+    start_point = ray.start_point
+    end_point = ray.end_point
+    sample_dr = (end_point - start_point)/(npoints-1)
+    sample_points = [np.arange(npoints)*sample_dr[i] for i in range(3)]
+    sample_points = uvstack(sample_points).T + start_point
+    ray_coordinates = uvstack([ray[d] for d in 'xyz']).T
+    ray_dds = uvstack([ray['d'+d] for d in 'xyz']).T
+    ray_field = ray[field]
+    field_values = ray.ds.arr(np.zeros(npoints), ray_field.units)
+    for i, sample_point in enumerate(sample_points):
+        ray_contains = ((sample_point >= (ray_coordinates - ray_dds/2)) &
+                        (sample_point <= (ray_coordinates + ray_dds/2)))
+        ray_contains = ray_contains.all(axis=-1)
+        # use argmax to find the first nonzero index, sometimes there
+        # are two indices if the sampling point happens to fall exactly at
+        # a cell boundary
+        field_values[i] = ray_field[np.argmax(ray_contains)]
+    dr = np.sqrt((sample_dr**2).sum())
+    x = np.arange(npoints)/(npoints-1)*(dr*npoints)
+    return x, field_values
 
 class CartesianCoordinateHandler(CoordinateHandler):
     name = "cartesian"
@@ -65,6 +101,11 @@
 
     def pixelize(self, dimension, data_source, field, bounds, size,
                  antialias = True, periodic = True):
+        """
+        Method for pixelizing datasets in preparation for
+        two-dimensional image plots. Relies on several sampling
+        routines written in cython
+        """
         index = data_source.ds.index
         if (hasattr(index, 'meshes') and
            not isinstance(index.meshes[0], SemiStructuredMesh)):
@@ -119,6 +160,57 @@
             return self._oblique_pixelize(data_source, field, bounds, size,
                                           antialias)
 
+
+    def pixelize_line(self, field, start_point, end_point, npoints):
+        """
+        Method for sampling datasets along a line in preparation for
+        one-dimensional line plots. For UnstructuredMesh, relies on a
+        sampling routine written in cython
+        """
+        if npoints < 2:
+            raise ValueError("Must have at least two sample points in order "
+                             "to draw a line plot.")
+        index = self.ds.index
+        if (hasattr(index, 'meshes') and
+           not isinstance(index.meshes[0], SemiStructuredMesh)):
+            ftype, fname = field
+            if ftype == "all":
+                mesh_id = 0
+                indices = np.concatenate([mesh.connectivity_indices for mesh in index.mesh_union])
+            else:
+                mesh_id = int(ftype[-1]) - 1
+                indices = index.meshes[mesh_id].connectivity_indices
+
+            coords = index.meshes[mesh_id].connectivity_coords
+            if coords.shape[1] != end_point.size != start_point.size:
+                raise ValueError("The coordinate dimension doesn't match the "
+                                 "start and end point dimensions.")
+
+
+            offset = index.meshes[mesh_id]._index_offset
+            ad = self.ds.all_data()
+            field_data = ad[field]
+            if field_data.shape[1] == 27:
+                # hexahedral
+                mylog.warning("High order elements not yet supported, " +
+                              "dropping to 1st order.")
+                field_data = field_data[:, 0:8]
+                indices = indices[:, 0:8]
+
+            arc_length, plot_values = pixelize_element_mesh_line(coords, indices,
+                                                                 start_point,
+                                                                 end_point,
+                                                                 npoints, field_data,
+                                                                 index_offset=offset)
+            arc_length = YTArray(arc_length, start_point.units)
+            plot_values = YTArray(plot_values, field_data.units)
+        else:
+            ray = self.ds.ray(start_point, end_point)
+            arc_length, plot_values = _sample_ray(ray, npoints, field)
+        return arc_length, plot_values
+
+
+
     def _ortho_pixelize(self, data_source, field, bounds, size, antialias,
                         dim, periodic):
         # We should be using fcoords
@@ -129,13 +221,13 @@
             period = period.in_units("code_length").d
 
         buff = np.zeros((size[1], size[0]), dtype="f8")
-        
+
         finfo = self.ds._get_field_info(field)
         nodal_flag = finfo.nodal_flag
         if np.any(nodal_flag):
             nodal_data = get_nodal_data(data_source, field)
             coord = data_source.coord.d
-            pixelize_cartesian_nodal(buff, 
+            pixelize_cartesian_nodal(buff,
                                      data_source['px'], data_source['py'], data_source['pz'],
                                      data_source['pdx'], data_source['pdy'], data_source['pdz'],
                                      nodal_data, coord, bounds, int(antialias),
@@ -147,7 +239,7 @@
                                bounds, int(antialias),
                                period, int(periodic))
         return buff
-            
+
     def _oblique_pixelize(self, data_source, field, bounds, size, antialias):
         indices = np.argsort(data_source['pdx'])[::-1].astype(np.int_)
         buff = np.zeros((size[1], size[0]), dtype="f8")

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 yt/geometry/coordinates/coordinate_handler.py
--- a/yt/geometry/coordinates/coordinate_handler.py
+++ b/yt/geometry/coordinates/coordinate_handler.py
@@ -72,7 +72,7 @@
 
 class CoordinateHandler(object):
     name = None
-    
+
     def __init__(self, ds, ordering):
         self.ds = weakref.proxy(ds)
         self.axis_order = ordering
@@ -86,6 +86,9 @@
         # pixelizer
         raise NotImplementedError
 
+    def pixelize_line(self, field, start_point, end_point, npoints):
+        raise NotImplementedError
+
     def distance(self, start, end):
         p1 = self.convert_to_cartesian(start)
         p2 = self.convert_to_cartesian(end)
@@ -265,4 +268,3 @@
     c2[...,1] = np.sin(coord[...,0]) * coord[...,1] + center[1]
     c2[...,2] = coord[...,2]
     return c2
-

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 yt/utilities/lib/fixed_interpolator.pxd
--- a/yt/utilities/lib/fixed_interpolator.pxd
+++ b/yt/utilities/lib/fixed_interpolator.pxd
@@ -30,4 +30,3 @@
                        np.float64_t vl[3], np.float64_t dds[3],
                        np.float64_t x, np.float64_t y, np.float64_t z,
                        int vind1, int vind2) nogil
-

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -119,7 +119,7 @@
     # (lr) and then iterate up to "right column" (rc) and "uppeR row" (rr),
     # depositing into them the data value.  Overlap computes the relative
     # overlap of a data value with a pixel.
-    # 
+    #
     # NOTE ON ROWS AND COLUMNS:
     #
     #   The way that images are plotting in matplotlib is somewhat different
@@ -254,7 +254,7 @@
     cdef int lc, lr, rc, rr
     cdef np.float64_t lypx, rypx, lxpx, rxpx, overlap1, overlap2
     # These are the temp vars we get from the arrays
-    cdef np.float64_t oxsp, oysp, ozsp 
+    cdef np.float64_t oxsp, oysp, ozsp
     cdef np.float64_t xsp, ysp, zsp
     cdef np.float64_t dxsp, dysp, dzsp
     # Some periodicity helpers
@@ -303,7 +303,7 @@
     # (lr) and then iterate up to "right column" (rc) and "uppeR row" (rr),
     # depositing into them the data value.  Overlap computes the relative
     # overlap of a data value with a pixel.
-    # 
+    #
     # NOTE ON ROWS AND COLUMNS:
     #
     #   The way that images are plotting in matplotlib is somewhat different
@@ -497,7 +497,7 @@
     cdef np.float64_t r_i, theta_i, dr_i, dtheta_i, dthetamin
     cdef np.float64_t costheta, sintheta
     cdef int i, pi, pj
-    
+
     cdef int imax = np.asarray(radius).argmax()
     rmax = radius[imax] + dradius[imax]
 
@@ -862,3 +862,107 @@
     free(vertices)
     free(field_vals)
     return img
+
+def pixelize_element_mesh_line(np.ndarray[np.float64_t, ndim=2] coords,
+                               np.ndarray[np.int64_t, ndim=2] conn,
+                               np.ndarray[np.float64_t, ndim=1] start_point,
+                               np.ndarray[np.float64_t, ndim=1] end_point,
+                               npoints,
+                               np.ndarray[np.float64_t, ndim=2] field,
+                               int index_offset = 0):
+
+    # This routine chooses the correct element sampler to interpolate field
+    # values at evenly spaced points along a sampling line
+    cdef np.float64_t *vertices
+    cdef np.float64_t *field_vals
+    cdef int nvertices = conn.shape[1]
+    cdef int ndim = coords.shape[1]
+    cdef int num_field_vals = field.shape[1]
+    cdef int num_plot_nodes = npoints
+    cdef int num_intervals = npoints - 1
+    cdef double[4] mapped_coord
+    cdef ElementSampler sampler
+    cdef np.ndarray[np.float64_t, ndim=1] lin_vec
+    cdef np.ndarray[np.float64_t, ndim=1] lin_inc
+    cdef np.ndarray[np.float64_t, ndim=2] lin_sample_points
+    cdef np.int64_t i, n, j, k
+    cdef np.ndarray[np.float64_t, ndim=1] arc_length
+    cdef np.float64_t lin_length, inc_length
+    cdef np.ndarray[np.float64_t, ndim=1] plot_values
+    cdef np.float64_t sample_point[3]
+
+    lin_vec = np.zeros(ndim, dtype="float64")
+    lin_inc = np.zeros(ndim, dtype="float64")
+
+    lin_sample_points = np.zeros((num_plot_nodes, ndim), dtype="float64")
+    arc_length = np.zeros(num_plot_nodes, dtype="float64")
+    plot_values = np.zeros(num_plot_nodes, dtype="float64")
+
+    # Pick the right sampler and allocate storage for the mapped coordinate
+    if ndim == 3 and nvertices == 4:
+        sampler = P1Sampler3D()
+    elif ndim == 3 and nvertices == 6:
+        sampler = W1Sampler3D()
+    elif ndim == 3 and nvertices == 8:
+        sampler = Q1Sampler3D()
+    elif ndim == 3 and nvertices == 20:
+        sampler = S2Sampler3D()
+    elif ndim == 2 and nvertices == 3:
+        sampler = P1Sampler2D()
+    elif ndim == 1 and nvertices == 2:
+        sampler = P1Sampler1D()
+    elif ndim == 2 and nvertices == 4:
+        sampler = Q1Sampler2D()
+    elif ndim == 2 and nvertices == 9:
+        sampler = Q2Sampler2D()
+    elif ndim == 2 and nvertices == 6:
+        sampler = T2Sampler2D()
+    elif ndim == 3 and nvertices == 10:
+        sampler = Tet2Sampler3D()
+    else:
+        raise YTElementTypeNotRecognized(ndim, nvertices)
+
+    # allocate temporary storage
+    vertices = <np.float64_t *> malloc(ndim * sizeof(np.float64_t) * nvertices)
+    field_vals = <np.float64_t *> malloc(sizeof(np.float64_t) * num_field_vals)
+
+    lin_vec = end_point - start_point
+    lin_length = np.linalg.norm(lin_vec)
+    lin_inc = lin_vec / num_intervals
+    inc_length = lin_length / num_intervals
+    for j in range(ndim):
+        lin_sample_points[0, j] = start_point[j]
+    arc_length[0] = 0
+    for i in range(1, num_intervals + 1):
+        for j in range(ndim):
+            lin_sample_points[i, j] = lin_sample_points[i-1, j] + lin_inc[j]
+            arc_length[i] = arc_length[i-1] + inc_length
+
+    for i in range(num_intervals + 1):
+        for j in range(3):
+            if j < ndim:
+                sample_point[j] = lin_sample_points[i][j]
+            else:
+                sample_point[j] = 0
+        for ci in range(conn.shape[0]):
+            for n in range(num_field_vals):
+                field_vals[n] = field[ci, n]
+
+            # Fill the vertices
+            for n in range(nvertices):
+                cj = conn[ci, n] - index_offset
+                for k in range(ndim):
+                    vertices[ndim*n + k] = coords[cj, k]
+
+            sampler.map_real_to_unit(mapped_coord, vertices, sample_point)
+            if not sampler.check_inside(mapped_coord) and ci != conn.shape[0] - 1:
+                continue
+            elif not sampler.check_inside(mapped_coord):
+                raise ValueError("Check to see that both starting and ending line points "
+                                 "are within the domain of the mesh.")
+            plot_values[i] = sampler.sample_at_unit_point(mapped_coord, field_vals)
+            break
+
+    free(vertices)
+    free(field_vals)
+    return arc_length, plot_values

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 yt/visualization/api.py
--- a/yt/visualization/api.py
+++ b/yt/visualization/api.py
@@ -53,6 +53,9 @@
     ProjectionPlot, \
     OffAxisProjectionPlot
 
+from .line_plot import \
+    LinePlot
+
 from .profile_plotter import \
     ProfilePlot, \
     PhasePlot

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 yt/visualization/base_plot_types.py
--- a/yt/visualization/base_plot_types.py
+++ b/yt/visualization/base_plot_types.py
@@ -50,7 +50,7 @@
 
 
 class CallbackWrapper(object):
-    def __init__(self, viewer, window_plot, frb, field, font_properties, 
+    def __init__(self, viewer, window_plot, frb, field, font_properties,
                  font_color):
         self.frb = frb
         self.data = frb.data_source
@@ -86,6 +86,8 @@
         import matplotlib.figure
         self._plot_valid = True
         if figure is None:
+            if not iterable(fsize):
+                fsize = (fsize, fsize)
             self.figure = matplotlib.figure.Figure(figsize=fsize, frameon=True)
         else:
             figure.set_size_inches(fsize)
@@ -164,6 +166,8 @@
     def _get_labels(self):
         ax = self.axes
         labels = ax.xaxis.get_ticklabels() + ax.yaxis.get_ticklabels()
+        labels += ax.xaxis.get_minorticklabels()
+        labels += ax.yaxis.get_minorticklabels()
         labels += [ax.title, ax.xaxis.label, ax.yaxis.label,
                    ax.xaxis.get_offset_text(), ax.yaxis.get_offset_text()]
         return labels
@@ -279,10 +283,10 @@
         x_frac_widths = xbins/size[0]
         y_frac_widths = ybins/size[1]
 
-        # axrect is the rectangle defining the area of the 
-        # axis object of the plot.  Its range goes from 0 to 1 in 
-        # x and y directions.  The first two values are the x,y 
-        # start values of the axis object (lower left corner), and the 
+        # axrect is the rectangle defining the area of the
+        # axis object of the plot.  Its range goes from 0 to 1 in
+        # x and y directions.  The first two values are the x,y
+        # start values of the axis object (lower left corner), and the
         # second two values are the size of the axis object.  To get
         # the upper right corner, add the first x,y to the second x,y.
         axrect = (
@@ -452,5 +456,3 @@
             ax.clear()
             cbars.append(ax)
     return fig, tr, cbars
-
-

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 yt/visualization/fixed_resolution.py
--- a/yt/visualization/fixed_resolution.py
+++ b/yt/visualization/fixed_resolution.py
@@ -30,7 +30,6 @@
 
 import numpy as np
 import weakref
-import re
 import types
 
 class FixedResolutionBuffer(object):
@@ -155,38 +154,6 @@
             if f not in exclude and f[0] not in self.data_source.ds.particle_types:
                 self[f]
 
-    def _is_ion( self, fname ):
-        p = re.compile("_p[0-9]+_")
-        result = False
-        if p.search( fname ) is not None:
-            result = True
-        return result
-
-    def _ion_to_label( self, fname ):
-        pnum2rom = {
-            "0":"I", "1":"II", "2":"III", "3":"IV", "4":"V",
-            "5":"VI", "6":"VII", "7":"VIII", "8":"IX", "9":"X",
-            "10":"XI", "11":"XII", "12":"XIII", "13":"XIV", "14":"XV",
-            "15":"XVI", "16":"XVII", "17":"XVIII", "18":"XIX", "19":"XX"}
-
-        p = re.compile("_p[0-9]+_")
-        m = p.search( fname )
-        if m is not None:
-            pstr = m.string[m.start()+1:m.end()-1]
-            segments = fname.split("_")
-            for i,s in enumerate(segments):
-                segments[i] = s.capitalize()
-                if s == pstr:
-                    ipstr = i
-            element = segments[ipstr-1]
-            roman = pnum2rom[pstr[1:]]
-            label = element + '\ ' + roman + '\ ' + \
-                '\ '.join(segments[ipstr+1:])
-        else:
-            label = fname
-        return label
-
-
     def _get_info(self, item):
         info = {}
         ftype, fname = field = self.data_source._determine_fields(item)[0]
@@ -210,18 +177,7 @@
         except AttributeError:
             pass
 
-        info['label'] = finfo.display_name
-        if info['label'] is None:
-            if self._is_ion( fname ):
-                fname = self._ion_to_label( fname )
-                info['label'] = r'$\rm{'+fname+r'}$'
-                info['label'] = r'$\rm{'+fname.replace('_','\ ')+r'}$'
-            else:
-                info['label'] = r'$\rm{'+fname+r'}$'
-                info['label'] = r'$\rm{'+fname.replace('_','\ ').title()+r'}$'
-        elif info['label'].find('$') == -1:
-            info['label'] = info['label'].replace(' ','\ ')
-            info['label'] = r'$\rm{'+info['label']+r'}$'
+        info['label'] = finfo.get_latex_display_name()
 
         return info
 

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 yt/visualization/line_plot.py
--- /dev/null
+++ b/yt/visualization/line_plot.py
@@ -0,0 +1,304 @@
+"""
+A mechanism for plotting field values along a line through a dataset
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2017, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+import numpy as np
+
+from collections import defaultdict
+from yt.funcs import \
+    iterable
+from yt.units.unit_object import \
+    Unit
+from yt.units.yt_array import \
+    YTArray
+from yt.visualization.base_plot_types import \
+    PlotMPL
+from yt.visualization.plot_container import \
+    PlotContainer, \
+    PlotDictionary, \
+    log_transform, \
+    linear_transform, \
+    invalidate_plot
+
+class LinePlotDictionary(PlotDictionary):
+    def __init__(self, data_source):
+        super(LinePlotDictionary, self).__init__(data_source)
+        self.known_dimensions = {}
+
+    def _sanitize_dimensions(self, item):
+        field = self.data_source._determine_fields(item)[0]
+        finfo = self.data_source.ds.field_info[field]
+        dimensions = Unit(
+            finfo.units, registry=self.data_source.ds.unit_registry).dimensions
+        if dimensions not in self.known_dimensions:
+            self.known_dimensions[dimensions] = item
+            ret_item = item
+        else:
+            ret_item = self.known_dimensions[dimensions]
+        return ret_item
+
+    def __getitem__(self, item):
+        ret_item = self._sanitize_dimensions(item)
+        return super(LinePlotDictionary, self).__getitem__(ret_item)
+
+    def __setitem__(self, item, value):
+        ret_item = self._sanitize_dimensions(item)
+        super(LinePlotDictionary, self).__setitem__(ret_item, value)
+
+    def __contains__(self, item):
+        ret_item = self._sanitize_dimensions(item)
+        return super(LinePlotDictionary, self).__contains__(ret_item)
+
+class LinePlot(PlotContainer):
+    r"""
+    A class for constructing line plots
+
+    Parameters
+    ----------
+
+    ds : :class:`yt.data_objects.static_output.Dataset`
+        This is the dataset object corresponding to the
+        simulation output to be plotted.
+    fields : string / tuple, or list of strings / tuples
+        The name(s) of the field(s) to be plotted.
+    start_point : n-element list, tuple, ndarray, or YTArray
+        Contains the coordinates of the first point for constructing the line.
+        Must contain n elements where n is the dimensionality of the dataset.
+    end_point : n-element list, tuple, ndarray, or YTArray
+        Contains the coordinates of the first point for constructing the line.
+        Must contain n elements where n is the dimensionality of the dataset.
+    npoints : int
+        How many points to sample between start_point and end_point for
+        constructing the line plot
+    figure_size : int or two-element iterable of ints
+        Size in inches of the image.
+        Default: 5 (5x5)
+    fontsize : int
+        Font size for all text in the plot.
+        Default: 14
+    labels : dictionary
+        Keys should be the field names. Values should be latex-formattable
+        strings used in the LinePlot legend
+        Default: None
+
+
+    Example
+    -------
+
+    >>> import yt
+    >>>
+    >>> ds = yt.load('IsolatedGalaxy/galaxy0030/galaxy0030')
+    >>>
+    >>> plot = yt.LinePlot(ds, 'density', [0, 0, 0], [1, 1, 1], 512)
+    >>> plot.add_legend('density')
+    >>> plot.set_x_unit('cm')
+    >>> plot.set_unit('density', 'kg/cm**3')
+    >>> plot.save()
+
+    """
+    _plot_type = 'line_plot'
+
+    def __init__(self, ds, fields, start_point, end_point, npoints,
+                 figure_size=5., fontsize=14., labels=None):
+        """
+        Sets up figure and axes
+        """
+        self.start_point = _validate_point(start_point, ds, start=True)
+        self.end_point = _validate_point(end_point, ds)
+        self.npoints = npoints
+        self._x_unit = None
+        self._y_units = {}
+        self._titles = {}
+
+        data_source = ds.all_data()
+
+        self.fields = data_source._determine_fields(fields)
+        self.plots = LinePlotDictionary(data_source)
+        self.include_legend = defaultdict(bool)
+        if labels is None:
+            self.labels = {}
+        else:
+            self.labels = labels
+
+        super(LinePlot, self).__init__(data_source, figure_size, fontsize)
+
+        for f in self.fields:
+            if f not in self.labels:
+                self.labels[f] = f[1]
+            finfo = self.data_source.ds._get_field_info(*f)
+            if finfo.take_log:
+                self._field_transform[f] = log_transform
+            else:
+                self._field_transform[f] = linear_transform
+
+        self._setup_plots()
+
+    @invalidate_plot
+    def add_legend(self, field):
+        """Adds a legend to the `LinePlot` instance"""
+        self.include_legend[field] = True
+
+    def _setup_plots(self):
+        if self._plot_valid is True:
+            return
+        for plot in self.plots.values():
+            plot.axes.cla()
+        dimensions_counter = defaultdict(int)
+        for field in self.fields:
+            fontscale = self._font_properties._size / 14.
+            top_buff_size = 0.35*fontscale
+
+            x_axis_size = 1.35*fontscale
+            y_axis_size = 0.7*fontscale
+            right_buff_size = 0.2*fontscale
+
+            if iterable(self.figure_size):
+                figure_size = self.figure_size
+            else:
+                figure_size = (self.figure_size, self.figure_size)
+
+            xbins = np.array([x_axis_size, figure_size[0],
+                              right_buff_size])
+            ybins = np.array([y_axis_size, figure_size[1], top_buff_size])
+
+            size = [xbins.sum(), ybins.sum()]
+
+            x_frac_widths = xbins/size[0]
+            y_frac_widths = ybins/size[1]
+
+            axrect = (
+                x_frac_widths[0],
+                y_frac_widths[0],
+                x_frac_widths[1],
+                y_frac_widths[1],
+            )
+
+            try:
+                plot = self.plots[field]
+            except KeyError:
+                plot = PlotMPL(self.figure_size, axrect, None, None)
+                self.plots[field] = plot
+
+            x, y = self.ds.coordinates.pixelize_line(
+                field, self.start_point, self.end_point, self.npoints)
+
+            if self._x_unit is None:
+                unit_x = x.units
+            else:
+                unit_x = self._x_unit
+
+            if field in self._y_units:
+                unit_y = self._y_units[field]
+            else:
+                unit_y = y.units
+
+            x = x.to(unit_x)
+            y = y.to(unit_y)
+
+            plot.axes.plot(x, y, label=self.labels[field])
+
+            if self._field_transform[field] != linear_transform:
+                if (y < 0).any():
+                    plot.axes.set_yscale('symlog')
+                else:
+                    plot.axes.set_yscale('log')
+
+            plot._set_font_properties(self._font_properties, None)
+
+            axes_unit_labels = self._get_axes_unit_labels(unit_x, unit_y)
+
+            finfo = self.ds.field_info[field]
+
+            x_label = r'$\rm{Path\ Length' + axes_unit_labels[0]+'}$'
+
+            finfo = self.ds.field_info[field]
+            dimensions = Unit(finfo.units,
+                              registry=self.ds.unit_registry).dimensions
+            dimensions_counter[dimensions] += 1
+            if dimensions_counter[dimensions] > 1:
+                y_label = (r'$\rm{Multiple\ Fields}$' + r'$\rm{' +
+                           axes_unit_labels[1]+'}$')
+            else:
+                y_label = (finfo.get_latex_display_name() + r'$\rm{' +
+                           axes_unit_labels[1]+'}$')
+
+            plot.axes.set_xlabel(x_label)
+            plot.axes.set_ylabel(y_label)
+
+            if field in self._titles:
+                plot.axes.set_title(self._titles[field])
+
+            if self.include_legend[field]:
+                plot.axes.legend()
+
+    @invalidate_plot
+    def set_x_unit(self, unit_name):
+        """Set the unit to use along the x-axis
+
+        Parameters
+        ----------
+        unit_name: str
+          The name of the unit to use for the x-axis unit
+        """
+        self._x_unit = unit_name
+
+    @invalidate_plot
+    def set_unit(self, field, unit_name):
+        """Set the unit used to plot the field
+
+        Parameters
+        ----------
+        field: str or field tuple
+           The name of the field to set the units for
+        unit_name: str
+           The name of the unit to use for this field
+        """
+        self._y_units[self.data_source._determine_fields(field)[0]] = unit_name
+
+    @invalidate_plot
+    def annotate_title(self, field, title):
+        """Set the unit used to plot the field
+
+        Parameters
+        ----------
+        field: str or field tuple
+           The name of the field to set the units for
+        title: str
+           The title to use for the plot
+        """
+        self._titles[self.data_source._determine_fields(field)[0]] = title
+
+def _validate_point(point, ds, start=False):
+    if not iterable(point):
+        raise RuntimeError(
+            "Input point must be array-like"
+        )
+    if not isinstance(point, YTArray):
+        point = ds.arr(point, 'code_length')
+    if len(point.shape) != 1:
+        raise RuntimeError(
+            "Input point must be a 1D array"
+        )
+    if point.shape[0] < ds.dimensionality:
+        raise RuntimeError(
+            "Input point must have an element for each dimension"
+        )
+    # need to pad to 3D elements to avoid issues later
+    if point.shape[0] < 3:
+        if start:
+            val = 0
+        else:
+            val = 1
+        point = np.append(point.d, [val]*(3-ds.dimensionality))*point.uq
+    return point

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 yt/visualization/plot_container.py
--- a/yt/visualization/plot_container.py
+++ b/yt/visualization/plot_container.py
@@ -30,11 +30,19 @@
 
 from yt.config import \
     ytcfg
+from yt.data_objects.time_series import \
+    DatasetSeries
 from yt.funcs import \
     get_image_suffix, \
     iterable, \
     ensure_dir, \
     ensure_list
+from yt.units.unit_lookup_table import \
+    prefixable_units, latex_prefixes
+from yt.units.unit_object import \
+    Unit
+from yt.utilities.definitions import \
+    formatted_length_unit_names
 from yt.utilities.exceptions import \
     YTNotInsideNotebook
 from yt.visualization.color_maps import \
@@ -178,36 +186,27 @@
         self.data_source = data_source
         return defaultdict.__init__(self, default_factory)
 
-class ImagePlotContainer(object):
-    """A container for plots with colorbars.
-
-    """
+class PlotContainer(object):
+    """A container for generic plots"""
     _plot_type = None
     _plot_valid = False
-    _colorbar_valid = False
 
     def __init__(self, data_source, figure_size, fontsize):
         from matplotlib.font_manager import FontProperties
-
         self.data_source = data_source
+        self.ds = data_source.ds
+        self.ts = self._initialize_dataset(self.ds)
         if iterable(figure_size):
             self.figure_size = float(figure_size[0]), float(figure_size[1])
         else:
             self.figure_size = float(figure_size)
-        self.plots = PlotDictionary(data_source)
-        self._callbacks = []
-        self._field_transform = {}
-        self._colormaps = defaultdict(
-            lambda: ytcfg.get("yt", "default_colormap"))
         font_path = matplotlib.get_data_path() + '/fonts/ttf/STIXGeneral.ttf'
         self._font_properties = FontProperties(size=fontsize, fname=font_path)
         self._font_color = None
         self._xlabel = None
         self._ylabel = None
         self._minorticks = {}
-        self._cbar_minorticks = {}
-        self._colorbar_label = PlotDictionary(
-            self.data_source, lambda: None)
+        self._field_transform = {}
 
     @invalidate_plot
     def set_log(self, field, log, linthresh=None):
@@ -230,10 +229,10 @@
         for field in self.data_source._determine_fields(fields):
             if log:
                 if linthresh is not None:
-                    if not linthresh > 0.: 
+                    if not linthresh > 0.:
                         raise ValueError('\"linthresh\" must be positive')
                     self._field_transform[field] = symlog_transform
-                    self._field_transform[field].func = linthresh 
+                    self._field_transform[field].func = linthresh
                 else:
                     self._field_transform[field] = log_transform
             else:
@@ -270,109 +269,6 @@
         return self
 
     @invalidate_plot
-    def set_cmap(self, field, cmap):
-        """set the colormap for one of the fields
-
-        Parameters
-        ----------
-        field : string
-            the field to set the colormap
-            if field == 'all', applies to all plots.
-        cmap : string or tuple
-            If a string, will be interpreted as name of the colormap.
-            If a tuple, it is assumed to be of the form (name, type, number)
-            to be used for palettable functionality. (name, type, number, bool)
-            can be used to specify if a reverse colormap is to be used.
-
-        """
-
-        if field == 'all':
-            fields = list(self.plots.keys())
-        else:
-            fields = [field]
-        for field in self.data_source._determine_fields(fields):
-            self._colorbar_valid = False
-            self._colormaps[field] = cmap
-        return self
-
-    @invalidate_plot
-    def set_background_color(self, field, color=None):
-        """set the background color to match provided color
-
-        Parameters
-        ----------
-        field : string
-            the field to set the colormap
-            if field == 'all', applies to all plots.
-        color : string or RGBA tuple (optional)
-            if set, set the background color to this color
-            if unset, background color is set to the bottom value of 
-            the color map
-
-        """
-        actual_field = self.data_source._determine_fields(field)[0]
-        if color is None:
-            cmap = self._colormaps[actual_field]
-            if isinstance(cmap, string_types):
-                try:
-                    cmap = yt_colormaps[cmap]
-                except KeyError:
-                    cmap = getattr(matplotlib.cm, cmap)
-            color = cmap(0)
-        if LooseVersion(matplotlib.__version__) < LooseVersion("2.0.0"):
-            self.plots[actual_field].axes.set_axis_bgcolor(color)
-        else:
-            self.plots[actual_field].axes.set_facecolor(color)
-        return self
-
-    @invalidate_plot
-    def set_zlim(self, field, zmin, zmax, dynamic_range=None):
-        """set the scale of the colormap
-
-        Parameters
-        ----------
-        field : string
-            the field to set a colormap scale
-            if field == 'all', applies to all plots.
-        zmin : float
-            the new minimum of the colormap scale. If 'min', will
-            set to the minimum value in the current view.
-        zmax : float
-            the new maximum of the colormap scale. If 'max', will
-            set to the maximum value in the current view.
-
-        Other Parameters
-        ----------------
-        dynamic_range : float (default: None)
-            The dynamic range of the image.
-            If zmin == None, will set zmin = zmax / dynamic_range
-            If zmax == None, will set zmax = zmin * dynamic_range
-            When dynamic_range is specified, defaults to setting
-            zmin = zmax / dynamic_range.
-
-        """
-        if field is 'all':
-            fields = list(self.plots.keys())
-        else:
-            fields = ensure_list(field)
-        for field in self.data_source._determine_fields(fields):
-            myzmin = zmin
-            myzmax = zmax
-            if zmin == 'min':
-                myzmin = self.plots[field].image._A.min()
-            if zmax == 'max':
-                myzmax = self.plots[field].image._A.max()
-            if dynamic_range is not None:
-                if zmax is None:
-                    myzmax = myzmin * dynamic_range
-                else:
-                    myzmin = myzmax / dynamic_range
-
-            self.plots[field].zmin = myzmin
-            self.plots[field].zmax = myzmax
-        return self
-
-    @invalidate_plot
     def set_minorticks(self, field, state):
         """turn minor ticks on or off in the current plot
 
@@ -398,36 +294,16 @@
                 self._minorticks[field] = False
         return self
 
-    @invalidate_plot
-    def set_cbar_minorticks(self, field, state):
-        """turn colorbar minor ticks on or off in the current plot
-
-        Displaying minor ticks reduces performance; turn them off 
-        using set_cbar_minorticks('all', 'off') if drawing speed is a problem.
-
-        Parameters
-        ----------
-        field : string
-            the field to remove colorbar minorticks
-        state : string
-            the state indicating 'on' or 'off'
-
-        """
-        if field == 'all':
-            fields = list(self.plots.keys())
-        else:
-            fields = [field]
-        for field in self.data_source._determine_fields(fields):
-            if state == 'on':
-                self._cbar_minorticks[field] = True
-            else:
-                self._cbar_minorticks[field] = False
-        return self
-
     def _setup_plots(self):
         # Left blank to be overriden in subclasses
         pass
 
+    def _initialize_dataset(self, ts):
+        if not isinstance(ts, DatasetSeries):
+            if not iterable(ts): ts = [ts]
+            ts = DatasetSeries(ts)
+        return ts
+
     def _switch_ds(self, new_ds, data_source=None):
         old_object = self.data_source
         name = old_object._type_name
@@ -449,6 +325,8 @@
                 lim = getattr(self, lim_name)
                 lim = tuple(new_ds.quan(l.value, str(l.units)) for l in lim)
                 setattr(self, lim_name, lim)
+        self.plots.data_source = new_object
+        self._colorbar_label.data_source = new_object
         self._setup_plots()
 
     @validate_plot
@@ -471,7 +349,7 @@
         ----------
 
         font_dict : dict
-            A dict of keyword parameters to be passed to 
+            A dict of keyword parameters to be passed to
             :class:`matplotlib.font_manager.FontProperties`.
 
             Possible keys include:
@@ -585,8 +463,11 @@
                 for k, v in iteritems(self.plots):
                     names.append(v.save(name, mpl_kwargs))
                 return names
-        axis = self.ds.coordinates.axis_name.get(
-            self.data_source.axis, '')
+        if hasattr(self.data_source, 'axis'):
+            axis = self.ds.coordinates.axis_name.get(
+                self.data_source.axis, '')
+        else:
+            axis = None
         weight = None
         type = self._plot_type
         if type in ['Projection', 'OffAxisProjection']:
@@ -616,21 +497,6 @@
         return self
 
     @validate_plot
-    def _send_zmq(self):
-        from ._mpl_imports import FigureCanvasAgg
-        try:
-            # pre-IPython v1.0
-            from IPython.zmq.pylab.backend_inline import send_figure as display
-        except ImportError:
-            # IPython v1.0+
-            from IPython.core.display import display
-        for k, v in sorted(iteritems(self.plots)):
-            # Due to a quirk in the matplotlib API, we need to create
-            # a dummy canvas variable here that is never used.
-            canvas = FigureCanvasAgg(v.figure)  # NOQA
-            display(v.figure)
-
-    @validate_plot
     def show(self):
         r"""This will send any existing plots to the IPython notebook.
 
@@ -657,6 +523,8 @@
             if "__IPYTHON__" in dir(builtins):
                 from IPython.display import display
                 display(self)
+            else:
+                raise YTNotInsideNotebook
 
     @validate_plot
     def display(self, name=None, mpl_kwargs=None):
@@ -713,6 +581,211 @@
         self._ylabel = label
         return self
 
+    def _get_axes_unit_labels(self, unit_x, unit_y):
+        axes_unit_labels = ['', '']
+        comoving = False
+        hinv = False
+        for i, un in enumerate((unit_x, unit_y)):
+            unn = None
+            if hasattr(self.data_source, 'axis'):
+                if hasattr(self.ds.coordinates, "image_units"):
+                    # This *forces* an override
+                    unn = self.ds.coordinates.image_units[
+                        self.data_source.axis][i]
+                elif hasattr(self.ds.coordinates, "default_unit_label"):
+                    axax = getattr(self.ds.coordinates,
+                                   "%s_axis" % ("xy"[i]))[self.data_source.axis]
+                    unn = self.ds.coordinates.default_unit_label.get(
+                        axax, None)
+            if unn is not None:
+                axes_unit_labels[i] = r'\ \ \left('+unn+r'\right)'
+                continue
+            # Use sympy to factor h out of the unit.  In this context 'un'
+            # is a string, so we call the Unit constructor.
+            expr = Unit(un, registry=self.ds.unit_registry).expr
+            h_expr = Unit('h', registry=self.ds.unit_registry).expr
+            # See http://docs.sympy.org/latest/modules/core.html#sympy.core.expr.Expr
+            h_power = expr.as_coeff_exponent(h_expr)[1]
+            # un is now the original unit, but with h factored out.
+            un = str(expr*h_expr**(-1*h_power))
+            un_unit = Unit(un, registry=self.ds.unit_registry)
+            cm = Unit('cm').expr
+            if str(un).endswith('cm') and cm not in un_unit.expr.atoms():
+                comoving = True
+                un = un[:-2]
+            # no length units besides code_length end in h so this is safe
+            if h_power == -1:
+                hinv = True
+            elif h_power != 0:
+                # It doesn't make sense to scale a position by anything
+                # other than h**-1
+                raise RuntimeError
+            if un not in ['1', 'u', 'unitary']:
+                if un in formatted_length_unit_names:
+                    un = formatted_length_unit_names[un]
+                else:
+                    un = Unit(un, registry=self.ds.unit_registry)
+                    un = un.latex_representation()
+                    if hinv:
+                        un = un + '\,h^{-1}'
+                    if comoving:
+                        un = un + '\,(1+z)^{-1}'
+                    pp = un[0]
+                    if pp in latex_prefixes:
+                        symbol_wo_prefix = un[1:]
+                        if symbol_wo_prefix in prefixable_units:
+                            un = un.replace(
+                                pp, "{"+latex_prefixes[pp]+"}", 1)
+                axes_unit_labels[i] = '\ \ ('+un+')'
+        return axes_unit_labels
+
+
+class ImagePlotContainer(PlotContainer):
+    """A container for plots with colorbars.
+
+    """
+    _colorbar_valid = False
+
+    def __init__(self, data_source, figure_size, fontsize):
+        super(ImagePlotContainer, self).__init__(
+            data_source, figure_size, fontsize)
+        self.plots = PlotDictionary(data_source)
+        self._callbacks = []
+        self._colormaps = defaultdict(
+            lambda: ytcfg.get("yt", "default_colormap"))
+        self._cbar_minorticks = {}
+        self._colorbar_label = PlotDictionary(
+            self.data_source, lambda: None)
+
+    @invalidate_plot
+    def set_cmap(self, field, cmap):
+        """set the colormap for one of the fields
+
+        Parameters
+        ----------
+        field : string
+            the field to set the colormap
+            if field == 'all', applies to all plots.
+        cmap : string or tuple
+            If a string, will be interpreted as name of the colormap.
+            If a tuple, it is assumed to be of the form (name, type, number)
+            to be used for palettable functionality. (name, type, number, bool)
+            can be used to specify if a reverse colormap is to be used.
+
+        """
+
+        if field == 'all':
+            fields = list(self.plots.keys())
+        else:
+            fields = [field]
+        for field in self.data_source._determine_fields(fields):
+            self._colorbar_valid = False
+            self._colormaps[field] = cmap
+        return self
+
+    @invalidate_plot
+    def set_background_color(self, field, color=None):
+        """set the background color to match provided color
+
+        Parameters
+        ----------
+        field : string
+            the field to set the colormap
+            if field == 'all', applies to all plots.
+        color : string or RGBA tuple (optional)
+            if set, set the background color to this color
+            if unset, background color is set to the bottom value of
+            the color map
+
+        """
+        actual_field = self.data_source._determine_fields(field)[0]
+        if color is None:
+            cmap = self._colormaps[actual_field]
+            if isinstance(cmap, string_types):
+                try:
+                    cmap = yt_colormaps[cmap]
+                except KeyError:
+                    cmap = getattr(matplotlib.cm, cmap)
+            color = cmap(0)
+        if LooseVersion(matplotlib.__version__) < LooseVersion("2.0.0"):
+            self.plots[actual_field].axes.set_axis_bgcolor(color)
+        else:
+            self.plots[actual_field].axes.set_facecolor(color)
+        return self
+
+    @invalidate_plot
+    def set_zlim(self, field, zmin, zmax, dynamic_range=None):
+        """set the scale of the colormap
+
+        Parameters
+        ----------
+        field : string
+            the field to set a colormap scale
+            if field == 'all', applies to all plots.
+        zmin : float
+            the new minimum of the colormap scale. If 'min', will
+            set to the minimum value in the current view.
+        zmax : float
+            the new maximum of the colormap scale. If 'max', will
+            set to the maximum value in the current view.
+
+        Other Parameters
+        ----------------
+        dynamic_range : float (default: None)
+            The dynamic range of the image.
+            If zmin == None, will set zmin = zmax / dynamic_range
+            If zmax == None, will set zmax = zmin * dynamic_range
+            When dynamic_range is specified, defaults to setting
+            zmin = zmax / dynamic_range.
+
+        """
+        if field is 'all':
+            fields = list(self.plots.keys())
+        else:
+            fields = ensure_list(field)
+        for field in self.data_source._determine_fields(fields):
+            myzmin = zmin
+            myzmax = zmax
+            if zmin == 'min':
+                myzmin = self.plots[field].image._A.min()
+            if zmax == 'max':
+                myzmax = self.plots[field].image._A.max()
+            if dynamic_range is not None:
+                if zmax is None:
+                    myzmax = myzmin * dynamic_range
+                else:
+                    myzmin = myzmax / dynamic_range
+
+            self.plots[field].zmin = myzmin
+            self.plots[field].zmax = myzmax
+        return self
+
+    @invalidate_plot
+    def set_cbar_minorticks(self, field, state):
+        """turn colorbar minor ticks on or off in the current plot
+
+        Displaying minor ticks reduces performance; turn them off
+        using set_cbar_minorticks('all', 'off') if drawing speed is a problem.
+
+        Parameters
+        ----------
+        field : string
+            the field to remove colorbar minorticks
+        state : string
+            the state indicating 'on' or 'off'
+
+        """
+        if field == 'all':
+            fields = list(self.plots.keys())
+        else:
+            fields = [field]
+        for field in self.data_source._determine_fields(fields):
+            if state == 'on':
+                self._cbar_minorticks[field] = True
+            else:
+                self._cbar_minorticks[field] = False
+        return self
+
     @invalidate_plot
     def set_colorbar_label(self, field, label):
         r"""

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -36,8 +36,6 @@
     invalidate_data, invalidate_plot, apply_callback
 from .base_plot_types import CallbackWrapper
 
-from yt.data_objects.time_series import \
-    DatasetSeries
 from yt.data_objects.image_array import \
     ImageArray
 from yt.extern.six import string_types
@@ -50,23 +48,19 @@
     Unit
 from yt.units.unit_registry import \
     UnitParseError
-from yt.units.unit_lookup_table import \
-    prefixable_units, latex_prefixes
 from yt.units.yt_array import \
     YTArray, YTQuantity
-from yt.utilities.definitions import \
-    formatted_length_unit_names
 from yt.utilities.math_utils import \
     ortho_find
 from yt.utilities.orientation import \
     Orientation
 from yt.utilities.exceptions import \
-    YTUnitNotRecognized, \
     YTCannotParseUnitDisplayName, \
-    YTUnitConversionError, \
     YTPlotCallbackError, \
     YTDataTypeUnsupported, \
-    YTInvalidFieldType
+    YTInvalidFieldType, \
+    YTUnitNotRecognized, \
+    YTUnitConversionError
 
 MPL_VERSION = LooseVersion(matplotlib.__version__)
 
@@ -179,11 +173,6 @@
                  periodic=True, origin='center-window', oblique=False, right_handed=True,
                  window_size=8.0, fields=None, fontsize=18, aspect=None,
                  setup=False):
-        if not hasattr(self, "ds"):
-            self.ds = data_source.ds
-            ts = self._initialize_dataset(self.ds)
-            self.ts = ts
-        self._axes_unit_names = None
         self.center = None
         self._periodic = periodic
         self.oblique = oblique
@@ -191,6 +180,7 @@
         self._equivalencies = defaultdict(lambda: (None, {}))
         self.buff_size = buff_size
         self.antialias = antialias
+        self._axes_unit_names = None
 
         self.aspect = aspect
         skip = list(FixedResolutionBuffer._exclude_fields) + data_source._key_fields
@@ -220,12 +210,6 @@
         self.setup_callbacks()
         self._setup_plots()
 
-    def _initialize_dataset(self, ts):
-        if not isinstance(ts, DatasetSeries):
-            if not iterable(ts): ts = [ts]
-            ts = DatasetSeries(ts)
-        return ts
-
     def __iter__(self):
         for ds in self.ts:
             mylog.warning("Switching to %s", ds)
@@ -856,59 +840,7 @@
                 ax = self.plots[f].axes
                 ax.invert_xaxis()
 
-            axes_unit_labels = ['', '']
-            comoving = False
-            hinv = False
-            for i, un in enumerate((unit_x, unit_y)):
-                unn = None
-                if hasattr(self.ds.coordinates, "image_units"):
-                    # This *forces* an override
-                    unn = self.ds.coordinates.image_units[axis_index][i]
-                elif hasattr(self.ds.coordinates, "default_unit_label"):
-                    axax = getattr(self.ds.coordinates,
-                                   "%s_axis" % ("xy"[i]))[axis_index]
-                    unn = self.ds.coordinates.default_unit_label.get(axax,
-                        None)
-                if unn is not None:
-                    axes_unit_labels[i] = r'\ \ \left('+unn+r'\right)'
-                    continue
-                # Use sympy to factor h out of the unit.  In this context 'un'
-                # is a string, so we call the Unit constructor.
-                expr = Unit(un, registry=self.ds.unit_registry).expr
-                h_expr = Unit('h', registry=self.ds.unit_registry).expr
-                # See http://docs.sympy.org/latest/modules/core.html#sympy.core.expr.Expr
-                h_power = expr.as_coeff_exponent(h_expr)[1]
-                # un is now the original unit, but with h factored out.
-                un = str(expr*h_expr**(-1*h_power))
-                un_unit = Unit(un, registry=self.ds.unit_registry)
-                cm = Unit('cm').expr
-                if str(un).endswith('cm') and cm not in un_unit.expr.atoms():
-                    comoving = True
-                    un = un[:-2]
-                # no length units besides code_length end in h so this is safe
-                if h_power == -1:
-                    hinv = True
-                elif h_power != 0:
-                    # It doesn't make sense to scale a position by anything
-                    # other than h**-1
-                    raise RuntimeError
-                if un not in ['1', 'u', 'unitary']:
-                    if un in formatted_length_unit_names:
-                        un = formatted_length_unit_names[un]
-                    else:
-                        un = Unit(un, registry=self.ds.unit_registry)
-                        un = un.latex_representation()
-                        if hinv:
-                            un = un + '\,h^{-1}'
-                        if comoving:
-                            un = un + '\,(1+z)^{-1}'
-                        pp = un[0]
-                        if pp in latex_prefixes:
-                            symbol_wo_prefix = un[1:]
-                            if symbol_wo_prefix in prefixable_units:
-                                un = un.replace(
-                                    pp, "{"+latex_prefixes[pp]+"}", 1)
-                    axes_unit_labels[i] = '\ \ ('+un+')'
+            axes_unit_labels = self._get_axes_unit_labels(unit_x, unit_y)
 
             if self.oblique:
                 labels = [r'$\rm{Image\ x'+axes_unit_labels[0]+'}$',
@@ -1036,7 +968,7 @@
     @invalidate_plot
     def annotate_clear(self, index=None):
         """
-        Clear callbacks from the plot.  If index is not set, clear all 
+        Clear callbacks from the plot.  If index is not set, clear all
         callbacks.  If index is set, clear that index (ie 0 is the first one
         created, 1 is the 2nd one created, -1 is the last one created, etc.)
         """
@@ -1051,7 +983,7 @@
         for f in self.fields:
             keys = self.frb.keys()
             for name, (args, kwargs) in self._callbacks:
-                cbw = CallbackWrapper(self, self.plots[f], self.frb, f, 
+                cbw = CallbackWrapper(self, self.plots[f], self.frb, f,
                                       self._font_properties, self._font_color)
                 CallbackMaker = callback_registry[name]
                 callback = CallbackMaker(*args[1:], **kwargs)
@@ -1069,8 +1001,8 @@
 
     def hide_colorbar(self, field=None):
         """
-        Hides the colorbar for a plot and updates the size of the 
-        plot accordingly.  Defaults to operating on all fields for a 
+        Hides the colorbar for a plot and updates the size of the
+        plot accordingly.  Defaults to operating on all fields for a
         PlotWindow object.
 
         Parameters
@@ -1110,8 +1042,8 @@
 
     def show_colorbar(self, field=None):
         """
-        Shows the colorbar for a plot and updates the size of the 
-        plot accordingly.  Defaults to operating on all fields for a 
+        Shows the colorbar for a plot and updates the size of the
+        plot accordingly.  Defaults to operating on all fields for a
         PlotWindow object.  See hide_colorbar().
 
         Parameters
@@ -1129,8 +1061,8 @@
 
     def hide_axes(self, field=None):
         """
-        Hides the axes for a plot and updates the size of the 
-        plot accordingly.  Defaults to operating on all fields for a 
+        Hides the axes for a plot and updates the size of the
+        plot accordingly.  Defaults to operating on all fields for a
         PlotWindow object.
 
         Parameters
@@ -1168,8 +1100,8 @@
 
     def show_axes(self, field=None):
         """
-        Shows the axes for a plot and updates the size of the 
-        plot accordingly.  Defaults to operating on all fields for a 
+        Shows the axes for a plot and updates the size of the
+        plot accordingly.  Defaults to operating on all fields for a
         PlotWindow object.  See hide_axes().
 
         Parameters
@@ -1283,7 +1215,7 @@
          A dictionary of field parameters than can be accessed by derived
          fields.
     data_source: YTSelectionContainer object
-         Object to be used for data selection. Defaults to ds.all_data(), a 
+         Object to be used for data selection. Defaults to ds.all_data(), a
          region covering the full domain
 
     Examples
@@ -1304,9 +1236,6 @@
                  origin='center-window', right_handed=True, fontsize=18, field_parameters=None,
                  window_size=8.0, aspect=None, data_source=None):
         # this will handle time series data and controllers
-        ts = self._initialize_dataset(ds)
-        self.ts = ts
-        ds = self.ds = ts[0]
         axis = fix_axis(axis, ds)
         (bounds, center, display_center) = \
             get_window_parameters(axis, center, width, ds)
@@ -1438,7 +1367,7 @@
     fontsize : integer
          The size of the fonts for the axis, colorbar, and tick labels.
     method : string
-         The method of projection.  Valid methods are: 
+         The method of projection.  Valid methods are:
 
          "integrate" with no weight_field specified : integrate the requested
          field along the line of sight.
@@ -1464,7 +1393,7 @@
          A dictionary of field parameters than can be accessed by derived
          fields.
     data_source: YTSelectionContainer object
-         Object to be used for data selection. Defaults to ds.all_data(), a 
+         Object to be used for data selection. Defaults to ds.all_data(), a
          region covering the full domain
 
     Examples
@@ -1486,11 +1415,8 @@
                  right_handed=True, fontsize=18, field_parameters=None, data_source=None,
                  method = "integrate", proj_style = None, window_size=8.0,
                  aspect=None):
-        ts = self._initialize_dataset(ds)
-        self.ts = ts
-        ds = self.ds = ts[0]
         axis = fix_axis(axis, ds)
-        # proj_style is deprecated, but if someone specifies then it trumps 
+        # proj_style is deprecated, but if someone specifies then it trumps
         # method.
         if proj_style is not None:
             method = proj_style
@@ -1519,7 +1445,7 @@
                            field_parameters=field_parameters, method=method,
                            max_level=max_level)
         PWViewerMPL.__init__(self, proj, bounds, fields=fields, origin=origin,
-                             right_handed=right_handed, fontsize=fontsize, window_size=window_size, 
+                             right_handed=right_handed, fontsize=fontsize, window_size=window_size,
                              aspect=aspect)
         if axes_unit is None:
             axes_unit = get_axes_unit(width, ds)
@@ -1593,7 +1519,7 @@
          A dictionary of field parameters than can be accessed by derived
          fields.
     data_source : YTSelectionContainer Object
-         Object to be used for data selection.  Defaults ds.all_data(), a 
+         Object to be used for data selection.  Defaults ds.all_data(), a
          region covering the full domain.
     """
 
@@ -1748,7 +1674,7 @@
          This should only be used for uniform resolution grid datasets, as other
          datasets may result in unphysical images.
     data_source: YTSelectionContainer object
-         Object to be used for data selection. Defaults to ds.all_data(), a 
+         Object to be used for data selection. Defaults to ds.all_data(), a
          region covering the full domain
     """
     _plot_type = 'OffAxisProjection'

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 yt/visualization/profile_plotter.py
--- a/yt/visualization/profile_plotter.py
+++ b/yt/visualization/profile_plotter.py
@@ -41,7 +41,8 @@
 from yt.funcs import \
     ensure_list, \
     get_image_suffix, \
-    matplotlib_style_context
+    matplotlib_style_context, \
+    iterable
 
 def get_canvas(name):
     from . import _mpl_imports as mpl
@@ -60,7 +61,7 @@
         canvas_cls = mpl.FigureCanvasAgg
     return canvas_cls
 
-class PlotContainer(OrderedDict):
+class PlotContainerDict(OrderedDict):
     def __missing__(self, key):
         plot = PlotMPL((10, 8), [0.1, 0.1, 0.8, 0.8], None, None)
         self[key] = plot
@@ -378,7 +379,7 @@
         if plot_specs is None:
             plot_specs = [dict() for p in obj.profiles]
         obj.plot_spec = plot_specs
-        obj.plots = PlotContainer()
+        obj.plots = PlotContainerDict()
         obj.figures = FigureContainer(obj.plots)
         obj.axes = AxesContainer(obj.plots)
         obj._setup_plots()
@@ -1370,7 +1371,10 @@
         if fontscale < 1.0:
             fontscale = np.sqrt(fontscale)
 
-        self._cb_size = 0.0375*figure_size
+        if iterable(figure_size):
+            self._cb_size = 0.0375*figure_size[0]
+        else:
+            self._cb_size = 0.0375*figure_size
         self._ax_text_size = [1.1*fontscale, 0.9*fontscale]
         self._top_buff_size = 0.30*fontscale
         self._aspect = 1.0

diff -r d7bc05ca16057f120acd5d043f90b6a8a32a2de5 -r 41f22e5d10cbe6cd5a029b315a774d78513e43b1 yt/visualization/tests/test_line_plots.py
--- /dev/null
+++ b/yt/visualization/tests/test_line_plots.py
@@ -0,0 +1,63 @@
+"""
+Tests for making line plots
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2017, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+import yt
+from yt.utilities.answer_testing.framework import \
+    requires_ds, \
+    data_dir_load, \
+    GenericImageTest
+from yt.testing import fake_random_ds
+import os
+import tempfile
+import shutil
+
+def setup():
+    """Test specific setup."""
+    from yt.config import ytcfg
+    ytcfg["yt", "__withintesting"] = "True"
+
+def compare(ds, fields, point1, point2, resolution, test_prefix, decimals=12):
+    def line_plot(filename_prefix):
+        ln = yt.LinePlot(ds, fields, point1, point2, resolution)
+        image_file = ln.save(filename_prefix)
+        return image_file
+
+    line_plot.__name__ = "line_{}".format(test_prefix)
+    test = GenericImageTest(ds, line_plot, decimals)
+    test.prefix = test_prefix
+    return test
+
+tri2 = "SecondOrderTris/RZ_p_no_parts_do_nothing_bcs_cone_out.e"
+
+ at requires_ds(tri2)
+def test_line_plot():
+    ds = data_dir_load(tri2, kwargs={'step':-1})
+    fields = [field for field in ds.field_list if field[0] == 'all']
+    yield compare(ds, fields, (0, 0, 0), (1, 1, 0), 1000, "answers_line_plot")
+
+def test_line_plot_methods():
+    # Perform I/O in safe place instead of yt main dir
+    tmpdir = tempfile.mkdtemp()
+    curdir = os.getcwd()
+    os.chdir(tmpdir)
+
+    ds = fake_random_ds(32)
+
+    plot = yt.LinePlot(ds, 'density', [0, 0, 0], [1, 1, 1], 512)
+    plot.add_legend('density')
+    plot.set_x_unit('cm')
+    plot.set_unit('density', 'kg/cm**3')
+    plot.save()
+
+    os.chdir(curdir)
+    # clean up
+    shutil.rmtree(tmpdir)

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