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

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Tue Jun 27 11:15:06 PDT 2017


19 new commits in yt:

https://bitbucket.org/yt_analysis/yt/commits/b7d230b3c819/
Changeset:   b7d230b3c819
User:        brittonsmith
Date:        2017-06-05 04:13:34+00:00
Summary:     Adding Enzo-P frontend.
Affected #:  8 files

diff -r 2848c2031acc2b429d4effd45c4abf70d215cc80 -r b7d230b3c819c2fde33f9deafb57724d14426dd5 yt/frontends/api.py
--- a/yt/frontends/api.py
+++ b/yt/frontends/api.py
@@ -25,6 +25,7 @@
     'chombo',
     'eagle',
     'enzo',
+    'enzo_p',
     'exodus_ii',
     'fits',
     'flash',

diff -r 2848c2031acc2b429d4effd45c4abf70d215cc80 -r b7d230b3c819c2fde33f9deafb57724d14426dd5 yt/frontends/enzo_p/api.py
--- /dev/null
+++ b/yt/frontends/enzo_p/api.py
@@ -0,0 +1,26 @@
+"""
+API for yt.frontends.enzo_p
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2017, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+from .data_structures import \
+    EnzoPGrid, \
+    EnzoPHierarchy, \
+    EnzoPDataset
+
+from .fields import \
+    EnzoPFieldInfo
+add_enzop_field = EnzoPFieldInfo.add_field
+
+from .io import \
+     EPIOHandler

diff -r 2848c2031acc2b429d4effd45c4abf70d215cc80 -r b7d230b3c819c2fde33f9deafb57724d14426dd5 yt/frontends/enzo_p/data_structures.py
--- /dev/null
+++ b/yt/frontends/enzo_p/data_structures.py
@@ -0,0 +1,353 @@
+"""
+Data structures for Enzo-P
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2017, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+from yt.utilities.on_demand_imports import \
+    _h5py as h5py
+import numpy as np
+import os
+import stat
+
+from yt.funcs import \
+    ensure_tuple, \
+    get_pbar, \
+    setdefaultattr
+from yt.data_objects.grid_patch import \
+    AMRGridPatch
+from yt.data_objects.static_output import \
+    Dataset
+from yt.fields.field_info_container import \
+    NullFunc
+from yt.geometry.grid_geometry_handler import \
+    GridIndex
+from yt.utilities.logger import \
+    ytLogger as mylog
+
+from .fields import \
+    EnzoPFieldInfo
+
+from yt.frontends.enzo_p.misc import \
+    get_block_info, \
+    get_root_blocks, \
+    get_root_block_id
+
+class EnzoPGrid(AMRGridPatch):
+    """
+    Class representing a single EnzoP Grid instance.
+    """
+
+    def __init__(self, id, index, block_name, filename = None):
+        """
+        Returns an instance of EnzoPGrid with *id*, associated with
+        *filename* and *index*.
+        """
+        # All of the field parameters will be passed to us as needed.
+        AMRGridPatch.__init__(self, id, filename=filename, index=index)
+        self.block_name = block_name
+        self._children_ids = []
+        self._parent_id = -1
+        self.Level = -1
+
+    def __repr__(self):
+        return "EnzoPBlock_%s" % self.block_name
+
+    @property
+    def Parent(self):
+        if self._parent_id == -1: return None
+        return self.index.grids[self._parent_id - self._id_offset]
+
+    @property
+    def Children(self):
+        return [self.index.grids[cid - self._id_offset]
+                for cid in self._children_ids]
+
+class EnzoPHierarchy(GridIndex):
+
+    _strip_path = False
+    grid = EnzoPGrid
+    _preload_implemented = True
+
+    def __init__(self, ds, dataset_type):
+
+        self.dataset_type = dataset_type
+        self.directory = os.path.dirname(ds.parameter_filename)
+        self.index_filename = ds.parameter_filename
+        if os.path.getsize(self.index_filename) == 0:
+            raise IOError(-1,"File empty", self.index_filename)
+
+        GridIndex.__init__(self, ds, dataset_type)
+        self.dataset.dataset_type = self.dataset_type
+
+    def _count_grids(self):
+        fblock_size = 32768
+        f = open(self.ds.parameter_filename, "r")
+        file_size = f.seek(0, 2)
+        nblocks = np.ceil(float(file_size) /
+                          fblock_size).astype(np.int64)
+        offset = f.seek(0)
+        ngrids = 0
+        for ib in range(nblocks):
+            my_block = min(fblock_size, file_size - offset)
+            buff = f.read(my_block)
+            ngrids += buff.count("\n")
+            offset += my_block
+        f.close()
+        self.num_grids = ngrids
+        self.dataset_type = "enzo_p"
+
+    def _parse_index(self):
+        self.grids = np.empty(self.num_grids, dtype='object')
+
+        pbar = get_pbar("Parsing Hierarchy ", self.num_grids)
+        f = open(self.ds.parameter_filename, "r")
+        fblock_size = 32768
+        file_size = f.seek(0, 2)
+        nblocks = np.ceil(float(file_size) /
+                          fblock_size).astype(np.int64)
+        offset = f.seek(0)
+        lstr = ""
+        # place child blocks after the root blocks
+        rbdim = self.ds.root_block_dimensions
+        nroot_blocks = rbdim.prod()
+        child_id = nroot_blocks
+        child_id_queue = np.empty((self.num_grids - nroot_blocks, 2),
+                                  dtype=np.int64)
+
+        slope = self.ds.domain_width / \
+          self.ds.arr(np.ones(3), "code_length")
+
+        for ib in range(nblocks):
+            fblock = min(fblock_size, file_size - offset)
+            buff = lstr + f.read(fblock)
+            bnl = 0
+            for inl in range(buff.count("\n")):
+                nnl = buff.find("\n", bnl)
+                line = buff[bnl:nnl]
+                block_name, block_file = line.split()
+                level, left, right = get_block_info(block_name)
+
+                rbindex = get_root_block_id(block_name)
+                rbid = rbindex[0] * rbdim[1:].prod() + \
+                  rbindex[1] * rbdim[2:].prod() + rbindex[2]
+                if level == 0:
+                    grid_id = rbid
+                    parent_id = -1
+                else:
+                    grid_id = child_id
+                    parent_id = rbid
+                    # Queue up the child ids and set them later
+                    # when all the grids have been created.
+                    child_id_queue[child_id - nroot_blocks] = \
+                      rbid, child_id
+                    child_id += 1
+
+                my_grid = self.grid(
+                    grid_id, self, block_name,
+                    filename=os.path.join(self.directory, block_file))
+                my_grid.Level = level
+                my_grid._parent_id = parent_id
+
+                self.grids[grid_id]           = my_grid
+                self.grid_levels[grid_id]     = level
+                self.grid_left_edge[grid_id]  = left
+                self.grid_right_edge[grid_id] = right
+                self.grid_dimensions[grid_id] = self.ds.active_grid_dimensions
+
+                bnl = nnl + 1
+                pbar.update(1)
+            lstr = buff[bnl:]
+            offset += fblock
+
+        f.close()
+        pbar.finish()
+
+        self.grid_left_edge   = self.grid_left_edge  * slope + \
+          self.ds.domain_left_edge
+        self.grid_right_edge  = self.grid_right_edge * slope + \
+          self.ds.domain_left_edge
+
+        for rbid, child_id in child_id_queue:
+            self.grids[rbid]._children_ids.append(child_id)
+
+    def _populate_grid_objects(self):
+        for g in self.grids:
+            g._prepare_grid()
+            g._setup_dx()
+        self.max_level = self.grid_levels.max()
+
+    def _setup_derived_fields(self):
+        super(EnzoPHierarchy, self)._setup_derived_fields()
+        for fname, field in self.ds.field_info.items():
+            if not field.particle_type: continue
+            if isinstance(fname, tuple): continue
+            if field._function is NullFunc: continue
+
+    def _detect_output_fields(self):
+        self.field_list = []
+        # Do this only on the root processor to save disk work.
+        if self.comm.rank in (0, None):
+            mylog.info("Gathering a field list (this may take a moment.)")
+            field_list = set()
+            random_sample = self._generate_random_grids()
+            for grid in random_sample:
+                if not hasattr(grid, 'filename'): continue
+                try:
+                    gf = self.io._read_field_names(grid)
+                except self.io._read_exception:
+                    raise IOError("Grid %s is a bit funky?", grid.id)
+                mylog.debug("Grid %s has: %s", grid.id, gf)
+                field_list = field_list.union(gf)
+            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:
+            field_list = None
+            ptypes = None
+            ptypes_raw = None
+        self.field_list = list(self.comm.mpi_bcast(field_list))
+        self.dataset.particle_types = list(self.comm.mpi_bcast(ptypes))
+        self.dataset.particle_types_raw = list(self.comm.mpi_bcast(ptypes_raw))
+
+    def _generate_random_grids(self):
+        if self.num_grids > 40:
+            starter = np.random.randint(0, 20)
+            random_sample = np.mgrid[starter:len(self.grids)-1:20j].astype("int32")
+            # We also add in a bit to make sure that some of the grids have
+            # particles
+            gwp = self.grid_particle_count > 0
+            if np.any(gwp) and not np.any(gwp[(random_sample,)]):
+                # We just add one grid.  This is not terribly efficient.
+                first_grid = np.where(gwp)[0][0]
+                random_sample.resize((21,))
+                random_sample[-1] = first_grid
+                mylog.debug("Added additional grid %s", first_grid)
+            mylog.debug("Checking grids: %s", random_sample.tolist())
+        else:
+            random_sample = np.mgrid[0:max(len(self.grids),1)].astype("int32")
+        return self.grids[(random_sample,)]
+
+class EnzoPDataset(Dataset):
+    """
+    Enzo-P-specific output, set at a fixed time.
+    """
+    _index_class = EnzoPHierarchy
+    _field_info_class = EnzoPFieldInfo
+
+    def __init__(self, filename, dataset_type=None,
+                 file_style = None,
+                 parameter_override = None,
+                 conversion_override = None,
+                 storage_filename = None,
+                 units_override=None,
+                 unit_system="cgs"):
+        """
+        This class is a stripped down class that simply reads and parses
+        *filename* without looking at the index.  *dataset_type* gets passed
+        to the index to pre-determine the style of data-output.  However,
+        it is not strictly necessary.  Optionally you may specify a
+        *parameter_override* dictionary that will override anything in the
+        paarmeter file and a *conversion_override* dictionary that consists
+        of {fieldname : conversion_to_cgs} that will override the #DataCGS.
+        """
+        self.fluid_types += ("enzop",)
+        if parameter_override is None: parameter_override = {}
+        self._parameter_override = parameter_override
+        if conversion_override is None: conversion_override = {}
+        self._conversion_override = conversion_override
+        self.storage_filename = storage_filename
+        Dataset.__init__(self, filename, dataset_type, file_style=file_style,
+                         units_override=units_override, unit_system=unit_system)
+
+    def _parse_parameter_file(self):
+        """
+        Parses the parameter file and establishes the various
+        dictionaries.
+        """
+
+        f = open(self.parameter_filename, "r")
+        # get dimension from first block name
+        b0, fn0 = f.readline().strip().split()
+        level0, left0, right0 = get_block_info(b0, min_dim=0)
+        root_blocks = get_root_blocks(b0)
+        f.close()
+        self.dimensionality = left0.size
+        self.periodicity = \
+          ensure_tuple(np.ones(self.dimensionality, dtype=bool))
+
+        fh = h5py.File(os.path.join(self.directory, fn0), "r")
+        self.domain_left_edge  = fh.attrs["lower"]
+        self.domain_right_edge = fh.attrs["upper"]
+
+        # all blocks are the same size
+        ablock = fh[list(fh.keys())[0]]
+        self.current_time = ablock.attrs["time"][0]
+        gsi = ablock.attrs["enzo_GridStartIndex"]
+        gei = ablock.attrs["enzo_GridEndIndex"]
+        self.ghost_zones = gsi[0]
+        self.root_block_dimensions = root_blocks
+        self.active_grid_dimensions = gei - gsi + 1
+        self.grid_dimensions = ablock.attrs["enzo_GridDimension"]
+        self.domain_dimensions = root_blocks * self.active_grid_dimensions
+        fh.close()
+
+        self.periodicity += (False, ) * (3 - self.dimensionality)
+
+        # WIP hard-coded for now
+        self.refine_by = 2
+        self.cosmological_simulation = 0
+        self.gamma = 5. / 3.
+        self.particle_types = ()
+        self.particle_types_raw = self.particle_types
+        self.unique_identifier = \
+          str(int(os.stat(self.parameter_filename)[stat.ST_CTIME]))
+
+    def _set_code_unit_attributes(self):
+        setdefaultattr(self, 'length_unit', self.quan(1, "cm"))
+        setdefaultattr(self, 'mass_unit', self.quan(1, "g"))
+        setdefaultattr(self, 'time_unit', self.quan(1, "s"))
+        setdefaultattr(self, 'velocity_unit',
+                       self.length_unit / self.time_unit)
+        magnetic_unit = np.sqrt(4*np.pi * self.mass_unit /
+                                (self.time_unit**2 * self.length_unit))
+        magnetic_unit = np.float64(magnetic_unit.in_cgs())
+        setdefaultattr(self, 'magnetic_unit',
+                       self.quan(magnetic_unit, "gauss"))
+
+    @classmethod
+    def _is_valid(cls, *args, **kwargs):
+        fn = args[0]
+        ddir = os.path.dirname(fn)
+        if not fn.endswith(".block_list"):
+            return False
+        try:
+            with open(fn, "r") as f:
+                block, block_file = f.readline().strip().split()
+                get_block_info(block)
+                if not os.path.exists(os.path.join(ddir, block_file)):
+                    return False
+        except:
+            return False
+        return True

