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

Bitbucket commits-noreply at bitbucket.org
Thu Nov 10 08:17:54 PST 2011


2 new commits in yt:


https://bitbucket.org/yt_analysis/yt/changeset/8608363b3d60/
changeset:   8608363b3d60
branch:      yt
user:        MatthewTurk
date:        2011-11-10 17:16:29
summary:     Adding a "pannable map" button to slice and projection view.  Closes #287.
affected #:  6 files

diff -r c038dd1e297af8267633def26899d7c7b2c1d671 -r 8608363b3d609d754c9f27a01e717dccc09b8ba5 yt/gui/reason/bottle_mods.py
--- a/yt/gui/reason/bottle_mods.py
+++ b/yt/gui/reason/bottle_mods.py
@@ -149,6 +149,7 @@
             print "WARNING: %s has no _route_prefix attribute.  Not notifying."
             continue
             w._route_prefix = token
+    repl._global_token = token
     repl.activate()
     repl.execution_thread.wait()
     print


diff -r c038dd1e297af8267633def26899d7c7b2c1d671 -r 8608363b3d609d754c9f27a01e717dccc09b8ba5 yt/gui/reason/extdirect_repl.py
--- a/yt/gui/reason/extdirect_repl.py
+++ b/yt/gui/reason/extdirect_repl.py
@@ -220,6 +220,7 @@
                               _resources = ("/resources/:path#.+#", "GET"),
                               _philogl = ("/philogl/:path#.+#", "GET"),
                               _js = ("/js/:path#.+#", "GET"),
+                              _leaflet = ("/leaflet/:path#.+#", "GET"),
                               _images = ("/images/:path#.+#", "GET"),
                               _theme = ("/theme/:path#.+#", "GET"),
                               _session_py = ("/session.py", "GET"),
@@ -340,6 +341,13 @@
             return
         return open(pp).read()
 
+    def _leaflet(self, path):
+        pp = os.path.join(local_dir, "html", "leaflet", 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):
@@ -515,6 +523,21 @@
                                          'widget_data_name': '_twidget_data'})
 
     @lockit
+    def create_mapview(self, widget_name):
+        # We want multiple maps simultaneously
+        uu = "/%s/%s" % (getattr(self, "_global_token", ""),
+                        str(uuid.uuid1()).replace("-","_"))
+        from .pannable_map import PannableMapServer
+        data = self.locals[widget_name].data_source
+        field_name = self.locals[widget_name]._current_field
+        pm = PannableMapServer(data, field_name, route_prefix = uu)
+        self.locals['_tpm'] = pm
+        self.locals['_twidget_data'] = {'prefix': uu, 'field':field_name}
+        self.execution_thread.queue.put({'type': 'add_widget',
+                                         'name': '_tpm',
+                                         'widget_data_name': '_twidget_data'})
+
+    @lockit
     def create_slice(self, pfname, center, axis, field, onmax):
         if not onmax: 
             center_string = \


diff -r c038dd1e297af8267633def26899d7c7b2c1d671 -r 8608363b3d609d754c9f27a01e717dccc09b8ba5 yt/gui/reason/html/index.html
--- a/yt/gui/reason/html/index.html
+++ b/yt/gui/reason/html/index.html
@@ -80,6 +80,10 @@
          In that case, it will default to whatever is in the family. --><link rel="stylesheet" type="text/css" href="http://fonts.googleapis.com/css?family=Inconsolata">
 
+    <!-- LEAFLET STUFF -->
+    <script type="text/javascript" src="leaflet/leaflet.js"></script>
+    <link rel="stylesheet" href="leaflet/leaflet.css" />
+
     <!-- LIBS --><script type="text/javascript" src="resources/adapter/ext/ext-base.js"></script><script type="text/javascript" src="resources/ext-all.js"></script>
@@ -119,6 +123,9 @@
     <!-- THE GRID VIEWER FUNCTIONS --><script type="text/javascript" src="js/widget_isocontour.js"></script>
 
