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

commits-noreply at bitbucket.org commits-noreply at bitbucket.org
Fri Oct 6 07:52:11 PDT 2017


17 new commits in yt:

https://bitbucket.org/yt_analysis/yt/commits/1e137c888a30/
Changeset:   1e137c888a30
User:        Corentin Cadiou
Date:        2017-09-04 14:53:58+00:00
Summary:     add support for sink particles in RAMSES
Affected #:  3 files

diff -r e6ea9f86b3eeaf95f3ece1ccec8b35253eff4459 -r 1e137c888a30d5a25ba0e6ba42c8fab62067e04c yt/frontends/ramses/data_structures.py
--- a/yt/frontends/ramses/data_structures.py
+++ b/yt/frontends/ramses/data_structures.py
@@ -62,11 +62,12 @@
             os.path.abspath(
               os.path.dirname(ds.parameter_filename)),
             num, domain_id)
-        for t in ['grav', 'hydro', 'part', 'amr']:
+        for t in ['grav', 'hydro', 'part', 'amr', 'sink']:
             setattr(self, "%s_fn" % t, basename % t)
         self._read_amr_header()
         self._read_hydro_header()
         self._read_particle_header()
+        self._read_sink_header()
         self._read_amr()
 
     _hydro_offset = None
@@ -75,13 +76,21 @@
     def __repr__(self):
         return "RAMSESDomainFile: %i" % self.domain_id
 
-    def _is_hydro(self):
+    @property
+    def _has_hydro(self):
         '''
         Does the output include hydro?
         '''
         return os.path.exists(self.hydro_fn)
 
     @property
+    def _has_sink(self):
+        '''
+        Does the output include sinks (black holes)?
+        '''
+        return os.path.exists(self.sink_fn)
+
+    @property
     def level_count(self):
         if self._level_count is not None: return self._level_count
         self.hydro_offset
@@ -125,14 +134,75 @@
 
     def _read_hydro_header(self):
         # If no hydro file is found, return
-        if not self._is_hydro():
+        if not self._has_hydro:
             return
         if self.nvar > 0: return self.nvar
-        # Read the number of hydro  variables
+        # Read the number of hydro variables
         f = open(self.hydro_fn, "rb")
         fpu.skip(f, 1)
         self.nvar = fpu.read_vector(f, "i")[0]
 
+
+    def _read_sink_header(self):
+        if not self._has_sink:
+            self.local_sink_count = 0
+            self.sink_field_offsets = {}
+            return
+        f = open(self.sink_fn, "rb")
+        f.seek(0, os.SEEK_END)
+        flen = f.tell()
+        f.seek(0)
+        hvals = {}
+        attrs = (('nsink', 1, 'I'),
+                 ('nindsink', 1, 'I'))
+        hvals.update(fpu.read_attrs(f, attrs))
+        self.sink_header = hvals
+        self.local_sink_count = hvals['nsink']
+
+        sink_fields = [
+            ("particle_identifier", "i"),
+            ("particle_mass", "d"),
+            ("particle_position_x", "d"),
+            ("particle_position_y", "d"),
+            ("particle_position_z", "d"),
+            ("particle_velocity_x", "d"),
+            ("particle_velocity_y", "d"),
+            ("particle_velocity_z", "d"),
+            ("particle_age", "d"),
+            ("real_accretion", "d"),
+            ("bondi_accretion", "d"),
+            ("eddington_accretion", "d"),
+            ("esave", "d"),
+            ("spin_x", "d"),
+            ("spin_y", "d"),
+            ("spin_z", "d"),
+            ("spin", "d"),
+            ("efficiency", "d")]
+
+        for i in range(self.ds.dimensionality*2+1):
+            for j in range(self.ds.max_level, self.ds.min_level):
+                sink_fields.append((
+                    "particle_prop_%s_%s" % (i, j), "d"
+                ))
+
+        field_offsets = {}
+        _pfields = {}
+        for field, vtype in sink_fields:
+            if f.tell() >= flen: break
+            field_offsets["sink", field] = f.tell()
+            _pfields["sink", field] = vtype
+            fpu.skip(f, 1)
+        self.sink_field_offsets = field_offsets
+        self.sink_field_types = _pfields
+
+        try:
+            ptypes = list(self.particles_types)
+        except AttributeError:
+            ptypes = []
+        finally:
+            ptypes.append('sink')
+        self.particle_types = self.particle_types_raw = tuple(ptypes)
+
     def _read_particle_header(self):
         if not os.path.exists(self.part_fn):
             self.local_particle_count = 0
@@ -175,14 +245,16 @@
 
         field_offsets = {}
         _pfields = {}
+
+        ptype = 'particle' if self._has_sink else 'io'
+
         for field, vtype in particle_fields:
             if f.tell() >= flen: break
-            field_offsets["io", field] = f.tell()
-            _pfields["io", field] = vtype
+            field_offsets[ptype, field] = f.tell()
+            _pfields[ptype, field] = vtype
             fpu.skip(f, 1)
         self.particle_field_offsets = field_offsets
         self.particle_field_types = _pfields
-        self.particle_types = self.particle_types_raw = ("io",)
 
     def _read_amr_header(self):
         hvals = {}
