Skip to main content

halogal

Python Version License: MIT

A Python package for modeling UV luminosity functions and galaxy clustering using Halo Occupation Distribution (HOD) models for high-redshift galaxies.

Features

  • Simple API: Only redshift required, scientifically validated defaults
  • UV-Halo Mass Relation (UVHMR): Connect halo mass to UV luminosity through star formation
  • Halo Occupation Distribution: Model central and satellite galaxy populations
  • Luminosity Functions: Compute UV luminosity functions at high redshift
  • Galaxy Bias: Calculate galaxy clustering bias
  • Correlation Functions: Angular, real-space, and projected 2PCFs via halomod
  • Efficient Parameter Updates: All observables accept inline **params for fast MCMC loops
  • Dust Attenuation: Self-consistent treatment following Bouwens+2013-14
  • Fitted Parameters: Defaults from Shuntov+2025
  • Redshift Evolution: Built-in parameter evolution with redshift

Installation

From PyPI (when published)

pip install halogal

From source

git clone https://github.com/mshuntov/halogal.git
cd halogal
pip install -e .

Dependencies

  • numpy >= 1.20
  • scipy >= 1.7
  • astropy >= 5.0
  • colossus >= 1.3
  • halomod >= 1.4

Quick Start

Minimal Example

import numpy as np
from halogal import HODModel
from halogal.model import Observables

# Create model — only redshift required!
# Uses fitted defaults from Shuntov+2025
model = HODModel(z=6.0)
obs = Observables(model)

# Compute luminosity function
MUV = np.linspace(-22, -16, 20)
phi = obs.luminosity_function(MUV)

Galaxy Bias

bias = obs.galaxy_bias(MUV)

UV-Halo Mass Relation

# UVHMR methods live directly on the model
Mh = 1e11  # M_sun
MUV = model.MUV(Mh)
sfr = model.sfr(Mh)

print(f"Halo mass {Mh:.2e} M_sun:")
print(f"  M_UV = {MUV:.2f}")
print(f"  SFR = {sfr:.1f} M_sun/yr")

# Inverse relation
Mh_recovered = model.Mhalo(MUV)

Override Specific Parameters

# At construction
model = HODModel(z=6.0, eps0=0.25, sigma_UV=0.5)
obs = Observables(model)

# Or inline when computing observables — no need to recreate anything
phi = obs.luminosity_function(MUV, eps0=0.3, sigma_UV=0.6)

Efficient MCMC Fitting

All observable methods on Observables accept **params keyword arguments to update the underlying model in-place before computing. This avoids object re-creation in tight loops:

obs = Observables(HODModel(z=6.0))
MUV = np.linspace(-22, -16, 20)

for eps0, sigma_UV in mcmc_samples:
    phi  = obs.luminosity_function(MUV, eps0=eps0, sigma_UV=sigma_UV)
    bg   = obs.galaxy_bias(MUV, eps0=eps0, sigma_UV=sigma_UV)
    mh   = obs.mean_halo_mass(-19.0, eps0=eps0, sigma_UV=sigma_UV)
    ngal = obs.number_density(-19.0, eps0=eps0, sigma_UV=sigma_UV)

For correlation functions, use the initialize/update pattern which leverages halomod's internal caching:

obs = Observables(HODModel(z=6.0))

# Initialize once (expensive)
result = obs.initialize_correlation_model(
    MUV_thresh1=-19.1, correlation_type='angular'
)

# Update efficiently in MCMC loop
for eps0, sigma_UV in mcmc_samples:
    result = obs.update_correlation_model(eps0=eps0, sigma_UV=sigma_UV)
    w_theta = result['correlation']
    theta   = result['separation']

Redshift Evolution

from halogal.models.parametrization import eps0_fz, Mc_fz
from halogal.config import DEFAULT_REDSHIFT_EVOLUTION

z_array = np.linspace(4, 8, 20)

# Get evolved parameters
eps0_z = eps0_fz(
    z_array, 
    deps_dz=DEFAULT_REDSHIFT_EVOLUTION['d_eps0_dz'],
    eps_off=DEFAULT_REDSHIFT_EVOLUTION['C_eps0']
)

Mc_z = 10**Mc_fz(
    z_array,
    dMc_dz=DEFAULT_REDSHIFT_EVOLUTION['d_logMc_dz'],
    Mc_off=DEFAULT_REDSHIFT_EVOLUTION['C_logMc']
)

# Compute with evolved parameters inline
obs = Observables(HODModel(z=z_array[0]))
for z, eps0, Mc in zip(z_array, eps0_z, Mc_z):
    phi = obs.luminosity_function(MUV, eps0=eps0, Mc=Mc)

Compare to Observations

from bouwens21_data import bouwens21, redshift_centers

data = bouwens21['z6']
z_obs = redshift_centers['z6']

obs = Observables(HODModel(z=z_obs))
MUV_model = np.linspace(-23, -15, 50)
phi_model = obs.luminosity_function(MUV_model)

plt.errorbar(data['M_AB'], data['Fi_k'], yerr=data['Fi_k_error'],
            fmt='o', label='Bouwens+2021')
plt.semilogy(MUV_model, phi_model, '-', label='Model')
plt.legend()
plt.show()

Documentation

Full documentation is available at https://uvlf-hod.readthedocs.io/en/latest/#.

Package Structure

halogal/
├── __init__.py          # Public API
├── config.py            # Configuration and defaults
├── cosmology.py         # Halo mass function and bias
├── model.py             # Unified UVHMR, HOD, and Observables
├── luminosity.py        # UV luminosity and dust
└── models/
    └── parametrization.py  # Redshift parametrizations