+    <!-- THE PANNABLE MAP FUNCTIONS -->
+    <script type="text/javascript" src="js/widget_pannablemap.js"></script>
+
     <script id="gv-shader-fs" type="x-shader/x-fragment">
     #ifdef GL_ES
     precision highp float;


diff -r c038dd1e297af8267633def26899d7c7b2c1d671 -r 8608363b3d609d754c9f27a01e717dccc09b8ba5 yt/gui/reason/html/js/widget_pannablemap.js
--- /dev/null
+++ b/yt/gui/reason/html/js/widget_pannablemap.js
@@ -0,0 +1,76 @@
+/**********************************************************************
+The Pannable Map Widget
+
+Author: Matthew Turk <matthewturk at gmail.com>
+Affiliation: Columbia University
+Homepage: http://yt-project.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 WidgetPannableMap = function(python_varname, widget_data) {
+    this.id = python_varname;
+    this.widget_data = widget_data;
+
+    viewport.get("center-panel").add(
+        {
+            xtype: 'panel',
+            id: "pm_" + this.id,
+            title: "Pannable Map",
+            iconCls: 'graph',
+            autoScroll: true,
+            layout:'absolute',
+            closable: true,
+            items: [ 
+                {
+                    xtype:'box',
+                    autoEl: {
+                        tag: 'div',
+                        id: "map_" + this.id,
+                        width: 512,
+                        height: 512,
+                    },
+                    x: 10,
+                    y: 10,
+                    width: 512,
+                    height: 512,
+                    listeners: {afterrender:
+                        function() {
+                          var map = new L.Map('map_' + python_varname, {
+                                  center: new L.LatLng(0.0, 0.0),
+                                  zoom: 0,
+                                  });
+                          var cloudmadeUrl = widget_data['prefix'] + '/map/{z}/{x}/{y}.png';
+                          cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 18});
+                          map.addLayer(cloudmade);
+                    }},
+                }  
+            ]
+        }
+    );
+
+    viewport.get("center-panel").activate("pm_" + this.id);
+    viewport.doLayout();
+    this.panel = viewport.get("center-panel").get("pm_" + this.id);
+    this.panel.doLayout();
+    examine = this.panel;
+
+    this.accept_results = function(payload) { }
+}
+
+widget_types['pannable_map'] = WidgetPannableMap;


diff -r c038dd1e297af8267633def26899d7c7b2c1d671 -r 8608363b3d609d754c9f27a01e717dccc09b8ba5 yt/gui/reason/html/js/widget_plotwindow.js
--- a/yt/gui/reason/html/js/widget_plotwindow.js
+++ b/yt/gui/reason/html/js/widget_plotwindow.js
@@ -331,6 +331,21 @@
                         }); 
                     }
                 },{
+                    xtype: 'button',
+                    text: 'Pannable Map',
+                    x: 10,
+                    y: 335,
+                    width: 80,
+                    tooltip: "Open a pannable map in a new tab",
+                    handler: function(b,e) {
+                        img_data = image_dom.src;
+                        yt_rpc.ExtDirectREPL.create_mapview(
+                            {widget_name:python_varname},
+                        function(rv) {
+                            /*alert(rv);*/
+                        }); 
+                    }
+                },{
                     xtype: 'panel',
                     layout: 'vbox',
                     id: 'rhs_panel_' + python_varname,


diff -r c038dd1e297af8267633def26899d7c7b2c1d671 -r 8608363b3d609d754c9f27a01e717dccc09b8ba5 yt/gui/reason/pannable_map.py
--- a/yt/gui/reason/pannable_map.py
+++ b/yt/gui/reason/pannable_map.py
@@ -47,14 +47,16 @@
     return func
 
 class PannableMapServer(object):
-    def __init__(self, data, field):
+    _widget_name = "pannable_map"
+    def __init__(self, data, field, route_prefix = ""):
         self.data = data
         self.pf = data.pf
         self.field = field
-        bottle.route("/map/:L/:x/:y.png")(self.map)
-        bottle.route("/")(self.index)
-        bottle.route("/index.html")(self.index)
-        bottle.route("/static/:filename#.+#")(self.static)
+        
+        bottle.route("%s/map/:L/:x/:y.png" % route_prefix)(self.map)
+        bottle.route("%s/" % route_prefix)(self.index)
+        bottle.route("%s/index.html" % route_prefix)(self.index)
+        bottle.route("%s/static/:filename#.+#" % route_prefix)(self.static)
         # This is a double-check, since we do not always mandate this for
         # slices:
         self.data[self.field] = self.data[self.field].astype("float64")



https://bitbucket.org/yt_analysis/yt/changeset/f9e18f610a3f/
changeset:   f9e18f610a3f
branch:      yt
user:        MatthewTurk
date:        2011-11-10 17:17:32
summary:     Merging
affected #:  6 files

diff -r 8608363b3d609d754c9f27a01e717dccc09b8ba5 -r f9e18f610a3ff3fa3eb5463277aad44ca6b2a8d4 tests/object_field_values.py
--- a/tests/object_field_values.py
+++ b/tests/object_field_values.py
@@ -73,12 +73,38 @@
         YTStaticOutputTest.setup(self)
         known_objects[self.object_name](self)
 
+class YTExtractIsocontoursTest(YTFieldValuesTest):
+    def run(self):
+        val = self.data_object.quantities["WeightedAverageQuantity"](
+            "Density", "Density")
+        triangles = self.data_object.extract_isocontours("Density",
+            val, rescale = False, sample_values = "Temperature")
+        self.result = triangles
+
+    def compare(self, old_result):
+        self.compare_array_delta(self.result, old_result, 1e-7)
+
+class YTIsocontourFluxTest(YTFieldValuesTest):
+    def run(self):
+        val = self.data_object.quantities["WeightedAverageQuantity"](
+            "Density", "Density")
+        flux = self.data_object.calculate_isocontour_flux(
+           "Density", val, "x-velocity", "y-velocity", "z-velocity")
+        self.result = flux
+
+    def compare(self, old_result):
+        self.compare_value_delta(self.result, old_result, 1e-7)
+
 for object_name in known_objects:
     for field in field_list + particle_field_list:
         if "cut_region" in object_name and field in particle_field_list:
             continue
         create_test(YTFieldValuesTest, "%s_%s" % (object_name, field),
                     field = field, object_name = object_name)
+    create_test(YTExtractIsocontoursTest, "%s" % (object_name),
+                object_name = object_name)
+    create_test(YTIsocontourFluxTest, "%s" % (object_name),
+                object_name = object_name)
     
 class YTDerivedQuantityTest(YTStaticOutputTest):
     def setup(self):
@@ -140,4 +166,3 @@
                     "%s_%s" % (object_name, field),
                     field_name = field, 
                     object_name = object_name)