@@ -210,7 +282,7 @@
 
     def _read_amr(self):
         """Open the oct file, read in octs level-by-level.
-           For each oct, only the position, index, level and domain 
+           For each oct, only the position, index, level and domain
            are needed - its position in the octree is found automatically.
            The most important is finding all the information to feed
            oct_handler.add
@@ -236,7 +308,7 @@
         min_level = self.ds.min_level
         # yt max level is not the same as the RAMSES one.
         # yt max level is the maximum number of additional refinement levels
-        # so for a uni grid run with no refinement, it would be 0. 
+        # so for a uni grid run with no refinement, it would be 0.
         # So we initially assume that.
         max_level = 0
         nx, ny, nz = (((i-1.0)/2.0) for i in self.amr_header['nx'])
@@ -369,8 +441,12 @@
         dsl = set([])
         if self.fluid_field_list is None or len(self.fluid_field_list) <= 0:
             self._setup_auto_fields()
+
         for domain in self.domains:
             dsl.update(set(domain.particle_field_offsets.keys()))
+            dsl.update(set(domain.sink_field_offsets.keys()))
+            ptypes = domain.particle_types
+
         self.particle_field_list = list(dsl)
         self.field_list = [("ramses", f) for f in self.fluid_field_list] \
                         + self.particle_field_list
@@ -388,6 +464,7 @@
               os.path.dirname(self.dataset.parameter_filename)),
             num, testdomain)
         hydro_fn = basename % "hydro"
+        sink_fn = basename % "sink"
         # Do we have a hydro file?
         if not os.path.exists(hydro_fn):
             self.fluid_field_list = []
@@ -405,7 +482,7 @@
         self.ds.gamma = hvals['gamma']
         nvar = hvals['nvar']
         # OK, we got NVAR, now set up the arrays depending on what NVAR is
-        # but first check for radiative transfer!    
+        # but first check for radiative transfer!
         foldername  = os.path.abspath(os.path.dirname(self.ds.parameter_filename))
         rt_flag = any(glob.glob(os.sep.join([foldername, 'info_rt_*.txt'])))
         if rt_flag: # rt run
@@ -414,32 +491,32 @@
                 fields = ["Density", "x-velocity", "y-velocity", "z-velocity", "Pressure", "Metallicity", "HII", "HeII", "HeIII"]
             else:
                 mylog.info('Detected RAMSES-RT file WITH IR trapping.')
-                fields = ["Density", "x-velocity", "y-velocity", "z-velocity", "Pres_IR", "Pressure", "Metallicity", "HII", "HeII", "HeIII"]     
-        else:            
+                fields = ["Density", "x-velocity", "y-velocity", "z-velocity", "Pres_IR", "Pressure", "Metallicity", "HII", "HeII", "HeIII"]
+        else:
             if nvar < 5:
                 mylog.debug("nvar=%s is too small! YT doesn't currently support 1D/2D runs in RAMSES %s")
                 raise ValueError
             # Basic hydro runs
             if nvar == 5:
                 fields = ["Density",
-                          "x-velocity", "y-velocity", "z-velocity", 
+                          "x-velocity", "y-velocity", "z-velocity",
                           "Pressure"]
             if nvar > 5 and nvar < 11:
-                fields = ["Density", 
-                          "x-velocity", "y-velocity", "z-velocity", 
+                fields = ["Density",
+                          "x-velocity", "y-velocity", "z-velocity",
                           "Pressure", "Metallicity"]
             # MHD runs - NOTE: THE MHD MODULE WILL SILENTLY ADD 3 TO THE NVAR IN THE MAKEFILE
             if nvar == 11:
-                fields = ["Density", 
-                          "x-velocity", "y-velocity", "z-velocity", 
-                          "x-Bfield-left", "y-Bfield-left", "z-Bfield-left", 
-                          "x-Bfield-right", "y-Bfield-right", "z-Bfield-right", 
+                fields = ["Density",
+                          "x-velocity", "y-velocity", "z-velocity",
+                          "x-Bfield-left", "y-Bfield-left", "z-Bfield-left",
+                          "x-Bfield-right", "y-Bfield-right", "z-Bfield-right",
                           "Pressure"]
             if nvar > 11:
-                fields = ["Density", 
-                          "x-velocity", "y-velocity", "z-velocity", 
-                          "x-Bfield-left", "y-Bfield-left", "z-Bfield-left", 
-                          "x-Bfield-right", "y-Bfield-right", "z-Bfield-right", 
+                fields = ["Density",
+                          "x-velocity", "y-velocity", "z-velocity",
+                          "x-Bfield-left", "y-Bfield-left", "z-Bfield-left",
+                          "x-Bfield-right", "y-Bfield-right", "z-Bfield-right",
                           "Pressure","Metallicity"]
         # Allow some wiggle room for users to add too many variables
         while len(fields) < nvar:
@@ -498,9 +575,9 @@
         return {'io': npart}
 
     def print_stats(self):
-        
+
         # This function prints information based on the fluid on the grids,
-        # and therefore does not work for DM only runs. 
+        # and therefore does not work for DM only runs.
         if not self.fluid_field_list:
             print("This function is not implemented for DM only runs")
             return
@@ -540,7 +617,7 @@
     _index_class = RAMSESIndex
     _field_info_class = RAMSESFieldInfo
     gamma = 1.4 # This will get replaced on hydro_fn open
-    
+
     def __init__(self, filename, dataset_type='ramses',
                  fields=None, storage_filename=None,
                  units_override=None, unit_system="cgs",
@@ -552,7 +629,7 @@
         fields: An array of hydro variable fields in order of position in the hydro_XXXXX.outYYYYY file
                 If set to None, will try a default set of fields
         extra_particle_fields: An array of extra particle variables in order of position in the particle_XXXXX.outYYYYY file.
-        cosmological: If set to None, automatically detect cosmological simulation. If a boolean, force 
+        cosmological: If set to None, automatically detect cosmological simulation. If a boolean, force
                       its value.
         '''
         self.fluid_types += ("ramses",)
@@ -561,6 +638,7 @@
         self.force_cosmological = cosmological
         Dataset.__init__(self, filename, dataset_type, units_override=units_override,
                          unit_system=unit_system)
+
         self.storage_filename = storage_filename
 
 
@@ -578,7 +656,7 @@
         time_unit = self.parameters['unit_t']
 
         # calculating derived units (except velocity and temperature, done below)
-        mass_unit = density_unit * length_unit**3     
+        mass_unit = density_unit * length_unit**3
         magnetic_unit = np.sqrt(4*np.pi * mass_unit /
                                 (time_unit**2 * length_unit))
         pressure_unit = density_unit * (length_unit / time_unit)**2
@@ -685,9 +763,11 @@
 
             self.time_simu = self.t_frw[iage  ]*(age-self.tau_frw[iage-1])/(self.tau_frw[iage]-self.tau_frw[iage-1])+ \
                              self.t_frw[iage-1]*(age-self.tau_frw[iage  ])/(self.tau_frw[iage-1]-self.tau_frw[iage])
- 
+
             self.current_time = (self.time_tot + self.time_simu)/(self.hubble_constant*1e7/3.08e24)/self.parameters['unit_t']
 
+        self.particle_types = self.particle_types_raw = ('sink', 'particle')
+
 
     @classmethod
     def _is_valid(self, *args, **kwargs):

diff -r e6ea9f86b3eeaf95f3ece1ccec8b35253eff4459 -r 1e137c888a30d5a25ba0e6ba42c8fab62067e04c yt/frontends/ramses/fields.py
--- a/yt/frontends/ramses/fields.py
+++ b/yt/frontends/ramses/fields.py
@@ -32,6 +32,7 @@
 rho_units = "code_density"
 vel_units = "code_velocity"
 pressure_units = "code_pressure"
+e_units = "code_mass * code_velocity**2 / code_time**2"
 
 known_species_masses = dict(
   (sp, mh * v) for sp, v in [
@@ -89,6 +90,27 @@
         ("particle_metallicity", ("", [], None)),
     )
 
+    known_sink_fields = (
+        ("particle_position_x", ("code_length", [], None)),
+        ("particle_position_y", ("code_length", [], None)),
+        ("particle_position_z", ("code_length", [], None)),
+        ("particle_velocity_x", (vel_units, [], None)),
+        ("particle_velocity_y", (vel_units, [], None)),
+        ("particle_velocity_z", (vel_units, [], None)),
+        ("particle_mass", ("code_mass", [], None)),
+        ("particle_identifier", ("", ["particle_index"], None)),
+        ("particle_age", ("code_time", ['age'], None)),
+        ("particle_real_accretion", ("code_mass/code_time", [], None)),
+        ("particle_bondi_accretion", ("code_mass/code_time", [], None)),
+        ("particle_eddington_accretion", ("code_mass/code_time", [], None)),
+        ("particle_esave", (e_units, [], None)),
+        ("particle_spin_x", ("", [], None)),
+        ("particle_spin_y", ("", [], None)),
+        ("particle_spin_z", ("", [], None)),
+        ("particle_spin", ("", [], None)),
+        ("particle_efficiency", ("", [], None))
+    )
+
     def setup_fluid_fields(self):
         def _temperature(field, data):
             rv = data["gas", "pressure"]/data["gas", "density"]

diff -r e6ea9f86b3eeaf95f3ece1ccec8b35253eff4459 -r 1e137c888a30d5a25ba0e6ba42c8fab62067e04c yt/frontends/ramses/io.py
--- a/yt/frontends/ramses/io.py
+++ b/yt/frontends/ramses/io.py
@@ -91,28 +91,53 @@
                         data = np.asarray(rv.pop((ptype, field))[mask], "=f8")
                         yield (ptype, field), data
 
-    def _read_particle_subset(self, subset, fields):
-        f = open(subset.domain.part_fn, "rb")
-        foffsets = subset.domain.particle_field_offsets
+    def _generic_handler(self, fname, foffsets, data_types,
+                         subset, fields):
         tr = {}
-        # We do *all* conversion into boxlen here.
-        # This means that no other conversions need to be applied to convert
-        # positions into the same domain as the octs themselves.
-        for field in sorted(fields, key = lambda a: foffsets[a]):
-            f.seek(foffsets[field])
-            dt = subset.domain.particle_field_types[field]
-            tr[field] = fpu.read_vector(f, dt)
-            if field[1].startswith("particle_position"):
-                np.divide(tr[field], subset.domain.ds["boxlen"], tr[field])
-            cosmo = subset.domain.ds.cosmological_simulation
-            if cosmo == 1 and field[1] == "particle_age":
-                tf = subset.domain.ds.t_frw
-                dtau = subset.domain.ds.dtau
-                tauf = subset.domain.ds.tau_frw
-                tsim = subset.domain.ds.time_simu
-                h100 = subset.domain.ds.hubble_constant
-                nOver2 = subset.domain.ds.n_frw/2
-                t_scale = 1./(h100 * 100 * cm_per_km / cm_per_mpc)/subset.domain.ds['unit_t']
-                ages = tr[field]
-                tr[field] = get_ramses_ages(tf,tauf,dtau,tsim,t_scale,ages,nOver2,len(ages))            
+        with open(fname, "rb") as f:
+            # We do *all* conversion into boxlen here.
+            # This means that no other conversions need to be applied to convert
+            # positions into the same domain as the octs themselves.
+            for field in sorted(fields, key=lambda a: foffsets[a]):
+                f.seek(foffsets[field])
+                dt = data_types[field]
+                tr[field] = fpu.read_vector(f, dt)
+                if field[1].startswith("particle_position"):
+                    np.divide(tr[field], subset.domain.ds["boxlen"], tr[field])
+                cosmo = subset.domain.ds.cosmological_simulation
+                if cosmo == 1 and field[1] == "particle_age":
+                    tf = subset.domain.ds.t_frw
+                    dtau = subset.domain.ds.dtau
+                    tauf = subset.domain.ds.tau_frw
+                    tsim = subset.domain.ds.time_simu
+                    h100 = subset.domain.ds.hubble_constant
+                    nOver2 = subset.domain.ds.n_frw/2
+                    t_scale = 1./(h100 * 100 * cm_per_km / cm_per_mpc)/subset.domain.ds['unit_t']
+                    ages = tr[field]
+                    tr[field] = get_ramses_ages(tf,tauf,dtau,tsim,t_scale,ages,nOver2,len(ages))
         return tr
+
+
+    def _read_particle_subset(self, subset, fields):
+        tr = {}
+
+        # Read the particles by types (particle, sinks)
+        for ptype in set(f[0] for f in fields):
+
+            # Group by particle type
+            subs_fields = filter(lambda f: f[0] == ptype, fields)
+
+            if ptype in ['particle', 'io']:
+                fname = subset.domain.part_fn
+                foffsets = subset.domain.particle_field_offsets
+                data_types = subset.domain.particle_field_types
+
+            elif ptype in ['sink']:
+                fname = subset.domain.sink_fn
+                foffsets = subset.domain.sink_field_offsets
+                data_types = subset.domain.sink_field_types
+
+            tr.update(self._generic_handler(fname, foffsets, data_types,
+                                            subset, subs_fields))
+
+        return tr


https://bitbucket.org/yt_analysis/yt/commits/c4bd4c3b442f/
Changeset:   c4bd4c3b442f
User:        Corentin Cadiou
Date:        2017-09-04 14:59:59+00:00
Summary:     passes flake8
Affected #:  1 file

diff -r 1e137c888a30d5a25ba0e6ba42c8fab62067e04c -r c4bd4c3b442ff3ca1be31c1bf0ad1195b51957c8 yt/frontends/ramses/data_structures.py
--- a/yt/frontends/ramses/data_structures.py
+++ b/yt/frontends/ramses/data_structures.py
@@ -445,7 +445,6 @@
         for domain in self.domains:
             dsl.update(set(domain.particle_field_offsets.keys()))
             dsl.update(set(domain.sink_field_offsets.keys()))
-            ptypes = domain.particle_types
 
         self.particle_field_list = list(dsl)
         self.field_list = [("ramses", f) for f in self.fluid_field_list] \
@@ -464,12 +463,11 @@
               os.path.dirname(self.dataset.parameter_filename)),
             num, testdomain)
         hydro_fn = basename % "hydro"
-        sink_fn = basename % "sink"
         # Do we have a hydro file?
         if not os.path.exists(hydro_fn):
             self.fluid_field_list = []
             return
-        # Read the number of hydro  variables
+        # Read the number of hydro variables
         f = open(hydro_fn, "rb")
         hydro_header = ( ('ncpu', 1, 'i'),
                          ('nvar', 1, 'i'),


https://bitbucket.org/yt_analysis/yt/commits/323bdd5a7461/
Changeset:   323bdd5a7461
User:        Corentin Cadiou
Date:        2017-09-04 15:07:04+00:00
Summary:     add exception
Affected #:  1 file

diff -r c4bd4c3b442ff3ca1be31c1bf0ad1195b51957c8 -r 323bdd5a7461e4d5a4f406aa1caa34cede77d77e yt/frontends/ramses/io.py
--- a/yt/frontends/ramses/io.py
+++ b/yt/frontends/ramses/io.py
@@ -24,6 +24,7 @@
 from yt.utilities.lib.cosmology_time import \
     get_ramses_ages
 from yt.extern.six import PY3
+from yt.utilities.exceptions import YTFieldNotFound
 
 if PY3:
     from io import BytesIO as IO
@@ -137,6 +138,10 @@
                 foffsets = subset.domain.sink_field_offsets
                 data_types = subset.domain.sink_field_types
 
+            else:
+                # Raise here an exception
+                raise Exception('Unknown field %s' % ptype)
+
             tr.update(self._generic_handler(fname, foffsets, data_types,
                                             subset, subs_fields))
 


https://bitbucket.org/yt_analysis/yt/commits/b57a079c758d/
Changeset:   b57a079c758d
User:        Corentin Cadiou
Date:        2017-09-04 15:19:36+00:00
Summary:     fix units + fields
Affected #:  2 files

diff -r 323bdd5a7461e4d5a4f406aa1caa34cede77d77e -r b57a079c758d517d7b2b04ac544de4927da27c2a yt/frontends/ramses/data_structures.py
--- a/yt/frontends/ramses/data_structures.py
+++ b/yt/frontends/ramses/data_structures.py
@@ -169,15 +169,18 @@
             ("particle_velocity_y", "d"),
             ("particle_velocity_z", "d"),
             ("particle_age", "d"),
-            ("real_accretion", "d"),
-            ("bondi_accretion", "d"),
-            ("eddington_accretion", "d"),
-            ("esave", "d"),
-            ("spin_x", "d"),
-            ("spin_y", "d"),
-            ("spin_z", "d"),
-            ("spin", "d"),
-            ("efficiency", "d")]
+            ("BH_real_accretion", "d"),
+            ("BH_bondi_accretion", "d"),
+            ("BH_eddington_accretion", "d"),
+            ("BH_esave", "d"),
+            ("gas_spin_x", "d"),
+            ("gas_spin_y", "d"),
+            ("gas_spin_z", "d"),
+            ("BH_spin_x", "d"),
+            ("BH_spin_y", "d"),
+            ("BH_spin_z", "d"),
+            ("BH_spin", "d"),
+            ("BH_efficiency", "d")]
 
         for i in range(self.ds.dimensionality*2+1):
             for j in range(self.ds.max_level, self.ds.min_level):

diff -r 323bdd5a7461e4d5a4f406aa1caa34cede77d77e -r b57a079c758d517d7b2b04ac544de4927da27c2a yt/frontends/ramses/fields.py
--- a/yt/frontends/ramses/fields.py
+++ b/yt/frontends/ramses/fields.py
@@ -33,6 +33,7 @@
 vel_units = "code_velocity"
 pressure_units = "code_pressure"
 e_units = "code_mass * code_velocity**2 / code_time**2"
+am_units = "code_mass * code_velocity * code_length"
 
 known_species_masses = dict(
   (sp, mh * v) for sp, v in [
@@ -100,15 +101,18 @@
         ("particle_mass", ("code_mass", [], None)),
         ("particle_identifier", ("", ["particle_index"], None)),
         ("particle_age", ("code_time", ['age'], None)),
-        ("particle_real_accretion", ("code_mass/code_time", [], None)),
-        ("particle_bondi_accretion", ("code_mass/code_time", [], None)),
-        ("particle_eddington_accretion", ("code_mass/code_time", [], None)),
-        ("particle_esave", (e_units, [], None)),
-        ("particle_spin_x", ("", [], None)),
-        ("particle_spin_y", ("", [], None)),
-        ("particle_spin_z", ("", [], None)),
-        ("particle_spin", ("", [], None)),
-        ("particle_efficiency", ("", [], None))
+        ("BH_real_accretion", ("code_mass/code_time", [], None)),
+        ("BH_bondi_accretion", ("code_mass/code_time", [], None)),
+        ("BH_eddington_accretion", ("code_mass/code_time", [], None)),
+        ("BH_esave", (e_units, [], None)),
+        ("gas_spin_x", (am_units, [], None)),
+        ("gas_spin_y", (am_units, [], None)),
+        ("gas_spin_z", (am_units, [], None)),
+        ("BH_spin_x", ("", [], None)),
+        ("BH_spin_y", ("", [], None)),
+        ("BH_spin_z", ("", [], None)),
+        ("BH_spin", (am_units, [], None)),
+        ("BH_efficiency", ("", [], None))
     )
 
     def setup_fluid_fields(self):


https://bitbucket.org/yt_analysis/yt/commits/2e28ccccf604/
Changeset:   2e28ccccf604
User:        Corentin Cadiou
Date:        2017-09-06 08:03:08+00:00
Summary:     default particle type: io
Affected #:  2 files

diff -r b57a079c758d517d7b2b04ac544de4927da27c2a -r 2e28ccccf604605a5981c97444b63017d7b273a9 yt/frontends/ramses/data_structures.py
--- a/yt/frontends/ramses/data_structures.py
+++ b/yt/frontends/ramses/data_structures.py
@@ -249,7 +249,7 @@
         field_offsets = {}
         _pfields = {}
 
-        ptype = 'particle' if self._has_sink else 'io'
+        ptype = 'io'
 
         for field, vtype in particle_fields:
             if f.tell() >= flen: break
@@ -767,7 +767,13 @@
 
             self.current_time = (self.time_tot + self.time_simu)/(self.hubble_constant*1e7/3.08e24)/self.parameters['unit_t']
 
-        self.particle_types = self.particle_types_raw = ('sink', 'particle')
+        if self.index.domains[0]._has_sink:
+            ptypes = ('io', 'sink')
+        else:
+            ptypes = ('io', )
+
+        self.particle_types = self.particle_types_raw = ptypes
+
 
 
     @classmethod

diff -r b57a079c758d517d7b2b04ac544de4927da27c2a -r 2e28ccccf604605a5981c97444b63017d7b273a9 yt/frontends/ramses/io.py
--- a/yt/frontends/ramses/io.py
+++ b/yt/frontends/ramses/io.py
@@ -128,19 +128,19 @@
             # Group by particle type
             subs_fields = filter(lambda f: f[0] == ptype, fields)
 
-            if ptype in ['particle', 'io']:
+            if ptype == 'particle':
                 fname = subset.domain.part_fn
                 foffsets = subset.domain.particle_field_offsets
                 data_types = subset.domain.particle_field_types
 
-            elif ptype in ['sink']:
+            elif ptype == 'sink':
                 fname = subset.domain.sink_fn
                 foffsets = subset.domain.sink_field_offsets
                 data_types = subset.domain.sink_field_types
 
             else:
                 # Raise here an exception
-                raise Exception('Unknown field %s' % ptype)
+                raise Exception('Unknown particle type %s' % ptype)
 
             tr.update(self._generic_handler(fname, foffsets, data_types,
                                             subset, subs_fields))


https://bitbucket.org/yt_analysis/yt/commits/d2c1bf5889bc/
Changeset:   d2c1bf5889bc
User:        Corentin Cadiou
Date:        2017-09-06 08:11:33+00:00
Summary:     autodetect sinks
Affected #:  1 file

diff -r 2e28ccccf604605a5981c97444b63017d7b273a9 -r d2c1bf5889bc8d2ced9b23db4c6813b0ec1385b6 yt/frontends/ramses/data_structures.py
--- a/yt/frontends/ramses/data_structures.py
+++ b/yt/frontends/ramses/data_structures.py
@@ -767,7 +767,12 @@
 
             self.current_time = (self.time_tot + self.time_simu)/(self.hubble_constant*1e7/3.08e24)/self.parameters['unit_t']
 
-        if self.index.domains[0]._has_sink:
+        sink_files = os.path.join(
+            os.path.split(self.parameter_filename)[0],
+            'sink_?????.out?????')
+        has_sink = len(glob.glob(sink_files))
+
+        if has_sink:
             ptypes = ('io', 'sink')
         else:
             ptypes = ('io', )


https://bitbucket.org/yt_analysis/yt/commits/6f93d125f68d/
Changeset:   6f93d125f68d
User:        Corentin Cadiou
Date:        2017-09-06 08:13:49+00:00
Summary:     fix particle type in io
Affected #:  1 file

diff -r d2c1bf5889bc8d2ced9b23db4c6813b0ec1385b6 -r 6f93d125f68dd9434a6176119e51bf0e6e6926f0 yt/frontends/ramses/io.py
--- a/yt/frontends/ramses/io.py
+++ b/yt/frontends/ramses/io.py
@@ -128,7 +128,7 @@
             # Group by particle type
             subs_fields = filter(lambda f: f[0] == ptype, fields)
 
-            if ptype == 'particle':
+            if ptype == 'io':
                 fname = subset.domain.part_fn
                 foffsets = subset.domain.particle_field_offsets
                 data_types = subset.domain.particle_field_types


https://bitbucket.org/yt_analysis/yt/commits/97f1050797d6/
Changeset:   97f1050797d6
User:        Corentin Cadiou
Date:        2017-09-07 11:38:38+00:00
Summary:     flak8
Affected #:  1 file

diff -r 6f93d125f68dd9434a6176119e51bf0e6e6926f0 -r 97f1050797d6843a4af0aaf74fbde84bfc8a715c yt/frontends/ramses/io.py
--- a/yt/frontends/ramses/io.py
+++ b/yt/frontends/ramses/io.py
@@ -24,7 +24,6 @@
 from yt.utilities.lib.cosmology_time import \
     get_ramses_ages
 from yt.extern.six import PY3
-from yt.utilities.exceptions import YTFieldNotFound
 
 if PY3:
     from io import BytesIO as IO


https://bitbucket.org/yt_analysis/yt/commits/8d9c463a2fcf/
Changeset:   8d9c463a2fcf
User:        Corentin Cadiou
Date:        2017-09-07 11:41:48+00:00
Summary:     add test
Affected #:  1 file

diff -r 97f1050797d6843a4af0aaf74fbde84bfc8a715c -r 8d9c463a2fcf804305bca4a41d389592d50083bb yt/frontends/ramses/tests/test_outputs.py
--- a/yt/frontends/ramses/tests/test_outputs.py
+++ b/yt/frontends/ramses/tests/test_outputs.py
@@ -154,3 +154,30 @@
     for field in special_fields:
         assert(field in ds.derived_field_list)
         ad[field]
+
+
+ramses_sink = "ramses_sink_00016/output_00016/info_00016.txt"
+ at requires_file(ramses_sink)
+def test_ramses_sink():
+    ds = yt.load(ramses_sink)
+    ad = ds.all_data()
+
+    expected_fields = ["BH_bondi_accretion", "BH_eddington_accretion",
+                       "BH_efficiency", "BH_esave",
+                       "BH_real_accretion", "BH_spin", "BH_spin_x",
+                       "BH_spin_y", "BH_spin_z", "gas_spin_x",
+                       "gas_spin_y", "gas_spin_z", "particle_age",
+                       "particle_identifier", "particle_mass",
+                       "particle_position_x", "particle_position_y",
+                       "particle_position_z", "particle_prop_0_0",
+                       "particle_prop_0_1", "particle_prop_0_2",
+                       "particle_prop_0_3", "particle_prop_1_0",
+                       "particle_prop_1_1", "particle_prop_1_2",
+                       "particle_velocity_x", "particle_velocity_y",
+                       "particle_velocity_z"]
+
+    for field in expected_fields:
+        assert(('sink', field) in ds.field_list)
+
+        # test that field access works
+        ad['sink', field]


https://bitbucket.org/yt_analysis/yt/commits/8099e2402c52/
Changeset:   8099e2402c52
User:        Corentin Cadiou
Date:        2017-09-07 11:50:28+00:00
Summary:     checking that sinks are *only* detected as necessary
Affected #:  1 file

diff -r 8d9c463a2fcf804305bca4a41d389592d50083bb -r 8099e2402c52842e1a70281c4008930c26658265 yt/frontends/ramses/tests/test_outputs.py
--- a/yt/frontends/ramses/tests/test_outputs.py
+++ b/yt/frontends/ramses/tests/test_outputs.py
@@ -158,10 +158,8 @@
 
 ramses_sink = "ramses_sink_00016/output_00016/info_00016.txt"
 @requires_file(ramses_sink)
+ at requires_file(ramsesNonCosmo)
 def test_ramses_sink():
-    ds = yt.load(ramses_sink)
-    ad = ds.all_data()
-
     expected_fields = ["BH_bondi_accretion", "BH_eddington_accretion",
                        "BH_efficiency", "BH_esave",
                        "BH_real_accretion", "BH_spin", "BH_spin_x",
@@ -176,8 +174,20 @@
                        "particle_velocity_x", "particle_velocity_y",
                        "particle_velocity_z"]
 
+    # Check that sinks are autodetected
+    ds = yt.load(ramses_sink)
+    ad = ds.all_data()
+
     for field in expected_fields:
         assert(('sink', field) in ds.field_list)
 
         # test that field access works
         ad['sink', field]
+
+
+    # Checking that sinks are autodetected
+    ds = yt.load(ramsesCosmo)
+    ad = ds.all_data()
+
+    for field in expected_fields:
+        assert(('sink', 'field') not in ds.field_list)


https://bitbucket.org/yt_analysis/yt/commits/c44a48e296e9/
Changeset:   c44a48e296e9
User:        Corentin Cadiou
Date:        2017-09-07 11:58:34+00:00
Summary:     add documentation
Affected #:  1 file

diff -r 8099e2402c52842e1a70281c4008930c26658265 -r c44a48e296e95870d4aba21c50727240bd36eb55 doc/source/examining/loading_data.rst
--- a/doc/source/examining/loading_data.rst
+++ b/doc/source/examining/loading_data.rst
@@ -1988,13 +1988,21 @@
 
 yt supports outputs made by the mainline ``RAMSES`` code as well as the
 ``RAMSES-RT`` fork. Files produces by ``RAMSES-RT`` are recognized as such
-based on the presence of a ``into_rt_*.txt`` file in the output directory.
+based on the presence of a ``info_rt_*.txt`` file in the output directory.
 
 It is possible to force yt to treat the simulation as a cosmological
 simulation by providing the ``cosmological=True`` parameter (or
 ``False`` to force non-cosmology). If left to ``None``, the kind of
 the simulation is inferred from the data.
 
+yt also support outputs that include sinks (RAMSES' name for black
+holes) when the folder contains files like ``sink_XXXXX.outYYYYY``.
+
+Note: for backward compatibility, particles from the
+``particle_XXXXX.outYYYYY`` files have the particle type ``io`` by
+default (including dark matter, stars, tracer particles, …). Sink
+particles have the particle type ``sink``.
+
 .. _loading-sph-data:
 
 SPH Particle Data


https://bitbucket.org/yt_analysis/yt/commits/124cc374b0f9/
Changeset:   124cc374b0f9
User:        Corentin Cadiou
Date:        2017-09-07 12:05:40+00:00
Summary:     add coments
Affected #:  2 files

diff -r c44a48e296e95870d4aba21c50727240bd36eb55 -r 124cc374b0f970caf244afd8e156f61ff5748e50 yt/frontends/ramses/data_structures.py
--- a/yt/frontends/ramses/data_structures.py
+++ b/yt/frontends/ramses/data_structures.py
@@ -767,6 +767,7 @@
 
             self.current_time = (self.time_tot + self.time_simu)/(self.hubble_constant*1e7/3.08e24)/self.parameters['unit_t']
 
+        # Check for the presence of sink files
         sink_files = os.path.join(
             os.path.split(self.parameter_filename)[0],
             'sink_?????.out?????')

diff -r c44a48e296e95870d4aba21c50727240bd36eb55 -r 124cc374b0f970caf244afd8e156f61ff5748e50 yt/frontends/ramses/io.py
--- a/yt/frontends/ramses/io.py
+++ b/yt/frontends/ramses/io.py
@@ -93,6 +93,16 @@
 
     def _generic_handler(self, fname, foffsets, data_types,
                          subset, fields):
+        '''General file handler, called by _read_particle_subset
+
+        params:
+        -------
+        fname: filename to read from
+        foffsets: dictionary-like of the offset for the fields
+        data_types: dictionary_like of the data type for the fields
+        subset: a subset object
+        fields: list of fields to read
+        '''
         tr = {}
         with open(fname, "rb") as f:
             # We do *all* conversion into boxlen here.
@@ -119,12 +129,13 @@
 
 
     def _read_particle_subset(self, subset, fields):
+        '''Read the particle files.'''
         tr = {}
 
-        # Read the particles by types (particle, sinks)
+        # Sequential read depending on particle type (io or sink)
         for ptype in set(f[0] for f in fields):
 
-            # Group by particle type
+            # Select relevant fiels
             subs_fields = filter(lambda f: f[0] == ptype, fields)
 
             if ptype == 'io':


https://bitbucket.org/yt_analysis/yt/commits/605a166f4429/
Changeset:   605a166f4429
User:        Corentin Cadiou
Date:        2017-09-13 15:34:35+00:00
Summary:     more explicit unit names
Affected #:  1 file

diff -r 124cc374b0f970caf244afd8e156f61ff5748e50 -r 605a166f4429e712179f5e77f4079141301923a9 yt/frontends/ramses/fields.py
--- a/yt/frontends/ramses/fields.py
+++ b/yt/frontends/ramses/fields.py
@@ -32,8 +32,8 @@
 rho_units = "code_density"
 vel_units = "code_velocity"
 pressure_units = "code_pressure"
-e_units = "code_mass * code_velocity**2 / code_time**2"
-am_units = "code_mass * code_velocity * code_length"
+ener_units = "code_mass * code_velocity**2 / code_time**2"
+ang_mom_units = "code_mass * code_velocity * code_length"
 
 known_species_masses = dict(
   (sp, mh * v) for sp, v in [
@@ -104,14 +104,14 @@
         ("BH_real_accretion", ("code_mass/code_time", [], None)),
         ("BH_bondi_accretion", ("code_mass/code_time", [], None)),
         ("BH_eddington_accretion", ("code_mass/code_time", [], None)),
-        ("BH_esave", (e_units, [], None)),
-        ("gas_spin_x", (am_units, [], None)),
-        ("gas_spin_y", (am_units, [], None)),
-        ("gas_spin_z", (am_units, [], None)),
+        ("BH_esave", (ener_units, [], None)),
+        ("gas_spin_x", (ang_mom_units, [], None)),
+        ("gas_spin_y", (ang_mom_units, [], None)),
+        ("gas_spin_z", (ang_mom_units, [], None)),
         ("BH_spin_x", ("", [], None)),
         ("BH_spin_y", ("", [], None)),
         ("BH_spin_z", ("", [], None)),
-        ("BH_spin", (am_units, [], None)),
+        ("BH_spin", (ang_mom_units, [], None)),
         ("BH_efficiency", ("", [], None))
     )
 


https://bitbucket.org/yt_analysis/yt/commits/9e8e1512578c/
Changeset:   9e8e1512578c
User:        Corentin Cadiou
Date:        2017-09-13 15:34:49+00:00
Summary:     move generic file handler to own function
Affected #:  1 file

diff -r 605a166f4429e712179f5e77f4079141301923a9 -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 yt/frontends/ramses/io.py
--- a/yt/frontends/ramses/io.py
+++ b/yt/frontends/ramses/io.py
@@ -30,6 +30,48 @@
 else:
     from cStringIO import StringIO as IO
 
+def _ramses_particle_file_handler(fname, foffsets, data_types,
+                                  subset, fields):
+    '''General file handler, called by _read_particle_subset
+
+    Parameters
+    ----------
+    fname : string
+        filename to read from
+    foffsets: dict
+        Offsets in file of the fields
+    data_types: dict
+         Data type of the fields
+    subset: ``RAMSESDomainSubset``
+         A RAMSES domain subset object
+    fields: list of tuple
+         The fields to read
+    '''
+    tr = {}
+    with open(fname, "rb") as f:
+        # We do *all* conversion into boxlen here.
+        # This means that no other conversions need to be applied to convert
+        # positions into the same domain as the octs themselves.
+        for field in sorted(fields, key=lambda a: foffsets[a]):
+            f.seek(foffsets[field])
+            dt = data_types[field]
+            tr[field] = fpu.read_vector(f, dt)
+            if field[1].startswith("particle_position"):
+                np.divide(tr[field], subset.domain.ds["boxlen"], tr[field])
+            cosmo = subset.domain.ds.cosmological_simulation
+            if cosmo == 1 and field[1] == "particle_age":
+                tf = subset.domain.ds.t_frw
+                dtau = subset.domain.ds.dtau
+                tauf = subset.domain.ds.tau_frw
+                tsim = subset.domain.ds.time_simu
+                h100 = subset.domain.ds.hubble_constant
+                nOver2 = subset.domain.ds.n_frw/2
+                t_scale = 1./(h100 * 100 * cm_per_km / cm_per_mpc)/subset.domain.ds['unit_t']
+                ages = tr[field]
+                tr[field] = get_ramses_ages(tf,tauf,dtau,tsim,t_scale,ages,nOver2,len(ages))
+    return tr
+
+
 class IOHandlerRAMSES(BaseIOHandler):
     _dataset_type = "ramses"
 
@@ -91,42 +133,6 @@
                         data = np.asarray(rv.pop((ptype, field))[mask], "=f8")
                         yield (ptype, field), data
 
-    def _generic_handler(self, fname, foffsets, data_types,
-                         subset, fields):
-        '''General file handler, called by _read_particle_subset
-
-        params:
-        -------
-        fname: filename to read from
-        foffsets: dictionary-like of the offset for the fields
-        data_types: dictionary_like of the data type for the fields
-        subset: a subset object
-        fields: list of fields to read
-        '''
-        tr = {}
-        with open(fname, "rb") as f:
-            # We do *all* conversion into boxlen here.
-            # This means that no other conversions need to be applied to convert
-            # positions into the same domain as the octs themselves.
-            for field in sorted(fields, key=lambda a: foffsets[a]):
-                f.seek(foffsets[field])
-                dt = data_types[field]
-                tr[field] = fpu.read_vector(f, dt)
-                if field[1].startswith("particle_position"):
-                    np.divide(tr[field], subset.domain.ds["boxlen"], tr[field])
-                cosmo = subset.domain.ds.cosmological_simulation
-                if cosmo == 1 and field[1] == "particle_age":
-                    tf = subset.domain.ds.t_frw
-                    dtau = subset.domain.ds.dtau
-                    tauf = subset.domain.ds.tau_frw
-                    tsim = subset.domain.ds.time_simu
-                    h100 = subset.domain.ds.hubble_constant
-                    nOver2 = subset.domain.ds.n_frw/2
-                    t_scale = 1./(h100 * 100 * cm_per_km / cm_per_mpc)/subset.domain.ds['unit_t']
-                    ages = tr[field]
-                    tr[field] = get_ramses_ages(tf,tauf,dtau,tsim,t_scale,ages,nOver2,len(ages))
-        return tr
-
 
     def _read_particle_subset(self, subset, fields):
         '''Read the particle files.'''
@@ -152,7 +158,7 @@
                 # Raise here an exception
                 raise Exception('Unknown particle type %s' % ptype)
 
-            tr.update(self._generic_handler(fname, foffsets, data_types,
-                                            subset, subs_fields))
+            tr.update(_ramses_particle_file_handler(
+                fname, foffsets, data_types, subset, subs_fields))
 
         return tr


https://bitbucket.org/yt_analysis/yt/commits/309210b5e7f3/
Changeset:   309210b5e7f3
User:        Corentin Cadiou
Date:        2017-10-06 11:49:40+00:00
Summary:     Merge branch 'master' into feature/RAMSES-sinks
Affected #:  21 files

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd .travis.yml
--- a/.travis.yml
+++ b/.travis.yml
@@ -20,11 +20,12 @@
     SCIPY=scipy
     IPYTHON=ipython
     FASTCACHE=fastcache
+    FLAKE8=flake8
 
 matrix:
   include:
     - python: 2.7
-      env: NUMPY=numpy==1.10.4 CYTHON=cython==0.24 MATPLOTLIB=matplotlib==1.5.3 SYMPY=sympy==1.0 H5PY= SCIPY= FASTCACHE= IPYTHON=ipython==1.0
+      env: NUMPY=numpy==1.10.4 CYTHON=cython==0.24 MATPLOTLIB=matplotlib==1.5.3 SYMPY=sympy==1.0 H5PY= SCIPY= FASTCACHE= FLAKE8= IPYTHON=ipython==1.0
     - python: 2.7
     - python: 3.4
     - python: 3.5
@@ -65,7 +66,7 @@
     pip install --upgrade wheel
     pip install --upgrade setuptools
     # Install dependencies
-    pip install mock $NUMPY $SCIPY $H5PY $CYTHON $MATPLOTLIB $SYMPY $FASTCACHE $IPYTHON nose flake8 nose-timer
+    pip install mock $NUMPY $SCIPY $H5PY $CYTHON $MATPLOTLIB $SYMPY $FASTCACHE $IPYTHON $FLAKE8 nose nose-timer
     # install yt
     pip install -e .
 

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd doc/source/analyzing/units/2)_Fields_and_unit_conversion.ipynb
--- a/doc/source/analyzing/units/2)_Fields_and_unit_conversion.ipynb
+++ b/doc/source/analyzing/units/2)_Fields_and_unit_conversion.ipynb
@@ -322,7 +322,20 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Since we have a copy of the data, we can mess with it however we wish without disturbing the original data returned by the yt data object."
+    "Since we have a copy of the data, we can mess with it however we wish without disturbing the original data returned by the yt data object.\n",
+    "\n",
+    "There is yet another way to return a copy of the array data in a `YTArray` or the floating-point value of  a `YTQuantity`, which also allows for the possibility to convert to different units. This is done using the `to_value` method:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "print(dens.to_value()) # Don't change units\n",
+    "print(dens.to_value(\"Msun\")) # Change units to solar masses\n",
+    "print(dens[0].to_value(\"lbm\")) # Pick the first value and change its units to pounds"
    ]
   },
   {
@@ -388,7 +401,9 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {},
+   "metadata": {
+    "collapsed": true
+   },
    "outputs": [],
    "source": [
     "from astropy import units as u\n",
@@ -410,7 +425,9 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {},
+   "metadata": {
+    "collapsed": true
+   },
    "outputs": [],
    "source": [
     "a = np.random.random(size=10) * u.km/u.s\n",
@@ -437,7 +454,9 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {},
+   "metadata": {
+    "collapsed": true
+   },
    "outputs": [],
    "source": [
     "temp = dd[\"temperature\"]\n",
@@ -464,7 +483,9 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {},
+   "metadata": {
+    "collapsed": true
+   },
    "outputs": [],
    "source": [
     "from yt.units import kboltz\n",
@@ -496,7 +517,8 @@
    "source": [
     "k1 = kboltz.to_astropy()\n",
     "k2 = yt.YTQuantity.from_astropy(kb)\n",
-    "print (k1 == k2)"
+    "print(k1)\n",
+    "print(k2)"
    ]
   },
   {
@@ -520,7 +542,9 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {},
+   "metadata": {
+    "collapsed": true
+   },
    "outputs": [],
    "source": [
     "from pint import UnitRegistry\n",
@@ -542,7 +566,9 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {},
+   "metadata": {
+    "collapsed": true
+   },
    "outputs": [],
    "source": [
     "ptemp = temp.to_pint()"
@@ -593,9 +619,7 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
     "from yt.units import clight\n",
@@ -612,9 +636,7 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
     "from yt import YTQuantity\n",
@@ -633,7 +655,7 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
-    "collapsed": false
+    "collapsed": true
    },
    "outputs": [],
    "source": [
@@ -643,9 +665,7 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
     "dd = ds.all_data()\n",
@@ -655,23 +675,23 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python 3",
+   "display_name": "Python [default]",
    "language": "python",
    "name": "python3"
   },
   "language_info": {
    "codemirror_mode": {
     "name": "ipython",
-    "version": 3.0
+    "version": 3
    },
    "file_extension": ".py",
    "mimetype": "text/x-python",
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.5.1"
+   "version": "3.6.1"
   }
  },
  "nbformat": 4,
- "nbformat_minor": 0
+ "nbformat_minor": 1
 }

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd doc/source/analyzing/units/6)_Unit_Equivalencies.ipynb
--- a/doc/source/analyzing/units/6)_Unit_Equivalencies.ipynb
+++ b/doc/source/analyzing/units/6)_Unit_Equivalencies.ipynb
@@ -18,15 +18,13 @@
     "* `\"number_density\"`: conversions between density and number density ($n = \\rho/\\mu{m_p}$)\n",
     "* `\"sound_speed\"`: conversions between temperature and sound speed for an ideal gas ($c_s^2 = \\gamma{k_BT}/\\mu{m_p}$)\n",
     "\n",
-    "A `YTArray` or `YTQuantity` can be converted to an equivalent using `to_equivalent`, where the unit and the equivalence name are provided as arguments:"
+    "A `YTArray` or `YTQuantity` can be converted to an equivalent using `in_units` (previously described in [Fields and Unit Conversion](fields_and_unit_conversion.html)), where the unit and the equivalence name are provided as additional arguments:"
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
     "import yt\n",
@@ -37,12 +35,12 @@
     "\n",
     "dd = ds.all_data()\n",
     "\n",
-    "print (dd[\"temperature\"].to_equivalent(\"erg\", \"thermal\"))\n",
-    "print (dd[\"temperature\"].to_equivalent(\"eV\", \"thermal\"))\n",
+    "print (dd[\"temperature\"].in_units(\"erg\", equivalence=\"thermal\"))\n",
+    "print (dd[\"temperature\"].in_units(\"eV\", equivalence=\"thermal\"))\n",
     "\n",
     "# Rest energy of the proton\n",
     "from yt.units import mp\n",
-    "E_p = mp.to_equivalent(\"GeV\", \"mass_energy\")\n",
+    "E_p = mp.in_units(\"GeV\", equivalence=\"mass_energy\")\n",
     "print (E_p)"
    ]
   },
@@ -56,16 +54,31 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
     "from yt.units import clight\n",
     "v = 0.1*clight\n",
-    "g = v.to_equivalent(\"dimensionless\", \"lorentz\")\n",
+    "g = v.in_units(\"dimensionless\", equivalence=\"lorentz\")\n",
     "print (g)\n",
-    "print (g.to_equivalent(\"c\", \"lorentz\"))"
+    "print (g.in_units(\"c\", equivalence=\"lorentz\"))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "The previously described `to_value` method, which works like `in_units` except that it returns a bare NumPy array or floating-point number, also accepts equivalencies:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "print (dd[\"temperature\"].to_value(\"erg\", equivalence=\"thermal\"))\n",
+    "print (mp.to_value(\"GeV\", equivalence=\"mass_energy\"))"
    ]
   },
   {
@@ -85,14 +98,12 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
     "print (dd[\"density\"].max())\n",
-    "print (dd[\"density\"].to_equivalent(\"cm**-3\", \"number_density\").max())\n",
-    "print (dd[\"density\"].to_equivalent(\"cm**-3\", \"number_density\", mu=0.75).max())"
+    "print (dd[\"density\"].in_units(\"cm**-3\", equivalence=\"number_density\").max())\n",
+    "print (dd[\"density\"].in_units(\"cm**-3\", equivalence=\"number_density\", mu=0.75).max())"
    ]
   },
   {
@@ -105,13 +116,11 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
-    "print (dd[\"temperature\"].to_equivalent(\"km/s\", \"sound_speed\").mean())\n",
-    "print (dd[\"temperature\"].to_equivalent(\"km/s\", \"sound_speed\", gamma=4./3., mu=0.5).mean())"
+    "print (dd[\"temperature\"].in_units(\"km/s\", equivalence=\"sound_speed\").mean())\n",
+    "print (dd[\"temperature\"].in_units(\"km/s\", equivalence=\"sound_speed\", gamma=4./3., mu=0.5).mean())"
    ]
   },
   {
@@ -138,9 +147,7 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
     "Q1 = YTQuantity(1.0,\"C\")\n",
@@ -161,13 +168,11 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
     "from yt.units import qp # the elementary charge in esu\n",
-    "qp_SI = qp.to_equivalent(\"C\",\"SI\") # convert to Coulombs\n",
+    "qp_SI = qp.in_units(\"C\", equivalence=\"SI\") # convert to Coulombs\n",
     "print (qp)\n",
     "print (qp_SI)"
    ]
@@ -182,13 +187,11 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
     "B = YTQuantity(1.0,\"T\") # magnetic field in Tesla\n",
-    "print (B, B.to_equivalent(\"gauss\",\"CGS\")) # convert to Gauss"
+    "print (B, B.in_units(\"gauss\", equivalence=\"CGS\")) # convert to Gauss"
    ]
   },
   {
@@ -201,15 +204,13 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
     "I = YTQuantity(1.0,\"A\")\n",
-    "I_cgs = I.to_equivalent(\"statA\",\"CGS\")\n",
+    "I_cgs = I.in_units(\"statA\", equivalence=\"CGS\")\n",
     "R = YTQuantity(1.0,\"ohm\")\n",
-    "R_cgs = R.to_equivalent(\"statohm\",\"CGS\")\n",
+    "R_cgs = R.in_units(\"statohm\", equivalence=\"CGS\")\n",
     "P = I**2*R\n",
     "P_cgs = I_cgs**2*R_cgs"
    ]
@@ -224,9 +225,7 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
     "print (P_cgs.units.dimensions == P.units.dimensions)\n",
@@ -250,15 +249,13 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
     "from yt.utilities.exceptions import YTInvalidUnitEquivalence\n",
     "\n",
     "try:\n",
-    "    x = v.to_equivalent(\"angstrom\", \"spectral\")\n",
+    "    x = v.in_units(\"angstrom\", equivalence=\"spectral\")\n",
     "except YTInvalidUnitEquivalence as e:\n",
     "    print (e)"
    ]
@@ -273,9 +270,7 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
     "print (mp.has_equivalent(\"compton\"))\n",
@@ -292,9 +287,7 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
     "E_p.list_equivalencies()"
@@ -303,7 +296,7 @@
  ],
  "metadata": {
   "kernelspec": {
-   "display_name": "Python 3",
+   "display_name": "Python [default]",
    "language": "python",
    "name": "python3"
   },
@@ -317,9 +310,9 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.5.1"
+   "version": "3.6.1"
   }
  },
  "nbformat": 4,
- "nbformat_minor": 0
+ "nbformat_minor": 1
 }

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd doc/source/analyzing/units/fields_and_unit_conversion.rst
--- a/doc/source/analyzing/units/fields_and_unit_conversion.rst
+++ b/doc/source/analyzing/units/fields_and_unit_conversion.rst
@@ -1,4 +1,4 @@
-.. _data_selection_and_fields:
+.. _fields_and_unit_conversion:
 
 Fields and Unit Conversion
 ==========================

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/analysis_modules/halo_analysis/halo_catalog.py
--- a/yt/analysis_modules/halo_analysis/halo_catalog.py
+++ b/yt/analysis_modules/halo_analysis/halo_catalog.py
@@ -20,6 +20,7 @@
     save_as_dataset
 from yt.funcs import \
     ensure_dir, \
+    get_pbar, \
     mylog
 from yt.utilities.parallel_tools.parallel_analysis_interface import \
     ParallelAnalysisInterface, \
@@ -419,7 +420,12 @@
             self.add_default_quantities('all')
 
         my_index = np.argsort(self.data_source["all", "particle_identifier"])
+        nhalos = my_index.size
+        my_i = 0
+        my_n = self.comm.size
+        pbar = get_pbar("Creating catalog", nhalos, parallel=True)
         for i in parallel_objects(my_index, njobs=njobs, dynamic=dynamic):
+            my_i += min(my_n, nhalos - my_i)
             new_halo = Halo(self)
             halo_filter = True
             for action_type, action in self.actions:
@@ -427,7 +433,9 @@
                     action(new_halo)
                 elif action_type == "filter":
                     halo_filter = action(new_halo)
-                    if not halo_filter: break
+                    if not halo_filter:
+                        pbar.update(my_i)
+                        break
                 elif action_type == "quantity":
                     key, quantity = action
                     if quantity in self.halos_ds.field_info:
@@ -449,6 +457,8 @@
             else:
                 del new_halo
 
+            pbar.update(my_i)
+
         self.catalog.sort(key=lambda a:a['particle_identifier'].to_ndarray())
         if save_catalog:
             self.save_catalog()

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/data_objects/grid_patch.py
--- a/yt/data_objects/grid_patch.py
+++ b/yt/data_objects/grid_patch.py
@@ -21,8 +21,6 @@
 from yt.config import ytcfg
 from yt.data_objects.data_containers import \
     YTSelectionContainer
-from yt.data_objects.field_data import \
-    YTFieldData
 from yt.funcs import iterable
 from yt.geometry.selection_routines import convert_mask_to_indices
 import yt.geometry.particle_deposit as particle_deposit
@@ -58,8 +56,7 @@
     OverlappingSiblings = None
 
     def __init__(self, id, filename=None, index=None):
-        self.field_data = YTFieldData()
-        self.field_parameters = {}
+        super(AMRGridPatch, self).__init__(index.dataset, None)
         self.id = id
         self._child_mask = self._child_indices = self._child_index_mask = None
         self.ds = index.dataset
@@ -69,8 +66,6 @@
         self._last_mask = None
         self._last_count = -1
         self._last_selector_id = None
-        self._current_particle_type = 'all'
-        self._current_fluid_type = self.ds.default_fluid_type
 
     def get_global_startindex(self):
         """

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/data_objects/octree_subset.py
--- a/yt/data_objects/octree_subset.py
+++ b/yt/data_objects/octree_subset.py
@@ -55,10 +55,9 @@
     _block_reorder = None
 
     def __init__(self, base_region, domain, ds, over_refine_factor = 1):
