Skip to content

halocat.cosmology

cosmology

Cosmology-parameter table for the DEGRACE-pilot suite.

Maps (gravity, imodel) pairs to the cosmological parameter vector θ consumed by the ξ_AB emulator. Two schemas, gravity-conditional:

  • θ_LCDM = (Omega_m, h, n_s, S_8)
  • θ_fRn1 = (Omega_m, h, n_s, S_8, logf_R0)

Sources: - DEGRACE-pilot 64-row design at the path given by :data:DESIGN_FILE_DEFAULT (cols: Omega_m h n_s S_8 logf_R0). - Fiducial sentinels (imodel ∈ {-1, 0}) hard-coded in :data:FIDUCIAL_THETA.

The design file is referenced, not copied into the halocat repo — its physical location is a config in this module overridable via load_design_table(path=...).

DESIGN_FILE_DEFAULT module-attribute

DESIGN_FILE_DEFAULT = '/cosma8/data/dp203/dc-ruan1/mg_glam/DurMun_hmfemu/wide_sample_first_64.txt'

THETA_KEYS module-attribute

THETA_KEYS: Mapping[str, tuple[str, ...]] = {'LCDM': ('Omega_m', 'h', 'n_s', 'S_8'), 'fRn1': ('Omega_m', 'h', 'n_s', 'S_8', 'logf_R0')}

FIDUCIAL_THETA module-attribute

FIDUCIAL_THETA: dict[tuple[str, int], dict[str, float]] = {('LCDM', 0): {'Omega_m': 0.3089, 'h': 0.6774, 'n_s': 0.9667, 'S_8': 0.8159}, ('fRn1', 0): {'Omega_m': 0.3089, 'h': 0.6774, 'n_s': 0.9667, 'S_8': 0.8159, 'logf_R0': -5.0}, ('fRn1', -1): {'Omega_m': 0.3089, 'h': 0.6774, 'n_s': 0.9667, 'S_8': 0.8159, 'logf_R0': -6.0}}

theta_keys

theta_keys(gravity: str) -> tuple[str, ...]

Return the ordered θ-parameter names for a gravity.

Parameters:

Name Type Description Default
gravity ('LCDM', 'fRn1')
"LCDM"

Returns:

Name Type Description
keys tuple of str

Ordered parameter names. LCDM has 4; fRn1 has 5 (adds logf_R0).

Raises:

Type Description
KeyError

If gravity is not a recognised entry of :data:THETA_KEYS.

Source code in halocat/cosmology.py
def theta_keys(gravity: str) -> tuple[str, ...]:
    """Return the ordered θ-parameter names for a gravity.

    Parameters
    ----------
    gravity : {"LCDM", "fRn1"}

    Returns
    -------
    keys : tuple of str
        Ordered parameter names. LCDM has 4; fRn1 has 5 (adds
        ``logf_R0``).

    Raises
    ------
    KeyError
        If ``gravity`` is not a recognised entry of :data:`THETA_KEYS`.
    """
    if gravity not in THETA_KEYS:
        raise KeyError(
            f"unknown gravity {gravity!r}; expected one of {tuple(THETA_KEYS)}"
        )
    return THETA_KEYS[gravity]

load_design_table

load_design_table(path: str | None = None) -> dict[int, dict[str, float]]

Read the DEGRACE-pilot design table.

Parameters:

Name Type Description Default
path str

Path to the whitespace-separated design file with header line # Omega_m h n_s S_8 logf_R0 and one data row per imodel. Defaults to :data:DESIGN_FILE_DEFAULT.

None

Returns:

Name Type Description
table dict[int, dict[str, float]]

{imodel: {param: value}} for imodel ∈ {1..N} where N is the number of rows in the file (64 for the default file).

Raises:

Type Description
FileNotFoundError

If the path does not exist.

