|
1 | 1 | Examples |
2 | 2 | ======== |
3 | 3 |
|
4 | | -The ``examples/`` directory contains Jupyter notebooks demonstrating typical workflows. |
| 4 | +This page walks through the main workflows the package supports, with |
| 5 | +short, copy-pasteable code for each. See :doc:`quickstart` first if you |
| 6 | +haven't evaluated a King PDF yet. Each section below links to the full |
| 7 | +Jupyter notebook in ``examples/`` for additional plots and benchmarks. |
| 8 | + |
| 9 | +King PDF basics |
| 10 | +---------------- |
| 11 | + |
| 12 | +:class:`~kingmaker.pdf.KingPDF` evaluates the PDF/CDF (see :doc:`quickstart`), |
| 13 | +draws samples, and marginalizes over right ascension for signal-subtraction |
| 14 | +likelihoods. |
| 15 | + |
| 16 | +**Sampling angular offsets** |
| 17 | + |
| 18 | +.. code-block:: python |
| 19 | +
|
| 20 | + import numpy as np |
| 21 | + from kingmaker.pdf import KingPDF |
| 22 | +
|
| 23 | + king = KingPDF(angular_cutoff=np.pi) |
| 24 | + alpha, beta = np.radians(1.0), 2.0 |
| 25 | +
|
| 26 | + # Angular separations (radians) drawn from the King distribution via |
| 27 | + # inverse CDF. These are offsets from the source, not full positions -- |
| 28 | + # combine with a uniformly-random position angle to get a reconstructed |
| 29 | + # (ra, dec) for a given true source position. |
| 30 | + psi = king.sample(10_000, alpha, beta, n_grid=10_000) |
| 31 | +
|
| 32 | +**Marginalizing over right ascension** |
| 33 | + |
| 34 | +.. code-block:: python |
| 35 | +
|
| 36 | + source_dec = np.radians(30) |
| 37 | + sindec_bins, pdf_marginalized = king.marginalize( |
| 38 | + source_dec, alpha, beta, threshold=1e-6, nbins=100, |
| 39 | + ) |
| 40 | + # pdf_marginalized is the expected signal contribution as a function of |
| 41 | + # sin(declination), for use in a signal-subtraction likelihood term. |
| 42 | +
|
| 43 | +*Common options:* |
| 44 | + |
| 45 | +- ``angular_cutoff`` (constructor): truncates the PDF's support and |
| 46 | + renormalizes. Use this to restrict evaluation to a search window instead |
| 47 | + of the full sphere. |
| 48 | +- ``n_grid`` (``sample``): size of the CDF lookup grid used for inverse |
| 49 | + transform sampling. Higher values trade memory/setup time for accuracy; |
| 50 | + the default of 10000 gives roughly arcminute accuracy. |
| 51 | +- ``threshold`` / ``nbins`` (``marginalize``): ``threshold`` discards |
| 52 | + declination/RA bins whose relative PDF value is negligible (controls |
| 53 | + sparsity); ``nbins=None`` (default) uses adaptive binning sized to |
| 54 | + ``alpha`` instead of a fixed grid. |
5 | 55 |
|
6 | 56 | `basic_demo.ipynb <https://github.com/mjlarson/kingmaker/blob/main/examples/basic_demo.ipynb>`_ |
7 | | - King PDF basics, parameter effects, normalization, sampling, and speed benchmarks |
8 | | - for :class:`~kingmaker.pdf.KingPDF`. |
| 57 | + Parameter effects, normalization checks, and sampling/evaluation speed |
| 58 | + benchmarks. |
| 59 | + |
| 60 | +.. _fitting-psf-parameters: |
| 61 | + |
| 62 | +Fitting PSF parameters from Monte Carlo |
| 63 | +---------------------------------------- |
| 64 | + |
| 65 | +:class:`~kingmaker.fitting.KingPSFFitter` bins signal Monte Carlo along |
| 66 | +arbitrary observables (energy, declination, angular error estimate, ...) |
| 67 | +and fits King ``alpha``/``beta`` to the angular-error distribution in each |
| 68 | +bin. |
| 69 | + |
| 70 | +.. code-block:: python |
| 71 | +
|
| 72 | + from kingmaker.fitting import KingPSFFitter |
| 73 | + from kingmaker.pdf import KingPDF |
| 74 | + import numpy as np |
| 75 | +
|
| 76 | + # Build a small synthetic signal MC sample standing in for your own |
| 77 | + # simulation output -- a real dataset just needs the same field names. |
| 78 | + # Structured array with at least 'ra', 'dec' (reconstructed) and |
| 79 | + # 'trueRa', 'trueDec' (true) fields, in radians. |
| 80 | + rng = np.random.default_rng(0) |
| 81 | + n = 100_000 |
| 82 | + true_logE = rng.uniform(2, 6, n) |
| 83 | + true_dec = np.arcsin(rng.uniform(-1, 1, n)) |
| 84 | + true_ra = rng.uniform(0, 2 * np.pi, n) |
| 85 | +
|
| 86 | + psi = KingPDF().sample(n, np.radians(1.0), 2.5, rng=rng) |
| 87 | + phi = rng.uniform(0, 2 * np.pi, n) |
| 88 | + reco_dec = np.arcsin(np.clip( |
| 89 | + np.sin(true_dec) * np.cos(psi) + np.cos(true_dec) * np.sin(psi) * np.cos(phi), |
| 90 | + -1, 1, |
| 91 | + )) |
| 92 | + reco_ra = true_ra + np.arctan2( |
| 93 | + np.sin(phi) * np.sin(psi), |
| 94 | + np.cos(true_dec) * np.cos(psi) - np.sin(true_dec) * np.sin(psi) * np.cos(phi), |
| 95 | + ) |
| 96 | +
|
| 97 | + signal_events = np.empty(n, dtype=[ |
| 98 | + ("ra", float), ("dec", float), |
| 99 | + ("trueRa", float), ("trueDec", float), |
| 100 | + ("logE", float), ("ow", float), ("trueE", float), |
| 101 | + ]) |
| 102 | + signal_events["ra"], signal_events["dec"] = reco_ra, reco_dec |
| 103 | + signal_events["trueRa"], signal_events["trueDec"] = true_ra, true_dec |
| 104 | + signal_events["logE"] = true_logE |
| 105 | + signal_events["ow"] = 1.0 |
| 106 | + signal_events["trueE"] = 10**true_logE |
| 107 | +
|
| 108 | + parametrization_bins = {"logE": 5, "dec": 4} # equal-probability bins |
| 109 | +
|
| 110 | + fitter = KingPSFFitter( |
| 111 | + signal_events=signal_events, |
| 112 | + parametrization_bins=parametrization_bins, |
| 113 | + dpsi_nbins=51, |
| 114 | + minimum_counts=100, |
| 115 | + weight_field="ow", |
| 116 | + spectral_indices=[2.0, 2.5, 3.0], |
| 117 | + ) |
| 118 | + results = fitter.fit_all_bins(verbose=True) |
| 119 | +
|
| 120 | + alpha_fit = results["alpha"] # shape (n_gamma, n_logE, n_dec) |
| 121 | + beta_fit = results["beta"] |
| 122 | +
|
| 123 | + # Continuous evaluation between bin centers: |
| 124 | + alpha_interp, beta_interp = fitter.get_interpolator(gamma_index=0) |
| 125 | + point = np.array([[3.5, np.arcsin(0.0)]]) # [logE, dec] |
| 126 | + alpha_value = alpha_interp(point) |
| 127 | +
|
| 128 | + # Inspect a single bin's fit against its histogram: |
| 129 | + ax = fitter.plot_fit(bin_indices=(2, 2), gamma_index=0) |
| 130 | +
|
| 131 | +*Common options:* |
| 132 | + |
| 133 | +- ``parametrization_bins``: each value is either an ``int`` (equal-probability |
| 134 | + bins computed from the MC) or an explicit array of bin edges. |
| 135 | +- ``dpsi_nbins``: resolution of the angular-error histogram used in the fit. |
| 136 | +- ``minimum_counts``: bins with fewer events than this are skipped entirely |
| 137 | + (left at the default initial guess rather than fit). |
| 138 | +- ``remove_weight_outliers`` / ``weight_outlier_percentiles``: drop events |
| 139 | + with extreme weights (by sorted-index percentile, default ``[0, 95]``) |
| 140 | + before fitting, to keep a few outsized weights from destabilizing the fit. |
| 141 | +- ``weight_field``: name of the per-event weight field (e.g. ``"ow"``); pass |
| 142 | + ``None`` to use equal weights. |
| 143 | +- ``spectral_indices``: gamma values to fit independently. Each gets its own |
| 144 | + fitted alpha/beta grid, used later for interpolation over spectral index |
| 145 | + (see :class:`~kingmaker.wrapper.KingSpatialLikelihood` below). |
9 | 146 |
|
10 | 147 | `fitting_demo.ipynb <https://github.com/mjlarson/kingmaker/blob/main/examples/fitting_demo.ipynb>`_ |
11 | | - Fitting King PSF parameters to Monte Carlo simulations using |
12 | | - :class:`~kingmaker.fitting.KingPSFFitter` as a function of energy and declination. |
| 148 | + Fitting as a function of energy and declination, with diagnostic plots. |
13 | 149 |
|
14 | | -`template_demo.ipynb <https://github.com/mjlarson/kingmaker/blob/main/examples/template_demo.ipynb>`_ |
15 | | - Spherical-harmonic convolution of a HEALPix template with the King PSF using |
16 | | - :class:`~kingmaker.pdf.TemplateSmearedKingPDF`, including performance benchmarks. |
| 150 | +.. _point-source-likelihood: |
| 151 | + |
| 152 | +End-to-end point-source likelihood |
| 153 | +----------------------------------- |
| 154 | + |
| 155 | +:class:`~kingmaker.wrapper.KingSpatialLikelihood` wraps |
| 156 | +:class:`~kingmaker.fitting.KingPSFFitter` and |
| 157 | +:class:`~kingmaker.pdf.KingPDF` behind a single interface: fit (or load |
| 158 | +cached fit results) once, then evaluate the PDF per-event many times across |
| 159 | +trials. |
| 160 | + |
| 161 | +Continuing with the synthetic ``signal_events`` from the fitting example |
| 162 | +above: |
| 163 | + |
| 164 | +.. code-block:: python |
| 165 | +
|
| 166 | + from kingmaker.wrapper import KingSpatialLikelihood |
| 167 | + import numpy as np |
| 168 | +
|
| 169 | + wrapper = KingSpatialLikelihood( |
| 170 | + signal_events=signal_events, |
| 171 | + parametrization_bins=parametrization_bins, |
| 172 | + spectral_indices=[1.0, 2.0, 3.0, 4.0], |
| 173 | + cache_parameters=False, |
| 174 | + ) |
| 175 | +
|
| 176 | + # Stand-in "data" events and a source position for one trial. |
| 177 | + data_events = signal_events[:1000] |
| 178 | + source_ra, source_dec = 0.5, 0.2 |
| 179 | +
|
| 180 | + # Per trial: cache per-event parameters once, then evaluate as needed. |
| 181 | + wrapper.set_events( |
| 182 | + data_events, |
| 183 | + source_ras=np.array([source_ra]), |
| 184 | + source_decs=np.array([source_dec]), |
| 185 | + ) |
| 186 | + pdf_values = wrapper.evaluate_pdf(data_events, gamma=2.0) |
| 187 | + pdf_values_steeper = wrapper.evaluate_pdf(data_events, gamma=2.5) # interpolated |
| 188 | +
|
| 189 | +*Common options:* |
| 190 | + |
| 191 | +- ``cache_parameters`` / ``cache_name``: when ``True`` and ``cache_name`` |
| 192 | + exists on disk, fitting is skipped entirely and parameters are loaded from |
| 193 | + the cache; otherwise the fitter runs and (if ``cache_parameters``) saves |
| 194 | + its results there. Use this to avoid refitting across repeated runs/trials. |
| 195 | +- ``spectral_indices``: the gamma grid that gets fit up front; |
| 196 | + ``evaluate_pdf(events, gamma=...)`` interpolates between the two bracketing |
| 197 | + values, so pick a range that covers the spectral indices you plan to test. |
| 198 | +- ``parametrization_bins``: same ``int``-or-edges rules as |
| 199 | + :class:`~kingmaker.fitting.KingPSFFitter`. |
| 200 | +- **Gotcha:** ``evaluate_pdf`` requires ``set_events`` to have been called |
| 201 | + first with the *same* ``events`` array, and raises ``RuntimeError`` |
| 202 | + otherwise. Calling ``set_events`` repeatedly with identical events/sources |
| 203 | + is a cheap no-op, so it's safe to call once per trial unconditionally. |
17 | 204 |
|
18 | 205 | `likelihood_demo.ipynb <https://github.com/mjlarson/kingmaker/blob/main/examples/likelihood_demo.ipynb>`_ |
19 | | - End-to-end likelihood analysis using the :class:`~kingmaker.wrapper.KingSpatialLikelihood` |
20 | | - wrapper, covering event setup, spectral index interpolation, and PDF evaluation. |
| 206 | + Full walkthrough including event setup and spectral-index interpolation. |
| 207 | + |
| 208 | +.. _template-smearing: |
| 209 | + |
| 210 | +Template smearing for diffuse/extended sources |
| 211 | +------------------------------------------------ |
| 212 | + |
| 213 | +:class:`~kingmaker.pdf.TemplateSmearedKingPDF` convolves a HEALPix template |
| 214 | +map (e.g. Galactic diffuse emission) with the King PSF using a |
| 215 | +spherical-harmonic expansion, avoiding a per-event real-space convolution. |
| 216 | + |
| 217 | +.. code-block:: python |
| 218 | +
|
| 219 | + from kingmaker.pdf import TemplateSmearedKingPDF |
| 220 | + import numpy as np |
| 221 | + import healpy as hp |
| 222 | +
|
| 223 | + # A small synthetic HEALPix map standing in for a real diffuse template |
| 224 | + # (e.g. Fermi-LAT diffuse emission) -- concentrated near the equator like |
| 225 | + # a toy Galactic plane. Normalized to integrate to 1 internally. |
| 226 | + nside = 32 |
| 227 | + colat, _ = hp.pix2ang(nside, np.arange(hp.nside2npix(nside))) |
| 228 | + skymap = np.exp(-((colat - np.pi / 2) ** 2) / (2 * np.radians(10) ** 2)) |
| 229 | +
|
| 230 | + tskp = TemplateSmearedKingPDF( |
| 231 | + skymap=skymap, |
| 232 | + interpolation_method="nearest", |
| 233 | + memory_limit_gb=1.0, |
| 234 | + ) |
| 235 | +
|
| 236 | + alpha, beta = np.radians(5.0), 2.0 |
| 237 | +
|
| 238 | + # Full convolved map (e.g. for plotting): |
| 239 | + convolved_map = tskp.convolve_map(alpha, beta) |
| 240 | +
|
| 241 | + # Fast evaluation at a fixed set of source positions instead: |
| 242 | + eval_decs = np.radians([0.0, 30.0, -15.0]) |
| 243 | + eval_ras = np.radians([0.0, 45.0, 90.0]) |
| 244 | + tskp.set_coordinates(eval_decs, eval_ras) |
| 245 | + pdf_at_sources = tskp.convolve_at_grid_point(alpha, beta) |
| 246 | +
|
| 247 | +*Common options:* |
| 248 | + |
| 249 | +- ``interpolation_method``: ``"nearest"`` (default) snaps each |
| 250 | + ``(alpha, beta)`` to the closest precomputed grid point -- cheaper, and |
| 251 | + events landing in the same grid cell reuse the same convolution. |
| 252 | + ``"linear"`` bilinearly interpolates in log(alpha)/log(beta) space for |
| 253 | + smoother variation at extra cost. |
| 254 | +- ``lmax``: maximum spherical harmonic degree, defaulting to ``3 * nside - 1`` |
| 255 | + of the input map. Lower it to reduce memory/compute at the cost of |
| 256 | + angular detail in the convolution. |
| 257 | +- ``memory_limit_gb``: caps the batch size used when precomputing spherical |
| 258 | + harmonics for many ``set_coordinates`` points at once. |
| 259 | +- ``points_alpha`` / ``points_beta``: the grid of King parameters over which |
| 260 | + the convolution is precomputed (100 log-spaced points each, by default). |
| 261 | + Increase density here if using ``"linear"`` interpolation and seeing |
| 262 | + visible discretization. |
| 263 | + |
| 264 | +`template_demo.ipynb <https://github.com/mjlarson/kingmaker/blob/main/examples/template_demo.ipynb>`_ |
| 265 | + Convolution of a Fermi-LAT diffuse template with the King PSF, including |
| 266 | + performance benchmarks against healpy's Gaussian smoothing. |
0 commit comments