+        super(OctreeSubset, self).__init__(ds, None)
         self._num_zones = 1 << (over_refine_factor)
         self._oref = over_refine_factor
-        self.field_data = YTFieldData()
-        self.field_parameters = {}
         self.domain = domain
         self.domain_id = domain.domain_id
         self.ds = domain.ds
@@ -66,8 +65,6 @@
         self.oct_handler = domain.oct_handler
         self._last_mask = None
         self._last_selector_id = None
-        self._current_particle_type = 'all'
-        self._current_fluid_type = self.ds.default_fluid_type
         self.base_region = base_region
         self.base_selector = base_region.selector
 

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/data_objects/static_output.py
--- a/yt/data_objects/static_output.py
+++ b/yt/data_objects/static_output.py
@@ -24,6 +24,7 @@
 
 from collections import defaultdict
 from yt.extern.six import add_metaclass, string_types
+from six.moves import cPickle
 
 from yt.config import ytcfg
 from yt.fields.derived_field import \
@@ -252,14 +253,15 @@
                 obj.__init__(filename, *args, **kwargs)
             return obj
         apath = os.path.abspath(filename)
+        cache_key = (apath, cPickle.dumps(args), cPickle.dumps(kwargs))
         if ytcfg.getboolean("yt","skip_dataset_cache"):
             obj = object.__new__(cls)
