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

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Wed Jun 15 13:58:35 PDT 2016


9 new commits in yt:

https://bitbucket.org/yt_analysis/yt/commits/cacac5fc24c1/
Changeset:   cacac5fc24c1
Branch:      yt
User:        atmyers
Date:        2016-05-13 23:03:20+00:00
Summary:     implement scale, scale_units, and normalize kwargs for cutting quiver callbacks
Affected #:  1 file

diff -r 8d076b60c7dacde1de17041f79ff1a6bd0c3ea8e -r cacac5fc24c1da8a14e60766a4685771bfa98704 yt/visualization/plot_modifications.py
--- a/yt/visualization/plot_modifications.py
+++ b/yt/visualization/plot_modifications.py
@@ -261,7 +261,9 @@
         if plot._type_name == "CuttingPlane":
             qcb = CuttingQuiverCallback("cutting_plane_velocity_x",
                                         "cutting_plane_velocity_y",
-                                        self.factor)
+                                        self.factor, scale=self.scale,
+                                        normalize=self.normalize,
+                                        scale_units=self.scale_units)
         else:
             ax = plot.data.axis
             (xi, yi) = (plot.data.ds.coordinates.x_axis[ax],
@@ -306,7 +308,9 @@
         if plot._type_name == "CuttingPlane":
             qcb = CuttingQuiverCallback("cutting_plane_magnetic_field_x",
                                         "cutting_plane_magnetic_field_y",
-                                        self.factor)
+                                        self.factor, scale=self.scale, 
+                                        scale_units=self.scale_units, 
+                                        normalize=self.normalize)
         else:
             xax = plot.data.ds.coordinates.x_axis[plot.data.axis]
             yax = plot.data.ds.coordinates.y_axis[plot.data.axis]
@@ -818,11 +822,15 @@
     *field_y*, skipping every *factor* datapoint in the discretization.
     """
     _type_name = "cquiver"
-    def __init__(self, field_x, field_y, factor):
+    def __init__(self, field_x, field_y, factor=16, scale=None,
+                 scale_units=None, normalize=None):
         PlotCallback.__init__(self)
         self.field_x = field_x
         self.field_y = field_y
         self.factor = factor
+        self.scale = scale
+        self.scale_units = scale_units
+        self.normalize = normalize
 
     def __call__(self, plot):
         x0, x1 = plot.xlim
@@ -833,6 +841,7 @@
         nx = plot.image._A.shape[0] / self.factor
         ny = plot.image._A.shape[1] / self.factor
         indices = np.argsort(plot.data['dx'])[::-1]
+
         pixX = pixelize_off_axis_cartesian(
                                plot.data['x'], plot.data['y'], plot.data['z'],
                                plot.data['px'], plot.data['py'],
@@ -840,7 +849,7 @@
                                plot.data.center, plot.data._inv_mat, indices,
                                plot.data[self.field_x],
                                int(nx), int(ny),
-                               (x0, x1, y0, y1),).transpose()
+                               (x0, x1, y0, y1)).transpose()
         pixY = pixelize_off_axis_cartesian(
                                plot.data['x'], plot.data['y'], plot.data['z'],
                                plot.data['px'], plot.data['py'],
@@ -848,10 +857,16 @@
                                plot.data.center, plot.data._inv_mat, indices,
                                plot.data[self.field_y],
                                int(nx), int(ny),
-                               (x0, x1, y0, y1),).transpose()
+                               (x0, x1, y0, y1)).transpose()
         X,Y = np.meshgrid(np.linspace(xx0,xx1,nx,endpoint=True),
                           np.linspace(yy0,yy1,ny,endpoint=True))
-        plot._axes.quiver(X,Y, pixX, pixY)
+
+        if self.normalize:
+            nn = np.sqrt(pixX**2 + pixY**2)
+            pixX /= nn
+            pixY /= nn
+
+        plot._axes.quiver(X,Y, pixX, pixY, scale=self.scale, scale_units=self.scale_units)
         plot._axes.set_xlim(xx0,xx1)
         plot._axes.set_ylim(yy0,yy1)
         plot._axes.hold(False)


https://bitbucket.org/yt_analysis/yt/commits/1b3528fbd41d/
Changeset:   1b3528fbd41d
Branch:      yt
User:        atmyers
Date:        2016-05-13 23:04:43+00:00
Summary:     update docstrings to account for normalize option
Affected #:  1 file

diff -r cacac5fc24c1da8a14e60766a4685771bfa98704 -r 1b3528fbd41d5cd8905bcedbb0183318eab9e624 yt/visualization/plot_modifications.py
--- a/yt/visualization/plot_modifications.py
+++ b/yt/visualization/plot_modifications.py
@@ -245,8 +245,7 @@
     True, the velocity fields will be scaled by their local
     (in-plane) length, allowing morphological features to be more
     clearly seen for fields with substantial variation in field
-    strength (normalize is not implemented and thus ignored for
-    Cutting Planes).
+    strength.
     """
     _type_name = "velocity"
     def __init__(self, factor=16, scale=None, scale_units=None, normalize=False):


https://bitbucket.org/yt_analysis/yt/commits/eb557a1193ef/
Changeset:   eb557a1193ef
Branch:      yt
User:        atmyers
Date:        2016-05-13 23:05:27+00:00
Summary:     set slice bounds in code units, to match behavior of AxisAlignedSlicePlot.
Affected #:  1 file

diff -r 1b3528fbd41d5cd8905bcedbb0183318eab9e624 -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -102,7 +102,7 @@
         mat = np.transpose(np.column_stack((perp1,perp2,normal)))
         center = np.dot(mat,center)
 