Source code in halocat/cosmology.py
def load_design_table(path: str | None = None) -> dict[int, dict[str, float]]:
    """Read the DEGRACE-pilot design table.

    Parameters
    ----------
    path : str, optional
        Path to the whitespace-separated design file with header line
        ``# Omega_m h n_s S_8 logf_R0`` and one data row per imodel.
        Defaults to :data:`DESIGN_FILE_DEFAULT`.

    Returns
    -------
    table : dict[int, dict[str, float]]
        ``{imodel: {param: value}}`` for ``imodel ∈ {1..N}`` where
        ``N`` is the number of rows in the file (64 for the default
        file).

    Raises
    ------
    FileNotFoundError
        If the path does not exist.
    """
    p = path if path is not None else DESIGN_FILE_DEFAULT
    if not os.path.exists(p):
        raise FileNotFoundError(p)
    arr = np.loadtxt(p, dtype=np.float64)
    if arr.ndim != 2 or arr.shape[1] != 5:
        raise ValueError(
            f"unexpected design-file shape {arr.shape}; "
            f"expected (N, 5) with columns Omega_m h n_s S_8 logf_R0 in {p}"
        )
    out: dict[int, dict[str, float]] = {}
    cols = ("Omega_m", "h", "n_s", "S_8", "logf_R0")
    for i, row in enumerate(arr, start=1):
        out[i] = {k: float(v) for k, v in zip(cols, row)}
    return out

get_cosmology

get_cosmology(gravity: str, imodel: int, *, design_path: str | None = None) -> dict[str, float]

Return θ for one (gravity, imodel) realisation.

For imodel >= 1 the parameters come from the DEGRACE-pilot design table; for imodel ∈ {-1, 0} they come from :data:FIDUCIAL_THETA.

The returned dict has exactly the keys listed in :data:THETA_KEYS[gravity] — for LCDM the design's logf_R0 column is dropped, for fRn1 it is preserved.

Parameters:

Name Type Description Default
gravity ('LCDM', 'fRn1')
"LCDM"
imodel int

-1 or 0 for fiducials; 1..N for the design rows.

required
design_path str

Override the design-file path; passed to :func:load_design_table.

None

Returns:

Name Type Description
theta dict[str, float]

Raises:

Type Description
KeyError

If gravity is not in :data:THETA_KEYS, or if (gravity, imodel) is not in :data:FIDUCIAL_THETA for imodel <= 0, or if imodel is missing from the design table.

Source code in halocat/cosmology.py
def get_cosmology(gravity: str, imodel: int,
                  *, design_path: str | None = None) -> dict[str, float]:
    """Return θ for one ``(gravity, imodel)`` realisation.

    For ``imodel >= 1`` the parameters come from the DEGRACE-pilot
    design table; for ``imodel ∈ {-1, 0}`` they come from
    :data:`FIDUCIAL_THETA`.

    The returned dict has exactly the keys listed in
    :data:`THETA_KEYS[gravity]` — for LCDM the design's ``logf_R0``
    column is dropped, for fRn1 it is preserved.

    Parameters
    ----------
    gravity : {"LCDM", "fRn1"}
    imodel : int
        ``-1`` or ``0`` for fiducials; ``1..N`` for the design rows.
    design_path : str, optional
        Override the design-file path; passed to
        :func:`load_design_table`.

    Returns
    -------
    theta : dict[str, float]

    Raises
    ------
    KeyError
        If ``gravity`` is not in :data:`THETA_KEYS`, or if
        ``(gravity, imodel)`` is not in :data:`FIDUCIAL_THETA` for
        ``imodel <= 0``, or if ``imodel`` is missing from the design
        table.
    """
    keys = theta_keys(gravity)
    if imodel <= 0:
        if (gravity, imodel) not in FIDUCIAL_THETA:
            raise KeyError(
                f"no fiducial θ for (gravity={gravity!r}, imodel={imodel}); "
                f"known: {sorted(FIDUCIAL_THETA)}"
            )
        src = FIDUCIAL_THETA[(gravity, imodel)]
    else:
        table = load_design_table(design_path)
        if imodel not in table:
            raise KeyError(
                f"imodel={imodel} not in design table "
                f"(have {min(table)}..{max(table)})"
            )
        src = table[imodel]
    return {k: float(src[k]) for k in keys}

cosmology_hash

cosmology_hash(theta: Mapping[str, float]) -> str

blake2b-128 hex digest of a canonicalised θ dict.

Keys are sorted; values are formatted to 12 significant digits to avoid spurious cache misses from float-print noise.

Source code in halocat/cosmology.py
def cosmology_hash(theta: Mapping[str, float]) -> str:
    """blake2b-128 hex digest of a canonicalised θ dict.

    Keys are sorted; values are formatted to 12 significant digits to
    avoid spurious cache misses from float-print noise.
    """
    spec = {k: f"{float(theta[k]):.12g}" for k in sorted(theta)}
    payload = json.dumps(spec, sort_keys=True).encode("utf-8")
    return hashlib.blake2b(payload, digest_size=16).hexdigest()