-        elif apath not in _cached_datasets:
+        elif cache_key not in _cached_datasets:
             obj = object.__new__(cls)
             if obj._skip_cache is False:
-                _cached_datasets[apath] = obj
+                _cached_datasets[cache_key] = obj
         else:
-            obj = _cached_datasets[apath]
+            obj = _cached_datasets[cache_key]
         return obj
 
     def __init__(self, filename, dataset_type=None, file_style=None,

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/data_objects/unstructured_mesh.py
--- a/yt/data_objects/unstructured_mesh.py
+++ b/yt/data_objects/unstructured_mesh.py
@@ -23,8 +23,6 @@
 
 from yt.data_objects.data_containers import \
     YTSelectionContainer
-from yt.data_objects.field_data import \
-    YTFieldData
 import yt.geometry.particle_deposit as particle_deposit
 
 class UnstructuredMesh(YTSelectionContainer):
@@ -39,9 +37,8 @@
 
     def __init__(self, mesh_id, filename, connectivity_indices,
                  connectivity_coords, index):
-        self.field_data = YTFieldData()
+        super(UnstructuredMesh, self).__init__(index.dataset, None)
         self.filename = filename
-        self.field_parameters = {}
         self.mesh_id = mesh_id
         # This is where we set up the connectivity information
         self.connectivity_indices = connectivity_indices
@@ -56,8 +53,6 @@
         self._last_mask = None
         self._last_count = -1
         self._last_selector_id = None
-        self._current_particle_type = 'all'
-        self._current_fluid_type = self.ds.default_fluid_type
 
     def _check_consistency(self):
         if self.connectivity_indices.shape[1] != self._connectivity_length:

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/frontends/fits/data_structures.py
--- a/yt/frontends/fits/data_structures.py
+++ b/yt/frontends/fits/data_structures.py
@@ -109,13 +109,15 @@
             # the right case by comparing against known units. This
             # only really works for common units.
             units = set(re.split(regex_pattern, field_units))
-            if '' in units: units.remove('')
+            if '' in units: 
+                units.remove('')
             n = int(0)
             for unit in units:
                 if unit in known_units:
                     field_units = field_units.replace(unit, known_units[unit])
                     n += 1
-            if n != len(units): field_units = "dimensionless"
+            if n != len(units) or n == 0:
+                field_units = "dimensionless"
             if field_units[0] == "/":
                 field_units = "1%s" % field_units
             return field_units

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/frontends/gadget/definitions.py
--- a/yt/frontends/gadget/definitions.py
+++ b/yt/frontends/gadget/definitions.py
@@ -29,7 +29,7 @@
                     ('OmegaLambda', 1, 'd'),
                     ('HubbleParam', 1, 'd'),
                     ('FlagAge', 1, 'i'),
-                    ('FlagMEtals', 1, 'i'),
+                    ('FlagMetals', 1, 'i'),
                     ('NallHW', 6, 'i'),
                     ('unused', 16, 'i')),
     pad32       = (('empty',  32, 'c'),),

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/frontends/gamer/fields.py
--- a/yt/frontends/gamer/fields.py
+++ b/yt/frontends/gamer/fields.py
@@ -47,13 +47,14 @@
     )
 
     known_particle_fields = (
-        ( "ParMass", ("code_mass",     ["particle_mass"],       None) ),
-        ( "ParPosX", ("code_length",   ["particle_position_x"], None) ),
-        ( "ParPosY", ("code_length",   ["particle_position_y"], None) ),
-        ( "ParPosZ", ("code_length",   ["particle_position_z"], None) ),
-        ( "ParVelX", ("code_velocity", ["particle_velocity_x"], None) ),
-        ( "ParVelY", ("code_velocity", ["particle_velocity_y"], None) ),
-        ( "ParVelZ", ("code_velocity", ["particle_velocity_z"], None) ),
+        ( "ParMass",    ("code_mass",     ["particle_mass"],          None) ),
+        ( "ParPosX",    ("code_length",   ["particle_position_x"],    None) ),
+        ( "ParPosY",    ("code_length",   ["particle_position_y"],    None) ),
+        ( "ParPosZ",    ("code_length",   ["particle_position_z"],    None) ),
+        ( "ParVelX",    ("code_velocity", ["particle_velocity_x"],    None) ),
+        ( "ParVelY",    ("code_velocity", ["particle_velocity_y"],    None) ),
+        ( "ParVelZ",    ("code_velocity", ["particle_velocity_z"],    None) ),
+        ( "ParCreTime", ("code_time",     ["particle_creation_time"], None) ),
     )
 
     def __init__(self, ds, field_list):

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/frontends/ramses/data_structures.py
--- a/yt/frontends/ramses/data_structures.py
+++ b/yt/frontends/ramses/data_structures.py
@@ -240,22 +240,37 @@
                 ("particle_mass", "d"),
                 ("particle_identifier", "i"),
                 ("particle_refinement_level", "I")]