-    w = tuple(el.in_units('unitary') for el in width)
+    w = tuple(el.in_units('code_length') for el in width)
     bounds = tuple(((2*(i % 2))-1)*w[i//2]/2 for i in range(len(w)*2))
 
     return (bounds, center)


https://bitbucket.org/yt_analysis/yt/commits/f529e9c9959b/
Changeset:   f529e9c9959b
Branch:      yt
User:        atmyers
Date:        2016-06-03 19:21:16+00:00
Summary:     merging with tip.
Affected #:  88 files

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 .hgchurn
--- a/.hgchurn
+++ b/.hgchurn
@@ -1,6 +1,6 @@
 stephenskory at yahoo.com = s at skory.us
 "Stephen Skory stephenskory at yahoo.com" = s at skory.us
-yuan at astro.columbia.edu = bear0980 at gmail.com
+bear0980 at gmail.com = yuan at astro.columbia.edu
 juxtaposicion at gmail.com = cemoody at ucsc.edu
 chummels at gmail.com = chummels at astro.columbia.edu
 jwise at astro.princeton.edu = jwise at physics.gatech.edu
@@ -19,7 +19,6 @@
 sername=kayleanelson = kaylea.nelson at yale.edu
 kayleanelson = kaylea.nelson at yale.edu
 jcforbes at ucsc.edu = jforbes at ucolick.org
-ngoldbau at ucsc.edu = goldbaum at ucolick.org
 biondo at wisc.edu = Biondo at wisc.edu
 samgeen at googlemail.com = samgeen at gmail.com
 fbogert = fbogert at ucsc.edu
@@ -39,4 +38,12 @@
 jnaiman at ucolick.org = jnaiman
 migueld.deval = miguel at archlinux.net
 slevy at ncsa.illinois.edu = salevy at illinois.edu
-malzraa at gmail.com = kellerbw at mcmaster.ca
\ No newline at end of file
+malzraa at gmail.com = kellerbw at mcmaster.ca
+None = convert-repo
+dfenn = df11c at my.fsu.edu
+langmm = langmm.astro at gmail.com
+jmt354 = jmtomlinson95 at gmail.com
+desika = dnarayan at haverford.edu
+Ben Thompson = bthompson2090 at gmail.com
+goldbaum at ucolick.org = ngoldbau at illinois.edu
+ngoldbau at ucsc.edu = ngoldbau at illinois.edu

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 .hgignore
--- a/.hgignore
+++ b/.hgignore
@@ -45,9 +45,11 @@
 yt/utilities/lib/mesh_intersection.cpp
 yt/utilities/lib/mesh_samplers.cpp
 yt/utilities/lib/mesh_traversal.cpp
+yt/utilities/lib/mesh_triangulation.c
 yt/utilities/lib/mesh_utilities.c
 yt/utilities/lib/misc_utilities.c
 yt/utilities/lib/particle_mesh_operations.c
+yt/utilities/lib/primitives.c
 yt/utilities/lib/origami.c
 yt/utilities/lib/particle_mesh_operations.c
 yt/utilities/lib/pixelization_routines.c

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 CREDITS
--- a/CREDITS
+++ b/CREDITS
@@ -10,6 +10,7 @@
                 Alex Bogert (fbogert at ucsc.edu)
                 André-Patrick Bubel (code at andre-bubel.de)
                 Pengfei Chen (madcpf at gmail.com)
+                Yi-Hao Chen (yihaochentw at gmail.com)
                 David Collins (dcollins4096 at gmail.com)
                 Brian Crosby (crosby.bd at gmail.com)
                 Andrew Cunningham (ajcunn at gmail.com)
@@ -25,10 +26,12 @@
                 William Gray (graywilliamj at gmail.com)
                 Markus Haider (markus.haider at uibk.ac.at)
                 Eric Hallman (hallman13 at gmail.com)
+                David Hannasch (David.A.Hannasch at gmail.com)
                 Cameron Hummels (chummels at gmail.com)
                 Anni Järvenpää (anni.jarvenpaa at gmail.com)
                 Allyson Julian (astrohckr at gmail.com)
                 Christian Karch (chiffre at posteo.de)
+                Maximilian Katz (maximilian.katz at stonybrook.edu)
                 Ben W. Keller (kellerbw at mcmaster.ca)
                 Ji-hoon Kim (me at jihoonkim.org)
                 Steffen Klemer (sklemer at phys.uni-goettingen.de)
@@ -60,6 +63,7 @@
                 Anna Rosen (rosen at ucolick.org)
                 Chuck Rozhon (rozhon2 at illinois.edu)
                 Douglas Rudd (drudd at uchicago.edu)
+                Hsi-Yu Schive (hyschive at gmail.com)
                 Anthony Scopatz (scopatz at gmail.com)
                 Noel Scudder (noel.scudder at stonybrook.edu)
                 Pat Shriwise (shriwise at wisc.edu)
@@ -75,6 +79,7 @@
                 Elizabeth Tasker (tasker at astro1.sci.hokudai.ac.jp)
                 Benjamin Thompson (bthompson2090 at gmail.com)
                 Robert Thompson (rthompsonj at gmail.com)
+                Joseph Tomlinson (jmtomlinson95 at gmail.com)
                 Stephanie Tonnesen (stonnes at gmail.com)
                 Matthew Turk (matthewturk at gmail.com)
                 Rich Wagner (rwagner at physics.ucsd.edu)

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 appveyor.yml
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,38 @@
+# AppVeyor.com is a Continuous Integration service to build and run tests under
+# Windows
+
+environment:
+
+  global:
+      PYTHON: "C:\\Miniconda-x64"
+
+  matrix:
+
+      - PYTHON_VERSION: "2.7"
+
+      - PYTHON_VERSION: "3.5"
+
+
+platform:
+    -x64
+
+install:
+    - "SET PATH=%PYTHON%;%PYTHON%\\Scripts;%PATH%"
+
+    # Install the build and runtime dependencies of the project.
+    # Create a conda environment
+    - "conda create -q --yes -n test python=%PYTHON_VERSION%"
+    - "activate test"
+
+    # Check that we have the expected version of Python
+    - "python --version"
+
+    # Install specified version of numpy and dependencies
+    - "conda install -q --yes numpy nose setuptools ipython Cython sympy h5py matplotlib"
+    - "python setup.py develop"
+
+# Not a .NET project
+build: false
+
+test_script:
+  - "nosetests -e test_all_fields ."

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 doc/source/analyzing/fields.rst
--- a/doc/source/analyzing/fields.rst
+++ b/doc/source/analyzing/fields.rst
@@ -14,14 +14,117 @@
 out of favor, as these discrete fields can be any type of sparsely populated
 data.
 
+What are fields?
+----------------
+
+Fields in yt are denoted by a two-element tuple, of the form ``(field_type,
+field_name)``. The first element, the "field type" is a category for a
+field. Possible field types used in yt include *gas* (for fluid mesh fields
+defined on a mesh) or *io* (for fields defined at particle locations). Field
+types can also correspond to distinct particle of fluid types in a single
+simulation. For example, a plasma physics simulation using the Particle in Cell
+method might have particle types corresponding to *electrons* and *ions*. See
+:ref:`known-field-types` below for more info about field types in yt.
+
+The second element of field tuples, the "field name", denotes the specific field
+to select, given the field type. Possible field names include *density*,
+*velocity_x* or *pressure* --- these three fields are examples of field names
+that might be used for a fluid defined on a mesh. Examples of particle fields
+include *particle_mass*, *particle_position*, or *particle_velocity_x*. In
+general, particle field names are prefixed by "particle\_", which makes it easy
+to distinguish between a particle field or a mesh field when no field type is
+provided.
+
+What fields are available?
+--------------------------
+
+We provide a full list of fields that yt recognizes by default at
+:ref:`field-list`.  If you want to create additional custom derived fields,
+see :ref:`creating-derived-fields`.
+
+Every dataset has an attribute, ``ds.fields``.  This attribute possesses
+attributes itself, each of which is a "field type," and each field type has as
+its attributes the fields themselves.  When one of these is printed, it returns
+information about the field and things like units and so on.  You can use this
+for tab-completing as well as easier access to information.
+
+As an example, you might browse the available fields like so:
+
+.. code-block:: python
+
+  print(dir(ds.fields))
+  print(dir(ds.fields.gas))
+  print(ds.fields.gas.density)
+
+On an Enzo dataset, the result from the final command would look something like
+this:::
+
+  Alias Field for "('enzo', 'Density')" (gas, density): (units: g/cm**3)
+
+You can use this to easily explore available fields, particularly through
+tab-completion in Jupyter/IPython.
+
+For a more programmatic method of accessing fields, you can utilize the
+``ds.field_list``, ``ds.derived_field_list`` and some accessor methods to gain
+information about fields.  The full list of fields available for a dataset can
+be found as the attribute ``field_list`` for native, on-disk fields and
+``derived_field_list`` for derived fields (``derived_field_list`` is a superset
+of ``field_list``).  You can view these lists by examining a dataset like this:
+
+.. code-block:: python
+
+   ds = yt.load("my_data")
+   print(ds.field_list)
+   print(ds.derived_field_list)
+
+By using the ``field_info()`` class, one can access information about a given
+field, like its default units or the source code for it.
+
+.. code-block:: python
+
+   ds = yt.load("my_data")
+   ds.index
+   print(ds.field_info["gas", "pressure"].get_units())
+   print(ds.field_info["gas", "pressure"].get_source())
+
+Using fields to access data
+---------------------------
+
+The primary *use* of fields in yt is to access data from a dataset. For example,
+if I want to use a data object (see :ref:`Data-objects` for more detail about
+data objects) to access the ``('gas', 'density')`` field, one can do any of the
+following:
+
+.. code-block:: python
+
+    ad = ds.all_data()
+    density = ad['density']
+    density = ad['gas', 'density']
+    density = ad[('gas', 'density')]
+    dnesity = ad[ds.fields.gas.density]
+
+The first data access example is the simplest. In that example, the field type
+is inferred from the name of the field. The next two examples use the field type
+explicitly, this might be necessary if there is more than one field type with a
+"density" field defined in the same simulation. The third example is a slightly
+more verbose and is syntactically identical to the second example due to the way
+indexing functions in Python. The final example uses the ``ds.fields` object
+described above. This way of accessing fields lends itself to interactive use,
+especially if you make heavy use of IPython's tab completion features. Any of
+these ways of denoting the ``('gas', 'density')`` field can be used when
+supplying a field name to a yt data object, analysis routines, or plotting and
+visualization function.
+
+Accessing Fields without a Field Type
+-------------------------------------
+
 In previous versions of yt, there was a single mechanism of accessing fields on
-a data container -- by their name, which was mandated to be a single string,
-and which often varied between different code frontends.  yt 3.0 allows
-for datasets containing multiple different types of fluid fields, mesh fields,
-particles (with overlapping or disjoint lists of fields).  To enable accessing
-these fields in a meaningful, simple way, the mechanism for accessing them has
-changed to take an optional *field type* in addition to the *field name* of
-the form ('*field type*', '*field name*').
+a data container -- by their name, which was mandated to be a single string, and
+which often varied between different code frontends.  yt 3.0 allows for datasets
+containing multiple different types of fluid fields, mesh fields, particles
+(with overlapping or disjoint lists of fields). However, to preserve backward
+compatibility and make interactive use simpler, yt will still accept field names
+given as a string and will try to infer the field type given a field name.
 
 As an example, we may be in a situation where have multiple types of particles
 which possess the ``particle_position`` field.  In the case where a data
@@ -30,9 +133,9 @@
 
 .. code-block:: python
 
-   print(ad["humans", "particle_position"])
-   print(ad["dogs", "particle_position"])
-   print(ad["dinosaurs", "particle_position"])
+   print(ad["dark_matter", "particle_position"])
+   print(ad["stars", "particle_position"])
+   print(ad["black_holes", "particle_position"])
 
 Each of these three fields may have different sizes.  In order to enable
 falling back on asking only for a field by the name, yt will use the most
@@ -45,7 +148,8 @@
 
    print(ad["particle_velocity"])
 
-it would select ``dinosaurs`` as the field type.
+it would select ``black_holes`` as the field type, since the last field accessed
+used that field type.
 
 The same operations work for fluid and mesh fields.  As an example, in some
 cosmology simulations, we may want to examine the mass of particles in a region
@@ -189,58 +293,6 @@
  * Species fields, such as for chemistry species (yt can recognize the entire
    periodic table in field names and construct ionization fields as need be)
 
-What fields are available?
---------------------------
-
-We provide a full list of fields that yt recognizes by default at
-:ref:`field-list`.  If you want to create additional custom derived fields,
-see :ref:`creating-derived-fields`.
-
-Every dataset has an attribute, ``ds.fields``.  This attribute possesses
-attributes itself, each of which is a "field type," and each field type has as
-its attributes the fields themselves.  When one of these is printed, it returns
-information about the field and things like units and so on.  You can use this
-for tab-completing as well as easier access to information.
-
-As an example, you might browse the available fields like so:
-
-.. code-block:: python
-
-  print(dir(ds.fields))
-  print(dir(ds.fields.gas))
-  print(ds.fields.gas.density)
-
-On an Enzo dataset, the result from the final command would look something like
-this:::
-
-  Alias Field for "('enzo', 'Density')" (gas, density): (units: g/cm**3)
-
-You can use this to easily explore available fields, particularly through
-tab-completion in Jupyter/IPython.
-
-For a more programmatic method of accessing fields, you can utilize the
-``ds.field_list``, ``ds.derived_field_list`` and some accessor methods to gain
-information about fields.  The full list of fields available for a dataset can
-be found as the attribute ``field_list`` for native, on-disk fields and
-``derived_field_list`` for derived fields (``derived_field_list`` is a superset
-of ``field_list``).  You can view these lists by examining a dataset like this:
-
-.. code-block:: python
-
-   ds = yt.load("my_data")
-   print(ds.field_list)
-   print(ds.derived_field_list)
-
-By using the ``field_info()`` class, one can access information about a given
-field, like its default units or the source code for it.
-
-.. code-block:: python
-
-   ds = yt.load("my_data")
-   ds.index
-   print(ds.field_info["gas", "pressure"].get_units())
-   print(ds.field_info["gas", "pressure"].get_source())
-
 .. _bfields:
 
 Magnetic Fields

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 doc/source/cookbook/various_lens.py
--- a/doc/source/cookbook/various_lens.py
+++ b/doc/source/cookbook/various_lens.py
@@ -85,6 +85,7 @@
 cam = sc.add_camera(ds, lens_type='spherical')
 # Set the size ratio of the final projection to be 2:1, since spherical lens
 # will generate the final image with length of 2*pi and height of pi.
+# Recommended resolution for YouTube 360-degree videos is [3840, 2160]
 cam.resolution = [500, 250]
 # Standing at (x=0.4, y=0.5, z=0.5), we look in all the radial directions
 # from this point in spherical coordinate.
@@ -99,9 +100,11 @@
 
 # Stereo-spherical lens
 cam = sc.add_camera(ds, lens_type='stereo-spherical')
-# Set the size ratio of the final projection to be 4:1, since spherical-perspective lens
-# will generate the final image with both left-eye and right-eye ones jointed together.
-cam.resolution = [1000, 250]
+# Set the size ratio of the final projection to be 1:1, since spherical-perspective lens
+# will generate the final image with both left-eye and right-eye ones jointed together,
+# with left-eye image on top and right-eye image on bottom.
+# Recommended resolution for YouTube virtual reality videos is [3840, 2160]
+cam.resolution = [500, 500]
 cam.position = ds.arr([0.4, 0.5, 0.5], 'code_length')
 cam.switch_orientation(normal_vector=normal_vector,
                        north_vector=north_vector)

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 doc/source/developing/testing.rst
--- a/doc/source/developing/testing.rst
+++ b/doc/source/developing/testing.rst
@@ -95,17 +95,17 @@
 To create new unit tests:
 
 #. Create a new ``tests/`` directory next to the file containing the
-   functionality you want to test.  Be sure to add this new directory as a
-   subpackage in the setup.py script located in the directory you're adding a
-   new ``tests/`` folder to.  This ensures that the tests will be deployed in
-   yt source and binary distributions.
+   functionality you want to test and add an empty ``__init__.py`` file to
+   it.
 #. Inside that directory, create a new python file prefixed with ``test_`` and
    including the name of the functionality.
 #. Inside that file, create one or more routines prefixed with ``test_`` that
-   accept no arguments.  These should ``yield`` a tuple of the form
-   ``function``, ``argument_one``, ``argument_two``, etc.  For example
-   ``yield assert_equal, 1.0, 1.0`` would be captured by nose as a test that
-   asserts that 1.0 is equal to 1.0.
+   accept no arguments. The test function should do some work that tests some
+   functionality and should also verify that the results are correct using
+   assert statements or functions.  
+# Tests can ``yield`` a tuple of the form ``function``, ``argument_one``,
+   ``argument_two``, etc.  For example ``yield assert_equal, 1.0, 1.0`` would be
+   captured by nose as a test that asserts that 1.0 is equal to 1.0.
 #. Use ``fake_random_ds`` to test on datasets, and be sure to test for
    several combinations of ``nproc``, so that domain decomposition can be
    tested as well.
@@ -470,9 +470,9 @@
        test.prefix = "my_unique_name"
 
        # this ensures a nice test name in nose's output
-       test_my_ds.__description__ = test.description
+       test_my_ds.__name__ = test.description
 
-       yield test_my_ds
+       yield test
 
 Another good example of an image comparison test is the
 ``PlotWindowAttributeTest`` defined in the answer testing framework and used in

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 doc/source/examining/loading_data.rst
--- a/doc/source/examining/loading_data.rst
+++ b/doc/source/examining/loading_data.rst
@@ -775,32 +775,38 @@
 ----------
 
 FLASH HDF5 data is *mostly* supported and cared for by John ZuHone.  To load a
-FLASH dataset, you can use the ``yt.load`` command and provide it the file name of a plot file or checkpoint file, but particle
-files are not currently directly loadable by themselves, due to the fact that
-they typically lack grid information. For instance, if you were in a directory
-with the following files:
+FLASH dataset, you can use the ``yt.load`` command and provide it the file name of 
+a plot file, checkpoint file, or particle file. Particle files require special handling
+depending on the situation, the main issue being that they typically lack grid information. 
+The first case is when you have a plotfile and a particle file that you would like to 
+load together. In the simplest case, this occurs automatically. For instance, if you
+were in a directory with the following files:
 
 .. code-block:: none
 
-   cosmoSim_coolhdf5_chk_0026
+   radio_halo_1kpc_hdf5_plt_cnt_0100 # plotfile
+   radio_halo_1kpc_hdf5_part_0100 # particle file
 
-You would feed it the filename ``cosmoSim_coolhdf5_chk_0026``:
+where the plotfile and the particle file were created at the same time (therefore having 
+particle data consistent with the grid structure of the former). Notice also that the 
+prefix ``"radio_halo_1kpc_"`` and the file number ``100`` are the same. In this special case,
+the particle file will be loaded automatically when ``yt.load`` is called on the plotfile.
+This also works when loading a number of files in a time series.
 
-.. code-block:: python
-
-   import yt
-   ds = yt.load("cosmoSim_coolhdf5_chk_0026")
-
-If you have a FLASH particle file that was created at the same time as
-a plotfile or checkpoint file (therefore having particle data
-consistent with the grid structure of the latter), its data may be loaded with the
-``particle_filename`` optional argument:
+If the two files do not have the same prefix and number, but they nevertheless have the same
+grid structure and are at the same simulation time, the particle data may be loaded with the
+``particle_filename`` optional argument to ``yt.load``:
 
 .. code-block:: python
 
     import yt
     ds = yt.load("radio_halo_1kpc_hdf5_plt_cnt_0100", particle_filename="radio_halo_1kpc_hdf5_part_0100")
 
+However, if you don't have a corresponding plotfile for a particle file, but would still 
+like to load the particle data, you can still call ``yt.load`` on the file. However, the 
+grid information will not be available, and the particle data will be loaded in a fashion
+similar to SPH data. 
+
 .. rubric:: Caveats
 
 * Please be careful that the units are correctly utilized; yt assumes cgs by default, but conversion to
@@ -1313,6 +1319,29 @@
 ``bbox``
        The bounding box for the particle positions.
 
+.. _loading-gizmo-data:
+
+Gizmo Data
+----------
+
+Gizmo datasets, including FIRE outputs, can be loaded into yt in the usual 
+manner.  Like other SPH data formats, yt loads Gizmo data as particle fields 
+and then uses smoothing kernels to deposit those fields to an underlying 
+grid structure as spatial fields as described in :ref:`loading-gadget-data`.  
+To load Gizmo datasets using the standard HDF5 output format::
+
+   import yt
+   ds = yt.load("snapshot_600.hdf5")
+
+Because the Gizmo output format is similar to the Gadget format, yt
+may load Gizmo datasets as Gadget depending on the circumstances, but this
+should not pose a problem in most situations.  FIRE outputs will be loaded 
+accordingly due to the number of metallicity fields found (11 or 17).  
+
+For Gizmo outputs written as raw binary outputs, you may have to specify
+a bounding box, field specification, and units as are done for standard 
+Gadget outputs.  See :ref:`loading-gadget-data` for more information.
+
 .. _loading-pyne-data:
 
 Halo Catalog Data

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 doc/source/examining/low_level_inspection.rst
--- a/doc/source/examining/low_level_inspection.rst
+++ b/doc/source/examining/low_level_inspection.rst
@@ -232,3 +232,28 @@
 whatever interface they wish for displaying and saving their image data.
 You can use the :class:`~yt.visualization.fixed_resolution.FixedResolutionBuffer`
 to accomplish this as described in :ref:`fixed-resolution-buffers`.
+
+High-level Information about Particles
+--------------------------------------
+
+There are a number of high-level helpers attached to ``Dataset`` objects to find
+out information about the particles in an output file. First, one can check if
+there are any particles in a dataset at all by examining
+``ds.particles_exist``. This will be ``True`` for datasets the include particles
+and ``False`` otherwise.
+
+One can also see which particle types are available in a dataset. Particle types
+that are available in the dataset's on-disk output are known as "raw" particle
+types, and they will appear in ``ds.particle_types_raw``. Particle types that
+are dynamically defined via a particle filter of a particle union will also
+appear in the ``ds.particle_types`` list. If the simulation only has one
+particle type on-disk, its name will by ``'io'``. If there is more than one
+particle type, the names of the particle types will be inferred from the output
+file. For example, Gadget HDF5 files have particle type names like ``PartType0``
+and ``PartType1``, while Enzo data, which usually only has one particle type,
+will only have a particle named ``io``.
+
+Finally, one can see the number of each particle type by inspecting
+``ds.particle_type_counts``. This will be a dictionary mappying the names of
+particle types in ``ds.particle_types_raw`` to the number of each particle type
+in a simulation output.

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 doc/source/quickstart/2)_Data_Inspection.ipynb
--- a/doc/source/quickstart/2)_Data_Inspection.ipynb
+++ b/doc/source/quickstart/2)_Data_Inspection.ipynb
@@ -154,6 +154,35 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
+    "Finally, we can get basic information about the particle types and number of particles in a simulation:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "collapsed": false
+   },
+   "outputs": [],
+   "source": [
+    "print (ds.particle_types)\n",
+    "print (ds.particle_types_raw)\n",
+    "print (ds.particle_type_counts)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "For this dataset, we see that there are two particle types defined, (`io` and `all`), but that only one of these particle types in in `ds.particle_types_raw`. The `ds.particle_types` list contains *all* particle types in the simulation, including ones that are dynamically defined like particle unions. The `ds.particle_types_raw` list includes only particle types that are in the output file we loaded the dataset from.\n",
+    "\n",
+    "We can also see that there are a bit more than 1.1 million particles in this simulation. Only particle types in `ds.particle_types_raw` will appear in the `ds.particle_type_counts` dictionary."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
     "# Mesh Structure\n",
     "\n",
     "If you're using a simulation type that has grids (for instance, here we're using an Enzo simulation) you can examine the structure of the mesh.  For the most part, you probably won't have to use this unless you're debugging a simulation or examining in detail what is going on."

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 doc/source/reference/code_support.rst
--- a/doc/source/reference/code_support.rst
+++ b/doc/source/reference/code_support.rst
@@ -28,16 +28,18 @@
 +-----------------------+------------+-----------+------------+-------+----------+----------+------------+----------+
 | Enzo                  |     Y      |     Y     |      Y     |   Y   |    Y     |    Y     |     Y      |   Full   |
 +-----------------------+------------+-----------+------------+-------+----------+----------+------------+----------+
+| FITS                  |     Y      |    N/A    |      Y     |   Y   |    Y     |    Y     |     Y      |   Full   |
++-----------------------+------------+-----------+------------+-------+----------+----------+------------+----------+
 | FLASH                 |     Y      |     Y     |      Y     |   Y   |    Y     |    Y     |     Y      |   Full   |
 +-----------------------+------------+-----------+------------+-------+----------+----------+------------+----------+
-| FITS                  |     Y      |    N/A    |      Y     |   Y   |    Y     |    Y     |     Y      |   Full   |
-+-----------------------+------------+-----------+------------+-------+----------+----------+------------+----------+
 | Gadget                |     Y      |     Y     |      Y     |   Y   | Y [#f2]_ |    Y     |     Y      |   Full   |
 +-----------------------+------------+-----------+------------+-------+----------+----------+------------+----------+
 | GAMER                 |     Y      |     N     |      Y     |   Y   |    Y     |    Y     |     Y      |   Full   |
 +-----------------------+------------+-----------+------------+-------+----------+----------+------------+----------+
 | Gasoline              |     Y      |     Y     |      Y     |   Y   | Y [#f2]_ |    Y     |     Y      |   Full   |
 +-----------------------+------------+-----------+------------+-------+----------+----------+------------+----------+
+| Gizmo                 |     Y      |     Y     |      Y     |   Y   | Y [#f2]_ |    Y     |     Y      |   Full   |
++-----------------------+------------+-----------+------------+-------+----------+----------+------------+----------+
 | Grid Data Format (GDF)|     Y      |    N/A    |      Y     |   Y   |    Y     |    Y     |     Y      |   Full   |
 +-----------------------+------------+-----------+------------+-------+----------+----------+------------+----------+
 | Maestro               |   Y [#f1]_ |     N     |      Y     |   Y   |    Y     |    Y     |     N      | Partial  |

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 doc/source/visualizing/callbacks.rst
--- a/doc/source/visualizing/callbacks.rst
+++ b/doc/source/visualizing/callbacks.rst
@@ -692,6 +692,28 @@
    s.annotate_triangle_facets(points, plot_args={"colors": 'black'})
    s.save()
 
+.. _annotate-mesh-lines:
+
+Annotate Mesh Lines Callback
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+.. function:: annotate_mesh_lines(plot_args=None)
+
+   (This is a proxy for
+   :class:`~yt.visualization.plot_modifications.MeshLinesCallback`.)
+
+   This draws the mesh line boundaries over a plot using a Matplotlib
+   line collection. This callback is only useful for unstructured or 
+   semi-structured mesh datasets. 
+
+.. python-script::
+
+   import yt
+   ds = yt.load('MOOSE_sample_data/out.e')
+   sl = yt.SlicePlot(ds, 2, ('connect1', 'nodal_aux'))
+   sl.annotate_mesh_lines(plot_args={'color':'black'})
+   sl.save()
+
 .. _annotate-ray:
 
 Overplot the Path of a Ray

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 doc/source/visualizing/plots.rst
--- a/doc/source/visualizing/plots.rst
+++ b/doc/source/visualizing/plots.rst
@@ -406,16 +406,14 @@
    import yt
    ds = yt.load('MOOSE_sample_data/out.e-s010')
    sl = yt.SlicePlot(ds, 'z', ('connect1', 'diffused'))
-   sl.annotate_mesh_lines(thresh=0.1)
+   sl.annotate_mesh_lines(plot_args={'color':'black'})
    sl.zoom(0.75)
    sl.save()
 
-This annotation is performed by marking the pixels where the mapped coordinate is close
-to the element boundary. What counts as 'close' (in the mapped coordinate system) is
-determined by the ``thresh`` parameter, which can be varied to make the lines thicker or
-thinner.
+The ``plot_args`` parameter is a dictionary of keyword arguments that will be passed
+to matplotlib. It can be used to control the mesh line color, thickness, etc...
 
-The above example all involve 8-node hexahedral mesh elements. Here is another example from
+The above examples all involve 8-node hexahedral mesh elements. Here is another example from
 a dataset that uses 6-node wedge elements:
 
 .. python-script::

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 doc/source/visualizing/volume_rendering.rst
--- a/doc/source/visualizing/volume_rendering.rst
+++ b/doc/source/visualizing/volume_rendering.rst
@@ -31,8 +31,8 @@
 as grid or continent lines, and then to render a production-quality
 visualization.  By changing the "lens" used, a single camera path can output
 images suitable for planetarium domes, immersive and head tracking systems
-(such as the Oculus Rift or recent "spherical" movie viewers such as the
-mobile YouTube app), as well as standard screens.
+(such as the Oculus Rift or recent 360-degree/virtual reality movie viewers
+such as the mobile YouTube app), as well as standard screens.
 
 .. image:: _images/scene_diagram.svg
    :width: 50%
@@ -327,13 +327,19 @@
 
 The :class:`~yt.visualization.volume_rendering.lens.SphericalLens` produces
 a cylindrical-spherical projection.  Movies rendered in this way can be
-displayed in head-tracking devices (e.g. Oculus Rift) or in YouTube 360 view
-(for more information see `the YouTube help
-<https://support.google.com/youtube/answer/6178631?hl=en>`, but it's a
-simple matter of running a script on an encoded movie file.)
+displayed as YouTube 360-degree videos (for more information see
+`the YouTube help: Upload 360-degree videos
+<https://support.google.com/youtube/answer/6178631?hl=en>`_).
 :class:`~yt.visualization.volume_rendering.lens.StereoSphericalLens`
 is identical to :class:`~yt.visualization.volume_rendering.lens.SphericalLens`
-but it produces two images from nearby camera positions for use in 3D viewing.
+but it produces two images from nearby camera positions for virtual reality
+movies, which can be displayed in head-tracking devices (e.g. Oculus Rift)
+or in mobile YouTube app with Google Cardboard (for more information
+see `the YouTube help: Upload virtual reality videos
+<https://support.google.com/youtube/answer/6316263?hl=en>`_).
+`This virtual reality video
+<https://youtu.be/ZYWY53X7UQE>`_ on YouTube is an example produced with
+:class:`~yt.visualization.volume_rendering.lens.StereoSphericalLens`.
 
 .. _annotated-vr-example:
 

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 setup.py
--- a/setup.py
+++ b/setup.py
@@ -157,12 +157,6 @@
     Extension("yt.utilities.lib.bitarray",
               ["yt/utilities/lib/bitarray.pyx"],
               libraries=std_libs, depends=["yt/utilities/lib/bitarray.pxd"]),
-    Extension("yt.utilities.lib.primitives",
-              ["yt/utilities/lib/primitives.pyx"],
-              libraries=std_libs, 
-              depends=["yt/utilities/lib/primitives.pxd",
-                       "yt/utilities/lib/vec3_ops.pxd",
-                       "yt/utilities/lib/bounding_volume_hierarchy.pxd"]),
     Extension("yt.utilities.lib.bounding_volume_hierarchy",
               ["yt/utilities/lib/bounding_volume_hierarchy.pyx"],
               include_dirs=["yt/utilities/lib/"],
@@ -170,6 +164,7 @@
               extra_link_args=omp_args,
               libraries=std_libs,
               depends=["yt/utilities/lib/element_mappings.pxd",
+                       "yt/utilities/lib/mesh_triangulation.h",
                        "yt/utilities/lib/vec3_ops.pxd",
                        "yt/utilities/lib/primitives.pxd"]),
     Extension("yt.utilities.lib.contour_finding",
@@ -196,14 +191,22 @@
                        "yt/utilities/lib/fixed_interpolator.pxd",
                        "yt/utilities/lib/fixed_interpolator.h",
                        ]),
