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

Bitbucket commits-noreply at bitbucket.org
Thu Jun 21 02:07:36 PDT 2012


4 new commits in yt:


https://bitbucket.org/yt_analysis/yt/changeset/e2ad5b1b3f91/
changeset:   e2ad5b1b3f91
branch:      yt
user:        Andrew Myers
date:        2012-03-10 02:22:19
summary:     converting the basic Orion fields to the 'known' rather than 'unknown' type, and fixing unit labels.
affected #:  1 file

diff -r afd860cd46d8dfca26bf7aa7a33049b654cd79c5 -r e2ad5b1b3f91c303820920b50bcf26fae573e76c yt/frontends/orion/fields.py
--- a/yt/frontends/orion/fields.py
+++ b/yt/frontends/orion/fields.py
@@ -44,26 +44,26 @@
 OrionFieldInfo = FieldInfoContainer.create_with_fallback(FieldInfo)
 add_field = OrionFieldInfo.add_field
 
-add_field("density", function=lambda a,b: None, take_log=True,
-          validators = [ValidateDataField("density")],
-          units=r"\rm{g}/\rm{cm}^3")
-OrionFieldInfo["density"]._projected_units =r"\rm{g}/\rm{cm}^2"
+add_orion_field("density", function=lambda a,b: None, take_log=True,
+                validators = [ValidateDataField("density")],
+                units=r"\rm{g}/\rm{cm}^3")
+KnownOrionFields["density"]._projected_units =r"\rm{g}/\rm{cm}^2"
 
-add_field("eden", function=lambda a,b: None, take_log=True,
-          validators = [ValidateDataField("eden")],
-          units=r"\rm{erg}/\rm{cm}^3")
+add_orion_field("eden", function=lambda a,b: None, take_log=True,
+                validators = [ValidateDataField("eden")],
+                units=r"\rm{erg}/\rm{cm}^3")
 
-add_field("xmom", function=lambda a,b: None, take_log=False,
-          validators = [ValidateDataField("xmom")],
-          units=r"\rm{g}/\rm{cm^2\ s}")
+add_orion_field("xmom", function=lambda a,b: None, take_log=False,
+                validators = [ValidateDataField("xmom")],
+                units=r"\rm{g}/\rm{cm^2\ s}")
 
-add_field("ymom", function=lambda a,b: None, take_log=False,
-          validators = [ValidateDataField("ymom")],
-          units=r"\rm{gm}/\rm{cm^2\ s}")
+add_orion_field("ymom", function=lambda a,b: None, take_log=False,
+                validators = [ValidateDataField("ymom")],
+                units=r"\rm{gm}/\rm{cm^2\ s}")
 
-add_field("zmom", function=lambda a,b: None, take_log=False,
-          validators = [ValidateDataField("zmom")],
-          units=r"\rm{g}/\rm{cm^2\ s}")
+add_orion_field("zmom", function=lambda a,b: None, take_log=False,
+                validators = [ValidateDataField("zmom")],
+                units=r"\rm{g}/\rm{cm^2\ s}")
 
 translation_dict = {"x-velocity": "xvel",
                     "y-velocity": "yvel",
@@ -88,11 +88,11 @@
 
 def _xVelocity(field, data):
     """generate x-velocity from x-momentum and density
-
+    
     """
     return data["xmom"]/data["density"]
-add_field("x-velocity",function=_xVelocity, take_log=False,
-          units=r'\rm{cm}/\rm{s}')
+add_orion_field("x-velocity",function=_xVelocity, take_log=False,
+                units=r'\rm{cm}/\rm{s}')
 
 def _yVelocity(field,data):
     """generate y-velocity from y-momentum and density
@@ -102,16 +102,16 @@
     #    return data["xvel"]
     #except KeyError:
     return data["ymom"]/data["density"]
-add_field("y-velocity",function=_yVelocity, take_log=False,
-          units=r'\rm{cm}/\rm{s}')
+add_orion_field("y-velocity",function=_yVelocity, take_log=False,
+                units=r'\rm{cm}/\rm{s}')
 
 def _zVelocity(field,data):
     """generate z-velocity from z-momentum and density
-
+    
     """
     return data["zmom"]/data["density"]
-add_field("z-velocity",function=_zVelocity, take_log=False,
-          units=r'\rm{cm}/\rm{s}')
+add_orion_field("z-velocity",function=_zVelocity, take_log=False,
+                units=r'\rm{cm}/\rm{s}')
 
 def _ThermalEnergy(field, data):
     """generate thermal (gas energy). Dual Energy Formalism was
@@ -125,19 +125,19 @@
         data["x-velocity"]**2.0
         + data["y-velocity"]**2.0
         + data["z-velocity"]**2.0 )