-        if hvals["nstar_tot"] > 0:
-            particle_fields += [("particle_age", "d"),
-                                ("particle_metallicity", "d")]
+
         if self.ds._extra_particle_fields is not None:
             particle_fields += self.ds._extra_particle_fields
 
         field_offsets = {}
         _pfields = {}
 
-        ptype = 'io'
-
+        # Read offsets
         for field, vtype in particle_fields:
             if f.tell() >= flen: break
             field_offsets[ptype, field] = f.tell()
             _pfields[ptype, field] = vtype
             fpu.skip(f, 1)
+
+        iextra = 0
+        while f.tell() < flen:
+            iextra += 1
+            field, vtype = ('particle_extra_field_%i' % iextra, 'd')
+            particle_fields.append((field, vtype))
+
+            field_offsets["io", field] = f.tell()
+            _pfields["io", field] = vtype
+            fpu.skip(f, 1)
+
+        if iextra > 0 and not self.ds._warn_extra_fields:
+            self.ds._warn_extra_fields = True
+            w = ("Detected %s extra particle fields assuming kind "
+                 "`double`. Consider using the `extra_particle_fields` "
+                 "keyword argument if you have unexpected behavior.")
+            mylog.warning(w % iextra)
+
         self.particle_field_offsets = field_offsets
         self.particle_field_types = _pfields
 
@@ -636,6 +651,7 @@
         self.fluid_types += ("ramses",)
         self._fields_in_file = fields
         self._extra_particle_fields = extra_particle_fields
+        self._warn_extra_fields = False
         self.force_cosmological = cosmological
         Dataset.__init__(self, filename, dataset_type, units_override=units_override,
                          unit_system=unit_system)

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/frontends/ramses/tests/test_outputs.py
--- a/yt/frontends/ramses/tests/test_outputs.py
+++ b/yt/frontends/ramses/tests/test_outputs.py
@@ -114,7 +114,7 @@
 ramsesExtraFieldsSmall = 'ramses_extra_fields_small/output_00001'
 @requires_file(ramsesExtraFieldsSmall)
 def test_extra_fields():
-    extra_fields = [('family', 'I'), ('pointer', 'I')]
+    extra_fields = [('particle_family', 'I'), ('particle_pointer', 'I')]
     ds = yt.load(os.path.join(ramsesExtraFieldsSmall, 'info_00001.txt'),
                  extra_particle_fields=extra_fields)
 
@@ -124,9 +124,22 @@
 
     # Check the family (they should equal 100, for tracer particles)
     dd = ds.all_data()
-    families = dd[('all', 'family')]
+    families = dd[('all', 'particle_family')]
     assert all(families == 100)
 
+
+ at requires_file(ramsesExtraFieldsSmall)
+def test_extra_fields_2():
+    extra_fields = ['particle_extra_field_%s' % (i + 1) for i in range(2)]
+    ds = yt.load(os.path.join(ramsesExtraFieldsSmall, 'info_00001.txt'))
+
+    # the dataset should contain the fields
+    for field in extra_fields:
+        assert ('io', field) in ds.field_list
+
+    # In the dataset, the fields are integers, so we cannot test
+    # that they are accessed correctly.
+
 ramses_rt = "ramses_rt_00088/output_00088/info_00088.txt"
 @requires_file(ramses_rt)
 def test_ramses_rt():

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/frontends/tipsy/io.py
--- a/yt/frontends/tipsy/io.py
+++ b/yt/frontends/tipsy/io.py
@@ -325,8 +325,10 @@
         for f in glob.glob(data_file.filename + '.*'):
             afield = f.rsplit('.')[-1]
             filename = data_file.filename + '.' + afield
-            if not os.path.exists(filename): continue
+            if not os.path.exists(filename):
+                continue
             self._aux_fields.append(afield)
+        skip_afields = []
         for afield in self._aux_fields:
             filename = data_file.filename + '.' + afield
             # We need to do some fairly ugly detection to see what format the
@@ -338,6 +340,11 @@
                            count=1) != tot_parts:
                 with open(filename, 'rb') as f:
                     header_nparts = f.readline()
+                    try:
+                        header_nparts = int(header_nparts)
+                    except ValueError:
+                        skip_afields.append(afield)
+                        continue
                     if int(header_nparts) != tot_parts:
                         raise RuntimeError
                 self._aux_pdtypes[afield] = "ascii"