+    Extension("yt.utilities.lib.mesh_triangulation",
+              ["yt/utilities/lib/mesh_triangulation.pyx"],
+              depends=["yt/utilities/lib/mesh_triangulation.h"]),
     Extension("yt.utilities.lib.pixelization_routines",
               ["yt/utilities/lib/pixelization_routines.pyx",
                "yt/utilities/lib/pixelization_constants.c"],
               include_dirs=["yt/utilities/lib/"],
-              language="c++",
               libraries=std_libs, depends=["yt/utilities/lib/fp_utils.pxd",
                                         "yt/utilities/lib/pixelization_constants.h",
                                         "yt/utilities/lib/element_mappings.pxd"]),
+    Extension("yt.utilities.lib.primitives",
+              ["yt/utilities/lib/primitives.pyx"],
+              libraries=std_libs, 
+              depends=["yt/utilities/lib/primitives.pxd",
+                       "yt/utilities/lib/vec3_ops.pxd",
+                       "yt/utilities/lib/bounding_volume_hierarchy.pxd"]),
     Extension("yt.utilities.lib.origami",
               ["yt/utilities/lib/origami.pyx",
                "yt/utilities/lib/origami_tags.c"],
@@ -283,6 +286,7 @@
         Extension("yt.utilities.lib.mesh_construction",
                   ["yt/utilities/lib/mesh_construction.pyx"],
                   depends=["yt/utilities/lib/mesh_construction.pxd",
+                           "yt/utilities/lib/mesh_triangulation.h",
                            "yt/utilities/lib/mesh_intersection.pxd",
                            "yt/utlilites/lib/mesh_samplers.pxd",
                            "yt/utlilites/lib/mesh_traversal.pxd"]),
@@ -452,7 +456,7 @@
     license="BSD",
     zip_safe=False,
     scripts=["scripts/iyt"],
-    data_files=MAPSERVER_FILES + SHADERS_FILES,
+    data_files=MAPSERVER_FILES + [(SHADERS_DIR, SHADERS_FILES)],
     ext_modules=cython_extensions + extensions
 )
 

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 tests/tests.yaml
--- a/tests/tests.yaml
+++ b/tests/tests.yaml
@@ -14,7 +14,7 @@
   local_fits_000:
     - yt/frontends/fits/tests/test_outputs.py
 