-


diff -r 8608363b3d609d754c9f27a01e717dccc09b8ba5 -r f9e18f610a3ff3fa3eb5463277aad44ca6b2a8d4 yt/data_objects/data_containers.py
--- a/yt/data_objects/data_containers.py
+++ b/yt/data_objects/data_containers.py
@@ -2266,7 +2266,7 @@
             grid.child_mask, self.domain_width, dls[grid.Level],
             self.axis)
 
-class AMR3DData(AMRData, GridPropertiesMixin):
+class AMR3DData(AMRData, GridPropertiesMixin, ParallelAnalysisInterface):
     _key_fields = ['x','y','z','dx','dy','dz']
     """
     Class describing a cluster of data points, not necessarily sharing any
@@ -2280,6 +2280,7 @@
         used as a base class.  Note that *center* is supplied, but only used
         for fields and quantities that require it.
         """
+        ParallelAnalysisInterface.__init__(self)
         AMRData.__init__(self, pf, fields, **kwargs)
         self._set_center(center)
         self.coords = None
@@ -2364,11 +2365,14 @@
             f = grid[field]
             return na.array([f[i,:][pointI] for i in range(3)])
         else:
+            tr = grid[field]
+            if tr.size == 1: # dx, dy, dz, cellvolume
+                tr = tr * na.ones(grid.ActiveDimensions, dtype='float64')
+            if len(grid.Children) == 0 and grid.OverlappingSiblings is None \
+                and self._is_fully_enclosed(grid):
+                return tr.ravel()
             pointI = self._get_point_indices(grid)
