Skip to content

cosmotron_mcp.tools.covariance

compute_full_covariance_from_session

compute_full_covariance_from_session(session_dir: str, galaxy_bias: float = 1.5, cl_theory_mode: str = 'ccl', cosmo_params: dict | None = None, marginalise_sigma_m: bool = True, workspace_cache_scope: str = 'session', jobspec: dict | None = None) -> dict

Compute the full multi-tracer Gaussian covariance for a tomographic session.

Calls the multi-tracer compute_covariance(tracer_configs=[...]) API for all N tomographic bins simultaneously, producing a joint (N_pairs×n_bpw)² covariance matrix saved as results/covariance/covariance_all_bins.json, where N_pairs = N*(N+1)//2.

Use this instead of calling compute_covariance_from_session per bin when you need a full cross-bin covariance for tomographic SACC assembly. The existing compute_covariance_from_session (per-bin, block-diagonal) remains available for single-bin sessions.

Prerequisites: compute_all_cls_from_session must have run (auto-Cl files cls_bin_{i:02d}_x_{i:02d}.json must exist for every bin).

marginalise_sigma_m: when True (default) and any spin-2 bin carries a manifest sigma_m, add the analytic multiplicative-bias marginalisation term (2110.06947 Eq. 20 ≡ Cosmotheka m_marg) to the covariance. The multiplicative bias m itself is applied to the DATA at ingest (Eq. 2), so the covariance theory Cℓ stays plain; only sigma_m enters, as this additive term (kept separate so the Knox check validates the Gaussian part). No sigma_m set ⇒ no term ⇒ byte-identical to a pre-calibration covariance.

workspace_cache_scope: "session" (default) or "registry" — see compute_cls_from_session; a pure cache-location choice.

Parameters:

Name Type Description Default
session_dir str