-  local_flash_000:
+  local_flash_001:
     - yt/frontends/flash/tests/test_outputs.py
 
   local_gadget_000:
@@ -26,6 +26,9 @@
   local_gdf_000:
     - yt/frontends/gdf/tests/test_outputs.py
 
+  local_gizmo_000:
+    - yt/frontends/gizmo/tests/test_outputs.py
+
   local_halos_000:
     - yt/analysis_modules/halo_analysis/tests/test_halo_finders.py  # [py2]
     - yt/analysis_modules/halo_finding/tests/test_rockstar.py  # [py2]

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/analysis_modules/cosmological_observation/light_ray/light_ray.py
--- a/yt/analysis_modules/cosmological_observation/light_ray/light_ray.py
+++ b/yt/analysis_modules/cosmological_observation/light_ray/light_ray.py
@@ -35,10 +35,12 @@
 
 class LightRay(CosmologySplice):
     """
-    A LightRay object is a one-dimensional object representing the trajectory 
-    of a ray of light as it passes through one or more datasets (simple and
-    compound rays respectively).  One can sample any of the fields intersected
-    by the LightRay object as it passed through the dataset(s).
+    A 1D object representing the path of a light ray passing through a 
+    simulation.  LightRays can be either simple, where they pass through a 
+    single dataset, or compound, where they pass through consecutive 
+    datasets from the same cosmological simulation.  One can sample any of 
+    the fields intersected by the LightRay object as it passed through 
+    the dataset(s).
     
     For compound rays, the LightRay stacks together multiple datasets in a time
     series in order to approximate a LightRay's path through a volume

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/data_objects/data_containers.py
--- a/yt/data_objects/data_containers.py
+++ b/yt/data_objects/data_containers.py
@@ -24,6 +24,8 @@
 from contextlib import contextmanager
 
 from yt.data_objects.particle_io import particle_handler_registry
+from yt.fields.derived_field import \
+    DerivedField
 from yt.frontends.ytdata.utilities import \
     save_as_dataset
 from yt.funcs import \
@@ -348,6 +350,7 @@
             for i, chunk in enumerate(chunks):
                 with self._chunked_read(chunk):
                     gz = self._current_chunk.objs[0]
+                    gz.field_parameters = self.field_parameters
                     wogz = gz._base_grid
                     ind += wogz.select(
                         self.selector,
@@ -982,6 +985,9 @@
                     raise YTFieldNotParseable(field)
                 ftype, fname = field
                 finfo = self.ds._get_field_info(ftype, fname)
+            elif isinstance(field, DerivedField):
+                ftype, fname = field.name
+                finfo = field
             else:
                 fname = field
                 finfo = self.ds._get_field_info("unknown", fname)

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/data_objects/octree_subset.py
--- a/yt/data_objects/octree_subset.py
+++ b/yt/data_objects/octree_subset.py
@@ -433,57 +433,62 @@
         self.base_region = base_region
         self.base_selector = base_region.selector
 
+class OctreeSubsetBlockSlicePosition(object):
+    def __init__(self, ind, block_slice):
+        self.ind = ind
+        self.block_slice = block_slice
+        nz = self.block_slice.octree_subset.nz
+        self.ActiveDimensions = np.array([nz,nz,nz], dtype="int64")
+
+    def __getitem__(self, key):
+        bs = self.block_slice
+        rv = bs.octree_subset[key][:,:,:,self.ind]
+        if bs.octree_subset._block_reorder:
+            rv = rv.copy(order=bs.octree_subset._block_reorder)
+        return rv
+
+    @property
+    def id(self):
+        return self.ind
+
+    @property
+    def Level(self):
+        return self.block_slice._ires[0,0,0,self.ind]
+
+    @property
+    def LeftEdge(self):
+        LE = (self.block_slice._fcoords[0,0,0,self.ind,:]
+            - self.block_slice._fwidth[0,0,0,self.ind,:]*0.5)
+        return LE
+
+    @property
+    def RightEdge(self):
+        RE = (self.block_slice._fcoords[-1,-1,-1,self.ind,:]
+            + self.block_slice._fwidth[-1,-1,-1,self.ind,:]*0.5)
+        return RE
+
+    @property
+    def dds(self):
+        return self.block_slice._fwidth[0,0,0,self.ind,:]
+
+    def clear_data(self):
+        pass
+
+    def get_vertex_centered_data(self, *args, **kwargs):
+        raise NotImplementedError
+
+
 class OctreeSubsetBlockSlice(object):
     def __init__(self, octree_subset):
-        self.ind = None
         self.octree_subset = octree_subset
         # Cache some attributes
-        nz = octree_subset.nz
-        self.ActiveDimensions = np.array([nz,nz,nz], dtype="int64")
         for attr in ["ires", "icoords", "fcoords", "fwidth"]:
             v = getattr(octree_subset, attr)
             setattr(self, "_%s" % attr, octree_subset._reshape_vals(v))
 
     def __iter__(self):
         for i in range(self._ires.shape[-1]):
-            self.ind = i
-            yield i, self
-
-    def clear_data(self):
-        pass
-
-    def __getitem__(self, key):
-        rv = self.octree_subset[key][:,:,:,self.ind]
-        if self.octree_subset._block_reorder:
-            rv = rv.copy(order=self.octree_subset._block_reorder)
-        return rv
-
-    def get_vertex_centered_data(self, *args, **kwargs):
-        raise NotImplementedError
-
-    @property
-    def id(self):
-        return np.random.randint(1)
-
-    @property
-    def Level(self):
-        return self._ires[0,0,0,self.ind]
-
-    @property
-    def LeftEdge(self):
-        LE = (self._fcoords[0,0,0,self.ind,:]
-            - self._fwidth[0,0,0,self.ind,:]*0.5)
-        return LE
-
-    @property
-    def RightEdge(self):
-        RE = (self._fcoords[-1,-1,-1,self.ind,:]
-            + self._fwidth[-1,-1,-1,self.ind,:]*0.5)
-        return RE
-
-    @property
-    def dds(self):
-        return self._fwidth[0,0,0,self.ind,:]
+            yield i, OctreeSubsetBlockSlicePosition(i, self)
 
 class YTPositionArray(YTArray):
     @property

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/data_objects/profiles.py
--- a/yt/data_objects/profiles.py
+++ b/yt/data_objects/profiles.py
@@ -15,6 +15,7 @@
 
 import numpy as np
 
+from yt.fields.derived_field import DerivedField
 from yt.frontends.ytdata.utilities import \
     save_as_dataset
 from yt.funcs import \
@@ -249,8 +250,11 @@
 
     def __getitem__(self, field):
         fname = self.field_map.get(field, None)
-        if fname is None and isinstance(field, tuple):
-            fname = self.field_map.get(field[1], None)
+        if fname is None:
+            if isinstance(field, tuple):
+                fname = self.field_map.get(field[1], None)
+            elif isinstance(field, DerivedField):
+                fname = self.field_map.get(field.name[1], None)
         if fname is None:
             raise KeyError(field)
         else:

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/data_objects/static_output.py
--- a/yt/data_objects/static_output.py
+++ b/yt/data_objects/static_output.py
@@ -15,6 +15,7 @@
 #-----------------------------------------------------------------------------
 
 import functools
+import itertools
 import numpy as np
 import os
 import time
@@ -24,6 +25,8 @@
 from yt.extern.six import add_metaclass, string_types
 
 from yt.config import ytcfg
+from yt.fields.derived_field import \
+    DerivedField
 from yt.funcs import \
     mylog, \
     set_intersection, \
@@ -62,7 +65,6 @@
 from yt.units.unit_systems import create_code_unit_system
 from yt.data_objects.region_expression import \
     RegionExpression
-
 from yt.geometry.coordinates.api import \
     CoordinateHandler, \
     CartesianCoordinateHandler, \
@@ -171,6 +173,7 @@
     derived_field_list = requires_index("derived_field_list")
     fields = requires_index("fields")
     _instantiated = False
+    _particle_type_counts = None
 
     def __new__(cls, filename=None, *args, **kwargs):
         if not isinstance(filename, string_types):
@@ -590,7 +593,10 @@
     def _get_field_info(self, ftype, fname = None):
         self.index
         if fname is None:
-            ftype, fname = "unknown", ftype
+            if isinstance(ftype, DerivedField):
+                ftype, fname = ftype.name
+            else:
+                ftype, fname = "unknown", ftype
         guessing_type = False
         if ftype == "unknown":
             guessing_type = True
@@ -763,6 +769,27 @@
         return fields
 
     @property
+    def particles_exist(self):
+        for pt, f in itertools.product(self.particle_types_raw, self.field_list):
+            if pt == f[0]:
+                return True
+        return False
+
+    @property
+    def particle_type_counts(self):
+        self.index
+        if self.particles_exist is False:
+            return {}
+
+        # frontends or index implementation can populate this dict while
+        # creating the index if they know particle counts at that time
+        if self._particle_type_counts is not None:
+            return self._particle_type_counts
+
+        self._particle_type_counts = self.index._get_particle_type_counts()
+        return self._particle_type_counts
+
+    @property
     def ires_factor(self):
         o2 = np.log2(self.refine_by)
         if o2 != int(o2):

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/data_objects/tests/test_dataset_access.py
--- a/yt/data_objects/tests/test_dataset_access.py
+++ b/yt/data_objects/tests/test_dataset_access.py
@@ -1,4 +1,8 @@
-from yt.testing import fake_amr_ds, assert_equal
+from yt.testing import \
+    assert_equal, \
+    fake_amr_ds, \
+    fake_particle_ds, \
+    fake_random_ds
 
 # This will test the "dataset access" method.
 
@@ -37,3 +41,10 @@
     rho *= 2.0
     yield assert_equal, dd["density"]*2.0, ds.r["density"]
     yield assert_equal, dd["gas", "density"]*2.0, ds.r["gas", "density"]
+
+def test_particle_counts():
+    ds = fake_random_ds(16, particles=100)
+    assert ds.particle_type_counts == {'io': 100}
+
+    pds = fake_particle_ds(npart=128)
+    assert pds.particle_type_counts == {'io': 128}

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/fields/field_info_container.py
--- a/yt/fields/field_info_container.py
+++ b/yt/fields/field_info_container.py
@@ -253,12 +253,17 @@
                 self[name] = DerivedField(name, f, **kwargs)
                 return f
             return create_function
-        ptype = kwargs.get("particle_type", False)
-        if ptype:
+
+        if isinstance(name, tuple):
+            self[name] = DerivedField(name, function, **kwargs)
+            return
+
+        if kwargs.get("particle_type", False):
             ftype = 'all'
         else:
             ftype = self.ds.default_fluid_type
-        if not isinstance(name, tuple) and (ftype, name) not in self:
+
+        if (ftype, name) not in self:
             tuple_name = (ftype, name)
             self[tuple_name] = DerivedField(tuple_name, function, **kwargs)
             self.alias(name, tuple_name)

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/fields/tests/test_field_access.py
--- /dev/null
+++ b/yt/fields/tests/test_field_access.py
@@ -0,0 +1,40 @@
+from yt.testing import fake_random_ds, assert_equal
+from yt.data_objects.profiles import create_profile
+from yt.visualization.plot_window import \
+    SlicePlot, \
+    ProjectionPlot, \
+    OffAxisProjectionPlot
+from yt.visualization.profile_plotter import \
+    ProfilePlot, \
+    PhasePlot
+
+def test_field_access():
+    ds = fake_random_ds(16)
+
+    ad = ds.all_data()
+    sp = ds.sphere(ds.domain_center, 0.25)
+    cg = ds.covering_grid(0, ds.domain_left_edge, ds.domain_dimensions)
+    scg = ds.smoothed_covering_grid(0, ds.domain_left_edge, ds.domain_dimensions)
+    sl = ds.slice(0, ds.domain_center[0])
+    proj = ds.proj('density', 0)
+    prof = create_profile(ad, 'radius', 'density')
+
+    for data_object in [ad, sp, cg, scg, sl, proj, prof]:
+        assert_equal(
+            data_object['gas', 'density'],
+            data_object[ds.fields.gas.density]
+        )
+
+    for field in [('gas', 'density'), ds.fields.gas.density]:
+        ad = ds.all_data()
+        prof = ProfilePlot(ad, 'radius', field)
+        phase = PhasePlot(ad, 'radius', field, 'cell_mass')
+        s = SlicePlot(ds, 2, field)
+        oas = SlicePlot(ds, [1, 1, 1], field)
+        p = ProjectionPlot(ds, 2, field)
+        oap = OffAxisProjectionPlot(ds, [1, 1, 1], field)
+
+        for plot_object in [s, oas, p, oap, prof, phase]:
+            plot_object._setup_plots()
+            if hasattr(plot_object, '_frb'):
+                plot_object._frb[field]

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/api.py
--- a/yt/frontends/api.py
+++ b/yt/frontends/api.py
@@ -31,6 +31,7 @@
     'gadget_fof',
     'gamer',
     'gdf',
+    'gizmo',
     'halo_catalog',
     'http_stream',
     'moab',

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/art/data_structures.py
--- a/yt/frontends/art/data_structures.py
+++ b/yt/frontends/art/data_structures.py
@@ -337,6 +337,8 @@
             mylog.info("Discovered %i species of particles", len(ls_nonzero))
             mylog.info("Particle populations: "+'%9i '*len(ls_nonzero),
                        *ls_nonzero)
+            self._particle_type_counts = dict(
+                zip(self.particle_types_raw, ls_nonzero))
             for k, v in particle_header_vals.items():
                 if k in self.parameters.keys():
                     if not self.parameters[k] == v:

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/art/tests/test_outputs.py
--- a/yt/frontends/art/tests/test_outputs.py
+++ b/yt/frontends/art/tests/test_outputs.py
@@ -69,6 +69,14 @@
                          ad[('specie2', 'particle_type')].size +
                          ad[('specie3', 'particle_type')].size), AnaNDM
 
+    for spnum in range(5):
+        npart_read = ad['specie%s' % spnum, 'particle_type'].size
+        npart_header = ds.particle_type_counts['specie%s' % spnum]
+        if spnum == 3:
+            # see issue 814
+            npart_read += 1
+        assert_equal(npart_read, npart_header)
+
     AnaBoxSize = YTQuantity(7.1442196564, 'Mpc')
     AnaVolume = YTQuantity(364.640074656, 'Mpc**3')
     Volume = 1

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/artio/data_structures.py
--- a/yt/frontends/artio/data_structures.py
+++ b/yt/frontends/artio/data_structures.py
@@ -182,6 +182,17 @@
         return (self.dataset.domain_width /
                 (self.dataset.domain_dimensions * 2**(self.max_level))).min()
 
+    def _get_particle_type_counts(self):
+        # this could be done in the artio C interface without creating temporary
+        # arrays but I don't want to touch that code
+        # if a future brave soul wants to try, take a look at
+        # `read_sfc_particles` in _artio_caller.pyx
+        result = {}
+        ad = self.ds.all_data()
+        for ptype in self.ds.particle_types_raw:
+            result[ptype] = ad[ptype, 'PID'].size
+        return result
+
     def convert(self, unit):
         return self.dataset.conversion_factors[unit]
 

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/artio/tests/test_outputs.py
--- a/yt/frontends/artio/tests/test_outputs.py
+++ b/yt/frontends/artio/tests/test_outputs.py
@@ -48,6 +48,7 @@
         s1 = dobj["ones"].sum()
         s2 = sum(mask.sum() for block, mask in dobj.blocks)
         yield assert_equal, s1, s2
+    assert_equal(ds.particle_type_counts, {'N-BODY': 100000, 'STAR': 110650})
 
 
 @requires_file(sizmbhloz)

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/enzo/data_structures.py
--- a/yt/frontends/enzo/data_structures.py
+++ b/yt/frontends/enzo/data_structures.py
@@ -444,6 +444,16 @@
             if "AppendActiveParticleType" in self.dataset.parameters:
                 ap_fields = self._detect_active_particle_fields()
                 field_list = list(set(field_list).union(ap_fields))
+                if not any(f[0] == 'io' for f in field_list):
+                    if 'io' in self.dataset.particle_types_raw:
+                        ptypes_raw = list(self.dataset.particle_types_raw)
+                        ptypes_raw.remove('io')
+                        self.dataset.particle_types_raw = tuple(ptypes_raw)
+
+                    if 'io' in self.dataset.particle_types:
+                        ptypes = list(self.dataset.particle_types)
+                        ptypes.remove('io')
+                        self.dataset.particle_types = tuple(ptypes)
             ptypes = self.dataset.particle_types
             ptypes_raw = self.dataset.particle_types_raw
         else:
@@ -473,6 +483,15 @@
             random_sample = np.mgrid[0:max(len(self.grids),1)].astype("int32")
         return self.grids[(random_sample,)]
 
+    def _get_particle_type_counts(self):
+        try:
+            ret = {}
+            for ptype in self.grid_active_particle_count:
+                ret[ptype] = self.grid_active_particle_count[ptype].sum()
+            return ret
+        except AttributeError:
+            return super(EnzoHierarchy, self)._get_particle_type_counts()
+
     def find_particles_by_type(self, ptype, max_num=None, additional_fields=None):
         """
         Returns a structure of arrays with all of the particles'

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/enzo/tests/test_outputs.py
--- a/yt/frontends/enzo/tests/test_outputs.py
+++ b/yt/frontends/enzo/tests/test_outputs.py
@@ -83,6 +83,7 @@
     for test in big_patch_amr(ds, _fields):
         test_galaxy0030.__name__ = test.description
         yield test
