<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>