Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
c658572
add `ty` to dependencies
ggalloni Jan 7, 2026
4773137
exclude non-critical folders
ggalloni Jan 7, 2026
8b8adb9
Start by ignoring all issues
ggalloni Jan 7, 2026
0920f9f
add `ty` step in CI tests
ggalloni Jan 7, 2026
7098235
Solve unknown argument
ggalloni Jan 7, 2026
445329a
fix invalid type
ggalloni Jan 7, 2026
c25afc4
fix automatic call
ggalloni Jan 7, 2026
71c33a1
fix `deprecated`
ggalloni Jan 7, 2026
3b0c6dc
Modernize type annotations from comments to inline syntax
ggalloni Jan 7, 2026
3f0fe20
Fix invalid-parameter-default: add | None to parameters with None def…
ggalloni Jan 7, 2026
06b2d6e
Fix invalid-assignment errors for ty type checker
ggalloni Jan 7, 2026
27a5ada
Fix dets_random type annotation bug in gaindrifts.py
ggalloni Jan 7, 2026
ef91374
Fix pol_angle_rad type annotation bug in hwp_diff_emiss.py
ggalloni Jan 7, 2026
cb86d7a
Fix dets_random type annotation bug in non_linearity.py
ggalloni Jan 7, 2026
3b89e18
Update ty rule counts, remove solved invalid-assignment
ggalloni Jan 7, 2026
e0bdaaa
run codestyle also on skipci
ggalloni Jan 7, 2026
9a43292
fix `unsupported-operator`
ggalloni Jan 8, 2026
ecafcc5
fix `not-subscriptable`
ggalloni Jan 8, 2026
b82c2b7
fix `unresolved-attribute`
ggalloni Jan 8, 2026
0d8a0a4
[skipci] clean
ggalloni Jan 8, 2026
84c8c53
fix `not-iterable`
ggalloni Jan 8, 2026
808d293
fix `invalid-argument-type`
ggalloni Jan 8, 2026
210e8ad
fix `possibly-missing-attribute`
ggalloni Jan 8, 2026
8541945
[skipci] clean
ggalloni Jan 8, 2026
ad6492c
fix `unresolved-import`
ggalloni Jan 8, 2026
36e12e3
clean pyproject
ggalloni Jan 8, 2026
e68be2c
fix tests
ggalloni Jan 8, 2026
9be65c9
local imports
ggalloni Jan 9, 2026
c4e84f1
fix notebook
ggalloni Jan 9, 2026
90ac909
more local imports
ggalloni Jan 9, 2026
a7fd77e
ignore `unresolved-import`
ggalloni Jan 9, 2026
a2773c7
[skipci] add pre-commit hook checking types
ggalloni Jan 9, 2026
5168993
[skipci] revert skipci logic in codestyle
ggalloni Jan 9, 2026
456aac6
[skipci] avoid reassignment
ggalloni Jan 9, 2026
83ed45b
update CHANGELOG.md
paganol Jan 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/codestyle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,8 @@ jobs:
set -euo pipefail
uv run ruff check . --config ./pyproject.toml
uv run ruff format --diff . --config ./pyproject.toml

- name: Check types with ty
run: |
set -euo pipefail
uv run ty check .
8 changes: 8 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,11 @@ repos:
# Run the formatter.
- id: ruff-format
types_or: [ python, pyi, jupyter ]
- repo: local
hooks:
- id: ty
name: ty type checker
entry: uv run ty check
language: system
types: [python]
pass_filenames: false
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# HEAD

