<html><body>
<p>8 new commits in yt:</p>
<p><a href="https://bitbucket.org/yt_analysis/yt/commits/139fdd132265/">https://bitbucket.org/yt_analysis/yt/commits/139fdd132265/</a> Changeset:   139fdd132265 Branch:      yt User:        brittonsmith Date:        2016-03-25 23:49:22+00:00 Summary:     HOP halo finder now accepts a ptype keyword. Affected #:  1 file</p>
<p>diff -r a776e67368c5629296b1e365dfca0b68b41ef1d5 -r 139fdd1322659cbcf58afd18f3e96e5ff809efce yt/analysis_modules/halo_finding/halo_objects.py --- a/yt/analysis_modules/halo_finding/halo_objects.py +++ b/yt/analysis_modules/halo_finding/halo_objects.py @@ -59,7 +59,10 @@</p>
<pre>def __init__(self, halo_list, id, indices=None, size=None, CoM=None,
    max_dens_point=None, group_total_mass=None, max_radius=None,</pre>
<ul><li><p>bulk_vel=None, tasks=None, rms_vel=None, supp=None):</p></li></ul>
<p>+        bulk_vel=None, tasks=None, rms_vel=None, supp=None, ptype=None): +        if ptype is None: +            ptype = “all” +        self.ptype = ptype</p>
<pre>self.halo_list = halo_list
self._max_dens = halo_list._max_dens
self.id = id</pre>
<p>@@ -276,10 +279,7 @@</p>
<pre>        return r.max()

    def __getitem__(self, key):</pre>
<ul><li><p>if ytcfg.getboolean("yt", “inline”) is False:</p></li>
<li><p>return self.data[key][self.indices]</p></li>
<li><p>else:</p></li>
<li><p>return self.data[key][self.indices]</p></li></ul>
<p>+        return self.data[(self.ptype, key)][self.indices]</p>
<pre>def get_sphere(self, center_of_mass=True):
    r"""Returns a sphere source.</pre>
<p>@@ -954,7 +954,8 @@</p>
<pre>    _fields = ["particle_position_%s" % ax for ax in 'xyz']
</pre>
<ul><li><p>def __init__(self, data_source, dm_only=True, redshift=-1):</p></li></ul>
<p>+    def __init__(self, data_source, dm_only=True, redshift=-1, +                 ptype=None):</p>
<pre>"""
Run hop on *data_source* with a given density *threshold*.  If
*dm_only* is True (default), only run it on the dark matter particles,</pre>
<p>@@ -963,6 +964,9 @@</p>
<pre>"""
self._data_source = data_source
self.dm_only = dm_only</pre>
<p>+        if ptype is None: +            ptype = “all” +        self.ptype = ptype</p>
<pre>self._groups = []
self._max_dens = {}
self.__obtain_particles()</pre>
<p>@@ -979,14 +983,14 @@</p>
<pre>ii = slice(None)
         self.particle_fields = {}
         for field in self._fields:</pre>
<ul><li><p>tot_part = self._data_source[field].size</p></li></ul>
<p>+            tot_part = self._data_source[(self.ptype, field)].size</p>
<pre>if field == "particle_index":
    self.particle_fields[field] = \</pre>
<ul><li><p>self._data_source[field][ii].astype('int64')</p></li></ul>
<p>+                    self._data_source[(self.ptype, field)][ii].astype('int64')</p>
<pre>else:
    self.particle_fields[field] = \</pre>
<ul><li><p>self._data_source[field][ii].astype('float64')</p></li>
<li><p>del self._data_source[field]</p></li></ul>
<p>+                    self._data_source[(self.ptype, field)][ii].astype('float64') +            del self._data_source[(self.ptype, field)]</p>
<pre>        self._base_indices = np.arange(tot_part)[ii]
        gc.collect()
</pre>
<p>@@ -1014,11 +1018,12 @@</p>
<pre>    cp += counts[i + 1]
    continue
group_indices = grab_indices[cp:cp_c]</pre>
<ul><li><p>self._groups.append(self._halo_class(self, i, group_indices))</p></li></ul>
<p>+            self._groups.append(self._halo_class(self, i, group_indices, +                                                 ptype=self.ptype))</p>
<pre>md_i = np.argmax(dens[cp:cp_c])
px, py, pz = \
    [self.particle_fields['particle_position_%s' % ax][group_indices]</pre>
<ul><li><p>for ax in ‘xyz’]</p></li></ul>
<p>+                 for ax in ‘xyz’]</p>
<pre>self._max_dens[i] = (dens[cp:cp_c][md_i], px[md_i],
    py[md_i], pz[md_i])
cp += counts[i + 1]</pre>
<p>@@ -1260,10 +1265,11 @@</p>
<pre>    _fields = ["particle_position_%s" % ax for ax in 'xyz'] + \
              ["particle_mass"]
</pre>
<ul><li><p>def __init__(self, data_source, threshold=160.0, dm_only=True):</p></li></ul>
<p>+    def __init__(self, data_source, threshold=160.0, dm_only=True, +                 ptype=None):</p>
<pre>self.threshold = threshold
mylog.info("Initializing HOP")</pre>
<ul><li><p>HaloList.__init__(self, data_source, dm_only)</p></li></ul>
<p>+        HaloList.__init__(self, data_source, dm_only, ptype=ptype)</p>
<pre>def _run_finder(self):
    self.densities, self.tags = \</pre>
<p>@@ -1450,12 +1456,15 @@</p>
<pre>            halo += 1

class GenericHaloFinder(HaloList, ParallelAnalysisInterface):</pre>
<ul><li><p>def __init__(self, ds, data_source, dm_only=True, padding=0.0):</p></li></ul>
<p>+    def __init__(self, ds, data_source, padding=0.0, ptype=None):</p>
<pre>         ParallelAnalysisInterface.__init__(self)
         self.ds = ds
         self.index = ds.index
         self.center = (np.array(data_source.right_edge) +
np.array(data_source.left_edge)) / 2.0</pre>
<p>+        if ptype is None: +            ptype = “all” +        self.ptype = ptype</p>
<pre>def _parse_halolist(self, threshold_adjustment):
    groups = []</pre>
<p>@@ -1474,7 +1483,7 @@</p>
<pre>    threshold_adjustment
max_dens[hi] = [max_dens_temp] + \
    list(self._max_dens[halo.id])[1:4]</pre>
<ul><li><p>groups.append(self._halo_class(self, hi))</p></li></ul>
<p>+                groups.append(self._halo_class(self, hi, ptype=self.ptype))</p>
<pre>groups[-1].indices = halo.indices
self.comm.claim_object(groups[-1])
hi += 1</pre>
<p>@@ -1505,9 +1514,11 @@</p>
<pre># Note: we already know which halos we own!
after = my_first_id + len(self._groups)
# One single fake halo, not owned, does the trick</pre>
<ul><li><p>self._groups = [self._halo_class(self, i) for i in range(my_first_id)] + \</p></li></ul>
<p>+        self._groups = [self._halo_class(self, i, ptype=self.ptype) +                        for i in range(my_first_id)] + \</p>
<pre>self._groups + \</pre>
<ul><li><p>[self._halo_class(self, i) for i in range(after, nhalos)]</p></li></ul>
<p>+                       [self._halo_class(self, i, ptype=self.ptype) +                        for i in range(after, nhalos)]</p>
<pre>         id = 0
         for proc in sorted(halo_info.keys()):
for halo in self._groups[id:id + halo_info[proc]]:</pre>
<p>@@ -1540,7 +1551,7 @@</p>
<pre>LE, RE = bounds
dw = self.ds.domain_right_edge - self.ds.domain_left_edge
for i, ax in enumerate('xyz'):</pre>
<ul><li><p>arr = self._data_source["particle_position_%s" % ax]</p></li></ul>
<p>+            arr = self._data_source[self.ptype, “particle_position_%s” % ax]</p>
<pre>            arr[arr < LE[i] - self.padding] += dw[i]
            arr[arr > RE[i] + self.padding] -= dw[i]
</pre>
<p>@@ -1697,14 +1708,14 @@</p>
<pre>>>> halos = HaloFinder(ds)
"""
def __init__(self, ds, subvolume=None, threshold=160, dm_only=True,</pre>
<ul><li><p>padding=0.02, total_mass=None):</p></li></ul>
<p>+                 ptype=None, padding=0.02, total_mass=None):</p>
<pre>         if subvolume is not None:
ds_LE = np.array(subvolume.left_edge)
ds_RE = np.array(subvolume.right_edge)
         self.period = ds.domain_right_edge - ds.domain_left_edge
         self._data_source = ds.all_data()</pre>