-add_field("ThermalEnergy", function=_ThermalEnergy,
-          units=r"\rm{ergs}/\rm{cm^3}")
+add_orion_field("ThermalEnergy", function=_ThermalEnergy,
+                units=r"\rm{ergs}/\rm{cm^3}")
 
 def _Pressure(field,data):
     """M{(Gamma-1.0)*e, where e is thermal energy density
        NB: this will need to be modified for radiation
     """
     return (data.pf["Gamma"] - 1.0)*data["ThermalEnergy"]
-add_field("Pressure", function=_Pressure, units=r"\rm{dyne}/\rm{cm}^{2}")
+add_orion_field("Pressure", function=_Pressure, units=r"\rm{dyne}/\rm{cm}^{2}")
 
 def _Temperature(field,data):
     return (data.pf["Gamma"]-1.0)*data.pf["mu"]*mh*data["ThermalEnergy"]/(kboltz*data["Density"])
-add_field("Temperature",function=_Temperature,units=r"\rm{Kelvin}",take_log=False)
+add_orion_field("Temperature",function=_Temperature,units=r"\rm{Kelvin}",take_log=False)
 
 # particle fields
 
@@ -170,6 +170,6 @@
 
 for pf in _particle_field_list:
     pfunc = particle_func("particle_%s" % (pf))
-    add_field("particle_%s" % pf, function=pfunc,
-              validators = [ValidateSpatial(0)],
-              particle_type=True)
+    add_orion_field("particle_%s" % pf, function=pfunc,
+                    validators = [ValidateSpatial(0)],
+                    particle_type=True)



https://bitbucket.org/yt_analysis/yt/changeset/362cdeac10f8/
changeset:   362cdeac10f8
branch:      yt
user:        Andrew Myers
date:        2012-03-10 02:32:00
summary:     fixing the out-of-date chombo reader in the main dev branch
affected #:  3 files

diff -r e2ad5b1b3f91c303820920b50bcf26fae573e76c -r 362cdeac10f8b435de5bccc999c6aa5503333951 yt/frontends/chombo/data_structures.py
--- a/yt/frontends/chombo/data_structures.py
+++ b/yt/frontends/chombo/data_structures.py
@@ -68,10 +68,26 @@
         self.Parent = []
         self.Children = []
         self.Level = level
-        self.start_index = start.copy()#.transpose()
-        self.stop_index = stop.copy()#.transpose()
         self.ActiveDimensions = stop - start + 1
 
+    def get_global_startindex(self):
+        """
+        Return the integer starting index for each dimension at the current
+        level.
+        
+        """
+        if self.start_index != None:
+            return self.start_index
+        if self.Parent == []:
+            iLE = self.LeftEdge - self.pf.domain_left_edge
+            start_index = iLE / self.dds
+            return na.rint(start_index).astype('int64').ravel()
+        pdx = self.Parent[0].dds
+        start_index = (self.Parent[0].get_global_startindex()) + \
+            na.rint((self.LeftEdge - self.Parent[0].LeftEdge)/pdx)
+        self.start_index = (start_index*self.pf.refine_by).astype('int64').ravel()
+        return self.start_index
+
     def _setup_dx(self):
         # So first we figure out what the index is.  We don't assume
         # that dx=dy=dz , at least here.  We probably do elsewhere.
@@ -91,8 +107,8 @@
     grid = ChomboGrid
     
     def __init__(self,pf,data_style='chombo_hdf5'):
-        self.domain_left_edge = pf.domain_left_edge # need these to determine absolute grid locations
-        self.domain_right_edge = pf.domain_right_edge # need these to determine absolute grid locations
+        self.domain_left_edge = pf.domain_left_edge
+        self.domain_right_edge = pf.domain_right_edge
         self.data_style = data_style
         self.field_indexes = {}
         self.parameter_file = weakref.proxy(pf)