-            if grid[field].size == 1: # dx, dy, dz, cellvolume
-                t = grid[field] * na.ones(grid.ActiveDimensions, dtype='float64')
-                return t[pointI].ravel()
-            return grid[field][pointI].ravel()
+            return tr[pointI].ravel()
 
     def _flush_data_to_grids(self, field, default_val, dtype='float32'):
         """
@@ -2479,12 +2483,19 @@
             format.  Suitable for loading into meshlab.
         rescale : bool, optional
             If true, the vertices will be rescaled within their min/max.
+        sample_values : string, optional
+            Any field whose value should be extracted at the center of each
+            triangle.
 
         Returns
         -------
         verts : array of floats
             The array of vertices, x,y,z.  Taken in threes, these are the
             triangle vertices.
+        samples : array of floats
+            If `sample_values` is specified, this will be returned and will
+            contain the values of the field specified at the center of each
+            triangle.
 
         References
         ----------
@@ -2504,9 +2515,7 @@
         """
         verts = []
         samples = []
-        pb = get_pbar("Extracting Isocontours", len(self._grids))
-        for i, g in enumerate(self._grids):
-            pb.update(i)
+        for i, g in enumerate(self._get_grid_objs()):
             mask = self._get_cut_mask(g) * g.child_mask
             vals = g.get_vertex_centered_data(field)
             if sample_values is not None:
@@ -2519,20 +2528,24 @@
                 my_verts, svals = my_verts
                 samples.append(svals)
             verts.append(my_verts)
-        pb.finish()
-        verts = na.concatenate(verts)
+        verts = na.concatenate(verts).transpose()
+        verts = self.comm.par_combine_object(verts, op='cat', datatype='array')
+        verts = verts.transpose()
         if sample_values is not None:
             samples = na.concatenate(samples)
+            samples = self.comm.par_combine_object(samples, op='cat',
+                                datatype='array')
         if rescale:
             mi = na.min(verts, axis=0)
             ma = na.max(verts, axis=0)
             verts = (verts - mi) / (ma - mi).max()
-        if filename is not None:
+        if filename is not None and self.comm.rank == 0:
             f = open(filename, "w")
             for v1 in verts:
                 f.write("v %0.16e %0.16e %0.16e\n" % (v1[0], v1[1], v1[2]))
             for i in range(len(verts)/3):
                 f.write("f %s %s %s\n" % (i*3+1, i*3+2, i*3+3))
+            f.close()
         if sample_values is not None:
             return verts, samples
         return verts