<ul><li><p>GenericHaloFinder.__init__(self, ds, self._data_source, dm_only,</p></li>
<li><p>padding)</p></li></ul>
<p>+        GenericHaloFinder.__init__(self, ds, self._data_source, padding, +                                   ptype=ptype)</p>
<pre># do it once with no padding so the total_mass is correct
# (no duplicated particles), and on the entire volume, even if only
# a small part is actually going to be used.</pre>
<p>@@ -1712,6 +1723,17 @@</p>
<pre>         padded, LE, RE, self._data_source = \
self.partition_index_3d(ds=self._data_source,
    padding=self.padding)</pre>
<p>+ +        # Don't allow dm_only=True and setting a ptype. +        if dm_only and ptype is not None: +            raise RuntimeError( +                “If dm_only is True, ptype must be None.  " + \ +                “dm_only must be False if ptype is set.”) + +        if ptype is None: +            ptype = "all” +        self.ptype = ptype +</p>
<pre>         # For scaling the threshold, note that it's a passthrough
         if total_mass is None:
if dm_only:</pre>
<p>@@ -1719,7 +1741,9 @@</p>
<pre>    total_mass = \
        self.comm.mpi_allreduce((self._data_source['all', "particle_mass"][select].in_units('Msun')).sum(dtype='float64'), op='sum')
else:</pre>
<ul><li><p>total_mass = self.comm.mpi_allreduce(self._data_source.quantities["TotalQuantity"]("particle_mass").in_units('Msun'), op='sum')</p></li></ul>
<p>+                total_mass = self.comm.mpi_allreduce( +                    self._data_source.quantities.total_quantity( +                        (self.ptype, "particle_mass")).in_units('Msun'), op='sum')</p>
<pre># MJT: Note that instead of this, if we are assuming that the particles
# are all on different processors, we should instead construct an
# object representing the entire domain and sum it "lazily" with</pre>
<p>@@ -1743,9 +1767,10 @@</p>
<pre>sub_mass = self._data_source["particle_mass"][select].in_units('Msun').sum(dtype='float64')
         else:
sub_mass = \</pre>
<ul><li><p>self._data_source.quantities["TotalQuantity"]("particle_mass").in_units('Msun')</p></li></ul>
<p>+                self._data_source.quantities.total_quantity( +                    (self.ptype, "particle_mass")).in_units('Msun')</p>
<pre>HOPHaloList.__init__(self, self._data_source,</pre>
<ul><li><p>threshold * total_mass / sub_mass, dm_only)</p></li></ul>
<p>+            threshold * total_mass / sub_mass, dm_only, ptype=self.ptype)</p>
<pre>        self._parse_halolist(total_mass / sub_mass)
        self._join_halolists()