diff -r 2848c2031acc2b429d4effd45c4abf70d215cc80 -r b7d230b3c819c2fde33f9deafb57724d14426dd5 yt/frontends/enzo_p/definitions.py
--- /dev/null
+++ b/yt/frontends/enzo_p/definitions.py
@@ -0,0 +1,15 @@
+"""
+Definitions specific to Enzo-P
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2017, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+

diff -r 2848c2031acc2b429d4effd45c4abf70d215cc80 -r b7d230b3c819c2fde33f9deafb57724d14426dd5 yt/frontends/enzo_p/fields.py
--- /dev/null
+++ b/yt/frontends/enzo_p/fields.py
@@ -0,0 +1,64 @@
+"""
+Fields specific to Enzo-P
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2013, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+from yt.fields.field_info_container import \
+    FieldInfoContainer
+
+rho_units = "code_mass / code_length**3"
+vel_units = "code_velocity"
+acc_units = "code_velocity / code_time"
+energy_units = "code_velocity**2"
+
+known_species_names = {
+}
+
+NODAL_FLAGS = {
+    'BxF': [1, 0, 0],
+    'ByF': [0, 1, 0],
+    'BzF': [0, 0, 1],
+    'Ex': [0, 1, 1],
+    'Ey': [1, 0, 1],
+    'Ez': [1, 1, 0],
+    'AvgElec0': [0, 1, 1],
+    'AvgElec1': [1, 0, 1],
+    'AvgElec2': [1, 1, 0],
+}
+
+class EnzoPFieldInfo(FieldInfoContainer):
+    known_other_fields = (
+        ("velocity_x", (vel_units, ["velocity_x"], None)),
+        ("velocity_y", (vel_units, ["velocity_y"], None)),
+        ("velocity_z", (vel_units, ["velocity_z"], None)),
+        ("acceleration_x", (acc_units, ["acceleration_x"], None)),
+        ("acceleration_y", (acc_units, ["acceleration_y"], None)),
+        ("acceleration_z", (acc_units, ["acceleration_z"], None)),
+        ("density", (rho_units, ["density"], None)),
+        ("total_density", (rho_units, ["total_density"], None)),
+        ("total_energy", (energy_units, ["total_energy"], None)),
+        ("internal_energy", (energy_units, ["internal_energy"], None)),
+    )
+
+    known_particle_fields = (
+        ("x", ("code_length", [], None)),
+        ("y", ("code_length", [], None)),
+        ("z", ("code_length", [], None)),
+        ("vx", (vel_units, [], None)),
+        ("vy", (vel_units, [], None)),
+        ("vz", (vel_units, [], None)),
+        ("ax", (acc_units, [], None)),
+        ("ay", (acc_units, [], None)),
+        ("az", (acc_units, [], None)),
+        ("mass", ("code_mass", [], None)),
+    )

diff -r 2848c2031acc2b429d4effd45c4abf70d215cc80 -r b7d230b3c819c2fde33f9deafb57724d14426dd5 yt/frontends/enzo_p/io.py
--- /dev/null
+++ b/yt/frontends/enzo_p/io.py
@@ -0,0 +1,160 @@
+"""
+Enzo-P-specific IO functions
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2017, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+from yt.utilities.io_handler import \
+    BaseIOHandler
+from yt.utilities.logger import ytLogger as mylog
+from yt.extern.six import b, iteritems
+from yt.utilities.on_demand_imports import _h5py as h5py
+from yt.geometry.selection_routines import GridSelector
+import numpy as np
+
+
+_convert_mass = ("particle_mass","mass")
+
+_particle_position_names = {}
+
+class EPIOHandler(BaseIOHandler):
+
+    _dataset_type = "enzo_p"
+    _base = slice(None)
+    _field_dtype = "float64"
+
+    def __init__(self, *args, **kwargs):
+        super(EPIOHandler, self).__init__(*args, **kwargs)
+        self._base = self.ds.dimensionality * \
+          (slice(self.ds.ghost_zones,
+                 -self.ds.ghost_zones),)
+
+    def _read_field_names(self, grid):
+        if grid.filename is None: return []
+        f = h5py.File(grid.filename, "r")
+        try:
+            group = f[grid.block_name]
+        except KeyError:
+            group = f
+        fields = []
+        dtypes = set([])
+        for name, v in iteritems(group):
+            if not hasattr(v, "shape") or v.dtype == "O":
+                continue
+            # mesh fields are "field <name>"
+            if name.startswith("field"):
+                dummy, fname = name.split(maxsplit=1)
+                fields.append(("enzop", fname))
+                dtypes.add(v.dtype)
+            # particle fields are "particle <type><name>"
+            else:
+                dummy, ftype, fname = name.split(maxsplit=2)
+                fields.append((ftype, fname))
+
+        if len(dtypes) == 1:
+            # Now, if everything we saw was the same dtype, we can go ahead and
+            # set it here.  We do this because it is a HUGE savings for 32 bit
+            # floats, since our numpy copying/casting is way faster than
+            # h5py's, for some reason I don't understand.  This does *not* need
+            # to be correct -- it will get fixed later -- it just needs to be
+            # okay for now.
+            self._field_dtype = list(dtypes)[0]
+        f.close()
+        return fields
+
+    @property
+    def _read_exception(self):
+        return (KeyError,)
+
+    def _read_particle_coords(self, chunks, ptf):
+        for rv in self._read_particle_fields(chunks, ptf, None):
+            yield rv
+
+    def _read_particle_fields(self, chunks, ptf, selector):
+        chunks = list(chunks)
+        for chunk in chunks: # These should be organized by grid filename
+            f = None
+            for g in chunk.objs:
+                if g.filename is None: continue
+                if f is None:
+                    f = h5py.File(g.filename, "r")
+                nap = sum(g.NumberOfActiveParticles.values())
+                if g.NumberOfParticles == 0 and nap == 0:
+                    continue
+                ds = f.get(g.block_name)
+                for ptype, field_list in sorted(ptf.items()):
+                    if ptype != "io":
+                        if g.NumberOfActiveParticles[ptype] == 0: continue
+                        pds = ds.get("Particles/%s" % ptype)
+                    else:
+                        pds = ds
+                    pn = _particle_position_names.get(ptype,
+                            r"particle_position_%s")
+                    x, y, z = (np.asarray(pds.get(pn % ax).value, dtype="=f8")
+                               for ax in 'xyz')
+                    if selector is None:
+                        # This only ever happens if the call is made from
+                        # _read_particle_coords.
+                        yield ptype, (x, y, z)
+                        continue
+                    mask = selector.select_points(x, y, z, 0.0)
+                    if mask is None: continue
+                    for field in field_list:
+                        data = np.asarray(pds.get(field).value, "=f8")
+                        if field in _convert_mass:
+                            data *= g.dds.prod(dtype="f8")
+                        yield (ptype, field), data[mask]
+            if f: f.close()
+
+    def io_iter(self, chunks, fields):
+        for chunk in chunks:
+            fid = None
+            filename = -1
+            for obj in chunk.objs:
+                if obj.filename is None: continue
+                if obj.filename != filename:
+                    # Note one really important thing here: even if we do
+                    # implement LRU caching in the _read_obj_field function,
+                    # we'll still be doing file opening and whatnot.  This is a
+                    # problem, but one we can return to.
+                    if fid is not None:
+                        fid.close()
+                    fid = h5py.h5f.open(b(obj.filename), h5py.h5f.ACC_RDONLY)
+                    filename = obj.filename
+                for field in fields:
+                    data = None
+                    yield field, obj, self._read_obj_field(
+                        obj, field, (fid, data))
+        if fid is not None:
+            fid.close()
+
+    def _read_obj_field(self, obj, field, fid_data):
+        if fid_data is None: fid_data = (None, None)
+        fid, data = fid_data
+        if fid is None:
+            close = True
+            fid = h5py.h5f.open(b(obj.filename), h5py.h5f.ACC_RDONLY)
+        else:
+            close = False
+        ftype, fname = field
+        node = "/%s/field %s" % (obj.block_name, fname)
+        dg = h5py.h5d.open(fid, b(node))
+        rdata = np.empty(self.ds.grid_dimensions[:self.ds.dimensionality],
+                         dtype=self._field_dtype)
+        dg.read(h5py.h5s.ALL, h5py.h5s.ALL, rdata)
+        if close:
+            fid.close()
+        data = rdata[self._base].T
+        if self.ds.dimensionality < 3:
+            nshape = data.shape + (1,)*(3 - self.ds.dimensionality)
+            data  = np.reshape(data, nshape)
+        return data

diff -r 2848c2031acc2b429d4effd45c4abf70d215cc80 -r b7d230b3c819c2fde33f9deafb57724d14426dd5 yt/frontends/enzo_p/misc.py
--- /dev/null
+++ b/yt/frontends/enzo_p/misc.py
@@ -0,0 +1,62 @@
+"""
+Miscellaneous functions that are Enzo-P-specific
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2017, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+import numpy as np
+
+def bdecode(block):
+    if ":" in block:
+        level = len(block) - block.find(":") - 1
+    else:
+        level = 0
+    bst   = block.replace(":", "")
+    d     = float(2**len(bst))
+    left  = int(bst, 2)
+    right = left + 1
+    left  /= d
+    right /= d
+    return level, left, right
+
+def get_block_info(block, min_dim=3):
+    mybs = block[1:].split("_")
+    dim = max(len(mybs), min_dim)
+    left = np.zeros(dim)
+    right = np.ones(dim)
+    for i, myb in enumerate(mybs):
+        level, left[i], right[i] = bdecode(myb)
+    return level, left, right
+
+def get_root_blocks(block, min_dim=3):
+    mybs = block[1:].split("_")
+    dim = max(len(mybs), min_dim)
+    nb = np.ones(dim, dtype=int)
+    for i, myb in enumerate(mybs):
+        if ":" in myb:
+            s = myb.find(":")
+        else:
+            s = len(myb)
+        nb[i] = 2**s
+    return nb
+
+def get_root_block_id(block, min_dim=3):
+    mybs = block[1:].split("_")
+    dim = max(len(mybs), min_dim)
+    rbid = np.zeros(dim, dtype=int)
+    for i, myb in enumerate(mybs):
+        if ":" in myb:
+            s = myb.find(":")
+        else:
+            s = len(myb)
+        rbid[i] = int(myb[:s], 2)
+    return rbid