+    assert_equal(ds.particle_type_counts, {'io': 1124453})
 
 @requires_ds(enzotiny)
 def test_simulated_halo_mass_function():
@@ -126,7 +127,9 @@
 def test_active_particle_datasets():
     two_sph = data_dir_load(two_sphere_test)
     assert 'AccretingParticle' in two_sph.particle_types_raw
-    assert_equal(len(two_sph.particle_unions), 0)
+    assert 'io' not in two_sph.particle_types_raw
+    assert 'all' in two_sph.particle_types
+    assert_equal(len(two_sph.particle_unions), 1)
     pfields = ['GridID', 'creation_time', 'dynamical_time',
                'identifier', 'level', 'metallicity', 'particle_mass']
     pfields += ['particle_position_%s' % d for d in 'xyz']
@@ -150,3 +153,6 @@
         [f for f in apcos.field_list if f[0] == 'CenOstriker'])
 
     assert_equal(apcos_fields, real_apcos_fields)
+
+    assert_equal(apcos.particle_type_counts,
+                 {'CenOstriker': 899755, 'DarkMatter': 32768})

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/exodus_ii/data_structures.py
--- a/yt/frontends/exodus_ii/data_structures.py
+++ b/yt/frontends/exodus_ii/data_structures.py
@@ -154,7 +154,8 @@
                                               units_override=units_override)
         self.index_filename = filename
         self.storage_filename = storage_filename
-        self.default_field = ("connect1", "diffused")
+        self.default_field = [f for f in self.field_list 
+                              if f[0] == 'connect1'][-1]
 
     def _set_code_unit_attributes(self):
         # This is where quantities are created that represent the various

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/flash/api.py
--- a/yt/frontends/flash/api.py
+++ b/yt/frontends/flash/api.py
@@ -16,12 +16,14 @@
 from .data_structures import \
       FLASHGrid, \
       FLASHHierarchy, \
-      FLASHDataset
+      FLASHDataset, \
+      FLASHParticleDataset
 
 from .fields import \
       FLASHFieldInfo
 
 from .io import \
-      IOHandlerFLASH
+      IOHandlerFLASH, \
+      IOHandlerFLASHParticle
 
 from . import tests

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/flash/data_structures.py
--- a/yt/frontends/flash/data_structures.py
+++ b/yt/frontends/flash/data_structures.py
@@ -18,13 +18,15 @@
 import numpy as np
 import weakref
 
-from yt.funcs import mylog
 from yt.data_objects.grid_patch import \
     AMRGridPatch
+from yt.data_objects.static_output import \
+    Dataset, ParticleFile
+from yt.funcs import mylog
 from yt.geometry.grid_geometry_handler import \
     GridIndex
-from yt.data_objects.static_output import \
-    Dataset
+from yt.geometry.particle_geometry_handler import \
+    ParticleIndex
 from yt.utilities.file_handler import \
     HDF5FileHandler
 from yt.utilities.physical_ratios import cm_per_mpc
@@ -251,8 +253,6 @@
         self.time_unit = self.quan(1.0, "s")
         self.velocity_unit = self.quan(1.0, "cm/s")
         self.temperature_unit = self.quan(temperature_factor, "K")
-        # Still need to deal with:
-        #self.conversion_factors['temp'] = (1.0 + self.current_redshift)**-2.0
         self.unit_registry.modify("code_magnetic", self.magnetic_unit)
         
     def set_code_units(self):
@@ -437,3 +437,52 @@
 
     def close(self):
         self._handle.close()
+
+class FLASHParticleFile(ParticleFile):
+    pass
+
+class FLASHParticleDataset(FLASHDataset):
+    _index_class = ParticleIndex
+    over_refine_factor = 1
+    filter_bbox = False
+    _file_class = FLASHParticleFile
+
+    def __init__(self, filename, dataset_type='flash_particle_hdf5',
+                 storage_filename = None,
+                 units_override = None,
+                 n_ref = 64, unit_system = "cgs"):
+
+        if self._handle is not None: return
+        self._handle = HDF5FileHandler(filename)
+        self.n_ref = n_ref
+        self.refine_by = 2
+        Dataset.__init__(self, filename, dataset_type, units_override=units_override,
+                         unit_system=unit_system)
+        self.storage_filename = storage_filename
+
+    def _parse_parameter_file(self):
+        # Let the superclass do all the work but then
+        # fix the domain dimensions
+        super(FLASHParticleDataset, self)._parse_parameter_file()
+        nz = 1 << self.over_refine_factor
+        self.domain_dimensions = np.zeros(3, "int32")
+        self.domain_dimensions[:self.dimensionality] = nz
+        self.filename_template = self.parameter_filename
+        self.file_count = 1
+
+    @classmethod
+    def _is_valid(self, *args, **kwargs):
+        try:
+            fileh = HDF5FileHandler(args[0])
+            if "bounding box" not in fileh["/"].keys() \
+                and "localnp" in fileh["/"].keys():
+                return True
+        except IOError:
+            pass
+        return False
+
+    @classmethod
+    def _guess_candidates(cls, base, directories, files):
+        candidates = [_ for _ in files if "_hdf5_part_" in _]
+        # Typically, Flash won't have nested outputs.
+        return candidates, (len(candidates) == 0)

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/flash/io.py
--- a/yt/frontends/flash/io.py
+++ b/yt/frontends/flash/io.py
@@ -20,6 +20,8 @@
     BaseIOHandler
 from yt.utilities.logger import ytLogger as mylog
 from yt.geometry.selection_routines import AlwaysSelector
+from yt.utilities.lib.geometry_utils import \
+    compute_morton
 
 # http://stackoverflow.com/questions/2361945/detecting-consecutive-integers-in-a-list
 def particle_sequences(grids):
@@ -34,6 +36,16 @@
         seq = list(v[1] for v in g)
         yield seq
 
+def determine_particle_fields(handle):
+    try:
+        particle_fields = [s[0].decode("ascii","ignore").strip()
+                           for s in handle["/particle names"][:]]
+        _particle_fields = dict([("particle_" + s, i) for i, s in
+                                 enumerate(particle_fields)])
+    except KeyError:
+        _particle_fields = {}
+    return _particle_fields
+
 class IOHandlerFLASH(BaseIOHandler):
     _particle_reader = False
     _dataset_type = "flash_hdf5"
@@ -43,15 +55,7 @@
         # Now we cache the particle fields
         self._handle = ds._handle
         self._particle_handle = ds._particle_handle
-        
-        try :
-            particle_fields = [s[0].decode("ascii","ignore").strip()
-                               for s in
-                               self._particle_handle["/particle names"][:]]
-            self._particle_fields = dict([("particle_" + s, i) for i, s in
-                                          enumerate(particle_fields)])
-        except KeyError:
-            self._particle_fields = {}
+        self._particle_fields = determine_particle_fields(self._particle_handle)
 
     def _read_particles(self, fields_to_read, type, args, grid_list,
             count_list, conv_factors):
@@ -154,3 +158,96 @@
                     rv[g.id][field] = np.asarray(data[...,i], "=f8")
         return rv
 