Key Classes

  • HODModel: Galaxy population model combining UVHMR + HOD (parameters and occupation functions)
  • Observables: Compute observables (UVLF, bias, correlation functions) from an HODModel; supports efficient inline parameter updates via **params
  • UVHMRModel: Base class for UV-halo mass relations only

Model Architecture

UVHMRModel (base class)
├── Handles UV-halo mass relations
├── Methods: sfr(), MUV(), Mhalo(), star_formation_efficiency()
└── Parameters: z (required), eps0, Mc, a, b (optional)

HODModel (extends UVHMRModel)
├── Inherits all UVHMR methods
├── Adds occupation distributions: Ncen(), Nsat(), Ngal()
└── Additional parameters: sigma_UV, Mcut, Msat, asat (optional)

Observables (takes an HODModel)
├── luminosity_function(MUV, **params)
├── galaxy_bias(MUV, **params)
├── mean_halo_mass(MUV_thresh, **params)
├── mean_bias(MUV_thresh, **params)
├── number_density(MUV_thresh, **params)
├── initialize_correlation_model() / update_correlation_model()
└── compute_correlation_function()

All Observables methods accept **params (eps0, Mc, a, b, sigma_UV, Mcut, Msat, asat) to update the model inline before computing.

Default Parameters

All defaults from Shuntov+2025 (2025A&A...699A.231S) at z~5.4:

UVHMR Parameters

Parameter Default Description
eps0 0.19 Star formation efficiency
Mc 10^11.64 M_☉ Characteristic halo mass
a 0.69 Low-mass slope
b 0.65 High-mass slope

HOD Parameters

Parameter Default Description
sigma_UV 0.69 mag UV magnitude scatter
Mcut 10^9.57 M_☉ Satellite cutoff mass
Msat 10^12.65 M_☉ Satellite normalization
asat 0.85 Satellite power-law slope

Redshift Evolution

All parameters evolve as: param(z) = d_param/dz × z + C_param

Evolution parameters available in DEFAULT_REDSHIFT_EVOLUTION.

Examples

See the examples/ directory for detailed examples:

  • basic_usage.ipynb: Interactive Jupyter notebook with complete workflow
  • bouwens21_data.py: Observational data compilation

Testing

Run tests with pytest:

pytest tests/

Contributing

Contributions are welcome! Please:

  1. Fork the repository
  2. Create a feature branch
  3. Add tests for new features
  4. Submit a pull request

Citation

If you use this package in your research, please cite:

@ARTICLE{2025A&A...699A.231S,
       author = {{Shuntov}, Marko and {Oesch}, Pascal A. and {Toft}, Sune and {Meyer}, Romain A. and {Covelo-Paz}, Alba and {Paquereau}, Louise and {Bouwens}, Rychard and {Brammer}, Gabriel and {Gelli}, Viola and {Giovinazzo}, Emma and {Herard-Demanche}, Thomas and {Illingworth}, Garth D. and {Mason}, Charlotte and {Naidu}, Rohan P. and {Weibel}, Andrea and {Xiao}, Mengyuan},
        title = "{Constraints on the early Universe star formation efficiency from galaxy clustering and halo modeling of H{\ensuremath{\alpha}} and [O III] emitters}",
      journal = {\aap},
     keywords = {galaxies: evolution, galaxies: high-redshift, galaxies: luminosity function, mass function, galaxies: statistics, Astrophysics of Galaxies},
         year = 2025,
        month = jul,
       volume = {699},
          eid = {A231},
        pages = {A231},
          doi = {10.1051/0004-6361/202554618},
archivePrefix = {arXiv},
       eprint = {2503.14280},
 primaryClass = {astro-ph.GA},
       adsurl = {https://ui.adsabs.harvard.edu/abs/2025A&A...699A.231S},
      adsnote = {Provided by the SAO/NASA Astrophysics Data System}
}

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

Based on methodology from:

  • Shuntov et al. 2025, A&A 699 A321
  • Sabti et al. 2022
  • Muñoz et al. 2023
  • Bouwens et al. 2013, 2014
  • And others...

Contact

For questions or issues, please open an issue on GitHub or contact marko.shuntov@nbu.ku.dk.

Metadata

Release files for halogal 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for halogal 0.1.0
File Size Uploaded Provenance Actions
halogal-0.1.0.tar.gz 1.5 MB Without provenance Details for halogal-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for halogal 0.1.0
File Interpreter ABI Platform Actions
halogal-0.1.0-py3-none-any.whl Python 3 none any Details for halogal-0.1.0-py3-none-any.whl

Total release size: 2.5 MB

Release files / halogal-0.1.0.tar.gz

Download URL halogal-0.1.0.tar.gz
Size 1.5 MB
Tags Source
SHA-256 checksum
How to use checksums
4d834b96b107557548b9441406e592349b7ed1d469704096aa3bc21f86d22b9e
BLAKE2b-256 checksum
How to use checksums
c4a102c4028b2e09256542856a18d4288491b84b157feea2768f80b8189e7544
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.7.12

Release files / halogal-0.1.0-py3-none-any.whl

Download URL halogal-0.1.0-py3-none-any.whl
Size 1.0 MB
Tags Python 3
SHA-256 checksum
How to use checksums
4301ec52707f14d4bab97add34d6810248a8a21ee10f71adc381b771ede47282
BLAKE2b-256 checksum
How to use checksums
8b3e4badea6f10162cdac3b227ffcaabcf14b31b4b39020cc3df060170fa3636
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/4.0.2 CPython/3.7.12

Security for halogal 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page