- Add automatic type-checking with ty [#481](https://github.com/litebird/litebird_sim/pull/481)

- Stop using sphinxcontrib-asciinema [#478](https://github.com/litebird/litebird_sim/pull/478)

- TODs handling improved [#472](https://github.com/litebird/litebird_sim/pull/472)
Expand Down
2 changes: 1 addition & 1 deletion docs/source/profiling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Here is an example::
from litebird_sim import TimeProfiler, profile_list_to_speedscope
from time import sleep

perf_list = [] # type: list[TimeProfiler]
perf_list: list[TimeProfiler] = []

# First code block to profile
with TimeProfiler(name="function_A") as perf:
Expand Down
2 changes: 1 addition & 1 deletion litebird_sim/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@

# Privilege TBB over OpenPM and the internal Numba implementation of a
# work queue
numba.config.THREADING_LAYER_CONFIG = ["tbb", "omp", "workqueue"]
numba.config.THREADING_LAYER_CONFIG = ["tbb", "omp", "workqueue"] # type: ignore


__all__ = [
Expand Down
4 changes: 3 additions & 1 deletion litebird_sim/bandpass_template_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ def beam_throughtput(freqs):

# Define bandpass profile
def bandpass_profile(
freqs: np.ndarray | None = None,
freqs: np.ndarray,
bandpass: dict | None = None,
include_beam_throughput: bool | None = None,
):
Expand All @@ -132,6 +132,8 @@ def bandpass_profile(

profile = np.ones_like(freqs)

assert bandpass is not None, "bandpass dict is required"

if "bandpass_file" in bandpass.keys():
try:
f, profile = np.loadtxt(
Expand Down
12 changes: 9 additions & 3 deletions litebird_sim/bandpasses.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from dataclasses import dataclass
from dataclasses import dataclass, fields

import numpy as np
import scipy as sp
Expand Down Expand Up @@ -84,13 +84,13 @@ class BandPassInfo:
name: str = ""
bandtype: str = "top-hat"
normalize: bool = False
model: str = None
model: str | None = None

alpha_exp: float = 1
beta_exp: float = 1
cosine_apo_length: float = 5
cheby_poly_order: int = 3
cheby_ripple_dB: int = 3
cheby_ripple_dB: int | float = 3

def __post_init__(self):
"""
Expand Down Expand Up @@ -240,6 +240,12 @@ def find_central_frequency(self):
/ self.get_normalization()
)

@classmethod
def from_dict(cls, d: dict) -> "BandPassInfo":
"""Create a BandPassInfo object from a dictionary."""
field_names = {f.name for f in fields(cls)}
return cls(**{k: v for k, v in d.items() if k in field_names})

@staticmethod
def from_imo(imo: Imo, url: UUID | str):
"""Create a `BandPassInfo` object from a definition in the IMO
Expand Down
19 changes: 13 additions & 6 deletions litebird_sim/beam_convolution.py
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ def add_convolved_sky_to_one_detector(
beam_mmax = beam_alms_det.mmax

default_lmax = min(sky_lmax, beam_lmax)
assert beam_mmax is not None
default_mmax = min(default_lmax - 4, beam_mmax)

logging.warning(
Expand Down Expand Up @@ -303,12 +304,16 @@ def add_convolved_sky(
if input_sky_names is None:
sky_alms_det = sky_alms
else:
assert isinstance(sky_alms, dict)
sky_alms_det = sky_alms[input_sky_names[detector_idx]]
assert isinstance(sky_alms_det, SphericalHarmonics)

if input_beam_names is None:
beam_alms_det = beam_alms
else:
assert isinstance(beam_alms, dict)
beam_alms_det = beam_alms[input_beam_names[detector_idx]]
assert isinstance(beam_alms_det, SphericalHarmonics)

if mueller_hwp is None:
mueller_matrix = None
Expand All @@ -331,12 +336,12 @@ def add_convolved_sky(
def add_convolved_sky_to_observations(
observations: Observation | list[Observation],
sky_alms: (
SphericalHarmonics | dict[str, SphericalHarmonics]
SphericalHarmonics | dict[str, SphericalHarmonics] | None
), # at some point optional, taken from the obs
beam_alms: (
SphericalHarmonics | dict[str, SphericalHarmonics]
SphericalHarmonics | dict[str, SphericalHarmonics] | None
), # at some point optional, taken from the obs
pointings: npt.ArrayLike | list[npt.ArrayLike] | None = None,
pointings: npt.NDArray | list[npt.NDArray] | None = None,
hwp: HWP | None = None,
input_sky_alms_in_galactic: bool = True,
convolution_params: BeamConvolutionParameters | None = None,
Expand Down Expand Up @@ -396,20 +401,22 @@ def add_convolved_sky_to_observations(

if sky_alms is None:
try:
sky_alms = observations[0].sky
assert obs_list, "No observations available"
sky_alms = obs_list[0].sky
except AttributeError:
msg = "'sky_alms' is None and nothing is found in the observation. You should either pass the spherical harmonics, or store them in the observations if 'mbs' is used."
raise AttributeError(msg)
assert sky_alms["type"] == "alms", (
assert isinstance(sky_alms, dict) and sky_alms["type"] == "alms", (
"'sky_alms' should be of type 'alms'. Use 'store_alms' of 'MbsParameters' to make it so."
)

if beam_alms is None:
try:
beam_alms = observations[0].blms
beam_alms = obs_list[0].blms
except AttributeError:
msg = "'beam_alms' is None and nothing is found in the observation. You should either pass the spherical harmonics of the beam, or store them in the observations if 'get_gauss_beam_alms' is used."
raise AttributeError(msg)
assert beam_alms is not None

for cur_obs, cur_ptg in zip(obs_list, ptg_list):
# Determine input sky names
Expand Down
9 changes: 5 additions & 4 deletions litebird_sim/beam_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,10 @@ def gauss_beam_to_alm(
lmax: int,
mmax: int,
fwhm_rad: float,
ellipticity: float | None = 1.0,
psi_ell_rad: float | None = 0.0,
psi_pol_rad: float | None = 0.0,
cross_polar_leakage: float | None = 0.0,
ellipticity: float = 1.0,
psi_ell_rad: float = 0.0,
psi_pol_rad: float = 0.0,
cross_polar_leakage: float = 0.0,
) -> SphericalHarmonics:
"""
Compute spherical harmonics coefficients a_ℓm representing a Gaussian beam.
Expand Down Expand Up @@ -238,6 +238,7 @@ def generate_gauss_beam_alms(

blms = {}

assert observation.name is not None
if channels is None:
# Use detectors from observations
for detector_idx, det_name in enumerate(observation.name):
Expand Down
11 changes: 7 additions & 4 deletions litebird_sim/detectors.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,14 +188,15 @@ class DetectorInfo:
quat: Any = None
pol_angle_rad: float = 0.0
pol_efficiency: float = 1.0
mueller_hwp: None | np.ndarray | dict[np.ndarray] = None
mueller_hwp_solver: None | np.ndarray | dict[np.ndarray] = None
jones_hwp: None | np.ndarray | dict[np.ndarray] = None
jones_hwp_solver: None | np.ndarray | dict[np.ndarray] = None
mueller_hwp: None | np.ndarray | dict[str, np.ndarray] = None
mueller_hwp_solver: None | np.ndarray | dict[str, np.ndarray] = None
jones_hwp: None | np.ndarray | dict[str, np.ndarray] = None
jones_hwp_solver: None | np.ndarray | dict[str, np.ndarray] = None
pointing_theta_phi_psi_deg: None | np.ndarray = None
pointing_u_v: None | np.ndarray = None
g_one_over_k: float = 0.0
amplitude_2f_k: float = 0.0
band: BandPassInfo | None = None

def __post_init__(self):
if self.quat is None:
Expand All @@ -204,6 +205,7 @@ def __post_init__(self):
self.quat = normalize_time_dependent_quaternion(self.quat)

if isinstance(self.band_freqs_ghz, np.ndarray):
assert self.band_weights is not None
assert len(self.band_freqs_ghz) == len(self.band_weights)

# Warn if mueller_hwp is not a 4x4 numpy array
Expand Down Expand Up @@ -341,6 +343,7 @@ class FreqChannelInfo:
number_of_detectors: int = 0
detector_names: list[str] = field(default_factory=list)
detector_objs: list[UUID] = field(default_factory=list)
band: BandPassInfo | None = None

"""A data class representing the configuration of a frequency channel in LiteBIRD.

Expand Down
10 changes: 5 additions & 5 deletions litebird_sim/dipole.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,31 +137,31 @@ def add_dipole_for_one_detector(
dipole_type: DipoleType,
):
if dipole_type == DipoleType.LINEAR:
for i in prange(len(tod_det)):
for i in prange(len(tod_det)): # type: ignore[not-iterable]
tod_det[i] += compute_dipole_for_one_sample_linear(
theta=theta_phi_det[i, 0],
phi=theta_phi_det[i, 1],
v_km_s=velocity[i],
t_cmb_k=t_cmb_k,
)
elif dipole_type == DipoleType.QUADRATIC_EXACT:
for i in prange(len(tod_det)):
for i in prange(len(tod_det)): # type: ignore[not-iterable]
tod_det[i] += compute_dipole_for_one_sample_quadratic_exact(
theta=theta_phi_det[i, 0],
phi=theta_phi_det[i, 1],
v_km_s=velocity[i],
t_cmb_k=t_cmb_k,
)
elif dipole_type == DipoleType.TOTAL_EXACT:
for i in prange(len(tod_det)):
for i in prange(len(tod_det)): # type: ignore[not-iterable]
tod_det[i] += compute_dipole_for_one_sample_total_exact(
theta=theta_phi_det[i, 0],
phi=theta_phi_det[i, 1],
v_km_s=velocity[i],
t_cmb_k=t_cmb_k,
)
elif dipole_type == DipoleType.QUADRATIC_FROM_LIN_T:
for i in prange(len(tod_det)):
for i in prange(len(tod_det)): # type: ignore[not-iterable]
tod_det[i] += compute_dipole_for_one_sample_quadratic_from_lin_t(
theta=theta_phi_det[i, 0],
phi=theta_phi_det[i, 1],
Expand All @@ -171,7 +171,7 @@ def add_dipole_for_one_detector(
)
elif dipole_type == DipoleType.TOTAL_FROM_LIN_T:
planck_t0 = planck(nu_hz, t_cmb_k)
for i in prange(len(tod_det)):
for i in prange(len(tod_det)): # type: ignore[not-iterable]
tod_det[i] += compute_dipole_for_one_sample_total_from_lin_t(
theta=theta_phi_det[i, 0],
phi=theta_phi_det[i, 1],
Expand Down
2 changes: 1 addition & 1 deletion litebird_sim/distribute.py
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ def distribute_optimally(elements, num_of_groups, weight_fn=None) -> list[Span]:

max_weight = _partition(elements, len(elements), num_of_groups, weight_fn)

result = [] # type: list[Span]
result: list[Span] = []
start_idx = 0
weight = 0
cur_num = 0
Expand Down
33 changes: 18 additions & 15 deletions litebird_sim/gaindrifts.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import numpy as np

from enum import IntEnum
import logging as log
from dataclasses import dataclass
from enum import IntEnum

import numpy as np

from .observations import Observation
from .seeding import regenerate_or_check_detector_generators
Expand All @@ -22,7 +23,7 @@ class GainDriftType(IntEnum):

LINEAR_GAIN = 0
THERMAL_GAIN = 1
# SLOW_GAIN = 2 # Remains to be implemented
SLOW_GAIN = 2 # Remains to be implemented


class SamplingDist(IntEnum):
Expand Down Expand Up @@ -202,8 +203,8 @@ def _get_psd(
def _noise_timestream(
tod_size: int,
sampling_freq_hz: float,
drift_params: GainDriftParams = None,
random: np.random.Generator = None,
drift_params: GainDriftParams | None = None,
random: np.random.Generator | None = None,
) -> np.ndarray:
"""The function to generate the thermal noise time stream with
:math:`1/f` power spectral density.
Expand Down Expand Up @@ -279,9 +280,9 @@ def _noise_timestream(
def apply_gaindrift_for_one_detector(
det_tod: np.ndarray,
sampling_freq_hz: float,
drift_params: GainDriftParams = None,
noise_timestream: np.ndarray = None,
random: np.random.Generator = None,
drift_params: GainDriftParams | None = None,
noise_timestream: np.ndarray | None = None,
random: np.random.Generator | None = None,
):
"""This function applies the gain drift on the TOD corresponding to only one
detector.
Expand Down Expand Up @@ -389,7 +390,7 @@ def apply_gaindrift_for_one_detector(
det_tod *= _responsivity_function(dT)

elif drift_params.drift_type == GainDriftType.SLOW_GAIN:
# !!! Remains to be implemented
log.warning("Slow gain drift is not yet implemented.")
pass
else:
raise ValueError(
Expand All @@ -401,9 +402,9 @@ def apply_gaindrift_for_one_detector(
def apply_gaindrift_to_tod(
tod: np.ndarray,
sampling_freq_hz: float,
drift_params: GainDriftParams = None,
focalplane_attr: list | np.ndarray = None,
dets_random: np.random.Generator | None = None,
drift_params: GainDriftParams | None = None,
focalplane_attr: list | np.ndarray | None = None,
dets_random: list[np.random.Generator] | None = None,
):
"""The function to apply the gain drift to all the detectors of a given TOD object.

Expand Down Expand Up @@ -448,6 +449,8 @@ def apply_gaindrift_to_tod(
if drift_params is None:
drift_params = GainDriftParams()

assert dets_random is not None, "dets_random is required"

tod_size = len(tod[0])

if drift_params.drift_type == GainDriftType.LINEAR_GAIN:
Expand Down Expand Up @@ -501,10 +504,10 @@ def apply_gaindrift_to_tod(

def apply_gaindrift_to_observations(
observations: Observation | list[Observation],
drift_params: GainDriftParams = None,
drift_params: GainDriftParams | None = None,
user_seed: int | None = None,
component: str = "tod",
dets_random: list[np.random.Generator] = None,
dets_random: list[np.random.Generator] | None = None,
):
"""
Apply gain drift to one or more observations.
Expand Down
Loading
Loading