</pre>
<p>@@ -1800,8 +1825,7 @@</p>
<pre>self.index = ds.index
self.redshift = ds.current_redshift
self._data_source = ds.all_data()</pre>
<ul><li><p>GenericHaloFinder.__init__(self, ds, self._data_source, dm_only,</p></li>
<li><p>padding)</p></li></ul>
<p>+        GenericHaloFinder.__init__(self, ds, self._data_source, padding)</p>
<pre>self.padding = 0.0  # * ds["unitary"] # This should be clevererer
# get the total number of particles across all procs, with no padding
padded, LE, RE, self._data_source = \</pre>
<p><a href="https://bitbucket.org/yt_analysis/yt/commits/437700bb0b0b/">https://bitbucket.org/yt_analysis/yt/commits/437700bb0b0b/</a> Changeset:   437700bb0b0b Branch:      yt User:        brittonsmith Date:        2016-03-25 23:59:35+00:00 Summary:     FOF halo finder now accepts a ptype keyword. Affected #:  1 file</p>
<p>diff -r 139fdd1322659cbcf58afd18f3e96e5ff809efce -r 437700bb0b0bc77e7bc3e3bc5bf2ec089692ecd3 yt/analysis_modules/halo_finding/halo_objects.py --- a/yt/analysis_modules/halo_finding/halo_objects.py +++ b/yt/analysis_modules/halo_finding/halo_objects.py @@ -1304,10 +1304,12 @@</p>
<pre>    _name = "FOF"
    _halo_class = FOFHalo
</pre>
<ul><li><p>def __init__(self, data_source, link=0.2, dm_only=True, redshift=-1):</p></li></ul>
<p>+    def __init__(self, data_source, link=0.2, dm_only=True, redshift=-1, +                 ptype=None):</p>
<pre>self.link = link
mylog.info("Initializing FOF")</pre>
<ul><li><p>HaloList.__init__(self, data_source, dm_only, redshift=redshift)</p></li></ul>
<p>+        HaloList.__init__(self, data_source, dm_only, redshift=redshift, +                          ptype=ptype)</p>
<pre>def _run_finder(self):
    self.tags = \</pre>
