.. DO NOT EDIT. .. THIS FILE WAS AUTOMATICALLY GENERATED BY SPHINX-GALLERY. .. TO MAKE CHANGES, EDIT THE SOURCE PYTHON FILE: .. "auto_examples/schedules/plot_cadence.py" .. LINE NUMBERS ARE GIVEN BELOW. .. only:: html .. note:: :class: sphx-glr-download-link-note :ref:`Go to the end ` to download the full example code. .. rst-class:: sphx-glr-example-title .. _sphx_glr_auto_examples_schedules_plot_cadence.py: Cadence Diagnostics on the Default Schedule ============================================= :ref:`user_guide_surveys_cadence` lays out six cadence diagnostics that :class:`~uvex_transients.surveys.base.SurveySchedule` can compute, each as a full-sky HEALPix map, to answer some flavor of "how well does this survey sample transients on a particular timescale?" That page's own runnable examples build small synthetic schedules to keep things fast and self-contained. Here, we instead run every one of those diagnostics against the real thing: the *default* UVEX schedule, as published by the `UVEX scheduler `_ project and fetched via :func:`~uvex_transients.surveys.utils.get_schedule`. Two of the six diagnostics, *Pair Counts* and *Control-Time Curve*, come in a ``_curve`` variant that sweeps an entire array of transient timescales, re-scanning every observed sky pixel once per timescale. That's the right tool for scanning "how does sensitivity change with timescale," but on a two-year, ~34,000-observation schedule it's also the one part of this page we deliberately skip: each single-timescale call below already touches every observed pixel once, so sweeping a few dozen timescales means paying that cost a few dozen times over. The :ref:`user_guide_surveys_cadence` page runs the ``_curve`` sweep itself, on a much smaller synthetic schedule built for exactly that purpose. .. GENERATED FROM PYTHON SOURCE LINES 28-36 Loading the default schedule ------------------------------ :func:`~uvex_transients.surveys.utils.get_schedule`, called with no arguments, downloads and locally caches whichever schedule is registered as ``config["schedules.default_schedule"]``; network access is required the first time this runs for a given schedule, after which the cached copy is reused. .. GENERATED FROM PYTHON SOURCE LINES 36-52 .. code-block:: Python import matplotlib.pyplot as plt import numpy as np from astropy import units as u from uvex_transients.surveys import get_schedule from uvex_transients.utils.plotting import get_default_cmap, plot_healpix_map, plot_histogram, set_plot_style set_plot_style() schedule = get_schedule() print(schedule) n_observations = np.count_nonzero(schedule.table["action"] == "observe") print(f"{n_observations} observations over {(schedule.end_time - schedule.start_time).to(u.day)}") .. rst-class:: sphx-glr-script-out .. code-block:: none 53480 observations over 730.2597227546296 d .. GENERATED FROM PYTHON SOURCE LINES 53-75 Every diagnostic below gets the same two views: a full-sky map and a histogram over its per-pixel (or, for *Successive Gaps*, per-pair) values. :func:`~uvex_transients.utils.plotting.plot_healpix_map` and :func:`~uvex_transients.utils.plotting.plot_histogram` do the plotting for all of them. Every quantity on this page (a count, a separation, a duration) is strictly positive and spans several orders of magnitude between its quietest and busiest sky pixels, so both helpers plot on a **log** scale throughout: :class:`~matplotlib.colors.LogNorm` for the map color, and log-spaced bins for the histogram. Both settle on a single perceptually uniform colormap, ``config["plotting.default_cmap"]`` (``viridis``), kept consistent across every plot on the page (and echoed in the histogram bars) rather than switching palettes diagnostic to diagnostic; it avoids the very dark, near-black low end other sequential colormaps (e.g. ``magma``) use, which reads poorly next to the black axis labels and titles surrounding each plot. ``nside=64`` is used throughout, coarser than the ``nside=128`` default used elsewhere in the docs, purely to keep this page's dozen full-sky passes over a real two-year schedule quick to render; nothing here is sensitive to that choice. .. GENERATED FROM PYTHON SOURCE LINES 75-82 .. code-block:: Python NSIDE = 64 # A mid-tone point on the shared default colormap, so the histograms read as part of the same # palette as the maps' colorbars. HIST_COLOR = get_default_cmap()(0.55) .. GENERATED FROM PYTHON SOURCE LINES 83-90 Visit Count Distribution -------------------------- The coarsest diagnostic: how many times, :math:`N(p)`, did an ``"observe"`` footprint cover each HEALPix pixel :math:`p`. It says nothing about *when* those visits happened, but it's the right first question: every other diagnostic on this page is only defined where :math:`N(p) \geq 2`. .. GENERATED FROM PYTHON SOURCE LINES 90-98 .. code-block:: Python visit_count = schedule.compute_visit_count(nside=NSIDE) covered = np.count_nonzero(visit_count > 0) print(f"{covered}/{visit_count.size} pixels observed ({covered / visit_count.size:.1%} of the sky)") plot_healpix_map(visit_count, nside=NSIDE, title="Visit count", cbar_label="Visits") .. image-sg:: /auto_examples/schedules/images/sphx_glr_plot_cadence_001.png :alt: Visit count :srcset: /auto_examples/schedules/images/sphx_glr_plot_cadence_001.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none 48626/49152 pixels observed (98.9% of the sky) (
, ) .. GENERATED FROM PYTHON SOURCE LINES 99-103 :meth:`~uvex_transients.surveys.base.SurveySchedule.compute_visit_count_histogram` gives the same information as a proper histogram over *observed* pixels: the one below, plotted on log-log axes since a handful of heavily-revisited pixels span orders of magnitude more visits than the typical one. .. GENERATED FROM PYTHON SOURCE LINES 103-115 .. code-block:: Python visit_counts, pixel_counts = schedule.compute_visit_count_histogram(nside=NSIDE) observed = visit_counts > 0 fig, ax = plt.subplots(figsize=(9, 5.5)) ax.bar(visit_counts[observed], pixel_counts[observed], color=HIST_COLOR) ax.set_xscale("log") ax.set_yscale("log") ax.set_xlabel("Visits to a pixel") ax.set_ylabel("Pixels") ax.set_title("Visit count histogram") .. image-sg:: /auto_examples/schedules/images/sphx_glr_plot_cadence_002.png :alt: Visit count histogram :srcset: /auto_examples/schedules/images/sphx_glr_plot_cadence_002.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Text(0.5, 1.0, 'Visit count histogram') .. GENERATED FROM PYTHON SOURCE LINES 116-128 Pair-wise Cadence ------------------- For every pixel with :math:`N(p) \geq 2`, consider every unique pair of its own visits and how far apart in time they fall (:meth:`~uvex_transients.surveys.base.SurveySchedule.compute_cadence_time_differences`, ``pairs='all'``) and reduce that to per-pixel statistics (:meth:`~uvex_transients.surveys.base.SurveySchedule.compute_cadence_statistics`). The median of that distribution is a general-purpose "how far apart in time were the observations of this point" number, the one to compare against a transient's characteristic fade time to see whether typical sampling could even resolve it. .. GENERATED FROM PYTHON SOURCE LINES 128-139 .. code-block:: Python cadence_stats = schedule.compute_cadence_statistics(nside=NSIDE, pairs="all") median_separation = cadence_stats["median"].to_value(u.day) plot_healpix_map( median_separation, nside=NSIDE, title="Median pairwise separation", cbar_label="Median separation [days]", ) .. image-sg:: /auto_examples/schedules/images/sphx_glr_plot_cadence_003.png :alt: Median pairwise separation :srcset: /auto_examples/schedules/images/sphx_glr_plot_cadence_003.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none (
, ) .. GENERATED FROM PYTHON SOURCE LINES 140-141 The same values, pooled into a histogram rather than mapped by position: .. GENERATED FROM PYTHON SOURCE LINES 141-148 .. code-block:: Python plot_histogram( median_separation, title="Median pairwise separation", xlabel="Median separation [days]", ) .. image-sg:: /auto_examples/schedules/images/sphx_glr_plot_cadence_004.png :alt: Median pairwise separation :srcset: /auto_examples/schedules/images/sphx_glr_plot_cadence_004.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none (
, ) .. GENERATED FROM PYTHON SOURCE LINES 149-157 Successive Gaps ------------------ The ``pairs='consecutive'`` case of the same underlying method isolates the gaps between one visit and the very next one. Unlike the ``'all'`` distribution above, it can't be inflated by long baselines between distant, non-adjacent revisits of the same pixel, so it's the more literal answer to "how long between one look and the next." .. GENERATED FROM PYTHON SOURCE LINES 157-168 .. code-block:: Python consecutive_stats = schedule.compute_cadence_statistics(nside=NSIDE, pairs="consecutive") median_gap = consecutive_stats["median"].to_value(u.day) plot_healpix_map( median_gap, nside=NSIDE, title="Median successive-visit gap", cbar_label="Median gap [days]", ) .. image-sg:: /auto_examples/schedules/images/sphx_glr_plot_cadence_005.png :alt: Median successive-visit gap :srcset: /auto_examples/schedules/images/sphx_glr_plot_cadence_005.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none (
, ) .. GENERATED FROM PYTHON SOURCE LINES 169-174 Pooling every pixel's *individual* successive gaps into one histogram, rather than mapping their per-pixel median as above, shows the shape of that distribution across the whole schedule at once: a sub-day peak from same-block revisits, with a long tail out toward seasonal, solar-avoidance-driven gaps. .. GENERATED FROM PYTHON SOURCE LINES 174-184 .. code-block:: Python successive_gaps, _ = schedule.compute_cadence_time_differences(nside=NSIDE, pairs="consecutive") plot_histogram( successive_gaps.to_value(u.day), title="Successive-gap distribution, pooled over the whole sky", xlabel="Successive-visit gap [days]", ylabel="Pairs of visits", ) .. image-sg:: /auto_examples/schedules/images/sphx_glr_plot_cadence_006.png :alt: Successive-gap distribution, pooled over the whole sky :srcset: /auto_examples/schedules/images/sphx_glr_plot_cadence_006.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none (
, ) .. GENERATED FROM PYTHON SOURCE LINES 185-194 Max Gap --------- The single worst-case successive gap at each pixel (:meth:`~uvex_transients.surveys.base.SurveySchedule.compute_max_gap`): the maximum, rather than the mean or median, of the *Successive Gaps* set. A pixel can look fine on the median-gap map above and still hide one long lapse here, e.g. across a seasonal visibility gap, during which a fast transient could rise and fade without a single supporting observation. .. GENERATED FROM PYTHON SOURCE LINES 194-199 .. code-block:: Python max_gap = schedule.compute_max_gap(nside=NSIDE).to_value(u.day) plot_healpix_map(max_gap, nside=NSIDE, title="Worst-case successive gap", cbar_label="Max gap [days]") .. image-sg:: /auto_examples/schedules/images/sphx_glr_plot_cadence_007.png :alt: Worst-case successive gap :srcset: /auto_examples/schedules/images/sphx_glr_plot_cadence_007.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none (
, ) .. GENERATED FROM PYTHON SOURCE LINES 200-201 The same values, pooled into a histogram rather than mapped by position: .. GENERATED FROM PYTHON SOURCE LINES 201-204 .. code-block:: Python plot_histogram(max_gap, title="Worst-case successive gap", xlabel="Max gap [days]") .. image-sg:: /auto_examples/schedules/images/sphx_glr_plot_cadence_008.png :alt: Worst-case successive gap :srcset: /auto_examples/schedules/images/sphx_glr_plot_cadence_008.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none (
, ) .. GENERATED FROM PYTHON SOURCE LINES 205-218 Pair Counts ------------- The first timescale-specific diagnostic: fix a characteristic transient timescale :math:`T` (here, three days, roughly the rise-or-fade timescale of a kilonova, one of the transient classes this package simulates) and a qualifying separation window :math:`[0.5T,\ 2T]` wide enough to bracket it. :meth:`~uvex_transients.surveys.base.SurveySchedule.compute_pair_counts` counts, per pixel, how many visit pairs fall in that window, and reports the total **sensitive area**: the solid angle of pixels with at least one qualifying pair at all. It's a cheap *existence* question, "could this cadence ever catch a three-day transient rising or fading here?", not yet "for how much of the survey." .. GENERATED FROM PYTHON SOURCE LINES 218-231 .. code-block:: Python kilonova_timescale = 3 * u.day pair_counts, sensitive_area = schedule.compute_pair_counts(kilonova_timescale, nside=NSIDE) print(f"Sensitive area at {kilonova_timescale}: {sensitive_area.to(u.deg**2):.1f}") plot_healpix_map( pair_counts, nside=NSIDE, title=f"Qualifying pairs for a {kilonova_timescale} timescale", cbar_label="Qualifying pairs", ) .. image-sg:: /auto_examples/schedules/images/sphx_glr_plot_cadence_009.png :alt: Qualifying pairs for a 3.0 d timescale :srcset: /auto_examples/schedules/images/sphx_glr_plot_cadence_009.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Sensitive area at 3.0 d: 14907.5 deg2 (
, ) .. GENERATED FROM PYTHON SOURCE LINES 232-233 The same values, pooled into a histogram rather than mapped by position: .. GENERATED FROM PYTHON SOURCE LINES 233-241 .. code-block:: Python plot_histogram( pair_counts, title=f"Qualifying pairs for a {kilonova_timescale} timescale", xlabel="Qualifying pairs", ylabel="Pixels", ) .. image-sg:: /auto_examples/schedules/images/sphx_glr_plot_cadence_010.png :alt: Qualifying pairs for a 3.0 d timescale :srcset: /auto_examples/schedules/images/sphx_glr_plot_cadence_010.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none (
, ) .. GENERATED FROM PYTHON SOURCE LINES 242-255 Control Time -------------- :meth:`~uvex_transients.surveys.base.SurveySchedule.compute_control_time` refines *Pair Counts* from "does a qualifying pair exist" to "for how much of the survey would a transient starting here actually be caught." That distinguishes a pixel with one lucky qualifying pair from one with continuous, repeated cadence support at this timescale, something *Pair Counts* alone can't tell apart. This is the single-timescale half of the diagnostic; sweeping it into a curve over many timescales (:meth:`~uvex_transients.surveys.base.SurveySchedule.compute_control_time_curve`) is exactly the sweep this page skips. See :ref:`user_guide_surveys_cadence` for that version, run on a small synthetic schedule. .. GENERATED FROM PYTHON SOURCE LINES 255-268 .. code-block:: Python control_time, exposure = schedule.compute_control_time(kilonova_timescale, nside=NSIDE) print(f"Area-time exposure at {kilonova_timescale}: {exposure.to(u.deg**2 * u.day):.1f}") control_time_days = control_time.to_value(u.day) plot_healpix_map( control_time_days, nside=NSIDE, title=f"Control time for a {kilonova_timescale} timescale", cbar_label="Control time [days]", ) .. image-sg:: /auto_examples/schedules/images/sphx_glr_plot_cadence_011.png :alt: Control time for a 3.0 d timescale :srcset: /auto_examples/schedules/images/sphx_glr_plot_cadence_011.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none Area-time exposure at 3.0 d: 574858.1 deg2 d (
, ) .. GENERATED FROM PYTHON SOURCE LINES 269-270 The same values, pooled into a histogram rather than mapped by position: .. GENERATED FROM PYTHON SOURCE LINES 270-276 .. code-block:: Python plot_histogram( control_time_days, title=f"Control time for a {kilonova_timescale} timescale", xlabel="Control time [days]", ) .. image-sg:: /auto_examples/schedules/images/sphx_glr_plot_cadence_012.png :alt: Control time for a 3.0 d timescale :srcset: /auto_examples/schedules/images/sphx_glr_plot_cadence_012.png :class: sphx-glr-single-img .. rst-class:: sphx-glr-script-out .. code-block:: none (
, ) .. rst-class:: sphx-glr-timing **Total running time of the script:** (1 minutes 1.471 seconds) .. _sphx_glr_download_auto_examples_schedules_plot_cadence.py: .. only:: html .. container:: sphx-glr-footer sphx-glr-footer-example .. container:: sphx-glr-download sphx-glr-download-jupyter :download:`Download Jupyter notebook: plot_cadence.ipynb ` .. container:: sphx-glr-download sphx-glr-download-python :download:`Download Python source code: plot_cadence.py ` .. container:: sphx-glr-download sphx-glr-download-zip :download:`Download zipped: plot_cadence.zip ` .. only:: html .. rst-class:: sphx-glr-signature `Gallery generated by Sphinx-Gallery `_