[Yt-svn] commit/yt: 6 new changesets

Bitbucket commits-noreply at bitbucket.org
Mon Apr 4 11:10:27 PDT 2011


6 new changesets in yt:

http://bitbucket.org/yt_analysis/yt/changeset/6697efe33f2c/
changeset:   r4065:6697efe33f2c
branch:      yt
user:        MatthewTurk
date:        2011-03-24 17:43:51
summary:     Adding in John's conversion of the Zoltan Hilbert Curve.  I've modified it to
work with 64-bits, which I believe I have done correctly.  Testing is still in
progress.
affected #:  4 files (4.6 KB)

--- a/yt/frontends/ramses/_ramses_reader.pyx	Wed Mar 23 19:53:53 2011 -0400
+++ b/yt/frontends/ramses/_ramses_reader.pyx	Thu Mar 24 12:43:51 2011 -0400
@@ -33,6 +33,9 @@
 cimport numpy as np
 cimport cython
 
+cdef extern:
+    unsigned long HilbertCurve3D(double *coord)
+
 cdef inline np.int64_t i64max(np.int64_t i0, np.int64_t i1):
     if i0 > i1: return i0
     return i1
@@ -547,7 +550,10 @@
                               np.ndarray[np.float64_t, ndim=2] right_edges,
                               np.ndarray[np.int32_t, ndim=2] grid_levels,
                               np.ndarray[np.int64_t, ndim=2] grid_file_locations,
-                              np.ndarray[np.int32_t, ndim=2] child_mask):
+                              np.ndarray[np.uint64_t, ndim=1] grid_hilbert_indices,
+                              np.ndarray[np.int32_t, ndim=2] child_mask,
+                              np.ndarray[np.float64_t, ndim=1] domain_left,
+                              np.ndarray[np.float64_t, ndim=1] domain_right):
         # We need to do simulation domains here
 
         cdef unsigned idomain, ilevel
@@ -562,10 +568,12 @@
         cdef int grid_ind = 0, grid_aind = 0
         cdef unsigned parent_ind
         cdef bint ci
-
+        cdef double pos[3]
         cdef double grid_half_width 
+        cdef unsigned long rv
 
         cdef np.int32_t rr
+        cdef int i
         cell_count = []
         level_cell_counts = {}
         for idomain in range(1, self.rsnap.m_header.ncpu + 1):
@@ -587,9 +595,11 @@
                         grid_it.next()
                         continue
                     gvec = local_tree.grid_pos_double(grid_it)
-                    left_edges[grid_aind, 0] = gvec.x - grid_half_width
-                    left_edges[grid_aind, 1] = gvec.y - grid_half_width
-                    left_edges[grid_aind, 2] = gvec.z - grid_half_width
+                    left_edges[grid_aind, 0] = pos[0] = gvec.x - grid_half_width
+                    left_edges[grid_aind, 1] = pos[1] = gvec.y - grid_half_width
+                    left_edges[grid_aind, 2] = pos[2] = gvec.z - grid_half_width
+                    for i in range(3):
+                        pos[i] = (pos[i] - domain_left[i]) / (domain_right[i] - domain_left[i])
                     right_edges[grid_aind, 0] = gvec.x + grid_half_width
                     right_edges[grid_aind, 1] = gvec.y + grid_half_width
                     right_edges[grid_aind, 2] = gvec.z + grid_half_width
@@ -598,6 +608,7 @@
                     father_it = grid_it.get_parent()
                     grid_file_locations[grid_aind, 0] = <np.int64_t> idomain
                     grid_file_locations[grid_aind, 1] = grid_ind - level_cell_counts[ilevel]
+                    grid_hilbert_indices[grid_aind] = HilbertCurve3D(pos)
                     parent_ind = father_it.get_absolute_position()
                     if ilevel > 0:
                         # We calculate the REAL parent index
@@ -850,3 +861,13 @@
             tr[1,i] = self.right_edge[i]
             tr[2,i] = self.dimensions[i]
         return tr
+
+def hilbert_position(pos):
+    cdef double coord[3]
+    cdef int i
+    cdef unsigned long new_pos
+    coord[0] = pos[0]
+    coord[1] = pos[1]
+    coord[2] = pos[2]
+    new_pos = HilbertCurve3D(coord)
+    return new_pos


--- a/yt/frontends/ramses/data_structures.py	Wed Mar 23 19:53:53 2011 -0400
+++ b/yt/frontends/ramses/data_structures.py	Thu Mar 24 12:43:51 2011 -0400
@@ -124,17 +124,23 @@
 
     def _count_grids(self):
         # We have to do all the patch-coalescing here.
+        LEVEL_OF_EDGE = 7
+        MAX_EDGE = (2 << (LEVEL_OF_EDGE- 1))
         level_info = self.tree_proxy.count_zones()
         num_ogrids = sum(level_info)
         ogrid_left_edge = na.zeros((num_ogrids,3), dtype='float64')
         ogrid_right_edge = na.zeros((num_ogrids,3), dtype='float64')
         ogrid_levels = na.zeros((num_ogrids,1), dtype='int32')
         ogrid_file_locations = na.zeros((num_ogrids,6), dtype='int64')
+        ogrid_hilbert_indices = na.zeros(num_ogrids, dtype='uint64')
         ochild_masks = na.zeros((num_ogrids, 8), dtype='int32')
         self.tree_proxy.fill_hierarchy_arrays(
             self.pf.domain_dimensions,
             ogrid_left_edge, ogrid_right_edge,
-            ogrid_levels, ogrid_file_locations, ochild_masks)
+            ogrid_levels, ogrid_file_locations, 
+            ogrid_hilbert_indices, ochild_masks,
+            self.pf.domain_left_edge, self.pf.domain_right_edge)
+        insert_ipython()
         # Now we can rescale
         mi, ma = ogrid_left_edge.min(), ogrid_right_edge.max()
         DL = self.pf.domain_left_edge
@@ -155,7 +161,6 @@
             # Now our initial protosubgrid
             #if level == 6: raise RuntimeError
             # We want grids that cover no more than MAX_EDGE cells in every direction
-            MAX_EDGE = 128
             psgs = []
             left_index = na.rint((ogrid_left_edge[ggi,:]) * nd / DW ).astype('int64')
             right_index = left_index + 2


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/frontends/ramses/ramses_headers/HilbertCurve3D.c	Thu Mar 24 12:43:51 2011 -0400
@@ -0,0 +1,122 @@
+/***********************************************************************
+/
+/  3D HILBERT CURVE
+/
+/  written by: John Wise
+/  date:       April, 2010
+/  modified1:
+/
+/  NOTES: Based on the Zoltan library
+/         http://www.cs.sandia.gov/Zoltan/
+/
+/         Given a Cartesian coordinate [0..1], this routine will return
+/         a Hilbert key [0..1] that progresses along a 3D Hilbert curve.
+/
+************************************************************************/
+
+#include "stdio.h"
+#include "math.h"
+
+unsigned long HilbertCurve3D(double *coord)
+{
+  int MaxLevel = 38;
+
+  const unsigned long IMAX = ~0;
+
+  static const char idata3d[] = {
+    0,  7,  3,  4,  1,  6,  2,  5,
+    0,  1,  3,  2,  7,  6,  4,  5,
+    0,  3,  7,  4,  1,  2,  6,  5,
+    2,  3,  5,  4,  1,  0,  6,  7,
+    4,  5,  3,  2,  7,  6,  0,  1,
+    4,  7,  3,  0,  5,  6,  2,  1,
+    6,  7,  5,  4,  1,  0,  2,  3,
+    0,  1,  7,  6,  3,  2,  4,  5,
+    2,  1,  5,  6,  3,  0,  4,  7,
+    6,  1,  5,  2,  7,  0,  4,  3,
+    0,  7,  1,  6,  3,  4,  2,  5,
+    2,  1,  3,  0,  5,  6,  4,  7,
+    4,  7,  5,  6,  3,  0,  2,  1,
+    4,  5,  7,  6,  3,  2,  0,  1,
+    6,  1,  7,  0,  5,  2,  4,  3,
+    0,  3,  1,  2,  7,  4,  6,  5,
+    2,  3,  1,  0,  5,  4,  6,  7,
+    6,  7,  1,  0,  5,  4,  2,  3,
+    2,  5,  1,  6,  3,  4,  0,  7,
+    4,  3,  7,  0,  5,  2,  6,  1,
+    4,  3,  5,  2,  7,  0,  6,  1,
+    6,  5,  1,  2,  7,  4,  0,  3,
+    2,  5,  3,  4,  1,  6,  0,  7,
+    6,  5,  7,  4,  1,  2,  0,  3};
+
+  static const char istate3d[] = {
+     1,  6,  3,  4,  2,  5,  0,  0,
+     0,  7,  8,  1,  9,  4,  5,  1,
+    15, 22, 23, 20,  0,  2, 19,  2,
+     3, 23,  3, 15,  6, 20, 16, 22,
+    11,  4, 12,  4, 20,  1, 22, 13,
+    22, 12, 20, 11,  5,  0,  5, 19,
+    17,  0,  6, 21,  3,  9,  6,  2,
+    10,  1, 14, 13, 11,  7, 12,  7,
+     8,  9,  8, 18, 14, 12, 10, 11,
+    21,  8,  9,  9,  1,  6, 17,  7,
+     7, 17, 15, 12, 16, 13, 10, 10,
+    11, 14,  9,  5, 11, 22,  0,  8,
+    18,  5, 12, 10, 19,  8, 12, 20,
+     8, 13, 19,  7,  5, 13, 18,  4,
+    23, 11,  7, 17, 14, 14,  6,  1,
+     2, 18, 10, 15, 21, 19, 20, 15,
+    16, 21, 17, 19, 16,  2,  3, 18,
+     6, 10, 16, 14, 17, 23, 17, 15,
+    18, 18, 21,  8, 17,  7, 13, 16,
+     3,  4, 13, 16, 19, 19,  2,  5,
+    16, 13, 20, 20,  4,  3, 15, 12,
+     9, 21, 18, 21, 15, 14, 23, 10,
+    22, 22,  6,  1, 23, 11,  4,  3,
+    14, 23,  2,  9, 22, 23, 21,  0};
+
+  int dim, level, idx;
+  unsigned long temp, state, c[3], key[2];
+
+  /* Error check */
+  
+  for (dim = 0; dim < 3; dim++)
+    if (coord[dim] < 0 || coord[dim] > 1) {
+      fprintf(stderr, "Coordinates must be between 0 and 1.  coord[%d] = %f",
+		 dim, coord[dim]);
+         return 0.0;
+         }
+
+  /* Convert xyz to intergers in the range [0,IMAX] */
+  
+  for (dim = 0; dim < 3; dim++)
+    c[dim] = (unsigned long) (coord[dim] * (long double) IMAX);
+
+  /* Use state tables to convert nested quadrant's coordinates level
+     by level */
+
+  state = 0;
+  key[0] = key[1] = 0;
+  for (level = 0; level < MaxLevel; level++) {
+
+    // Extract 3 bits at this level
+    temp = ((c[0] >> (61-level)) & 4)
+      | ((c[1] >> (62-level)) & 2)
+      | ((c[2] >> (63-level)) & 1);
+    idx = 8*state+temp;
+
+    // Treat key[] as a long shift register and shift in converted
+    // coordinate
+    key[0] = (key[0] << 3) | (key[1] >> 61);
+    key[1] = (key[1] << 3) | idata3d[idx];
+
+    state = istate3d[idx];
+
+  } // ENDFOR level
+
+  return key[1];
+
+  // Convert 2 part Hilbert key to double and return
+  //return ldexp((double) key[0], -25) + ldexp((double) key[1], -57);
+
+}


--- a/yt/frontends/ramses/setup.py	Wed Mar 23 19:53:53 2011 -0400
+++ b/yt/frontends/ramses/setup.py	Thu Mar 24 12:43:51 2011 -0400
@@ -6,7 +6,8 @@
     from numpy.distutils.misc_util import Configuration
     config = Configuration('ramses',parent_package,top_path)
     config.add_extension("_ramses_reader",
-        ["yt/frontends/ramses/_ramses_reader.pyx"],
+        ["yt/frontends/ramses/_ramses_reader.pyx",
+         "yt/frontends/ramses/ramses_headers/HilbertCurve3D.c"],
         language="c++",
         include_dirs=["yt/frontends/ramses/ramses_headers/"],
         depends=glob.glob("yt/frontends/ramses/ramses_headers/*.hh")


http://bitbucket.org/yt_analysis/yt/changeset/8660e8e6829f/
changeset:   r4066:8660e8e6829f
branch:      yt
user:        MatthewTurk
date:        2011-04-03 14:25:12
summary:     First pass at implementing the hilbert curve for locating grids in the RAMSES
reader.  Does not currently work.
affected #:  2 files (273 bytes)

--- a/yt/frontends/enzo/data_structures.py	Thu Mar 24 12:43:51 2011 -0400
+++ b/yt/frontends/enzo/data_structures.py	Sun Apr 03 08:25:12 2011 -0400
@@ -137,7 +137,7 @@
         self.hierarchy_filename = os.path.abspath(
             "%s.hierarchy" % (pf.parameter_filename))
         harray_fn = self.hierarchy_filename[:-9] + "harrays"
-        if os.path.exists(harray_fn):
+        if ytcfg.getboolean("yt","serialize") and os.path.exists(harray_fn):
             try:
                 harray_fp = h5py.File(harray_fn)
                 self.num_grids = harray_fp["/Level"].len()
@@ -286,6 +286,7 @@
     _bn = "%s.cpu%%04i"
     def _parse_binary_hierarchy(self):
         mylog.info("Getting the binary hierarchy")
+        if not ytcfg.getboolean("yt","serialize"): return False
         try:
             f = h5py.File(self.hierarchy_filename[:-9] + "harrays")
         except h5py.h5.H5Error:


--- a/yt/frontends/ramses/data_structures.py	Thu Mar 24 12:43:51 2011 -0400
+++ b/yt/frontends/ramses/data_structures.py	Sun Apr 03 08:25:12 2011 -0400
@@ -140,7 +140,7 @@
             ogrid_levels, ogrid_file_locations, 
             ogrid_hilbert_indices, ochild_masks,
             self.pf.domain_left_edge, self.pf.domain_right_edge)
-        insert_ipython()
+        #insert_ipython()
         # Now we can rescale
         mi, ma = ogrid_left_edge.min(), ogrid_right_edge.max()
         DL = self.pf.domain_left_edge
@@ -169,49 +169,48 @@
             pbar = get_pbar("Re-gridding ", lefts[0].size)
             min_ind = na.min(left_index, axis=0)
             max_ind = na.max(right_index, axis=0)
-            for i,dli in enumerate(lefts[0]):
-                pbar.update(i)
-                if min_ind[0] > dli + nd[0]: continue
-                if max_ind[0] < dli: continue
-                idim = min(nd[0] - dli, MAX_EDGE)
-                gdi = ((dli  <= right_index[:,0])
-                     & (dli + idim >= left_index[:,0]))
-                if not na.any(gdi): continue
-                for dlj in lefts[1]:
-                    if min_ind[1] > dlj + nd[1]: continue
-                    if max_ind[1] < dlj: continue
-                    idim = min(nd[1] - dlj, MAX_EDGE)
-                    gdj = ((dlj  <= right_index[:,1])
-                         & (dlj + idim >= left_index[:,1])
-                         & (gdi))
-                    if not na.any(gdj): continue
-                    for dlk in lefts[2]:
-                        if min_ind[2] > dlk + nd[2]: continue
-                        if max_ind[2] < dlk: continue
-                        idim = min(nd[2] - dlk, MAX_EDGE)
-                        gdk = ((dlk  <= right_index[:,2])
-                             & (dlk + idim >= left_index[:,2])
-                             & (gdj))
-                        if not na.any(gdk): continue
-                        left = na.array([dli, dlj, dlk])
-                        domain_left = left.ravel()
-                        initial_left = na.zeros(3, dtype='int64') + domain_left
-                        idims = na.ones(3, dtype='int64') * na.minimum(nd - domain_left, MAX_EDGE)
-                        # We want to find how many grids are inside.
-                        dleft_index = left_index[gdk,:]
-                        dright_index = right_index[gdk,:]
-                        ddims = dims[gdk,:]
-                        dfl = fl[gdk,:]
-                        psg = _ramses_reader.ProtoSubgrid(initial_left, idims,
-                                        dleft_index, dright_index, ddims, dfl)
-                        #print "Gridding from %s to %s + %s" % (
-                        #    initial_left, initial_left, idims)
-                        if psg.efficiency <= 0: continue
-                        self.num_deep = 0
-                        psgs.extend(self._recursive_patch_splitting(
-                            psg, idims, initial_left, 
-                            dleft_index, dright_index, ddims, dfl))
-                        #psgs.extend([psg])
+            nbits = 61 - 3*(LEVEL_OF_EDGE + level + 1)
+            hilbert_indices_level = (ogrid_hilbert_indices[ggi] >> nbits)
+            NH = 2**(3*(level - LEVEL_OF_EDGE + 1))
+            if level <= LEVEL_OF_EDGE:
+                hilbert_indices_level[:] = 0
+                NH = 1
+            dlp = [None, None, None]
+            i = 0
+            dlis, dljs, dlks = na.mgrid[0:nd[0]:MAX_EDGE,0:nd[1]:MAX_EDGE,0:nd[2]:MAX_EDGE]
+            dlis = dlis.ravel(); dljs = dljs.ravel(); dlks = dlks.ravel()
+            for i in xrange(dlis.size):
+                dli = dlis[i]; dlj = dljs[i]; dlk = dlks[i]
+                dlp[0] = dli / float(nd[0])
+                dlp[1] = dlj / float(nd[1])
+                dlp[2] = dlk / float(nd[2])
+                hilbert_index = _ramses_reader.hilbert_position(dlp)
+                hilbert_index = (hilbert_index >> nbits)
+                if level > LEVEL_OF_EDGE:
+                    gdk = (hilbert_indices_level == hilbert_index)
+                else:
+                    gdk = na.ones(left_index.shape[0], dtype='bool')
+                if not na.any(gdk): continue
+                print hilbert_index, NH, hilbert_indices_level.max()
+                left = na.array([dli, dlj, dlk])
+                domain_left = left.ravel()
+                initial_left = na.zeros(3, dtype='int64') + domain_left
+                idims = na.ones(3, dtype='int64') * na.minimum(nd - domain_left, MAX_EDGE)
+                # We want to find how many grids are inside.
+                dleft_index = left_index[gdk,:]
+                dright_index = right_index[gdk,:]
+                ddims = dims[gdk,:]
+                dfl = fl[gdk,:]
+                psg = _ramses_reader.ProtoSubgrid(initial_left, idims,
+                                dleft_index, dright_index, ddims, dfl)
+                #print "Gridding from %s to %s + %s" % (
+                #    initial_left, initial_left, idims)
+                if psg.efficiency <= 0: continue
+                self.num_deep = 0
+                psgs.extend(self._recursive_patch_splitting(
+                    psg, idims, initial_left, 
+                    dleft_index, dright_index, ddims, dfl))
+                #psgs.extend([psg])
             pbar.finish()
             self.proto_grids.append(psgs)
             sums = na.zeros(3, dtype='int64')