+class IOHandlerFLASHParticle(BaseIOHandler):
+    _particle_reader = True
+    _dataset_type = "flash_particle_hdf5"
+
+    def __init__(self, ds):
+        super(IOHandlerFLASHParticle, self).__init__(ds)
+        # Now we cache the particle fields
+        self._handle = ds._handle
+        self._particle_fields = determine_particle_fields(self._handle)
+        self._position_fields = [self._particle_fields["particle_pos%s" % ax]
+                                 for ax in 'xyz']
+        self._chunksize = 32**3
+
+    def _read_fluid_selection(self, chunks, selector, fields, size):
+        raise NotImplementedError
+
+    def _read_particle_coords(self, chunks, ptf):
+        chunks = list(chunks)
+        data_files = set([])
+        assert(len(ptf) == 1)
+        for chunk in chunks:
+            for obj in chunk.objs:
+                data_files.update(obj.data_files)
+        px, py, pz = self._position_fields
+        p_fields = self._handle["/tracer particles"]
+        assert(len(data_files) == 1)
+        for data_file in sorted(data_files):
+            pcount = self._count_particles(data_file)["io"]
+            for ptype, field_list in sorted(ptf.items()):
+                total = 0
+                while total < pcount:
+                    count = min(self._chunksize, pcount - total)
+                    x = np.asarray(p_fields[total:total+count, px], dtype="=f8")
+                    y = np.asarray(p_fields[total:total+count, py], dtype="=f8")
+                    z = np.asarray(p_fields[total:total+count, pz], dtype="=f8")
+                    total += count
+                    yield ptype, (x, y, z)
+
+    def _read_particle_fields(self, chunks, ptf, selector):
+        chunks = list(chunks)
+        data_files = set([])
+        assert(len(ptf) == 1)
+        for chunk in chunks:
+            for obj in chunk.objs:
+                data_files.update(obj.data_files)
+        px, py, pz = self._position_fields
+        p_fields = self._handle["/tracer particles"]
+        assert(len(data_files) == 1)
+        for data_file in sorted(data_files):
+            pcount = self._count_particles(data_file)["io"]
+            for ptype, field_list in sorted(ptf.items()):
+                total = 0
+                while total < pcount:
+                    count = min(self._chunksize, pcount - total)
+                    x = np.asarray(p_fields[total:total+count, px], dtype="=f8")
+                    y = np.asarray(p_fields[total:total+count, py], dtype="=f8")
+                    z = np.asarray(p_fields[total:total+count, pz], dtype="=f8")
+                    total += count
+                    mask = selector.select_points(x, y, z, 0.0)
+                    del x, y, z
+                    if mask is None: continue
+                    for field in field_list:
+                        fi = self._particle_fields[field]
+                        data = p_fields[total-count:total, fi]
+                        yield (ptype, field), data[mask]
+
+    def _initialize_index(self, data_file, regions):
+        p_fields = self._handle["/tracer particles"]
+        px, py, pz = self._position_fields
+        pcount = self._count_particles(data_file)["io"]
+        morton = np.empty(pcount, dtype='uint64')
+        ind = 0
+        while ind < pcount:
+            npart = min(self._chunksize, pcount - ind)
+            pos = np.empty((npart, 3), dtype="=f8")
+            pos[:,0] = p_fields[ind:ind+npart, px]
+            pos[:,1] = p_fields[ind:ind+npart, py]
+            pos[:,2] = p_fields[ind:ind+npart, pz]
+            regions.add_data_file(pos, data_file.file_id)
+            morton[ind:ind+npart] = \
+                compute_morton(pos[:,0], pos[:,1], pos[:,2],
+                               data_file.ds.domain_left_edge,
+                               data_file.ds.domain_right_edge)
+            ind += self._chunksize
+        return morton
+
+    def _count_particles(self, data_file):
+        pcount = {"io": self._handle["/localnp"][:].sum()}
+        return pcount
+
+    def _identify_fields(self, data_file):
+        fields = [("io", field) for field in self._particle_fields]
+        return fields, {}

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/flash/tests/test_outputs.py
--- a/yt/frontends/flash/tests/test_outputs.py
+++ b/yt/frontends/flash/tests/test_outputs.py
@@ -20,8 +20,11 @@
 from yt.utilities.answer_testing.framework import \
     requires_ds, \
     small_patch_amr, \
-    data_dir_load
-from yt.frontends.flash.api import FLASHDataset
+    data_dir_load, \
+    sph_answer
+from yt.frontends.flash.api import FLASHDataset, \
+    FLASHParticleDataset
+from collections import OrderedDict
 
 _fields = ("temperature", "density", "velocity_magnitude")
 
@@ -45,7 +48,6 @@
         test_wind_tunnel.__name__ = test.description
         yield test
 
-
 @requires_file(wt)
 def test_FLASHDataset():
     assert isinstance(data_dir_load(wt), FLASHDataset)
@@ -54,3 +56,28 @@
 def test_units_override():
     for test in units_override_check(sloshing):
         yield test
+
+fid_1to3_b1 = "fiducial_1to3_b1/fiducial_1to3_b1_hdf5_part_0080"
+
+fid_1to3_b1_fields = OrderedDict(
+    [
+        (("deposit", "all_density"), None),
+        (("deposit", "all_count"), None),
+        (("deposit", "all_cic"), None),
+        (("deposit", "all_cic_velocity_x"), ("deposit", "all_cic")),
+        (("deposit", "all_cic_velocity_y"), ("deposit", "all_cic")),
+        (("deposit", "all_cic_velocity_z"), ("deposit", "all_cic")),
+    ]
+)
+
+
+ at requires_file(fid_1to3_b1)
+def test_FLASHParticleDataset():
+    assert isinstance(data_dir_load(fid_1to3_b1), FLASHParticleDataset)
+
+ at requires_ds(fid_1to3_b1, big_data=True)
+def test_fid_1to3_b1():
+    ds = data_dir_load(fid_1to3_b1)
+    for test in sph_answer(ds, 'fiducial_1to3_b1_hdf5_part_0080', 6684119, fid_1to3_b1_fields):
+        test_fid_1to3_b1.__name__ = test.description
+        yield test

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/gizmo/api.py
--- /dev/null
+++ b/yt/frontends/gizmo/api.py
@@ -0,0 +1,21 @@
+"""
+API for Gizmo frontend.
+
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2016, 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.
+#-----------------------------------------------------------------------------
+
+from .data_structures import \
+    GizmoDataset
+
+from .fields import \
+    GizmoFieldInfo

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/gizmo/data_structures.py
--- /dev/null
+++ b/yt/frontends/gizmo/data_structures.py
@@ -0,0 +1,44 @@
+"""
+Data structures for Gizmo frontend.
+
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2016, 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.
+#-----------------------------------------------------------------------------
+
+from yt.utilities.on_demand_imports import _h5py as h5py
+
+from yt.frontends.gadget.data_structures import \
+    GadgetHDF5Dataset
+
+from .fields import \
+    GizmoFieldInfo
+
+class GizmoDataset(GadgetHDF5Dataset):
+    _field_info_class = GizmoFieldInfo
+
+    @classmethod
+    def _is_valid(self, *args, **kwargs):
+        need_groups = ['Header']
+        veto_groups = ['FOF', 'Group', 'Subhalo']
+        valid = True
+        try:
+            fh = h5py.File(args[0], mode='r')
+            valid = all(ng in fh["/"] for ng in need_groups) and \
+              not any(vg in fh["/"] for vg in veto_groups)
+            dmetal = "/PartType0/Metallicity"
+            if dmetal not in fh or fh[dmetal].shape[1] not in (11, 17):
+                valid = False
+            fh.close()
+        except:
+            valid = False
+            pass
+        return valid

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/gizmo/fields.py
--- /dev/null
+++ b/yt/frontends/gizmo/fields.py
@@ -0,0 +1,116 @@
+"""
+Gizmo-specific fields
+
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2016, 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.
+#-----------------------------------------------------------------------------
+
+from yt.fields.particle_fields import \
+    add_volume_weighted_smoothed_field
+from yt.fields.species_fields import \
+    add_species_field_by_density
+from yt.frontends.gadget.fields import \
+    GadgetFieldInfo
+from yt.frontends.sph.fields import \
+    SPHFieldInfo
+
+class GizmoFieldInfo(GadgetFieldInfo):
+    known_particle_fields = (
+        ("Mass", ("code_mass", ["particle_mass"], None)),
+        ("Masses", ("code_mass", ["particle_mass"], None)),
+        ("Coordinates", ("code_length", ["particle_position"], None)),
+        ("Velocity", ("code_velocity", ["particle_velocity"], None)),
+        ("Velocities", ("code_velocity", ["particle_velocity"], None)),
+        ("ParticleIDs", ("", ["particle_index"], None)),
+        ("InternalEnergy", ("code_velocity ** 2", ["thermal_energy"], None)),
+        ("SmoothingLength", ("code_length", ["smoothing_length"], None)),
+        ("Density", ("code_mass / code_length**3", ["density"], None)),
+        ("MaximumTemperature", ("K", [], None)),
+        ("Temperature", ("K", ["temperature"], None)),
+        ("Epsilon", ("code_length", [], None)),
+        ("Metals", ("code_metallicity", ["metallicity"], None)),
+        ("Metallicity", ("code_metallicity", ["metallicity"], None)),
+        ("Phi", ("code_length", [], None)),
+        ("StarFormationRate", ("Msun / yr", [], None)),
+        ("FormationTime", ("code_time", ["creation_time"], None)),
+        ("Metallicity_00", ("", ["metallicity"], None)),
+        ("Metallicity_01", ("", ["He_metallicity"], None)),
+        ("Metallicity_02", ("", ["C_metallicity"], None)),
+        ("Metallicity_03", ("", ["N_metallicity"], None)),
+        ("Metallicity_04", ("", ["O_metallicity"], None)),
+        ("Metallicity_05", ("", ["Ne_metallicity"], None)),
+        ("Metallicity_06", ("", ["Mg_metallicity"], None)),
+        ("Metallicity_07", ("", ["Si_metallicity"], None)),
+        ("Metallicity_08", ("", ["S_metallicity"], None)),
+        ("Metallicity_09", ("", ["Ca_metallicity"], None)),
+        ("Metallicity_10", ("", ["Fe_metallicity"], None)),
+    )
+
+    def __init__(self, *args, **kwargs):
+        super(SPHFieldInfo, self).__init__(*args, **kwargs)
+        if ("PartType0", "Metallicity_00") in self.field_list:
+            self.nuclei_names = ["He", "C", "N", "O", "Ne", "Mg", "Si", "S",
+                                 "Ca", "Fe"]
+
+    def setup_gas_particle_fields(self, ptype):
+        super(GizmoFieldInfo, self).setup_gas_particle_fields(ptype)
+        self.alias((ptype, "temperature"), (ptype, "Temperature"))
+
+        def _h_density(field, data):
+            x_H = 1.0 - data[(ptype, "He_metallicity")] - \
+              data[(ptype, "metallicity")]
+            return x_H * data[(ptype, "density")] * \
+              data[(ptype, "NeutralHydrogenAbundance")]
+
+        self.add_field(
+            (ptype, "H_density"),
+            function=_h_density,
+            particle_type=True,
+            units=self.ds.unit_system["density"])
+        add_species_field_by_density(self, ptype, "H", particle_type=True)
+        for suffix in ["density", "fraction", "mass", "number_density"]:
+            self.alias((ptype, "H_p0_%s" % suffix), (ptype, "H_%s" % suffix))
+
+        def _h_p1_density(field, data):
+            x_H = 1.0 - data[(ptype, "He_metallicity")] - \
+              data[(ptype, "metallicity")]
+            return x_H * data[(ptype, "density")] * \
+              (1.0 - data[(ptype, "NeutralHydrogenAbundance")])
+
+        self.add_field(
+            (ptype, "H_p1_density"),
+            function=_h_p1_density,
+            particle_type=True,
+            units=self.ds.unit_system["density"])
+        add_species_field_by_density(self, ptype, "H_p1", particle_type=True)
+
+        def _nuclei_mass_density_field(field, data):
+            species = field.name[1][:field.name[1].find("_")]
+            return data[ptype, "density"] * \
+              data[ptype, "%s_metallicity" % species]
+
+        num_neighbors = 64
+        for species in self.nuclei_names:
+            self.add_field(
+                (ptype, "%s_nuclei_mass_density" % species),
+                function=_nuclei_mass_density_field,
+                particle_type=True,
+                units=self.ds.unit_system["density"])
+
+            for suf in ["_nuclei_mass_density", "_metallicity"]:
+                field = "%s%s" % (species, suf)
+                fn = add_volume_weighted_smoothed_field(
+                    ptype, "particle_position", "particle_mass",
+                    "smoothing_length", "density", field,
+                    self, num_neighbors)
+
+                self.alias(("gas", field), fn[0])

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/gizmo/tests/test_outputs.py
--- /dev/null
+++ b/yt/frontends/gizmo/tests/test_outputs.py
@@ -0,0 +1,46 @@
+"""
+Gizmo frontend tests
+
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2015, 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.
+#-----------------------------------------------------------------------------
+
+from collections import OrderedDict
+
+from yt.utilities.answer_testing.framework import \
+    data_dir_load, \
+    requires_ds, \
+    sph_answer
+from yt.frontends.gizmo.api import GizmoDataset
+
+FIRE_m12i = 'FIRE_M12i_ref11/snapshot_600.hdf5'
+
+# This maps from field names to weight field names to use for projections
+fields = OrderedDict(
+    [
+        (("gas", "density"), None),
+        (("gas", "temperature"), ('gas', 'density')),
+        (("gas", "metallicity"), ('gas', 'density')),
+        (("gas", "O_metallicity"), ('gas', 'density')),
+        (('gas', 'velocity_magnitude'), None),
+        (("deposit", "all_count"), None),
+        (("deposit", "all_cic"), None),
+    ]
+)
+
+ at requires_ds(FIRE_m12i)
+def test_GizmoDataset():
+    ds = data_dir_load(FIRE_m12i)
+    assert isinstance(ds, GizmoDataset)
+    for test in sph_answer(ds, 'snapshot_600', 4786950, fields):
+        test_GizmoDataset.__name__ = test.description
+        yield test

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/ramses/data_structures.py
--- a/yt/frontends/ramses/data_structures.py
+++ b/yt/frontends/ramses/data_structures.py
@@ -474,6 +474,13 @@
         for level in range(self.max_level+1):
             self.level_stats[level+self.dataset.min_level+1]['numcells'] = levels[level]
 