@@ -349,8 +356,9 @@
                 else:
                     self._aux_pdtypes[afield] = np.dtype([('aux', endian + 'f')])
             else:
-                raise RuntimeError
-
+                skip_afields.append(afield)
+        for afield in skip_afields:
+            self._aux_fields.remove(afield)
         # Add the auxiliary fields to each ptype we have
         for ptype in self._ptypes:
             if any([ptype == field[0] for field in self._field_list]):

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/frontends/ytdata/tests/test_outputs.py
--- a/yt/frontends/ytdata/tests/test_outputs.py
+++ b/yt/frontends/ytdata/tests/test_outputs.py
@@ -28,7 +28,8 @@
     assert_allclose_units, \
     assert_equal, \
     assert_fname, \
-    fake_random_ds
+    fake_random_ds, \
+    requires_module
 from yt.utilities.answer_testing.framework import \
     requires_ds, \
     data_dir_load, \
@@ -229,6 +230,7 @@
     os.chdir(curdir)
     shutil.rmtree(tmpdir)
 
+ at requires_module('h5py')
 def test_plot_data():
     tmpdir = tempfile.mkdtemp()
     curdir = os.getcwd()

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/funcs.py
--- a/yt/funcs.py
+++ b/yt/funcs.py
@@ -642,7 +642,7 @@
     import pkg_resources
     yt_provider = pkg_resources.get_provider("yt")
     path = os.path.dirname(yt_provider.module_path)
-    version = get_hg_version(path)
+    version = get_git_version(path)
     if version is None:
         return version
     else:

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/units/tests/test_ytarray.py
--- a/yt/units/tests/test_ytarray.py
+++ b/yt/units/tests/test_ytarray.py
@@ -991,6 +991,17 @@
     for op in [operator.abs, operator.neg, operator.pos]:
         unary_op_registry_comparison(op)
 
+def test_to_value():
+
+    a = YTArray([1.0, 2.0, 3.0], "kpc")
+    assert_equal(a.to_value(), np.array([1.0, 2.0, 3.0]))
+    assert_equal(a.to_value(), a.value)
+    assert_equal(a.to_value("km"), a.in_units("km").value)
+
+    b = YTQuantity(5.5, "Msun")
+    assert_equal(b.to_value(), 5.5)
+    assert_equal(b.to_value("g"), b.in_units("g").value)
+
 @requires_module("astropy")
 def test_astropy():
     from yt.utilities.on_demand_imports import _astropy
@@ -1113,83 +1124,88 @@
 
     # Mass-energy
 
-    E = mp.to_equivalent("keV","mass_energy")
+    E = mp.in_units("keV","mass_energy")
     assert_equal(E, mp*clight*clight)
-    assert_allclose_units(mp, E.to_equivalent("g", "mass_energy"))
+    assert_allclose_units(mp, E.in_units("g", "mass_energy"))
 
     # Thermal
 
     T = YTQuantity(1.0e8,"K")
-    E = T.to_equivalent("W*hr","thermal")
+    E = T.in_units("W*hr","thermal")
     assert_equal(E, (kboltz*T).in_units("W*hr"))
-    assert_allclose_units(T, E.to_equivalent("K", "thermal"))
+    assert_allclose_units(T, E.in_units("K", "thermal"))
 
     # Spectral
 
     l = YTQuantity(4000.,"angstrom")
-    nu = l.to_equivalent("Hz","spectral")
+    nu = l.in_units("Hz","spectral")
     assert_equal(nu, clight/l)
     E = hcgs*nu
-    l2 = E.to_equivalent("angstrom", "spectral")
+    l2 = E.in_units("angstrom", "spectral")
     assert_allclose_units(l, l2)
     nu2 = clight/l2.in_units("cm")
     assert_allclose_units(nu, nu2)
-    E2 = nu2.to_equivalent("keV", "spectral")
+    E2 = nu2.in_units("keV", "spectral")
     assert_allclose_units(E2, E.in_units("keV"))
 
     # Sound-speed
 
     mu = 0.6
     gg = 5./3.
-    c_s = T.to_equivalent("km/s","sound_speed")
+    c_s = T.in_units("km/s", equivalence="sound_speed")
     assert_equal(c_s, np.sqrt(gg*kboltz*T/(mu*mh)))
-    assert_allclose_units(T, c_s.to_equivalent("K","sound_speed"))
+    assert_allclose_units(T, c_s.in_units("K","sound_speed"))
 
     mu = 0.5
     gg = 4./3.
-    c_s = T.to_equivalent("km/s","sound_speed", mu=mu, gamma=gg)
+    c_s = T.in_units("km/s","sound_speed", mu=mu, gamma=gg)
     assert_equal(c_s, np.sqrt(gg*kboltz*T/(mu*mh)))
-    assert_allclose_units(T, c_s.to_equivalent("K","sound_speed", mu=mu, gamma=gg))
+    assert_allclose_units(T, c_s.in_units("K","sound_speed", mu=mu, gamma=gg))
 
     # Lorentz
 
     v = 0.8*clight
-    g = v.to_equivalent("dimensionless","lorentz")
+    g = v.in_units("dimensionless","lorentz")
     g2 = YTQuantity(1./np.sqrt(1.-0.8*0.8), "dimensionless")
     assert_allclose_units(g, g2)
-    v2 = g2.to_equivalent("mile/hr", "lorentz")
+    v2 = g2.in_units("mile/hr", "lorentz")
     assert_allclose_units(v2, v.in_units("mile/hr"))
 
     # Schwarzschild
 
-    R = mass_sun_cgs.to_equivalent("kpc","schwarzschild")
+    R = mass_sun_cgs.in_units("kpc","schwarzschild")
     assert_equal(R.in_cgs(), 2*G*mass_sun_cgs/(clight*clight))
-    assert_allclose_units(mass_sun_cgs, R.to_equivalent("g", "schwarzschild"))
+    assert_allclose_units(mass_sun_cgs, R.in_units("g", "schwarzschild"))
 
     # Compton
 
-    l = me.to_equivalent("angstrom","compton")
+    l = me.in_units("angstrom","compton")
     assert_equal(l, hcgs/(me*clight))
-    assert_allclose_units(me, l.to_equivalent("g", "compton"))
+    assert_allclose_units(me, l.in_units("g", "compton"))
 
     # Number density
 
     rho = mp/u.cm**3
 
-    n = rho.to_equivalent("cm**-3","number_density")
+    n = rho.in_units("cm**-3","number_density")
     assert_equal(n, rho/(mh*0.6))
-    assert_allclose_units(rho, n.to_equivalent("g/cm**3","number_density"))
+    assert_allclose_units(rho, n.in_units("g/cm**3","number_density"))
 
-    n = rho.to_equivalent("cm**-3","number_density", mu=0.75)
+    n = rho.in_units("cm**-3", equivalence="number_density", mu=0.75)
     assert_equal(n, rho/(mh*0.75))
-    assert_allclose_units(rho, n.to_equivalent("g/cm**3","number_density", mu=0.75))
+    assert_allclose_units(rho, n.in_units("g/cm**3", equivalence="number_density", mu=0.75))
 
     # Effective temperature
 
     T = YTQuantity(1.0e4, "K")
-    F = T.to_equivalent("erg/s/cm**2","effective_temperature")
+    F = T.in_units("erg/s/cm**2",equivalence="effective_temperature")
     assert_equal(F, stefan_boltzmann_constant_cgs*T**4)
-    assert_allclose_units(T, F.to_equivalent("K", "effective_temperature"))
+    assert_allclose_units(T, F.in_units("K", equivalence="effective_temperature"))
+
+    # to_value test
+
+    assert_equal(F.value, T.to_value("erg/s/cm**2", equivalence="effective_temperature"))
+    assert_equal(n.value, rho.to_value("cm**-3", equivalence="number_density", mu=0.75))
 
 def test_electromagnetic():
     from yt.units.dimensions import charge_mks, pressure, current_cgs, \
@@ -1199,19 +1215,19 @@
 
     # Various tests of SI and CGS electromagnetic units
 
-    qp_mks = qp.to_equivalent("C", "SI")
+    qp_mks = qp.in_units("C", "SI")
     assert_equal(qp_mks.units.dimensions, charge_mks)
     assert_array_almost_equal(qp_mks.v, 10.0*qp.v/speed_of_light_cm_per_s)
 
-    qp_cgs = qp_mks.to_equivalent("esu", "CGS")
+    qp_cgs = qp_mks.in_units("esu", "CGS")
     assert_array_almost_equal(qp_cgs, qp)
     assert_equal(qp_cgs.units.dimensions, qp.units.dimensions)
     
-    qp_mks_k = qp.to_equivalent("kC", "SI")
+    qp_mks_k = qp.in_units("kC", "SI")
     assert_array_almost_equal(qp_mks_k.v, 1.0e-2*qp.v/speed_of_light_cm_per_s)
 
     B = YTQuantity(1.0, "T")
-    B_cgs = B.to_equivalent("gauss", "CGS")
+    B_cgs = B.in_units("gauss", "CGS")
     assert_equal(B.units.dimensions, magnetic_field_mks)
     assert_equal(B_cgs.units.dimensions, magnetic_field_cgs)
     assert_array_almost_equal(B_cgs, YTQuantity(1.0e4, "gauss"))
@@ -1223,13 +1239,13 @@
     assert_array_almost_equal(u_mks.in_cgs(), u_cgs)
     
     I = YTQuantity(1.0, "A")
-    I_cgs = I.to_equivalent("statA", "CGS")
+    I_cgs = I.in_units("statA", equivalence="CGS")
     assert_array_almost_equal(I_cgs, YTQuantity(0.1*speed_of_light_cm_per_s, "statA"))
-    assert_array_almost_equal(I_cgs.to_equivalent("mA", "SI"), I.in_units("mA"))
+    assert_array_almost_equal(I_cgs.in_units("mA", equivalence="SI"), I.in_units("mA"))
     assert_equal(I_cgs.units.dimensions, current_cgs)
     
     R = YTQuantity(1.0, "ohm")
-    R_cgs = R.to_equivalent("statohm", "CGS")
+    R_cgs = R.in_units("statohm", "CGS")
     P_mks = I*I*R
     P_cgs = I_cgs*I_cgs*R_cgs
     assert_equal(P_mks.units.dimensions, power)
@@ -1238,7 +1254,7 @@
     assert_array_almost_equal(P_cgs.in_mks(), YTQuantity(1.0, "W"))
     
     V = YTQuantity(1.0, "statV")
-    V_mks = V.to_equivalent("V", "SI")
+    V_mks = V.in_units("V", "SI")
     assert_array_almost_equal(V_mks.v, 1.0e8*V.v/speed_of_light_cm_per_s)
 
 def test_ytarray_coercion():

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/units/yt_array.py
--- a/yt/units/yt_array.py
+++ b/yt/units/yt_array.py
@@ -589,38 +589,94 @@
         """
         return self.convert_to_units(self.units.get_mks_equivalent())
 
-    def in_units(self, units):
+    def in_units(self, units, equivalence=None, **kwargs):
         """
-        Creates a copy of this array with the data in the supplied units, and
-        returns it.
+        Creates a copy of this array with the data in the supplied 
+        units, and returns it.
+
+        Optionally, an equivalence can be specified to convert to an 
+        equivalent quantity which is not in the same dimensions. 
+
+        .. note::
+
+            All additional keyword arguments are passed to the 
+            equivalency, which should be used if that particular 
+            equivalency requires them.
 
         Parameters
         ----------
         units : Unit object or string
             The units you want to get a new quantity in.
+        equivalence : string, optional
+            The equivalence you wish to use. To see which 
+            equivalencies are supported for this unitful 
+            quantity, try the :meth:`list_equivalencies` 
+            method. Default: None
 
         Returns
         -------
         YTArray
+        """
+        if equivalence is None:
+            new_units = _unit_repr_check_same(self.units, units)
+            (conversion_factor, offset) = self.units.get_conversion_factor(new_units)
 
-        """
-        new_units = _unit_repr_check_same(self.units, units)
-        (conversion_factor, offset) = self.units.get_conversion_factor(new_units)
-
-        new_array = type(self)(self.ndview * conversion_factor, new_units)
+            new_array = type(self)(self.ndview * conversion_factor, new_units)
 
-        if offset:
-            np.subtract(new_array, offset*new_array.uq, new_array)
+            if offset:
+                np.subtract(new_array, offset*new_array.uq, new_array)
 
-        return new_array
+            return new_array
+        else:
+            return self.to_equivalent(units, equivalence, **kwargs)
 
-    def to(self, units):
+    def to(self, units, equivalence=None, **kwargs):
         """
         An alias for YTArray.in_units().
 
         See the docstrings of that function for details.
         """
-        return self.in_units(units)
+        return self.in_units(units, equivalence=equivalence, **kwargs)
+
+    def to_value(self, units=None, equivalence=None, **kwargs):
+        """
+        Creates a copy of this array with the data in the supplied
+        units, and returns it without units. Output is therefore a 
+        bare NumPy array.
+
+        Optionally, an equivalence can be specified to convert to an 
+        equivalent quantity which is not in the same dimensions. 
+
+        .. note::
+
+            All additional keyword arguments are passed to the 
+            equivalency, which should be used if that particular 
+            equivalency requires them.
+
+        Parameters
+        ----------
+        units : Unit object or string, optional
+            The units you want to get the bare quantity in. If not
+            specified, the value will be returned in the current units.
+
+        equivalence : string, optional
+            The equivalence you wish to use. To see which 
+            equivalencies are supported for this unitful 
+            quantity, try the :meth:`list_equivalencies` 
+            method. Default: None
+
+        Returns
+        -------
+        NumPy array
+        """
+        if units is None:
+            v = self.value
+        else:
+            v = self.in_units(units, equivalence=equivalence, **kwargs).value
+        if isinstance(self, YTQuantity):
+            return float(v)
+        else:
+            return v
 
     def in_base(self, unit_system="cgs"):
         """
@@ -684,6 +740,8 @@
         >>> a.to_equivalent("keV", "thermal")
         """
         conv_unit = Unit(unit, registry=self.units.registry)