<p>@@ -1816,7 +1818,7 @@</p>
<pre>>>> halos = FOFHaloFinder(ds)
"""
def __init__(self, ds, subvolume=None, link=0.2, dm_only=True,</pre>
<ul><li><p>padding=0.02):</p></li></ul>
<p>+                 ptype=None, padding=0.02):</p>
<pre>         if subvolume is not None:
ds_LE = np.array(subvolume.left_edge)
ds_RE = np.array(subvolume.right_edge)</pre>
<p>@@ -1831,6 +1833,17 @@</p>
<pre>         padded, LE, RE, self._data_source = \
self.partition_index_3d(ds=self._data_source,
padding=self.padding)</pre>
<p>+ +        # Don't allow dm_only=True and setting a ptype. +        if dm_only and ptype is not None: +            raise RuntimeError( +                “If dm_only is True, ptype must be None.  " + \ +                “dm_only must be False if ptype is set.”) + +        if ptype is None: +            ptype = "all” +        self.ptype = ptype +</p>
<pre>         if link > 0.0:
n_parts = self.comm.mpi_allreduce(self._data_source["particle_position_x"].size, op='sum')
# get the average spacing between particles</pre>
<p>@@ -1858,7 +1871,7 @@</p>
<pre># here is where the FOF halo finder is run
mylog.info("Using a linking length of %0.3e", linking_length)
FOFHaloList.__init__(self, self._data_source, linking_length, dm_only,</pre>
<ul><li><p>redshift=self.redshift)</p></li></ul>
<p>+                             redshift=self.redshift, ptype=self.ptype)</p>
<pre>        self._parse_halolist(1.)
        self._join_halolists()
</pre>
<p><a href="https://bitbucket.org/yt_analysis/yt/commits/e7b4f4c98019/">https://bitbucket.org/yt_analysis/yt/commits/e7b4f4c98019/</a> Changeset:   e7b4f4c98019 Branch:      yt User:        brittonsmith Date:        2016-03-26 03:56:42+00:00 Summary:     Adding docstring for ptype. Affected #:  1 file</p>
<p>diff -r 437700bb0b0bc77e7bc3e3bc5bf2ec089692ecd3 -r e7b4f4c980193f4fdcef849589bb34e7050b5c0d yt/analysis_modules/halo_finding/halo_objects.py --- a/yt/analysis_modules/halo_finding/halo_objects.py +++ b/yt/analysis_modules/halo_finding/halo_objects.py @@ -1687,6 +1687,10 @@</p>
<pre>dm_only : bool
    If True, only dark matter particles are used when building halos.
    Default = True.</pre>
<p>+    ptype : string +        When dm_only is set to False, this sets the type of particle to be +        used for halo finding, with a default of “all”.  This should not be +        used when dm_only is set to True.</p>
<pre>padding : float
    When run in parallel, the finder needs to surround each subvolume
    with duplicated particles for halo finidng to work. This number</pre>
<p>@@ -1806,6 +1810,10 @@</p>
<pre>dm_only : bool
    If True, only dark matter particles are used when building halos.
    Default = True.</pre>
<p>+    ptype : string +        When dm_only is set to False, this sets the type of particle to be +        used for halo finding, with a default of “all”.  This should not be +        used when dm_only is set to True.</p>
<pre>padding : float
    When run in parallel, the finder needs to surround each subvolume
    with duplicated particles for halo finidng to work. This number</pre>
<p><a href="https://bitbucket.org/yt_analysis/yt/commits/83558f21c55f/">https://bitbucket.org/yt_analysis/yt/commits/83558f21c55f/</a> Changeset:   83558f21c55f Branch:      yt User:        brittonsmith Date:        2016-03-26 04:06:10+00:00 Summary:     Updating docs. Affected #:  1 file</p>
<p>diff -r e7b4f4c980193f4fdcef849589bb34e7050b5c0d -r 83558f21c55f3893fbef86226aa3b297c2e88db6 doc/source/analyzing/analysis_modules/halo_finders.rst --- a/doc/source/analyzing/analysis_modules/halo_finders.rst +++ b/doc/source/analyzing/analysis_modules/halo_finders.rst @@ -44,8 +44,8 @@</p>
<pre>Please see the `HOP method paper
<http://adsabs.harvard.edu/abs/1998ApJ...498..137E>`_ for
full details and the</pre>
<p>-:class:`~yt.analysis_modules.halo_finding.halo_objects.HOPHalo` and -:class:`~yt.analysis_modules.halo_finding.halo_objects.Halo` classes. +:class:`~yt.analysis_modules.halo_finding.halo_objects.HOPHaloFinder` +documentation.</p>
<pre>.. _fof:
</pre>
<p>@@ -53,8 +53,8 @@</p>
<pre>---

A basic friends-of-friends halo finder is included.  See the</pre>
<p>-:class:`~yt.analysis_modules.halo_finding.halo_objects.FOFHalo` and -:class:`~yt.analysis_modules.halo_finding.halo_objects.Halo` classes. +:class:`~yt.analysis_modules.halo_finding.halo_objects.FOFHaloFinder` +documentation.</p>
<pre>.. _rockstar:
</pre>
<p><a href="https://bitbucket.org/yt_analysis/yt/commits/61cf9dd0c54c/">https://bitbucket.org/yt_analysis/yt/commits/61cf9dd0c54c/</a> Changeset:   61cf9dd0c54c Branch:      yt User:        brittonsmith Date:        2016-03-26 04:42:17+00:00 Summary:     Adding test. Affected #:  1 file</p>
<p>diff -r 83558f21c55f3893fbef86226aa3b297c2e88db6 -r 61cf9dd0c54cc6baf3b4c411f0036cdf67653a1e yt/analysis_modules/halo_finding/tests/test_halo_finders.py --- /dev/null +++ b/yt/analysis_modules/halo_finding/tests/test_halo_finders.py @@ -0,0 +1,61 @@ +""" +Tests for HOP and FOF halo finders. + + + +""" + +#----------------------------------------------------------------------------- +# Copyright © 2016, yt Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +from yt.convenience import \ +    load +from yt.data_objects.particle_filters import \ +    add_particle_filter +from yt.analysis_modules.halo_analysis.api import \ +    HaloCatalog +from yt.testing import \ +    requires_file, \ +    assert_array_equal +from yt.utilities.answer_testing.framework import \ +    data_dir_load + +import tempfile +import os +import shutil + +def dm(pfilter, data): +    return data["creation_time"] <= 0. +add_particle_filter("dm", dm, filtered_type='all', +                    requires=["creation_time"]) + +enzotiny = “enzo_tiny_cosmology/DD0046/DD0046” +@requires_file(enzotiny) +def test_datacontainer_data(): +    tmpdir = tempfile.mkdtemp() +    curdir = os.getcwd() +    os.chdir(tmpdir) +    ds = data_dir_load(enzotiny) +    ds.add_particle_filter("dm") + +    for method in ["fof", “hop"]: +        hc = HaloCatalog(data_ds=ds, finder_method=method, +                         output_dir="hc1”, +                         finder_kwargs={"dm_only": True}) +        hc.create() +        hc = HaloCatalog(data_ds=ds, finder_method=method, +                         output_dir="hc2", +                         finder_kwargs={"dm_only": False, “ptype”: “dm"}) +        hc.create() + +        ds1 = load("hc1/hc1.0.h5”) +        ds2 = load("hc2/hc2.0.h5") +        assert_array_equal(ds1.r["particle_mass"], ds2.r["particle_mass"]) + +    os.chdir(curdir) +    shutil.rmtree(tmpdir)</p>
<p><a href="https://bitbucket.org/yt_analysis/yt/commits/9fd335415ec6/">https://bitbucket.org/yt_analysis/yt/commits/9fd335415ec6/</a> Changeset:   9fd335415ec6 Branch:      yt User:        brittonsmith Date:        2016-03-27 11:31:00+00:00 Summary:     Adding deprecation warning for dm_only flag. Affected #:  1 file</p>
<p>diff -r 61cf9dd0c54cc6baf3b4c411f0036cdf67653a1e -r 9fd335415ec6eb68ad4faa20ea2554f70b5da82f yt/analysis_modules/halo_finding/halo_objects.py --- a/yt/analysis_modules/halo_finding/halo_objects.py +++ b/yt/analysis_modules/halo_finding/halo_objects.py @@ -1730,6 +1730,10 @@</p>
<pre>            self.partition_index_3d(ds=self._data_source,
                padding=self.padding)
</pre>
<p>+        if dm_only: +            mylog.warn("dm_only is deprecated.  " + +                       “Use ptype to specify a particle type, instead.”) +</p>
<pre>         # Don't allow dm_only=True and setting a ptype.
         if dm_only and ptype is not None:
raise RuntimeError(</pre>
<p>@@ -1842,6 +1846,10 @@</p>
<pre>            self.partition_index_3d(ds=self._data_source,
            padding=self.padding)
</pre>
<p>+        if dm_only: +            mylog.warn("dm_only is deprecated.  " + +                       “Use ptype to specify a particle type, instead.”) +</p>
<pre>         # Don't allow dm_only=True and setting a ptype.
         if dm_only and ptype is not None:
raise RuntimeError(</pre>
<p><a href="https://bitbucket.org/yt_analysis/yt/commits/3c5509da45e6/">https://bitbucket.org/yt_analysis/yt/commits/3c5509da45e6/</a> Changeset:   3c5509da45e6 Branch:      yt User:        brittonsmith Date:        2016-03-27 17:00:47+00:00 Summary:     Adding note in docstring to mention that keyword is deprecated. Affected #:  1 file</p>
<p>diff -r 9fd335415ec6eb68ad4faa20ea2554f70b5da82f -r 3c5509da45e65bf9472b206d04f78fa1b74e65fc yt/analysis_modules/halo_finding/halo_objects.py --- a/yt/analysis_modules/halo_finding/halo_objects.py +++ b/yt/analysis_modules/halo_finding/halo_objects.py @@ -1684,8 +1684,10 @@</p>
<pre>    to the full volume automatically.
threshold : float
    The density threshold used when building halos. Default = 160.0.</pre>
<ul><li><p>dm_only : bool</p></li></ul>
<p>+    dm_only : bool (deprecated)</p>
<pre>If True, only dark matter particles are used when building halos.</pre>
<p>+        This has been deprecated.  Instead, the ptype keyword should be +        used to specify a particle type.</p>
<pre>    Default = True.
ptype : string
    When dm_only is set to False, this sets the type of particle to be</pre>
<p>@@ -1811,8 +1813,10 @@</p>
<pre>average) used to build the halos. If negative, this is taken to be
the *actual* linking length, and no other calculations will be
applied.  Default = 0.2.</pre>
<ul><li><p>dm_only : bool</p></li></ul>
<p>+    dm_only : bool (deprecated)</p>
<pre>If True, only dark matter particles are used when building halos.</pre>
<p>+        This has been deprecated.  Instead, the ptype keyword should be +        used to specify a particle type.</p>
<pre>    Default = True.
ptype : string
    When dm_only is set to False, this sets the type of particle to be</pre>
<p><a href="https://bitbucket.org/yt_analysis/yt/commits/4cb2ccc2ff3c/">https://bitbucket.org/yt_analysis/yt/commits/4cb2ccc2ff3c/</a> Changeset:   4cb2ccc2ff3c Branch:      yt User:        brittonsmith Date:        2016-03-31 13:51:07+00:00 Summary:     Merging. Affected #:  3 files</p>
<p>diff -r 02e42b39f5de3bd266e103eb7df7bbf0e390ad64 -r 4cb2ccc2ff3c145dd52c8405d3894dc1a15bcebb doc/source/analyzing/analysis_modules/halo_finders.rst --- a/doc/source/analyzing/analysis_modules/halo_finders.rst +++ b/doc/source/analyzing/analysis_modules/halo_finders.rst @@ -41,11 +41,11 @@</p>
<pre>   depending on the user-supplied over density
   threshold parameter. The default is 160.0.
</pre>
<p>-Please see the `HOP method paper -<<a href="http://adsabs.harvard.edu/abs/1998ApJ…498..137E">http://adsabs.harvard.edu/abs/1998ApJ…498..137E</a>>`_ for -full details and the -:class:`~yt.analysis_modules.halo_finding.halo_objects.HOPHalo` and -:class:`~yt.analysis_modules.halo_finding.halo_objects.Halo` classes. +Please see the `HOP method paper +<<a href="http://adsabs.harvard.edu/abs/1998ApJ…498..137E">http://adsabs.harvard.edu/abs/1998ApJ…498..137E</a>>`_ for +full details and the +:class:`~yt.analysis_modules.halo_finding.halo_objects.HOPHaloFinder` +documentation.</p>
<pre>.. _fof:
</pre>
<p>@@ -53,8 +53,8 @@</p>
<pre>---

A basic friends-of-friends halo finder is included.  See the</pre>
<p>-:class:`~yt.analysis_modules.halo_finding.halo_objects.FOFHalo` and -:class:`~yt.analysis_modules.halo_finding.halo_objects.Halo` classes. +:class:`~yt.analysis_modules.halo_finding.halo_objects.FOFHaloFinder` +documentation.</p>
<pre>.. _rockstar:
</pre>
<p>diff -r 02e42b39f5de3bd266e103eb7df7bbf0e390ad64 -r 4cb2ccc2ff3c145dd52c8405d3894dc1a15bcebb yt/analysis_modules/halo_finding/halo_objects.py --- a/yt/analysis_modules/halo_finding/halo_objects.py +++ b/yt/analysis_modules/halo_finding/halo_objects.py @@ -59,7 +59,10 @@</p>
<pre>def __init__(self, halo_list, id, indices=None, size=None, CoM=None,
    max_dens_point=None, group_total_mass=None, max_radius=None,</pre>
<ul><li><p>bulk_vel=None, tasks=None, rms_vel=None, supp=None):</p></li></ul>
<p>+        bulk_vel=None, tasks=None, rms_vel=None, supp=None, ptype=None): +        if ptype is None: +            ptype = “all” +        self.ptype = ptype</p>
<pre>self.halo_list = halo_list
self._max_dens = halo_list._max_dens
self.id = id</pre>
<p>@@ -276,10 +279,7 @@</p>
<pre>        return r.max()

    def __getitem__(self, key):</pre>
<ul><li><p>if ytcfg.getboolean("yt", “inline”) is False:</p></li>
<li><p>return self.data[key][self.indices]</p></li>
<li><p>else:</p></li>
<li><p>return self.data[key][self.indices]</p></li></ul>
<p>+        return self.data[(self.ptype, key)][self.indices]</p>
<pre>def get_sphere(self, center_of_mass=True):
    r"""Returns a sphere source.</pre>
