Skip to content

brahmap.core.ProcessTimeSamples

Bases: BaseProcessTimeSamples

A standard process-time-samples data container to store pre-processed pointing information, pre-computed map-making weights and metadata.

This class ingests raw pointing arrays, polarization angles, and noise weights, and computes the necessary pixel-space representations (such as hit counts and trigonometric weight sums) required for the iterative map-making process. It automatically drops unobserved or pathological pixels to minimize the memory footprint of the container.

After pre-processing, the container object can be used to create pointing operators, block-diagonal preconditioners, etc. as required for map-making.

This container manages pixel-space hit counts and trigonometric weight sums on each MPI process. It uses standard MPI collectives (like MPI_Allreduce) to globally synchronize and check observed pixels and calculate pixel-space map objects.

Parameters:

Name Type Description Default
npix int

Number of pixels on which the map-making has to be done (e.g. healpy.nside2npix(nside))

required
pointings NDArray[integer]

A 1-d array of pixel indices pointing to the sky map for each time sample

required
pointings_flag NDArray[bool_] | None

A 1-d boolean array where True indicates a valid pointing and False flags a bad pointing, by default None. If set as None, all the pointings are considered valid

None
solver_type SolverType

The level of map-making solver to construct (\(I\), \(QU\), or \(IQU\)), by default SolverType.IQU

IQU
pol_angles NDArray[number] | None

A 1-d array containing the polarization orientation angles of the detectors for each sample, by default None

None
noise_weights NDArray[number] | None

A 1-d array containing the inverse noise variance for each time sample, by default None. If set as None, the inverse noise variance is set to 1 for each time sample

None
threshold float

The condition number threshold used to flag degenerate or under-sampled pixels, by default 1.0e-5

1e-05
dtype_float DTypeFloat | None

The data type to use for floating point arrays, by default None. If set as None, the data type is inferred from the input noise_weights or pol_angles array. If none of them are supplied, it will be set to np.float64

None
update_pointings_inplace bool

If True, the class will perform operations on the pointings array in-place to save memory. This can modify the input array. If False, the class will create a copy of the original array. By default False

False

Methods:

Name Description
get_hit_counts

Returns hit counts of the pixel indices.

Attributes:

Name Type Description
npix int

Number of pixels on which the map-making has to be done.

pointings NDArray[integer]

A 1-d array of pixel indices pointing to the observed sky pixel

pointings_flag NDArray[bool_] | None

A 1-d boolean array where True indicates a valid pointing and

nsamples int

The number of time samples processed by the current MPI rank

nsamples_global int

The total number of time samples across all MPI ranks

solver_type SolverType

The current map-making solver configuration (\(I\), \(QU\), or \(IQU\))

threshold float

The condition number threshold used to flag bad pixels

dtype_float Any

The inferred or specified data type for floating point arrays

observed_pixels NDArray[integer]

A 1-d array containing the original indices of the pixels that

pixel_flag NDArray[bool_]

A 1-d boolean array of size npix where True indicates a bad

bad_pixels NDArray[integer]

A 1-d array that contains all the pixel indices that will be excluded

old2new_pixel NDArray[integer]

A 1-d array mapping old pixel indices to new pixel indices

weighted_counts NDArray[number]

A 1-d array accumulating the inverse noise weights per valid pixel

sin2phi NDArray[number]

A 1-d array containing \(\sin(2\phi)\) evaluated at the valid time samples

cos2phi NDArray[number]

A 1-d array containing \(\cos(2\phi)\) evaluated at the valid time samples

weighted_sin NDArray[number]

A 1-d array accumulating the noise-weighted \(\sin(2\phi)\) sum

weighted_cos NDArray[number]

A 1-d array accumulating the noise-weighted \(\cos(2\phi)\) sum

weighted_sin_sq NDArray[number]

A 1-d array accumulating the noise-weighted \(\sin^2(2\phi)\) sum

weighted_cos_sq NDArray[number]

A 1-d array accumulating the noise-weighted \(\cos^2(2\phi)\) sum