+    def _get_particle_type_counts(self):
+        npart = 0
+        for dom in self.domains:
+            npart += dom.local_particle_count
+
+        return {'io': npart}
+
     def print_stats(self):
         
         # This function prints information based on the fluid on the grids,

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/ramses/tests/test_outputs.py
--- a/yt/frontends/ramses/tests/test_outputs.py
+++ b/yt/frontends/ramses/tests/test_outputs.py
@@ -47,7 +47,7 @@
         s1 = dobj["ones"].sum()
         s2 = sum(mask.sum() for block, mask in dobj.blocks)
         yield assert_equal, s1, s2
-
+    assert_equal(ds.particle_type_counts, {'io': 1090895})
 
 @requires_file(output_00080)
 def test_RAMSESDataset():

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/sph/fields.py
--- a/yt/frontends/sph/fields.py
+++ b/yt/frontends/sph/fields.py
@@ -41,27 +41,9 @@
         ("Phi", ("code_length", [], None)),
         ("StarFormationRate", ("Msun / yr", [], None)),
         ("FormationTime", ("code_time", ["creation_time"], None)),
-        # These are metallicity fields that get discovered for FIRE simulations
         ("Metallicity_00", ("", ["metallicity"], None)),
-        ("Metallicity_01", ("", ["He_fraction"], None)),
-        ("Metallicity_02", ("", ["C_fraction"], None)),
-        ("Metallicity_03", ("", ["N_fraction"], None)),
-        ("Metallicity_04", ("", ["O_fraction"], None)),
-        ("Metallicity_05", ("", ["Ne_fraction"], None)),
-        ("Metallicity_06", ("", ["Mg_fraction"], None)),
-        ("Metallicity_07", ("", ["Si_fraction"], None)),
-        ("Metallicity_08", ("", ["S_fraction"], None)),
-        ("Metallicity_09", ("", ["Ca_fraction"], None)),
-        ("Metallicity_10", ("", ["Fe_fraction"], None)),
     )
 
-    def __init__(self, *args, **kwargs):
-        super(SPHFieldInfo, self).__init__(*args, **kwargs)
-        # Special case for FIRE
-        if ("PartType0", "Metallicity_00") in self.field_list:
-            self.species_names += ["He", "C", "N", "O", "Ne", "Mg", "Si", "S",
-                "Ca", "Fe"]
-
     def setup_particle_fields(self, ptype, *args, **kwargs):
         super(SPHFieldInfo, self).setup_particle_fields(ptype, *args, **kwargs)
         setup_species_fields(self, ptype)

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/frontends/stream/data_structures.py
--- a/yt/frontends/stream/data_structures.py
+++ b/yt/frontends/stream/data_structures.py
@@ -1826,5 +1826,7 @@
 
     sds._node_fields = node_data[0].keys()
     sds._elem_fields = elem_data[0].keys()
+    sds.default_field = [f for f in sds.field_list 
+                         if f[0] == 'connect1'][-1]
 
     return sds

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/funcs.py
--- a/yt/funcs.py
+++ b/yt/funcs.py
@@ -318,7 +318,10 @@
         ipshell(header = __header % dd,
                 local_ns = loc, global_ns = glo)
     else:
-        from IPython.config.loader import Config
+        try:
+            from traitlets.config.loader import Config
+        except ImportError:
+            from IPython.config.loader import Config
         cfg = Config()
         cfg.InteractiveShellEmbed.local_ns = loc
         cfg.InteractiveShellEmbed.global_ns = glo

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/geometry/coordinates/cartesian_coordinates.py
--- a/yt/geometry/coordinates/cartesian_coordinates.py
+++ b/yt/geometry/coordinates/cartesian_coordinates.py
@@ -76,10 +76,15 @@
             field_data = ad[field]
             buff_size = size[0:dimension] + (1,) + size[dimension:]
 
+            ax = data_source.axis
+            xax = self.x_axis[ax]
+            yax = self.y_axis[ax]
             c = np.float64(data_source.center[dimension].d)
-            bounds.insert(2*dimension, c)
-            bounds.insert(2*dimension, c)
-            bounds = np.reshape(bounds, (3, 2))
+
+            extents = np.zeros((3, 2))
+            extents[ax] = np.array([c, c])
+            extents[xax] = bounds[0:2]
+            extents[yax] = bounds[2:4]
 
             # if this is an element field, promote to 2D here
             if len(field_data.shape) == 1:
@@ -101,13 +106,10 @@
 
             img = pixelize_element_mesh(coords,
                                         indices,
-                                        buff_size, field_data, bounds,
+                                        buff_size, field_data, extents,
                                         index_offset=offset)
 
             # re-order the array and squeeze out the dummy dim
-            ax = data_source.axis
-            xax = self.x_axis[ax]
-            yax = self.y_axis[ax]
             return np.squeeze(np.transpose(img, (yax, xax, ax)))
 
         elif dimension < 3:

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/geometry/geometry_handler.py
--- a/yt/geometry/geometry_handler.py
+++ b/yt/geometry/geometry_handler.py
@@ -57,10 +57,6 @@
         mylog.debug("Detecting fields.")
         self._detect_output_fields()
 
-    def __del__(self):
-        if self._data_file is not None:
-            self._data_file.close()
-
     def _initialize_state_variables(self):
         self._parallel_locking = False
         self._data_file = None
@@ -195,6 +191,10 @@
         except TypeError:
             return self._data_file[full_name]
 
+    def _get_particle_type_counts(self):
+        # this is implemented by subclasses
+        raise NotImplementedError
+
     def _close_data_file(self):
         if self._data_file:
             self._data_file.close()

diff -r eb557a1193ef88a5de49cc9e0b05da5aab0abf5d -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 yt/geometry/grid_geometry_handler.py
--- a/yt/geometry/grid_geometry_handler.py
+++ b/yt/geometry/grid_geometry_handler.py
@@ -119,6 +119,9 @@
         """
         return self.select_grids(self.grid_levels.max())[0].dds[:].min()
 
+    def _get_particle_type_counts(self):
+        return {self.ds.particle_types_raw[0]: self.grid_particle_count.sum()}
+
     def _initialize_level_stats(self):
         # Now some statistics:
         #   0 = number of grids

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

https://bitbucket.org/yt_analysis/yt/commits/c8ce44b9c15d/
Changeset:   c8ce44b9c15d
Branch:      yt
User:        atmyers
Date:        2016-06-03 22:27:26+00:00
Summary:     off axis pixelization needs to be transposed.
Affected #:  1 file

diff -r f529e9c9959b05833645d7b6b4fc890652ff8ef5 -r c8ce44b9c15d45c28fb0f1c911aea250dea23b9f yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -282,7 +282,7 @@
                     mask[i, j] += 1
                     my_array[i, j] += dsp
     my_array /= mask
-    return my_array
+    return my_array.T
 
 
 @cython.cdivision(True)


https://bitbucket.org/yt_analysis/yt/commits/f0fe3a0da29a/
Changeset:   f0fe3a0da29a
Branch:      yt
User:        atmyers
Date:        2016-06-03 23:45:07+00:00
Summary:     tighten tolerance slightly.
Affected #:  1 file

diff -r c8ce44b9c15d45c28fb0f1c911aea250dea23b9f -r f0fe3a0da29a65d112a118f10de35fd042c07e7a yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -275,9 +275,9 @@
                     cx = inv_mat[0,0]*cxpx + inv_mat[0,1]*cypx + center[0]
                     cy = inv_mat[1,0]*cxpx + inv_mat[1,1]*cypx + center[1]
                     cz = inv_mat[2,0]*cxpx + inv_mat[2,1]*cypx + center[2]
-                    if fabs(xsp - cx) * 0.95 > dxsp or \
-                       fabs(ysp - cy) * 0.95 > dysp or \
-                       fabs(zsp - cz) * 0.95 > dzsp:
+                    if fabs(xsp - cx) * 0.99 > dxsp or \
+                       fabs(ysp - cy) * 0.99 > dysp or \
+                       fabs(zsp - cz) * 0.99 > dzsp:
                         continue
                     mask[i, j] += 1
                     my_array[i, j] += dsp


https://bitbucket.org/yt_analysis/yt/commits/6b2328634230/
Changeset:   6b2328634230
Branch:      yt
User:        atmyers
Date:        2016-06-03 23:51:56+00:00
Summary:     add a test that makes sure on- and off-axis slice plots are consistent.
Affected #:  1 file

diff -r f0fe3a0da29a65d112a118f10de35fd042c07e7a -r 6b2328634230033a891d1460077865d853f01850 yt/visualization/tests/test_plotwindow.py
--- a/yt/visualization/tests/test_plotwindow.py
+++ b/yt/visualization/tests/test_plotwindow.py
@@ -13,6 +13,7 @@
 # The full license is in the file COPYING.txt, distributed with this software.
 #-----------------------------------------------------------------------------
 import itertools
+import numpy as np
 import os
 import tempfile
 import shutil
@@ -27,6 +28,7 @@
 from yt.visualization.api import \
     SlicePlot, ProjectionPlot, OffAxisSlicePlot, OffAxisProjectionPlot
 from yt.units.yt_array import YTArray, YTQuantity
+from yt.frontends.stream.api import load_uniform_grid
 from collections import OrderedDict
 
 def setup():
@@ -400,3 +402,20 @@
         [assert_array_almost_equal(py, y, 14) for py, y in zip(plot.ylim, ylim)]
         [assert_array_almost_equal(pw, w, 14) for pw, w in zip(plot.width, pwidth)]
         assert_true(aun == plot._axes_unit_names)
+
+def test_on_off_compare():
+    # fake density field that varies in the x-direction only
+    den = np.arange(32**3) / 32**2 + 1
+    den = den.reshape(32, 32, 32)
+    den = np.array(den, dtype=np.float64)
+    data = dict(density = (den, "g/cm**3"))
+    bbox = np.array([[-1.5, 1.5], [-1.5, 1.5], [-1.5, 1.5]])
+    ds = load_uniform_grid(data, den.shape, length_unit="Mpc", bbox=bbox, nprocs=64)
+
+    sl_on = SlicePlot(ds, "z", ["density"])
+
+    L = [0, 0, 1]
+    north_vector = [0, 1, 0]
+    sl_off = OffAxisSlicePlot(ds, L, 'density', center=[0,0,0], north_vector=north_vector)
+
+    assert_array_almost_equal(sl_on.frb['density'], sl_off.frb['density'])


https://bitbucket.org/yt_analysis/yt/commits/db3fc359aa9e/
Changeset:   db3fc359aa9e
Branch:      yt
User:        atmyers
Date:        2016-06-15 18:24:00+00:00
Summary:     merging with tip
Affected #:  4 files

diff -r bb67af7434bfd0dba16114e1674c03401361dcce -r db3fc359aa9e52857f06c9de332d0442646e69c2 yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -275,14 +275,14 @@
                     cx = inv_mat[0,0]*cxpx + inv_mat[0,1]*cypx + center[0]
                     cy = inv_mat[1,0]*cxpx + inv_mat[1,1]*cypx + center[1]
                     cz = inv_mat[2,0]*cxpx + inv_mat[2,1]*cypx + center[2]
-                    if fabs(xsp - cx) * 0.95 > dxsp or \
-                       fabs(ysp - cy) * 0.95 > dysp or \
-                       fabs(zsp - cz) * 0.95 > dzsp:
+                    if fabs(xsp - cx) * 0.99 > dxsp or \
+                       fabs(ysp - cy) * 0.99 > dysp or \
+                       fabs(zsp - cz) * 0.99 > dzsp:
                         continue
                     mask[i, j] += 1
                     my_array[i, j] += dsp
     my_array /= mask
-    return my_array
+    return my_array.T
 
 
 @cython.cdivision(True)

diff -r bb67af7434bfd0dba16114e1674c03401361dcce -r db3fc359aa9e52857f06c9de332d0442646e69c2 yt/visualization/plot_modifications.py
--- a/yt/visualization/plot_modifications.py
+++ b/yt/visualization/plot_modifications.py
@@ -273,8 +273,7 @@
     True, the velocity fields will be scaled by their local
     (in-plane) length, allowing morphological features to be more
     clearly seen for fields with substantial variation in field
-    strength (normalize is not implemented and thus ignored for
-    Cutting Planes).
+    strength.
     """
     _type_name = "velocity"
     _supported_geometries = ("cartesian", "spectral_cube")
@@ -290,7 +289,9 @@
         if plot._type_name == "CuttingPlane":
             qcb = CuttingQuiverCallback("cutting_plane_velocity_x",
                                         "cutting_plane_velocity_y",
-                                        self.factor)
+                                        self.factor, scale=self.scale,
+                                        normalize=self.normalize,
+                                        scale_units=self.scale_units)
         else:
             ax = plot.data.axis
             (xi, yi) = (plot.data.ds.coordinates.x_axis[ax],
@@ -336,7 +337,9 @@
         if plot._type_name == "CuttingPlane":
             qcb = CuttingQuiverCallback("cutting_plane_magnetic_field_x",
                                         "cutting_plane_magnetic_field_y",
-                                        self.factor)
+                                        self.factor, scale=self.scale, 
+                                        scale_units=self.scale_units, 
+                                        normalize=self.normalize)
         else:
             xax = plot.data.ds.coordinates.x_axis[plot.data.axis]
             yax = plot.data.ds.coordinates.y_axis[plot.data.axis]