https://bitbucket.org/yt_analysis/yt/commits/d4099de18b00/
Changeset:   d4099de18b00
User:        brittonsmith
Date:        2017-06-10 15:02:25+00:00
Summary:     Make id_offset zero.
Affected #:  1 file

diff -r b7d230b3c819c2fde33f9deafb57724d14426dd5 -r d4099de18b00698f234f0d38659ff1fa7d37ad1d yt/frontends/enzo_p/data_structures.py
--- a/yt/frontends/enzo_p/data_structures.py
+++ b/yt/frontends/enzo_p/data_structures.py
@@ -47,6 +47,8 @@
     Class representing a single EnzoP Grid instance.
     """
 
+    _id_offset = 0
+
     def __init__(self, id, index, block_name, filename = None):
         """
         Returns an instance of EnzoPGrid with *id*, associated with


https://bitbucket.org/yt_analysis/yt/commits/d9901478d1a3/
Changeset:   d9901478d1a3
User:        brittonsmith
Date:        2017-06-10 15:05:32+00:00
Summary:     Removing unused code and fixing some naming conventions.
Affected #:  4 files

diff -r d4099de18b00698f234f0d38659ff1fa7d37ad1d -r d9901478d1a32df4b1c39bca38b6de51f86280d9 yt/frontends/enzo_p/api.py
--- a/yt/frontends/enzo_p/api.py
+++ b/yt/frontends/enzo_p/api.py
@@ -23,4 +23,4 @@
 add_enzop_field = EnzoPFieldInfo.add_field
 
 from .io import \
-     EPIOHandler
+     EnzoPIOHandler

diff -r d4099de18b00698f234f0d38659ff1fa7d37ad1d -r d9901478d1a32df4b1c39bca38b6de51f86280d9 yt/frontends/enzo_p/data_structures.py
--- a/yt/frontends/enzo_p/data_structures.py
+++ b/yt/frontends/enzo_p/data_structures.py
@@ -62,7 +62,7 @@
         self.Level = -1
 
     def __repr__(self):
-        return "EnzoPBlock_%s" % self.block_name
+        return "EnzoPGrid_%04d" % self.id
 
     @property
     def Parent(self):

diff -r d4099de18b00698f234f0d38659ff1fa7d37ad1d -r d9901478d1a32df4b1c39bca38b6de51f86280d9 yt/frontends/enzo_p/fields.py
--- a/yt/frontends/enzo_p/fields.py
+++ b/yt/frontends/enzo_p/fields.py
@@ -24,18 +24,6 @@
 known_species_names = {
 }
 