+        if self.units.same_dimensions_as(conv_unit):
+            return self.in_units(conv_unit)
         this_equiv = equivalence_registry[equiv]()
         oneway_or_equivalent = (
             conv_unit.has_equivalent(equiv) or this_equiv._one_way)

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/utilities/grid_data_format/tests/test_writer.py
--- a/yt/utilities/grid_data_format/tests/test_writer.py
+++ b/yt/utilities/grid_data_format/tests/test_writer.py
@@ -17,7 +17,9 @@
 import os
 from yt.utilities.on_demand_imports import _h5py as h5
 from yt.testing import \
-    fake_random_ds, assert_equal
+    fake_random_ds, \
+    assert_equal, \
+    requires_module
 from yt.utilities.grid_data_format.writer import \
     write_to_gdf
 from yt.frontends.gdf.data_structures import \
@@ -35,6 +37,7 @@
     ytcfg["yt", "__withintesting"] = "True"
 
 
+ at requires_module('h5py')
 def test_write_gdf():
     """Main test suite for write_gdf"""
     tmpdir = tempfile.mkdtemp()

diff -r 9e8e1512578c6f23f8478c6d5d4be368f6e3a2e0 -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd yt/visualization/plot_window.py
--- a/yt/visualization/plot_window.py
+++ b/yt/visualization/plot_window.py
@@ -2049,7 +2049,7 @@
     """
     if ds.dimensionality != 2:
         raise RuntimeError("plot_2d only plots 2D datasets!")
-    if ds.geometry == "cartesian" or ds.geometry == "polar":
+    if ds.geometry in ["cartesian", "polar", "spectral_cube"]:
         axis = "z"
     elif ds.geometry == "cylindrical":
         axis = "theta"


https://bitbucket.org/yt_analysis/yt/commits/e5a41015c49f/
Changeset:   e5a41015c49f
User:        Corentin Cadiou
Date:        2017-10-06 12:02:00+00:00
Summary:     Use generic _add_ptype function
Affected #:  1 file

diff -r 309210b5e7f3e1d9cb19eb76baea3bbb7081b4cd -r e5a41015c49f368a2f299bc7fe23ac1c6deddf55 yt/frontends/ramses/data_structures.py
--- a/yt/frontends/ramses/data_structures.py
+++ b/yt/frontends/ramses/data_structures.py
@@ -132,6 +132,14 @@
         self._level_count = level_count
         return self._hydro_offset
 
+    def _add_ptype(self, ptype):
+        if hasattr(self, 'particle_types'):
+            new = set(self.particle_types)
+        else:
+            new = set()
+        new.add(ptype)
+        self.particle_types = self.particle_types_raw = tuple(new)
+
     def _read_hydro_header(self):
         # If no hydro file is found, return
         if not self._has_hydro:
@@ -198,13 +206,8 @@
         self.sink_field_offsets = field_offsets
         self.sink_field_types = _pfields
 
-        try:
-            ptypes = list(self.particles_types)
-        except AttributeError:
-            ptypes = []
-        finally:
-            ptypes.append('sink')
-        self.particle_types = self.particle_types_raw = tuple(ptypes)
+        self._add_ptype('sink')
+
 
     def _read_particle_header(self):
         if not os.path.exists(self.part_fn):
@@ -247,6 +250,8 @@
         field_offsets = {}
         _pfields = {}
 
+        ptype = 'io'
+
         # Read offsets
         for field, vtype in particle_fields:
             if f.tell() >= flen: break
@@ -260,8 +265,8 @@
             field, vtype = ('particle_extra_field_%i' % iextra, 'd')
             particle_fields.append((field, vtype))
 
-            field_offsets["io", field] = f.tell()
-            _pfields["io", field] = vtype
+            field_offsets[ptype, field] = f.tell()
+            _pfields[ptype, field] = vtype
             fpu.skip(f, 1)
 
         if iextra > 0 and not self.ds._warn_extra_fields:
@@ -274,6 +279,9 @@
         self.particle_field_offsets = field_offsets
         self.particle_field_types = _pfields
 
+        # Register the particle type
+        self._add_ptype(ptype)
+
     def _read_amr_header(self):
         hvals = {}
         f = open(self.amr_fn, "rb")


https://bitbucket.org/yt_analysis/yt/commits/a3b693280fad/
Changeset:   a3b693280fad
User:        ngoldbaum
Date:        2017-10-06 14:50:43+00:00
Summary:     Merge pull request #1548 from cphyc/feature/RAMSES-sinks

[RAMSES] Add support for sink particles
Affected #:  5 files

diff -r 0231a9c04ac1101ef070028c7af3f71684643597 -r a3b693280fadc19b7c0d321a140eb3c117c1bdf6 doc/source/examining/loading_data.rst
--- a/doc/source/examining/loading_data.rst
+++ b/doc/source/examining/loading_data.rst
@@ -1988,13 +1988,21 @@
 
 yt supports outputs made by the mainline ``RAMSES`` code as well as the
 ``RAMSES-RT`` fork. Files produces by ``RAMSES-RT`` are recognized as such
-based on the presence of a ``into_rt_*.txt`` file in the output directory.
+based on the presence of a ``info_rt_*.txt`` file in the output directory.
 
 It is possible to force yt to treat the simulation as a cosmological
 simulation by providing the ``cosmological=True`` parameter (or
 ``False`` to force non-cosmology). If left to ``None``, the kind of
 the simulation is inferred from the data.
 
+yt also support outputs that include sinks (RAMSES' name for black
+holes) when the folder contains files like ``sink_XXXXX.outYYYYY``.
+
+Note: for backward compatibility, particles from the
+``particle_XXXXX.outYYYYY`` files have the particle type ``io`` by
+default (including dark matter, stars, tracer particles, …). Sink
+particles have the particle type ``sink``.
+
 .. _loading-sph-data:
 
 SPH Particle Data

diff -r 0231a9c04ac1101ef070028c7af3f71684643597 -r a3b693280fadc19b7c0d321a140eb3c117c1bdf6 yt/frontends/ramses/data_structures.py
--- a/yt/frontends/ramses/data_structures.py
+++ b/yt/frontends/ramses/data_structures.py
@@ -62,11 +62,12 @@
             os.path.abspath(
               os.path.dirname(ds.parameter_filename)),
             num, domain_id)
-        for t in ['grav', 'hydro', 'part', 'amr']:
+        for t in ['grav', 'hydro', 'part', 'amr', 'sink']:
             setattr(self, "%s_fn" % t, basename % t)
         self._read_amr_header()
         self._read_hydro_header()
         self._read_particle_header()
+        self._read_sink_header()
         self._read_amr()
 
     _hydro_offset = None
@@ -75,13 +76,21 @@
     def __repr__(self):
         return "RAMSESDomainFile: %i" % self.domain_id
 
-    def _is_hydro(self):
+    @property
+    def _has_hydro(self):
         '''
         Does the output include hydro?
         '''
         return os.path.exists(self.hydro_fn)
 
     @property
+    def _has_sink(self):
+        '''
+        Does the output include sinks (black holes)?
+        '''
+        return os.path.exists(self.sink_fn)
+
+    @property
     def level_count(self):
         if self._level_count is not None: return self._level_count
         self.hydro_offset
@@ -123,16 +132,83 @@
         self._level_count = level_count
         return self._hydro_offset
 
+    def _add_ptype(self, ptype):
+        if hasattr(self, 'particle_types'):
+            new = set(self.particle_types)
+        else:
+            new = set()
+        new.add(ptype)
+        self.particle_types = self.particle_types_raw = tuple(new)
+
     def _read_hydro_header(self):
         # If no hydro file is found, return
-        if not self._is_hydro():
+        if not self._has_hydro:
             return
         if self.nvar > 0: return self.nvar
-        # Read the number of hydro  variables
+        # Read the number of hydro variables
         f = open(self.hydro_fn, "rb")
         fpu.skip(f, 1)
         self.nvar = fpu.read_vector(f, "i")[0]
 
+
+    def _read_sink_header(self):
+        if not self._has_sink:
+            self.local_sink_count = 0
+            self.sink_field_offsets = {}
+            return
+        f = open(self.sink_fn, "rb")
+        f.seek(0, os.SEEK_END)
+        flen = f.tell()
+        f.seek(0)
+        hvals = {}
+        attrs = (('nsink', 1, 'I'),
+                 ('nindsink', 1, 'I'))
+        hvals.update(fpu.read_attrs(f, attrs))
+        self.sink_header = hvals
+        self.local_sink_count = hvals['nsink']
+
+        sink_fields = [
+            ("particle_identifier", "i"),
+            ("particle_mass", "d"),
+            ("particle_position_x", "d"),
+            ("particle_position_y", "d"),
+            ("particle_position_z", "d"),
+            ("particle_velocity_x", "d"),
+            ("particle_velocity_y", "d"),
+            ("particle_velocity_z", "d"),
+            ("particle_age", "d"),
+            ("BH_real_accretion", "d"),
+            ("BH_bondi_accretion", "d"),
+            ("BH_eddington_accretion", "d"),
+            ("BH_esave", "d"),
+            ("gas_spin_x", "d"),
+            ("gas_spin_y", "d"),
+            ("gas_spin_z", "d"),
+            ("BH_spin_x", "d"),
+            ("BH_spin_y", "d"),
+            ("BH_spin_z", "d"),
+            ("BH_spin", "d"),
+            ("BH_efficiency", "d")]
+
+        for i in range(self.ds.dimensionality*2+1):
+            for j in range(self.ds.max_level, self.ds.min_level):
+                sink_fields.append((
+                    "particle_prop_%s_%s" % (i, j), "d"
+                ))
+
+        field_offsets = {}
+        _pfields = {}
+        for field, vtype in sink_fields:
+            if f.tell() >= flen: break
+            field_offsets["sink", field] = f.tell()
+            _pfields["sink", field] = vtype
+            fpu.skip(f, 1)
+        self.sink_field_offsets = field_offsets
+        self.sink_field_types = _pfields
+
+        self._add_ptype('sink')
+
+
     def _read_particle_header(self):
         if not os.path.exists(self.part_fn):
             self.local_particle_count = 0
@@ -174,11 +250,13 @@
         field_offsets = {}
         _pfields = {}
 
+        ptype = 'io'
+
         # Read offsets
         for field, vtype in particle_fields:
             if f.tell() >= flen: break
-            field_offsets["io", field] = f.tell()
-            _pfields["io", field] = vtype
+            field_offsets[ptype, field] = f.tell()
+            _pfields[ptype, field] = vtype
             fpu.skip(f, 1)
 
         iextra = 0
@@ -187,8 +265,8 @@
             field, vtype = ('particle_extra_field_%i' % iextra, 'd')
             particle_fields.append((field, vtype))
 
-            field_offsets["io", field] = f.tell()
-            _pfields["io", field] = vtype
+            field_offsets[ptype, field] = f.tell()
+            _pfields[ptype, field] = vtype
             fpu.skip(f, 1)
 
         if iextra > 0 and not self.ds._warn_extra_fields:
@@ -200,7 +278,9 @@
 
         self.particle_field_offsets = field_offsets
         self.particle_field_types = _pfields
-        self.particle_types = self.particle_types_raw = ("io",)
+
+        # Register the particle type
+        self._add_ptype(ptype)
 
     def _read_amr_header(self):
         hvals = {}
@@ -228,7 +308,7 @@
 
     def _read_amr(self):
         """Open the oct file, read in octs level-by-level.
-           For each oct, only the position, index, level and domain 
+           For each oct, only the position, index, level and domain
            are needed - its position in the octree is found automatically.
            The most important is finding all the information to feed
            oct_handler.add
@@ -254,7 +334,7 @@
         min_level = self.ds.min_level
         # yt max level is not the same as the RAMSES one.
         # yt max level is the maximum number of additional refinement levels
-        # so for a uni grid run with no refinement, it would be 0. 
+        # so for a uni grid run with no refinement, it would be 0.
         # So we initially assume that.
         max_level = 0
         nx, ny, nz = (((i-1.0)/2.0) for i in self.amr_header['nx'])
@@ -387,8 +467,11 @@
         dsl = set([])
         if self.fluid_field_list is None or len(self.fluid_field_list) <= 0:
             self._setup_auto_fields()
+
         for domain in self.domains:
             dsl.update(set(domain.particle_field_offsets.keys()))
+            dsl.update(set(domain.sink_field_offsets.keys()))
+
         self.particle_field_list = list(dsl)
         self.field_list = [("ramses", f) for f in self.fluid_field_list] \
                         + self.particle_field_list
@@ -410,7 +493,7 @@
         if not os.path.exists(hydro_fn):
             self.fluid_field_list = []
             return
