[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