<p>@@ -954,7 +954,8 @@</p>
<pre>    _fields = ["particle_position_%s" % ax for ax in 'xyz']
</pre>
<ul><li><p>def __init__(self, data_source, dm_only=True, redshift=-1):</p></li></ul>
<p>+    def __init__(self, data_source, dm_only=True, redshift=-1, +                 ptype=None):</p>
<pre>"""
Run hop on *data_source* with a given density *threshold*.  If
*dm_only* is True (default), only run it on the dark matter particles,</pre>
<p>@@ -963,6 +964,9 @@</p>
<pre>"""
self._data_source = data_source
self.dm_only = dm_only</pre>
<p>+        if ptype is None: +            ptype = “all” +        self.ptype = ptype</p>
<pre>self._groups = []
self._max_dens = {}
self.__obtain_particles()</pre>
<p>@@ -979,14 +983,14 @@</p>
<pre>ii = slice(None)
         self.particle_fields = {}
         for field in self._fields:</pre>
<ul><li><p>tot_part = self._data_source[field].size</p></li></ul>
<p>+            tot_part = self._data_source[(self.ptype, field)].size</p>
<pre>if field == "particle_index":
    self.particle_fields[field] = \</pre>
<ul><li><p>self._data_source[field][ii].astype('int64')</p></li></ul>
<p>+                    self._data_source[(self.ptype, field)][ii].astype('int64')</p>
<pre>else:
    self.particle_fields[field] = \</pre>
