.. _using-objects:

Using and Manipulating Objects and Fields
=========================================

To generate standard plots, objects rarely need to be directly constructed.
However, for detailed data inspection as well as hand-crafted derived data,
objects can be exceptionally useful and even necessary.

.. _accessing-fields:

Accessing Fields in Objects
---------------------------

``yt`` utilizes load-on-demand objects to represent physical regions in space.
(See :ref:`philo-objects`.)  Data objects in ``yt`` all respect the following
protocol for accessing data:

.. code-block:: python

   my_object["Density"]

where ``"Density"`` can be any field name.  The full list of objects is
available in :ref:`available-objects`, and information about how to create an
object can be found in :ref:`creating-objects`.  The field is returned as a
single, flattened array without spatial information.  The best mechanism for
manipulating spatial data is the :class:`~yt.lagos.CoveringGridBase` object.

The full list of fields that are available can be found as a property of the
Hierarchy or Static Output object that you wish to access.  This property is
calculated every time the object is instantiated.  The full list of fields that
have been identified in the output file, which need no processing (besides unit
conversion) are in the property ``field_list`` and the full list of
potentially-accessible derived fields (see :ref:`philo-derived-fields`) is
available in the property ``derived_field_list``.  You can see these by
examining the two properties:

.. code-block:: python

   pf = load("my_data")
   print pf.h.field_list
   print pf.h.derived_field_list

When a field is added, it is added to a container that hangs off of the
parameter file, as well.  All of the field creation options
(:ref:`derived-field-options`) are accessible through this object:

.. code-block:: python

   pf = load("my_data")
   print pf.h.field_info["Pressure"].units

This is a fast way to examine the units of a given field, and additionally you
can use :meth:`yt.lagos.DerivedField.get_source` to get the source code:

.. code-block:: python

   field = pf.h.field_info["Pressure"]
   print field.get_source()

.. _available-objects:

Available Objects
-----------------

Objects are instantiated by direct access of a hierarchy.  Each of the objects
that can be generated by a hierarchy are in fact fully-fledged data objects
respecting the standard protocol for interaction.

The following objects are available, all of which hang off of the hierarchy
object.  To access them, you would do something like this (as for a
:class:`region`):

.. code-block:: python

   from yt.mods import *
   pf = load("RedshiftOutput0005")
   reg = pf.h.region([0.5, 0.5, 0.5], [0.0, 0.0, 0.0], [1.0, 1.0, 1.0])

.. include:: _obj_docstrings.inc

.. _object-serialization:

Storing and Loading Objects
---------------------------

Often, when operating interactively or via the scripting interface, it is
convenient to save an object or multiple objects out to disk and then restart
the calculation later.  Personally, I found this most useful when dealing with
identification of clumps and contours (see :ref:`cookbook` for a recipe on how
to find clumps and the API documentation for both :mod:`~yt.lagos.ContourFinder`
and :mod:`~yt.lagos.Clump`) where the identification step can be quite
time-consuming, but the analysis may be relatively fast.

Typically, the save and load operations are used on 3D data objects.  ``yt``
has a separate set of serialization operations for 2D objects such as
projections.

.. _parameter_file_serialization:

``yt`` will save out 3D objects to disk under the presupposition that the
construction of the objects is the difficult part, rather than the generation
of the data -- this means that you can save out an object as a description of
how to recreate it in space, but not the actual data arrays affiliated with
that object.  The information that is saved includes the parameter file off of
which the object "hangs."  It is this piece of information that is the most
difficult; the object, when reloaded, must be able to reconstruct a parameter
file from whatever limited information it has in the save file.

To do this, ``yt`` is able to identify parameter files based on a "hash"
generated from the base file name, the "CurrentTimeIdentifier", and the
simulation time.  These three characteristics should never be changed outside
of a simulation, they are independent of the file location on disk, and in
conjunction they should be uniquely identifying.  (This process is all done in
:mod:`~yt.fido` via :class:`~yt.fido.ParameterFileStore`.)

To save an object, you can either save it in the ``.yt`` file affiliated with
the hierarchy (:ref:`dot-yt-files`) or as a standalone file.  For instance,
using :meth:`~yt.lagos.AMRHierarchy.save_object` we can save a sphere.

.. code-block:: python

   from yt.mods import *
   pf = load("my_data")
   sp = pf.h.sphere([0.5, 0.5, 0.5], 10.0/pf['kpc'])

   pf.h.save_object(sp, "sphere_to_analyze_later")


In a later session, we can load it using
:meth:`~yt.lagos.AMRHierarchy.save_object`:

.. code-block:: python

   from yt.mods import *

   pf = load("my_data")
   sphere_to_analyze = pf.h.load_object("sphere_to_analyze_later")

Additionally, if we want to store the object independent of the ``.yt`` file,
we can save the object directly:

.. code-block:: python

   from yt.mods import *

   pf = load("my_data")
   sp = pf.h.sphere([0.5, 0.5, 0.5], 10.0/pf['kpc'])

   sp.save_object("my_sphere", "my_storage_file.cpkl")

This will store the object as ``my_sphere`` in the file
``my_storage_file.cpkl``, which will be created or accessed using the standard
python module :mod:`shelve`.  Note that if a filename is not supplied, it will
be saved via the hierarchy, as above.

To re-load an object saved this way, you can use the shelve module directly:

.. code-block:: python

   from yt.mods import *
   import shelve

   obj_file = shelve.open("my_storage_file.cpkl")
   pf, obj = obj_file["my_sphere"]

Note here that this behaves slightly differently than above -- we do not need
to load the parameter file ourselves, as the load process actually does that
for us!  Additionally, we can store multiple objects in a single shelve file,
so we have to call the sphere by name.

.. note:: It's also possible to use the standard :mod:`cPickle` module for
          loading and storing objects -- so in theory you could even save a
          list of objects!

This method works for clumps, as well, and the entire clump hierarchy will be
stored and restored upon load.
