polarized_beam_fitting is a JAX-based analysis pipeline for measuring the polarized beam response of mm-wave telescopes using point-source observations.
It was developed for SPT-3G, but the fitting code is experiment-agnostic everywhere except in the data-loading step. This package models temperature and polarization cutout maps, subtracts temperature-to-polarization leakage templates, and fits a shared beam model together with per-source position and flux parameters.
The default configuration in config.py points to local SPT-3G data products, cache directories, and betapol reference files used for de Haan et al. (2026). To reproduce those results in another environment, override the paths and run settings before instantiating the fitter.
The SPT-3G-specific code is confined to the default data loader. To adapt the pipeline to another experiment:
- Implement a loader subclass in the style of
ExampleExperimentDataLoaderindata_loader.py. Your loader should yieldSourceMapRecordobjects with T/Q/U maps in mK, weight maps in1 / mK^2, and pixel resolution in radians. - Use source IDs that are stable across bands. The fitter matches records by removing the configured band suffix, such as
-150GHz. - Group input files by observing field in
config.coadd_filenames, set the frequency bands inconfig.bands, and setconfig.data_loader_class = YourExperimentDataLoader. - Start with
config.use_precomputed_leakage_templates = Falseunless you have generated leakage-template pickle files with the expected schema. - Choose a beam model. The
beta_polandbeta_Tmodels require abetapol_data_pathfile with two radial profiles to interpolate between;gaussianand B-spline-based models are easier starting points for new data.
This code was used for the paper "Characterization of the Polarization Beam Response of SPT-3G Using Point Sources" by Tijmen de Haan, Melanie Archipley, Nicholas Huang, and the SPT-3G Collaboration. The paper was submitted in April 2026.
The main workflow is:
- Read cutout map files for one or more observing fields and frequency bands.
- Select sources that are present in every configured band and are not in the skip list.
- Estimate or load per-source offsets and T/Q/U amplitudes.
- Subtract a field-dependent T->Q/U leakage template, optionally using precomputed templates stored on disk.
- Build either real-space weights or Fourier-space precision matrices.
- Fit a beam model shared across sources while also fitting per-source offsets and fluxes.
- Optionally run bootstrap resampling or MCMC posterior sampling for uncertainty estimation.
config.py defines a single mutable configuration object, BeamFittingConfig, with defaults for:
- input coadd files grouped by field
- cache and output directories
- selected bands
- map geometry and apodization
- beam model choice and parameter bounds
- skip-source handling
- leakage template mode
- CDRC calibration/deprojection parameters
- Fourier vs real-space chi-squared
- optimizer settings
- bootstrap and sampler settings
beam_model.py provides a common interface for several beam parameterizations:
gaussian: separate T and P Gaussian FWHM valuesbeta_pol: fixed T beam from stitched profiles, polarized beam interpolated bybeta_polbeta_T: test model that interpolates the T beam insteadbsplines_plus_gaussian: shared Gaussian core plus orthonormal B-spline corrections for T and Pbsplines_plus_main: fixed T beam plus B-spline perturbations around the polarization main beam
fitter.py contains PolarizedBeamFitter, which:
- builds the beam models for each configured band
- loads cached or freshly prepared source data
- constructs a real-space or Fourier-space objective
- parameterizes bounded parameters through logit transforms
- runs optimization with a tuned two-stage Adam/AMSGrad schedule, or alternative minimizers
- supports Hessian-based whitening for
NUTS,MCLMC, and Newton-PCG
Useful methods:
run_fit(): run maximum-likelihood optimizationsample_with_nuts(): run posterior sampling with NumPyro NUTSsample_with_mclmc(): run BlackJAX MCLMCcreate_model_maps(): generate model thumbnails at fitted parameterscreate_beam_profile_maps(): generate centered T and P beam mapscalculate_individual_chi2s(): inspect source-level fit quality
bootstrap.py wraps the base fitter in BootstrapBeamFitter. It first finds the ML solution, then resamples sources with replacement.
Unless a name or docstring explicitly states otherwise, map amplitudes and fluxes are in mK and angular quantities are in radians.
This is the minimal package-level workflow:
from polarized_beam_fitting import BeamFittingConfig, PolarizedBeamFitter, create_diagnostic_plots
config = BeamFittingConfig()
# Override site-specific defaults before running outside my environment.
config.coadd_filenames = {
"myfield1": ["/path/to/myfield1_coadds.g3"],
}
config.output_dir = "/path/to/output"
config.cache_dir = "/path/to/cache"
config.leakage_template_dir = "/path/to/cache/leakage_templates"
config.betapol_data_path = "/path/to/polarized_beam_fitting/data/betapol_TdH.npz"
fitter = PolarizedBeamFitter(config)
best_fit_params = fitter.run_fit()
print(best_fit_params["beams"])
create_diagnostic_plots(fitter, best_fit_params)jaxnumpyscipymatplotliboptaxoptimistixblackjaxnumpyrocambarvizcorner
spt3g_software is optional. It is only required by the default loader for reading .g3 coadd files.
__init__.py: public exportsconfig.py: run configurationfitter.py: ML fitting and posterior samplingbeam_model.py: beam parameterizationsdata_loader.py: G3 loading, NumPy map preparation, and an example non-SPT data-loader adapterprecision.py: Fourier covariance and precision constructionbootstrap.py: bootstrap resampling wrapperplotting.py: diagnostics and summary figurestemplate_construction.py: precompute leakage templates for iterative leakage handlingsource_fitting.py: Gaussian source fits for initializationutils.py: interpolation, masks, parameter transforms, and other helpers