<ul><li><p>self._data_source[field][ii].astype('float64')</p></li>
<li><p>del self._data_source[field]</p></li></ul>
<p>+                    self._data_source[(self.ptype, field)][ii].astype('float64') +            del self._data_source[(self.ptype, field)]</p>
<pre>        self._base_indices = np.arange(tot_part)[ii]
        gc.collect()
</pre>
<p>@@ -1014,11 +1018,12 @@</p>
<pre>    cp += counts[i + 1]
    continue
group_indices = grab_indices[cp:cp_c]</pre>
<ul><li><p>self._groups.append(self._halo_class(self, i, group_indices))</p></li></ul>
<p>+            self._groups.append(self._halo_class(self, i, group_indices, +                                                 ptype=self.ptype))</p>
<pre>md_i = np.argmax(dens[cp:cp_c])
px, py, pz = \
    [self.particle_fields['particle_position_%s' % ax][group_indices]</pre>
<ul><li><p>for ax in ‘xyz’]</p></li></ul>
<p>+                 for ax in ‘xyz’]</p>
<pre>self._max_dens[i] = (dens[cp:cp_c][md_i], px[md_i],
    py[md_i], pz[md_i])
cp += counts[i + 1]</pre>
<p>@@ -1260,10 +1265,11 @@</p>
<pre>    _fields = ["particle_position_%s" % ax for ax in 'xyz'] + \
              ["particle_mass"]
</pre>
<ul><li><p>def __init__(self, data_source, threshold=160.0, dm_only=True):</p></li></ul>
<p>+    def __init__(self, data_source, threshold=160.0, dm_only=True, +                 ptype=None):</p>
<pre>self.threshold = threshold
mylog.info("Initializing HOP")</pre>
<ul><li><p>HaloList.__init__(self, data_source, dm_only)</p></li></ul>
<p>+        HaloList.__init__(self, data_source, dm_only, ptype=ptype)</p>
<pre>def _run_finder(self):
    self.densities, self.tags = \</pre>
<p>@@ -1298,10 +1304,12 @@</p>
<pre>    _name = "FOF"
    _halo_class = FOFHalo
</pre>
<ul><li><p>def __init__(self, data_source, link=0.2, dm_only=True, redshift=-1):</p></li></ul>
<p>+    def __init__(self, data_source, link=0.2, dm_only=True, redshift=-1, +                 ptype=None):</p>
<pre>self.link = link
mylog.info("Initializing FOF")</pre>
<ul><li><p>HaloList.__init__(self, data_source, dm_only, redshift=redshift)</p></li></ul>
<p>+        HaloList.__init__(self, data_source, dm_only, redshift=redshift, +                          ptype=ptype)</p>
<pre>def _run_finder(self):
    self.tags = \</pre>
<p>@@ -1450,12 +1458,15 @@</p>
<pre>            halo += 1

class GenericHaloFinder(HaloList, ParallelAnalysisInterface):</pre>
<ul><li><p>def __init__(self, ds, data_source, dm_only=True, padding=0.0):</p></li></ul>
<p>+    def __init__(self, ds, data_source, padding=0.0, ptype=None):</p>
<pre>         ParallelAnalysisInterface.__init__(self)
         self.ds = ds
         self.index = ds.index
         self.center = (np.array(data_source.right_edge) +
np.array(data_source.left_edge)) / 2.0</pre>
<p>+        if ptype is None: +            ptype = “all” +        self.ptype = ptype</p>
<pre>def _parse_halolist(self, threshold_adjustment):
    groups = []</pre>
<p>@@ -1474,7 +1485,7 @@</p>
<pre>    threshold_adjustment
max_dens[hi] = [max_dens_temp] + \
    list(self._max_dens[halo.id])[1:4]</pre>
<ul><li><p>groups.append(self._halo_class(self, hi))</p></li></ul>
<p>+                groups.append(self._halo_class(self, hi, ptype=self.ptype))</p>
<pre>groups[-1].indices = halo.indices
self.comm.claim_object(groups[-1])
hi += 1</pre>
<p>@@ -1505,9 +1516,11 @@</p>
<pre># Note: we already know which halos we own!
after = my_first_id + len(self._groups)
# One single fake halo, not owned, does the trick</pre>
<ul><li><p>self._groups = [self._halo_class(self, i) for i in range(my_first_id)] + \</p></li></ul>
<p>+        self._groups = [self._halo_class(self, i, ptype=self.ptype) +                        for i in range(my_first_id)] + \</p>
<pre>self._groups + \</pre>
<ul><li><p>[self._halo_class(self, i) for i in range(after, nhalos)]</p></li></ul>
<p>+                       [self._halo_class(self, i, ptype=self.ptype) +                        for i in range(after, nhalos)]</p>
<pre>         id = 0
         for proc in sorted(halo_info.keys()):