weighted_sincos NDArray[number]

A 1-d array accumulating the noise-weighted \(\sin(2\phi)\cos(2\phi)\)

one_over_determinant NDArray[number]

A 1-d array containing the inverse determinant of the

new_npix int

The number of pixels on which the map-making will be done

Source code in brahmap/core/process_time_samples.py
class ProcessTimeSamples(BaseProcessTimeSamples):
    """A standard process-time-samples data container to store pre-processed
    pointing information, pre-computed map-making weights and metadata.

    This class ingests raw pointing arrays, polarization angles, and
    noise weights, and computes the necessary pixel-space representations
    (such as hit counts and trigonometric weight sums) required for the
    iterative map-making process. It automatically drops unobserved or
    pathological pixels to minimize the memory footprint of the container.

    After pre-processing, the container object can be used to create
    pointing operators, block-diagonal preconditioners, etc. as required
    for map-making.

    This container manages pixel-space hit counts and trigonometric
    weight sums on each MPI process. It uses standard MPI collectives (like
    `MPI_Allreduce`) to globally synchronize and check observed pixels and
    calculate pixel-space map objects.

    Parameters
    ----------
    npix : int
        Number of pixels on which the map-making has to be done (e.g.
        `healpy.nside2npix(nside)`)
    pointings : npt.NDArray[np.integer]
        A 1-d array of pixel indices pointing to the sky map for each time sample
    pointings_flag : npt.NDArray[np.bool_] | None, optional
        A 1-d boolean array where `True` indicates a valid pointing and
        `False` flags a bad pointing, by default `None`. If set as `None`,
        all the pointings are considered valid
    solver_type : SolverType, optional
        The level of map-making solver to construct ($I$, $QU$, or
        $IQU$), by default `SolverType.IQU`
    pol_angles : npt.NDArray[np.number] | None, optional
        A 1-d array containing the polarization orientation angles of the
        detectors for each sample, by default `None`
    noise_weights : npt.NDArray[np.number] | None, optional
        A 1-d array containing the inverse noise variance for each time
        sample, by default `None`. If set as `None`, the inverse noise
        variance is set to 1 for each time sample
    threshold : float, optional
        The condition number threshold used to flag degenerate or
        under-sampled pixels, by default `1.0e-5`
    dtype_float : DTypeFloat | None, optional
        The data type to use for floating point arrays, by default
        `None`. If set as `None`, the data type is inferred from the input
        `noise_weights` or `pol_angles` array. If none of them are
        supplied, it will be set to `np.float64`
    update_pointings_inplace : bool, optional
        If `True`, the class will perform operations on the `pointings`
        array in-place to save memory. This can modify
        the input array. If `False`, the class will create a copy of the
        original array. By default `False`
    """

    def __init__(
        self,
        npix: int,
        pointings: npt.NDArray[np.integer],
        pointings_flag: npt.NDArray[np.bool_] | None = None,
        solver_type: SolverType = SolverType.IQU,
        pol_angles: npt.NDArray[np.number] | None = None,
        noise_weights: npt.NDArray[np.number] | None = None,
        threshold: float = 1.0e-5,
        dtype_float: DTypeFloat | None = None,
        update_pointings_inplace: bool = False,
    ):
        super().__init__(
            npix=npix,
            pointings=pointings,
            pointings_flag=pointings_flag,
            solver_type=solver_type,
            pol_angles=pol_angles,
            noise_weights=noise_weights,
            threshold=threshold,
            dtype_float=dtype_float,
            update_pointings_inplace=update_pointings_inplace,
        )

    def _compute_weights(
        self,
        pol_angles: npt.NDArray[np.number],
        noise_weights: npt.NDArray[np.number],
    ):
        self._hit_counts = np.zeros(self.npix, dtype=self._pointings.dtype)
        self._weighted_counts = np.zeros(self.npix, dtype=self.dtype_float)
        self._observed_pixels = np.zeros(self.npix, dtype=self._pointings.dtype)
        self._old2new_pixel = np.zeros(self.npix, dtype=self._pointings.dtype)
        self._pixel_flag = np.zeros(self.npix, dtype=bool)

        if self.solver_type == SolverType.I:
            self._new_npix = compute_weights.compute_weights_pol_I(
                npix=self.npix,
                nsamples=self.nsamples,
                pointings=self._pointings,
                pointings_flag=self._pointings_flag,
                noise_weights=noise_weights,
                hit_counts=self._hit_counts,
                weighted_counts=self._weighted_counts,
                observed_pixels=self._observed_pixels,
                __old2new_pixel=self._old2new_pixel,  # type: ignore
                pixel_flag=self._pixel_flag,
                comm=MPI_UTILS.comm,
            )

        else:
            self._sin2phi = np.zeros(self.nsamples, dtype=self.dtype_float)
            self._cos2phi = np.zeros(self.nsamples, dtype=self.dtype_float)

            self._weighted_sin_sq = np.zeros(self.npix, dtype=self.dtype_float)
            self._weighted_cos_sq = np.zeros(self.npix, dtype=self.dtype_float)
            self._weighted_sincos = np.zeros(self.npix, dtype=self.dtype_float)

            self._one_over_determinant = np.zeros(self.npix, dtype=self.dtype_float)

            if self.solver_type == SolverType.QU:
                compute_weights.compute_weights_pol_QU(
                    npix=self.npix,
                    nsamples=self.nsamples,
                    pointings=self._pointings,
                    pointings_flag=self._pointings_flag,
                    noise_weights=noise_weights,
                    pol_angles=pol_angles,
                    hit_counts=self._hit_counts,
                    weighted_counts=self._weighted_counts,
                    sin2phi=self._sin2phi,
                    cos2phi=self._cos2phi,
                    weighted_sin_sq=self._weighted_sin_sq,
                    weighted_cos_sq=self._weighted_cos_sq,
                    weighted_sincos=self._weighted_sincos,
                    one_over_determinant=self._one_over_determinant,
                    comm=MPI_UTILS.comm,
                )

            elif self.solver_type == SolverType.IQU:
                self._weighted_sin = np.zeros(self.npix, dtype=self.dtype_float)
                self._weighted_cos = np.zeros(self.npix, dtype=self.dtype_float)

                compute_weights.compute_weights_pol_IQU(
                    npix=self.npix,
                    nsamples=self.nsamples,
                    pointings=self._pointings,
                    pointings_flag=self._pointings_flag,
                    noise_weights=noise_weights,
                    pol_angles=pol_angles,
                    hit_counts=self._hit_counts,
                    weighted_counts=self._weighted_counts,
                    sin2phi=self._sin2phi,
                    cos2phi=self._cos2phi,
                    weighted_sin_sq=self._weighted_sin_sq,
                    weighted_cos_sq=self._weighted_cos_sq,
                    weighted_sincos=self._weighted_sincos,
                    weighted_sin=self._weighted_sin,
                    weighted_cos=self._weighted_cos,
                    one_over_determinant=self._one_over_determinant,
                    comm=MPI_UTILS.comm,
                )

            self._new_npix = compute_weights.get_pixel_mask_pol(
                solver_type=self.solver_type,
                npix=self.npix,
                threshold=self.threshold,
                hit_counts=self._hit_counts,
                one_over_determinant=self._one_over_determinant,
                observed_pixels=self._observed_pixels,
                __old2new_pixel=self._old2new_pixel,  # type: ignore
                pixel_flag=self._pixel_flag,
            )

        self._observed_pixels.resize(self._new_npix, refcheck=False)

    def _repixelization(self):
        if self.solver_type == SolverType.I:
            repixelize.repixelize_pol_I(
                new_npix=self._new_npix,
                observed_pixels=self._observed_pixels,
                hit_counts=self._hit_counts,
                weighted_counts=self._weighted_counts,
            )

            self._hit_counts.resize(self._new_npix, refcheck=False)
            self._weighted_counts.resize(self._new_npix, refcheck=False)

        elif self.solver_type == SolverType.QU:
            repixelize.repixelize_pol_QU(
                new_npix=self._new_npix,
                observed_pixels=self._observed_pixels,
                hit_counts=self._hit_counts,
                weighted_counts=self._weighted_counts,
                weighted_sin_sq=self._weighted_sin_sq,
                weighted_cos_sq=self._weighted_cos_sq,
                weighted_sincos=self._weighted_sincos,
                one_over_determinant=self._one_over_determinant,
            )

            self._hit_counts.resize(self._new_npix, refcheck=False)
            self._weighted_counts.resize(self._new_npix, refcheck=False)
            self._weighted_sin_sq.resize(self._new_npix, refcheck=False)
            self._weighted_cos_sq.resize(self._new_npix, refcheck=False)
            self._weighted_sincos.resize(self._new_npix, refcheck=False)
            self._one_over_determinant.resize(self._new_npix, refcheck=False)

        elif self.solver_type == SolverType.IQU:
            repixelize.repixelize_pol_IQU(
                new_npix=self._new_npix,
                observed_pixels=self._observed_pixels,
                hit_counts=self._hit_counts,
                weighted_counts=self._weighted_counts,
                weighted_sin_sq=self._weighted_sin_sq,
                weighted_cos_sq=self._weighted_cos_sq,
                weighted_sincos=self._weighted_sincos,
                weighted_sin=self._weighted_sin,
                weighted_cos=self._weighted_cos,
                one_over_determinant=self._one_over_determinant,
            )

            self._hit_counts.resize(self._new_npix, refcheck=False)
            self._weighted_counts.resize(self._new_npix, refcheck=False)
            self._weighted_sin_sq.resize(self._new_npix, refcheck=False)
            self._weighted_cos_sq.resize(self._new_npix, refcheck=False)
            self._weighted_sincos.resize(self._new_npix, refcheck=False)
            self._weighted_sin.resize(self._new_npix, refcheck=False)
            self._weighted_cos.resize(self._new_npix, refcheck=False)
            self._one_over_determinant.resize(self._new_npix, refcheck=False)