@@ -100,12 +116,11 @@
         self.hierarchy_filename = self.parameter_file.parameter_filename
         self.hierarchy = os.path.abspath(self.hierarchy_filename)
         self.directory = os.path.dirname(self.hierarchy_filename)
-        self._fhandle = h5py.File(self.hierarchy_filename)
+        self._fhandle = h5py.File(self.hierarchy_filename, 'r')
 
         self.float_type = self._fhandle['/level_0']['data:datatype=0'].dtype.name
-        self._levels = [fn for fn in self._fhandle if fn != "Chombo_global"]
+        self._levels = self._fhandle.keys()[1:]
         AMRHierarchy.__init__(self,pf,data_style)
-
         self._fhandle.close()
 
     def _initialize_data_storage(self):
@@ -113,7 +128,7 @@
 
     def _detect_fields(self):
         ncomp = int(self._fhandle['/'].attrs['num_components'])
-        self.field_list = [c[1] for c in self._fhandle['/'].attrs.listitems()[-ncomp:]]
+        self.field_list = [c[1] for c in self._fhandle['/'].attrs.items()[-ncomp:]]
     
     def _setup_classes(self):
         dd = self._get_data_reader_dict()
@@ -130,8 +145,8 @@
         
         # this relies on the first Group in the H5 file being
         # 'Chombo_global'
-        levels = [fn for fn in f if fn != "Chombo_global"]
-        self.grids = []
+        levels = f.keys()[1:]
+        grids = []
         i = 0
         for lev in levels:
             level_number = int(re.match('level_(\d+)',lev).groups()[0])
@@ -140,17 +155,18 @@
             for level_id, box in enumerate(boxes):
                 si = na.array([box['lo_%s' % ax] for ax in 'ijk'])
                 ei = na.array([box['hi_%s' % ax] for ax in 'ijk'])
