limTOD 1.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,215 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [1.3.0] - 2026-07-25
11
+
12
+ ### Added
13
+
14
+ - 🚀 **`limtod_jax` package**: pure-JAX, differentiable port of the sky→TOD
15
+ chain (pointing → ZYZ Euler angles → Wigner rotation of beam alms →
16
+ beam-weighted harmonic dot), implementing the e-RHINO
17
+ `docs/limtod-port-contract.md`. Ships `zyz_of_pointing`, `rotate_alm`,
18
+ `beam_weighted_sum`, `generate_tod_sky` (+ exact adjoint),
19
+ `generate_projection_rows`, and JAX-side HEALPix map↔alm wrappers.
20
+ Matches `limTOD.simulator.generate_TOD_sky(..., truncate_frac_thres=0.0)`
21
+ to ~1e-12 relative in float64 (oracle-tested at extreme pointing corners);
22
+ jit/vmap/grad-safe. Install with `pip install -e ".[jax]"` (Python ≥ 3.11).
23
+ - 📦 **Dependency extras**: `[mpi]` (mpi4py), `[gdsm]` (pygdsm), `[jax]`
24
+ (jax + s2fft), and `[full]` (all of the above).
25
+ - 🛡️ **MPI launcher guard**: importing limTOD under `mpirun`/`srun`
26
+ *without* mpi4py now raises a clear `RuntimeError` instead of silently
27
+ running N duplicated serial copies (each process believing it is rank 0).
28
+ Escape hatch for intentionally independent serial copies:
29
+ `LIMTOD_FORCE_SERIAL=1`.
30
+ - 🚢 **First PyPI release**: `pip install limTOD`.
31
+
32
+ ### Code quality (post-review polish)
33
+
34
+ - ✅ **New test suites**: full-Stokes physical invariants (spin structure,
35
+ 1D/3-row/4-row consistency, linearity, normalization) pin the polarized
36
+ chain the Stokes-I oracle never covered; a flicker-noise boundary sweep
37
+ over (alpha, fc, tau) corners; and an independent end-to-end
38
+ `HPW_mapmaking` oracle that rebuilds the regularized normal equations in
39
+ the test (unfiltered, priors, per-TOD noise variance, high-pass
40
+ consistency, gain/injection calibration).
41
+ - 🧾 **Type annotations** across the numpy package (`mypy
42
+ --disallow-untyped-defs` clean); the previously dead ArrayLike aliases
43
+ are now used.
44
+ - 🪵 **Logging**: all library `print()` calls migrated to module loggers
45
+ (`logging.getLogger`); informational output is now opt-in via standard
46
+ logging configuration instead of unconditional stdout, and MPI ranks no
47
+ longer duplicate messages.
48
+ - 🧩 **`HPW_mapmaking.__call__` decomposed** into `_filter_and_stack`,
49
+ `_build_priors`, and `_normalize_noise_variance` (behavior pinned
50
+ bit-exact by the new oracle tests before refactoring).
51
+ - 📓 Notebooks that require external, non-shipped data
52
+ (`demonstration.ipynb`, `RotatingBeam/baseSim*.ipynb`) now carry a
53
+ warning cell up top instead of failing with a bare `FileNotFoundError`.
54
+
55
+ ### Documentation
56
+
57
+ - 📚 **Restructured**: README is now a concise landing page (install,
58
+ quick starts, citation); detailed guides moved to `docs/`
59
+ (tod-simulation, mapmaking, theory & conventions, api-reference,
60
+ limtod-jax). Fixed duplicated/garbled parameter listings, broken
61
+ fences, and case-mismatched notebook links from the old monolithic
62
+ README.
63
+
64
+ ### Packaging
65
+
66
+ - Version is single-sourced from `pyproject.toml`: both
67
+ `limTOD.__version__` and `limtod_jax.__version__` read the installed
68
+ distribution metadata (the hardcoded copies had drifted once).
69
+ - Modernized metadata (SPDX license expression + `license-files`,
70
+ Python 3.12/3.13 classifiers, Documentation/Changelog URLs) and a lean
71
+ sdist (packages + docs + CHANGELOG; notebooks/data stay on GitHub).
72
+
73
+ ### Changed
74
+
75
+ - **Fresh-install default changed**: `mpi4py` and `pygdsm` are no longer
76
+ required dependencies — a plain `pip install -e .` now installs a lighter,
77
+ wheel-only stack (numpy, healpy, astropy, scipy, tqdm, mpmath). Rationale:
78
+ mpi4py needs a system MPI toolchain and pygdsm downloads sky-model data,
79
+ both an unnecessary burden for downstream users (e.g. e-RHINO) who only
80
+ consume the JAX port. **Existing environments are unaffected** (already-
81
+ installed packages stay); for a fresh full setup use
82
+ `pip install -e ".[full]"`, for MPI runs `.[mpi]`, for GDSM skies `.[gdsm]`.
83
+ - `limTOD.mpiutil` falls back to serial mode (`rank=0, size=1, world=None`)
84
+ when mpi4py is absent — restoring the graceful-degradation behavior of the
85
+ upstream caput `mpiutil` it is adapted from. With mpi4py installed,
86
+ behavior is unchanged.
87
+ - `GDSM_sky_model` imports pygdsm lazily and raises a clear `ImportError`
88
+ pointing at `pip install "limTOD[gdsm]"` when it is missing. All other
89
+ functionality works without pygdsm.
90
+
91
+ ### Fixed
92
+
93
+ Pre-release full-code review (independent numpy-side and JAX-side passes)
94
+ caught and fixed, with regression tests for each:
95
+
96
+ - 🐛 **`horizontal_mask` orientation**: the horizontal-frame mask was
97
+ rotated with `elevation=0` instead of the zenith pointing
98
+ (`elevation=90`), tipping it 90° onto the horizon — every masked
99
+ simulation placed the mask at the wrong sky location. Fixed; a
100
+ regression test pins the mask cap to (RA = LST, Dec = latitude).
101
+ - 🐛 **Silent NaN from zero-sum beams**: `normalize_beam`/`normalize=True`
102
+ divided by the beam's pixel sum unguarded; a fully-truncated or
103
+ fully-masked pointing silently poisoned the TOD with NaN. Now raises a
104
+ clear `ValueError` naming the pointing conditions; negative beam sums
105
+ scale all Stokes rows consistently (previously Q/U/V were left
106
+ unscaled).
107
+ - 🐛 **Flicker-noise error handling**: `aux_int` no longer swallows
108
+ mpmath errors into a printed message plus a fabricated `inf`
109
+ (it raises with context); `flicker_corr`/`sim_noise` explicitly reject
110
+ the singular `alpha=1` exponent instead of failing type-dependently.
111
+ - 🐛 **`wiener_filter_map` short TODs**: the default rolling-variance
112
+ estimator crashed with an opaque matmul error for TODs shorter than its
113
+ 100-sample window; the window now caps at the TOD length and mismatched
114
+ `noise_variance` lengths raise a clear error.
115
+ - 🐛 **`HPW_mapmaking` prior routing**: mixed 1D/2D
116
+ `Tsys_other_prior_inv_cov_group` elements were all routed by the FIRST
117
+ element's dimensionality, silently discarding off-diagonal covariance
118
+ entries; each element now routes by its own shape.
119
+ - 🐛 **`generate_gaussian_field(seed=None)`** no longer reseeds the global
120
+ NumPy RNG from OS entropy (which clobbered callers' reproducibility
121
+ seeding); it only seeds when a seed is given.
122
+ - 🐛 **Bugs surfaced by the typing pass** (all regression-tested):
123
+ `HPW_mapmaking(return_full_cov=True)` crashed unpacking
124
+ `wiener_filter_map`'s 3-tuple — the posterior covariance is now returned
125
+ as the final element; `wiener_filter_map` no longer hits a `NameError`
126
+ when the covariance is uncomputable (clear `LinAlgError` instead);
127
+ `Tsys_others_operator_group` accepts the documented bare-2D-array form
128
+ (and validates its length against the number of TODs);
129
+ `sim_noise` accepts a plain Python list `time_list` as documented.
130
+ - Pointing-array length mismatches now raise upfront (zip previously
131
+ truncated silently); MPI-unsafe unconditional prints are rank-0-gated;
132
+ `HPW_mapmaking` argument validation raises `ValueError` instead of
133
+ `assert` (which `python -O` strips); example scripts' obfuscated
134
+ serializer imports (written to dodge a security hook) were replaced with
135
+ plain, warning-commented imports.
136
+ - Aligned `pyproject.toml`/`limTOD.__version__` (stuck at 1.1.0) with the
137
+ changelog version history.
138
+
139
+ ## [1.2.0] - 2025-10-06
140
+
141
+ ### Added
142
+
143
+ - Add `CHANGELOG.md`
144
+
145
+ ### Changed
146
+
147
+ - **BREAKING**: Renamed `TODsim` class to `TODSim` for better Python naming conventions
148
+ - Renamed old `limTODsim` class references to `TODSim` throughout codebase
149
+ - Updated all import statements and class instantiations to use `TODSim`
150
+ - Updated `__init__.py` and `__all__` exports to reflect new class name
151
+
152
+ ### Improved
153
+
154
+ - 📝 **Documentation**:
155
+ - **Structure**: Moved "Latest Updates" section from README.md to dedicated CHANGELOG.md file
156
+ - 📋 **Table of Contents**: Updated README.md Table of Contents to accurately reflect document structure
157
+ - **Examples**: Removed in flavour of example notebooks to simplify maintainace
158
+ - 🔧 **Code Organization**:
159
+ - Move example notebooks to `examples/`
160
+ - Improved consistency in class naming across all files including:
161
+ - Source code (`simulator.py`)
162
+ - Package exports (`__init__.py`)
163
+ - Documentation (`README.md`)
164
+ - Example notebooks (`examples.ipynb`, `mm_example.ipynb`)
165
+ - Change Log (`CHANGELOG.md`)
166
+
167
+ ### Fixed
168
+
169
+ - Corrected all references to use consistent `TODSim` class name
170
+ - Fixed import statements in example notebooks and documentation
171
+
172
+ ## [1.1.0] - 2025-10-05
173
+
174
+ ### Added
175
+
176
+ - 🎯 **Full Stokes Support**: Added complete polarization handling (I, Q, U, V) for both TOD simulation and map-making
177
+ - 🗺️ **Map-Making Pipeline**: Implemented `HPW_mapmaking` class combining high-pass filtering and Wiener filtering for sky reconstruction from TOD
178
+ - 🎲 **Gaussian Random Field Generator**: Added generator for correlated sky realizations from frequency-frequency angular power spectra C_ℓ(ν,ν'), enabling realistic simulation of line intensity mapping signals with spectral correlations (credit: Katrine Alice Glasscock, Philip Bull)
179
+ - 📓 **Example Notebooks**: Added comprehensive Jupyter notebook demonstrating the full map-making workflow ([examples/mm_example.ipynb](examples/mm_example.ipynb))
180
+
181
+ ### Changed
182
+
183
+ - **BREAKING**: `beam_func` and `sky_func` now require keyword-only arguments, two of which must be `freq` and `nside`:
184
+
185
+ ```python
186
+ # Old: beam_func(freq, nside) and sky_func(freq, nside)
187
+ # New: beam_func(freq=xx, nside=xx) and sky_func(freq=xx, nside=xx)
188
+ ```
189
+
190
+ - **BREAKING**: Function outputs must be HEALPix maps with specific shapes:
191
+ - 1D array of length npix for unpolarized (**I**) beam/sky
192
+ - 2D array of shape (3, npix) for polarized (**I, Q, U**) beam/sky
193
+ - 2D array of shape (4, npix) for polarized (**I, Q, U, V**) beam/sky
194
+
195
+ ### Fixed
196
+
197
+ - 🐛 **Bug Fix**: Corrected a critical sign error in coordinate rotation transformations
198
+
199
+ ## [1.0.0] - 2025-09-01
200
+
201
+ ### Initial Release
202
+
203
+ - Initial release of limTOD: Time-Ordered Data simulation for single-dish radio telescopes
204
+
205
+ ### Key Features
206
+
207
+ - TOD simulation with realistic noise models (1/f noise, white noise, gain variations)
208
+ - Support for asymmetric beam patterns
209
+ - Direct beam convolution using HEALPix spherical harmonics rotation and sum to calculate Tsky
210
+ - Flexible beam and sky model functions
211
+ - Global Sky Model (GDSM) integration
212
+ - MPI parallelization support
213
+ - Example scanning patterns and beam models
214
+ - Example notebooks for getting started
215
+ - Documentation and examples
limtod-1.3.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Zheng Zhang
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,7 @@
1
+ # sdist contents beyond the packages themselves. Deliberately lean: the big
2
+ # example notebooks/data and conventions.pdf stay on GitHub (linked from docs).
3
+ include CHANGELOG.md
4
+ recursive-include docs *.md
5
+ prune examples
6
+ prune scripts
7
+ prune tests
limtod-1.3.0/PKG-INFO ADDED
@@ -0,0 +1,201 @@
1
+ Metadata-Version: 2.4
2
+ Name: limTOD
3
+ Version: 1.3.0
4
+ Summary: Time-Ordered Data simulator for single-dish radio intensity mapping, with a differentiable JAX port (limtod_jax)
5
+ Author-email: Zheng Zhang <zheng.zhang@manchester.ac.uk>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/zzhang0123/limTOD
8
+ Project-URL: Documentation, https://github.com/zzhang0123/limTOD/tree/main/docs
9
+ Project-URL: Changelog, https://github.com/zzhang0123/limTOD/blob/main/CHANGELOG.md
10
+ Project-URL: Bug Reports, https://github.com/zzhang0123/limTOD/issues
11
+ Project-URL: Source Code, https://github.com/zzhang0123/limTOD
12
+ Keywords: time-ordered data,simulation,MeerKAT,HEALPix,beam patterns,sky models,noise modeling,map making
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Science/Research
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.8
18
+ Classifier: Programming Language :: Python :: 3.9
19
+ Classifier: Programming Language :: Python :: 3.10
20
+ Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Topic :: Scientific/Engineering :: Astronomy
24
+ Classifier: Topic :: Scientific/Engineering :: Physics
25
+ Requires-Python: >=3.8
26
+ Description-Content-Type: text/markdown
27
+ License-File: LICENSE
28
+ Requires-Dist: numpy>=1.19.0
29
+ Requires-Dist: healpy>=1.14.0
30
+ Requires-Dist: astropy>=4.0.0
31
+ Requires-Dist: scipy>=1.5.0
32
+ Requires-Dist: tqdm>=4.60.0
33
+ Requires-Dist: mpmath>=1.2.0
34
+ Provides-Extra: mpi
35
+ Requires-Dist: mpi4py>=3.0.0; extra == "mpi"
36
+ Provides-Extra: gdsm
37
+ Requires-Dist: pygdsm>=1.2.0; extra == "gdsm"
38
+ Provides-Extra: full
39
+ Requires-Dist: mpi4py>=3.0.0; extra == "full"
40
+ Requires-Dist: pygdsm>=1.2.0; extra == "full"
41
+ Requires-Dist: jax>=0.5; extra == "full"
42
+ Requires-Dist: s2fft>=1.4.0; extra == "full"
43
+ Provides-Extra: dev
44
+ Requires-Dist: pytest>=6.0; extra == "dev"
45
+ Requires-Dist: pytest-cov>=2.0; extra == "dev"
46
+ Requires-Dist: black>=21.0; extra == "dev"
47
+ Requires-Dist: flake8>=3.8; extra == "dev"
48
+ Requires-Dist: mypy>=0.800; extra == "dev"
49
+ Requires-Dist: sphinx>=4.0; extra == "dev"
50
+ Requires-Dist: sphinx-rtd-theme>=1.0; extra == "dev"
51
+ Requires-Dist: jupyter>=1.0; extra == "dev"
52
+ Requires-Dist: matplotlib>=3.3.0; extra == "dev"
53
+ Provides-Extra: docs
54
+ Requires-Dist: sphinx>=4.0; extra == "docs"
55
+ Requires-Dist: sphinx-rtd-theme>=1.0; extra == "docs"
56
+ Requires-Dist: jupyter>=1.0; extra == "docs"
57
+ Requires-Dist: matplotlib>=3.3.0; extra == "docs"
58
+ Provides-Extra: notebooks
59
+ Requires-Dist: jupyter>=1.0; extra == "notebooks"
60
+ Requires-Dist: matplotlib>=3.3.0; extra == "notebooks"
61
+ Requires-Dist: seaborn>=0.11.0; extra == "notebooks"
62
+ Provides-Extra: jax
63
+ Requires-Dist: jax>=0.5; extra == "jax"
64
+ Requires-Dist: s2fft>=1.4.0; extra == "jax"
65
+ Dynamic: license-file
66
+
67
+ # limTOD
68
+
69
+ **Time-Ordered Data simulator for single-dish (autocorrelation) radio
70
+ intensity mapping — with a differentiable pure-JAX port.**
71
+
72
+ limTOD simulates the time-ordered data (TOD) of a single-dish telescope
73
+ scanning a HEALPix sky with an arbitrary (asymmetric) beam: the beam is
74
+ rotated to each pointing in spherical-harmonic space and dotted with the
75
+ sky, then combined with 1/f gain noise and white noise. The package also
76
+ ships:
77
+
78
+ - **`HPW_mapmaking`** — high-pass + Wiener-filter map-making from TOD back
79
+ to sky maps;
80
+ - **`limtod_jax`** — a pure-JAX, jit/vmap/grad-safe port of the sky→TOD
81
+ chain, verified against the numpy implementation to ~1e-12 in float64.
82
+ It powers the differentiable pipeline of
83
+ [e-RHINO](https://github.com/zzhang0123/e-RHINO).
84
+
85
+ Latest changes:
86
+ [CHANGELOG](https://github.com/zzhang0123/limTOD/blob/main/CHANGELOG.md).
87
+
88
+ ## Installation
89
+
90
+ ```bash
91
+ pip install limTOD
92
+ ```
93
+
94
+ The base install is deliberately lightweight (wheel-only: numpy, healpy,
95
+ astropy, scipy, tqdm, mpmath — no compiler needed). Heavier dependencies
96
+ are opt-in extras:
97
+
98
+ | Extra | Installs | When you need it |
99
+ |-------|----------|------------------|
100
+ | `[mpi]` | mpi4py | MPI-parallel simulation (`mpirun -n N ...`). Without it limTOD runs serially; launching under `mpirun` *without* mpi4py fails loudly instead of silently duplicating work. |
101
+ | `[gdsm]` | pygdsm | The `GDSM_sky_model` sky function (Global Sky Model). Everything else works without it. |
102
+ | `[jax]` | jax, s2fft | The `limtod_jax` package (Python ≥ 3.11). |
103
+ | `[full]` | all of the above | The complete setup. |
104
+
105
+ ```bash
106
+ pip install "limTOD[full]"
107
+ ```
108
+
109
+ From source: clone the
110
+ [repository](https://github.com/zzhang0123/limTOD) and
111
+ `pip install -e ".[dev,full]"` (runs the test suite over both the
112
+ MPI-present and serial-fallback paths).
113
+
114
+ ## Quick start
115
+
116
+ Simulate multi-frequency TOD for a MeerKAT-like scan
117
+ (sky model here needs `[gdsm]`; pass your own `sky_func` to go without):
118
+
119
+ ```python
120
+ from limTOD import TODSim, example_scan
121
+
122
+ simulator = TODSim(
123
+ ant_latitude_deg=-30.7130, ant_longitude_deg=21.4430, ant_height_m=1054,
124
+ beam_nside=256, sky_nside=256,
125
+ )
126
+ time_list, azimuth_list = example_scan()
127
+ tod, sky_tod, gain_noise = simulator.generate_TOD(
128
+ freq_list=[950, 1000, 1050], # MHz
129
+ time_list=time_list,
130
+ azimuth_deg_list=azimuth_list,
131
+ elevation_deg=41.5,
132
+ ) # each (n_freq, n_time)
133
+ ```
134
+
135
+ The same sky→TOD chain, differentiable in JAX (`[jax]` extra):
136
+
137
+ ```python
138
+ import jax; jax.config.update("jax_enable_x64", True)
139
+ import jax.numpy as jnp
140
+ import limtod_jax as ltj
141
+
142
+ sky_alm = ltj.map2alm_quad(sky_map, nside=nside, lmax=lmax)
143
+ psi, theta, phi = ltj.zyz_of_pointing(lst_deg, lat_deg, az_deg, el_deg, 0.0)
144
+ tod = ltj.generate_tod_sky(
145
+ beam_alm, sky_alm, jnp.stack([psi, theta, phi], axis=-1), lmax=lmax,
146
+ )
147
+ grad = jax.grad(lambda b: ltj.generate_tod_sky(
148
+ b, sky_alm, jnp.stack([psi, theta, phi], axis=-1), lmax=lmax).sum().real
149
+ )(beam_alm) # d(TOD sum)/d(beam alms)
150
+ ```
151
+
152
+ ## Documentation
153
+
154
+ | Page | Contents |
155
+ |------|----------|
156
+ | [TOD simulation](https://github.com/zzhang0123/limTOD/blob/main/docs/tod-simulation.md) | `TODSim` guide: inputs, outputs, noise model, MPI, troubleshooting |
157
+ | [Map-making](https://github.com/zzhang0123/limTOD/blob/main/docs/mapmaking.md) | `HPW_mapmaking`: high-pass + Wiener filtering |
158
+ | [Theory & conventions](https://github.com/zzhang0123/limTOD/blob/main/docs/theory.md) | Signal model, coordinate chain, Euler-angle conventions |
159
+ | [API reference](https://github.com/zzhang0123/limTOD/blob/main/docs/api-reference.md) | Public numpy API signatures |
160
+ | [limtod_jax](https://github.com/zzhang0123/limTOD/blob/main/docs/limtod-jax.md) | The JAX port: usage, exactness contract, precision requirements |
161
+
162
+ Worked notebooks:
163
+ [TOD simulation](https://github.com/zzhang0123/limTOD/blob/main/examples/TODsim_examples.ipynb)
164
+ and
165
+ [map-making](https://github.com/zzhang0123/limTOD/blob/main/examples/mm_example.ipynb).
166
+ Mathematical derivations:
167
+ [conventions.pdf](https://github.com/zzhang0123/limTOD/blob/main/conventions.pdf).
168
+
169
+ ## Citation
170
+
171
+ If you use limTOD in your research, please cite:
172
+
173
+ ```bibtex
174
+ @ARTICLE{2026RASTI...5ag024Z,
175
+ author = {{Zhang}, Zheng and {Bull}, Philip and {Santos}, Mario G. and {Nasirudin}, Ainulnabilah},
176
+ title = "{Joint Bayesian calibration and map-making for intensity mapping experiments}",
177
+ journal = {RAS Techniques and Instruments},
178
+ keywords = {Data Methods, methods: data analysis, techniques: spectroscopic, radio lines: general, Instrumentation and Methods for Astrophysics},
179
+ year = 2026,
180
+ month = jan,
181
+ volume = {5},
182
+ eid = {rzag024},
183
+ pages = {rzag024},
184
+ doi = {10.1093/rasti/rzag024},
185
+ archivePrefix = {arXiv},
186
+ eprint = {2509.10992},
187
+ primaryClass = {astro-ph.IM},
188
+ adsurl = {https://ui.adsabs.harvard.edu/abs/2026RASTI...5ag024Z},
189
+ adsnote = {Provided by the SAO/NASA Astrophysics Data System}
190
+ }
191
+ ```
192
+
193
+ ## License and authorship
194
+
195
+ MIT License — see
196
+ [LICENSE](https://github.com/zzhang0123/limTOD/blob/main/LICENSE).
197
+
198
+ limTOD is developed and maintained by Zheng Zhang (University of
199
+ Manchester), with help and advice from members of the MeerKLASS
200
+ and RHINO collaborations — including Phil Bull, Piyanat Kittiwisit,
201
+ Geoff Murphy, and Mario Santos.
limtod-1.3.0/README.md ADDED
@@ -0,0 +1,135 @@
1
+ # limTOD
2
+
3
+ **Time-Ordered Data simulator for single-dish (autocorrelation) radio
4
+ intensity mapping — with a differentiable pure-JAX port.**
5
+
6
+ limTOD simulates the time-ordered data (TOD) of a single-dish telescope
7
+ scanning a HEALPix sky with an arbitrary (asymmetric) beam: the beam is
8
+ rotated to each pointing in spherical-harmonic space and dotted with the
9
+ sky, then combined with 1/f gain noise and white noise. The package also
10
+ ships:
11
+
12
+ - **`HPW_mapmaking`** — high-pass + Wiener-filter map-making from TOD back
13
+ to sky maps;
14
+ - **`limtod_jax`** — a pure-JAX, jit/vmap/grad-safe port of the sky→TOD
15
+ chain, verified against the numpy implementation to ~1e-12 in float64.
16
+ It powers the differentiable pipeline of
17
+ [e-RHINO](https://github.com/zzhang0123/e-RHINO).
18
+
19
+ Latest changes:
20
+ [CHANGELOG](https://github.com/zzhang0123/limTOD/blob/main/CHANGELOG.md).
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ pip install limTOD
26
+ ```
27
+
28
+ The base install is deliberately lightweight (wheel-only: numpy, healpy,
29
+ astropy, scipy, tqdm, mpmath — no compiler needed). Heavier dependencies
30
+ are opt-in extras:
31
+
32
+ | Extra | Installs | When you need it |
33
+ |-------|----------|------------------|
34
+ | `[mpi]` | mpi4py | MPI-parallel simulation (`mpirun -n N ...`). Without it limTOD runs serially; launching under `mpirun` *without* mpi4py fails loudly instead of silently duplicating work. |
35
+ | `[gdsm]` | pygdsm | The `GDSM_sky_model` sky function (Global Sky Model). Everything else works without it. |
36
+ | `[jax]` | jax, s2fft | The `limtod_jax` package (Python ≥ 3.11). |
37
+ | `[full]` | all of the above | The complete setup. |
38
+
39
+ ```bash
40
+ pip install "limTOD[full]"
41
+ ```
42
+
43
+ From source: clone the
44
+ [repository](https://github.com/zzhang0123/limTOD) and
45
+ `pip install -e ".[dev,full]"` (runs the test suite over both the
46
+ MPI-present and serial-fallback paths).
47
+
48
+ ## Quick start
49
+
50
+ Simulate multi-frequency TOD for a MeerKAT-like scan
51
+ (sky model here needs `[gdsm]`; pass your own `sky_func` to go without):
52
+
53
+ ```python
54
+ from limTOD import TODSim, example_scan
55
+
56
+ simulator = TODSim(
57
+ ant_latitude_deg=-30.7130, ant_longitude_deg=21.4430, ant_height_m=1054,
58
+ beam_nside=256, sky_nside=256,
59
+ )
60
+ time_list, azimuth_list = example_scan()
61
+ tod, sky_tod, gain_noise = simulator.generate_TOD(
62
+ freq_list=[950, 1000, 1050], # MHz
63
+ time_list=time_list,
64
+ azimuth_deg_list=azimuth_list,
65
+ elevation_deg=41.5,
66
+ ) # each (n_freq, n_time)
67
+ ```
68
+
69
+ The same sky→TOD chain, differentiable in JAX (`[jax]` extra):
70
+
71
+ ```python
72
+ import jax; jax.config.update("jax_enable_x64", True)
73
+ import jax.numpy as jnp
74
+ import limtod_jax as ltj
75
+
76
+ sky_alm = ltj.map2alm_quad(sky_map, nside=nside, lmax=lmax)
77
+ psi, theta, phi = ltj.zyz_of_pointing(lst_deg, lat_deg, az_deg, el_deg, 0.0)
78
+ tod = ltj.generate_tod_sky(
79
+ beam_alm, sky_alm, jnp.stack([psi, theta, phi], axis=-1), lmax=lmax,
80
+ )
81
+ grad = jax.grad(lambda b: ltj.generate_tod_sky(
82
+ b, sky_alm, jnp.stack([psi, theta, phi], axis=-1), lmax=lmax).sum().real
83
+ )(beam_alm) # d(TOD sum)/d(beam alms)
84
+ ```
85
+
86
+ ## Documentation
87
+
88
+ | Page | Contents |
89
+ |------|----------|
90
+ | [TOD simulation](https://github.com/zzhang0123/limTOD/blob/main/docs/tod-simulation.md) | `TODSim` guide: inputs, outputs, noise model, MPI, troubleshooting |
91
+ | [Map-making](https://github.com/zzhang0123/limTOD/blob/main/docs/mapmaking.md) | `HPW_mapmaking`: high-pass + Wiener filtering |
92
+ | [Theory & conventions](https://github.com/zzhang0123/limTOD/blob/main/docs/theory.md) | Signal model, coordinate chain, Euler-angle conventions |
93
+ | [API reference](https://github.com/zzhang0123/limTOD/blob/main/docs/api-reference.md) | Public numpy API signatures |
94
+ | [limtod_jax](https://github.com/zzhang0123/limTOD/blob/main/docs/limtod-jax.md) | The JAX port: usage, exactness contract, precision requirements |
95
+
96
+ Worked notebooks:
97
+ [TOD simulation](https://github.com/zzhang0123/limTOD/blob/main/examples/TODsim_examples.ipynb)
98
+ and
99
+ [map-making](https://github.com/zzhang0123/limTOD/blob/main/examples/mm_example.ipynb).
100
+ Mathematical derivations:
101
+ [conventions.pdf](https://github.com/zzhang0123/limTOD/blob/main/conventions.pdf).
102
+
103
+ ## Citation
104
+
105
+ If you use limTOD in your research, please cite:
106
+
107
+ ```bibtex
108
+ @ARTICLE{2026RASTI...5ag024Z,
109
+ author = {{Zhang}, Zheng and {Bull}, Philip and {Santos}, Mario G. and {Nasirudin}, Ainulnabilah},
110
+ title = "{Joint Bayesian calibration and map-making for intensity mapping experiments}",
111
+ journal = {RAS Techniques and Instruments},
112
+ keywords = {Data Methods, methods: data analysis, techniques: spectroscopic, radio lines: general, Instrumentation and Methods for Astrophysics},
113
+ year = 2026,
114
+ month = jan,
115
+ volume = {5},
116
+ eid = {rzag024},
117
+ pages = {rzag024},
118
+ doi = {10.1093/rasti/rzag024},
119
+ archivePrefix = {arXiv},
120
+ eprint = {2509.10992},
121
+ primaryClass = {astro-ph.IM},
122
+ adsurl = {https://ui.adsabs.harvard.edu/abs/2026RASTI...5ag024Z},
123
+ adsnote = {Provided by the SAO/NASA Astrophysics Data System}
124
+ }
125
+ ```
126
+
127
+ ## License and authorship
128
+
129
+ MIT License — see
130
+ [LICENSE](https://github.com/zzhang0123/limTOD/blob/main/LICENSE).
131
+
132
+ limTOD is developed and maintained by Zheng Zhang (University of
133
+ Manchester), with help and advice from members of the MeerKLASS
134
+ and RHINO collaborations — including Phil Bull, Piyanat Kittiwisit,
135
+ Geoff Murphy, and Mario Santos.
@@ -0,0 +1,12 @@
1
+ # limTOD documentation
2
+
3
+ | Page | Contents |
4
+ |------|----------|
5
+ | [TOD simulation](tod-simulation.md) | `TODSim` user guide: inputs, outputs, noise model, examples, troubleshooting |
6
+ | [Map-making](mapmaking.md) | `HPW_mapmaking`: high-pass + Wiener filtering from TOD to sky maps |
7
+ | [Theory & conventions](theory.md) | Signal model, coordinate transformations, Euler-angle conventions, workflow |
8
+ | [API reference](api-reference.md) | Signatures and parameters of the public numpy API |
9
+ | [limtod_jax](limtod-jax.md) | The pure-JAX differentiable port: usage, conventions, exactness contract, precision |
10
+
11
+ Release notes live in [CHANGELOG.md](../CHANGELOG.md); mathematical
12
+ derivations in [conventions.pdf](../conventions.pdf).