http://bitbucket.org/yt_analysis/yt/changeset/409e6d36caf1/
changeset:   r4067:409e6d36caf1
branch:      yt
user:        MatthewTurk
date:        2011-04-04 19:08:32
summary:     Porting a more-comprehensible hilbert indexing system from
https://github.com/cortesi/scurve .
affected #:  1 file (2.5 KB)

--- a/yt/frontends/ramses/_ramses_reader.pyx	Sun Apr 03 08:25:12 2011 -0400
+++ b/yt/frontends/ramses/_ramses_reader.pyx	Mon Apr 04 13:08:32 2011 -0400
@@ -33,6 +33,10 @@
 cimport numpy as np
 cimport cython
 
+cdef extern from "math.h":
+    double ceil(double x)
+    double log2(double x)
+
 cdef extern:
     unsigned long HilbertCurve3D(double *coord)
 
@@ -862,6 +866,94 @@
             tr[2,i] = self.dimensions[i]
         return tr
 
+ at cython.cdivision
+cdef np.int64_t graycode(np.int64_t x):
+    return x^(x>>1)
+
+ at cython.cdivision
+cdef np.int64_t igraycode(np.int64_t x):
+    cdef np.int64_t i, j
+    if x == 0:
+        return x
+    m = <np.int64_t> ceil(log2(x)) + 1
+    i, j = x, 1
+    while j < m:
+        i = i ^ (x>>j)
+        j += 1
+    return i
+
+ at cython.cdivision
+cdef np.int64_t direction(np.int64_t x, np.int64_t n):
+    #assert x < 2**n
+    if x == 0:
+        return 0
+    elif x%2 == 0:
+        return tsb(x-1, n)%n
+    else:
+        return tsb(x, n)%n
+
+ at cython.cdivision
+cdef np.int64_t tsb(np.int64_t x, np.int64_t width):
+    #assert x < 2**width
+    cdef np.int64_t i = 0
+    while x&1 and i <= width:
+        x = x >> 1
+        i += 1
+    return i
+
+ at cython.cdivision
+cdef np.int64_t bitrange(np.int64_t x, np.int64_t width,
+                         np.int64_t start, np.int64_t end):
+    return x >> (width-end) & ((2**(end-start))-1)
+
+ at cython.cdivision
+cdef np.int64_t rrot(np.int64_t x, np.int64_t i, np.int64_t width):
+    i = i%width
+    x = (x>>i) | (x<<width-i)
+    return x&(2**width-1)
+
+ at cython.cdivision
+cdef np.int64_t lrot(np.int64_t x, np.int64_t i, np.int64_t width):
+    i = i%width
+    x = (x<<i) | (x>>width-i)
+    return x&(2**width-1)
+
+ at cython.cdivision
+cdef np.int64_t transform(np.int64_t entry, np.int64_t direction,
+                          np.int64_t width, np.int64_t x):
+    return rrot((x^entry), direction + 1, width)
+
+ at cython.cdivision
+cdef np.int64_t entry(np.int64_t x):
+    if x == 0: return 0
+    return graycode(2*((x-1)/2))
+
+ at cython.cdivision
+def get_hilbert_indices(int order, np.ndarray[np.int64_t, ndim=2] left_index):
+    # This is inspired by the scurve package by user cortesi on GH.
+    cdef int o, i
+    cdef np.int64_t x, w, h, e, d, l, b
+    cdef np.int64_t p[3]
+    cdef np.ndarray[np.int64_t, ndim=1] hilbert_indices
+    hilbert_indices = np.zeros(left_index.shape[0], 'int64')
+    for o in range(left_index.shape[0]):
+        p[0] = left_index[o, 0]
+        p[1] = left_index[o, 1]
+        p[2] = left_index[o, 2]
+        h = e = d = 0
+        for i in range(order):
+            l = 0
+            for x in range(3):
+                b = bitrange(p[3-x-1], order, i, i+1)
+                l |= (b<<x)
+            l = transform(e, d, 3, l)
+            w = igraycode(l)
+            e = e ^ lrot(entry(w), d+1, 3)
+            d = (d + direction(w, 3) + 1)%3
+            h = (h<<3)|w
+        hilbert_indices[o] = h
+    return hilbert_indices
+
 def hilbert_position(pos):
     cdef double coord[3]
     cdef int i


http://bitbucket.org/yt_analysis/yt/changeset/08f216a812c1/
changeset:   r4068:08f216a812c1
branch:      yt
user:        MatthewTurk
date:        2011-04-04 19:55:06
summary:     Rewrite of the ramses protosubgrid finder to utilize the new hilbert curve
mechanism.  There may still be lingering bugs in it.
affected #:  2 files (758 bytes)

--- a/yt/frontends/ramses/_ramses_reader.pyx	Mon Apr 04 13:08:32 2011 -0400
+++ b/yt/frontends/ramses/_ramses_reader.pyx	Mon Apr 04 13:55:06 2011 -0400
@@ -954,12 +954,3 @@
         hilbert_indices[o] = h
     return hilbert_indices
 
-def hilbert_position(pos):
-    cdef double coord[3]
-    cdef int i
-    cdef unsigned long new_pos
-    coord[0] = pos[0]
-    coord[1] = pos[1]
-    coord[2] = pos[2]
-    new_pos = HilbertCurve3D(coord)
-    return new_pos


--- a/yt/frontends/ramses/data_structures.py	Mon Apr 04 13:08:32 2011 -0400
+++ b/yt/frontends/ramses/data_structures.py	Mon Apr 04 13:55:06 2011 -0400
@@ -153,64 +153,51 @@
         self.proto_grids = []
         for level in xrange(len(level_info)):
             if level_info[level] == 0: continue
+            # Get the indices of grids on this level
             ggi = (ogrid_levels == level).ravel()
+            dims = na.ones((ggi.sum(), 3), dtype='int64') * 2 
             mylog.info("Re-gridding level %s: %s octree grids", level, ggi.sum())
             nd = self.pf.domain_dimensions * 2**level
-            dims = na.ones((ggi.sum(), 3), dtype='int64') * 2
             fl = ogrid_file_locations[ggi,:]
             # Now our initial protosubgrid
             #if level == 6: raise RuntimeError
             # We want grids that cover no more than MAX_EDGE cells in every direction
             psgs = []
+            # left_index is integers of the index, with respect to this level
             left_index = na.rint((ogrid_left_edge[ggi,:]) * nd / DW ).astype('int64')
+            # we've got octs, so it's +2
             right_index = left_index + 2
-            lefts = [na.mgrid[0:nd[i]:MAX_EDGE] for i in range(3)]
-            #lefts = zip(*[l.ravel() for l in lefts])
-            pbar = get_pbar("Re-gridding ", lefts[0].size)
-            min_ind = na.min(left_index, axis=0)
-            max_ind = na.max(right_index, axis=0)
-            nbits = 61 - 3*(LEVEL_OF_EDGE + level + 1)
-            hilbert_indices_level = (ogrid_hilbert_indices[ggi] >> nbits)
-            NH = 2**(3*(level - LEVEL_OF_EDGE + 1))
-            if level <= LEVEL_OF_EDGE:
-                hilbert_indices_level[:] = 0
-                NH = 1
+            pbar = get_pbar("Re-gridding ", left_index.shape[0])
             dlp = [None, None, None]
             i = 0
-            dlis, dljs, dlks = na.mgrid[0:nd[0]:MAX_EDGE,0:nd[1]:MAX_EDGE,0:nd[2]:MAX_EDGE]
-            dlis = dlis.ravel(); dljs = dljs.ravel(); dlks = dlks.ravel()
-            for i in xrange(dlis.size):
-                dli = dlis[i]; dlj = dljs[i]; dlk = dlks[i]
-                dlp[0] = dli / float(nd[0])
-                dlp[1] = dlj / float(nd[1])
-                dlp[2] = dlk / float(nd[2])
-                hilbert_index = _ramses_reader.hilbert_position(dlp)
-                hilbert_index = (hilbert_index >> nbits)
-                if level > LEVEL_OF_EDGE:
-                    gdk = (hilbert_indices_level == hilbert_index)
-                else:
-                    gdk = na.ones(left_index.shape[0], dtype='bool')
-                if not na.any(gdk): continue
-                print hilbert_index, NH, hilbert_indices_level.max()
-                left = na.array([dli, dlj, dlk])
-                domain_left = left.ravel()
-                initial_left = na.zeros(3, dtype='int64') + domain_left
-                idims = na.ones(3, dtype='int64') * na.minimum(nd - domain_left, MAX_EDGE)
-                # We want to find how many grids are inside.
-                dleft_index = left_index[gdk,:]
-                dright_index = right_index[gdk,:]
-                ddims = dims[gdk,:]
-                dfl = fl[gdk,:]
+            # We now calculate the hilbert curve position of every left_index,
+            # of the octs, with respect to a lower order hilbert curve.
+            left_index_gridpatch = left_index >> LEVEL_OF_EDGE
+            order = max(level + 1 - LEVEL_OF_EDGE, 0)
+            # I'm not sure the best way to do this.
+            hilbert_indices = _ramses_reader.get_hilbert_indices(order, left_index_gridpatch)
+            #print level, hilbert_indices.min(), hilbert_indices.max()
+            # Strictly speaking, we don't care about the index of any
+            # individual oct at this point.  So we can then split them up.
+            unique_indices = na.unique(hilbert_indices)
+            for curve_index in unique_indices:
+                #print "Handling", curve_index
+                my_octs = (hilbert_indices == curve_index)
+                dleft_index = left_index[my_octs,:]
+                dright_index = left_index[my_octs,:] + 2
+                ddims = (dright_index * 0) + 2
+                dfl = fl[my_octs,:]
+                initial_left = na.min(dleft_index, axis=0)
+                idims = (na.max(dright_index, axis=0) - initial_left).ravel()
+                #if level > 6: insert_ipython()
+                #print initial_left, idims
                 psg = _ramses_reader.ProtoSubgrid(initial_left, idims,
                                 dleft_index, dright_index, ddims, dfl)
-                #print "Gridding from %s to %s + %s" % (
-                #    initial_left, initial_left, idims)
                 if psg.efficiency <= 0: continue
                 self.num_deep = 0
                 psgs.extend(self._recursive_patch_splitting(
                     psg, idims, initial_left, 
                     dleft_index, dright_index, ddims, dfl))
-                #psgs.extend([psg])
             pbar.finish()
             self.proto_grids.append(psgs)
             sums = na.zeros(3, dtype='int64')


http://bitbucket.org/yt_analysis/yt/changeset/241457328bc6/
changeset:   r4069:241457328bc6
branch:      yt
user:        MatthewTurk
date:        2011-04-04 20:09:15
summary:     Removing HilbertCurve3D.c file.
affected #:  3 files (200 bytes)

--- a/yt/frontends/ramses/_ramses_reader.pyx	Mon Apr 04 13:55:06 2011 -0400
+++ b/yt/frontends/ramses/_ramses_reader.pyx	Mon Apr 04 14:09:15 2011 -0400
@@ -37,9 +37,6 @@
     double ceil(double x)
     double log2(double x)
 
-cdef extern:
-    unsigned long HilbertCurve3D(double *coord)
-
 cdef inline np.int64_t i64max(np.int64_t i0, np.int64_t i1):
     if i0 > i1: return i0
     return i1
@@ -612,7 +609,6 @@
                     father_it = grid_it.get_parent()
                     grid_file_locations[grid_aind, 0] = <np.int64_t> idomain
                     grid_file_locations[grid_aind, 1] = grid_ind - level_cell_counts[ilevel]
-                    grid_hilbert_indices[grid_aind] = HilbertCurve3D(pos)
                     parent_ind = father_it.get_absolute_position()
                     if ilevel > 0:
                         # We calculate the REAL parent index


--- a/yt/frontends/ramses/ramses_headers/HilbertCurve3D.c	Mon Apr 04 13:55:06 2011 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,122 +0,0 @@
-/***********************************************************************
-/
-/  3D HILBERT CURVE
-/
-/  written by: John Wise
-/  date:       April, 2010
-/  modified1:
-/
-/  NOTES: Based on the Zoltan library
-/         http://www.cs.sandia.gov/Zoltan/
-/
-/         Given a Cartesian coordinate [0..1], this routine will return
-/         a Hilbert key [0..1] that progresses along a 3D Hilbert curve.
-/
-************************************************************************/
-
-#include "stdio.h"
-#include "math.h"
-
-unsigned long HilbertCurve3D(double *coord)
-{
-  int MaxLevel = 38;
-
-  const unsigned long IMAX = ~0;
-
-  static const char idata3d[] = {
-    0,  7,  3,  4,  1,  6,  2,  5,
-    0,  1,  3,  2,  7,  6,  4,  5,
-    0,  3,  7,  4,  1,  2,  6,  5,
-    2,  3,  5,  4,  1,  0,  6,  7,
-    4,  5,  3,  2,  7,  6,  0,  1,
-    4,  7,  3,  0,  5,  6,  2,  1,
-    6,  7,  5,  4,  1,  0,  2,  3,
-    0,  1,  7,  6,  3,  2,  4,  5,
-    2,  1,  5,  6,  3,  0,  4,  7,
-    6,  1,  5,  2,  7,  0,  4,  3,
-    0,  7,  1,  6,  3,  4,  2,  5,
-    2,  1,  3,  0,  5,  6,  4,  7,
-    4,  7,  5,  6,  3,  0,  2,  1,
-    4,  5,  7,  6,  3,  2,  0,  1,
-    6,  1,  7,  0,  5,  2,  4,  3,
-    0,  3,  1,  2,  7,  4,  6,  5,
-    2,  3,  1,  0,  5,  4,  6,  7,
-    6,  7,  1,  0,  5,  4,  2,  3,
-    2,  5,  1,  6,  3,  4,  0,  7,
-    4,  3,  7,  0,  5,  2,  6,  1,
-    4,  3,  5,  2,  7,  0,  6,  1,
-    6,  5,  1,  2,  7,  4,  0,  3,
-    2,  5,  3,  4,  1,  6,  0,  7,
-    6,  5,  7,  4,  1,  2,  0,  3};
-
-  static const char istate3d[] = {
-     1,  6,  3,  4,  2,  5,  0,  0,
-     0,  7,  8,  1,  9,  4,  5,  1,
-    15, 22, 23, 20,  0,  2, 19,  2,
-     3, 23,  3, 15,  6, 20, 16, 22,
-    11,  4, 12,  4, 20,  1, 22, 13,
-    22, 12, 20, 11,  5,  0,  5, 19,
-    17,  0,  6, 21,  3,  9,  6,  2,
-    10,  1, 14, 13, 11,  7, 12,  7,
-     8,  9,  8, 18, 14, 12, 10, 11,
-    21,  8,  9,  9,  1,  6, 17,  7,
-     7, 17, 15, 12, 16, 13, 10, 10,
-    11, 14,  9,  5, 11, 22,  0,  8,
-    18,  5, 12, 10, 19,  8, 12, 20,
-     8, 13, 19,  7,  5, 13, 18,  4,
-    23, 11,  7, 17, 14, 14,  6,  1,
-     2, 18, 10, 15, 21, 19, 20, 15,
-    16, 21, 17, 19, 16,  2,  3, 18,
-     6, 10, 16, 14, 17, 23, 17, 15,
-    18, 18, 21,  8, 17,  7, 13, 16,
-     3,  4, 13, 16, 19, 19,  2,  5,
-    16, 13, 20, 20,  4,  3, 15, 12,
-     9, 21, 18, 21, 15, 14, 23, 10,
-    22, 22,  6,  1, 23, 11,  4,  3,
-    14, 23,  2,  9, 22, 23, 21,  0};
-
-  int dim, level, idx;
-  unsigned long temp, state, c[3], key[2];
-
-  /* Error check */
-  
-  for (dim = 0; dim < 3; dim++)
-    if (coord[dim] < 0 || coord[dim] > 1) {
-      fprintf(stderr, "Coordinates must be between 0 and 1.  coord[%d] = %f",
-		 dim, coord[dim]);
-         return 0.0;
-         }
-
-  /* Convert xyz to intergers in the range [0,IMAX] */
-  
-  for (dim = 0; dim < 3; dim++)
-    c[dim] = (unsigned long) (coord[dim] * (long double) IMAX);
-
-  /* Use state tables to convert nested quadrant's coordinates level
-     by level */
-
-  state = 0;
-  key[0] = key[1] = 0;
-  for (level = 0; level < MaxLevel; level++) {
-
-    // Extract 3 bits at this level
-    temp = ((c[0] >> (61-level)) & 4)
-      | ((c[1] >> (62-level)) & 2)
-      | ((c[2] >> (63-level)) & 1);
-    idx = 8*state+temp;
-
-    // Treat key[] as a long shift register and shift in converted
-    // coordinate
-    key[0] = (key[0] << 3) | (key[1] >> 61);
-    key[1] = (key[1] << 3) | idata3d[idx];
-
-    state = istate3d[idx];
-
-  } // ENDFOR level
-
-  return key[1];
-
-  // Convert 2 part Hilbert key to double and return
-  //return ldexp((double) key[0], -25) + ldexp((double) key[1], -57);
-
-}