for halo in self._groups[id:id + halo_info[proc]]:</pre>
<p>@@ -1540,7 +1553,7 @@</p>
<pre>LE, RE = bounds
dw = self.ds.domain_right_edge - self.ds.domain_left_edge
for i, ax in enumerate('xyz'):</pre>
<ul><li><p>arr = self._data_source["particle_position_%s" % ax]</p></li></ul>
<p>+            arr = self._data_source[self.ptype, “particle_position_%s” % ax]</p>
<pre>            arr[arr < LE[i] - self.padding] += dw[i]
            arr[arr > RE[i] + self.padding] -= dw[i]
</pre>
<p>@@ -1671,9 +1684,15 @@</p>
<pre>    to the full volume automatically.
threshold : float
    The density threshold used when building halos. Default = 160.0.</pre>
<ul><li><p>dm_only : bool</p></li></ul>
<p>+    dm_only : bool (deprecated)</p>
<pre>If True, only dark matter particles are used when building halos.</pre>
<p>+        This has been deprecated.  Instead, the ptype keyword should be +        used to specify a particle type.</p>
<pre>Default = True.</pre>
<p>+    ptype : string +        When dm_only is set to False, this sets the type of particle to be +        used for halo finding, with a default of “all”.  This should not be +        used when dm_only is set to True.</p>
<pre>padding : float
    When run in parallel, the finder needs to surround each subvolume
    with duplicated particles for halo finidng to work. This number</pre>
<p>@@ -1697,14 +1716,14 @@</p>
<pre>>>> halos = HaloFinder(ds)
"""
def __init__(self, ds, subvolume=None, threshold=160, dm_only=True,</pre>
<ul><li><p>padding=0.02, total_mass=None):</p></li></ul>
<p>+                 ptype=None, padding=0.02, total_mass=None):</p>
<pre>         if subvolume is not None:
ds_LE = np.array(subvolume.left_edge)
ds_RE = np.array(subvolume.right_edge)
         self.period = ds.domain_right_edge - ds.domain_left_edge
         self._data_source = ds.all_data()</pre>
<ul><li><p>GenericHaloFinder.__init__(self, ds, self._data_source, dm_only,</p></li>
<li><p>padding)</p></li></ul>
<p>+        GenericHaloFinder.__init__(self, ds, self._data_source, padding, +                                   ptype=ptype)</p>
<pre># do it once with no padding so the total_mass is correct
# (no duplicated particles), and on the entire volume, even if only
# a small part is actually going to be used.</pre>
<p>@@ -1712,6 +1731,21 @@</p>
<pre>         padded, LE, RE, self._data_source = \
self.partition_index_3d(ds=self._data_source,
    padding=self.padding)</pre>
<p>+ +        if dm_only: +            mylog.warn("dm_only is deprecated.  " + +                       “Use ptype to specify a particle type, instead.”) + +        # Don't allow dm_only=True and setting a ptype. +        if dm_only and ptype is not None: +            raise RuntimeError( +                “If dm_only is True, ptype must be None.  " + \ +                “dm_only must be False if ptype is set.”) + +        if ptype is None: +            ptype = "all” +        self.ptype = ptype +</p>
<pre>         # For scaling the threshold, note that it's a passthrough
         if total_mass is None:
if dm_only:</pre>
<p>@@ -1719,7 +1753,9 @@</p>
<pre>    total_mass = \
        self.comm.mpi_allreduce((self._data_source['all', "particle_mass"][select].in_units('Msun')).sum(dtype='float64'), op='sum')
else:</pre>
<ul><li><p>total_mass = self.comm.mpi_allreduce(self._data_source.quantities["TotalQuantity"]("particle_mass").in_units('Msun'), op='sum')</p></li></ul>
<p>+                total_mass = self.comm.mpi_allreduce( +                    self._data_source.quantities.total_quantity( +                        (self.ptype, "particle_mass")).in_units('Msun'), op='sum')</p>
<pre># MJT: Note that instead of this, if we are assuming that the particles
# are all on different processors, we should instead construct an
# object representing the entire domain and sum it "lazily" with</pre>
<p>@@ -1743,9 +1779,10 @@</p>
<pre>sub_mass = self._data_source["particle_mass"][select].in_units('Msun').sum(dtype='float64')
         else:
sub_mass = \</pre>
<ul><li><p>self._data_source.quantities["TotalQuantity"]("particle_mass").in_units('Msun')</p></li></ul>
<p>+                self._data_source.quantities.total_quantity( +                    (self.ptype, "particle_mass")).in_units('Msun')</p>
<pre>HOPHaloList.__init__(self, self._data_source,</pre>
<ul><li><p>threshold * total_mass / sub_mass, dm_only)</p></li></ul>
<p>+            threshold * total_mass / sub_mass, dm_only, ptype=self.ptype)</p>
<pre>        self._parse_halolist(total_mass / sub_mass)
        self._join_halolists()
</pre>
<p>@@ -1776,9 +1813,15 @@</p>
<pre>average) used to build the halos. If negative, this is taken to be
the *actual* linking length, and no other calculations will be
applied.  Default = 0.2.</pre>
<ul><li><p>dm_only : bool</p></li></ul>
<p>+    dm_only : bool (deprecated)</p>
<pre>If True, only dark matter particles are used when building halos.</pre>
<p>+        This has been deprecated.  Instead, the ptype keyword should be +        used to specify a particle type.</p>
<pre>Default = True.</pre>
<p>+    ptype : string +        When dm_only is set to False, this sets the type of particle to be +        used for halo finding, with a default of “all”.  This should not be +        used when dm_only is set to True.</p>
<pre>padding : float
    When run in parallel, the finder needs to surround each subvolume
    with duplicated particles for halo finidng to work. This number</pre>
<p>@@ -1791,7 +1834,7 @@</p>
<pre>>>> halos = FOFHaloFinder(ds)
"""
def __init__(self, ds, subvolume=None, link=0.2, dm_only=True,</pre>
<ul><li><p>padding=0.02):</p></li></ul>
<p>+                 ptype=None, padding=0.02):</p>
<pre>         if subvolume is not None:
ds_LE = np.array(subvolume.left_edge)
ds_RE = np.array(subvolume.right_edge)</pre>
<p>@@ -1800,13 +1843,27 @@</p>
<pre>self.index = ds.index
self.redshift = ds.current_redshift
self._data_source = ds.all_data()</pre>
<ul><li><p>GenericHaloFinder.__init__(self, ds, self._data_source, dm_only,</p></li>
<li><p>padding)</p></li></ul>
<p>+        GenericHaloFinder.__init__(self, ds, self._data_source, padding)</p>
<pre>         self.padding = 0.0  # * ds["unitary"] # This should be clevererer
         # get the total number of particles across all procs, with no padding
         padded, LE, RE, self._data_source = \