Attributes

npix: int property

Number of pixels on which the map-making has to be done.

Returns:

Type Description
int

Number of pixels on which the map-making has to be done

pointings: npt.NDArray[np.integer] property

A 1-d array of pixel indices pointing to the observed sky pixel for each time sample

Returns:

Type Description
NDArray[integer]

A 1-d array of pixel pointing indices for each time sample

pointings_flag: npt.NDArray[np.bool_] | None property

A 1-d boolean array where True indicates a valid pointing and False flags a bad pointing

Returns:

Type Description
NDArray[bool_]

The 1-d array of flags indicating valid (True) or discarded

(`False`) time samples

nsamples: int property

The number of time samples processed by the current MPI rank

Returns:

Type Description
int

Number of samples on current MPI rank

nsamples_global: int property

The total number of time samples across all MPI ranks

Returns:

Type Description
int

Global number of samples

solver_type: SolverType property

The current map-making solver configuration (\(I\), \(QU\), or \(IQU\))

Returns:

Type Description
SolverType

Level of map-making: \(I\), \(QU\), or \(IQU\)

threshold: float property

The condition number threshold used to flag bad pixels

Returns:

Type Description
float

Threshold to used for flagging the pixels in the sky

dtype_float: Any property

The inferred or specified data type for floating point arrays

