From c2259409c46b1b1f744a7e7185a48a1472bd7930 Mon Sep 17 00:00:00 2001 From: Andrew Hearin Date: Fri, 19 Feb 2021 16:33:16 -0600 Subject: [PATCH 1/9] Pep8 changes to test_zheng07.py module --- .../hod_models/tests/test_zheng07.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/halotools/empirical_models/composite_models/hod_models/tests/test_zheng07.py b/halotools/empirical_models/composite_models/hod_models/tests/test_zheng07.py index 2cddcf469..81e125949 100644 --- a/halotools/empirical_models/composite_models/hod_models/tests/test_zheng07.py +++ b/halotools/empirical_models/composite_models/hod_models/tests/test_zheng07.py @@ -14,8 +14,7 @@ def test_zheng07_composite1(): - """ Ensure that the ``zheng07`` pre-built model does not accept non-Zheng07Cens. - """ + """Ensure that the ``zheng07`` pre-built model does not accept non-Zheng07Cens.""" model1 = PrebuiltHodModelFactory("zheng07") model2 = PrebuiltHodModelFactory("zheng07", modulate_with_cenocc=True) @@ -30,7 +29,7 @@ def test_zheng07_composite1(): def test_zheng07_composite2(): - """ This test ensures that the source code provided in the + """This test ensures that the source code provided in the ``Advanced usage of the ``zheng07`` model`` tutorial behaves as expected. """ @@ -76,7 +75,7 @@ def mean_occupation(self, **kwargs): def test_modulate_with_cenocc1(): - """ Regression test for Issue #646. Verify that the ``modulate_with_cenocc`` + """Regression test for Issue #646. Verify that the ``modulate_with_cenocc`` keyword argument is actually passed to the satellite occupation component. """ model1 = PrebuiltHodModelFactory("zheng07", modulate_with_cenocc=True) @@ -86,7 +85,7 @@ def test_modulate_with_cenocc1(): def test_modulate_with_cenocc2(): - """ Regression test for Issue #646. Verify that the ``modulate_with_cenocc`` + """Regression test for Issue #646. Verify that the ``modulate_with_cenocc`` keyword argument results in behavior that is properly modified by the centrals. """ model = PrebuiltHodModelFactory("zheng07", modulate_with_cenocc=True) @@ -107,8 +106,7 @@ def test_host_centric_distance(): def test_mass_definition_flexibility(): - """ Regression test for Issue #993. - """ + """Regression test for Issue #993.""" model = PrebuiltHodModelFactory( "zheng07", mdef="200m", halo_boundary_key="halo_radius_arbitrary" ) From 0d42df8e319fb50fdcd8f39d5e2f3353956afc5d Mon Sep 17 00:00:00 2001 From: Andrew Hearin Date: Fri, 19 Feb 2021 16:34:12 -0600 Subject: [PATCH 2/9] Added new failing unit test to address #993 --- .../hod_models/tests/test_zheng07.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/halotools/empirical_models/composite_models/hod_models/tests/test_zheng07.py b/halotools/empirical_models/composite_models/hod_models/tests/test_zheng07.py index 81e125949..4d672d54e 100644 --- a/halotools/empirical_models/composite_models/hod_models/tests/test_zheng07.py +++ b/halotools/empirical_models/composite_models/hod_models/tests/test_zheng07.py @@ -116,3 +116,20 @@ def test_mass_definition_flexibility(): halocat.halo_table["halo_mvir"] ) model.populate_mock(halocat, seed=43) + + +def test_mass_definition_flexibility2(): + """Regression test for Issue #993.""" + halocat = FakeSim() + halocat.halo_table["halo_mass_custom"] = np.copy(halocat.halo_table["halo_mvir"]) + halocat.halo_table["halo_radius_custom"] = np.copy(halocat.halo_table["halo_rvir"]) + halocat.halo_table.remove_column("halo_upid") + halocat.halo_table.remove_column("halo_mvir") + halocat.halo_table.remove_column("halo_rvir") + model = PrebuiltHodModelFactory( + "zheng07", + mdef="custom", + prim_haloprop_key="halo_mass_custom", + halo_boundary_key="halo_radius_custom", + ) + model.populate_mock(halocat) From 98e960a151883e4c7dec62875a3f2644b65f9621 Mon Sep 17 00:00:00 2001 From: Andrew Hearin Date: Fri, 19 Feb 2021 16:56:45 -0600 Subject: [PATCH 3/9] Pep8 changes to model_defaults.py --- halotools/empirical_models/model_defaults.py | 49 ++++++++++++-------- 1 file changed, 29 insertions(+), 20 deletions(-) diff --git a/halotools/empirical_models/model_defaults.py b/halotools/empirical_models/model_defaults.py index b4789bbd4..1967b547e 100644 --- a/halotools/empirical_models/model_defaults.py +++ b/halotools/empirical_models/model_defaults.py @@ -4,7 +4,7 @@ """ import numpy as np -__all__ = ['get_halo_boundary_key', 'get_halo_mass_key'] +__all__ = ["get_halo_boundary_key", "get_halo_mass_key"] # Default thresholds for mocks @@ -15,30 +15,38 @@ # Used when executing a Monte Carlo realization of a Poission distribution # whose mean is formally zero, which causes the built-in # scipy method to raise an exception. -default_tiny_poisson_fluctuation = 1.e-20 +default_tiny_poisson_fluctuation = 1.0e-20 default_smhm_scatter = 0.2 -default_smhm_haloprop = 'halo_mpeak' +default_smhm_haloprop = "halo_mpeak" default_binary_galprop_haloprop = default_smhm_haloprop # At minimum, the following halo and galaxy properties # will be bound to each mock galaxy -host_haloprop_prefix = 'halo_' +host_haloprop_prefix = "halo_" -default_haloprop_list_inherited_by_mock = ( - ['halo_id', 'halo_hostid', 'halo_x', 'halo_y', 'halo_z', - 'halo_vx', 'halo_vy', 'halo_vz', - 'halo_mvir', 'halo_rvir', 'halo_upid'] - ) +default_haloprop_list_inherited_by_mock = [ + "halo_id", + "halo_hostid", + "halo_x", + "halo_y", + "halo_z", + "halo_vx", + "halo_vy", + "halo_vz", + "halo_mvir", + "halo_rvir", + "halo_upid", +] -prim_haloprop_key = 'halo_mvir' -sec_haloprop_key = 'halo_nfw_conc' +prim_haloprop_key = "halo_mvir" +sec_haloprop_key = "halo_nfw_conc" -halo_mass_definition = 'vir' +halo_mass_definition = "vir" def get_halo_boundary_key(mdef): - """ For the input mass definition, + """For the input mass definition, return the string used to access halo table column storing the halo radius. @@ -56,11 +64,11 @@ def get_halo_boundary_key(mdef): -------- radius_key : str """ - return 'halo_r'+mdef + return "halo_r" + mdef def get_halo_mass_key(mdef): - """ For the input mass definition, + """For the input mass definition, return the string used to access halo table column storing the halo mass. @@ -78,7 +86,7 @@ def get_halo_mass_key(mdef): -------- mass_key : str """ - return 'halo_m'+mdef + return "halo_m" + mdef # Number of bins to use in the lookup table attached to the NFWProfile. @@ -88,14 +96,15 @@ def get_halo_mass_key(mdef): max_permitted_conc = 20.0 default_conc_gal_bias_bins = np.linspace(0.1, 10, 10) -default_conc_gal_bias_bins = np.insert(default_conc_gal_bias_bins, - np.searchsorted(default_conc_gal_bias_bins, 1), 1) +default_conc_gal_bias_bins = np.insert( + default_conc_gal_bias_bins, np.searchsorted(default_conc_gal_bias_bins, 1), 1 +) Npts_radius_table = 101 default_lograd_min = -3 default_lograd_max = 0 -conc_mass_model = 'direct_from_halo_catalog' -concentration_key = 'halo_nfw_conc' +conc_mass_model = "direct_from_halo_catalog" +concentration_key = "halo_nfw_conc" default_rbins = np.logspace(-1, 1.25, 15) From 106c6d8197cd816d00739d5169a7ea3a198c73ca Mon Sep 17 00:00:00 2001 From: Andrew Hearin Date: Fri, 19 Feb 2021 16:57:09 -0600 Subject: [PATCH 4/9] Changes to profile model template to permit arbitrary mdef --- .../analytic_models/profile_model_template.py | 35 ++++++++++++------- .../satellites/nfw/nfw_profile.py | 28 +++++++++++---- 2 files changed, 43 insertions(+), 20 deletions(-) diff --git a/halotools/empirical_models/phase_space_models/analytic_models/profile_model_template.py b/halotools/empirical_models/phase_space_models/analytic_models/profile_model_template.py index 16831938d..b49528c50 100644 --- a/halotools/empirical_models/phase_space_models/analytic_models/profile_model_template.py +++ b/halotools/empirical_models/phase_space_models/analytic_models/profile_model_template.py @@ -26,7 +26,7 @@ @six.add_metaclass(ABCMeta) class AnalyticDensityProf(object): - r""" Container class for any analytical radial profile model. + r"""Container class for any analytical radial profile model. See :ref:`profile_template_tutorial` for a review of the mathematics of halo profiles, and a thorough description of how the relevant equations @@ -63,16 +63,20 @@ def __init__(self, cosmology, redshift, mdef, halo_boundary_key=None, **kwargs): self.redshift = redshift self.mdef = mdef - # The following four attributes are derived quantities from the above, - # so that self-consistency between them is ensured - self.density_threshold = halo_boundary_functions.density_threshold( - cosmology=self.cosmology, redshift=self.redshift, mdef=self.mdef - ) if halo_boundary_key is None: self.halo_boundary_key = model_defaults.get_halo_boundary_key(self.mdef) else: self.halo_boundary_key = halo_boundary_key - self.prim_haloprop_key = model_defaults.get_halo_mass_key(self.mdef) + # The following four attributes are derived quantities from the above, + # so that self-consistency between them is ensured + if self.mdef == "custom": + self.density_threshold = None + self.prim_haloprop_key = kwargs["prim_haloprop_key"] + else: + self.density_threshold = halo_boundary_functions.density_threshold( + cosmology=self.cosmology, redshift=self.redshift, mdef=self.mdef + ) + self.prim_haloprop_key = model_defaults.get_halo_mass_key(self.mdef) self.gal_prof_param_keys = [] self.halo_prof_param_keys = [] @@ -169,6 +173,12 @@ def mass_density(self, radius, mass, *prof_params): scaled_radius, *prof_params ) + if self.density_threshold is None: + msg = ( + "When using mdef=`custom`, " + "there is no way to compute the SO mass_density" + ) + raise ValueError(msg) density = self.density_threshold * dimensionless_mass return density @@ -282,7 +292,7 @@ def enclosed_mass(self, radius, total_mass, *prof_params): return mass def dimensionless_circular_velocity(self, scaled_radius, *prof_params): - r""" Circular velocity scaled by the virial velocity, + r"""Circular velocity scaled by the virial velocity, :math:`V_{\rm cir}(x) / V_{\rm vir}`, as a function of dimensionless position :math:`\tilde{r} = r / R_{\rm vir}`. @@ -312,7 +322,7 @@ def dimensionless_circular_velocity(self, scaled_radius, *prof_params): ) def virial_velocity(self, total_mass): - r""" The circular velocity evaluated at the halo boundary, + r"""The circular velocity evaluated at the halo boundary, :math:`V_{\rm vir} \equiv \sqrt{GM_{\rm halo}/R_{\rm halo}}`. Parameters @@ -369,13 +379,12 @@ def circular_velocity(self, radius, total_mass, *prof_params): ) * self.virial_velocity(total_mass) def _vmax_helper(self, scaled_radius, *prof_params): - """ Helper function used to calculate `vmax` and `rmax`. - """ + """Helper function used to calculate `vmax` and `rmax`.""" encl = self.cumulative_mass_PDF(scaled_radius, *prof_params) return -1.0 * encl / scaled_radius def rmax(self, total_mass, *prof_params): - r""" Radius at which the halo attains its maximum circular velocity. + r"""Radius at which the halo attains its maximum circular velocity. Parameters ---------- @@ -405,7 +414,7 @@ def rmax(self, total_mass, *prof_params): return result.x[0] * halo_radius def vmax(self, total_mass, *prof_params): - r""" Maximum circular velocity of the halo profile. + r"""Maximum circular velocity of the halo profile. Parameters ---------- diff --git a/halotools/empirical_models/phase_space_models/analytic_models/satellites/nfw/nfw_profile.py b/halotools/empirical_models/phase_space_models/analytic_models/satellites/nfw/nfw_profile.py index c547c9c8e..95e61057a 100644 --- a/halotools/empirical_models/phase_space_models/analytic_models/satellites/nfw/nfw_profile.py +++ b/halotools/empirical_models/phase_space_models/analytic_models/satellites/nfw/nfw_profile.py @@ -25,7 +25,7 @@ class NFWProfile(AnalyticDensityProf): - r""" Model for the spatial distribution of mass + r"""Model for the spatial distribution of mass and/or galaxies residing in an NFW halo profile, based on Navarro, Frenk and White (1995), `arXiv:9508025 `_. @@ -44,6 +44,7 @@ def __init__( conc_mass_model=model_defaults.conc_mass_model, concentration_key=model_defaults.concentration_key, halo_boundary_key=None, + prim_haloprop_key=None, **kwargs ): r""" @@ -60,9 +61,17 @@ def __init__( mdef: str, optional String specifying the halo mass definition, e.g., 'vir' or '200m'. Default is set in `~halotools.empirical_models.model_defaults`. + Use "custom" to allow arbitrary relationshipts between mass and radius. + When mdef is set to "custom", the `prim_haloprop_key` must also be passed. halo_boundary_key : str, optional Default behavior is to use the column associated with the input mdef. + When mdef is set to "custom", any halo boundary is permitted. + When mdef is set to "custom", the `prim_haloprop_key` must also be passed. + + prim_haloprop_key : str, optional + Default behavior is to use the column associated with the input mdef. + When mdef is set to "custom", the `prim_haloprop_key` must also be passed. conc_mass_model : string or callable, optional Specifies the function used to model the relation between @@ -85,7 +94,12 @@ def __init__( >>> nfw = NFWProfile() """ AnalyticDensityProf.__init__( - self, cosmology, redshift, mdef, halo_boundary_key=halo_boundary_key + self, + cosmology, + redshift, + mdef, + halo_boundary_key=halo_boundary_key, + prim_haloprop_key=prim_haloprop_key, ) self.gal_prof_param_keys = ["conc_NFWmodel"] @@ -106,7 +120,7 @@ def _initialize_conc_mass_behavior(self, conc_mass_model, **kwargs): self.conc_mass_model = conc_mass_model def conc_NFWmodel(self, *args, **kwargs): - r""" NFW concentration as a function of halo mass. + r"""NFW concentration as a function of halo mass. Parameters ---------- @@ -387,7 +401,7 @@ def enclosed_mass(self, radius, total_mass, conc): return AnalyticDensityProf.enclosed_mass(self, radius, total_mass, conc) def virial_velocity(self, total_mass): - r""" The circular velocity evaluated at the halo boundary, + r"""The circular velocity evaluated at the halo boundary, :math:`V_{\rm vir} \equiv \sqrt{GM_{\rm halo}/R_{\rm halo}}`. Parameters @@ -455,7 +469,7 @@ def circular_velocity(self, radius, total_mass, conc): return AnalyticDensityProf.circular_velocity(self, radius, total_mass, conc) def rmax(self, total_mass, conc): - r""" Radius at which the halo attains its maximum circular velocity, + r"""Radius at which the halo attains its maximum circular velocity, :math:`R_{\rm max}^{\rm NFW} = 2.16258R_{\Delta}/c`. Parameters @@ -492,7 +506,7 @@ def rmax(self, total_mass, conc): return 2.16258 * scale_radius def vmax(self, total_mass, conc): - r""" Maximum circular velocity of the halo profile, + r"""Maximum circular velocity of the halo profile, :math:`V_{\rm max}^{\rm NFW} = V_{\rm cir}^{\rm NFW}(r = 2.16258R_{\Delta}/c)`. Parameters @@ -577,7 +591,7 @@ def halo_radius_to_halo_mass(self, radius): return AnalyticDensityProf.halo_radius_to_halo_mass(self, radius) def mc_generate_nfw_radial_positions(self, **kwargs): - r""" Return a Monte Carlo realization of points in an NFW profile. + r"""Return a Monte Carlo realization of points in an NFW profile. See :ref:`monte_carlo_nfw_spatial_profile` for a discussion of this technique. From e786fc0a579809cfca616baaeda3e0170caeb74c Mon Sep 17 00:00:00 2001 From: Andrew Hearin Date: Fri, 19 Feb 2021 16:59:15 -0600 Subject: [PATCH 5/9] Pep8 changes to hod_mock_factory.py module --- halotools/empirical_models/factories/hod_mock_factory.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/halotools/empirical_models/factories/hod_mock_factory.py b/halotools/empirical_models/factories/hod_mock_factory.py index 1cab463ea..5b3ab7e7a 100644 --- a/halotools/empirical_models/factories/hod_mock_factory.py +++ b/halotools/empirical_models/factories/hod_mock_factory.py @@ -39,7 +39,7 @@ class HodMockFactory(MockFactory): - """ Class responsible for populating a simulation with a + """Class responsible for populating a simulation with a population of mock galaxies based on an HOD-style model built by the `~halotools.empirical_models.HodModelFactory` class. @@ -101,7 +101,7 @@ def __init__( self.preprocess_halo_catalog(halocat) def preprocess_halo_catalog(self, halocat): - """ Method to pre-process a halo catalog upon instantiation of + """Method to pre-process a halo catalog upon instantiation of the mock object. This pre-processing includes identifying the catalog columns that will be used by the model to create the mock, building lookup tables associated with the halo profile, @@ -401,7 +401,7 @@ def populate(self, seed=None, **kwargs): self.galaxy_table = self.galaxy_table[mask] def allocate_memory(self, seed=None): - """ Method allocates the memory for all the numpy arrays + """Method allocates the memory for all the numpy arrays that will store the information about the mock. These arrays are bound directly to the mock object. @@ -509,7 +509,7 @@ def allocate_memory(self, seed=None): self.galaxy_table[key] = np.zeros(self.Ngals, dtype=dt[key].type) def estimate_ngals(self, seed=None): - """ Method to estimate the number of galaxies produced by the + """Method to estimate the number of galaxies produced by the mock.populate() method. It runs one realization of all mc_occupation methods and reports the total number of galaxies produced. However, no extra memory is allocated for the From 418f5e3e45c45575904676f2b1588157c8be75d3 Mon Sep 17 00:00:00 2001 From: Andrew Hearin Date: Fri, 19 Feb 2021 17:04:57 -0600 Subject: [PATCH 6/9] Changed default behavior of HOD models so that when halo_upid column is missing, all halos assumed to be hosts --- .../factories/hod_mock_factory.py | 6 +- .../factories/tests/test_hod_model_factory.py | 133 +++++++++--------- 2 files changed, 66 insertions(+), 73 deletions(-) diff --git a/halotools/empirical_models/factories/hod_mock_factory.py b/halotools/empirical_models/factories/hod_mock_factory.py index 5b3ab7e7a..fe9094e30 100644 --- a/halotools/empirical_models/factories/hod_mock_factory.py +++ b/halotools/empirical_models/factories/hod_mock_factory.py @@ -122,10 +122,8 @@ def preprocess_halo_catalog(self, halocat): Default is set in `~halotools.empirical_models.model_defaults`. """ - try: - assert "halo_upid" in list(halocat.halo_table.keys()) - except AssertionError: - raise HalotoolsError(missing_halo_upid_msg) + if "halo_upid" not in list(halocat.halo_table.keys()): + halocat.halo_table["halo_upid"] = -1 # Make cuts on halo catalog # # Select host halos only, since this is an HOD-style model diff --git a/halotools/empirical_models/factories/tests/test_hod_model_factory.py b/halotools/empirical_models/factories/tests/test_hod_model_factory.py index 71d61a004..2cea5e0b4 100644 --- a/halotools/empirical_models/factories/tests/test_hod_model_factory.py +++ b/halotools/empirical_models/factories/tests/test_hod_model_factory.py @@ -13,7 +13,7 @@ from ....empirical_models import zheng07_model_dictionary, OccupationComponent from ....custom_exceptions import HalotoolsError -__all__ = ('test_empty_arguments', ) +__all__ = ("test_empty_arguments",) def test_empty_arguments(): @@ -24,29 +24,29 @@ def test_empty_arguments(): def test_Num_ptcl_requirement(): - """ Demonstrate that passing in varying values for + """Demonstrate that passing in varying values for Num_ptcl_requirement results in the proper behavior. """ - model = PrebuiltHodModelFactory('zheng07') + model = PrebuiltHodModelFactory("zheng07") halocat = FakeSim() - actual_mvir_min = halocat.halo_table['halo_mvir'].min() + actual_mvir_min = halocat.halo_table["halo_mvir"].min() model.populate_mock(halocat) - default_mvir_min = model.mock.particle_mass*model.mock.Num_ptcl_requirement + default_mvir_min = model.mock.particle_mass * model.mock.Num_ptcl_requirement # verify that the cut was applied - assert np.all(model.mock.halo_table['halo_mvir'] > default_mvir_min) + assert np.all(model.mock.halo_table["halo_mvir"] > default_mvir_min) # verify that the cut was non-trivial - assert np.any(halocat.halo_table['halo_mvir'] < default_mvir_min) + assert np.any(halocat.halo_table["halo_mvir"] < default_mvir_min) del model.mock - model.populate_mock(halocat, Num_ptcl_requirement=0.) - assert model.mock.Num_ptcl_requirement == 0. - assert np.any(model.mock.halo_table['halo_mvir'] < default_mvir_min) + model.populate_mock(halocat, Num_ptcl_requirement=0.0) + assert model.mock.Num_ptcl_requirement == 0.0 + assert np.any(model.mock.halo_table["halo_mvir"] < default_mvir_min) def test_unavailable_haloprop(): halocat = FakeSim() - m = PrebuiltHodModelFactory('zheng07') + m = PrebuiltHodModelFactory("zheng07") m._haloprop_list.append("Jose Canseco") with pytest.raises(HalotoolsError) as err: m.populate_mock(halocat=halocat) @@ -57,13 +57,11 @@ def test_unavailable_haloprop(): def test_unavailable_upid(): halocat = FakeSim() - del halocat.halo_table['halo_upid'] - m = PrebuiltHodModelFactory('zheng07') + del halocat.halo_table["halo_upid"] + m = PrebuiltHodModelFactory("zheng07") - with pytest.raises(HalotoolsError) as err: - m.populate_mock(halocat=halocat) - substr = "does not have the ``halo_upid`` column." - assert substr in err.value.args[0] + m.populate_mock(halocat=halocat) + assert np.allclose(m.mock.halo_table["halo_upid"], -1) def test_censat_consistency_check(): @@ -81,9 +79,9 @@ def test_censat_consistency_check(): class DummySatComponent(OccupationComponent): def __init__(self): self.central_occupation_model = 43 - self.gal_type = 'satellites' + self.gal_type = "satellites" - model.model_dictionary['dummy_key'] = DummySatComponent() + model.model_dictionary["dummy_key"] = DummySatComponent() with pytest.raises(HalotoolsError) as err: model._test_censat_occupation_consistency(model.model_dictionary) substr = "has a ``central_occupation_model`` attribute with an inconsistent " @@ -91,27 +89,25 @@ def __init__(self): def test_factory_constructor_redshift1(): - """ Verify that redundantly passing in a compatible redshift causes no problems - """ + """Verify that redundantly passing in a compatible redshift causes no problems""" model_dict1 = zheng07_model_dictionary(redshift=1) model1 = HodModelFactory(**model_dict1) assert model1.redshift == 1 model_dict2 = deepcopy(model_dict1) - model_dict2['redshift'] = 1 + model_dict2["redshift"] = 1 model2 = HodModelFactory(**model_dict2) assert model2.redshift == 1 def test_factory_constructor_redshift2(): - """ Verify that passing in an incompatible redshift raises the correct exception - """ + """Verify that passing in an incompatible redshift raises the correct exception""" model_dict1 = zheng07_model_dictionary(redshift=1) model1 = HodModelFactory(**model_dict1) assert model1.redshift == 1 model_dict2 = deepcopy(model_dict1) - model_dict2['redshift'] = 2 + model_dict2["redshift"] = 2 # with pytest.raises(AssertionError) as err: model2 = HodModelFactory(**model_dict2) @@ -120,141 +116,140 @@ def test_factory_constructor_redshift2(): def test_factory_constructor_redshift3(): - """ Verify correct redshift behavior when using the baseline_model_instance argument - """ + """Verify correct redshift behavior when using the baseline_model_instance argument""" model_dict1 = zheng07_model_dictionary(redshift=1) model1 = HodModelFactory(**model_dict1) - model2 = HodModelFactory(baseline_model_instance=model1, - redshift=model1.redshift) + model2 = HodModelFactory(baseline_model_instance=model1, redshift=model1.redshift) assert model2.redshift == model1.redshift with pytest.raises(AssertionError) as err: - model3 = HodModelFactory(baseline_model_instance=model1, - redshift=model1.redshift+1) + model3 = HodModelFactory( + baseline_model_instance=model1, redshift=model1.redshift + 1 + ) substr = "that is inconsistent with the redshift z =" assert substr in err.value.args[0] def test_factory_constructor_redshift4(): - """ Verify correct redshift behavior when the model dictionary + """Verify correct redshift behavior when the model dictionary has no components with redshifts defined """ model_dict_no_redshift = zheng07_model_dictionary(redshift=1) for component_model in model_dict_no_redshift.values(): del component_model.redshift - model_dict_no_redshift['redshift'] = 1.5 + model_dict_no_redshift["redshift"] = 1.5 model2 = HodModelFactory(**model_dict_no_redshift) assert model2.redshift == 1.5 def test_raises_appropriate_exception1(): - """ - """ + """""" model_dict_no_redshift = zheng07_model_dictionary() model = HodModelFactory(**model_dict_no_redshift) with pytest.raises(HalotoolsError) as err: - model.build_model_feature_calling_sequence({'model_feature_calling_sequence': (4, 5)}) - substr = "does not appear in the keyword arguments you passed to the HodModelFactory" + model.build_model_feature_calling_sequence( + {"model_feature_calling_sequence": (4, 5)} + ) + substr = ( + "does not appear in the keyword arguments you passed to the HodModelFactory" + ) assert substr in err.value.args[0] def test_raises_appropriate_exception2(): - """ - """ + """""" model_dict_no_redshift = zheng07_model_dictionary() model = HodModelFactory(**model_dict_no_redshift) with pytest.raises(HalotoolsError) as err: - model._test_model_feature_calling_sequence_consistency( - ['abc'], model.gal_types) + model._test_model_feature_calling_sequence_consistency(["abc"], model.gal_types) substr = "input ``model_feature_calling_sequence`` has a ``abc`` element" assert substr in err.value.args[0] def test_raises_appropriate_exception3(): - """ - """ + """""" model_dict_no_redshift = zheng07_model_dictionary() model = HodModelFactory(**model_dict_no_redshift) - model_dictionary_key = 'Air Bud 4: Seventh Inning Fetch' + model_dictionary_key = "Air Bud 4: Seventh Inning Fetch" gal_type_list = model.gal_types - known_gal_type = 'Josh' + known_gal_type = "Josh" with pytest.raises(HalotoolsError) as err: model._infer_gal_type_and_feature_name( - model_dictionary_key, gal_type_list, known_gal_type=known_gal_type) + model_dictionary_key, gal_type_list, known_gal_type=known_gal_type + ) substr = "The first substring of each key of the ``model_dictionary``" assert substr in err.value.args[0] def test_raises_appropriate_exception4(): - """ - """ + """""" model_dict_no_redshift = zheng07_model_dictionary() model = HodModelFactory(**model_dict_no_redshift) - model_dictionary_key = 'Air Bud 4: Seventh Inning Fetch'.lower() + model_dictionary_key = "Air Bud 4: Seventh Inning Fetch".lower() gal_type_list = model.gal_types - known_gal_type = 'Air Bud 4'.lower() + known_gal_type = "Air Bud 4".lower() with pytest.raises(HalotoolsError) as err: model._infer_gal_type_and_feature_name( - model_dictionary_key, gal_type_list, known_gal_type=known_gal_type) + model_dictionary_key, gal_type_list, known_gal_type=known_gal_type + ) substr = "The model_dictionary key ``air bud 4: seventh inning fetch`` must be comprised of" assert substr in err.value.args[0] def test_raises_appropriate_exception5(): - """ - """ + """""" model_dict_no_redshift = zheng07_model_dictionary() model = HodModelFactory(**model_dict_no_redshift) - model_dictionary_key = 'Air Bud_: The Dog is in the House'.lower() + model_dictionary_key = "Air Bud_: The Dog is in the House".lower() gal_type_list = model.gal_types with pytest.raises(HalotoolsError) as err: model._infer_gal_type_and_feature_name( - model_dictionary_key, gal_type_list, - known_feature_name="He Sits, He Stays, He Shoots, He Scores") + model_dictionary_key, + gal_type_list, + known_feature_name="He Sits, He Stays, He Shoots, He Scores", + ) substr = "The second substring of each key of the ``model_dictionary`` " assert substr in err.value.args[0] def test_raises_appropriate_exception6(): - """ - """ + """""" model_dict_no_redshift = zheng07_model_dictionary() model = HodModelFactory(**model_dict_no_redshift) - model_dictionary_key = 'Air Bud_: The Dog is in the House'.lower() + model_dictionary_key = "Air Bud_: The Dog is in the House".lower() gal_type_list = model.gal_types with pytest.raises(HalotoolsError) as err: model._infer_gal_type_and_feature_name( - model_dictionary_key, gal_type_list, - known_feature_name="The Dog is in the House".lower()) + model_dictionary_key, + gal_type_list, + known_feature_name="The Dog is in the House".lower(), + ) substr = "the ``gal_type`` and ``feature_name`` substrings, separated by a '_', in that order" assert substr in err.value.args[0] def test_raises_appropriate_exception7(): - """ - """ + """""" model_dict_no_redshift = zheng07_model_dictionary() model = HodModelFactory(**model_dict_no_redshift) - model_dictionary_key = 'Air Bud_: The Dog is in the House'.lower() - gal_type_list = ['Air', 'Bud'] + model_dictionary_key = "Air Bud_: The Dog is in the House".lower() + gal_type_list = ["Air", "Bud"] with pytest.raises(HalotoolsError) as err: - model._infer_gal_type_and_feature_name( - model_dictionary_key, gal_type_list) + model._infer_gal_type_and_feature_name(model_dictionary_key, gal_type_list) substr = "The ``_infer_gal_type_and_feature_name`` method was unable to identify" assert substr in err.value.args[0] def test_raises_appropriate_exception8(): - """ - """ + """""" model_dict_no_redshift = zheng07_model_dictionary() model = HodModelFactory(**model_dict_no_redshift) From 2b642246b6f7fb77e09c6e468f3526f0b24d2660 Mon Sep 17 00:00:00 2001 From: Andrew Hearin Date: Fri, 19 Feb 2021 17:16:50 -0600 Subject: [PATCH 7/9] Pep8 changes to subhalo mock factory --- .../factories/subhalo_mock_factory.py | 65 +++++++++++-------- 1 file changed, 38 insertions(+), 27 deletions(-) diff --git a/halotools/empirical_models/factories/subhalo_mock_factory.py b/halotools/empirical_models/factories/subhalo_mock_factory.py index 9ff31af66..1e3445695 100644 --- a/halotools/empirical_models/factories/subhalo_mock_factory.py +++ b/halotools/empirical_models/factories/subhalo_mock_factory.py @@ -19,15 +19,17 @@ from ...custom_exceptions import HalotoolsError -__all__ = ['SubhaloMockFactory'] -__author__ = ['Andrew Hearin'] +__all__ = ["SubhaloMockFactory"] +__author__ = ["Andrew Hearin"] -unavailable_haloprop_msg = ("Your model requires that the ``%s`` key appear in the halo catalog,\n" - "but this column is not available in the catalog you attempted to populate.\n") +unavailable_haloprop_msg = ( + "Your model requires that the ``%s`` key appear in the halo catalog,\n" + "but this column is not available in the catalog you attempted to populate.\n" +) class SubhaloMockFactory(MockFactory): - """ Class responsible for populating a simulation with a + """Class responsible for populating a simulation with a population of mock galaxies based on models generated by `~halotools.empirical_models.SubhaloModelFactory`. @@ -61,23 +63,25 @@ def __init__(self, **kwargs): """ MockFactory.__init__(self, **kwargs) - halocat = kwargs['halocat'] + halocat = kwargs["halocat"] # Pre-compute any additional halo properties required by the model self.preprocess_halo_catalog(halocat) self.precompute_galprops() def preprocess_halo_catalog(self, halocat): - """ Method to pre-process a halo catalog upon instantiation of the mock object. - """ + """Method to pre-process a halo catalog upon instantiation of the mock object.""" halo_table = halocat.halo_table - if (('halo_hostid' not in self.additional_haloprops) & ('halo_hostid' in list(halo_table.keys()))): - self.additional_haloprops.append('halo_hostid') + if ("halo_hostid" not in self.additional_haloprops) & ( + "halo_hostid" in list(halo_table.keys()) + ): + self.additional_haloprops.append("halo_hostid") - if (('halo_mvir_host_halo' not in self.additional_haloprops) & - ('halo_mvir_host_halo' in list(halo_table.keys()))): - self.additional_haloprops.append('halo_mvir_host_halo') + if ("halo_mvir_host_halo" not in self.additional_haloprops) & ( + "halo_mvir_host_halo" in list(halo_table.keys()) + ): + self.additional_haloprops.append("halo_mvir_host_halo") self.halo_table = Table() for key in self.additional_haloprops: @@ -87,7 +91,7 @@ def preprocess_halo_catalog(self, halocat): raise HalotoolsError(unavailable_haloprop_msg % key) def precompute_galprops(self): - """ Method pre-processes the input subhalo catalog, and pre-computes + """Method pre-processes the input subhalo catalog, and pre-computes all halo properties that will be inherited by the ``galaxy_table``. For example, in subhalo-based models, the phase space coordinates of the @@ -107,13 +111,15 @@ def precompute_galprops(self): self.galaxy_table[key] = self.halo_table[key] self._precomputed_galprop_list.append(key) - phase_space_keys = ['x', 'y', 'z', 'vx', 'vy', 'vz'] + phase_space_keys = ["x", "y", "z", "vx", "vy", "vz"] for newkey in phase_space_keys: - self.galaxy_table[newkey] = self.galaxy_table[model_defaults.host_haloprop_prefix+newkey] + self.galaxy_table[newkey] = self.galaxy_table[ + model_defaults.host_haloprop_prefix + newkey + ] self._precomputed_galprop_list.append(newkey) - self.galaxy_table['galid'] = np.arange(len(self.galaxy_table)) - self._precomputed_galprop_list.append('galid') + self.galaxy_table["galid"] = np.arange(len(self.galaxy_table)) + self._precomputed_galprop_list.append("galid") # Component models may explicitly distinguish between certain types of halos, # e.g., subhalos vs. host halos. Since this assignment is not dynamic, @@ -122,21 +128,24 @@ def precompute_galprops(self): try: f = component_model.gal_type_func - newkey = feature + '_gal_type' + newkey = feature + "_gal_type" self.galaxy_table[newkey] = f(table=self.galaxy_table) self._precomputed_galprop_list.append(newkey) except AttributeError: pass except: clname = component_model.__class__.__name__ - msg = ("\nThe `gal_type_func` attribute of the " + clname + - "\nraises an unexpected exception when passed a halo table as a " + msg = ( + "\nThe `gal_type_func` attribute of the " + + clname + + "\nraises an unexpected exception when passed a halo table as a " "table keyword argument. \n" "If the features in your component model have explicit dependence " "on galaxy type, \nthen you must implement the `gal_type_func` mechanism " "in such a way that\nthis function accepts a " "length-N halo table as a ``table`` keyword argument, \n" - "and returns a length-N array of strings.\n") + "and returns a length-N array of strings.\n" + ) raise HalotoolsError(msg) def populate(self, seed=None): @@ -237,17 +246,19 @@ def populate(self, seed=None): seed += 1 func(table=self.galaxy_table, seed=seed) - if hasattr(self.model, 'galaxy_selection_func'): + if hasattr(self.model, "galaxy_selection_func"): mask = self.model.galaxy_selection_func(self.galaxy_table) self.galaxy_table = self.galaxy_table[mask] def _allocate_memory(self, seed=None): - """ - """ + """""" Ngals = len(self.galaxy_table) - new_column_generator = (key for key in self.model._galprop_dtypes_to_allocate.names - if key not in self._precomputed_galprop_list) + new_column_generator = ( + key + for key in self.model._galprop_dtypes_to_allocate.names + if key not in self._precomputed_galprop_list + ) for key in new_column_generator: dt = self.model._galprop_dtypes_to_allocate[key] From 2100bb2d983c8c93a74e383675589648c3ddcaf6 Mon Sep 17 00:00:00 2001 From: Andrew Hearin Date: Fri, 19 Feb 2021 17:19:48 -0600 Subject: [PATCH 8/9] Removed halo_upid column from default_haloprop_list_inherited_by_mock --- halotools/empirical_models/factories/subhalo_mock_factory.py | 2 ++ .../empirical_models/factories/tests/test_hod_model_factory.py | 1 - halotools/empirical_models/model_defaults.py | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/halotools/empirical_models/factories/subhalo_mock_factory.py b/halotools/empirical_models/factories/subhalo_mock_factory.py index 1e3445695..a678187e6 100644 --- a/halotools/empirical_models/factories/subhalo_mock_factory.py +++ b/halotools/empirical_models/factories/subhalo_mock_factory.py @@ -73,6 +73,8 @@ def preprocess_halo_catalog(self, halocat): """Method to pre-process a halo catalog upon instantiation of the mock object.""" halo_table = halocat.halo_table + self.additional_haloprops.append("halo_upid") + if ("halo_hostid" not in self.additional_haloprops) & ( "halo_hostid" in list(halo_table.keys()) ): diff --git a/halotools/empirical_models/factories/tests/test_hod_model_factory.py b/halotools/empirical_models/factories/tests/test_hod_model_factory.py index 2cea5e0b4..3017a35d7 100644 --- a/halotools/empirical_models/factories/tests/test_hod_model_factory.py +++ b/halotools/empirical_models/factories/tests/test_hod_model_factory.py @@ -61,7 +61,6 @@ def test_unavailable_upid(): m = PrebuiltHodModelFactory("zheng07") m.populate_mock(halocat=halocat) - assert np.allclose(m.mock.halo_table["halo_upid"], -1) def test_censat_consistency_check(): diff --git a/halotools/empirical_models/model_defaults.py b/halotools/empirical_models/model_defaults.py index 1967b547e..8fc67120f 100644 --- a/halotools/empirical_models/model_defaults.py +++ b/halotools/empirical_models/model_defaults.py @@ -36,7 +36,6 @@ "halo_vz", "halo_mvir", "halo_rvir", - "halo_upid", ] prim_haloprop_key = "halo_mvir" From 7cfacdc9531fae6707f0cc5bc4b4acb2f54ee72c Mon Sep 17 00:00:00 2001 From: Andrew Hearin Date: Fri, 19 Feb 2021 17:49:52 -0600 Subject: [PATCH 9/9] Added dedicated regression testing module for convenience --- .../tests/test_github_993_regression.py | 25 +++++++++++++++++++ .../hod_models/tests/test_zheng07.py | 17 ------------- halotools/empirical_models/model_defaults.py | 2 -- 3 files changed, 25 insertions(+), 19 deletions(-) create mode 100644 halotools/empirical_models/composite_models/hod_models/tests/test_github_993_regression.py diff --git a/halotools/empirical_models/composite_models/hod_models/tests/test_github_993_regression.py b/halotools/empirical_models/composite_models/hod_models/tests/test_github_993_regression.py new file mode 100644 index 000000000..65925b771 --- /dev/null +++ b/halotools/empirical_models/composite_models/hod_models/tests/test_github_993_regression.py @@ -0,0 +1,25 @@ +""" +""" +from .....sim_manager import FakeSim +from ....factories import PrebuiltHodModelFactory +import numpy as np + + +def test_mass_definition_flexibility2(): + """Regression test for Issue #993.""" + halocat = FakeSim() + halocat.halo_table["halo_mass_custom"] = np.copy(halocat.halo_table["halo_mvir"]) + halocat.halo_table["halo_radius_custom"] = np.copy(halocat.halo_table["halo_rvir"]) + halocat.halo_table.remove_column("halo_upid") + halocat.halo_table.remove_column("halo_mvir") + halocat.halo_table.remove_column("halo_rvir") + model = PrebuiltHodModelFactory( + "zheng07", + mdef="custom", + halo_mass_column_key="halo_mass_custom", + prim_haloprop_key="halo_mass_custom", + halo_boundary_key="halo_radius_custom", + ) + assert model.halo_boundary_key == "halo_mass_custom" + assert model.prim_haloprop_key == "halo_mass_custom" + model.populate_mock(halocat) diff --git a/halotools/empirical_models/composite_models/hod_models/tests/test_zheng07.py b/halotools/empirical_models/composite_models/hod_models/tests/test_zheng07.py index 4d672d54e..81e125949 100644 --- a/halotools/empirical_models/composite_models/hod_models/tests/test_zheng07.py +++ b/halotools/empirical_models/composite_models/hod_models/tests/test_zheng07.py @@ -116,20 +116,3 @@ def test_mass_definition_flexibility(): halocat.halo_table["halo_mvir"] ) model.populate_mock(halocat, seed=43) - - -def test_mass_definition_flexibility2(): - """Regression test for Issue #993.""" - halocat = FakeSim() - halocat.halo_table["halo_mass_custom"] = np.copy(halocat.halo_table["halo_mvir"]) - halocat.halo_table["halo_radius_custom"] = np.copy(halocat.halo_table["halo_rvir"]) - halocat.halo_table.remove_column("halo_upid") - halocat.halo_table.remove_column("halo_mvir") - halocat.halo_table.remove_column("halo_rvir") - model = PrebuiltHodModelFactory( - "zheng07", - mdef="custom", - prim_haloprop_key="halo_mass_custom", - halo_boundary_key="halo_radius_custom", - ) - model.populate_mock(halocat) diff --git a/halotools/empirical_models/model_defaults.py b/halotools/empirical_models/model_defaults.py index 8fc67120f..8ca09a139 100644 --- a/halotools/empirical_models/model_defaults.py +++ b/halotools/empirical_models/model_defaults.py @@ -34,8 +34,6 @@ "halo_vx", "halo_vy", "halo_vz", - "halo_mvir", - "halo_rvir", ] prim_haloprop_key = "halo_mvir"