-NODAL_FLAGS = {
-    'BxF': [1, 0, 0],
-    'ByF': [0, 1, 0],
-    'BzF': [0, 0, 1],
-    'Ex': [0, 1, 1],
-    'Ey': [1, 0, 1],
-    'Ez': [1, 1, 0],
-    'AvgElec0': [0, 1, 1],
-    'AvgElec1': [1, 0, 1],
-    'AvgElec2': [1, 1, 0],
-}
-
 class EnzoPFieldInfo(FieldInfoContainer):
     known_other_fields = (
         ("velocity_x", (vel_units, ["velocity_x"], None)),

diff -r d4099de18b00698f234f0d38659ff1fa7d37ad1d -r d9901478d1a32df4b1c39bca38b6de51f86280d9 yt/frontends/enzo_p/io.py
--- a/yt/frontends/enzo_p/io.py
+++ b/yt/frontends/enzo_p/io.py
@@ -26,14 +26,14 @@
 
 _particle_position_names = {}
 
-class EPIOHandler(BaseIOHandler):
+class EnzoPIOHandler(BaseIOHandler):
 
     _dataset_type = "enzo_p"
     _base = slice(None)
     _field_dtype = "float64"
 
     def __init__(self, *args, **kwargs):
-        super(EPIOHandler, self).__init__(*args, **kwargs)
+        super(EnzoPIOHandler, self).__init__(*args, **kwargs)
         self._base = self.ds.dimensionality * \
           (slice(self.ds.ghost_zones,
                  -self.ds.ghost_zones),)


https://bitbucket.org/yt_analysis/yt/commits/86012baa2428/
Changeset:   86012baa2428
User:        brittonsmith
Date:        2017-06-10 16:27:45+00:00
Summary:     Removing unused imports.
Affected #:  1 file

diff -r d9901478d1a32df4b1c39bca38b6de51f86280d9 -r 86012baa2428bd65b01a46be0135c82ba1c1508a yt/frontends/enzo_p/io.py
--- a/yt/frontends/enzo_p/io.py
+++ b/yt/frontends/enzo_p/io.py
@@ -15,10 +15,8 @@
 
 from yt.utilities.io_handler import \
     BaseIOHandler
-from yt.utilities.logger import ytLogger as mylog
 from yt.extern.six import b, iteritems
 from yt.utilities.on_demand_imports import _h5py as h5py
-from yt.geometry.selection_routines import GridSelector
 import numpy as np
 
 


https://bitbucket.org/yt_analysis/yt/commits/928c2a920c44/
Changeset:   928c2a920c44
User:        brittonsmith
Date:        2017-06-10 20:47:57+00:00
Summary:     Small reorg.
Affected #:  1 file

diff -r 86012baa2428bd65b01a46be0135c82ba1c1508a -r 928c2a920c4423513ff42ec8a238378935bb19d6 yt/frontends/enzo_p/data_structures.py
--- a/yt/frontends/enzo_p/data_structures.py
+++ b/yt/frontends/enzo_p/data_structures.py
@@ -19,24 +19,23 @@
 import os
 import stat
 
-from yt.funcs import \
-    ensure_tuple, \
-    get_pbar, \
-    setdefaultattr
 from yt.data_objects.grid_patch import \
     AMRGridPatch
 from yt.data_objects.static_output import \
     Dataset
 from yt.fields.field_info_container import \
     NullFunc
+from yt.funcs import \
+    ensure_tuple, \
+    get_pbar, \
+    setdefaultattr
 from yt.geometry.grid_geometry_handler import \
     GridIndex
 from yt.utilities.logger import \
     ytLogger as mylog
 
-from .fields import \
+from yt.frontends.enzo_p.fields import \
     EnzoPFieldInfo
-
 from yt.frontends.enzo_p.misc import \
     get_block_info, \
     get_root_blocks, \
@@ -126,9 +125,6 @@
         child_id_queue = np.empty((self.num_grids - nroot_blocks, 2),
                                   dtype=np.int64)
 
-        slope = self.ds.domain_width / \
-          self.ds.arr(np.ones(3), "code_length")
-
         for ib in range(nblocks):
             fblock = min(fblock_size, file_size - offset)
             buff = lstr + f.read(fblock)
@@ -174,6 +170,8 @@
         f.close()
         pbar.finish()
 
+        slope = self.ds.domain_width / \
+          self.ds.arr(np.ones(3), "code_length")
         self.grid_left_edge   = self.grid_left_edge  * slope + \
           self.ds.domain_left_edge
         self.grid_right_edge  = self.grid_right_edge * slope + \


https://bitbucket.org/yt_analysis/yt/commits/13e795ec0568/
Changeset:   13e795ec0568
User:        brittonsmith
Date:        2017-06-11 03:10:08+00:00
Summary:     Fix parent/child indexing.
Affected #:  1 file

diff -r 928c2a920c4423513ff42ec8a238378935bb19d6 -r 13e795ec0568113d87009c82bce7f82400c6b337 yt/frontends/enzo_p/data_structures.py
--- a/yt/frontends/enzo_p/data_structures.py
+++ b/yt/frontends/enzo_p/data_structures.py
@@ -47,6 +47,7 @@
     """
 
     _id_offset = 0
+    _refine_by = 2
 
     def __init__(self, id, index, block_name, filename = None):
         """
@@ -56,21 +57,59 @@
         # All of the field parameters will be passed to us as needed.
         AMRGridPatch.__init__(self, id, filename=filename, index=index)
         self.block_name = block_name
-        self._children_ids = []
+        self._children_ids = None
         self._parent_id = -1
         self.Level = -1
 
     def __repr__(self):
         return "EnzoPGrid_%04d" % self.id
 
+    def get_parent_id(self, desc_block_name):
+        if self.block_name == desc_block_name:
+            raise RuntimeError("Child and parent are the same!")
+        dim = self.index.ds.dimensionality
+        d_block = desc_block_name[1:].replace(":", "")
+        parent = self
+
+        while True:
+            a_block = parent.block_name[1:].replace(":", "")
+            gengap = (len(d_block) - len(a_block)) / dim
+            if gengap <= 1:
+                return parent.id
+            cid = ""
+            for aind, dind in zip(a_block.split("_"),
+                                  d_block.split("_")):
+                assert dind[:len(aind)] == aind
+                cid += dind[len(aind)]
+            cid = int(cid, 2)
+            parent = self.index.grids[parent._children_ids[cid]]
+
+    def add_child(self, child):
+        if self._children_ids is None:
+            self._children_ids = \
+            -1*np.ones(self._refine_by**
+                       self.index.ds.dimensionality,
+                       dtype=np.int64)
+
+        a_block =  self.block_name[1:].replace(":", "")
+        d_block = child.block_name[1:].replace(":", "")
+        cid = ""
+        for aind, dind in zip(a_block.split("_"),
+                              d_block.split("_")):
+            cid += dind[len(aind)]
+        cid = int(cid, 2)
+        self._children_ids[cid] = child.id
+
     @property
     def Parent(self):
         if self._parent_id == -1: return None
-        return self.index.grids[self._parent_id - self._id_offset]
+        return self.index.grids[self._parent_id]
 
     @property
     def Children(self):
-        return [self.index.grids[cid - self._id_offset]
+        if self._children_ids is None:
+            return []
+        return [self.index.grids[cid]
                 for cid in self._children_ids]
 
 class EnzoPHierarchy(GridIndex):
@@ -122,8 +161,6 @@
         rbdim = self.ds.root_block_dimensions
         nroot_blocks = rbdim.prod()
         child_id = nroot_blocks
-        child_id_queue = np.empty((self.num_grids - nroot_blocks, 2),
-                                  dtype=np.int64)
 
         for ib in range(nblocks):
             fblock = min(fblock_size, file_size - offset)
@@ -143,11 +180,7 @@
                     parent_id = -1
                 else:
                     grid_id = child_id
-                    parent_id = rbid
-                    # Queue up the child ids and set them later
-                    # when all the grids have been created.
-                    child_id_queue[child_id - nroot_blocks] = \
-                      rbid, child_id
+                    parent_id = self.grids[rbid].get_parent_id(block_name)
                     child_id += 1
 
                 my_grid = self.grid(
@@ -162,6 +195,9 @@
                 self.grid_right_edge[grid_id] = right
                 self.grid_dimensions[grid_id] = self.ds.active_grid_dimensions
 
+                if level > 0:
+                    self.grids[parent_id].add_child(my_grid)
+
                 bnl = nnl + 1
                 pbar.update(1)
             lstr = buff[bnl:]
@@ -177,9 +213,6 @@
         self.grid_right_edge  = self.grid_right_edge * slope + \
           self.ds.domain_left_edge
 
-        for rbid, child_id in child_id_queue:
-            self.grids[rbid]._children_ids.append(child_id)
-
     def _populate_grid_objects(self):
         for g in self.grids:
             g._prepare_grid()


https://bitbucket.org/yt_analysis/yt/commits/311ca721b1bb/
Changeset:   311ca721b1bb
User:        brittonsmith
Date:        2017-06-11 03:13:51+00:00
Summary:     Refactor duplicate code.
Affected #:  2 files

diff -r 13e795ec0568113d87009c82bce7f82400c6b337 -r 311ca721b1bb2d7fb562fc567145d40e088f0b29 yt/frontends/enzo_p/data_structures.py
--- a/yt/frontends/enzo_p/data_structures.py
+++ b/yt/frontends/enzo_p/data_structures.py
@@ -38,6 +38,7 @@
     EnzoPFieldInfo
 from yt.frontends.enzo_p.misc import \
     get_block_info, \
+    get_child_index, \
     get_root_blocks, \
     get_root_block_id
 
@@ -76,12 +77,7 @@
             gengap = (len(d_block) - len(a_block)) / dim
             if gengap <= 1:
                 return parent.id
-            cid = ""
-            for aind, dind in zip(a_block.split("_"),
-                                  d_block.split("_")):
-                assert dind[:len(aind)] == aind
-                cid += dind[len(aind)]
-            cid = int(cid, 2)
+            cid = get_child_index(a_block, d_block)
             parent = self.index.grids[parent._children_ids[cid]]
 
     def add_child(self, child):
@@ -93,11 +89,7 @@
 
         a_block =  self.block_name[1:].replace(":", "")
         d_block = child.block_name[1:].replace(":", "")
-        cid = ""
-        for aind, dind in zip(a_block.split("_"),
-                              d_block.split("_")):
-            cid += dind[len(aind)]
-        cid = int(cid, 2)
+        cid = get_child_index(a_block, d_block)
         self._children_ids[cid] = child.id
 
     @property

diff -r 13e795ec0568113d87009c82bce7f82400c6b337 -r 311ca721b1bb2d7fb562fc567145d40e088f0b29 yt/frontends/enzo_p/misc.py
--- a/yt/frontends/enzo_p/misc.py
+++ b/yt/frontends/enzo_p/misc.py
@@ -60,3 +60,11 @@
             s = len(myb)
         rbid[i] = int(myb[:s], 2)
     return rbid
+
+def get_child_index(anc_id, desc_id):
+    cid = ""
+    for aind, dind in zip( anc_id.split("_"),
+                          desc_id.split("_")):
+        cid += dind[len(aind)]
+    cid = int(cid, 2)
+    return cid


https://bitbucket.org/yt_analysis/yt/commits/8456a962c594/
Changeset:   8456a962c594
User:        brittonsmith
Date:        2017-06-11 03:41:25+00:00
Summary:     Speed up indexing by checking id of last parent before searching.
Affected #:  2 files

diff -r 311ca721b1bb2d7fb562fc567145d40e088f0b29 -r 8456a962c59444495d2bc04ee78668ff902d6adc yt/frontends/enzo_p/data_structures.py
--- a/yt/frontends/enzo_p/data_structures.py
+++ b/yt/frontends/enzo_p/data_structures.py
@@ -40,7 +40,8 @@
     get_block_info, \
     get_child_index, \
     get_root_blocks, \
-    get_root_block_id
+    get_root_block_id, \
+    is_parent
 
 class EnzoPGrid(AMRGridPatch):
     """
@@ -154,6 +155,7 @@
         nroot_blocks = rbdim.prod()
         child_id = nroot_blocks
 
+        last_pid = None
         for ib in range(nblocks):
             fblock = min(fblock_size, file_size - offset)
             buff = lstr + f.read(fblock)
@@ -172,7 +174,13 @@
                     parent_id = -1
                 else:
                     grid_id = child_id
-                    parent_id = self.grids[rbid].get_parent_id(block_name)
+                    # Try the last parent_id first
+                    if last_pid is not None and \
+                      is_parent(self.grids[last_pid].block_name, block_name):
+                        parent_id = last_pid
+                    else:
+                        parent_id = self.grids[rbid].get_parent_id(block_name)
+                    last_pid = parent_id
                     child_id += 1
 
                 my_grid = self.grid(

diff -r 311ca721b1bb2d7fb562fc567145d40e088f0b29 -r 8456a962c59444495d2bc04ee78668ff902d6adc yt/frontends/enzo_p/misc.py
--- a/yt/frontends/enzo_p/misc.py
+++ b/yt/frontends/enzo_p/misc.py
@@ -68,3 +68,10 @@
         cid += dind[len(aind)]
     cid = int(cid, 2)
     return cid
+
+def is_parent(anc_block, desc_block):
+    for aind, dind in zip( anc_block.split("_"),
+                          desc_block.split("_")):
+        if not dind.startswith(aind):
+            return False
+    return True


https://bitbucket.org/yt_analysis/yt/commits/844319d899d2/
Changeset:   844319d899d2
User:        brittonsmith
Date:        2017-06-15 21:09:29+00:00
Summary:     Adding some docs for Enzo-P loading.
Affected #:  1 file

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


https://bitbucket.org/yt_analysis/yt/commits/a184cbee5372/
Changeset:   a184cbee5372
User:        brittonsmith
Date:        2017-06-15 22:45:19+00:00
Summary:     Adding tests for Enzo-P frontend.
Affected #:  4 files

diff -r 844319d899d2c387b45f69a62ea3318d17f5d94a -r a184cbee5372e4cf1d9d28cd3985c568cef0b28f tests/tests.yaml
--- a/tests/tests.yaml
+++ b/tests/tests.yaml
@@ -14,6 +14,9 @@
   local_enzo_003:
     - yt/frontends/enzo
 
+  local_enzo_p_001:
+    - yt/frontends/enzo_p/tests/test_outputs.py
+
   local_fits_001:
     - yt/frontends/fits/tests/test_outputs.py
 

diff -r 844319d899d2c387b45f69a62ea3318d17f5d94a -r a184cbee5372e4cf1d9d28cd3985c568cef0b28f yt/frontends/enzo_p/api.py
--- a/yt/frontends/enzo_p/api.py
+++ b/yt/frontends/enzo_p/api.py
@@ -24,3 +24,5 @@
 
 from .io import \
      EnzoPIOHandler
+
+from . import tests

diff -r 844319d899d2c387b45f69a62ea3318d17f5d94a -r a184cbee5372e4cf1d9d28cd3985c568cef0b28f yt/frontends/enzo_p/tests/test_outputs.py
--- /dev/null
+++ b/yt/frontends/enzo_p/tests/test_outputs.py
@@ -0,0 +1,78 @@
+"""
+Enzo-P frontend tests
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2017, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+from yt.utilities.on_demand_imports import \
+    _h5py as h5py
+import numpy as np
+
+from yt.testing import \
+    assert_almost_equal, \
+    assert_equal, \
+    requires_file, \
+    units_override_check, \
+    assert_array_equal
+from yt.utilities.answer_testing.framework import \
+    create_obj, \
+    data_dir_load, \
+    requires_ds, \
+    PixelizedProjectionValuesTest, \
+    FieldValuesTest
+from yt.frontends.enzo_p.api import EnzoPDataset
+
+_fields = ("density", "total_energy",
+           "velocity_x", "velocity_y")
+
+hello_world = "hello-0200/hello-0200.block_list"
+
+
+ at requires_file(hello_world)
+def test_EnzoPDataset():
+    assert isinstance(data_dir_load(hello_world), EnzoPDataset)
+
+ at requires_ds(hello_world)
+def test_hello_world():
+    ds = data_dir_load(hello_world)
+
+    dso = [ None, ("sphere", ("max", (0.25, 'unitary')))]
+    for dobj_name in dso:
+        for field in _fields:
+            for axis in [0, 1, 2]:
+                for weight_field in [None, "density"]:
+                    yield PixelizedProjectionValuesTest(
+                        hello_world, axis, field, weight_field,
+                        dobj_name)
+            yield FieldValuesTest(hello_world, field, dobj_name)
+        dobj = create_obj(ds, dobj_name)
+        s1 = dobj["ones"].sum()
+        s2 = sum(mask.sum() for block, mask in dobj.blocks)
+        assert_equal(s1, s2)
+
+ at requires_file(hello_world)
+def test_hierarchy():
+    ds = data_dir_load(hello_world)
+
+    fh = h5py.File(ds.index.grids[0].filename, "r")
+    for grid in ds.index.grids:
+        assert_array_equal(
+            grid.LeftEdge.d, fh[grid.block_name].attrs["enzo_GridLeftEdge"])
+        assert_array_equal(
+            ds.index.grid_left_edge[grid.id], grid.LeftEdge)
+        assert_array_equal(
+            ds.index.grid_right_edge[grid.id], grid.RightEdge)
+        for child in grid.Children:
+            assert (child.LeftEdge >= grid.LeftEdge).all()
+            assert (child.RightEdge <= grid.RightEdge).all()
+            assert child.Parent.id == grid.id
+    fh.close()


https://bitbucket.org/yt_analysis/yt/commits/f2f5c185fff9/
Changeset:   f2f5c185fff9
User:        brittonsmith
Date:        2017-06-15 22:58:05+00:00
Summary:     Remove unused imports.
Affected #:  1 file

diff -r a184cbee5372e4cf1d9d28cd3985c568cef0b28f -r f2f5c185fff99b39b81cef2e6ff26843d8df89b4 yt/frontends/enzo_p/tests/test_outputs.py
--- a/yt/frontends/enzo_p/tests/test_outputs.py
+++ b/yt/frontends/enzo_p/tests/test_outputs.py
@@ -18,10 +18,8 @@
 import numpy as np
 
 from yt.testing import \
-    assert_almost_equal, \
     assert_equal, \
     requires_file, \
-    units_override_check, \
     assert_array_equal
 from yt.utilities.answer_testing.framework import \
     create_obj, \
@@ -74,5 +72,5 @@
         for child in grid.Children:
             assert (child.LeftEdge >= grid.LeftEdge).all()
             assert (child.RightEdge <= grid.RightEdge).all()
-            assert child.Parent.id == grid.id
+            assert_equal(child.Parent.id, grid.id)
     fh.close()


https://bitbucket.org/yt_analysis/yt/commits/9779c454903c/
Changeset:   9779c454903c
User:        brittonsmith
Date:        2017-06-16 16:45:39+00:00
Summary:     Remove unused import.
Affected #:  1 file

diff -r f2f5c185fff99b39b81cef2e6ff26843d8df89b4 -r 9779c454903c9df0abf28b57cde16b12be62833b yt/frontends/enzo_p/tests/test_outputs.py
--- a/yt/frontends/enzo_p/tests/test_outputs.py
+++ b/yt/frontends/enzo_p/tests/test_outputs.py
@@ -15,7 +15,6 @@
 
 from yt.utilities.on_demand_imports import \
     _h5py as h5py
-import numpy as np
 
 from yt.testing import \
     assert_equal, \


https://bitbucket.org/yt_analysis/yt/commits/6a3f77cea4e3/
Changeset:   6a3f77cea4e3
User:        brittonsmith
Date:        2017-06-23 02:40:49+00:00
Summary:     Use ds associated with Grid object.
Affected #:  1 file

diff -r 9779c454903c9df0abf28b57cde16b12be62833b -r 6a3f77cea4e3e77fb7ecb569a1b345354efc114f yt/frontends/enzo_p/data_structures.py
--- a/yt/frontends/enzo_p/data_structures.py
+++ b/yt/frontends/enzo_p/data_structures.py
@@ -69,7 +69,7 @@
     def get_parent_id(self, desc_block_name):
         if self.block_name == desc_block_name:
             raise RuntimeError("Child and parent are the same!")
-        dim = self.index.ds.dimensionality
+        dim = self.ds.dimensionality
         d_block = desc_block_name[1:].replace(":", "")
         parent = self
 
@@ -84,8 +84,7 @@
     def add_child(self, child):
         if self._children_ids is None:
             self._children_ids = \
-            -1*np.ones(self._refine_by**
-                       self.index.ds.dimensionality,
+            -1*np.ones(self._refine_by**self.ds.dimensionality,
                        dtype=np.int64)
 
         a_block =  self.block_name[1:].replace(":", "")


https://bitbucket.org/yt_analysis/yt/commits/fb9b5b8183e3/
Changeset:   fb9b5b8183e3
User:        brittonsmith
Date:        2017-06-23 17:34:24+00:00
Summary:     seek does not return position in python2.
Affected #:  1 file

diff -r 6a3f77cea4e3e77fb7ecb569a1b345354efc114f -r fb9b5b8183e3b82cf654929b923e904bc5af6967 yt/frontends/enzo_p/data_structures.py
--- a/yt/frontends/enzo_p/data_structures.py
+++ b/yt/frontends/enzo_p/data_structures.py
@@ -124,10 +124,12 @@
     def _count_grids(self):
         fblock_size = 32768
         f = open(self.ds.parameter_filename, "r")
-        file_size = f.seek(0, 2)
+        f.seek(0, 2)
+        file_size = f.tell()
         nblocks = np.ceil(float(file_size) /
                           fblock_size).astype(np.int64)
-        offset = f.seek(0)
+        f.seek(0)
+        offset = f.tell()
         ngrids = 0
         for ib in range(nblocks):
             my_block = min(fblock_size, file_size - offset)
@@ -144,10 +146,12 @@
         pbar = get_pbar("Parsing Hierarchy ", self.num_grids)
         f = open(self.ds.parameter_filename, "r")
         fblock_size = 32768
-        file_size = f.seek(0, 2)
+        f.seek(0, 2)
+        file_size = f.tell()
         nblocks = np.ceil(float(file_size) /
                           fblock_size).astype(np.int64)
-        offset = f.seek(0)
+        f.seek(0)
+        offset = f.tell()
         lstr = ""
         # place child blocks after the root blocks
         rbdim = self.ds.root_block_dimensions


https://bitbucket.org/yt_analysis/yt/commits/bab566948258/
Changeset:   bab566948258
User:        brittonsmith
Date:        2017-06-23 17:55:53+00:00
Summary:     Remove random grid sampling and active particle stuff from field detection.
Affected #:  2 files

diff -r fb9b5b8183e3b82cf654929b923e904bc5af6967 -r bab56694825855ead177bdb07b2d1ab52324c34c yt/frontends/enzo_p/data_structures.py
--- a/yt/frontends/enzo_p/data_structures.py
+++ b/yt/frontends/enzo_p/data_structures.py
@@ -233,30 +233,10 @@
         self.field_list = []
         # Do this only on the root processor to save disk work.
         if self.comm.rank in (0, None):
-            mylog.info("Gathering a field list (this may take a moment.)")
-            field_list = set()
-            random_sample = self._generate_random_grids()
-            for grid in random_sample:
-                if not hasattr(grid, 'filename'): continue
-                try:
-                    gf = self.io._read_field_names(grid)
-                except self.io._read_exception:
-                    raise IOError("Grid %s is a bit funky?", grid.id)
-                mylog.debug("Grid %s has: %s", grid.id, gf)
-                field_list = field_list.union(gf)
-            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)
+            # Just check the first grid.
+            grid = self.grids[0]
+            field_list = self.io._read_field_names(grid)
+            mylog.debug("Grid %s has: %s", grid.id, field_list)
             ptypes = self.dataset.particle_types
             ptypes_raw = self.dataset.particle_types_raw
         else:
@@ -267,24 +247,6 @@
         self.dataset.particle_types = list(self.comm.mpi_bcast(ptypes))
         self.dataset.particle_types_raw = list(self.comm.mpi_bcast(ptypes_raw))
 
-    def _generate_random_grids(self):
-        if self.num_grids > 40:
-            starter = np.random.randint(0, 20)
-            random_sample = np.mgrid[starter:len(self.grids)-1:20j].astype("int32")
-            # We also add in a bit to make sure that some of the grids have
-            # particles
-            gwp = self.grid_particle_count > 0
-            if np.any(gwp) and not np.any(gwp[(random_sample,)]):
-                # We just add one grid.  This is not terribly efficient.
-                first_grid = np.where(gwp)[0][0]
-                random_sample.resize((21,))
-                random_sample[-1] = first_grid
-                mylog.debug("Added additional grid %s", first_grid)
-            mylog.debug("Checking grids: %s", random_sample.tolist())
-        else:
-            random_sample = np.mgrid[0:max(len(self.grids),1)].astype("int32")
-        return self.grids[(random_sample,)]
-
 class EnzoPDataset(Dataset):
     """
     Enzo-P-specific output, set at a fixed time.

diff -r fb9b5b8183e3b82cf654929b923e904bc5af6967 -r bab56694825855ead177bdb07b2d1ab52324c34c yt/frontends/enzo_p/io.py
--- a/yt/frontends/enzo_p/io.py
+++ b/yt/frontends/enzo_p/io.py
@@ -13,6 +13,8 @@
 # The full license is in the file COPYING.txt, distributed with this software.
 #-----------------------------------------------------------------------------
 
+from yt.utilities.exceptions import \
+    YTException
 from yt.utilities.io_handler import \
     BaseIOHandler
 from yt.extern.six import b, iteritems
@@ -42,7 +44,9 @@
         try:
             group = f[grid.block_name]
         except KeyError:
-            group = f
+            raise YTException(
+                message="Grid %s is missing from data file %s." %
+                (grid.block_name, grid.filename), ds=self.ds)
         fields = []
         dtypes = set([])
         for name, v in iteritems(group):


https://bitbucket.org/yt_analysis/yt/commits/50348a24b3de/
Changeset:   50348a24b3de
User:        brittonsmith
Date:        2017-06-23 17:57:53+00:00
Summary:     except Exception.
Affected #:  1 file

diff -r bab56694825855ead177bdb07b2d1ab52324c34c -r 50348a24b3de0508d0c7a352812af6332f6e586c yt/frontends/enzo_p/data_structures.py
--- a/yt/frontends/enzo_p/data_structures.py
+++ b/yt/frontends/enzo_p/data_structures.py
@@ -346,6 +346,6 @@
                 get_block_info(block)
                 if not os.path.exists(os.path.join(ddir, block_file)):
                     return False
-        except:
+        except Exception:
             return False
         return True


https://bitbucket.org/yt_analysis/yt/commits/8c521408507e/
Changeset:   8c521408507e
User:        brittonsmith
Date:        2017-06-24 01:31:03+00:00
Summary:     Remove unused exception.
Affected #:  1 file

diff -r 50348a24b3de0508d0c7a352812af6332f6e586c -r 8c521408507efadd335160f0897c7384c8cf1230 yt/frontends/enzo_p/io.py
--- a/yt/frontends/enzo_p/io.py
+++ b/yt/frontends/enzo_p/io.py
@@ -73,10 +73,6 @@
         f.close()
         return fields
 
-    @property
-    def _read_exception(self):
-        return (KeyError,)
-
     def _read_particle_coords(self, chunks, ptf):
         for rv in self._read_particle_fields(chunks, ptf, None):
             yield rv


https://bitbucket.org/yt_analysis/yt/commits/a3d48abcba9a/
Changeset:   a3d48abcba9a
User:        brittonsmith
Date:        2017-06-26 17:45:19+00:00
Summary:     Remove py2 incompatible use of string.split.
Affected #:  1 file

diff -r 8c521408507efadd335160f0897c7384c8cf1230 -r a3d48abcba9a22160b0fece959b2eda1c183e2de yt/frontends/enzo_p/io.py
--- a/yt/frontends/enzo_p/io.py
+++ b/yt/frontends/enzo_p/io.py
@@ -54,12 +54,12 @@
                 continue
             # mesh fields are "field <name>"
             if name.startswith("field"):
-                dummy, fname = name.split(maxsplit=1)
+                dummy, fname = name.split(" ", 1)
                 fields.append(("enzop", fname))
                 dtypes.add(v.dtype)
             # particle fields are "particle <type><name>"
             else:
-                dummy, ftype, fname = name.split(maxsplit=2)
+                dummy, ftype, fname = name.split(" ", 2)
                 fields.append((ftype, fname))
 
         if len(dtypes) == 1:


https://bitbucket.org/yt_analysis/yt/commits/b8ae5d5e6168/
Changeset:   b8ae5d5e6168
User:        ngoldbaum
Date:        2017-06-27 18:14:47+00:00
Summary:     Merge pull request #1447 from brittonsmith/enzop

Add Enzo-P frontend
Affected #:  12 files

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

diff -r 272125cbb417b446f20fec4d38e3c1e4efa54375 -r b8ae5d5e61686d61d691f33ace5397ffcf7f5c35 tests/tests.yaml
--- a/tests/tests.yaml
+++ b/tests/tests.yaml
@@ -14,6 +14,9 @@
   local_enzo_003:
     - yt/frontends/enzo
 
+  local_enzo_p_001:
+    - yt/frontends/enzo_p/tests/test_outputs.py
+
   local_fits_001:
     - yt/frontends/fits/tests/test_outputs.py
 

diff -r 272125cbb417b446f20fec4d38e3c1e4efa54375 -r b8ae5d5e61686d61d691f33ace5397ffcf7f5c35 yt/frontends/api.py
--- a/yt/frontends/api.py
+++ b/yt/frontends/api.py
@@ -25,6 +25,7 @@
     'chombo',
     'eagle',
     'enzo',
+    'enzo_p',
     'exodus_ii',
     'fits',
     'flash',

diff -r 272125cbb417b446f20fec4d38e3c1e4efa54375 -r b8ae5d5e61686d61d691f33ace5397ffcf7f5c35 yt/frontends/enzo_p/api.py
--- /dev/null
+++ b/yt/frontends/enzo_p/api.py
@@ -0,0 +1,28 @@
+"""
+API for yt.frontends.enzo_p
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2017, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+from .data_structures import \
+    EnzoPGrid, \
+    EnzoPHierarchy, \
+    EnzoPDataset
+
+from .fields import \
+    EnzoPFieldInfo
+add_enzop_field = EnzoPFieldInfo.add_field
+
+from .io import \
+     EnzoPIOHandler
+
+from . import tests

diff -r 272125cbb417b446f20fec4d38e3c1e4efa54375 -r b8ae5d5e61686d61d691f33ace5397ffcf7f5c35 yt/frontends/enzo_p/data_structures.py
--- /dev/null
+++ b/yt/frontends/enzo_p/data_structures.py
@@ -0,0 +1,351 @@
+"""
+Data structures for Enzo-P
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2017, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+from yt.utilities.on_demand_imports import \
+    _h5py as h5py
+import numpy as np
+import os
+import stat
+
+from yt.data_objects.grid_patch import \
+    AMRGridPatch
+from yt.data_objects.static_output import \
+    Dataset
+from yt.fields.field_info_container import \
+    NullFunc
+from yt.funcs import \
+    ensure_tuple, \
+    get_pbar, \
+    setdefaultattr
+from yt.geometry.grid_geometry_handler import \
+    GridIndex
+from yt.utilities.logger import \
+    ytLogger as mylog
+
+from yt.frontends.enzo_p.fields import \
+    EnzoPFieldInfo
+from yt.frontends.enzo_p.misc import \
+    get_block_info, \
+    get_child_index, \
+    get_root_blocks, \
+    get_root_block_id, \
+    is_parent
+
+class EnzoPGrid(AMRGridPatch):
+    """
+    Class representing a single EnzoP Grid instance.
+    """
+
+    _id_offset = 0
+    _refine_by = 2
+
+    def __init__(self, id, index, block_name, filename = None):
+        """
+        Returns an instance of EnzoPGrid with *id*, associated with
+        *filename* and *index*.
+        """
+        # All of the field parameters will be passed to us as needed.
+        AMRGridPatch.__init__(self, id, filename=filename, index=index)
+        self.block_name = block_name
+        self._children_ids = None
+        self._parent_id = -1
+        self.Level = -1
+
+    def __repr__(self):
+        return "EnzoPGrid_%04d" % self.id
+
+    def get_parent_id(self, desc_block_name):
+        if self.block_name == desc_block_name:
+            raise RuntimeError("Child and parent are the same!")
+        dim = self.ds.dimensionality
+        d_block = desc_block_name[1:].replace(":", "")
+        parent = self
+
+        while True:
+            a_block = parent.block_name[1:].replace(":", "")
+            gengap = (len(d_block) - len(a_block)) / dim
+            if gengap <= 1:
+                return parent.id
+            cid = get_child_index(a_block, d_block)
+            parent = self.index.grids[parent._children_ids[cid]]
+
+    def add_child(self, child):
+        if self._children_ids is None:
+            self._children_ids = \
+            -1*np.ones(self._refine_by**self.ds.dimensionality,
+                       dtype=np.int64)
+
+        a_block =  self.block_name[1:].replace(":", "")
+        d_block = child.block_name[1:].replace(":", "")
+        cid = get_child_index(a_block, d_block)
+        self._children_ids[cid] = child.id
+
+    @property
+    def Parent(self):
+        if self._parent_id == -1: return None
+        return self.index.grids[self._parent_id]
+
+    @property
+    def Children(self):
+        if self._children_ids is None:
+            return []
+        return [self.index.grids[cid]
+                for cid in self._children_ids]
+
+class EnzoPHierarchy(GridIndex):
+
+    _strip_path = False
+    grid = EnzoPGrid
+    _preload_implemented = True
+
+    def __init__(self, ds, dataset_type):
+
+        self.dataset_type = dataset_type
+        self.directory = os.path.dirname(ds.parameter_filename)
+        self.index_filename = ds.parameter_filename
+        if os.path.getsize(self.index_filename) == 0:
+            raise IOError(-1,"File empty", self.index_filename)
+
+        GridIndex.__init__(self, ds, dataset_type)
+        self.dataset.dataset_type = self.dataset_type
+
+    def _count_grids(self):
+        fblock_size = 32768
+        f = open(self.ds.parameter_filename, "r")
+        f.seek(0, 2)
+        file_size = f.tell()
+        nblocks = np.ceil(float(file_size) /
+                          fblock_size).astype(np.int64)
+        f.seek(0)
+        offset = f.tell()
+        ngrids = 0
+        for ib in range(nblocks):
+            my_block = min(fblock_size, file_size - offset)
+            buff = f.read(my_block)
+            ngrids += buff.count("\n")
+            offset += my_block
+        f.close()
+        self.num_grids = ngrids
+        self.dataset_type = "enzo_p"
+
+    def _parse_index(self):
+        self.grids = np.empty(self.num_grids, dtype='object')
+
+        pbar = get_pbar("Parsing Hierarchy ", self.num_grids)
+        f = open(self.ds.parameter_filename, "r")
+        fblock_size = 32768
+        f.seek(0, 2)
+        file_size = f.tell()
+        nblocks = np.ceil(float(file_size) /
+                          fblock_size).astype(np.int64)
+        f.seek(0)
+        offset = f.tell()
+        lstr = ""
+        # place child blocks after the root blocks
+        rbdim = self.ds.root_block_dimensions
+        nroot_blocks = rbdim.prod()
+        child_id = nroot_blocks
+
+        last_pid = None
+        for ib in range(nblocks):
+            fblock = min(fblock_size, file_size - offset)
+            buff = lstr + f.read(fblock)
+            bnl = 0
+            for inl in range(buff.count("\n")):
+                nnl = buff.find("\n", bnl)
+                line = buff[bnl:nnl]
+                block_name, block_file = line.split()
+                level, left, right = get_block_info(block_name)
+
+                rbindex = get_root_block_id(block_name)
+                rbid = rbindex[0] * rbdim[1:].prod() + \
+                  rbindex[1] * rbdim[2:].prod() + rbindex[2]
+                if level == 0:
+                    grid_id = rbid
+                    parent_id = -1
+                else:
+                    grid_id = child_id
+                    # Try the last parent_id first
+                    if last_pid is not None and \
+                      is_parent(self.grids[last_pid].block_name, block_name):
+                        parent_id = last_pid
+                    else:
+                        parent_id = self.grids[rbid].get_parent_id(block_name)
+                    last_pid = parent_id
+                    child_id += 1
+
+                my_grid = self.grid(
+                    grid_id, self, block_name,
+                    filename=os.path.join(self.directory, block_file))
+                my_grid.Level = level
+                my_grid._parent_id = parent_id
+
+                self.grids[grid_id]           = my_grid
+                self.grid_levels[grid_id]     = level
+                self.grid_left_edge[grid_id]  = left
+                self.grid_right_edge[grid_id] = right
+                self.grid_dimensions[grid_id] = self.ds.active_grid_dimensions
+
+                if level > 0:
+                    self.grids[parent_id].add_child(my_grid)
+
+                bnl = nnl + 1
+                pbar.update(1)
+            lstr = buff[bnl:]
+            offset += fblock
+
+        f.close()
+        pbar.finish()
+
+        slope = self.ds.domain_width / \
+          self.ds.arr(np.ones(3), "code_length")
+        self.grid_left_edge   = self.grid_left_edge  * slope + \
+          self.ds.domain_left_edge
+        self.grid_right_edge  = self.grid_right_edge * slope + \
+          self.ds.domain_left_edge
+
+    def _populate_grid_objects(self):
+        for g in self.grids:
+            g._prepare_grid()
+            g._setup_dx()
+        self.max_level = self.grid_levels.max()
+
+    def _setup_derived_fields(self):
+        super(EnzoPHierarchy, self)._setup_derived_fields()
+        for fname, field in self.ds.field_info.items():
+            if not field.particle_type: continue
+            if isinstance(fname, tuple): continue
+            if field._function is NullFunc: continue
+
+    def _detect_output_fields(self):
+        self.field_list = []
+        # Do this only on the root processor to save disk work.
+        if self.comm.rank in (0, None):
+            # Just check the first grid.
+            grid = self.grids[0]
+            field_list = self.io._read_field_names(grid)
+            mylog.debug("Grid %s has: %s", grid.id, field_list)
+            ptypes = self.dataset.particle_types
+            ptypes_raw = self.dataset.particle_types_raw
+        else:
+            field_list = None
+            ptypes = None
+            ptypes_raw = None
+        self.field_list = list(self.comm.mpi_bcast(field_list))
+        self.dataset.particle_types = list(self.comm.mpi_bcast(ptypes))
+        self.dataset.particle_types_raw = list(self.comm.mpi_bcast(ptypes_raw))
+
+class EnzoPDataset(Dataset):
+    """
+    Enzo-P-specific output, set at a fixed time.
+    """
+    _index_class = EnzoPHierarchy
+    _field_info_class = EnzoPFieldInfo
+
+    def __init__(self, filename, dataset_type=None,
+                 file_style = None,
+                 parameter_override = None,
+                 conversion_override = None,
+                 storage_filename = None,
+                 units_override=None,
+                 unit_system="cgs"):
+        """
+        This class is a stripped down class that simply reads and parses
+        *filename* without looking at the index.  *dataset_type* gets passed
+        to the index to pre-determine the style of data-output.  However,
+        it is not strictly necessary.  Optionally you may specify a
+        *parameter_override* dictionary that will override anything in the
+        paarmeter file and a *conversion_override* dictionary that consists
+        of {fieldname : conversion_to_cgs} that will override the #DataCGS.
+        """
+        self.fluid_types += ("enzop",)
+        if parameter_override is None: parameter_override = {}
+        self._parameter_override = parameter_override
+        if conversion_override is None: conversion_override = {}
+        self._conversion_override = conversion_override
+        self.storage_filename = storage_filename
+        Dataset.__init__(self, filename, dataset_type, file_style=file_style,
+                         units_override=units_override, unit_system=unit_system)
+
+    def _parse_parameter_file(self):
+        """
+        Parses the parameter file and establishes the various
+        dictionaries.
+        """
+
+        f = open(self.parameter_filename, "r")
+        # get dimension from first block name
+        b0, fn0 = f.readline().strip().split()
+        level0, left0, right0 = get_block_info(b0, min_dim=0)
+        root_blocks = get_root_blocks(b0)
+        f.close()
+        self.dimensionality = left0.size
+        self.periodicity = \
+          ensure_tuple(np.ones(self.dimensionality, dtype=bool))
+
+        fh = h5py.File(os.path.join(self.directory, fn0), "r")
+        self.domain_left_edge  = fh.attrs["lower"]
+        self.domain_right_edge = fh.attrs["upper"]
+
+        # all blocks are the same size
+        ablock = fh[list(fh.keys())[0]]
+        self.current_time = ablock.attrs["time"][0]
+        gsi = ablock.attrs["enzo_GridStartIndex"]
+        gei = ablock.attrs["enzo_GridEndIndex"]
+        self.ghost_zones = gsi[0]
+        self.root_block_dimensions = root_blocks
+        self.active_grid_dimensions = gei - gsi + 1
+        self.grid_dimensions = ablock.attrs["enzo_GridDimension"]
+        self.domain_dimensions = root_blocks * self.active_grid_dimensions
+        fh.close()
+
+        self.periodicity += (False, ) * (3 - self.dimensionality)
+
+        # WIP hard-coded for now
+        self.refine_by = 2
+        self.cosmological_simulation = 0
+        self.gamma = 5. / 3.
+        self.particle_types = ()
+        self.particle_types_raw = self.particle_types
+        self.unique_identifier = \
+          str(int(os.stat(self.parameter_filename)[stat.ST_CTIME]))
+
+    def _set_code_unit_attributes(self):
+        setdefaultattr(self, 'length_unit', self.quan(1, "cm"))
+        setdefaultattr(self, 'mass_unit', self.quan(1, "g"))
+        setdefaultattr(self, 'time_unit', self.quan(1, "s"))
+        setdefaultattr(self, 'velocity_unit',
+                       self.length_unit / self.time_unit)
+        magnetic_unit = np.sqrt(4*np.pi * self.mass_unit /
+                                (self.time_unit**2 * self.length_unit))
+        magnetic_unit = np.float64(magnetic_unit.in_cgs())
+        setdefaultattr(self, 'magnetic_unit',
+                       self.quan(magnetic_unit, "gauss"))
+
+    @classmethod
+    def _is_valid(cls, *args, **kwargs):
+        fn = args[0]
+        ddir = os.path.dirname(fn)
+        if not fn.endswith(".block_list"):
+            return False
+        try:
+            with open(fn, "r") as f:
+                block, block_file = f.readline().strip().split()
+                get_block_info(block)
+                if not os.path.exists(os.path.join(ddir, block_file)):
+                    return False
+        except Exception:
+            return False
+        return True

diff -r 272125cbb417b446f20fec4d38e3c1e4efa54375 -r b8ae5d5e61686d61d691f33ace5397ffcf7f5c35 yt/frontends/enzo_p/definitions.py
--- /dev/null
+++ b/yt/frontends/enzo_p/definitions.py
@@ -0,0 +1,15 @@
+"""
+Definitions specific to Enzo-P
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2017, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+

diff -r 272125cbb417b446f20fec4d38e3c1e4efa54375 -r b8ae5d5e61686d61d691f33ace5397ffcf7f5c35 yt/frontends/enzo_p/fields.py
--- /dev/null
+++ b/yt/frontends/enzo_p/fields.py
@@ -0,0 +1,52 @@
+"""
+Fields specific to Enzo-P
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2013, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+from yt.fields.field_info_container import \
+    FieldInfoContainer
+
+rho_units = "code_mass / code_length**3"
+vel_units = "code_velocity"
+acc_units = "code_velocity / code_time"
+energy_units = "code_velocity**2"
+
+known_species_names = {
+}
+
+class EnzoPFieldInfo(FieldInfoContainer):
+    known_other_fields = (
+        ("velocity_x", (vel_units, ["velocity_x"], None)),
+        ("velocity_y", (vel_units, ["velocity_y"], None)),
+        ("velocity_z", (vel_units, ["velocity_z"], None)),
+        ("acceleration_x", (acc_units, ["acceleration_x"], None)),
+        ("acceleration_y", (acc_units, ["acceleration_y"], None)),
+        ("acceleration_z", (acc_units, ["acceleration_z"], None)),
+        ("density", (rho_units, ["density"], None)),
+        ("total_density", (rho_units, ["total_density"], None)),
+        ("total_energy", (energy_units, ["total_energy"], None)),
+        ("internal_energy", (energy_units, ["internal_energy"], None)),
+    )
+
+    known_particle_fields = (
+        ("x", ("code_length", [], None)),
+        ("y", ("code_length", [], None)),
+        ("z", ("code_length", [], None)),
+        ("vx", (vel_units, [], None)),
+        ("vy", (vel_units, [], None)),
+        ("vz", (vel_units, [], None)),
+        ("ax", (acc_units, [], None)),
+        ("ay", (acc_units, [], None)),
+        ("az", (acc_units, [], None)),
+        ("mass", ("code_mass", [], None)),
+    )

diff -r 272125cbb417b446f20fec4d38e3c1e4efa54375 -r b8ae5d5e61686d61d691f33ace5397ffcf7f5c35 yt/frontends/enzo_p/io.py
--- /dev/null
+++ b/yt/frontends/enzo_p/io.py
@@ -0,0 +1,158 @@
+"""
+Enzo-P-specific IO functions
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2017, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+from yt.utilities.exceptions import \
+    YTException
+from yt.utilities.io_handler import \
+    BaseIOHandler
+from yt.extern.six import b, iteritems
+from yt.utilities.on_demand_imports import _h5py as h5py
+import numpy as np
+
+
+_convert_mass = ("particle_mass","mass")
+
+_particle_position_names = {}
+
+class EnzoPIOHandler(BaseIOHandler):
+
+    _dataset_type = "enzo_p"
+    _base = slice(None)
+    _field_dtype = "float64"
+
+    def __init__(self, *args, **kwargs):
+        super(EnzoPIOHandler, self).__init__(*args, **kwargs)
+        self._base = self.ds.dimensionality * \
+          (slice(self.ds.ghost_zones,
+                 -self.ds.ghost_zones),)
+
+    def _read_field_names(self, grid):
+        if grid.filename is None: return []
+        f = h5py.File(grid.filename, "r")
+        try:
+            group = f[grid.block_name]
+        except KeyError:
+            raise YTException(
+                message="Grid %s is missing from data file %s." %
+                (grid.block_name, grid.filename), ds=self.ds)
+        fields = []
+        dtypes = set([])
+        for name, v in iteritems(group):
+            if not hasattr(v, "shape") or v.dtype == "O":
+                continue
+            # mesh fields are "field <name>"
+            if name.startswith("field"):
+                dummy, fname = name.split(" ", 1)
+                fields.append(("enzop", fname))
+                dtypes.add(v.dtype)
+            # particle fields are "particle <type><name>"
+            else:
+                dummy, ftype, fname = name.split(" ", 2)
+                fields.append((ftype, fname))
+
+        if len(dtypes) == 1:
+            # Now, if everything we saw was the same dtype, we can go ahead and
+            # set it here.  We do this because it is a HUGE savings for 32 bit
+            # floats, since our numpy copying/casting is way faster than
+            # h5py's, for some reason I don't understand.  This does *not* need
+            # to be correct -- it will get fixed later -- it just needs to be
+            # okay for now.
+            self._field_dtype = list(dtypes)[0]
+        f.close()
+        return fields
+
+    def _read_particle_coords(self, chunks, ptf):
+        for rv in self._read_particle_fields(chunks, ptf, None):
+            yield rv
+
+    def _read_particle_fields(self, chunks, ptf, selector):
+        chunks = list(chunks)
+        for chunk in chunks: # These should be organized by grid filename
+            f = None
+            for g in chunk.objs:
+                if g.filename is None: continue
+                if f is None:
+                    f = h5py.File(g.filename, "r")
+                nap = sum(g.NumberOfActiveParticles.values())
+                if g.NumberOfParticles == 0 and nap == 0:
+                    continue
+                ds = f.get(g.block_name)
+                for ptype, field_list in sorted(ptf.items()):
+                    if ptype != "io":
+                        if g.NumberOfActiveParticles[ptype] == 0: continue
+                        pds = ds.get("Particles/%s" % ptype)
+                    else:
+                        pds = ds
+                    pn = _particle_position_names.get(ptype,
+                            r"particle_position_%s")
+                    x, y, z = (np.asarray(pds.get(pn % ax).value, dtype="=f8")
+                               for ax in 'xyz')
+                    if selector is None:
+                        # This only ever happens if the call is made from
+                        # _read_particle_coords.
+                        yield ptype, (x, y, z)
+                        continue
+                    mask = selector.select_points(x, y, z, 0.0)
+                    if mask is None: continue
+                    for field in field_list:
+                        data = np.asarray(pds.get(field).value, "=f8")
+                        if field in _convert_mass:
+                            data *= g.dds.prod(dtype="f8")
+                        yield (ptype, field), data[mask]
+            if f: f.close()
+
+    def io_iter(self, chunks, fields):
+        for chunk in chunks:
+            fid = None
+            filename = -1
+            for obj in chunk.objs:
+                if obj.filename is None: continue
+                if obj.filename != filename:
+                    # Note one really important thing here: even if we do
+                    # implement LRU caching in the _read_obj_field function,
+                    # we'll still be doing file opening and whatnot.  This is a
+                    # problem, but one we can return to.
+                    if fid is not None:
+                        fid.close()
+                    fid = h5py.h5f.open(b(obj.filename), h5py.h5f.ACC_RDONLY)
+                    filename = obj.filename
+                for field in fields:
+                    data = None
+                    yield field, obj, self._read_obj_field(
+                        obj, field, (fid, data))
+        if fid is not None:
+            fid.close()
+
+    def _read_obj_field(self, obj, field, fid_data):
+        if fid_data is None: fid_data = (None, None)
+        fid, data = fid_data
+        if fid is None:
+            close = True
+            fid = h5py.h5f.open(b(obj.filename), h5py.h5f.ACC_RDONLY)
+        else:
+            close = False
+        ftype, fname = field
+        node = "/%s/field %s" % (obj.block_name, fname)
+        dg = h5py.h5d.open(fid, b(node))
+        rdata = np.empty(self.ds.grid_dimensions[:self.ds.dimensionality],
+                         dtype=self._field_dtype)
+        dg.read(h5py.h5s.ALL, h5py.h5s.ALL, rdata)
+        if close:
+            fid.close()
+        data = rdata[self._base].T
+        if self.ds.dimensionality < 3:
+            nshape = data.shape + (1,)*(3 - self.ds.dimensionality)
+            data  = np.reshape(data, nshape)
+        return data

diff -r 272125cbb417b446f20fec4d38e3c1e4efa54375 -r b8ae5d5e61686d61d691f33ace5397ffcf7f5c35 yt/frontends/enzo_p/misc.py
--- /dev/null
+++ b/yt/frontends/enzo_p/misc.py
@@ -0,0 +1,77 @@
+"""
+Miscellaneous functions that are Enzo-P-specific
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2017, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+import numpy as np
+
+def bdecode(block):
+    if ":" in block:
+        level = len(block) - block.find(":") - 1
+    else:
+        level = 0
+    bst   = block.replace(":", "")
+    d     = float(2**len(bst))
+    left  = int(bst, 2)
+    right = left + 1
+    left  /= d
+    right /= d
+    return level, left, right
+
+def get_block_info(block, min_dim=3):
+    mybs = block[1:].split("_")
+    dim = max(len(mybs), min_dim)
+    left = np.zeros(dim)
+    right = np.ones(dim)
+    for i, myb in enumerate(mybs):
+        level, left[i], right[i] = bdecode(myb)
+    return level, left, right
+
+def get_root_blocks(block, min_dim=3):
+    mybs = block[1:].split("_")
+    dim = max(len(mybs), min_dim)
+    nb = np.ones(dim, dtype=int)
+    for i, myb in enumerate(mybs):
+        if ":" in myb:
+            s = myb.find(":")
+        else:
+            s = len(myb)
+        nb[i] = 2**s
+    return nb
+
+def get_root_block_id(block, min_dim=3):
+    mybs = block[1:].split("_")
+    dim = max(len(mybs), min_dim)
+    rbid = np.zeros(dim, dtype=int)
+    for i, myb in enumerate(mybs):
+        if ":" in myb:
+            s = myb.find(":")
+        else:
+            s = len(myb)
+        rbid[i] = int(myb[:s], 2)
+    return rbid
+
+def get_child_index(anc_id, desc_id):
+    cid = ""
+    for aind, dind in zip( anc_id.split("_"),
+                          desc_id.split("_")):
+        cid += dind[len(aind)]
+    cid = int(cid, 2)
+    return cid
+
+def is_parent(anc_block, desc_block):
+    for aind, dind in zip( anc_block.split("_"),
+                          desc_block.split("_")):
+        if not dind.startswith(aind):
+            return False
+    return True

diff -r 272125cbb417b446f20fec4d38e3c1e4efa54375 -r b8ae5d5e61686d61d691f33ace5397ffcf7f5c35 yt/frontends/enzo_p/tests/test_outputs.py
--- /dev/null
+++ b/yt/frontends/enzo_p/tests/test_outputs.py
@@ -0,0 +1,75 @@
+"""
+Enzo-P frontend tests
+
+
+
+"""
+
+#-----------------------------------------------------------------------------
+# Copyright (c) 2017, yt Development Team.
+#
+# Distributed under the terms of the Modified BSD License.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+from yt.utilities.on_demand_imports import \
+    _h5py as h5py
+
+from yt.testing import \
+    assert_equal, \
+    requires_file, \
+    assert_array_equal
+from yt.utilities.answer_testing.framework import \
+    create_obj, \
+    data_dir_load, \
+    requires_ds, \
+    PixelizedProjectionValuesTest, \
+    FieldValuesTest
+from yt.frontends.enzo_p.api import EnzoPDataset
+
+_fields = ("density", "total_energy",
+           "velocity_x", "velocity_y")
+
+hello_world = "hello-0200/hello-0200.block_list"
+
+
+ at requires_file(hello_world)
+def test_EnzoPDataset():
+    assert isinstance(data_dir_load(hello_world), EnzoPDataset)
+
+ at requires_ds(hello_world)
+def test_hello_world():
+    ds = data_dir_load(hello_world)
+
+    dso = [ None, ("sphere", ("max", (0.25, 'unitary')))]
+    for dobj_name in dso:
+        for field in _fields:
+            for axis in [0, 1, 2]:
+                for weight_field in [None, "density"]:
+                    yield PixelizedProjectionValuesTest(
+                        hello_world, axis, field, weight_field,
+                        dobj_name)
+            yield FieldValuesTest(hello_world, field, dobj_name)
+        dobj = create_obj(ds, dobj_name)
+        s1 = dobj["ones"].sum()
+        s2 = sum(mask.sum() for block, mask in dobj.blocks)
+        assert_equal(s1, s2)
+
+ at requires_file(hello_world)
+def test_hierarchy():
+    ds = data_dir_load(hello_world)
+
+    fh = h5py.File(ds.index.grids[0].filename, "r")
+    for grid in ds.index.grids:
+        assert_array_equal(
+            grid.LeftEdge.d, fh[grid.block_name].attrs["enzo_GridLeftEdge"])
+        assert_array_equal(
+            ds.index.grid_left_edge[grid.id], grid.LeftEdge)
+        assert_array_equal(
+            ds.index.grid_right_edge[grid.id], grid.RightEdge)
+        for child in grid.Children:
+            assert (child.LeftEdge >= grid.LeftEdge).all()
+            assert (child.RightEdge <= grid.RightEdge).all()
+            assert_equal(child.Parent.id, grid.id)
+    fh.close()

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