Returns:

Type Description
DTypeFloat

dtype of the floating point arrays

observed_pixels: npt.NDArray[np.integer] property

A 1-d array containing the original indices of the pixels that are fully valid for map-making

Returns:

Type Description
NDArray[integer]

A 1-d array that contains all the pixel indices that are considered valid for map-making

pixel_flag: npt.NDArray[np.bool_] property

A 1-d boolean array of size npix where True indicates a bad pixel and False flags a valid pixel

Returns:

Type Description
NDArray[bool_]

A 1-d boolean array of size npix where True indicates a dropped or pathological pixel

bad_pixels: npt.NDArray[np.integer] property

A 1-d array that contains all the pixel indices that will be excluded in map-making.

Returns:

Type Description
NDArray[integer]

A 1-d array that contains all the pixel indices that will be excluded in map-making

old2new_pixel: npt.NDArray[np.integer] property

A 1-d array mapping old pixel indices to new pixel indices

Returns:

Type Description
NDArray[integer]

A 1-d array mapping old pixel indices to new pixel indices

weighted_counts: npt.NDArray[np.number] property

A 1-d array accumulating the inverse noise weights per valid pixel

Returns:

Type Description
NDArray[number]

A 1-d array accumulating the inverse noise weights per valid pixel

sin2phi: npt.NDArray[np.number] property