@@ -2602,7 +2615,7 @@
         ...     "x-velocity", "y-velocity", "z-velocity", "Metal_Density")
         """
         flux = 0.0
-        for g in self._grids:
+        for g in self._get_grid_objs():
             mask = self._get_cut_mask(g) * g.child_mask
             vals = g.get_vertex_centered_data(field)
             if fluxing_field is None:
@@ -2613,6 +2626,7 @@
                          [field_x, field_y, field_z]]
             flux += march_cubes_grid_flux(value, vals, xv, yv, zv,
                         ff, mask, g.LeftEdge, g.dds)
+        flux = self.comm.mpi_allreduce(flux, op="sum")
         return flux
 
     def extract_connected_sets(self, field, num_levels, min_val, max_val,


diff -r 8608363b3d609d754c9f27a01e717dccc09b8ba5 -r f9e18f610a3ff3fa3eb5463277aad44ca6b2a8d4 yt/frontends/nyx/fields.py
--- a/yt/frontends/nyx/fields.py
+++ b/yt/frontends/nyx/fields.py
@@ -46,13 +46,13 @@
 # Density
 add_field("density", function=lambda a, b: None, take_log=True,
           validators=[ValidateDataField("density")],
-          units=r"\rm{g}} / \rm{cm}^3",
-          projected_units =r"\rm{g}} / \rm{cm}^2")
-nyx_fields["density"]._projected_units =r"\rm{g}} / \rm{cm}^2"
+          units=r"\rm{g} / \rm{cm}^3",
+          projected_units =r"\rm{g} / \rm{cm}^2")
+nyx_fields["density"]._projected_units =r"\rm{g} / \rm{cm}^2"
 
 add_field("Density", function=lambda a, b: b["density"], take_log=True,
-          units=r"\rm{g}} / \rm{cm}^3",
-          projected_units =r"\rm{g}} / \rm{cm}^2")
+          units=r"\rm{g} / \rm{cm}^3",
+          projected_units =r"\rm{g} / \rm{cm}^2")
 
 # Particle mass in units of $ M_{\odot}
 def _convertParticleMassMsun(data):
@@ -64,8 +64,8 @@
           particle_type=True, convert_function=_convertParticleMassMsun, take_log=True, units=r"\rm{M_{\odot}}")
           
 add_field("Dark_Matter_Density", function=lambda a, b: b["particle_mass_density"], take_log=True,
-          units=r"\rm{g}} / \rm{cm}^3",particle_type=True,
-          projected_units =r"\rm{g}} / \rm{cm}^2")
+          units=r"\rm{g} / \rm{cm}^3",particle_type=True,
+          projected_units =r"\rm{g} / \rm{cm}^2")
 
 
 # Energy Density


diff -r 8608363b3d609d754c9f27a01e717dccc09b8ba5 -r f9e18f610a3ff3fa3eb5463277aad44ca6b2a8d4 yt/frontends/nyx/io.py
--- a/yt/frontends/nyx/io.py
+++ b/yt/frontends/nyx/io.py
@@ -28,7 +28,7 @@
 
 import os
 import numpy as na
-from yt.utilities.amr_utils import read_castro_particles
+from yt.utilities.amr_utils import read_castro_particles, read_and_seek
 from yt.utilities.io_handler import BaseIOHandler
 
 from definitions import fab_header_pattern, nyx_particle_field_names, \
@@ -57,80 +57,24 @@
         if field in nyx_particle_field_names:
             return self._read_particle_field(grid, field)
         filen = os.path.expanduser(grid.filename[field])
-        off = grid._offset[field]
-        inFile = open(filen, 'rb')
-        inFile.seek(off)
-        header = inFile.readline()
-        header.strip()
-
-        """
-        if grid._paranoid:
-            mylog.warn("Castro Native reader: Paranoid read mode.")
-            header_re = re.compile(fab_header_pattern)
-            bytesPerReal, endian, start, stop, centerType, nComponents = \
-                headerRe.search(header).groups()
-
-            # we will build up a dtype string, starting with endian.
-            # @todo: this code is ugly.
-            bytesPerReal = int(bytesPerReal)
-            if bytesPerReal == int(endian[0]):
-                dtype = '<'
-            elif bytesPerReal == int(endian[-1]):
-                dtype = '>'
-            else:
-                raise ValueError("FAB header is neither big nor little endian. Perhaps the file is corrupt?")
-
-            dtype += ('f%i' % bytesPerReal)  # always a floating point
-
-            # determine size of FAB
-            start = na.array(map(int, start.split(',')))
-            stop = na.array(map(int, stop.split(',')))
-
-            gridSize = stop - start + 1
-
-            error_count = 0
-            if (start != grid.start).any():
-                print "Paranoia Error: Cell_H and %s do not agree on grid start." % grid.filename
-                error_count += 1
-            if (stop != grid.stop).any():
-                print "Paranoia Error: Cell_H and %s do not agree on grid stop." % grid.filename
-                error_count += 1
-            if (gridSize != grid.ActiveDimensions).any():
-                print "Paranoia Error: Cell_H and %s do not agree on grid dimensions." % grid.filename
-                error_count += 1
-            if bytesPerReal != grid.hierarchy._bytesPerReal:
-                print "Paranoia Error: Cell_H and %s do not agree on bytes per real number." % grid.filename
-                error_count += 1
-            if (bytesPerReal == grid.hierarchy._bytesPerReal and dtype != grid.hierarchy._dtype):
-                print "Paranoia Error: Cell_H and %s do not agree on endianness." % grid.filename
-                error_count += 1
-
-            if error_count > 0:
-                raise RunTimeError("Paranoia unveiled %i differences between Cell_H and %s." % (error_count, grid.filename))
-        else:
-        """
-        start = grid.start_index
-        stop = grid.stop_index
-        dtype = grid.hierarchy._dtype
+        offset1 = grid._offset[field]
+        # one field has nElements * bytesPerReal bytes and is located
+        # nElements * bytesPerReal * field_index from the offset location
         bytesPerReal = grid.hierarchy._bytesPerReal
 
+        fieldname = yt_to_nyx_fields_dict.get(field, field)
+        field_index = grid.field_indexes[fieldname]
         nElements = grid.ActiveDimensions.prod()
+        offset2 = int(nElements*bytesPerReal*field_index)
 
-        # one field has nElements * bytesPerReal bytes and is located
-        # nElements * bytesPerReal * field_index from the offset location
-        if yt_to_nyx_fields_dict.has_key(field):
-            fieldname = yt_to_nyx_fields_dict[field]
-        else:
-            fieldname = field
-        field_index = grid.field_indexes[fieldname]
-        inFile.seek(int(nElements*bytesPerReal*field_index),1)
-        field = na.fromfile(inFile, count=nElements, dtype=dtype)
-        field = field.reshape(grid.ActiveDimensions[::-1]).swapaxes(0,2)
+        dtype = grid.hierarchy._dtype
+        field = na.empty(nElements, dtype=grid.hierarchy._dtype)
+        read_and_seek(filen, offset1, offset2, field, nElements * bytesPerReal)
+        field = field.reshape(grid.ActiveDimensions, order='F')
 
         # @todo: we can/should also check against the max and min in the header
         # file
 
-        inFile.close()
         return field
 
     def _read_data_slice(self, grid, field, axis, coord):


diff -r 8608363b3d609d754c9f27a01e717dccc09b8ba5 -r f9e18f610a3ff3fa3eb5463277aad44ca6b2a8d4 yt/frontends/orion/io.py
--- a/yt/frontends/orion/io.py
+++ b/yt/frontends/orion/io.py
@@ -111,7 +111,7 @@
         field_index = grid.field_indexes[fieldname]
         inFile.seek(int(nElements*bytesPerReal*field_index),1)
         field = na.fromfile(inFile,count=nElements,dtype=dtype)
-        field = field.reshape(grid.ActiveDimensions[::-1]).swapaxes(0,2)
+        field = field.reshape(grid.ActiveDimensions, order='F')
 
         # we can/should also check against the max and min in the header file
 


diff -r 8608363b3d609d754c9f27a01e717dccc09b8ba5 -r f9e18f610a3ff3fa3eb5463277aad44ca6b2a8d4 yt/utilities/_amr_utils/fortran_reader.pyx
--- a/yt/utilities/_amr_utils/fortran_reader.pyx
+++ b/yt/utilities/_amr_utils/fortran_reader.pyx
@@ -28,6 +28,7 @@
 cimport cython
 
 from stdio cimport fopen, fclose, FILE
+cimport libc.stdlib as stdlib
 
 #cdef inline int imax(int i0, int i1):
     #if i0 > i1: return i0
@@ -48,6 +49,21 @@
     int fseek(FILE *stream, long offset, int whence)
     size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
     long ftell(FILE *stream)
+    char *fgets(char *s, int size, FILE *stream)
+
+ at cython.boundscheck(False)
+ at cython.wraparound(False)
+def read_and_seek(char *filename, int offset1, int offset2,
+                  np.ndarray buffer, int bytes):
+    cdef FILE *f = fopen(filename, "rb")
+    cdef void *buf = <void *> buffer.data
+    cdef char line[1024]
+    cdef size_t n = 1023
+    fseek(f, offset1, SEEK_SET)
+    fgets(line, n, f)
+    fseek(f, offset2, SEEK_CUR)
+    fread(buf, 1, bytes, f)
+    fclose(f)
 
 @cython.boundscheck(False)
 @cython.wraparound(False)

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