-                pg = self.grid(len(self.grids),self,level=level_number,
+                pg = self.grid(len(grids),self,level=level_number,
                                start = si, stop = ei)
-                self.grids.append(pg)
-                self.grids[-1]._level_id = level_id
-                self.grid_left_edge[i] = dx*si.astype(self.float_type) + self.domain_left_edge
-                self.grid_right_edge[i] = dx*(ei.astype(self.float_type)+1) + self.domain_left_edge
+                grids.append(pg)
+                grids[-1]._level_id = level_id
+                self.grid_left_edge[i] = dx*si.astype(self.float_type)
+                self.grid_right_edge[i] = dx*(ei.astype(self.float_type)+1)
                 self.grid_particle_count[i] = 0
                 self.grid_dimensions[i] = ei - si + 1
                 i += 1
         self.grids = na.empty(len(grids), dtype='object')
         for gi, g in enumerate(grids): self.grids[gi] = g
+#        self.grids = na.array(self.grids, dtype='object')
 
     def _populate_grid_objects(self):
         for g in self.grids:
@@ -179,8 +195,8 @@
     
     def __init__(self, filename, data_style='chombo_hdf5',
                  storage_filename = None, ini_filename = None):
-        # hardcoded for now 
-        self.current_time = 0.0
+        fileh = h5py.File(filename,'r')
+        self.current_time = fileh.attrs['time']
         self.ini_filename = ini_filename
         StaticOutput.__init__(self,filename,data_style)
         self.storage_filename = storage_filename
@@ -230,15 +246,19 @@
         """
         if os.path.isfile('pluto.ini'):
             self._parse_pluto_file('pluto.ini')
-        elif os.path.isfile('orion2.ini'):
-            self._parse_pluto_file('orion2.ini')
         else:
+            if os.path.isfile('orion2.ini'): self._parse_pluto_file('orion2.ini')
             self.unique_identifier = \
-                                   int(os.stat(self.parameter_filename)[ST_CTIME])
-            self.domain_left_edge = na.array([0.,0.,0.])
+                int(os.stat(self.parameter_filename)[ST_CTIME])
+            self.domain_left_edge = self.__calc_left_edge()
             self.domain_right_edge = self.__calc_right_edge()
+            self.domain_dimensions = self.__calc_domain_dimensions()
             self.dimensionality = 3
-            self.refine_by = 2
+#            self.refine_by = []
+            fileh = h5py.File(self.parameter_filename,'r')
+#            for level in range(0,fileh.attrs['max_level']):
+#                self.refine_by.append(fileh['/level_'+str(level)].attrs['ref_ratio'])
+            self.refine_by = fileh['/level_0'].attrs['ref_ratio']
 
     def _parse_pluto_file(self, ini_filename):
         """
@@ -268,36 +288,26 @@
                     else:
                         self.parameters[paramName] = t
 
-            # assumes 3D for now
-            elif param.startswith("X1-grid"):
-                t = vals.split()
-                low1 = float(t[1])
-                high1 = float(t[4])
-                N1 = int(t[2])
-            elif param.startswith("X2-grid"):
-                t = vals.split()
-                low2 = float(t[1])
-                high2 = float(t[4])
-                N2 = int(t[2])
-            elif param.startswith("X3-grid"):
-                t = vals.split()
-                low3 = float(t[1])
-                high3 = float(t[4])
-                N3 = int(t[2])
+    def __calc_left_edge(self):
+        fileh = h5py.File(self.parameter_filename,'r')
+        dx0 = fileh['/level_0'].attrs['dx']
+        LE = dx0*((na.array(list(fileh['/level_0'].attrs['prob_domain'])))[0:3])
+        fileh.close()
+        return LE
 
-        self.dimensionality = 3
-        self.domain_left_edge = na.array([low1,low2,low3])
-        self.domain_right_edge = na.array([high1,high2,high3])
-        self.domain_dimensions = na.array([N1,N2,N3])
-        self.refine_by = self.parameters["RefineBy"]
-            
     def __calc_right_edge(self):
         fileh = h5py.File(self.parameter_filename,'r')
         dx0 = fileh['/level_0'].attrs['dx']
-        RE = dx0*((na.array(fileh['/level_0'].attrs['prob_domain']))[3:] + 1)
+        RE = dx0*((na.array(list(fileh['/level_0'].attrs['prob_domain'])))[3:] + 1)
         fileh.close()
         return RE
-                   
+                  
+    def __calc_domain_dimensions(self):
+        fileh = h5py.File(self.parameter_filename,'r')
+        L_index = ((na.array(list(fileh['/level_0'].attrs['prob_domain'])))[0:3])
+        R_index = ((na.array(list(fileh['/level_0'].attrs['prob_domain'])))[3:] + 1)
+        return R_index - L_index
+ 
     @classmethod
     def _is_valid(self, *args, **kwargs):
         try:
@@ -309,7 +319,6 @@
             pass
         return False
 
-
     @parallel_root_only
     def print_key_parameters(self):
         for a in ["current_time", "domain_dimensions", "domain_left_edge",


diff -r e2ad5b1b3f91c303820920b50bcf26fae573e76c -r 362cdeac10f8b435de5bccc999c6aa5503333951 yt/frontends/chombo/fields.py
--- a/yt/frontends/chombo/fields.py
+++ b/yt/frontends/chombo/fields.py
@@ -38,45 +38,64 @@
 add_chombo_field = KnownChomboFields.add_field
 
 ChomboFieldInfo = FieldInfoContainer.create_with_fallback(FieldInfo)
-add_chombo_field = ChomboFieldInfo.add_field
+add_field = ChomboFieldInfo.add_field
 
-add_field = add_chombo_field
+add_chombo_field("density", function=NullFunc, take_log=True,
+                 validators = [ValidateDataField("density")],
+                 units=r"\rm{g}/\rm{cm}^3")
 
-add_field("density", function=NullFunc, take_log=True,
-          validators = [ValidateDataField("density")],
-          units=r"\rm{g}/\rm{cm}^3")
+KnownChomboFields["density"]._projected_units =r"\rm{g}/\rm{cm}^2"
 
-ChomboFieldInfo["density"]._projected_units =r"\rm{g}/\rm{cm}^2"
+add_chombo_field("X-momentum", function=NullFunc, take_log=False,
+                 validators = [ValidateDataField("X-Momentum")],
+                 units=r"",display_name=r"M_x")
+KnownChomboFields["X-momentum"]._projected_units=r""
 
-add_field("X-momentum", function=NullFunc, take_log=False,
-          validators = [ValidateDataField("X-Momentum")],
-          units=r"",display_name=r"B_x")
-ChomboFieldInfo["X-momentum"]._projected_units=r""
+add_chombo_field("Y-momentum", function=NullFunc, take_log=False,
+                 validators = [ValidateDataField("Y-Momentum")],
+                 units=r"",display_name=r"M_y")
+KnownChomboFields["Y-momentum"]._projected_units=r""
 
-add_field("Y-momentum", function=NullFunc, take_log=False,
-          validators = [ValidateDataField("Y-Momentum")],
-          units=r"",display_name=r"B_y")
-ChomboFieldInfo["Y-momentum"]._projected_units=r""
+add_chombo_field("Z-momentum", function=NullFunc, take_log=False,
+                 validators = [ValidateDataField("Z-Momentum")],
+                 units=r"",display_name=r"M_z")
+KnownChomboFields["Z-momentum"]._projected_units=r""
 
-add_field("Z-momentum", function=NullFunc, take_log=False,
-          validators = [ValidateDataField("Z-Momentum")],
-          units=r"",display_name=r"B_z")
-ChomboFieldInfo["Z-momentum"]._projected_units=r""
+add_chombo_field("X-magnfield", function=NullFunc, take_log=False,
+                 validators = [ValidateDataField("X-Magnfield")],
+                 units=r"",display_name=r"B_x")
+KnownChomboFields["X-magnfield"]._projected_units=r""
 
-add_field("X-magnfield", function=NullFunc, take_log=False,
-          validators = [ValidateDataField("X-Magnfield")],
-          units=r"",display_name=r"B_x")
-ChomboFieldInfo["X-magnfield"]._projected_units=r""
+add_chombo_field("Y-magnfield", function=NullFunc, take_log=False,
+                 validators = [ValidateDataField("Y-Magnfield")],
+                 units=r"",display_name=r"B_y")
+KnownChomboFields["Y-magnfield"]._projected_units=r""
 
-add_field("Y-magnfield", function=NullFunc, take_log=False,
-          validators = [ValidateDataField("Y-Magnfield")],
-          units=r"",display_name=r"B_y")
-ChomboFieldInfo["Y-magnfield"]._projected_units=r""
+add_chombo_field("Z-magnfield", function=NullFunc, take_log=False,
+                  validators = [ValidateDataField("Z-Magnfield")],
+                  units=r"",display_name=r"B_z")
+KnownChomboFields["Z-magnfield"]._projected_units=r""
 
-add_field("Z-magnfield", function=NullFunc, take_log=False,
-          validators = [ValidateDataField("Z-Magnfield")],
-          units=r"",display_name=r"B_z")
-ChomboFieldInfo["Z-magnfield"]._projected_units=r""
+add_chombo_field("energy-density", function=lambda a,b: None, take_log=True,
+                 validators = [ValidateDataField("energy-density")],
+                 units=r"\rm{erg}/\rm{cm}^3")
+KnownChomboFields["energy-density"]._projected_units =r""
+
+add_chombo_field("radiation-energy-density", function=lambda a,b: None, take_log=True,
+                 validators = [ValidateDataField("radiation-energy-density")],
+                 units=r"\rm{erg}/\rm{cm}^3")
+KnownChomboFields["radiation-energy-density"]._projected_units =r""
+
+def _Density(field,data):
+    """A duplicate of the density field. This is needed because when you try 
+    to instantiate a PlotCollection without passing in a center, the code
+    will try to generate one for you using the "Density" field, which gives an error 
+    if it isn't defined.
+
+    """
+    return data["density"]
+add_field("Density",function=_Density, take_log=True,
+          units=r'\rm{g}/\rm{cm^3}')
 
 def _MagneticEnergy(field,data):
     return (data["X-magnfield"]**2 +


diff -r e2ad5b1b3f91c303820920b50bcf26fae573e76c -r 362cdeac10f8b435de5bccc999c6aa5503333951 yt/frontends/chombo/io.py
--- a/yt/frontends/chombo/io.py
+++ b/yt/frontends/chombo/io.py
@@ -36,7 +36,7 @@
 
     def _field_dict(self,fhandle):
         ncomp = int(fhandle['/'].attrs['num_components'])
-        temp =  fhandle['/'].attrs.listitems()[-ncomp:]
+        temp =  fhandle['/'].attrs.items()[-ncomp:]
         val, keys = zip(*temp)
         val = [int(re.match('component_(\d+)',v).groups()[0]) for v in val]
         return dict(zip(keys,val))
@@ -45,7 +45,7 @@
         fhandle = h5py.File(grid.filename,'r')
         ncomp = int(fhandle['/'].attrs['num_components'])
 
-        fns = [c[1] for c in f['/'].attrs.listitems()[-ncomp:]]
+        fns = [c[1] for c in f['/'].attrs.items()[-ncomp-1:-1]]
         fhandle.close()
     
     def _read_data_set(self,grid,field):
@@ -64,7 +64,6 @@
 
         fhandle.close()
         return data.reshape(dims, order='F')
-                                          
 
     def _read_data_slice(self, grid, field, axis, coord):
         sl = [slice(None), slice(None), slice(None)]



https://bitbucket.org/yt_analysis/yt/changeset/fd330d7949cc/
changeset:   fd330d7949cc
branch:      yt
user:        Andrew Myers
date:        2012-06-21 05:51:46
summary:     merging from tip
affected #:  194 files
Diff too large to display.

https://bitbucket.org/yt_analysis/yt/changeset/f1b54858bcd8/
changeset:   f1b54858bcd8
branch:      yt
user:        Andrew Myers
date:        2012-06-21 05:59:47
summary:     fixing a floating point bug in AMRSmoothedCoveringGrid, and removing all traces of that time I thought about adding a refine_by that can vary with level
affected #:  2 files

diff -r fd330d7949cc7e73af58284db13a0c9c970fd22f -r f1b54858bcd88741c1f3dd4c3956e4aae4ad6688 yt/data_objects/data_containers.py
--- a/yt/data_objects/data_containers.py
+++ b/yt/data_objects/data_containers.py
@@ -3649,7 +3649,7 @@
                     self.pf.domain_left_edge)/dx).astype('int64')
         if level == 0 and self.level > 0:
             # We use one grid cell at LEAST, plus one buffer on all sides
-            idims = na.rint((self.right_edge-self.left_edge)/dx).astype('int64') + 2
+            idims = na.rint((self.ActiveDimensions*self.dds)/dx).astype('int64') + 2
             fields = ensure_list(fields)
             for field in fields:
                 self.field_data[field] = na.zeros(idims,dtype='float64')-999
@@ -3657,7 +3657,7 @@
         elif level == 0 and self.level == 0:
             DLE = self.pf.domain_left_edge
             self.global_startindex = na.array(na.floor(LL/ dx), dtype='int64')
-            idims = na.rint((self.right_edge-self.left_edge)/dx).astype('int64')
+            idims = na.rint((self.ActiveDimensions*self.dds)/dx).astype('int64')
             fields = ensure_list(fields)
             for field in fields:
                 self.field_data[field] = na.zeros(idims,dtype='float64')-999
@@ -3668,8 +3668,7 @@
 
         input_left = (self._old_global_startindex + 0.5) * rf 
         dx = na.fromiter((self['cd%s' % ax] for ax in 'xyz'), count=3, dtype='float64')
-        output_dims = na.rint((self.right_edge-self.left_edge)/dx+0.5).astype('int32') + 2
-
+        output_dims = na.rint((self.ActiveDimensions*self.dds)/dx+0.5).astype('int32') + 2
         self._cur_dims = output_dims
 
         for field in fields:


diff -r fd330d7949cc7e73af58284db13a0c9c970fd22f -r f1b54858bcd88741c1f3dd4c3956e4aae4ad6688 yt/data_objects/grid_patch.py
--- a/yt/data_objects/grid_patch.py
+++ b/yt/data_objects/grid_patch.py
@@ -367,10 +367,7 @@
 
     #@time_execution
     def __fill_child_mask(self, child, mask, tofill):
-        try:
-            rf = self.pf.refine_by[child.Level-1]
-        except TypeError:
-            rf = self.pf.refine_by
+        rf = self.pf.refine_by
         gi, cgi = self.get_global_startindex(), child.get_global_startindex()
         startIndex = na.maximum(0, cgi / rf - gi)
         endIndex = na.minimum((cgi + child.ActiveDimensions) / rf - gi,

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