A 1-d array containing \(\sin(2\phi)\) evaluated at the valid time samples

Returns:

Type Description
NDArray[number]

A 1-d array containing \(\sin(2\phi)\) evaluated at the valid time samples

cos2phi: npt.NDArray[np.number] property

A 1-d array containing \(\cos(2\phi)\) evaluated at the valid time samples

Returns:

Type Description
NDArray[number]

A 1-d array containing \(\cos(2\phi)\) evaluated at the valid time samples

weighted_sin: npt.NDArray[np.number] property

A 1-d array accumulating the noise-weighted \(\sin(2\phi)\) sum per valid pixel

Returns:

Type Description
NDArray[number]

A 1-d array accumulating the noise-weighted \(\sin(2\phi)\) sum per valid pixel

weighted_cos: npt.NDArray[np.number] property

A 1-d array accumulating the noise-weighted \(\cos(2\phi)\) sum per valid pixel

Returns:

Type Description
NDArray[number]

A 1-d array accumulating the noise-weighted \(\cos(2\phi)\) sum per valid pixel

weighted_sin_sq: npt.NDArray[np.number] property

A 1-d array accumulating the noise-weighted \(\sin^2(2\phi)\) sum per valid pixel

Returns:

Type Description
NDArray[number]

A 1-d array accumulating the noise-weighted \(\sin^2(2\phi)\) sum per valid pixel

weighted_cos_sq: npt.NDArray[np.number] property

A 1-d array accumulating the noise-weighted \(\cos^2(2\phi)\) sum per valid pixel

Returns:

Type Description
NDArray[number]

A 1-d array accumulating the noise-weighted \(\cos^2(2\phi)\) sum per valid pixel

weighted_sincos: npt.NDArray[np.number] property

A 1-d array accumulating the noise-weighted \(\sin(2\phi)\cos(2\phi)\) sum per valid pixel

Returns:

Type Description
NDArray[number]

A 1-d array accumulating the noise-weighted \(\sin(2\phi)\cos(2\phi)\) sum per valid pixel

one_over_determinant: npt.NDArray[np.number] property

A 1-d array containing the inverse determinant of the block-diagonal operator \(P^T diag(N)^{-1} P\)

Returns:

Type Description
NDArray[number]

A 1-d array containing the inverse determinant of the block-diagonal operator \(P^T diag(N)^{-1} P\)

new_npix: int property

The number of pixels on which the map-making will be done

Returns:

Type Description
int

Number of pixels on which the map-making will be done

Methods:

get_hit_counts() -> np.ma.MaskedArray

Returns hit counts of the pixel indices.

Returns:

Type Description
NDArray[integer]

Hit counts of the pixel indices

Source code in brahmap/base/pts.py
def get_hit_counts(self) -> np.ma.MaskedArray:
    """Returns hit counts of the pixel indices.

    Returns
    -------
    npt.NDArray[np.integer]
        Hit counts of the pixel indices
    """
    hit_counts = np.ma.masked_array(
        data=np.zeros(self.npix),
        mask=np.logical_not(self._pixel_flag),
        fill_value=-1.6375e30,
    )

    hit_counts[~hit_counts.mask] = self._hit_counts
    return hit_counts