--- a/yt/frontends/ramses/setup.py	Mon Apr 04 13:55:06 2011 -0400
+++ b/yt/frontends/ramses/setup.py	Mon Apr 04 14:09:15 2011 -0400
@@ -6,8 +6,7 @@
     from numpy.distutils.misc_util import Configuration
     config = Configuration('ramses',parent_package,top_path)
     config.add_extension("_ramses_reader",
-        ["yt/frontends/ramses/_ramses_reader.pyx",
-         "yt/frontends/ramses/ramses_headers/HilbertCurve3D.c"],
+        ["yt/frontends/ramses/_ramses_reader.pyx"],
         language="c++",
         include_dirs=["yt/frontends/ramses/ramses_headers/"],
         depends=glob.glob("yt/frontends/ramses/ramses_headers/*.hh")


http://bitbucket.org/yt_analysis/yt/changeset/7247ddbbd2df/
changeset:   r4070:7247ddbbd2df
branch:      yt
user:        MatthewTurk
date:        2011-04-04 20:09:36
summary:     Merging
affected #:  52 files (165.4 KB)

--- a/.hgignore	Mon Apr 04 14:09:15 2011 -0400
+++ b/.hgignore	Mon Apr 04 14:09:36 2011 -0400
@@ -1,6 +1,13 @@
 build
 yt.egg-info
 __config__.py
+freetype.cfg
+hdf5.cfg
+png.cfg
+yt/frontends/ramses/_ramses_reader.cpp
+yt/utilities/amr_utils.c
+yt/utilities/kdtree/forthonf2c.h
+yt/utilities/libconfig_wrapper.c
 syntax: glob
 *.pyc
 .*.swp


--- a/doc/install_script.sh	Mon Apr 04 14:09:15 2011 -0400
+++ b/doc/install_script.sh	Mon Apr 04 14:09:36 2011 -0400
@@ -294,6 +294,8 @@
 get_enzotools h5py-1.2.0.tar.gz
 get_enzotools Cython-0.14.tar.gz
 get_enzotools Forthon-0.8.4.tar.gz
+get_enzotools ext-3.3.2.zip
+get_enzotools ext-slate-110328.zip
 
 if [ $INST_BZLIB -eq 1 ]
 then
@@ -522,6 +524,38 @@
     cd $MY_PWD
 fi
 
+# Now we open up the ext repository
+if [ ! -e ext-3.3.2/done ]
+then
+    ( unzip -o ext-3.3.2.zip 2>&1 ) 1>> ${LOG_FILE} || do_exit
+    ( echo "Symlinking ext-3.3.2 as ext-resources" 2>&1 ) 1>> ${LOG_FILE}
+    ln -sf ext-3.3.2 ext-resources
+    touch ext-3.3.2/done
+fi
+
+# Now we open up the ext theme
+if [ ! -e ext-slate-110328/done ]
+then
+    ( unzip -o ext-slate-110328.zip 2>&1 ) 1>> ${LOG_FILE} || do_exit
+    ( echo "Symlinking ext-slate-110328 as ext-theme" 2>&1 ) 1>> ${LOG_FILE}
+    ln -sf ext-slate-110328 ext-theme
+    touch ext-slate-110328/done
+fi
+
+if [ -e $HOME/.matplotlib/fontList.cache ] && \
+   ( grep -q python2.6 $HOME/.matplotlib/fontList.cache )
+then
+    echo "WARNING WARNING WARNING WARNING WARNING WARNING WARNING"
+    echo "*******************************************************"
+    echo
+    echo "  You likely need to remove your old fontList.cache!"
+    echo "  You can do this with this command:"
+    echo ""
+    echo "  rm $HOME/.matplotlib/fontList.cache"
+    echo
+    echo "*******************************************************"
+fi
+
 function print_afterword
 {
     echo


--- a/setup.py	Mon Apr 04 14:09:15 2011 -0400
+++ b/setup.py	Mon Apr 04 14:09:36 2011 -0400
@@ -1,4 +1,4 @@
-import os, os.path
+import os, os.path, glob
 import sys
 import time
 import subprocess
@@ -8,6 +8,11 @@
 from numpy.distutils.misc_util import appendpath
 from numpy.distutils import log
 
+DATA_FILES_HTML = glob.glob('yt/gui/reason/html/*.html')
+DATA_FILES_JS   = glob.glob('yt/gui/reason/html/js/*.js')
+DATA_FILES_PNG  = glob.glob('yt/gui/reason/html/images/*.png') \
+                + glob.glob('yt/gui/reason/html/images/*.ico')
+
 # Verify that we have Cython installed
 try:
     import Cython
@@ -73,7 +78,6 @@
 
 import setuptools
 
-DATA_FILES = []
 VERSION = "2.1dev"
 
 if os.path.exists('MANIFEST'): os.remove('MANIFEST')
@@ -126,9 +130,10 @@
         url = "http://yt.enzotools.org/",
         license="GPL-3",
         configuration=configuration,
-        data_files=DATA_FILES,
         zip_safe=False,
-        package_data = {'': ['*.so'], }
+        data_files = [('yt/gui/reason/html/', DATA_FILES_HTML),
+                      ('yt/gui/reason/html/js/', DATA_FILES_JS),
+                      ('yt/gui/reason/html/images/', DATA_FILES_PNG)],
         )
     return
 


--- a/yt/config.py	Mon Apr 04 14:09:15 2011 -0400
+++ b/yt/config.py	Mon Apr 04 14:09:36 2011 -0400
@@ -36,6 +36,7 @@
     suppressstreamlogging = 'False',
     loglevel = '20',
     inline = 'False',
+    __withinreason = 'False',
     __parallel = 'False',
     __parallel_rank = '0',
     __parallel_size = '1',


--- a/yt/data_objects/derived_quantities.py	Mon Apr 04 14:09:15 2011 -0400
+++ b/yt/data_objects/derived_quantities.py	Mon Apr 04 14:09:36 2011 -0400
@@ -35,6 +35,7 @@
 from yt.utilities.data_point_utilities import FindBindingEnergy
 from yt.utilities.parallel_tools.parallel_analysis_interface import \
     ParallelAnalysisInterface
+from yt.utilities.amr_utils import Octree
 
 __CUDA_BLOCK_SIZE = 256
 
@@ -272,15 +273,36 @@
 add_quantity("ParticleSpinParameter", function=_ParticleSpinParameter,
              combine_function=_combBaryonSpinParameter, n_ret=4)
     
-def _IsBound(data, truncate = True, include_thermal_energy = False):
-    """
-    This returns whether or not the object is gravitationally bound
+def _IsBound(data, truncate = True, include_thermal_energy = False,
+    treecode = True, opening_angle = 1.0, periodic_test = False):
+    r"""
+    This returns whether or not the object is gravitationally bound. If this
+    returns a value greater than one, it is bound, and otherwise not.
     
-    :param truncate: Should the calculation stop once the ratio of
-                     gravitational:kinetic is 1.0?
-    :param include_thermal_energy: Should we add the energy from ThermalEnergy
-                                   on to the kinetic energy to calculate 
-                                   binding energy?
+    Parameters
+    ----------
+    truncate : Bool
+        Should the calculation stop once the ratio of
+        gravitational:kinetic is 1.0?
+    include_thermal_energy : Bool
+        Should we add the energy from ThermalEnergy
+        on to the kinetic energy to calculate 
+        binding energy?
+    treecode : Bool
+        Whether or not to use the treecode.
+    opening_angle : Float 
+        The maximal angle a remote node may subtend in order
+        for the treecode method of mass conglomeration may be
+        used to calculate the potential between masses.
+    periodic_test : Bool 
+        Used for testing the periodic adjustment machinery
+        of this derived quantity. 
+
+    Examples
+    --------
+    >>> sp.quantities["IsBound"](truncate=False,
+    ... include_thermal_energy=True, treecode=False, opening_angle=2.0)
+    0.32493
     """
     # Kinetic energy
     bv_x,bv_y,bv_z = data.quantities["BulkVelocity"]()
@@ -294,19 +316,120 @@
     if (include_thermal_energy):
         thermal = (data["ThermalEnergy"] * data["CellMass"]).sum()
         kinetic += thermal
+    if periodic_test:
+        kinetic = na.ones_like(kinetic)
     # Gravitational potential energy
     # We only divide once here because we have velocity in cgs, but radius is
     # in code.
     G = 6.67e-8 / data.convert("cm") # cm^3 g^-1 s^-2
+    # Check for periodicity of the clump.
+    two_root = 2. / na.array(data.pf.domain_dimensions)
+    domain_period = data.pf.domain_right_edge - data.pf.domain_left_edge
+    periodic = na.array([0., 0., 0.])
+    for i,dim in enumerate(["x", "y", "z"]):
+        sorted = data[dim][data[dim].argsort()]
+        # If two adjacent values are different by (more than) two root grid
+        # cells, I think it's reasonable to assume that the clump wraps around.
+        diff = sorted[1:] - sorted[0:-1]
+        if (diff >= two_root[i]).any():
+            mylog.info("Adjusting clump for periodic boundaries in dim %s" % dim)
+            # We will record the distance of the larger of the two values that
+            # define the gap from the right boundary, which we'll use for the
+            # periodic adjustment later.
+            sel = (diff >= two_root[i])
+            index = na.min(na.nonzero(sel))
+            # The last addition term below ensures that the data makes a full
+            # wrap-around.
+            periodic[i] = data.pf.domain_right_edge[i] - sorted[index + 1] + \
+                two_root[i] / 2.
+    # This dict won't make a copy of the data, but it will make a copy to 
+    # change if needed in the periodic section immediately below.
+    local_data = {}
+    for label in ["x", "y", "z", "CellMass"]:
+        local_data[label] = data[label]
+    if periodic.any():
+        # Adjust local_data to re-center the clump to remove the periodicity
+        # by the gap calculated above.
+        for i,dim in enumerate(["x", "y", "z"]):
+            if not periodic[i]: continue
+            local_data[dim] = data[dim].copy()
+            local_data[dim] += periodic[i]
+            local_data[dim] %= domain_period[i]
+    if periodic_test:
+        local_data["CellMass"] = na.ones_like(local_data["CellMass"])
     import time
     t1 = time.time()
-    try:
-        pot = G*_cudaIsBound(data, truncate, kinetic/G)
-    except (ImportError, AssertionError):
-        pot = G*FindBindingEnergy(data["CellMass"],
-                                  data['x'],data['y'],data['z'],
-                                  truncate, kinetic/G)
-        mylog.info("Boundedness check took %0.3e seconds", time.time()-t1)
+    if treecode:
+        # Calculate the binding energy using the treecode method.
+        # Faster but less accurate.
+        # The octree doesn't like uneven root grids, so we will make it cubical.
+        root_dx = 1./na.array(data.pf.domain_dimensions).astype('float64')
+        left = min([na.amin(local_data['x']), na.amin(local_data['y']),
+            na.amin(local_data['z'])])
+        right = max([na.amax(local_data['x']), na.amax(local_data['y']),
+            na.amax(local_data['z'])])
+        cover_min = na.array([left, left, left])
+        cover_max = na.array([right, right, right])
+        # Fix the coverage to match to root grid cell left 
+        # edges for making indexes.
+        cover_min = cover_min - cover_min % root_dx
+        cover_max = cover_max - cover_max % root_dx
+        cover_imin = (cover_min * na.array(data.pf.domain_dimensions)).astype('int64')
+        cover_imax = (cover_max * na.array(data.pf.domain_dimensions) + 1).astype('int64')
+        cover_ActiveDimensions = cover_imax - cover_imin
+        # Create the octree with these dimensions.
+        # One value (mass) with incremental=True.
+        octree = Octree(cover_ActiveDimensions, 1, True)
+        #print 'here', cover_ActiveDimensions
+        # Now discover what levels this data comes from, not assuming
+        # symmetry.
+        dxes = na.unique(data['dx']) # unique returns a sorted array,
+        dyes = na.unique(data['dy']) # so these will all have the same
+        dzes = na.unique(data['dx']) # order.
+        # We only need one dim to figure out levels, we'll use x.
+        dx = 1./data.pf.domain_dimensions[0]
+        levels = na.floor(dx / dxes / data.pf.refine_by).astype('int')
+        lsort = levels.argsort()
+        levels = levels[lsort]
+        dxes = dxes[lsort]
+        dyes = dyes[lsort]
+        dzes = dzes[lsort]
+        # This step adds massless cells for all the levels we need in order
+        # to fully populate all the parent-child cells needed.
+        for L in range(min(data.pf.h.max_level+1, na.amax(levels)+1)):
+            ActiveDimensions = cover_ActiveDimensions * 2**L
+            i, j, k = na.indices(ActiveDimensions)
+            i = i.flatten()
+            j = j.flatten()
+            k = k.flatten()
+            octree.add_array_to_tree(L, i, j, k,
+                na.array([na.zeros_like(i)], order='F', dtype='float64'),
+                na.zeros_like(i).astype('float64'))
+        # Now we add actual data to the octree.
+        for L, dx, dy, dz in zip(levels, dxes, dyes, dzes):
+            mylog.info("Adding data to Octree for level %d" % L)
+            sel = (data["dx"] == dx)
+            thisx = (local_data["x"][sel] / dx).astype('int64') - cover_imin[0] * 2**L
+            thisy = (local_data["y"][sel] / dy).astype('int64') - cover_imin[1] * 2**L
+            thisz = (local_data["z"][sel] / dz).astype('int64') - cover_imin[2] * 2**L
+            vals = na.array([local_data["CellMass"][sel]], order='F')
+            octree.add_array_to_tree(L, thisx, thisy, thisz, vals,
+               na.ones_like(thisx).astype('float64'), treecode = 1)
+        # Now we calculate the binding energy using a treecode.
+        octree.finalize(treecode = 1)
+        mylog.info("Using a treecode to find gravitational energy for %d cells." % local_data['x'].size)
+        pot = G*octree.find_binding_energy(truncate, kinetic/G, root_dx,
+            opening_angle)
+        #octree.print_all_nodes()
+    else:
+        try:
+            pot = G*_cudaIsBound(local_data, truncate, kinetic/G)
+        except (ImportError, AssertionError):
+            pot = G*FindBindingEnergy(local_data["CellMass"],
+                                local_data['x'],local_data['y'],local_data['z'],
+                                truncate, kinetic/G)
+    mylog.info("Boundedness check took %0.3e seconds", time.time()-t1)
+    del local_data
     return [(pot / kinetic)]
 def _combIsBound(data, bound):
     return bound


--- a/yt/frontends/enzo/data_structures.py	Mon Apr 04 14:09:15 2011 -0400
+++ b/yt/frontends/enzo/data_structures.py	Mon Apr 04 14:09:36 2011 -0400
@@ -38,6 +38,7 @@
 from itertools import izip
 
 from yt.funcs import *
+from yt.config import ytcfg
 from yt.data_objects.grid_patch import \
     AMRGridPatch
 from yt.data_objects.hierarchy import \


--- a/yt/gui/reason/basic_repl.py	Mon Apr 04 14:09:15 2011 -0400
+++ b/yt/gui/reason/basic_repl.py	Mon Apr 04 14:09:36 2011 -0400
@@ -42,6 +42,18 @@
         self.buffer = []
         # Nominally at this point we could populate our namespace with widgets
         # or other useful functions.  We aren't really ready for that just yet.
+
+    def evaluate_cell(self, input):
+        result = []
+        for line in input.split("\n"):
+            r = self.push(line)
+            if r is not None: result.append(r)
+        # To catch if people leave a little bit at the end for us
+        if r is None:
+            r = self.push("\n")
+            if r is not None: result.append(r)
+        # Usually they have \n already
+        return "".join(result)
     
     def push(self, line):
         """Push 'line' and return exec results (None if more input needed)."""


--- a/yt/gui/reason/bottle_mods.py	Mon Apr 04 14:09:15 2011 -0400
+++ b/yt/gui/reason/bottle_mods.py	Mon Apr 04 14:09:36 2011 -0400
@@ -24,10 +24,17 @@
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 """
 
-from .bottle import server_names, debug, route
+from .bottle import server_names, debug, route, run, request
 import uuid
+from extdirect_router import DirectRouter, DirectProviderDefinition
+import json
+import logging
+from yt.utilities.logger import ytLogger as mylog
+from yt.funcs import *
 
 route_functions = {}
+route_watchers = []
+payloads = []
 
 def preroute(future_route, *args, **kwargs):
     def router(func):
@@ -35,20 +42,115 @@
         return func
     return router
 
-def uuid_serve_functions(pre_routed, open_browser=False):
+def notify_route(watcher):
+    route_watchers.append(watcher)
+
+class PayloadHandler(object):
+    _shared_state = {}
+    _hold = False
+
+    def __new__(cls, *p, **k):
+        self = object.__new__(cls, *p, **k)
+        self.__dict__ = cls._shared_state
+        return self
+
+    def __init__(self):
+        self.payloads = []
+
+    def deliver_payloads(self):
+        if self._hold: return []
+        payloads = self.payloads
+        self.payloads = []
+        return payloads
+
+    def add_payload(self, to_add):
+        self.payloads.append(to_add)
+
+_ph = PayloadHandler()
+
+def append_payloads(func):
+    @wraps(func)
+    def wrapper(self, *args, **kwargs):
+        reset = not _ph._hold
+        _ph._hold = True
+        func(self, *args, **kwargs)
+        # Assume it returns a dict
+        if not reset: return
+        # In case it sets it manually
+        _ph._hold = False
+        payloads = _ph.deliver_payloads()
+        return payloads
+    return wrapper
+
+class BottleDirectRouter(DirectRouter):
+    # This class implements a mechanism for auto-routing an ExtDirect-callable
+    # object through Bottle.  It should be used as a base class of an object,
+    # and the __init__ function will need to include the keyword argument
+    # 'route' for it to work.
+    _route_prefix = None
+    def __init__(self, *args, **kwargs):
+        future_route = self.api_url
+        super(BottleDirectRouter, self).__init__(*args, **kwargs)
+        self.__name__ = str(self.my_name)
+        route_functions[future_route] = ((), {'method':"POST"}, self)
+        preroute("/resources/ext-%s-api.js" % self.api_url, method="GET")(self._myapi)
+        notify_route(self)
+
+    def _myapi(self):
+        dpd = DirectProviderDefinition(self, self.api_url, ns="yt_rpc")
+        source = "Ext.Direct.addProvider(%s);" % json.dumps(dpd._config())
+        return source
+
+    def __call__(self):
+        #print "Hi there, I just got this request:",
+        val = request.body.read()
+        print val
+        #import pdb;pdb.set_trace()
+        rv = super(BottleDirectRouter, self).__call__(val)
+        #print "With this response:", rv
+        return rv
+
+def uuid_serve_functions(pre_routed = None, open_browser=False, port=9099):
+    if pre_routed == None: pre_routed = route_functions
     debug(mode=True)
     token = uuid.uuid1()
     for r in pre_routed:
         args, kwargs, f = pre_routed[r]
         if r[0] == "/": r = r[1:]
         rp = "/%s/%s" % (token, r)
-        print "Routing from %s => %s" % (rp, f.func_name)
+        func_name = getattr(f, 'func_name', str(f))
+        print "Routing from %s => %s" % (rp, func_name)
         route(rp, *args, **kwargs)(f)
-    print "Greetings! Your private token is %s ." % token
+    for w in route_watchers:
+        if not hasattr(w, "_route_prefix"):
+            print "WARNING: %s has no _route_prefix attribute.  Not notifying."
+            continue
+            w._route_prefix = token
+    print
+    print
+    print "============================================================================="
+    print "============================================================================="
+    print "Greetings, and welcome to Reason!"
+    print "Your private token is %s ." % token
+    print "DO NOT SHARE THIS TOKEN."
     print
     print "Please direct your browser to:"
     print
-    print "     http://localhost:8080/%s/" % token
+    print "     http://localhost:%s/%s/" % (port, token)
+    print
+    print "============================================================================="
+    print
+    print "If you are currently ssh'd into a remote machine, you should be able"
+    print "to create a new SSH tunnel by typing or copy/pasting this text"
+    print "verbatim, while waiting to see the 'ssh>' prompt after the first line."
+    print
+    print "~C"
+    print "-L%s:localhost:%s" % (port, port)
+    print
+    print "and then pointing a web browser on your local machine to the above URL."
+    print
+    print "============================================================================="
+    print "============================================================================="
     print
     print
     if open_browser:
@@ -59,13 +161,21 @@
             """Start a browser after waiting for half a second."""
             import webbrowser, threading
             def _local_browse():
-                webbrowser.open('http://localhost:%s/%s/' % (8080, token))
-            thread = threading.Timer(0.5, _open_browser)
+                webbrowser.open('http://localhost:%s/%s/' % (port, token))
+            thread = threading.Timer(0.5, _local_browse)
             thread.start()
         local_browse()
-    # Right now we only really support the built-in wsgiref, but this may
-    # change if we start using Rocket.
-    server_type = server_names.get("wsgiref")
-    server = server_type(host='localhost', port=8080)
+    try:
+        import rocket
+        server_name = "rocket"
+        log = logging.getLogger('Rocket')
+        log.setLevel(logging.INFO)
+        kwargs = {'timeout': 600, 'max_threads': 1}
+    except ImportError:
+        server_name = "wsgiref"
+        kwargs = {}
+    server_type = server_names.get(server_name)
+    server = server_type(host='localhost', port=port, **kwargs)
     #repl.locals['server'] = server
+    mylog.info("Starting up the server.")
     run(server=server)


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/gui/reason/extdirect_repl.py	Mon Apr 04 14:09:36 2011 -0400
@@ -0,0 +1,332 @@
+"""
+A read-eval-print-loop that is served up through Bottle and accepts its
+commands through ExtDirect calls
+
+Author: Matthew Turk <matthewturk at gmail.com>
+Affiliation: NSF / Columbia
+Homepage: http://yt.enzotools.org/
+License:
+  Copyright (C) 2011 Matthew Turk.  All Rights Reserved.
+
+  This file is part of yt.
+
+  yt is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+import json
+import os
+import cStringIO
+import logging
+import uuid
+import numpy as na
+
+from yt.funcs import *
+from yt.utilities.logger import ytLogger, ufstring
+from yt.utilities.definitions import inv_axis_names
+
+from .bottle_mods import preroute, BottleDirectRouter, notify_route, \
+                         PayloadHandler, append_payloads
+from .bottle import response, request, route
+from .basic_repl import ProgrammaticREPL
+
+try:
+    import pygments
+    import pygments.lexers
+    import pygments.formatters
+    def _highlighter():
+        pl = pygments.lexers.PythonLexer()
+        hf = pygments.formatters.HtmlFormatter(
+                linenos='table', linenospecial=2)
+        def __highlighter(a):
+            return pygments.highlight(a, pl, hf)
+        return __highlighter, hf.get_style_defs()
+    # We could add an additional '.highlight_pyg' in the call
+    highlighter, highlighter_css = _highlighter()
+except ImportError:
+    highlighter = lambda a: a
+    highlight_css = ''
+
+local_dir = os.path.dirname(__file__)
+
+class ExtDirectREPL(ProgrammaticREPL, BottleDirectRouter):
+    _skip_expose = ('index')
+    my_name = "ExtDirectREPL"
+
+    def __init__(self, base_extjs_path, locals=None):
+        # First we do the standard initialization
+        self.extjs_path = os.path.join(base_extjs_path, "ext-resources")
+        self.extjs_theme_path = os.path.join(base_extjs_path, "ext-theme")
+        ProgrammaticREPL.__init__(self, locals)
+        # Now, since we want to only preroute functions we know about, and
+        # since they have different arguments, and most of all because we only
+        # want to add them to the routing tables (which are a singleton for the
+        # entire interpreter state) we apply all the pre-routing now, rather
+        # than through metaclasses or other fancy decorating.
+        preroute_table = dict(index = ("/", "GET"),
+                              _help_html = ("/help.html", "GET"),
+                              _myapi = ("/resources/ext-repl-api.js", "GET"),
+                              _resources = ("/resources/:path#.+#", "GET"),
+                              _js = ("/js/:path#.+#", "GET"),
+                              _images = ("/images/:path#.+#", "GET"),
+                              _theme = ("/theme/:path#.+#", "GET"),
+                              _session_py = ("/session.py", "GET"),
+                              _highlighter_css = ("/highlighter.css", "GET"),
+                              )
+        for v, args in preroute_table.items():
+            preroute(args[0], method=args[1])(getattr(self, v))
+        # This has to be routed to the root directory
+        self.api_url = "repl"
+        BottleDirectRouter.__init__(self)
+        self.pflist = ExtDirectParameterFileList()
+        self.executed_cell_texts = []
+        self.payload_handler = PayloadHandler()
+        # Now we load up all the yt.mods stuff, but only after we've finished
+        # setting up.
+        self.execute("from yt.mods import *")
+        self.execute("from yt.data_objects.static_output import _cached_pfs")
+        self.locals['load_script'] = ext_load_script
+        self.locals['_widgets'] = {}
+        self.locals['add_widget'] = self._add_widget
+        self.locals['test_widget'] = self._test_widget
+        self._setup_logging_handlers()
+
+    def _setup_logging_handlers(self):
+        handler = PayloadLoggingHandler()
+        formatter = logging.Formatter(ufstring)
+        handler.setFormatter(formatter)
+        ytLogger.addHandler(handler)
+
+    def index(self):
+        """Return an HTTP-based Read-Eval-Print-Loop terminal."""
+        # For now this doesn't work!  We will need to move to a better method
+        # for this.  It should use the package data command.
+        vals = open(os.path.join(local_dir, "html/index.html")).read()
+        return vals
+
+    def heartbeep(self):
+        return {'alive': True}
+
+    def _help_html(self):
+        vals = open(os.path.join(local_dir, "html/help.html")).read()
+        return vals
+
+    def _resources(self, path):
+        pp = os.path.join(self.extjs_path, path)
+        if not os.path.exists(pp):
+            response.status = 404
+            return
+        return open(pp).read()
+
+    def _theme(self, path):
+        pp = os.path.join(self.extjs_theme_path, path)
+        if not os.path.exists(pp):
+            response.status = 404
+            return
+        return open(pp).read()
+
+    def _js(self, path):
+        pp = os.path.join(local_dir, "html", "js", path)
+        if not os.path.exists(pp):
+            response.status = 404
+            return
+        return open(pp).read()
+
+    def _images(self, path):
+        pp = os.path.join(local_dir, "html", "images", path)
+        if not os.path.exists(pp):
+            response.status = 404
+            return
+        return open(pp).read()
+
+    def _highlighter_css(self):
+        return highlighter_css
+
+    @append_payloads
+    def execute(self, code):
+        self.executed_cell_texts.append(code)
+        result = ProgrammaticREPL.execute(self, code)
+        self.payload_handler.add_payload(
+            {'type': 'cell_results',
+             'output': result,
+             'input': highlighter(code)})
+
+    def get_history(self):
+        return self.executed_cell_texts[:]
+
+    def save_session(self, filename):
+        if filename.startswith('~'):
+            filename = os.path.expanduser(filename)
+        elif not filename.startswith('/'):
+            filename = os.path.join(os.getcwd(), filename)
+        if os.path.exists(filename):
+            return {'status': 'FAIL', 'filename': filename,
+                    'error': 'File exists!'}
+        try:
+            f = open(filename, 'w')
+            f.write("\n######\n".join(self.executed_cell_texts))
+            f.close()
+        except IOError as (errno, strerror):
+            return {'status': 'FAIL', 'filename': filename,
+                    'error': strerror}
+        except:
+            return {'status': 'FAIL', 'filename': filename,
+                    'error': 'Unexpected error.'}
+        return {'status': 'SUCCESS', 'filename': filename}
+
+    def paste_session(self):
+        import xmlrpclib, cStringIO
+        p = xmlrpclib.ServerProxy(
+            "http://paste.enzotools.org/xmlrpc/",
+            allow_none=True)
+        cs = cStringIO.StringIO()
+        cs.write("\n######\n".join(self.executed_cell_texts))
+        cs = cs.getvalue()
+        ret = p.pastes.newPaste('pytb', cs, None, '', '', True)
+        site = "http://paste.enzotools.org/show/%s" % ret
+        return {'status': 'SUCCESS', 'site': site}
+
+    def _session_py(self):
+        cs = cStringIO.StringIO()
+        cs.write("\n######\n".join(self.executed_cell_texts))
+        cs.seek(0)
+        response.headers["content-disposition"] = "attachment;"
+        return cs
+
+    @append_payloads
+    def _add_widget(self, widget_name):
+        # This should be sanitized
+        widget = self.locals[widget_name]
+        uu = str(uuid.uuid1()).replace("-","_")
+        varname = "%s_%s" % (widget._widget_name, uu)
+        widget._ext_widget_id = varname
+        # THIS BREAKS THE SCRIPT DOWNLOAD!
+        # We need to make the variable be bound via an execution mechanism
+        payload = {'type': 'widget',
+                   'widget_type': widget._widget_name,
+                   'varname': varname}
+        self.payload_handler.add_payload(payload)
+        self.execute("%s = %s\n" % (varname, widget_name))
+
+    @append_payloads
+    def create_proj(self, pfname, axis, field, weight):
+        if weight == "None": weight = None
+        funccall = """
+        _tpf = %(pfname)s
+        _taxis = %(axis)s
+        _tfield = "%(field)s"
+        _tweight = "%(weight)s"
+        _tsl = _tpf.h.proj(_taxis,_tfield, weight_field=_tweight)
+        _txax, _tyax = x_dict[_taxis], y_dict[_taxis]
+        DLE, DRE = _tpf.domain_left_edge, _tpf.domain_right_edge
+        from yt.visualization.plot_window import PWViewerExtJS
+        _tpw = PWViewerExtJS(_tsl, (DLE[_txax], DRE[_txax], DLE[_tyax], DRE[_tyax]))
+        _tpw._current_field = _tfield
+        _tpw.set_log(_tfield, True)
+        add_widget('_tpw')
+        """ % dict(pfname = pfname,
+                   axis = inv_axis_names[axis],
+                   weight = weight,
+                   field=field)
+        # There is a call to do this, but I have forgotten it ...
+        funccall = "\n".join((line.strip() for line in funccall.splitlines()))
+        self.execute(funccall)
+
+    @append_payloads
+    def create_slice(self, pfname, center, axis, field):
+        funccall = """
+        _tpf = %(pfname)s
+        _tcenter = na.array([%(c1)0.20f, %(c2)0.20f, %(c3)0.20f], dtype='float64')
+        _taxis = %(axis)s
+        _tfield = "%(field)s"
+        _tcoord = _tcenter[_taxis]
+        _tsl = _tpf.h.slice(_taxis, _tcoord, center = _tcenter)
+        _txax, _tyax = x_dict[_taxis], y_dict[_taxis]
+        DLE, DRE = _tpf.domain_left_edge, _tpf.domain_right_edge
+        from yt.visualization.plot_window import PWViewerExtJS
+        _tpw = PWViewerExtJS(_tsl, (DLE[_txax], DRE[_txax], DLE[_tyax], DRE[_tyax]))
+        _tpw._current_field = _tfield
+        _tpw.set_log(_tfield, True)
+        add_widget('_tpw')
+        """ % dict(pfname = pfname,
+                   c1 = float(center[0]),
+                   c2 = float(center[1]),
+                   c3 = float(center[2]),
+                   axis = inv_axis_names[axis],
+                   field=field)
+        # There is a call to do this, but I have forgotten it ...
+        funccall = "\n".join((line.strip() for line in funccall.splitlines()))
+        self.execute(funccall)
+
+    def _test_widget(self):
+        class tt(object):
+            _widget_name = "plot_window"
+        mm = tt()
+        return mm
+
+class ExtDirectParameterFileList(BottleDirectRouter):
+    my_name = "ExtDirectParameterFileList"
+    api_url = "pflist"
+
+    def get_list_of_pfs(self):
+        # Note that this instantiates the hierarchy.  This can be a costly
+        # event.  However, we're going to assume that it's okay, if you have
+        # decided to load up the parameter file.
+        from yt.data_objects.static_output import _cached_pfs
+        rv = []
+        for fn, pf in sorted(_cached_pfs.items()):
+            objs = []
+            pf_varname = "_cached_pfs['%s']" % (fn)
+            fields = pf.h.field_list + pf.h.derived_field_list
+            fields.sort()
+            for i,obj in enumerate(pf.h.objects):
+                try:
+                    name = str(obj)
+                except ReferenceError:
+                    continue
+                objs.append(dict(name=name, type=obj._type_name,
+                                 varname = "%s.h.objects[%s]" % (pf_varname, i)))
+            rv.append( dict(name = str(pf), objects = objs, filename=fn,
+                            varname = pf_varname, field_list = fields) )
+        return rv
+
+def ext_load_script(filename):
+    contents = open(filename).read()
+    payload_handler = PayloadHandler()
+    payload_handler.add_payload(
+        {'type': 'cell_contents',
+         'value': contents}
+    )
+    return
+
+class PayloadLoggingHandler(logging.StreamHandler):
+    def __init__(self, *args, **kwargs):
+        logging.StreamHandler.__init__(self, *args, **kwargs)
+        self.payload_handler = PayloadHandler()
+
+    def emit(self, record):
+        msg = self.format(record)
+        self.payload_handler.add_payload(
+            {'type':'log_entry',
+             'log_entry':msg})
+
+if os.path.exists(os.path.expanduser("~/.yt/favicon.ico")):
+    ico = os.path.expanduser("~/.yt/favicon.ico")
+else:
+    ico = os.path.join(local_dir, "html", "images", "favicon.ico")
+ at route("/favicon.ico", method="GET")
+def _favicon_ico():
+    response.headers['Content-Type'] = "image/x-icon"
+    return open(ico).read()
+
+


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/gui/reason/extdirect_router.py	Mon Apr 04 14:09:36 2011 -0400
@@ -0,0 +1,179 @@
+"""
+This is code from the extdirect package:
+
+http://pypi.python.org/pypi/extdirect/0.3
+https://github.com/iancmcc/extdirect
+
+written by Ian McCracken
+
+This code was released under the BSD License.
+"""
+
+import inspect
+
+class DirectException(Exception):
+    pass
+
+
+try:
+    import json
+except ImportError:
+    try:
+        import simplejson as json
+    except ImportError:
+        raise DirectException("No JSON library available. Please install"
+                              " simplejson or upgrade to Python 2.6.")
+
+
+class DirectRouter(object):
+    """
+    Basic Ext.Direct router class.
+
+    Ext.Direct allows one to create an API that communicates with a single URL,
+    which then routes requests to the appropriate method. The client-side API
+    object matches the server-side API object.
+
+    This base class parses an Ext.Direct request, which contains the name of
+    the method and any data that should be passed, and routes the data to the
+    approriate method. It then receives the output of that call and puts it
+    into the data structure expected by Ext.Direct.
+
+    Call an instance of this class with the JSON from an Ext.Direct request.
+    """
+    def __call__(self, body):
+        """
+        """
+        # Decode the request data
+        body = json.loads(body)
+        self._body = body
+
+        if isinstance(body, list):
+            directRequests = body
+        elif isinstance(body, dict):
+            directRequests = [body]
+        else:
+            raise DirectException("Body is not a support type: %s" % body)
+
+        responses = []
+
+        for req in directRequests:
+            responses.append(self._doRequest(req))
+
+        if len(responses) == 1:
+            responses = responses[0]
+
+        return json.dumps(responses)
+
+    def _doRequest(self, request):
+
+        # Double-check that this request is meant for this class
+        action = request.get('action')
+        clsname = self.__class__.__name__
+        if action != clsname:
+            raise DirectException(("Action specified in request ('%s') is"
+                                  " not named %s.") % (action, clsname))
+
+        # Pull out the method name and make sure it exists on this class
+        method = request.get('method')
+        if not method:
+            raise DirectException("No method specified. Is this a valid"
+                                  " Ext.Direct request?")
+        try:
+            _targetfn = getattr(self, method)
+        except AttributeError:
+            raise DirectException("'%s' is not the name of a method on %s" % (
+                method, clsname
+            ))
+
+        # Pull out any arguments. Sent as an array containing a hash map, so
+        # get the first member.
+        data = request.get('data')
+        if not data:
+            data = {}
+        else:
+            data = data[0]
+
+        # Cast all keys as strings, in case of encoding or other wrinkles
+        data = dict((str(k), v) for k,v in data.iteritems())
+        self._data = data
+
+        # Finally, call the target method, passing in the data
+        result = _targetfn(**data)
+
+        return {
+            'type':'rpc',
+            'tid': request['tid'],
+            'action': action,
+            'method': method,
+            'result': result
+        }
+
+
+class DirectProviderDefinition(object):
+    """
+    Turns a L{DirectRouter} subclass into JavaScript object representing the
+    config of the client-side API.
+
+    Inspects the given subclass and retrieves the names of all public methods,
+    then defines those as actions on the Ext.Direct provider, and creates the
+    JS that adds the provider.
+
+    See http://extjs.com/products/extjs/direct.php for a full explanation of
+    protocols and features of Ext.Direct.
+    """
+    def __init__(self, routercls, url, ns=None, timeout=None):
+        """
+        @param routercls: A L{DirectRouter} subclass
+        @type routercls: class
+        @param url: The url at which C{routercls} is available
+        @type url: str
+        @param ns: The client-side namespace in which the provider should live.
+                   The provider will be available at [ns].[routercls.__name__].
+                   For example, if ns is 'Zenoss.remote' and routercls is named
+                   'EventConsole', client-side code would call
+                   C{Zenoss.remote.EventConsole.my_method(params, callback)}.
+        """
+        self.routercls = routercls
+        self.url = url
+        self.ns = ns
+        self.timeout = timeout
+
+    def _config(self):
+        actions = []
+        for name, value in inspect.getmembers(self.routercls):
+            if name.startswith("_"):
+                continue
+            if inspect.ismethod(value):
+
+                ## Update this when extdirect doesn't freak out when you specify
+                ## actual lens (we're passing them all in as a single dict, so
+                ## from the perspective of Ext.Direct they are all len 1)
+                #args = inspect.getargspec(value)[0]
+                #args.remove('self')
+                #arglen = len(args)
+                arglen = 1
+
+                actions.append({'name':name, 'len':arglen})
+        config = {
+            'id': self.routercls.__name__,
+            'type': 'remoting',
+            'url': self.url,
+            'actions': {
+                self.routercls.__name__: actions
+            }
+        }
+        if self.timeout:
+            config['timeout'] = self.timeout
+        if self.ns:
+            config['namespace'] = self.ns
+        return config
+
+    def render(self):
+        """
+        Generate and return an Ext.Direct provider definition, wrapped in a
+        <script> tag and ready for inclusion in an HTML document.
+        """
+        config = self._config()
+        source = "Ext.Direct.addProvider(%s);" % json.dumps(config)
+        return '<script type="text/javascript"> %s </script>' % source.strip()
+


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/gui/reason/html/help.html	Mon Apr 04 14:09:36 2011 -0400
@@ -0,0 +1,145 @@
+<HTML>
+<title>Reason: a GUI for yt</title>
+<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Crimson+Text">
+<link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Inconsolata">
+<style type="text/css">
+body {
+    font-family: "Crimson Text", serif;
+}
+pre {
+    font-family: "Inconsolata", sans-serif;
+}
+div.semi_linked {
+    font-family: "Inconsolata", sans-serif;
+    padding: 1em;
+}
+pre.code{
+    background-color: #E0E0E0;
+    padding: 10px;
+}
+div.yt_logo {
+    float: left;
+    margin-left: 25px;
+    width:200px;
+}
+h1 {
+   font-size: 900%;
+   margin-top: 0.1em;
+   margin-bottom: 0.1em;
+}
+span.logo_y {
+   color: #000000;
+}
+span.logo_t {
+   color: #000000;
+}
+div.faq {
+    text-align: left;
+    width: 640px;
+    margin-left: auto;
+    margin-right: auto;
+    /*background-color: #F9F9F9;*/
+    border-left: 2px solid #D0D0D0;
+    border-right: 2px solid #D0D0D0;
+    padding: 25px;
+    position: absolute;
+    left: 300px;
+}
+p b {
+   color: #444444;
+}
+span.yt_name {
+    font-family: "Inconsolata", sans-serif;
+   color: #009955;
+}
+ul.quick_list {
+    list-style-type: none;
+    padding-left: 0px;
+    line-height: 200%;
+    border-top: 2px solid #D0D0D0;
+    padding-top: 15px;
+    font-size: 125%;
+}
+
+a {
+    /*text-decoration: none;*/
+}
+
+a:link {
+    color: #CC0055;
+}
+
+a:visited {
+    
+    color: #660033;
+}
+
+img.yt_image {
+    width: 220px;
+    height: auto;
+    max-height: 220px;
+    float: right;
+    padding: 5px;
+}
+</style>
+<body>
+<div class="yt_logo">
+<h1>yt:</h1>
+<p>because your data isn't going to analyze itself!</p>
+<ul class="quick_list">
+<li><a href="http://yt.enzotools.org/doc/">docs</a></li>
+<li><a href="http://yt.enzotools.org/wiki/Gallery">gallery</a> ( <a href="video_gallery.html">video</a> )</li>
+<li><a href="http://yt.enzotools.org/wiki">wiki</a></li>
+<li><a href="http://yt.enzotools.org/doc/orientation.html">quick start</a></li>
+<li><a href="http://yt.enzotools.org/newticket">report a bug</a></li>
+<li><a href="http://yt.enzotools.org/browser">source</a></li>
+<li><a href="principles.html">principles</a></li>
+<li><a href="http://blog.enzotools.org/">development blog</a></li>
+</ul>
+</div>
+<div class="faq">
+<h2>Reason: A GUI for yt</h2>
+<p>Reason is an interactive form of yt, totally accessible from a
+web browser.  It has full yt capabilities, as if you are acting from the Python
+intepreter, yet it also provides a new way of interacting directly with your
+data objects.  Because it is run through a web browser, you can run it remotely
+on HPC facilities where your data may reside, while manipulating and
+visualizing it in real time at your own local console.</p>
+
+<h2>Using the yt console</h2>
+<p>The Reason layout consists of a <i>yt input</i> box as well as an output
+window.  Whenever you press shift-enter, the contents of your input box is sent
+to the server and the results returned.  While the server is processing, the
+box around the input form will turn red and become read-only.  When the server
+has returned a result, it will return to normal and allow input.</p>
+
+<p>When using the Reason window to create a plot collection object, the saved
+images will be returned in the window.  You can click on these images to view
+them at full-size.</p>
+
+<h2>Using the interactive plot window</h2>
+<p>As you execute cells and load parameter files from disk, these parameter
+files will get added to the tree view on the left, along with any objects that
+have been created from them.</p>
+
+<p>Over time, these objects will expose right-click events to create plot
+windows, as well as drag-and-drop functionality.</p>
+
+<h2>Saving and loading sessions</h2>
+<p>There are three methods for saving your work.  You can click "Download" to
+download a copy of your session on your local machine.  By clicking "Save" you
+can save a copy on the server on which Reason is running.  By clicking
+"Pastebin" you can send a copy of it to the <a
+href="http://paste.enzotools.org/">yt pastebin</a>.</p>
+
+<p>If you use the command 'load_script' and supply it a file the server can
+find locally, it will read that file in and populate the contents of your next
+submission cell with it.</p>
+
+<h2>How to Quit</h2>
+<p>To quit, simply press Ctrl-C in the console window that you ran "yt serve"
+within.</p>
+
+</div>
+</body>
+</html>


Binary file yt/gui/reason/html/images/3d.png has changed


Binary file yt/gui/reason/html/images/3d_tab.png has changed


Binary file yt/gui/reason/html/images/binary.png has changed


Binary file yt/gui/reason/html/images/blockdevice.png has changed


Binary file yt/gui/reason/html/images/blockdevice_tab.png has changed


Binary file yt/gui/reason/html/images/console.png has changed


Binary file yt/gui/reason/html/images/console_tab.png has changed


Binary file yt/gui/reason/html/images/double_down.png has changed


Binary file yt/gui/reason/html/images/double_down_sm.png has changed


Binary file yt/gui/reason/html/images/double_left.png has changed


Binary file yt/gui/reason/html/images/double_left_sm.png has changed


Binary file yt/gui/reason/html/images/double_right.png has changed


Binary file yt/gui/reason/html/images/double_right_sm.png has changed


Binary file yt/gui/reason/html/images/double_up.png has changed


Binary file yt/gui/reason/html/images/double_up_sm.png has changed


Binary file yt/gui/reason/html/images/favicon.ico has changed


Binary file yt/gui/reason/html/images/graph.png has changed


Binary file yt/gui/reason/html/images/graph_tab.png has changed


Binary file yt/gui/reason/html/images/kivio_flw.png has changed


Binary file yt/gui/reason/html/images/single_down.png has changed


Binary file yt/gui/reason/html/images/single_down_sm.png has changed


Binary file yt/gui/reason/html/images/single_left.png has changed


Binary file yt/gui/reason/html/images/single_left_sm.png has changed


Binary file yt/gui/reason/html/images/single_right.png has changed


Binary file yt/gui/reason/html/images/single_right_sm.png has changed


Binary file yt/gui/reason/html/images/single_up.png has changed


Binary file yt/gui/reason/html/images/single_up_sm.png has changed


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/gui/reason/html/index.html	Mon Apr 04 14:09:36 2011 -0400
@@ -0,0 +1,104 @@
+<html>
+<head>
+  <title>Reason YT GUI</title>
+    <link rel="stylesheet" type="text/css" href="resources/resources/css/ext-all.css" />
+    <link rel="stylesheet" type="text/css" href="highlighter.css" />
+    <link rel="stylesheet" type="text/css" href="theme/css/xtheme-slate.css" />
+    
+    <style type="text/css">
+    html, body {
+        font:normal 12px verdana;
+        margin:0;
+        padding:0;
+        border:0 none;
+        overflow:hidden;
+        height:100%;
+    }
+    p {
+        margin:5px;
+    }
+    .pf_icon {
+        background-image:url(images/blockdevice_tab.png) !important;
+    }
+    .data_object {
+        background-image:url(images/3d_tab.png) !important;
+    }
+    .graph { 
+        background-image:url(images/graph_tab.png) !important; //add images to tabs
+    }
+    .console { 
+        background-image:url(images/console_tab.png) !important;
+    }
+    .doubleleftarrow { 
+        background-image:url(images/double_left_sm.png) !important;
+    }
+    .singleleftarrow { 
+        background-image:url(images/single_left_sm.png) !important;
+    }
+    .doubleuparrow { 
+        background-image:url(images/double_up_sm.png) !important;
+    }
+    .singleuparrow { 
+        background-image:url(images/single_up_sm.png) !important;
+    }
+    .doublerightarrow { 
+        background-image:url(images/double_right_sm.png) !important;
+    }
+    .singlerightarrow { 
+        background-image:url(images/single_right_sm.png) !important;
+    }
+    .doubledownarrow { 
+        background-image:url(images/double_down_sm.png) !important;
+    }
+    .singledownarrow { 
+        background-image:url(images/single_down_sm.png) !important;
+    }
+    #input_line {
+        font-family: monospace;
+    }
+    #cell_output {
+        font-family: monospace;
+    }
+    td.code {
+        padding-left: 20px;
+        font-family: monospace;
+    }
+    div#status-region div.x-grid3-cell-inner {
+        font-family: monospace;
+        font-size: 120%;
+    }
+    .cell_waiting {
+        background-color: #FF0000;
+    }
+    </style>
+
+    <!-- LIBS -->
+    <script type="text/javascript" src="resources/adapter/ext/ext-base.js"></script>
+    <script type="text/javascript" src="resources/ext-all.js"></script>
+
+    <!-- INTERACTIVE -->
+    <script type="text/javascript" src="resources/ext-repl-api.js"></script>
+    <script type="text/javascript" src="resources/ext-pflist-api.js"></script>
+
+    <!-- LOCAL FUNCTIONS -->
+    <script type="text/javascript" src="js/functions.js"></script>
+
+    <!-- THE MAIN FUNCTION -->
+    <script type="text/javascript" src="js/reason.js"></script>
+
+    <!-- THE PLOT WINDOW FUNCTIONS -->
+    <script type="text/javascript" src="js/widget_plotwindow.js"></script>
+</head>
+<body>
+    <!-- use class="x-hide-display" to prevent a brief flicker of the content -->
+    <div id="west" class="x-hide-display">
+    </div>
+    <div id="center2" class="x-hide-display">
+        <a id="hideit" href="#">Toggle the west region</a>
+    </div>
+    <div id="props-panel" class="x-hide-display" style="width:200px;height:200px;overflow:hidden;">
+    </div>
+    <div id="south" class="x-hide-display">
+    </div>
+</body>
+</html>


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/gui/reason/html/js/functions.js	Mon Apr 04 14:09:36 2011 -0400
@@ -0,0 +1,298 @@
+/**********************************************************************
+Functions for Reason
+
+Author: Cameron Hummels <chummels at gmail.com>
+Affiliation: Columbia
+Author: Jeffrey S. Oishi <jsoishi at gmail.com>
+Affiliation: KIPAC/SLAC/Stanford
+Author: Britton Smith <brittonsmith at gmail.com>
+Affiliation: MSU
+Author: Matthew Turk <matthewturk at gmail.com>
+Affiliation: NSF / Columbia
+Homepage: http://yt.enzotools.org/
+License:
+  Copyright (C) 2011 Matthew Turk.  All Rights Reserved.
+
+  This file is part of yt.
+
+  yt is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+***********************************************************************/
+
+function cell_finished(result) {
+    var new_log = false;
+    Ext.each(result, 
+    function(payload, index) {
+        if (payload['type'] == 'cell_results') {
+            text = "<pre>"+payload['output']+"</pre>";
+            formatted_input = payload['input']
+            var cell = new_cell(formatted_input, text);
+            OutputContainer.add(cell);
+            OutputContainer.doLayout();
+            notebook.doLayout();
+            repl_input.get("input_line").setValue("");
+            if (OutputContainer.items.length > 1) {
+                examine = cell;
+                OutputContainer.body.dom.scrollTop = 
+                OutputContainer.body.dom.scrollHeight -
+                cell.body.dom.scrollHeight - 20;
+            }
+        } else if (payload['type'] == 'png_string') {
+            new_cell.add(new Ext.Panel({
+                autoEl:{
+                    tag:'img', 
+                    width:'25%',
+                    src:"data:image/png;base64," + payload['image_data'],
+                    id:"payload_image_" + number_images,
+                    onClick: "display_image('payload_image_" + number_images + "');"
+		        }
+            }));
+	        new_cell.doLayout();
+	        number_images++;
+        } else if (payload['type'] == 'cell_contents') {
+	        var input_line = repl_input.get("input_line");
+	        input_line.setValue(payload['value']);
+        } else if (payload['type'] == 'log_entry') {
+	        var record = new logging_store.recordType(
+		        {record: payload['log_entry'] });
+	        logging_store.add(record, number_log_records++);
+            new_log = true;
+        } else if (payload['type'] == 'widget') {
+            var widget_type = payload['widget_type'];
+            var widget = new widget_types[widget_type](payload['varname']);
+            widget_list[widget.id] = widget;
+        } else if (payload['type'] == 'widget_payload') {
+            var widget = widget_list[payload['widget_id']];
+            widget.accept_results(payload);
+        }
+    });
+    yt_rpc.ExtDirectParameterFileList.get_list_of_pfs({}, fill_tree);
+    if (new_log == true){
+        viewport.get("status-region").getView().focusRow(number_log_records-1);
+    }
+    repl_input.body.removeClass("cell_waiting");
+    repl_input.get('input_line').setReadOnly(false);
+    repl_input.get("input_line").focus();
+}
+
+function cell_sent() {
+    repl_input.get('input_line').setReadOnly(true);
+    repl_input.body.addClass("cell_waiting");
+}
+
+function display_image(image_id) {
+    var image = Ext.get(image_id);
+    var src = image.dom.src;
+    var virtualdom = '<html><title>Image Viewer</title><body><img src="' 
+        + src + '"/></body></html>',
+    prev = window.open('', 'image_viewer');
+    prev.document.open();
+    prev.document.write(virtualdom);
+    prev.document.close();
+}
+
+// Create a tree in the left panel with the pfs and their objects.
+function fill_tree(my_pfs) {
+    treePanel.root.removeAll();
+    Ext.each(my_pfs, function(pf, index) {
+        treePanel.root.appendChild(new Ext.tree.TreeNode({
+            text: pf.name,
+            objdata: {fn: pf.filename, varname: pf.varname, type: 'pf',
+                      field_list: pf.field_list},
+            leaf:false, 
+            expanded:true, 
+            iconCls: 'pf_icon'}));
+        this_pf = treePanel.root.lastChild
+        Ext.each(pf.objects, function(obj, obj_index) {
+            this_pf.appendChild(new Ext.tree.TreeNode(
+                {text: obj.name,
+                 leaf: true,
+                 iconCls: 'data_obj',
+                 objdata: {varname: obj.varname, type: 'obj'},
+                 }));
+        });
+    });
+}
+
+function new_cell(input, result) {
+    var name = "cell_" + cell_count;
+    var CellPanel = new Ext.Panel(
+        { 
+            id: name, 
+            //title: "Cell " + cell_count,
+            items: [
+                new Ext.Panel({
+                    id:name+"_input",
+                    html:input,
+                }),
+                new Ext.Panel({
+                    id:name+"_result",
+                    autoScroll:true,
+                    width: "100%",
+                    html:result,
+                })
+            ]
+        }
+    );
+    cell_count++;
+    return CellPanel;
+}
+
+function getSliceHandler(node){
+function sliceHandler(item,pressed){
+    var win = new Ext.Window({
+        layout:'fit',
+        width:320,
+        height:200,
+        modal:true,
+        resizable:false,
+        draggable:false,
+        border:false,
+        title:'Slice Details for ' + node,
+        items: [{
+            xtype: 'form', // FormPanel
+            labelWidth:80,
+            frame:true,
+            items: [{
+                xtype:'textfield',
+                fieldLabel: 'Center X',
+                id: 'slice_x_center',
+                value: '0.5',
+                width: 90,
+                allowBlank:false,
+            },{
+                xtype:'textfield',
+                fieldLabel: 'Center Y',
+                id: 'slice_y_center',
+                value: '0.5',
+                width: 90,
+                allowBlank:false,
+            },{
+                xtype:'textfield',
+                fieldLabel: 'Center Z',
+                id: 'slice_z_center',
+                value: '0.5',
+                width: 90,
+                allowBlank:false,
+            },{
+                xtype:'combo',
+                fieldLabel: 'Axis',
+                id: 'slice_axis',
+                store:['X','Y','Z'],
+                width: 90,
+                allowBlank:false,
+            },{
+                xtype:'combo',
+                fieldLabel: 'Field',
+                id: 'slice_field',
+                store:node.attributes.objdata.field_list,
+                width: 200,
+                allowBlank:false,
+            }],
+            buttons: [
+                {
+                    text: 'Slice',
+                    handler: function(b, e){
+                        var center = [Ext.get("slice_x_center").getValue(),
+                                      Ext.get("slice_y_center").getValue(),
+                                      Ext.get("slice_z_center").getValue()];
+                        var axis = Ext.get("slice_axis").getValue();
+                        var field = Ext.get("slice_field").getValue();
+                        yt_rpc.ExtDirectREPL.create_slice({
+                            pfname:node.attributes.objdata.varname,
+                            center: center, axis:axis, field:field},
+                          handle_result);
+                        win.close();
+                    }
+                },{
+                    text: 'Cancel',
+                    handler: function(b, e){
+                        win.close();
+                    }
+                }
+            ]
+        }]
+    });
+    win.show(this);
+}
+return sliceHandler;
+}
+
+function widget_call(varname, method) {
+    var fcall = varname + "." + method;
+    yt_rpc.ExtDirectREPL.execute(
+        {code: fcall}, cell_finished);
+}
+
+
+function getProjectionHandler(node){
+function projectionHandler(item,pressed){
+    var win = new Ext.Window({
+        layout:'fit',
+        width:370,
+        height:170,
+        modal:true,
+        resizable:false,
+        draggable:false,
+        border:false,
+        title:'Projection Details for ' + node,
+        items: [{
+            xtype: 'form', // FormPanel
+            labelWidth:80,
+            frame:true,
+            items: [{
+                xtype:'combo',
+                fieldLabel: 'Axis',
+                id: 'axis',
+                store:['X','Y','Z'],
+                width: 90,
+                allowBlank:false,
+            },{
+                xtype:'combo',
+                fieldLabel: 'Field',
+                id: 'field',
+                store:node.attributes.objdata.field_list,
+                width: 230,
+                allowBlank:false,
+            },{
+                xtype:'combo',
+                fieldLabel: 'Weight Field',
+                id: 'weightField',
+                store:node.attributes.objdata.field_list,
+                width: 230,
+                allowBlank:false,
+            }],
+            buttons: [
+                {
+                    text: 'Project',
+                    handler: function(b, e){
+                        var axis = Ext.get("axis").getValue();
+                        var field = Ext.get("field").getValue();
+                        var weight = Ext.get("weightField").getValue();
+                        yt_rpc.ExtDirectREPL.create_proj({
+                                pfname: node.attributes.objdata.varname,
+                                axis: axis, field: field, weight: weight},
+                              handle_result);
+                        win.close();
+                    }
+                },{
+                    text: 'Cancel',
+                    handler: function(b, e){win.close()}
+                }
+            ]
+        }]
+    });
+    win.show(this);
+}
+return projectionHandler;
+}


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/gui/reason/html/js/reason.js	Mon Apr 04 14:09:36 2011 -0400
@@ -0,0 +1,418 @@
+/**********************************************************************
+The main GUI facility for Reason
+
+Author: Cameron Hummels <chummels at gmail.com>
+Affiliation: Columbia
+Author: Jeffrey S. Oishi <jsoishi at gmail.com>
+Affiliation: KIPAC/SLAC/Stanford
+Author: Britton Smith <brittonsmith at gmail.com>
+Affiliation: MSU
+Author: Matthew Turk <matthewturk at gmail.com>
+Affiliation: NSF / Columbia
+Homepage: http://yt.enzotools.org/
+License:
+  Copyright (C) 2011 Matthew Turk.  All Rights Reserved.
+
+  This file is part of yt.
+
+  yt is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+***********************************************************************/
+
+
+var viewport;
+var widget_types = {}
+var widget_list = {}
+
+var examine;
+var number_log_records = 0;
+var number_images = 0;
+
+var res;
+var cell_count = 0;
+
+var handle_result = function(f, a) {
+    var input_line = repl_input.get("input_line")
+    if (a.result == null) {
+        formatted_input = input_line.getValue();
+        text = "ERROR";
+        var cell = new_cell(formatted_input, text);
+        OutputContainer.add(cell);
+        notebook.doLayout();
+        input_line.setValue("");
+        return;
+    }
+    cell_finished(a.result);
+}
+
+    var handle_payload = function(pp) {
+        cell_finished(pp);
+    }
+
+    var repl_input = new Ext.FormPanel({
+        title: 'YT Input',
+        url: 'push',
+        flex: 0.2,
+        layout: 'fit',
+        padding: 5,
+        items: [{
+            id: 'input_line',
+            xtype: 'textarea',
+            width: '100%',
+            autoScroll: true,
+            name: 'line',
+            allowBlank: 'True',
+            bodyStyle: 'font-family: "monospace";',
+            listeners: {
+                specialkey: function(f, e){
+                    if (e.getKey() == e.ENTER) {
+                        cell_sent();
+                        yt_rpc.ExtDirectREPL.execute({
+                            code:repl_input.get('input_line').getValue()},
+                        handle_result);
+	                }
+	            },
+                afterrender: function(f, e){
+                    //var input_line_drop_target_el = repl_input.get("input_line").el.dom;
+                    var input_line_drop_target_el = repl_input.body.dom;
+
+                    var input_line_drop_target = new Ext.dd.DropTarget(input_line_drop_target_el, {
+                        ddGroup     : 'pfDDgroup',
+                        notifyEnter : function(ddSource, e, data) {
+                            repl_input.body.stopFx();
+                            repl_input.body.highlight();
+                        },
+                        notifyDrop  : function(ddSource, e, data){
+
+                            var varname = data.node.attributes.objdata.varname;
+                            /* There is possibly a better way to do this, where it's also
+                               inserted correctly. */
+                            var line = repl_input.get("input_line");
+                            line.setValue(line.getValue() + varname);
+                            line.focus();
+                            return(true);
+                        }
+                    });
+                },
+            },
+        },],
+    });
+
+    var NorthButton = new Ext.Button({
+        text : 'North',
+	    pageX : 205,
+        pageY : 10
+//        handler: function(b, e) { window.open("session.py", "_top"); }
+    });
+
+    var EastButton = new Ext.Button({
+        text:'East',
+        pageX : 410,
+        pageY : 205
+    });
+
+    var SouthButton = new Ext.Button({
+        text:'South',
+        pageX : 205,
+        pageY : 410
+    });
+
+    var WestButton = new Ext.Button({
+        text:'West',
+        pageX : 10,
+        pageY : 205
+    });
+
+    var OutputContainer = new Ext.Panel({
+        title: 'YT Output',
+        id: 'output_container',
+        autoScroll: true,
+        flex: 0.8,
+        items: []
+    });
+
+    var PlotPanel = new Ext.Panel({
+        title: 'Plot Window 1',
+        iconCls: 'graph',
+        autoScroll: true,
+        layout:'absolute',
+        items: [ 
+            NorthButton,
+            EastButton,
+            SouthButton,
+            WestButton
+        ]
+    });
+
+    var examine;
+    var notebook;
+
+
+    var treePanel = new Ext.tree.TreePanel({
+        iconCls: 'nav',
+        id: 'tree-panel',
+        layout: 'anchor',
+        region:'west',
+        split: true,
+        anchor: '100% -35',
+        minSize: 150,
+        autoScroll: true,
+        rootVisible: false,
+        ddGroup: 'pfDDgroup',
+        enableDD: true,
+        root:new Ext.tree.TreeNode({
+            expanded:true,
+            leaf:false,
+            text:''
+        }),
+        listeners: {
+                render: {
+                    fn: function() {
+                        Ext.getBody().on("contextmenu", Ext.emptyFn,
+                            null, {preventDefault: true});
+                    }
+                },
+                contextmenu: {
+                    fn: function(node, event){
+                        if (node.attributes.objdata.type == 'obj') return;
+                        var rightClickMenu = new Ext.menu.Menu({
+                            items: [
+                                {
+                                    text: 'Open slice',
+                                    handler: getSliceHandler(node),
+                                }, {
+                                    text: 'Open projection',
+                                    handler: getProjectionHandler(node),
+                                }
+                            ]
+                        });
+                        rightClickMenu.showAt(event.xy);
+                    }
+                }
+          }
+    });
+
+    var ButtonGroupPanel = new Ext.Panel({
+        layout: 'anchor',
+        ButtonAlign: 'center',
+        collapsible: false,
+        renderTo: document.body,
+        tbar: [{
+            xtype: 'buttongroup',
+            columns: 7,
+            items: [{
+                text: 'Download',
+                layout:'anchor',
+                anchor: '100% 25%',
+                handler: function(b, e) { 
+                    window.open("session.py", "_top"); 
+                    var record = new logging_store.recordType({
+                        record: 'Saved session locally.'});
+                    logging_store.add(record, number_log_records++);
+	            }
+            },{
+                xtype: 'tbseparator'
+            },{
+                text: 'Save',
+                layout:'anchor',
+	            anchor: '100% 50%',
+	            handler: function (b,e) { 
+                    Ext.Msg.prompt("We have important work to do.", 
+                    "Enter filename.", 
+                    function(btn, text) {
+                        if (btn == 'ok'){
+                            yt_rpc.ExtDirectREPL.save_session({filename:text}, 
+                            function(f, a) {
+                                if (a.result['status'] == 'SUCCESS') {
+                                    var alert_text = 'Saved session to ' + 
+                                    a.result['filename']
+                                    Ext.Msg.alert('Success!', alert_text);
+                                    var record = new logging_store.recordType(
+                                        {record: alert_text });
+                                    logging_store.add(record, number_log_records++);
+							    } else {
+							        Ext.Msg.alert('Always naysaying!',
+                                        'Failed to save to ' + 
+                                        a.result['filename'] + 
+                                        '<br>Error: ' + 
+                                        a.result['error']);
+                                }
+                            });
+                        }
+                    });
+                }
+            },{
+                xtype: 'tbseparator'
+            },{
+                text: 'Paste',
+                layout:'anchor',
+                anchor: '100% 75%',
+                handler: function (b,e) { 
+                    yt_rpc.ExtDirectREPL.paste_session({}, function(f, a) {
+                        if (a.result['status'] == 'SUCCESS') {
+                            var alert_text = 'Pasted session to:<br>' + 
+                            a.result['site']
+                            var alert_text_rec = 'Pasted session to: ' + 
+                            a.result['site']
+                            Ext.Msg.alert('Pastebin', alert_text);
+                            var record = new logging_store.recordType(
+                                {record: alert_text_rec });
+                            logging_store.add(record, number_log_records++);
+                        }
+                    }); 
+                }
+            },{
+                xtype: 'tbseparator'
+            },{
+                text: 'Help',
+                layout:'anchor',
+                anchor: '100% 100%',
+                handler: function (b,e) { 
+                        window.open("help.html", "_new");
+                }
+            }]
+        }]
+    });
+
+    var status_panel;
+    var logging_store = new Ext.data.Store({
+        fields: [{name:'record'}],
+        reader: new Ext.data.ArrayReader({}, [{name: 'record'}]),
+    });
+
+    var heartbeep_request = false;
+    var task_runner = new Ext.util.TaskRunner();
+
+
+    Ext.onReady(function(){
+       Ext.BLANK_IMAGE_URL = 'resources/resources/images/default/s.gif';
+
+    // NOTE: This is an example showing simple state management. During development,
+    // it is generally best to disable state management as dynamically-generated ids
+    // can change across page loads, leading to unpredictable results.  The developer
+    // should ensure that stable state ids are set for stateful components in real apps.
+    // it's a cold day for pontooning.
+        Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
+
+    // Go ahead and create the TreePanel now so that we can use it below
+        viewport = new Ext.Viewport({
+            layout: 'border',
+            items: [
+		    // lazily created panel (xtype:'panel' is default)
+                {
+                    xtype: 'grid',
+                    store: logging_store,
+                    defaults: { width: 800 },
+                    columns: [ {id:'record', 
+                        sortable: false,
+                        width:800} ],
+                    autofill: true,
+                    region: 'south',
+                    id: "status-region",
+                    cls: "status-logger",
+                    split: true,
+                    height: 100,
+                    maxSize: 200,
+                    collapsible: true,
+                    title: 'Status',
+                    margins: '0 0 0 0',
+                }, {
+                    region: 'west',
+                    id: 'west-panel', // see Ext.getCmp() below
+                    title: 'Data Objects',
+                    split: true,
+                    width: 200,
+                    minSize: 175,
+                    maxSize: 400,
+                    collapsible: true,
+                    margins: '0 0 0 5',
+                    layout: {
+                        type: 'anchor',
+                    },
+                    items: [
+                        treePanel,
+                        ButtonGroupPanel
+                    ]
+		  // in this instance the TabPanel is not wrapped by another panel
+		  // since no title is needed, this Panel is added directly
+		  // as a Container
+                },{
+                    xtype: 'tabpanel',
+                    region: 'center', 
+                    id: 'center-panel',
+                    deferredRender: false,
+                    activeTab: 0,     
+                    items: [
+                        {
+                            title: 'YT',
+                            id: 'notebook',
+                            layout: 'vbox',
+                            layoutConfig: {align:'stretch'},
+                            closable: false,
+                            autoScroll: false,
+                            iconCls: 'console',
+                            items: [repl_input, OutputContainer]
+                        }, 
+//                        PlotPanel
+                    ]
+                }
+            ]
+        });
+
+// get a reference to the HTML element with id "hideit" and add a click listener to it 
+    console.log('Mitchell!\nPardon me! Mitchell!')
+    Ext.get("hideit").on('click', function(){
+// get a reference to the Panel that was created with id = 'west-panel' 
+	    var w = Ext.getCmp('west-panel');
+// expand or collapse that Panel based on its collapsed property state
+// need to make room for six sour cream burritos
+        w.collapsed ? w.expand() : w.collapse();
+    });
+    
+    notebook = viewport.get("center-panel").get("notebook");
+    status_panel = viewport.get("status-region").get("status-div");
+    
+    var record = new logging_store.recordType(
+        {record: 'Welcome to yt.'});
+    logging_store.add(record, number_log_records++);
+
+    var record = new logging_store.recordType(
+        {record: 'After entering a line of code in the YT Input field, press shift-enter to evaluate.' });
+    logging_store.add(record, number_log_records++);
+
+    var record = new logging_store.recordType(
+        {record: '4d3d3d3 engaged.' });
+    logging_store.add(record, number_log_records++);
+
+    if (!Ext.state.Manager.get("reason_welcomed", false)) {
+        Ext.MessageBox.alert("Reason v0.5",
+        "Welcome to Reason.  <br>Treat the 'YT Input' field as a YT/python intepreter.<br>Press shift-enter to evaluate.",
+        function(b,e){ repl_input.get("input_line").focus(); });
+        Ext.state.Manager.set("reason_welcomed", true);
+    } else { 
+        repl_input.get("input_line").focus(); }
+
+    /* Set up the heartbeep */
+    var heartbeep = {
+    run:
+      function(){ if (heartbeep_request == true) return; 
+        yt_rpc.ExtDirectREPL.heartbeep(
+            {}, function(f, a) {
+            heartbeep_request = false;
+            if (f['alive'] == true) { 
+            }})},
+    interval: 5000};
+
+    //task_runner.start(heartbeep);
+                         
+    });


--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/yt/gui/reason/html/js/widget_plotwindow.js	Mon Apr 04 14:09:36 2011 -0400
@@ -0,0 +1,224 @@
+/**********************************************************************
+The Plot Window Widget
+
+Author: Cameron Hummels <chummels at gmail.com>
+Affiliation: Columbia
+Author: Jeffrey S. Oishi <jsoishi at gmail.com>
+Affiliation: KIPAC/SLAC/Stanford
+Author: Britton Smith <brittonsmith at gmail.com>
+Affiliation: MSU
+Author: Matthew Turk <matthewturk at gmail.com>
+Affiliation: NSF / Columbia
+Homepage: http://yt.enzotools.org/
+License:
+  Copyright (C) 2011 Matthew Turk.  All Rights Reserved.
+
+  This file is part of yt.
+
+  yt is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 3 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+***********************************************************************/
+
+
+
+var WidgetPlotWindow = function(python_varname) {
+    this.id = python_varname;
+    this.print_python = function(b, e) {
+        yt_rpc.ExtDirectREPL.execute(
+            {code:'print "' + python_varname + '"'},
+            function(f, a) {alert(a.result['output']);}
+        );
+    }
+
+    viewport.get("center-panel").add(
+        {
+            xtype: 'panel',
+            id: "pw_" + this.id,
+            title: "Plot Window",
+            iconCls: 'graph',
+            autoScroll: true,
+            layout:'absolute',
+            closable: true,
+            items: [ 
+                {
+                    xtype:'panel',
+                    id: 'image_panel_' + this.id,
+                    autoEl: {
+                        tag: 'img',
+                        id: "img_" + this.id,
+                        width: 400,
+                        height: 400,
+                    },
+                    x: 100,
+                    y: 10,
+                    width: 400,
+                    height: 400,
+                }, 
+                /* the single buttons for 10% pan*/
+                {
+                    xtype:'button',
+                    iconCls: 'singleuparrow',
+                    //text: 'North',
+                    x: 40,
+                    y: 10,
+                    handler: function(b,e) {
+                        cc = python_varname + '.pan_rel((0.0, -0.1))'
+                        yt_rpc.ExtDirectREPL.execute(
+                        {code:cc}, cell_finished); 
+                    }
+                }, {
+                    xtype:'button',
+                    iconCls: 'singlerightarrow',
+                    //text:'East',
+                    x : 60,
+                    y : 30,
+                    handler: function(b,e) {
+                        yt_rpc.ExtDirectREPL.execute(
+                            {code:python_varname + '.pan_rel((0.1, 0.0))'},
+                        cell_finished); 
+                    }
+                }, {
+                    xtype:'button',
+                    iconCls: 'singledownarrow',
+                    //text: 'South',
+                    x: 40,
+                    y: 50,
+                    handler: function(b,e) {
+                        yt_rpc.ExtDirectREPL.execute(
+                            {code:python_varname + '.pan_rel((0.0, 0.1))'},
+                        cell_finished); 
+                    }
+                }, {
+                    xtype: 'button',
+                    iconCls: 'singleleftarrow',
+                    //text: 'West',
+                    x: 20,
+                    y: 30,
+                    handler: function(b,e) {
+                        yt_rpc.ExtDirectREPL.execute(
+                            {code:python_varname + '.pan_rel((-0.1, 0.0))'},
+                        cell_finished); 
+                    }
+                }, 
+                /* the double buttons for 50% pan*/
+                {
+                    xtype:'button',
+                    iconCls: 'doubleuparrow',
+                    //text: 'North',
+                    x: 40,
+                    y: 80,
+                    handler: function(b,e) {
+                        cc = python_varname + '.pan_rel((0.0, -0.5))'
+                        yt_rpc.ExtDirectREPL.execute(
+                        {code:cc}, cell_finished); 
+                    }
+                }, {
+                    xtype:'button',
+                    iconCls: 'doublerightarrow',
+                    //text:'East',
+                    x : 60,
+                    y : 100,
+                    handler: function(b,e) {
+                        yt_rpc.ExtDirectREPL.execute(
+                            {code:python_varname + '.pan_rel((0.5, 0.0))'},
+                        cell_finished); 
+                    }
+                }, {
+                    xtype:'button',
+                    iconCls: 'doubledownarrow',
+                    //text: 'South',
+                    x: 40,
+                    y: 120,
+                    handler: function(b,e) {
+                        yt_rpc.ExtDirectREPL.execute(
+                            {code:python_varname + '.pan_rel((0.0, 0.5))'},
+                        cell_finished); 
+                    }
+                }, {
+                    xtype: 'button',
+                    iconCls: 'doubleleftarrow',
+                    //text: 'West',
+                    x: 20,
+                    y: 100,
+                    handler: function(b,e) {
+                        yt_rpc.ExtDirectREPL.execute(
+                            {code:python_varname + '.pan_rel((-0.5, 0.0))'},
+                        cell_finished); 
+                    }
+                },
+                /* Now the zoom buttons */
+                {
+                    xtype: 'button',
+                    text: 'Zoom In 10x',
+                    x: 10,
+                    y: 160,
+                    width: 80,
+                    handler: function(b,e) {
+                        yt_rpc.ExtDirectREPL.execute(
+                            {code:python_varname + '.zoom(10.0)'},
+                        cell_finished); 
+                    }
+                },{
+                    xtype: 'button',
+                    text: 'Zoom In 2x',
+                    x: 10,
+                    y: 185,
+                    width: 80,
+                    handler: function(b,e) {
+                        yt_rpc.ExtDirectREPL.execute(
+                            {code:python_varname + '.zoom(2.0)'},
+                        cell_finished); 
+                    }
+                },{
+                    xtype: 'button',
+                    text: 'Zoom Out 2x',
+                    x: 10,
+                    y: 210,
+                    width: 80,
+                    handler: function(b,e) {
+                        yt_rpc.ExtDirectREPL.execute(
+                            {code:python_varname + '.zoom(0.5)'},
+                        cell_finished); 
+                    }
+                },{
+                    xtype: 'button',
+                    text: 'Zoom Out 10x',
+                    x: 10,
+                    y: 235,
+                    width: 80,
+                    handler: function(b,e) {
+                        yt_rpc.ExtDirectREPL.execute(
+                            {code:python_varname + '.zoom(0.1)'},
+                        cell_finished); 
+                    }
+                }
+            ]
+        }
+    );
+
+    viewport.get("center-panel").activate("pw_" + this.id);
+    viewport.doLayout();
+    this.panel = viewport.get("center-panel").get("pw_" + python_varname);
+    this.panel.doLayout();
+    this.image_panel = this.panel.get("image_panel_"+python_varname);
+
+    this.accept_results = function(payload) {
+        this.image_panel.el.dom.src = "data:image/png;base64," + payload['image_data'];
+    }
+
+    yt_rpc.ExtDirectREPL.execute(
+        {code:python_varname + '.zoom(1.0)'},
+        cell_finished);
+}
+
+widget_types['plot_window'] = WidgetPlotWindow;


--- a/yt/gui/reason/http_repl.py	Mon Apr 04 14:09:15 2011 -0400
+++ b/yt/gui/reason/http_repl.py	Mon Apr 04 14:09:36 2011 -0400
@@ -30,6 +30,8 @@
 from .bottle_mods import preroute
 from .basic_repl import ProgrammaticREPL
 
+local_dir = os.path.dirname(__file__)
+
 class HTTPREPL(ProgrammaticREPL):
 
     def __init__(self, locals=None):
@@ -43,15 +45,17 @@
         preroute_table = dict(index = ("/", "GET"),
                               push = ("/push", "POST"),
                               dir = ("/dir", "GET"),
-                              doc = ("/doc", "GET"))
-        for v, args in preroute_table:
+                              doc = ("/doc", "GET"),
+                              resources = ("/resources/:val", "GET"))
+        for v, args in preroute_table.items():
             preroute(args[0], method=args[1])(getattr(self, v))
 
     def index(self):
         """Return an HTTP-based Read-Eval-Print-Loop terminal."""
         # For now this doesn't work!  We will need to move to a better method
         # for this.
-        return open(os.path.join(localDir, "httprepl.html")).read()
+        vals = open(os.path.join(local_dir, "httprepl.html")).read()
+        return vals
         
     def push(self):
         """Push 'line' and return exec results as a bare response."""
@@ -79,3 +83,11 @@
         if not result:
             response.status = 204
         return result
+
+    def resources(self, val):
+        pp = os.path.join(local_dir, "resources", val)
+        if not os.path.exists(pp):
+            response.status = 404
+            return
+        return open(pp).read()
+


--- a/yt/gui/reason/setup.py	Mon Apr 04 14:09:15 2011 -0400
+++ b/yt/gui/reason/setup.py	Mon Apr 04 14:09:36 2011 -0400
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 import setuptools
-import os, sys, os.path
+import os, sys, os.path, glob
 
 def configuration(parent_package='',top_path=None):
     from numpy.distutils.misc_util import Configuration


--- a/yt/utilities/_amr_utils/Octree.pyx	Mon Apr 04 14:09:15 2011 -0400
+++ b/yt/utilities/_amr_utils/Octree.pyx	Mon Apr 04 14:09:36 2011 -0400
@@ -32,24 +32,39 @@
 
 from stdlib cimport malloc, free, abs
 
+import sys, time
+
 cdef extern from "stdlib.h":
     # NOTE that size_t might not be int
     void *alloca(int)
 
+cdef extern from "math.h":
+    double sqrt(double x)
+
+cdef inline np.float64_t f64max(np.float64_t f0, np.float64_t f1):
+    if f0 > f1: return f0
+    return f1
+
 cdef struct OctreeNode:
     np.float64_t *val
     np.float64_t weight_val
     np.int64_t pos[3]
     int level
     int nvals
+    int max_level # The maximum level under this node with mass.
     OctreeNode *children[2][2][2]
+    OctreeNode *parent
+    OctreeNode *next
+    OctreeNode *up_next
 
 cdef void OTN_add_value(OctreeNode *self,
-        np.float64_t *val, np.float64_t weight_val):
+        np.float64_t *val, np.float64_t weight_val, int level, int treecode):
     cdef int i
     for i in range(self.nvals):
         self.val[i] += val[i]
     self.weight_val += weight_val
+    if treecode and val[0] > 0.:
+        self.max_level = imax(self.max_level, level)
 
 cdef void OTN_refine(OctreeNode *self, int incremental = 0):
     cdef int i, j, k, i1, j1
@@ -65,14 +80,14 @@
                 self.children[i][j][k] = OTN_initialize(
                             npos,
                             self.nvals, self.val, self.weight_val,
-                            self.level + 1)
+                            self.level + 1, self)
     if incremental: return
     for i in range(self.nvals): self.val[i] = 0.0
     self.weight_val = 0.0
 
 cdef OctreeNode *OTN_initialize(np.int64_t pos[3], int nvals,
                         np.float64_t *val, np.float64_t weight_val,
-                        int level):
+                        int level, OctreeNode *parent):
     cdef OctreeNode *node
     cdef int i, j, k
     node = <OctreeNode *> malloc(sizeof(OctreeNode))
@@ -80,6 +95,10 @@
     node.pos[1] = pos[1]
     node.pos[2] = pos[2]
     node.nvals = nvals
+    node.parent = parent
+    node.next = NULL
+    node.up_next = NULL
+    node.max_level = 0
     node.val = <np.float64_t *> malloc(
                 nvals * sizeof(np.float64_t))
     for i in range(nvals):
@@ -108,6 +127,12 @@
     cdef OctreeNode ****root_nodes
     cdef np.int64_t top_grid_dims[3]
     cdef int incremental
+    # Below is for the treecode.
+    cdef np.float64_t opening_angle
+    # We'll store dist here so it doesn't have to be calculated twice.
+    cdef np.float64_t dist
+    cdef np.float64_t root_dx[3]
+    cdef OctreeNode *last_node
 
     def __cinit__(self, np.ndarray[np.int64_t, ndim=1] top_grid_dims,
                   int nvals, int incremental = False):
@@ -144,19 +169,19 @@
                 for k in range(top_grid_dims[2]):
                     pos[2] = k
                     self.root_nodes[i][j][k] = OTN_initialize(
-                        pos, nvals, vals, weight_val, 0)
+                        pos, nvals, vals, weight_val, 0, NULL)
 
     cdef void add_to_position(self,
                  int level, np.int64_t pos[3],
                  np.float64_t *val,
-                 np.float64_t weight_val):
+                 np.float64_t weight_val, treecode):
         cdef int i, j, k, L
         cdef OctreeNode *node
         node = self.find_on_root_level(pos, level)
         cdef np.int64_t fac
         for L in range(level):
             if self.incremental:
-                OTN_add_value(node, val, weight_val)
+                OTN_add_value(node, val, weight_val, level, treecode)
             if node.children[0][0][0] == NULL:
                 OTN_refine(node, self.incremental)
             # Maybe we should use bitwise operators?
@@ -165,7 +190,7 @@
             j = (pos[1] >= fac*(2*node.pos[1]+1))
             k = (pos[2] >= fac*(2*node.pos[2]+1))
             node = node.children[i][j][k]
-        OTN_add_value(node, val, weight_val)
+        OTN_add_value(node, val, weight_val, level, treecode)
             
     cdef OctreeNode *find_on_root_level(self, np.int64_t pos[3], int level):
         # We need this because the root level won't just have four children
@@ -184,7 +209,8 @@
             np.ndarray[np.int64_t, ndim=1] pys,
             np.ndarray[np.int64_t, ndim=1] pzs,
             np.ndarray[np.float64_t, ndim=2] pvals,
-            np.ndarray[np.float64_t, ndim=1] pweight_vals):
+            np.ndarray[np.float64_t, ndim=1] pweight_vals,
+            int treecode = 0):
         cdef int np = pxs.shape[0]
         cdef int p
         cdef cnp.float64_t *vals
@@ -195,7 +221,7 @@
             pos[0] = pxs[p]
             pos[1] = pys[p]
             pos[2] = pzs[p]
-            self.add_to_position(level, pos, vals, pweight_vals[p])
+            self.add_to_position(level, pos, vals, pweight_vals[p], treecode)
 
     def add_grid_to_tree(self, int level,
                          np.ndarray[np.int64_t, ndim=1] start_index,
@@ -275,6 +301,306 @@
                             level, curpos + added, pdata, vdata, wdata)
         return added
 
+    @cython.cdivision(True)
+    cdef np.float64_t fbe_node_separation(self, OctreeNode *node1, OctreeNode *node2):
+        # Find the distance between the two nodes. To match FindBindingEnergy
+        # in data_point_utilities.c, we'll do this in code units.
+        cdef np.float64_t dx1, dx2, p1, p2, dist
+        cdef int i
+        dist = 0.0
+        for i in range(3):
+            # Discover the appropriate dx for each node/dim.
+            dx1 = self.root_dx[i] / (<np.float64_t> self.po2[node1.level])
+            dx2 = self.root_dx[i] / (<np.float64_t> self.po2[node2.level])
+            # The added term is to re-cell center the data.
+            p1 = (<np.float64_t> node1.pos[i]) * dx1 + dx1/2.
+            p2 = (<np.float64_t> node2.pos[i]) * dx2 + dx2/2.
+            dist += (p1 - p2) * (p1 - p2)
+        dist = sqrt(dist)
+        return dist
+    
+    @cython.cdivision(True)
+    cdef np.float64_t fbe_opening_angle(self, OctreeNode *node1,
+            OctreeNode *node2):
+        # Calculate the opening angle of node2 upon the center of node1.
+        # In order to keep things simple, we will not assume symmetry in all
+        # three directions of the octree, and we'll use the largest dimension
+        # if the tree is not symmetric. This is not strictly the opening angle
+        # the purest sense, but it's slightly more accurate, so it's OK.
+        # This is done in code units to match the distance calculation.
+        cdef np.float64_t d2, dx2, dist
+        cdef np.int64_t n2
+        cdef int i
+        d2 = 0.0
+        if node1 is node2: return 100000.0 # Just some large number.
+        if self.top_grid_dims[1] == self.top_grid_dims[0] and \
+                self.top_grid_dims[2] == self.top_grid_dims[0]:
+            # Symmetric
+            n2 = self.po2[node2.level] * self.top_grid_dims[0]
+            d2 = 1. / (<np.float64_t> n2)
+        else:
+            # Not symmetric
+            for i in range(3):
+                n2 = self.po2[node2.level] * self.top_grid_dims[i]
+                dx2 = 1. / (<np.float64_t> n2)
+                d2 = f64max(d2, dx2)
+        # Now calculate the opening angle.
+        dist = self.fbe_node_separation(node1, node2)
+        self.dist = dist
+        return d2 / dist
+
+    cdef void set_next(self, OctreeNode *node, int treecode):
+        # This sets up the linked list, pointing node.next to the next node
+        # in the iteration order.
+        cdef int i, j, k
+        if treecode and node.val[0] is not 0.:
+            # In a treecode, we only make a new next link if this node has mass.
+            self.last_node.next = node
+            self.last_node = node
+        elif treecode and node.val[0] is 0.:
+            # No mass means it's children have no mass, no need to dig an
+            # further.
+            return
+        else:
+            # We're not doing the treecode, but we still want a linked list,
+            # we don't care about val[0] necessarily.
+            self.last_node.next = node
+            self.last_node = node
+        if node.children[0][0][0] is NULL: return
+        for i in range(2):
+            for j in range(2):
+                for k in range(2):
+                    self.set_next(node.children[i][j][k], treecode)
+        return
+
+    cdef void set_up_next(self, OctreeNode *node):
+        # This sets up a second linked list, pointing node.up_next to the next
+        # node in the list that is at the same or lower (coarser) level than
+        # this node. This is useful in the treecode for skipping over nodes
+        # that don't need to be inspected.
+        cdef int i, j, k
+        cdef OctreeNode *initial_next
+        cdef OctreeNode *temp_next
+        initial_next = node.next
+        temp_next = node.next
+        if node.next is NULL: return
+        while temp_next.level > node.level:
+            temp_next = temp_next.next
+            if temp_next is NULL: break
+        node.up_next = temp_next
+        self.set_up_next(initial_next)
+
+    def finalize(self, int treecode = 0):
+        # Set up the linked list for the nodes.
+        # Set treecode = 1 if nodes with no mass are to be skipped in the
+        # list.
+        cdef int i, j, k, sum, top_grid_total, ii, jj, kk
+        cdef OctreeNode *this_node
+        self.last_node = self.root_nodes[0][0][0]
+        for i in range(self.top_grid_dims[0]):
+            for j in range(self.top_grid_dims[1]):
+                for k in range(self.top_grid_dims[2]):
+                    self.set_next(self.root_nodes[i][j][k], treecode)
+        # Now we want to link to the next node in the list that is
+        # on a level the same or lower (coarser) than us. This will be used
+        # during a treecode search so we can skip higher-level (finer) nodes.
+        sum = 1
+        top_grid_total = self.top_grid_dims[0] * self.top_grid_dims[1] * \
+            self.top_grid_dims[2]
+        for i in range(self.top_grid_dims[0]):
+            for j in range(self.top_grid_dims[1]):
+                for k in range(self.top_grid_dims[2]):
+                    self.set_up_next(self.root_nodes[i][j][k])
+                    # Point the root_nodes.up_next to the next root_node in the
+                    # list, except for the last one which stays pointing to NULL.
+                    if sum < top_grid_total - 1:
+                        ii = i
+                        jj = j
+                        kk = (k + 1) % self.top_grid_dims[2]
+                        if kk < k:
+                            jj = (j + 1) % self.top_grid_dims[1]
+                            if jj < j:
+                                ii = (i + 1) % self.top_grid_dims[0]
+                        self.root_nodes[i][j][k].up_next = \
+                            self.root_nodes[ii][jj][kk]
+                    sum += 1
+
+    @cython.cdivision(True)
+    cdef np.float64_t fbe_main(self, np.float64_t potential, int truncate,
+            np.float64_t kinetic):
+        # The work is done here. Starting at the top of the linked list of
+        # nodes, 
+        cdef np.float64_t angle, dist
+        cdef OctreeNode *this_node
+        cdef OctreeNode *pair_node
+        cdef int pair_count
+        cdef int to_break
+        to_break = 0
+        this_node = self.root_nodes[0][0][0]
+        while this_node is not NULL:
+            # Iterate down the list to a node that either has no children and
+            # is at the max_level of the tree, or to a node where
+            # all of its children are massless. The second case is when data
+            # from a level that isn't the deepest has been added to the tree.
+            while this_node.max_level is not this_node.level:
+                this_node = this_node.next
+                # In case we reach the end of the list...
+                if this_node is NULL: break
+            if this_node is NULL: break
+            if truncate and potential > kinetic:
+                print 'Truncating...'
+                break
+            pair_node = this_node.next
+            while pair_node is not NULL:
+                # If pair_node is massless, we can jump to up_next, because
+                # nothing pair_node contains will have mass either.
+                # I think that this should primarily happen for root_nodes
+                # created for padding to make the region cubical.
+                if pair_node.val[0] is 0.0:
+                    pair_node = pair_node.up_next
+                    continue
+                # If pair_node is a childless node, or is a coarser node with
+                # no children, we can calculate the pot
+                # right now, and get a new pair_node.
+                if pair_node.max_level is pair_node.level:
+                    dist = self.fbe_node_separation(this_node, pair_node)
+                    potential += this_node.val[0] * pair_node.val[0] / dist
+                    if truncate and potential > kinetic: break
+                    pair_node = pair_node.next
+                    continue
+                # Next, if the opening angle to pair_node is small enough,
+                # calculate the potential and get a new pair_node using
+                # up_next because we don't need to look at pair_node's children.
+                angle = self.fbe_opening_angle(this_node, pair_node)
+                if angle < self.opening_angle:
+                    # self.dist was just set in fbe_opening_angle, so we
+                    # can use it here without re-calculating it for these two
+                    # nodes.
+                    potential += this_node.val[0] * pair_node.val[0] / self.dist
+                    if truncate and potential > kinetic: break
+                    # We can skip all the nodes that are contained within 
+                    # pair_node, saving time walking the linked list.
+                    pair_node = pair_node.up_next
+                # If we've gotten this far, pair_node has children, but it's
+                # too coarse, so we simply dig deeper using .next.
+                else:
+                    pair_node = pair_node.next
+            # We've exhausted the pair_nodes.
+            # Now we find a new this_node in the list, and do more searches
+            # over pair_node.
+            this_node = this_node.next
+        return potential
+
+    @cython.boundscheck(False)
+    @cython.wraparound(False)
+    def find_binding_energy(self, int truncate, np.float64_t kinetic,
+        np.ndarray[np.float64_t, ndim=1] root_dx, float opening_angle = 1.0):
+        r"""Find the binding energy of an ensemble of data points using the
+        treecode method.
+        
+        Note: The first entry of the vals array MUST be Mass. Any other
+        values will be ignored, including the weight array.
+        """
+        # The real work is done in fbe_main(), this just sets things up
+        # and returns the potential.
+        cdef int i, j, k, sum
+        cdef np.float64_t potential
+        potential = 0.0
+        self.opening_angle = opening_angle
+        for i in range(3):
+            self.root_dx[i] = root_dx[i]
+        potential = self.fbe_main(potential, truncate, kinetic)
+        return potential
+
+    cdef int node_ID(self, OctreeNode *node):
+        # Returns an unique ID for this node based on its position and level.
+        cdef int ID, i, offset, root
+        cdef np.int64_t this_grid_dims[3]
+        offset = 0
+        root = 1
+        for i in range(3):
+            root *= self.top_grid_dims[i]
+            this_grid_dims[i] = self.top_grid_dims[i] * 2**node.level
+        for i in range(node.level):
+            offset += root * 2**(3 * i)
+        ID = offset + (node.pos[0] + this_grid_dims[0] * (node.pos[1] + \
+            this_grid_dims[1] * node.pos[2]))
+        return ID
+
+    cdef int node_ID_on_level(self, OctreeNode *node):
+        # Returns the node ID on node.level for this node.
+        cdef int ID, i
+        cdef np.int64_t this_grid_dims[3]
+        for i in range(3):
+            this_grid_dims[i] = self.top_grid_dims[i] * 2**node.level
+        ID = node.pos[0] + this_grid_dims[0] * (node.pos[1] + \
+            this_grid_dims[1] * node.pos[2])
+        return ID
+
+    cdef void print_node_info(self, OctreeNode *node):
+        cdef int i, j, k
+        line = "%d\t" % self.node_ID(node)
+        if node.next is not NULL:
+            line += "%d\t" % self.node_ID(node.next)
+        else: line += "-1\t"
+        if node.up_next is not NULL:
+            line += "%d\t" % self.node_ID(node.up_next)
+        else: line += "-1\t"
+        line += "%d\t%d\t%d\t%d\t" % (node.level,node.pos[0],node.pos[1],node.pos[2])
+        for i in range(node.nvals):
+            line += "%1.5e\t" % node.val[i]
+        line += "%f\t" % node.weight_val
+        line += "%s\t%s\t" % (node.children[0][0][0] is not NULL, node.parent is not NULL)
+        if node.children[0][0][0] is not NULL:
+            nline = ""
+            for i in range(2):
+                for j in range(2):
+                    for k in range(2):
+                        nline += "%d," % self.node_ID(node.children[i][j][k])
+            line += nline
+        print line
+        return
+
+    cdef void iterate_print_nodes(self, OctreeNode *node):
+        cdef int i, j, k
+        self.print_node_info(node)
+        if node.children[0][0][0] is NULL:
+            return
+        for i in range(2):
+            for j in range(2):
+                for k in range(2):
+                    self.iterate_print_nodes(node.children[i][j][k])
+        return
+
+    def print_all_nodes(self):
+        r"""
+        Prints out information about all the nodes in the octree.
+        
+        Parameters
+        ----------
+        None.
+        
+        Examples
+        --------
+        >>> octree.print_all_nodes()
+        (many lines of data)
+        """
+        cdef int i, j, k
+        sys.stdout.flush()
+        sys.stderr.flush()
+        line = "ID\tnext\tup_n\tlevel\tx\ty\tz\t"
+        for i in range(self.nvals):
+            line += "val%d\t\t" % i
+        line += "weight\t\tchild?\tparent?\tchildren"
+        print line
+        for i in range(self.top_grid_dims[0]):
+            for j in range(self.top_grid_dims[1]):
+                for k in range(self.top_grid_dims[2]):
+                    self.iterate_print_nodes(self.root_nodes[i][j][k])
+        sys.stdout.flush()
+        sys.stderr.flush()
+        return
+
     def __dealloc__(self):
         cdef int i, j, k
         for i in range(self.top_grid_dims[0]):


--- a/yt/utilities/answer_testing/output_tests.py	Mon Apr 04 14:09:15 2011 -0400
+++ b/yt/utilities/answer_testing/output_tests.py	Mon Apr 04 14:09:36 2011 -0400
@@ -134,7 +134,7 @@
 
     def __iter__(self):
         for line in open(self.io_log):
-            yield line[len(self.io_log_header):].strip()
+            yield line[len(self.io_log_header):].split()[0].strip()
 
 def create_test(base, new_name, **attrs):
     """


--- a/yt/utilities/answer_testing/runner.py	Mon Apr 04 14:09:15 2011 -0400
+++ b/yt/utilities/answer_testing/runner.py	Mon Apr 04 14:09:36 2011 -0400
@@ -125,7 +125,7 @@
             project = imp.load_module(mname, f, filename, desc)
 
     def _update_io_log(self, opts, kwargs):
-        if len(opts.datasets) == 0: return
+        if opts.datasets is None or len(opts.datasets) == 0: return
         f = tempfile.NamedTemporaryFile()
         kwargs['io_log'] = f.name
         for d in opts.datasets:


--- a/yt/utilities/command_line.py	Mon Apr 04 14:09:15 2011 -0400
+++ b/yt/utilities/command_line.py	Mon Apr 04 14:09:36 2011 -0400
@@ -324,7 +324,7 @@
             print
             print "This installation CAN be automatically updated."
             if opts.update_source:  
-                _vcs_updater[vc_type](path)
+                _update_hg(path)
             print "Updated successfully."
         elif opts.update_source:
             print
@@ -908,6 +908,52 @@
         print
         print "Good luck!"
 
+    @cmdln.option("-o", "--open-browser", action="store_true",
+                  default = False, dest='open_browser',
+                  help="Open a web browser.")
+    @cmdln.option("-p", "--port", action="store",
+                  default = 0, dest='port',
+                  help="Port to listen on")
+    def do_serve(self, subcmd, opts):
+        """
+        Run the Web GUI
+        """
+        # We have to do a couple things.
+        # First, we check that YT_DEST is set.
+        if "YT_DEST" not in os.environ:
+            print
+            print "*** You must set the environment variable YT_DEST ***"
+            print "*** to point to the installation location!        ***"
+            print
+            sys.exit(1)
+        if opts.port == 0:
+            # This means, choose one at random.  We do this by binding to a
+            # socket and allowing the OS to choose the port for that socket.
+            import socket
+            sock = socket.socket()
+            sock.bind(('', 0))
+            opts.port = sock.getsockname()[-1]
+            del sock
+        base_extjs_path = os.path.join(os.environ["YT_DEST"], "src")
+        if not os.path.isfile(os.path.join(base_extjs_path, "ext-resources", "ext-all.js")):
+            print
+            print "*** You are missing the ExtJS support files. You  ***"
+            print "*** You can get these by either rerunning the     ***"
+            print "*** install script installing, or downloading     ***"
+            print "*** them manually.                                ***"
+            print
+            sys.exit(1)
+        from yt.config import ytcfg;ytcfg["yt","__withinreason"]="True"
+        import yt.gui.reason.bottle as bottle
+        from yt.gui.reason.extdirect_repl import ExtDirectREPL
+        from yt.gui.reason.bottle_mods import uuid_serve_functions
+
+        hr = ExtDirectREPL(base_extjs_path)
+        bottle.debug()
+        uuid_serve_functions(open_browser=opts.open_browser,
+                    port=int(opts.port))
+
+
 def run_main():
     for co in ["--parallel", "--paste"]:
         if co in sys.argv: del sys.argv[sys.argv.index(co)]


--- a/yt/utilities/data_point_utilities.c	Mon Apr 04 14:09:15 2011 -0400
+++ b/yt/utilities/data_point_utilities.c	Mon Apr 04 14:09:36 2011 -0400
@@ -1604,7 +1604,6 @@
     Py_DECREF(x);
     Py_DECREF(y);
     Py_DECREF(z);
-
     PyObject *status = PyFloat_FromDouble(total_potential);
     return status;
 


--- a/yt/visualization/image_writer.py	Mon Apr 04 14:09:15 2011 -0400
+++ b/yt/visualization/image_writer.py	Mon Apr 04 14:09:36 2011 -0400
@@ -137,7 +137,7 @@
     au.write_png(bitmap_array.copy(), filename)
     return bitmap_array
 
-def write_image(image, filename, color_bounds = None, cmap_name = "algae"):
+def write_image(image, filename, color_bounds = None, cmap_name = "algae", func = lambda x: x):
     r"""Write out a floating point array directly to a PNG file, scaling it and
     applying a colormap.
 
@@ -157,7 +157,9 @@
     cmap_name : string, optional
         An acceptable colormap.  See either yt.visualization.color_maps or
         http://www.scipy.org/Cookbook/Matplotlib/Show_colormaps .
-        
+    func : function, optional
+        A function to transform the buffer before applying a colormap. 
+
     Returns
     -------
     scaled_image : uint8 image that has been saved
@@ -173,6 +175,36 @@
     if len(image.shape) == 3:
         mylog.info("Using only channel 1 of supplied image")
         image = image[:,:,0]
+    to_plot = apply_colormap(image, color_bounds = color_bounds, cmap_name = cmap_name)
+    au.write_png(to_plot, filename)
+    return to_plot
+
+def apply_colormap(image, color_bounds = None, cmap_name = 'algae', func=lambda x: x):
+    r"""Apply a colormap to a floating point image, scaling to uint8.
+
+    This function will scale an image and directly call libpng to write out a
+    colormapped version of that image.  It is designed for rapid-fire saving of
+    image buffers generated using `yt.visualization.api.FixedResolutionBuffers` and the like.
+
+    Parameters
+    ----------
+    image : array_like
+        This is an (unscaled) array of floating point values, shape (N,N,) to
+        save in a PNG file.
+    color_bounds : tuple of floats, optional
+        The min and max to scale between.  Outlying values will be clipped.
+    cmap_name : string, optional
+        An acceptable colormap.  See either yt.visualization.color_maps or
+        http://www.scipy.org/Cookbook/Matplotlib/Show_colormaps .
+    func : function, optional
+        A function to transform the buffer before applying a colormap. 
+
+    Returns
+    -------
+    to_plot : uint8 image with colorbar applied.
+
+    """
+    image = func(image)
     if color_bounds is None:
         mi = na.nanmin(image[~na.isinf(image)])
         ma = na.nanmax(image[~na.isinf(image)])
@@ -180,7 +212,6 @@
     image = (image - color_bounds[0])/(color_bounds[1] - color_bounds[0])
     to_plot = map_to_colors(image, cmap_name)
     to_plot = na.clip(to_plot, 0, 255)
-    au.write_png(to_plot, filename)
     return to_plot
 
 def annotate_image(image, text, xpos, ypos, font_name = "Vera",


--- a/yt/visualization/plot_collection.py	Mon Apr 04 14:09:15 2011 -0400
+++ b/yt/visualization/plot_collection.py	Mon Apr 04 14:09:36 2011 -0400
@@ -28,6 +28,7 @@
 
 from yt.funcs import *
 
+from yt.config import ytcfg
 from yt.data_objects.profiles import \
     BinnedProfile1D, \
     BinnedProfile2D
@@ -155,6 +156,16 @@
             fn.append(plot.save_image(basename, format=format, 
                       override=override, force_save=force_save))
             mylog.info("Saved %s", fn[-1])
+        if ytcfg.getboolean("yt", "__withinreason"):
+            from yt.gui.reason.bottle_mods import PayloadHandler
+            import base64
+            ph = PayloadHandler()
+            for f in fn:
+                if not f.endswith('png'): continue
+                img_data = base64.b64encode(open(f,'rb').read())
+                payload = {'type':'png_string',
+                           'image_data':img_data}
+                ph.add_payload(payload)
         return fn
 
     def set_xlim(self, xmin, xmax):


--- a/yt/visualization/plot_window.py	Mon Apr 04 14:09:15 2011 -0400
+++ b/yt/visualization/plot_window.py	Mon Apr 04 14:09:36 2011 -0400
@@ -23,9 +23,12 @@
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
 """
 
+import tempfile
+import numpy as na
 import color_maps
 from image_writer import \
-    write_image
+    write_image, apply_colormap
+from yt.utilities.amr_utils import write_png_to_file
 from fixed_resolution import \
     FixedResolutionBuffer
 import matplotlib.pyplot
@@ -36,7 +39,8 @@
         args[0]._data_valid = False
         args[0]._plot_valid = False
         args[0]._recreate_frb()
-        args[0]._setup_plots()
+        if args[0]._initfinished:
+            args[0]._setup_plots()
 
     return newfunc
 
@@ -60,12 +64,31 @@
         invalidated as the object is modified.
         
         Data is handled by a FixedResolutionBuffer object.
+
+        Parameters
+        ----------
+        data_source : :class:`yt.data_objects.data_containers.AMRProjBase` or :class:`yt.data_objects.data_containers.AMRSliceBase`
+            This is the source to be pixelized, which can be a projection or a
+            slice.  (For cutting planes, see
+            `yt.visualization.fixed_resolution.ObliqueFixedResolutionBuffer`.)
+        bounds : sequence of floats
+            Bounds are the min and max in the image plane that we want our
+            image to cover.  It's in the order of (xmin, xmax, ymin, ymax),
+            where the coordinates are all in the appropriate code units.
+        buff_size : sequence of ints
+            The size of the image to generate.
+        antialias : boolean
+            This can be true or false.  It determines whether or not sub-pixel
+            rendering is used during data deposition.
+
         """
+        self._initfinished = False
         self.plots = {}
         self.data_source = data_source
         self.buff_size = buff_size
         self.antialias = True
         self.set_window(bounds) # this automatically updates the data and plot
+        self._initfinished = True
 
     def __getitem__(self, item):
         return self.plots[item]
@@ -80,25 +103,14 @@
             raise RuntimeError("Failed to repixelize.")
         self._frb._get_data_source_fields()
         self._data_valid = True
-
+        
     def _setup_plots(self):
-        for f in self.fields:
-            self.plots[f] = YtWindowPlot(self._frb[f])
-        self._plot_valid = True
+        pass
 
     @property
     def fields(self):
         return self._frb.data.keys()
 
-    def save(self,name):
-        for k,v in self.plots.iteritems():
-            n = "%s_%s" % (name, k)
-            v.save(n)
-
-    @invalidate_data
-    def pan(self):
-        pass
-
     @property
     def width(self):
         Wx = self.xlim[1] - self.xlim[0]
@@ -111,8 +123,13 @@
 
     @invalidate_data
     def zoom(self, factor):
-        """
-        This zooms the window by *factor*.
+        r"""This zooms the window by *factor*.
+
+        Parameters
+        ----------
+        factor : float
+            multiplier for the current width
+
         """
         Wx, Wy = self.width
         centerx = self.xlim[0] + Wx*0.5
@@ -120,20 +137,33 @@
         nWx, nWy = Wx/factor, Wy/factor
         self.xlim = (centerx - nWx*0.5, centerx + nWx*0.5)
         self.ylim = (centery - nWy*0.5, centery + nWy*0.5)
-        #self._run_callbacks()
 
     @invalidate_data
     def pan(self, deltas):
-        """
-        This accepts a tuple of *deltas*, composed of (delta_x, delta_y) that
-        will pan the window by those values in absolute coordinates.
+        r"""Pan the image by specifying absolute code unit coordinate deltas.
+        
+        Parameters
+        ----------
+        deltas : sequence of floats
+            (delta_x, delta_y) in *absolute* code unit coordinates
+
         """
         self.xlim = (self.xlim[0] + deltas[0], self.xlim[1] + deltas[0])
         self.ylim = (self.ylim[0] + deltas[1], self.ylim[1] + deltas[1])
 
-    @invalidate_plot
-    def set_cmap(self):
-        pass
+    @invalidate_data
+    def pan_rel(self, deltas):
+        r"""Pan the image by specifying relative deltas, to the FOV.
+        
+        Parameters
+        ----------
+        deltas : sequence of floats
+            (delta_x, delta_y) in *relative* code unit coordinates
+
+        """
+        Wx, Wy = self.width
+        self.xlim = (self.xlim[0] + Wx*deltas[0], self.xlim[1] + Wx*deltas[0])
+        self.ylim = (self.ylim[0] + Wy*deltas[1], self.ylim[1] + Wy*deltas[1])
 
     @invalidate_data
     def set_field(self):
@@ -145,22 +175,139 @@
         self.ylim = bounds[2:]
 
     @invalidate_data
-    def set_width(self):
-        pass
+    def set_width(self, new_width):
+        """set the width of the plot window
+
+        parameters
+        ----------
+        new_width : float
+            the width of the image in code units.
+
+        """
+        Wx, Wy = self.width
+        centerx = self.xlim[0] + Wx*0.5
+        centery = self.ylim[0] + Wy*0.5
+        self.xlim[0] = centerx - new_width/2.
+        self.xlim[1] = centerx + new_width/2.
+        self.ylim[0] = centery - new_width/2.
+        self.ylim[1] = centery + new_width/2.
+
     @property
     def width(self):
         Wx = self.xlim[1] - self.xlim[0]
         Wy = self.ylim[1] - self.ylim[0]
         return (Wx, Wy)
 
-    # @invalidate_plot
-    # def set_zlim(self):
-    #     pass
-
     @invalidate_data
     def set_antialias(self,aa):
         self.antialias = aa
 
+class PWViewer(PlotWindow):
+    """A viewer for PlotWindows.
+
+    """
+    def __init__(self, *args,**kwargs):
+        PlotWindow.__init__(self, *args,**kwargs)
+        self._field_transform = {}
+        for field in self._frb.data.keys():
+            if self._frb.pf.field_info[field].take_log:
+                self._field_transform[field] = na.log
+            else:
+                self._field_transform[field] = lambda x: x
+
+        self._setup_plots()
+
+    def set_log(self,field,log):
+        """set a field to log or linear.
+        
+        Parameters
+        ----------
+        field : string
+            the field to set a transform
+        log : boolean
+            Log on/off.
+
+        """
+        if log:
+            self._field_transform[field] = na.log
+        else:
+            self._field_transform[field] = lambda x: x
+
+    def set_transform(self, field, func):
+        self._field_transform[field] = func
+
+    @invalidate_plot
+    def set_cmap(self):
+        pass
+
+    @invalidate_plot
+    def set_zlim(self):
+        pass
+
+class PWViewerMPL(PWViewer):
+    """Viewer using matplotlib as a backend via the YtWindowPlot. 
+
+    """
+    def _setup_plots(self):
+        for f in self.fields:
+            self.plots[f] = YtWindowPlot(self._frb[f])
+        self._plot_valid = True
+
+    def save(self,name):
+        for k,v in self.plots.iteritems():
+            n = "%s_%s" % (name, k)
+            v.save(n)
+
+class PWViewerRaw(PWViewer):
+    """A PlotWindow viewer that writes raw pngs (no MPL, no axes).
+
+    """
+    def _setup_plots(self):
+        self.save('')
+        self._plot_valid = True
+
+    def save(self,name):
+        for field in self._frb.data.keys():
+            nm = "%s_%s.png" % (name,field)
+            print "writing %s" % nm
+            write_image(self._frb[field],nm)
+
+class PWViewerExtJS(PWViewer):
+    """A viewer for the web interface.
+
+    """
+    _ext_widget_id = None
+    _current_field = None
+    _widget_name = "plot_window"
+    def _setup_plots(self):
+        from yt.gui.reason.bottle_mods import PayloadHandler
+        import base64
+        ph = PayloadHandler()
+        if self._current_field is not None \
+           and self._ext_widget_id is not None:
+            fields = [self._current_field]
+            addl_keys = {'type': 'widget_payload',
+                         'widget_id': self._ext_widget_id}
+        else:
+            fields = self._frb.data.keys()
+            addl_keys = {}
+        for field in fields:
+            tf = tempfile.TemporaryFile()
+            to_plot = apply_colormap(self._frb[field],func = self._field_transform[field])
+            write_png_to_file(to_plot, tf)
+            tf.seek(0)
+            img_data = base64.b64encode(tf.read())
+            tf.close()
+            payload = {'type':'png_string',
+                       'image_data':img_data}
+            payload.update(addl_keys)
+            ph.add_payload(payload)
+
+    def get_metadata(self):
+        pass
+
+
+
 class YtPlot(object):
     """A base class for all yt plots. It should abstract the actual
     plotting engine completely, allowing plotting even without matplotlib.

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