Session directory (all N tomographic bins' auto-Cl files must already exist).

required
galaxy_bias float

Forwarded to compute_covariance.

1.5
cl_theory_mode str

Forwarded to compute_covariance.

'ccl'
cosmo_params dict | None

Forwarded to compute_covariance.

None
marginalise_sigma_m bool

Add the analytic multiplicative-bias marginalisation term for spin-2 bins carrying a manifest sigma_m (see the prose above).

True
workspace_cache_scope str

"session" (default) or "registry".

'session'
jobspec dict | None

Optional {"backend": "slurm", "site": "<site>", ...} to run this as a non-blocking remote job instead of a local one. On submit, returns immediately {job_id, status, mode: "async", site, resource_class, next: "check_job"} — no covariance numbers. Poll with check_job(session_dir, job_id). This ALWAYS runs as a non-blocking job — omitting jobspec does not mean synchronous; it means a local background job (same async receipt shape, backend: "local"). Pass jobspec only to pick Slurm instead of local, or to forward local resource options.

None

Returns:

Type Description
dict

An async receipt ``{job_id, status, mode: "async", run_dir,

dict

session_dir, next: "check_job", ...}`` — never the covariance

dict

numbers. Poll check_job(session_dir, job_id); once

dict

status == "done", its result carries ``{session_dir, n_bins,

dict

n_pairs, pair_labels, output_path, covariance, sqrt_diag, ells}``.

Source code in cosmotron_mcp/server.py
@mcp.tool()
@sync_budget_guard
def compute_full_covariance_from_session(
    session_dir: str,
    galaxy_bias: float = 1.5,
    cl_theory_mode: str = "ccl",
    cosmo_params: dict | None = None,
    marginalise_sigma_m: bool = True,
    workspace_cache_scope: str = "session",
    jobspec: dict | None = None,
) -> dict:
    """Compute the full multi-tracer Gaussian covariance for a tomographic session.

    Calls the multi-tracer `compute_covariance(tracer_configs=[...])` API for all
    N tomographic bins simultaneously, producing a joint (N_pairs×n_bpw)²
    covariance matrix saved as `results/covariance/covariance_all_bins.json`, where
    N_pairs = N*(N+1)//2.

    Use this instead of calling `compute_covariance_from_session` per bin when you
    need a full cross-bin covariance for tomographic SACC assembly. The existing
    `compute_covariance_from_session` (per-bin, block-diagonal) remains available
    for single-bin sessions.

    Prerequisites: `compute_all_cls_from_session` must have run (auto-Cl files
    `cls_bin_{i:02d}_x_{i:02d}.json` must exist for every bin).

    marginalise_sigma_m: when True (default) and any spin-2 bin carries a manifest
    `sigma_m`, add the analytic multiplicative-bias marginalisation term
    (2110.06947 Eq. 20 ≡ Cosmotheka `m_marg`) to the covariance. The multiplicative
    bias `m` itself is applied to the DATA at ingest (Eq. 2), so the covariance
    theory Cℓ stays plain; only `sigma_m` enters, as this additive term (kept
    separate so the Knox check validates the Gaussian part). No `sigma_m` set ⇒ no
    term ⇒ byte-identical to a pre-calibration covariance.

    workspace_cache_scope: "session" (default) or "registry" — see
    `compute_cls_from_session`; a pure cache-location choice.

    Args:
        session_dir: Session directory (all N tomographic bins' auto-Cl
            files must already exist).
        galaxy_bias: Forwarded to `compute_covariance`.
        cl_theory_mode: Forwarded to `compute_covariance`.
        cosmo_params: Forwarded to `compute_covariance`.
        marginalise_sigma_m: Add the analytic multiplicative-bias
            marginalisation term for spin-2 bins carrying a manifest
            ``sigma_m`` (see the prose above).
        workspace_cache_scope: ``"session"`` (default) or ``"registry"``.
        jobspec: Optional ``{"backend": "slurm", "site": "<site>", ...}`` to
            run this as a non-blocking remote job instead of a local one. On
            submit, returns immediately ``{job_id, status, mode: "async",
            site, resource_class, next: "check_job"}`` — no covariance
            numbers. Poll with ``check_job(session_dir, job_id)``. This
            ALWAYS runs as a non-blocking job — omitting `jobspec` does not
            mean synchronous; it means a local background job (same async
            receipt shape, `backend: "local"`). Pass `jobspec` only to pick
            Slurm instead of local, or to forward local resource options.

    Returns:
        An async receipt ``{job_id, status, mode: "async", run_dir,
        session_dir, next: "check_job", ...}`` — never the covariance
        numbers. Poll `check_job(session_dir, job_id)`; once
        `status == "done"`, its `result` carries ``{session_dir, n_bins,
        n_pairs, pair_labels, output_path, covariance, sqrt_diag, ells}``.
    """
    _require_human_gates(session_dir)
    kwargs = {
        "galaxy_bias": galaxy_bias, "cl_theory_mode": cl_theory_mode,
        "cosmo_params": cosmo_params, "marginalise_sigma_m": marginalise_sigma_m,
        "workspace_cache_scope": workspace_cache_scope,
    }
    from cosmotron_mcp.remote.dispatch import route_heavy_tool
    return route_heavy_tool(
        "compute_full_covariance_from_session", session_dir,
        {"session_dir": session_dir, **kwargs}, jobspec,
    )

compute_validation_covariance_from_session

compute_validation_covariance_from_session(session_dir: str, component: str = 'BB', jobspec: dict | None = None) -> dict

Analytic Gaussian B-mode VALIDATION covariance (component "BB" or "EB").

B/EB modes are validated with their OWN covariance — they never enter the data vector or the EE-only data covariance. For a null signal the variance is noise-dominated (BB: 2·N²/(f_sky·modes); EB: N_EE·N_BB/(f_sky·modes)). Builds a block-diagonal matrix over the spin-2 auto-bins and writes results/covariance/covariance_validation_{BB|EB}.json (artefact type covariance_validation, NOT covariance). Feeds run_bmode_null_tests_from_session. Sessions with no spin-2 bins return {skipped: ...}.

Parameters:

Name Type Description Default
session_dir str

Session directory.

required
component str

"BB" or "EB".

'BB'
jobspec dict | None

Optional {"backend": "slurm", "site": "<site>", ...} to run this as a non-blocking remote job instead of a local one. On submit, returns immediately {job_id, status, mode: "async", site, resource_class, next: "check_job"}. Poll with check_job(session_dir, job_id). This ALWAYS runs as a non-blocking job — omitting jobspec does not mean synchronous; it means a local background job (same async receipt shape, backend: "local"). Pass jobspec only to pick Slurm instead of local.

None

Returns:

Type Description
dict

An async receipt ``{job_id, status, mode: "async", run_dir,

dict

session_dir, next: "check_job", ...}``. Poll

dict

check_job(session_dir, job_id); once status == "done", its

dict

result carries the validation covariance dict, or

dict

{skipped: ...} if the session has no spin-2 bins.

Source code in cosmotron_mcp/server.py
@mcp.tool()
@sync_budget_guard
def compute_validation_covariance_from_session(
    session_dir: str, component: str = "BB", jobspec: dict | None = None,
) -> dict:
    """Analytic Gaussian B-mode VALIDATION covariance (component "BB" or "EB").

    B/EB modes are validated with their OWN covariance — they never enter the data
    vector or the EE-only data covariance. For a null signal the variance is
    noise-dominated (BB: 2·N²/(f_sky·modes); EB: N_EE·N_BB/(f_sky·modes)). Builds a
    block-diagonal matrix over the spin-2 auto-bins and writes
    `results/covariance/covariance_validation_{BB|EB}.json` (artefact type
    `covariance_validation`, NOT `covariance`). Feeds `run_bmode_null_tests_from_session`.
    Sessions with no spin-2 bins return `{skipped: ...}`.

    Args:
        session_dir: Session directory.
        component: ``"BB"`` or ``"EB"``.
        jobspec: Optional ``{"backend": "slurm", "site": "<site>", ...}`` to
            run this as a non-blocking remote job instead of a local one. On
            submit, returns immediately ``{job_id, status, mode: "async",
            site, resource_class, next: "check_job"}``. Poll with
            ``check_job(session_dir, job_id)``. This ALWAYS runs as a
            non-blocking job — omitting `jobspec` does not mean synchronous;
            it means a local background job (same async receipt shape,
            `backend: "local"`). Pass `jobspec` only to pick Slurm instead of
            local.

    Returns:
        An async receipt ``{job_id, status, mode: "async", run_dir,
        session_dir, next: "check_job", ...}``. Poll
        `check_job(session_dir, job_id)`; once `status == "done"`, its
        `result` carries the validation covariance dict, or
        ``{skipped: ...}`` if the session has no spin-2 bins.
    """
    _require_human_gates(session_dir)
    kwargs = {"component": component}
    from cosmotron_mcp.remote.dispatch import route_heavy_tool
    return route_heavy_tool(
        "compute_validation_covariance_from_session", session_dir,
        {"session_dir": session_dir, **kwargs}, jobspec,
    )

compute_covariance

compute_covariance(cls_result: dict | None = None, survey_mask: list | None = None, z: list | None = None, nz: list | None = None, n_bar: float | None = None, nside: int = 512, cosmo_params: dict | None = None, galaxy_bias: float = 1.5, cl_theory_mode: str = 'ccl', workspace_path: str | None = None, covariance_workspace_path: str | None = None, output_path: str | None = None, generated_by: str | None = None, agent_name: str | None = None, tracer_configs: list[dict] | None = None, pairs: list | None = None) -> dict

Compute the Gaussian NMT covariance matrix using TJPCov.

PREFER compute_covariance_from_session (single bin) or compute_full_covariance_from_session (tomographic/3×2pt) — this low-level primitive takes no session_dir and needs every physical input hand-assembled.

Two calling conventions, both returning a dict:

Single-tracer (legacy): compute_covariance(cls_result, survey_mask, z, nz, n_bar=n_bar, nside=nside) Multi-tracer (N > 1 bins or spin-2), keyword-only: compute_covariance(tracer_configs=[{...}, {...}], nside=nside)

Supports spin-0 and spin-2 fields and mixed-spin probe combinations.

Parameters:

Name Type Description Default
cls_result dict | None

Output dict from compute_cls (legacy single-tracer API).

None
survey_mask list | None

HEALPix mask (legacy single-tracer API).

None
z list | None

Redshift distribution grid (legacy single-tracer API).

None
nz list | None

Redshift distribution n(z) values (legacy single-tracer API).

None
n_bar float | None

Mean counts per occupied HEALPix pixel (legacy single-tracer API). Must be > 0.

None
nside int

HEALPix resolution parameter.

512
cosmo_params dict | None

CCL cosmological parameters. Defaults to Planck 2018 ΛCDM.

None
galaxy_bias float

Galaxy bias for the single-tracer legacy path.

1.5
cl_theory_mode str

"ccl" or "measured". In "measured" mode the auto-Cl of each tracer is used as proxy; cross-Cls fall back to CCL.

'ccl'
workspace_path str | None

Optional disk path for caching NaMaster workspaces (single-tracer legacy path only).

None
covariance_workspace_path str | None

Optional disk path for caching the covariance NaMaster workspace (single-tracer legacy path only).

None
output_path str | None

If provided, write the result JSON to this path.

None
generated_by str | None

Infrastructure kwarg — auto-populated from the executor environment.

None
agent_name str | None

Infrastructure kwarg — auto-populated from the executor environment.

None
tracer_configs list[dict] | None

List of tracer config dicts (multi-tracer API). Each dict must have: name, cls_result, z, nz, n_bar, mask, spin (0 or 2), bias. Optional: sigma_e (shear dispersion, default 1.0).

None
pairs list | None

Optional subset of tracer pairs to build (multi-tracer API).

None

Returns:

Name Type Description
dict

Single-tracer (legacy API): dict with keys covariance,

dict

ells, cl_theory_mode, sqrt_diag, cls_theory.

dict

Multi-tracer (tracer_configs API): dict with keys

dict

covariance (shape N_pairs×n_bpw × N_pairs×n_bpw),

dict

pair_labels, ells, cl_theory_mode, sqrt_diag,

dict

cls_theory (dict keyed by pair), n_tracers.

dict

Requires the optional tjpcov and pyccl dependencies (and

dict

sacc for the multi-tracer path); these are imported lazily

dict

inside the function.

dict

This is a HEAVY_TOOLS entry — building the TJPCov Gaussian

dict

covariance (either path) is compute-node work, so it ALWAYS runs as

dict

a non-blocking local background job, never inline (no jobspec

dict

param — session-less, local job routing only). The dict shapes

dict

above describe what lands in result, not what this call itself

returns dict

it returns an async receipt ``{job_id, status, mode:

dict

"async", run_dir, session_dir, next: "check_job", ...}`` —

dict

session_dir here is a fixed shared job root (this tool has no

dict

session of its own), NOT a pipeline session; use the session_dir

dict

the receipt itself returns when polling. Poll

dict

check_job(session_dir, job_id); once status == "done", its

dict

result carries the dict described above.

Source code in cosmotron_mcp/server.py
@mcp.tool()
@sync_budget_guard
def compute_covariance(
    cls_result: dict | None = None,
    survey_mask: list | None = None,
    z: list | None = None,
    nz: list | None = None,
    n_bar: float | None = None,
    nside: int = 512,
    cosmo_params: dict | None = None,
    galaxy_bias: float = 1.5,
    cl_theory_mode: str = "ccl",
    workspace_path: str | None = None,
    covariance_workspace_path: str | None = None,
    output_path: str | None = None,
    generated_by: str | None = None,
    agent_name: str | None = None,
    tracer_configs: list[dict] | None = None,
    pairs: list | None = None,
) -> dict:
    """Compute the Gaussian NMT covariance matrix using TJPCov.

    PREFER `compute_covariance_from_session` (single bin) or
    `compute_full_covariance_from_session` (tomographic/3×2pt) — this
    low-level primitive takes no `session_dir` and needs every physical
    input hand-assembled.

    Two calling conventions, both returning a dict:

    Single-tracer (legacy):
        compute_covariance(cls_result, survey_mask, z, nz, n_bar=n_bar, nside=nside)
    Multi-tracer (N > 1 bins or spin-2), keyword-only:
        compute_covariance(tracer_configs=[{...}, {...}], nside=nside)

    Supports spin-0 and spin-2 fields and mixed-spin probe combinations.

    Args:
        cls_result: Output dict from `compute_cls` (legacy single-tracer API).
        survey_mask: HEALPix mask (legacy single-tracer API).
        z: Redshift distribution grid (legacy single-tracer API).
        nz: Redshift distribution n(z) values (legacy single-tracer API).
        n_bar: Mean counts per occupied HEALPix pixel (legacy single-tracer
            API). Must be > 0.
        nside: HEALPix resolution parameter.
        cosmo_params: CCL cosmological parameters. Defaults to Planck 2018
            ΛCDM.
        galaxy_bias: Galaxy bias for the single-tracer legacy path.
        cl_theory_mode: ``"ccl"`` or ``"measured"``. In ``"measured"`` mode
            the auto-Cl of each tracer is used as proxy; cross-Cls fall back
            to CCL.
        workspace_path: Optional disk path for caching NaMaster workspaces
            (single-tracer legacy path only).
        covariance_workspace_path: Optional disk path for caching the
            covariance NaMaster workspace (single-tracer legacy path only).
        output_path: If provided, write the result JSON to this path.
        generated_by: Infrastructure kwarg — auto-populated from the
            executor environment.
        agent_name: Infrastructure kwarg — auto-populated from the executor
            environment.
        tracer_configs: List of tracer config dicts (multi-tracer API). Each
            dict must have: ``name``, ``cls_result``, ``z``, ``nz``,
            ``n_bar``, ``mask``, ``spin`` (0 or 2), ``bias``. Optional:
            ``sigma_e`` (shear dispersion, default 1.0).
        pairs: Optional subset of tracer pairs to build (multi-tracer API).

    Returns:
        **Single-tracer** (legacy API): ``dict`` with keys ``covariance``,
        ``ells``, ``cl_theory_mode``, ``sqrt_diag``, ``cls_theory``.

        **Multi-tracer** (`tracer_configs` API): ``dict`` with keys
        ``covariance`` (shape N_pairs×n_bpw × N_pairs×n_bpw),
        ``pair_labels``, ``ells``, ``cl_theory_mode``, ``sqrt_diag``,
        ``cls_theory`` (dict keyed by pair), ``n_tracers``.

        Requires the optional ``tjpcov`` and ``pyccl`` dependencies (and
        ``sacc`` for the multi-tracer path); these are imported lazily
        inside the function.

        This is a `HEAVY_TOOLS` entry — building the TJPCov Gaussian
        covariance (either path) is compute-node work, so it ALWAYS runs as
        a non-blocking local background job, never inline (no `jobspec`
        param — session-less, local job routing only). The dict shapes
        above describe what lands in `result`, not what this call itself
        returns: it returns an async receipt ``{job_id, status, mode:
        "async", run_dir, session_dir, next: "check_job", ...}`` —
        `session_dir` here is a fixed shared job root (this tool has no
        session of its own), NOT a pipeline session; use the `session_dir`
        the receipt itself returns when polling. Poll
        `check_job(session_dir, job_id)`; once `status == "done"`, its
        `result` carries the dict described above.
    """
    from cosmotron_mcp import heavy
    from cosmotron_mcp.remote.dispatch import submit_local_job
    kwargs = {
        "cls_result": cls_result, "survey_mask": survey_mask, "z": z, "nz": nz,
        "n_bar": n_bar, "nside": nside, "cosmo_params": cosmo_params,
        "galaxy_bias": galaxy_bias, "cl_theory_mode": cl_theory_mode,
        "workspace_path": workspace_path,
        "covariance_workspace_path": covariance_workspace_path,
        "output_path": output_path, "generated_by": generated_by,
        "agent_name": agent_name, "tracer_configs": tracer_configs,
        "pairs": pairs,
    }
    return submit_local_job("compute_covariance", heavy.ADHOC_JOB_ROOT,
                             kwargs, inject_session_dir=False)

compute_covariance_from_session

compute_covariance_from_session(session_dir: str, bin_index: int = 0, nz_path: str | None = None, galaxy_bias: float = 1.5, cl_theory_mode: str = 'ccl', cosmo_params: dict | None = None, marginalise_sigma_m: bool = True, workspace_cache_scope: str = 'session', jobspec: dict | None = None) -> dict

Compute the Gaussian covariance for one bin straight from a session.

Session-driven wrapper around :func:compute_covariance, mirroring :func:cosmotron.tools.compute_cls_from_session. It assembles every argument from the session on disk so the caller never has to hand-build a survey mask, redshift distribution, or n_bar — the brittle assembly that repeatedly broke the compute_cls → compute_covariance handoff.

Steps

  1. Read pipeline_config (nside, full_sky, apodisation) and the manifest.
  2. Load the bin's results/spectra/cls_bin_{bin_index:02d}_x_{bin_index:02d}.json (must already exist — run compute_cls_from_session first).
  3. Build survey_mask: mask.fits if the manifest references one, else a full-sky map of ones — always a float64 ndarray.
  4. Pull n_bar from manifest[...]["catalogue_stats"]["n_bar"].
  5. Load the calibrated z/nz from the survey n(z) file (explicit nz_path or discovered in data_folder). A catalogue redshift histogram is deliberately NOT used — it gives the wrong theory Cl.
  6. Call :func:compute_covariance, writing results/covariance/covariance_bin_{bin_index:02d}_x_{bin_index:02d}.json and returning a JSON-serialisable dict (lists, not ndarrays).

Parameters:

Name Type Description Default
session_dir str

Session workspace directory (contains manifest.json + session_context.json).

required
bin_index int

Zero-based index into manifest["tomographic_bins"].

0
nz_path str | None

Path to the calibrated 2-column (z, dndz) n(z) file for this bin. If None, it is discovered in session_context["data_folder"].

None
galaxy_bias float

Forwarded to compute_covariance.

1.5
cl_theory_mode str

Forwarded to compute_covariance.

'ccl'
cosmo_params dict | None

Forwarded to compute_covariance.

None
marginalise_sigma_m bool

When True (default) and this spin-2 bin carries a manifest sigma_m, add the analytic multiplicative-bias marginalisation term (2110.06947 Eq. 20). m is applied to the data at ingest; only sigma_m enters the covariance. No sigma_m ⇒ byte-identical to a pre-calibration covariance.

True
workspace_cache_scope str

"session" (default) or "registry" — see compute_cls_from_session; a pure cache-location choice.

'session'
jobspec dict | None

Optional {"backend": "slurm", "site": "<site>", ...} to run this as a non-blocking remote job instead of a local one. On submit, returns immediately {job_id, status, mode: "async", site, resource_class, next: "check_job"} — no covariance numbers. Poll with check_job(session_dir, job_id); the covariance JSON and its artefact-registry entry land locally once the job's finalize() pulls results. This ALWAYS runs as a non-blocking job — omitting jobspec does not mean synchronous; it means a local background job (same async receipt shape, backend: "local"). Pass jobspec only to pick Slurm instead of local, or to forward local resource options.

None

Returns:

Type Description
dict

An async receipt ``{job_id, status, mode: "async", run_dir,

dict

session_dir, next: "check_job", ...}`` — never the covariance

dict

numbers. Poll check_job(session_dir, job_id); once

dict

status == "done", its result carries the same keys as the

dict

single-tracer compute_covariance result, but JSON-safe

dict

(covariance, sqrt_diag, cls_theory, ells are plain

dict

lists), plus session_dir, bin_index, output_path.

Source code in cosmotron_mcp/server.py
@mcp.tool()
@sync_budget_guard
def compute_covariance_from_session(
    session_dir: str,
    bin_index: int = 0,
    nz_path: str | None = None,
    galaxy_bias: float = 1.5,
    cl_theory_mode: str = "ccl",
    cosmo_params: dict | None = None,
    marginalise_sigma_m: bool = True,
    workspace_cache_scope: str = "session",
    jobspec: dict | None = None,
) -> dict:
    """Compute the Gaussian covariance for one bin straight from a session.

    Session-driven wrapper around :func:`compute_covariance`, mirroring
    :func:`cosmotron.tools.compute_cls_from_session`.  It assembles every
    argument from the session on disk so the caller never has to hand-build a
    survey mask, redshift distribution, or n_bar — the brittle assembly that
    repeatedly broke the compute_cls → compute_covariance handoff.

    Steps
    -----
    1. Read ``pipeline_config`` (nside, full_sky, apodisation) and the manifest.
    2. Load the bin's ``results/spectra/cls_bin_{bin_index:02d}_x_{bin_index:02d}.json`` (must
       already exist — run ``compute_cls_from_session`` first).
    3. Build ``survey_mask``: ``mask.fits`` if the manifest references one, else
       a full-sky map of ones — always a float64 ndarray.
    4. Pull ``n_bar`` from ``manifest[...]["catalogue_stats"]["n_bar"]``.
    5. Load the **calibrated** ``z``/``nz`` from the survey n(z) file (explicit
       ``nz_path`` or discovered in ``data_folder``). A catalogue redshift
       histogram is deliberately NOT used — it gives the wrong theory Cl.
    6. Call :func:`compute_covariance`, writing
       ``results/covariance/covariance_bin_{bin_index:02d}_x_{bin_index:02d}.json`` and returning a
       JSON-serialisable dict (lists, not ndarrays).

    Args:
        session_dir: Session workspace directory (contains manifest.json +
            session_context.json).
        bin_index: Zero-based index into ``manifest["tomographic_bins"]``.
        nz_path: Path to the calibrated 2-column (z, dndz) n(z) file for
            this bin. If `None`, it is discovered in
            ``session_context["data_folder"]``.
        galaxy_bias: Forwarded to `compute_covariance`.
        cl_theory_mode: Forwarded to `compute_covariance`.
        cosmo_params: Forwarded to `compute_covariance`.
        marginalise_sigma_m: When `True` (default) and this spin-2 bin
            carries a manifest ``sigma_m``, add the analytic
            multiplicative-bias marginalisation term (2110.06947 Eq. 20).
            ``m`` is applied to the data at ingest; only ``sigma_m`` enters
            the covariance. No ``sigma_m`` ⇒ byte-identical to a
            pre-calibration covariance.
        workspace_cache_scope: ``"session"`` (default) or ``"registry"`` —
            see `compute_cls_from_session`; a pure cache-location choice.
        jobspec: Optional ``{"backend": "slurm", "site": "<site>", ...}`` to
            run this as a non-blocking remote job instead of a local one. On
            submit, returns immediately ``{job_id, status, mode: "async",
            site, resource_class, next: "check_job"}`` — no covariance
            numbers. Poll with ``check_job(session_dir, job_id)``; the
            covariance JSON and its artefact-registry entry land locally once
            the job's ``finalize()`` pulls results. This ALWAYS runs as a
            non-blocking job — omitting `jobspec` does not mean synchronous;
            it means a local background job (same async receipt shape,
            `backend: "local"`). Pass `jobspec` only to pick Slurm instead of
            local, or to forward local resource options.

    Returns:
        An async receipt ``{job_id, status, mode: "async", run_dir,
        session_dir, next: "check_job", ...}`` — never the covariance
        numbers. Poll `check_job(session_dir, job_id)`; once
        `status == "done"`, its `result` carries the same keys as the
        single-tracer `compute_covariance` result, but JSON-safe
        (``covariance``, ``sqrt_diag``, ``cls_theory``, ``ells`` are plain
        lists), plus ``session_dir``, ``bin_index``, ``output_path``.
    """
    _require_human_gates(session_dir)
    kwargs = {
        "bin_index": bin_index, "nz_path": nz_path,
        "galaxy_bias": galaxy_bias, "cl_theory_mode": cl_theory_mode,
        "cosmo_params": cosmo_params, "marginalise_sigma_m": marginalise_sigma_m,
        "workspace_cache_scope": workspace_cache_scope,
    }
    from cosmotron_mcp.remote.dispatch import route_heavy_tool
    return route_heavy_tool(
        "compute_covariance_from_session", session_dir,
        {"session_dir": session_dir, **kwargs}, jobspec,
    )

validate_covariance_knox

validate_covariance_knox(session_dir: str | None = None, bin_index: int = 0, cov_result: dict | None = None, cls_result: dict | None = None, f_sky: float | None = None) -> dict

Scientifically validate a covariance against the analytic Knox baseline.

Compares the diagonal of the (TJPCov/NaMaster) covariance to the binned Gaussian Knox variance built from the same theory Cl + shot noise + f_sky. The verdict is f_sky-aware: a strict precision band near full sky, an order-of-magnitude sanity band on a cut sky (where mode-coupling makes the two legitimately differ). This catches a grossly wrong covariance — including the fabricated cov_diag = noise_bias regression — without false-failing a correct cut-sky covariance.

Call it either from a session (loads results/covariance/covariance_bin_NN_x_NN.json + results/spectra/cls_bin_NN_x_NN.json) or in-memory by passing cov_result + cls_result directly.

Parameters:

Name Type Description Default
session_dir str | None

Session directory to load the covariance/Cl JSON from; omit when passing cov_result/cls_result directly.

None
bin_index int

Tomographic bin index (session mode only).

0
cov_result dict | None

Covariance result dict (in-memory mode).

None
cls_result dict | None

Power-spectrum result dict (in-memory mode).

None
f_sky float | None

Sky fraction; inferred from the session when omitted.

None

Returns:

Type Description
dict

A JSON-safe dict: ratio (per-band diag/Knox), knox_diag,

dict

cov_diag, median_ratio, f_sky, regime

dict

(full_sky/cut_sky), tolerance band, frac_outside,

dict

verdict (pass/warn/fail), and a human-readable

dict

summary.

Source code in cosmotron_mcp/server.py
@mcp.tool()
@sync_budget_guard
def validate_covariance_knox(
    session_dir: str | None = None,
    bin_index: int = 0,
    cov_result: dict | None = None,
    cls_result: dict | None = None,
    f_sky: float | None = None,
) -> dict:
    """Scientifically validate a covariance against the analytic Knox baseline.

    Compares the diagonal of the (TJPCov/NaMaster) covariance to the binned
    Gaussian Knox variance built from the same theory Cl + shot noise + f_sky.
    The verdict is **f_sky-aware**: a strict precision band near full sky, an
    order-of-magnitude sanity band on a cut sky (where mode-coupling makes the two
    legitimately differ). This catches a grossly wrong covariance — including the
    fabricated ``cov_diag = noise_bias`` regression — without false-failing a
    correct cut-sky covariance.

    Call it either from a session (loads ``results/covariance/covariance_bin_NN_x_NN.json`` +
    ``results/spectra/cls_bin_NN_x_NN.json``) or in-memory by passing ``cov_result`` +
    ``cls_result`` directly.

    Args:
        session_dir: Session directory to load the covariance/Cl JSON from;
            omit when passing `cov_result`/`cls_result` directly.
        bin_index: Tomographic bin index (session mode only).
        cov_result: Covariance result dict (in-memory mode).
        cls_result: Power-spectrum result dict (in-memory mode).
        f_sky: Sky fraction; inferred from the session when omitted.

    Returns:
        A JSON-safe ``dict``: ``ratio`` (per-band diag/Knox), ``knox_diag``,
        ``cov_diag``, ``median_ratio``, ``f_sky``, ``regime``
        (``full_sky``/``cut_sky``), ``tolerance`` band, ``frac_outside``,
        ``verdict`` (``pass``/``warn``/``fail``), and a human-readable
        ``summary``.
    """
    result = _validate_covariance_knox(
        session_dir=session_dir,
        bin_index=bin_index,
        cov_result=cov_result,
        cls_result=cls_result,
        f_sky=f_sky,
    )
    if session_dir:
        _log_tool_call(session_dir, "validate_covariance_knox", {"bin_index": bin_index})
    return result