@@ -855,11 +858,16 @@
     """
     _type_name = "cquiver"
     _supported_geometries = ("cartesian", "spectral_cube")
-    def __init__(self, field_x, field_y, factor):
+
+    def __init__(self, field_x, field_y, factor=16, scale=None,
+                 scale_units=None, normalize=None):
         PlotCallback.__init__(self)
         self.field_x = field_x
         self.field_y = field_y
         self.factor = factor
+        self.scale = scale
+        self.scale_units = scale_units
+        self.normalize = normalize
 
     def __call__(self, plot):
         x0, x1 = plot.xlim
@@ -870,6 +878,7 @@
         nx = plot.image._A.shape[0] / self.factor
         ny = plot.image._A.shape[1] / self.factor
         indices = np.argsort(plot.data['dx'])[::-1]
+
         pixX = pixelize_off_axis_cartesian(
                                plot.data['x'], plot.data['y'], plot.data['z'],
                                plot.data['px'], plot.data['py'],
@@ -877,7 +886,7 @@
                                plot.data.center, plot.data._inv_mat, indices,
                                plot.data[self.field_x],
                                int(nx), int(ny),
-                               (x0, x1, y0, y1),).transpose()
+                               (x0, x1, y0, y1)).transpose()
         pixY = pixelize_off_axis_cartesian(
                                plot.data['x'], plot.data['y'], plot.data['z'],
                                plot.data['px'], plot.data['py'],
@@ -885,10 +894,16 @@
                                plot.data.center, plot.data._inv_mat, indices,
                                plot.data[self.field_y],
                                int(nx), int(ny),
-                               (x0, x1, y0, y1),).transpose()
+                               (x0, x1, y0, y1)).transpose()
         X,Y = np.meshgrid(np.linspace(xx0,xx1,nx,endpoint=True),
                           np.linspace(yy0,yy1,ny,endpoint=True))
-        plot._axes.quiver(X,Y, pixX, pixY)
+
+        if self.normalize:
+            nn = np.sqrt(pixX**2 + pixY**2)
+            pixX /= nn
+            pixY /= nn
+
+        plot._axes.quiver(X,Y, pixX, pixY, scale=self.scale, scale_units=self.scale_units)
         plot._axes.set_xlim(xx0,xx1)
         plot._axes.set_ylim(yy0,yy1)
         plot._axes.hold(False)

diff -r bb67af7434bfd0dba16114e1674c03401361dcce -r db3fc359aa9e52857f06c9de332d0442646e69c2 yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -103,7 +103,7 @@
         mat = np.transpose(np.column_stack((perp1,perp2,normal)))
         center = np.dot(mat,center)
 
-    w = tuple(el.in_units('unitary') for el in width)
+    w = tuple(el.in_units('code_length') for el in width)
     bounds = tuple(((2*(i % 2))-1)*w[i//2]/2 for i in range(len(w)*2))
 
     return (bounds, center)

diff -r bb67af7434bfd0dba16114e1674c03401361dcce -r db3fc359aa9e52857f06c9de332d0442646e69c2 yt/visualization/tests/test_plotwindow.py
--- a/yt/visualization/tests/test_plotwindow.py
+++ b/yt/visualization/tests/test_plotwindow.py
@@ -13,6 +13,7 @@
 # The full license is in the file COPYING.txt, distributed with this software.
 #-----------------------------------------------------------------------------
 import itertools
+import numpy as np
 import os
 import tempfile
 import shutil
@@ -27,6 +28,7 @@
 from yt.visualization.api import \
     SlicePlot, ProjectionPlot, OffAxisSlicePlot, OffAxisProjectionPlot
 from yt.units.yt_array import YTArray, YTQuantity
+from yt.frontends.stream.api import load_uniform_grid
 from collections import OrderedDict
 
 def setup():
@@ -400,3 +402,20 @@
         [assert_array_almost_equal(py, y, 14) for py, y in zip(plot.ylim, ylim)]
         [assert_array_almost_equal(pw, w, 14) for pw, w in zip(plot.width, pwidth)]
         assert_true(aun == plot._axes_unit_names)
+
+def test_on_off_compare():
+    # fake density field that varies in the x-direction only
+    den = np.arange(32**3) / 32**2 + 1
+    den = den.reshape(32, 32, 32)
+    den = np.array(den, dtype=np.float64)
+    data = dict(density = (den, "g/cm**3"))
+    bbox = np.array([[-1.5, 1.5], [-1.5, 1.5], [-1.5, 1.5]])
+    ds = load_uniform_grid(data, den.shape, length_unit="Mpc", bbox=bbox, nprocs=64)
+
+    sl_on = SlicePlot(ds, "z", ["density"])
+
+    L = [0, 0, 1]
+    north_vector = [0, 1, 0]
+    sl_off = OffAxisSlicePlot(ds, L, 'density', center=[0,0,0], north_vector=north_vector)
+
+    assert_array_almost_equal(sl_on.frb['density'], sl_off.frb['density'])


https://bitbucket.org/yt_analysis/yt/commits/5bb5a38e4eb1/
Changeset:   5bb5a38e4eb1
Branch:      yt
User:        ngoldbaum
Date:        2016-06-15 20:58:20+00:00
Summary:     Merged in atmyers/yt (pull request #2214)

[BUGFIX] Fixes for off-axis slice plots. Closes #1214.
Affected #:  4 files

diff -r 9bcee915d549eb251157fbe2b82c5b5e8fb783b9 -r 5bb5a38e4eb16d60edafa5c601ab956d578cb9ee yt/utilities/lib/pixelization_routines.pyx
--- a/yt/utilities/lib/pixelization_routines.pyx
+++ b/yt/utilities/lib/pixelization_routines.pyx
@@ -275,14 +275,14 @@
                     cx = inv_mat[0,0]*cxpx + inv_mat[0,1]*cypx + center[0]
                     cy = inv_mat[1,0]*cxpx + inv_mat[1,1]*cypx + center[1]
                     cz = inv_mat[2,0]*cxpx + inv_mat[2,1]*cypx + center[2]
-                    if fabs(xsp - cx) * 0.95 > dxsp or \
-                       fabs(ysp - cy) * 0.95 > dysp or \
-                       fabs(zsp - cz) * 0.95 > dzsp:
+                    if fabs(xsp - cx) * 0.99 > dxsp or \
+                       fabs(ysp - cy) * 0.99 > dysp or \
+                       fabs(zsp - cz) * 0.99 > dzsp:
                         continue
                     mask[i, j] += 1
                     my_array[i, j] += dsp
     my_array /= mask
-    return my_array
+    return my_array.T
 
 
 @cython.cdivision(True)

diff -r 9bcee915d549eb251157fbe2b82c5b5e8fb783b9 -r 5bb5a38e4eb16d60edafa5c601ab956d578cb9ee yt/visualization/plot_modifications.py
--- a/yt/visualization/plot_modifications.py
+++ b/yt/visualization/plot_modifications.py
@@ -273,8 +273,7 @@
     True, the velocity fields will be scaled by their local
     (in-plane) length, allowing morphological features to be more
     clearly seen for fields with substantial variation in field
-    strength (normalize is not implemented and thus ignored for
-    Cutting Planes).
+    strength.
     """
     _type_name = "velocity"
     _supported_geometries = ("cartesian", "spectral_cube")
@@ -290,7 +289,9 @@
         if plot._type_name == "CuttingPlane":
             qcb = CuttingQuiverCallback("cutting_plane_velocity_x",
                                         "cutting_plane_velocity_y",
-                                        self.factor)
+                                        self.factor, scale=self.scale,
+                                        normalize=self.normalize,
+                                        scale_units=self.scale_units)
         else:
             ax = plot.data.axis
             (xi, yi) = (plot.data.ds.coordinates.x_axis[ax],
@@ -336,7 +337,9 @@
         if plot._type_name == "CuttingPlane":
             qcb = CuttingQuiverCallback("cutting_plane_magnetic_field_x",
                                         "cutting_plane_magnetic_field_y",
-                                        self.factor)
+                                        self.factor, scale=self.scale, 
+                                        scale_units=self.scale_units, 
+                                        normalize=self.normalize)
         else:
             xax = plot.data.ds.coordinates.x_axis[plot.data.axis]
             yax = plot.data.ds.coordinates.y_axis[plot.data.axis]
@@ -855,11 +858,16 @@
     """
     _type_name = "cquiver"
     _supported_geometries = ("cartesian", "spectral_cube")
-    def __init__(self, field_x, field_y, factor):
+
+    def __init__(self, field_x, field_y, factor=16, scale=None,
+                 scale_units=None, normalize=None):
         PlotCallback.__init__(self)
         self.field_x = field_x
         self.field_y = field_y
         self.factor = factor
+        self.scale = scale
+        self.scale_units = scale_units
+        self.normalize = normalize
 
     def __call__(self, plot):
         x0, x1 = plot.xlim
@@ -870,6 +878,7 @@
         nx = plot.image._A.shape[0] / self.factor
         ny = plot.image._A.shape[1] / self.factor
         indices = np.argsort(plot.data['dx'])[::-1]
+
         pixX = pixelize_off_axis_cartesian(
                                plot.data['x'], plot.data['y'], plot.data['z'],
                                plot.data['px'], plot.data['py'],
@@ -877,7 +886,7 @@
                                plot.data.center, plot.data._inv_mat, indices,
                                plot.data[self.field_x],
                                int(nx), int(ny),
-                               (x0, x1, y0, y1),).transpose()
+                               (x0, x1, y0, y1)).transpose()
         pixY = pixelize_off_axis_cartesian(
                                plot.data['x'], plot.data['y'], plot.data['z'],
                                plot.data['px'], plot.data['py'],
@@ -885,10 +894,16 @@
                                plot.data.center, plot.data._inv_mat, indices,
                                plot.data[self.field_y],
                                int(nx), int(ny),
-                               (x0, x1, y0, y1),).transpose()
+                               (x0, x1, y0, y1)).transpose()
         X,Y = np.meshgrid(np.linspace(xx0,xx1,nx,endpoint=True),
                           np.linspace(yy0,yy1,ny,endpoint=True))
-        plot._axes.quiver(X,Y, pixX, pixY)
+
+        if self.normalize:
+            nn = np.sqrt(pixX**2 + pixY**2)
+            pixX /= nn
+            pixY /= nn
+
+        plot._axes.quiver(X,Y, pixX, pixY, scale=self.scale, scale_units=self.scale_units)
         plot._axes.set_xlim(xx0,xx1)
         plot._axes.set_ylim(yy0,yy1)
         plot._axes.hold(False)

diff -r 9bcee915d549eb251157fbe2b82c5b5e8fb783b9 -r 5bb5a38e4eb16d60edafa5c601ab956d578cb9ee yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -103,7 +103,7 @@
         mat = np.transpose(np.column_stack((perp1,perp2,normal)))
         center = np.dot(mat,center)
 
-    w = tuple(el.in_units('unitary') for el in width)
+    w = tuple(el.in_units('code_length') for el in width)
     bounds = tuple(((2*(i % 2))-1)*w[i//2]/2 for i in range(len(w)*2))
 
     return (bounds, center)

diff -r 9bcee915d549eb251157fbe2b82c5b5e8fb783b9 -r 5bb5a38e4eb16d60edafa5c601ab956d578cb9ee yt/visualization/tests/test_plotwindow.py
--- a/yt/visualization/tests/test_plotwindow.py
+++ b/yt/visualization/tests/test_plotwindow.py
@@ -13,6 +13,7 @@
 # The full license is in the file COPYING.txt, distributed with this software.
 #-----------------------------------------------------------------------------
 import itertools
+import numpy as np
 import os
 import tempfile
 import shutil
@@ -27,6 +28,7 @@
 from yt.visualization.api import \
     SlicePlot, ProjectionPlot, OffAxisSlicePlot, OffAxisProjectionPlot
 from yt.units.yt_array import YTArray, YTQuantity
+from yt.frontends.stream.api import load_uniform_grid
 from collections import OrderedDict
 
 def setup():
@@ -400,3 +402,20 @@
         [assert_array_almost_equal(py, y, 14) for py, y in zip(plot.ylim, ylim)]
         [assert_array_almost_equal(pw, w, 14) for pw, w in zip(plot.width, pwidth)]
         assert_true(aun == plot._axes_unit_names)
+
+def test_on_off_compare():
+    # fake density field that varies in the x-direction only
+    den = np.arange(32**3) / 32**2 + 1
+    den = den.reshape(32, 32, 32)
+    den = np.array(den, dtype=np.float64)
+    data = dict(density = (den, "g/cm**3"))
+    bbox = np.array([[-1.5, 1.5], [-1.5, 1.5], [-1.5, 1.5]])
+    ds = load_uniform_grid(data, den.shape, length_unit="Mpc", bbox=bbox, nprocs=64)
+
+    sl_on = SlicePlot(ds, "z", ["density"])
+
+    L = [0, 0, 1]
+    north_vector = [0, 1, 0]
+    sl_off = OffAxisSlicePlot(ds, L, 'density', center=[0,0,0], north_vector=north_vector)
+
+    assert_array_almost_equal(sl_on.frb['density'], sl_off.frb['density'])

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