-        # Read the number of hydro  variables
+        # Read the number of hydro variables
         f = open(hydro_fn, "rb")
         hydro_header = ( ('ncpu', 1, 'i'),
                          ('nvar', 1, 'i'),
@@ -423,7 +506,7 @@
         self.ds.gamma = hvals['gamma']
         nvar = hvals['nvar']
         # OK, we got NVAR, now set up the arrays depending on what NVAR is
-        # but first check for radiative transfer!    
+        # but first check for radiative transfer!
         foldername  = os.path.abspath(os.path.dirname(self.ds.parameter_filename))
         rt_flag = any(glob.glob(os.sep.join([foldername, 'info_rt_*.txt'])))
         if rt_flag: # rt run
@@ -432,32 +515,32 @@
                 fields = ["Density", "x-velocity", "y-velocity", "z-velocity", "Pressure", "Metallicity", "HII", "HeII", "HeIII"]
             else:
                 mylog.info('Detected RAMSES-RT file WITH IR trapping.')
-                fields = ["Density", "x-velocity", "y-velocity", "z-velocity", "Pres_IR", "Pressure", "Metallicity", "HII", "HeII", "HeIII"]     
-        else:            
+                fields = ["Density", "x-velocity", "y-velocity", "z-velocity", "Pres_IR", "Pressure", "Metallicity", "HII", "HeII", "HeIII"]
+        else:
             if nvar < 5:
                 mylog.debug("nvar=%s is too small! YT doesn't currently support 1D/2D runs in RAMSES %s")
                 raise ValueError
             # Basic hydro runs
             if nvar == 5:
                 fields = ["Density",
-                          "x-velocity", "y-velocity", "z-velocity", 
+                          "x-velocity", "y-velocity", "z-velocity",
                           "Pressure"]
             if nvar > 5 and nvar < 11:
-                fields = ["Density", 
-                          "x-velocity", "y-velocity", "z-velocity", 
+                fields = ["Density",
+                          "x-velocity", "y-velocity", "z-velocity",
                           "Pressure", "Metallicity"]
             # MHD runs - NOTE: THE MHD MODULE WILL SILENTLY ADD 3 TO THE NVAR IN THE MAKEFILE
             if nvar == 11:
-                fields = ["Density", 
-                          "x-velocity", "y-velocity", "z-velocity", 
-                          "x-Bfield-left", "y-Bfield-left", "z-Bfield-left", 
-                          "x-Bfield-right", "y-Bfield-right", "z-Bfield-right", 
+                fields = ["Density",
+                          "x-velocity", "y-velocity", "z-velocity",
+                          "x-Bfield-left", "y-Bfield-left", "z-Bfield-left",
+                          "x-Bfield-right", "y-Bfield-right", "z-Bfield-right",
                           "Pressure"]
             if nvar > 11:
-                fields = ["Density", 
-                          "x-velocity", "y-velocity", "z-velocity", 
-                          "x-Bfield-left", "y-Bfield-left", "z-Bfield-left", 
-                          "x-Bfield-right", "y-Bfield-right", "z-Bfield-right", 
+                fields = ["Density",
+                          "x-velocity", "y-velocity", "z-velocity",
+                          "x-Bfield-left", "y-Bfield-left", "z-Bfield-left",
+                          "x-Bfield-right", "y-Bfield-right", "z-Bfield-right",
                           "Pressure","Metallicity"]
         # Allow some wiggle room for users to add too many variables
         while len(fields) < nvar:
@@ -516,9 +599,9 @@
         return {'io': npart}
 
     def print_stats(self):
-        
+
         # This function prints information based on the fluid on the grids,
-        # and therefore does not work for DM only runs. 
+        # and therefore does not work for DM only runs.
         if not self.fluid_field_list:
             print("This function is not implemented for DM only runs")
             return
@@ -570,7 +653,7 @@
         fields: An array of hydro variable fields in order of position in the hydro_XXXXX.outYYYYY file
                 If set to None, will try a default set of fields
         extra_particle_fields: An array of extra particle variables in order of position in the particle_XXXXX.outYYYYY file.
-        cosmological: If set to None, automatically detect cosmological simulation. If a boolean, force 
+        cosmological: If set to None, automatically detect cosmological simulation. If a boolean, force
                       its value.
         '''
         self.fluid_types += ("ramses",)
@@ -580,6 +663,7 @@
         self.force_cosmological = cosmological
         Dataset.__init__(self, filename, dataset_type, units_override=units_override,
                          unit_system=unit_system)
+
         self.storage_filename = storage_filename
 
 
@@ -597,7 +681,7 @@
         time_unit = self.parameters['unit_t']
 
         # calculating derived units (except velocity and temperature, done below)
-        mass_unit = density_unit * length_unit**3     
+        mass_unit = density_unit * length_unit**3
         magnetic_unit = np.sqrt(4*np.pi * mass_unit /
                                 (time_unit**2 * length_unit))
         pressure_unit = density_unit * (length_unit / time_unit)**2
@@ -704,9 +788,23 @@
 
             self.time_simu = self.t_frw[iage  ]*(age-self.tau_frw[iage-1])/(self.tau_frw[iage]-self.tau_frw[iage-1])+ \
                              self.t_frw[iage-1]*(age-self.tau_frw[iage  ])/(self.tau_frw[iage-1]-self.tau_frw[iage])
- 
+
             self.current_time = (self.time_tot + self.time_simu)/(self.hubble_constant*1e7/3.08e24)/self.parameters['unit_t']
 
+        # Check for the presence of sink files
+        sink_files = os.path.join(
+            os.path.split(self.parameter_filename)[0],
+            'sink_?????.out?????')
+        has_sink = len(glob.glob(sink_files))
+
+        if has_sink:
+            ptypes = ('io', 'sink')
+        else:
+            ptypes = ('io', )
+
+        self.particle_types = self.particle_types_raw = ptypes
+
+
 
     @classmethod
     def _is_valid(self, *args, **kwargs):

diff -r 0231a9c04ac1101ef070028c7af3f71684643597 -r a3b693280fadc19b7c0d321a140eb3c117c1bdf6 yt/frontends/ramses/fields.py
--- a/yt/frontends/ramses/fields.py
+++ b/yt/frontends/ramses/fields.py
@@ -32,6 +32,8 @@
 rho_units = "code_density"
 vel_units = "code_velocity"
 pressure_units = "code_pressure"
+ener_units = "code_mass * code_velocity**2 / code_time**2"
+ang_mom_units = "code_mass * code_velocity * code_length"
 
 known_species_masses = dict(
   (sp, mh * v) for sp, v in [
@@ -89,6 +91,30 @@
         ("particle_metallicity", ("", [], None)),
     )
 
+    known_sink_fields = (
+        ("particle_position_x", ("code_length", [], None)),
+        ("particle_position_y", ("code_length", [], None)),
+        ("particle_position_z", ("code_length", [], None)),
+        ("particle_velocity_x", (vel_units, [], None)),
+        ("particle_velocity_y", (vel_units, [], None)),
+        ("particle_velocity_z", (vel_units, [], None)),
+        ("particle_mass", ("code_mass", [], None)),
+        ("particle_identifier", ("", ["particle_index"], None)),
+        ("particle_age", ("code_time", ['age'], None)),
+        ("BH_real_accretion", ("code_mass/code_time", [], None)),
+        ("BH_bondi_accretion", ("code_mass/code_time", [], None)),
+        ("BH_eddington_accretion", ("code_mass/code_time", [], None)),
+        ("BH_esave", (ener_units, [], None)),
+        ("gas_spin_x", (ang_mom_units, [], None)),
+        ("gas_spin_y", (ang_mom_units, [], None)),
+        ("gas_spin_z", (ang_mom_units, [], None)),
+        ("BH_spin_x", ("", [], None)),
+        ("BH_spin_y", ("", [], None)),
+        ("BH_spin_z", ("", [], None)),
+        ("BH_spin", (ang_mom_units, [], None)),
+        ("BH_efficiency", ("", [], None))
+    )
+
     def setup_fluid_fields(self):
         def _temperature(field, data):
             rv = data["gas", "pressure"]/data["gas", "density"]

diff -r 0231a9c04ac1101ef070028c7af3f71684643597 -r a3b693280fadc19b7c0d321a140eb3c117c1bdf6 yt/frontends/ramses/io.py
--- a/yt/frontends/ramses/io.py
+++ b/yt/frontends/ramses/io.py
@@ -30,6 +30,48 @@
 else:
     from cStringIO import StringIO as IO
 
+def _ramses_particle_file_handler(fname, foffsets, data_types,
+                                  subset, fields):
+    '''General file handler, called by _read_particle_subset
+
+    Parameters
+    ----------
+    fname : string
+        filename to read from
+    foffsets: dict
+        Offsets in file of the fields
+    data_types: dict
+         Data type of the fields
+    subset: ``RAMSESDomainSubset``
+         A RAMSES domain subset object
+    fields: list of tuple
+         The fields to read
+    '''
+    tr = {}
+    with open(fname, "rb") as f:
+        # We do *all* conversion into boxlen here.
+        # This means that no other conversions need to be applied to convert
+        # positions into the same domain as the octs themselves.
+        for field in sorted(fields, key=lambda a: foffsets[a]):
+            f.seek(foffsets[field])
+            dt = data_types[field]
+            tr[field] = fpu.read_vector(f, dt)
+            if field[1].startswith("particle_position"):
+                np.divide(tr[field], subset.domain.ds["boxlen"], tr[field])
+            cosmo = subset.domain.ds.cosmological_simulation
+            if cosmo == 1 and field[1] == "particle_age":
+                tf = subset.domain.ds.t_frw
+                dtau = subset.domain.ds.dtau
+                tauf = subset.domain.ds.tau_frw
+                tsim = subset.domain.ds.time_simu
+                h100 = subset.domain.ds.hubble_constant
+                nOver2 = subset.domain.ds.n_frw/2
+                t_scale = 1./(h100 * 100 * cm_per_km / cm_per_mpc)/subset.domain.ds['unit_t']
+                ages = tr[field]
+                tr[field] = get_ramses_ages(tf,tauf,dtau,tsim,t_scale,ages,nOver2,len(ages))
+    return tr
+
+
 class IOHandlerRAMSES(BaseIOHandler):
     _dataset_type = "ramses"
 
@@ -91,28 +133,32 @@
                         data = np.asarray(rv.pop((ptype, field))[mask], "=f8")
                         yield (ptype, field), data
 
+
     def _read_particle_subset(self, subset, fields):
-        f = open(subset.domain.part_fn, "rb")
-        foffsets = subset.domain.particle_field_offsets
+        '''Read the particle files.'''
         tr = {}
-        # We do *all* conversion into boxlen here.
-        # This means that no other conversions need to be applied to convert
-        # positions into the same domain as the octs themselves.
-        for field in sorted(fields, key = lambda a: foffsets[a]):
-            f.seek(foffsets[field])
-            dt = subset.domain.particle_field_types[field]
-            tr[field] = fpu.read_vector(f, dt)
-            if field[1].startswith("particle_position"):
-                np.divide(tr[field], subset.domain.ds["boxlen"], tr[field])
-            cosmo = subset.domain.ds.cosmological_simulation
-            if cosmo == 1 and field[1] == "particle_age":
-                tf = subset.domain.ds.t_frw
-                dtau = subset.domain.ds.dtau
-                tauf = subset.domain.ds.tau_frw
-                tsim = subset.domain.ds.time_simu
-                h100 = subset.domain.ds.hubble_constant
-                nOver2 = subset.domain.ds.n_frw/2
-                t_scale = 1./(h100 * 100 * cm_per_km / cm_per_mpc)/subset.domain.ds['unit_t']
-                ages = tr[field]
-                tr[field] = get_ramses_ages(tf,tauf,dtau,tsim,t_scale,ages,nOver2,len(ages))            
+
+        # Sequential read depending on particle type (io or sink)
+        for ptype in set(f[0] for f in fields):
+
+            # Select relevant fiels
+            subs_fields = filter(lambda f: f[0] == ptype, fields)
+
+            if ptype == 'io':
+                fname = subset.domain.part_fn
+                foffsets = subset.domain.particle_field_offsets
+                data_types = subset.domain.particle_field_types
+
+            elif ptype == 'sink':
+                fname = subset.domain.sink_fn
+                foffsets = subset.domain.sink_field_offsets
+                data_types = subset.domain.sink_field_types
+
+            else:
+                # Raise here an exception
+                raise Exception('Unknown particle type %s' % ptype)
+
+            tr.update(_ramses_particle_file_handler(
+                fname, foffsets, data_types, subset, subs_fields))
+
         return tr

diff -r 0231a9c04ac1101ef070028c7af3f71684643597 -r a3b693280fadc19b7c0d321a140eb3c117c1bdf6 yt/frontends/ramses/tests/test_outputs.py
--- a/yt/frontends/ramses/tests/test_outputs.py
+++ b/yt/frontends/ramses/tests/test_outputs.py
@@ -167,3 +167,40 @@
     for field in special_fields:
         assert(field in ds.derived_field_list)
         ad[field]
+
+
+ramses_sink = "ramses_sink_00016/output_00016/info_00016.txt"
+ at requires_file(ramses_sink)
+ at requires_file(ramsesNonCosmo)
+def test_ramses_sink():
+    expected_fields = ["BH_bondi_accretion", "BH_eddington_accretion",
+                       "BH_efficiency", "BH_esave",
+                       "BH_real_accretion", "BH_spin", "BH_spin_x",
+                       "BH_spin_y", "BH_spin_z", "gas_spin_x",
+                       "gas_spin_y", "gas_spin_z", "particle_age",
+                       "particle_identifier", "particle_mass",
+                       "particle_position_x", "particle_position_y",
+                       "particle_position_z", "particle_prop_0_0",
+                       "particle_prop_0_1", "particle_prop_0_2",
+                       "particle_prop_0_3", "particle_prop_1_0",
+                       "particle_prop_1_1", "particle_prop_1_2",
+                       "particle_velocity_x", "particle_velocity_y",
+                       "particle_velocity_z"]
+
+    # Check that sinks are autodetected
+    ds = yt.load(ramses_sink)
+    ad = ds.all_data()
+
+    for field in expected_fields:
+        assert(('sink', field) in ds.field_list)
+
+        # test that field access works
+        ad['sink', field]
+
+
+    # Checking that sinks are autodetected
+    ds = yt.load(ramsesCosmo)
+    ad = ds.all_data()
+
+    for field in expected_fields:
+        assert(('sink', 'field') not in ds.field_list)

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