self.partition_index_3d(ds=self._data_source,
padding=self.padding)</pre>
<p>+ +        if dm_only: +            mylog.warn("dm_only is deprecated.  " + +                       “Use ptype to specify a particle type, instead.”) + +        # Don't allow dm_only=True and setting a ptype. +        if dm_only and ptype is not None: +            raise RuntimeError( +                “If dm_only is True, ptype must be None.  " + \ +                “dm_only must be False if ptype is set.”) + +        if ptype is None: +            ptype = "all” +        self.ptype = ptype +</p>
<pre>         if link > 0.0:
n_parts = self.comm.mpi_allreduce(self._data_source["particle_position_x"].size, op='sum')
# get the average spacing between particles</pre>
<p>@@ -1834,7 +1891,7 @@</p>
<pre># here is where the FOF halo finder is run
mylog.info("Using a linking length of %0.3e", linking_length)
FOFHaloList.__init__(self, self._data_source, linking_length, dm_only,</pre>
<ul><li><p>redshift=self.redshift)</p></li></ul>
<p>+                             redshift=self.redshift, ptype=self.ptype)</p>
<pre>        self._parse_halolist(1.)
        self._join_halolists()
</pre>
<p>diff -r 02e42b39f5de3bd266e103eb7df7bbf0e390ad64 -r 4cb2ccc2ff3c145dd52c8405d3894dc1a15bcebb yt/analysis_modules/halo_finding/tests/test_halo_finders.py --- /dev/null +++ b/yt/analysis_modules/halo_finding/tests/test_halo_finders.py @@ -0,0 +1,61 @@ +""" +Tests for HOP and FOF halo finders. + + + +""" + +#----------------------------------------------------------------------------- +# Copyright © 2016, yt Development Team. +# +# Distributed under the terms of the Modified BSD License. +# +# The full license is in the file COPYING.txt, distributed with this software. +#----------------------------------------------------------------------------- + +from yt.convenience import \ +    load +from yt.data_objects.particle_filters import \ +    add_particle_filter +from yt.analysis_modules.halo_analysis.api import \ +    HaloCatalog +from yt.testing import \ +    requires_file, \ +    assert_array_equal +from yt.utilities.answer_testing.framework import \ +    data_dir_load + +import tempfile +import os +import shutil + +def dm(pfilter, data): +    return data["creation_time"] <= 0. +add_particle_filter("dm", dm, filtered_type='all', +                    requires=["creation_time"]) + +enzotiny = “enzo_tiny_cosmology/DD0046/DD0046” +@requires_file(enzotiny) +def test_datacontainer_data(): +    tmpdir = tempfile.mkdtemp() +    curdir = os.getcwd() +    os.chdir(tmpdir) +    ds = data_dir_load(enzotiny) +    ds.add_particle_filter("dm") + +    for method in ["fof", “hop"]: +        hc = HaloCatalog(data_ds=ds, finder_method=method, +                         output_dir="hc1”, +                         finder_kwargs={"dm_only": True}) +        hc.create() +        hc = HaloCatalog(data_ds=ds, finder_method=method, +                         output_dir="hc2", +                         finder_kwargs={"dm_only": False, “ptype”: “dm"}) +        hc.create() + +        ds1 = load("hc1/hc1.0.h5”) +        ds2 = load("hc2/hc2.0.h5") +        assert_array_equal(ds1.r["particle_mass"], ds2.r["particle_mass"]) + +    os.chdir(curdir) +    shutil.rmtree(tmpdir)</p>
<p>Repository URL: <a href="https://bitbucket.org/yt_analysis/yt/">https://bitbucket.org/yt_analysis/yt/</a></p>
<p>—</p>
<p>This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.</p>

<img src="http://link.bitbucket.org/wf/open?upn=ll4ctv0L-2ByeRZFC1LslHcg6aJmnQ70VruLbmeLQr27DUSfKeD2y3b64JynMEcreOy-2BpLM1RObnGrUI-2FGUSKO0hxJA6gd4ksx9cqlTi0YTmGUcLYhjDiu8vvLQDENBhVHshDj3AdGMnIUsn2zEu-2BzVbvvbxBvY1raO-2F9COmgYwLTDf7AhfYPHW-2BghwCbRTTp4tIwPJHnUk-2BtC95xxOdWUR-2F-2B16k325CxsCv4EuBNogrc-3D" alt="" width="1" height="1" border="0" style="height:1px !important;width:1px !important;border-width:0 !important;margin-top:0 !important;margin-bottom:0 !important;margin-right:0 !important;margin-left:0 !important;padding-top:0 !important;padding-bottom:0 !important;padding-right:0 !important;padding-left:0 !important;"/>
</body></html>