Comparing Multiple Targets with Sample#

When planning observations, it is common to ask: how does the required exposure time change with redshift? or how much data will I accumulate over a survey?

The slicersim.sample.Sample class lets you manage a collection of targets together and answer these questions efficiently.

import numpy as np
import matplotlib.pyplot as plt
import slicersim
from slicersim.sample import Sample

1. Build a sample of type Ia supernovae#

Sample.from_sneia() creates a collection of LazuliSupernova objects at the given redshifts. The spectrograph field ("narrow" or "wide") is automatically selected based on the redshift: "narrow" up to z=0.8, "wide" beyond.

# Create a sample of 8 SN Ia spanning z=0.2 to z=1.4
redshifts = np.linspace(0.2, 1.4, 8)
sample = Sample.from_sneia(redshifts)
print(f"Sample has {sample.ntargets} targets")
Sample has 8 targets

You can access individual targets by index:

# Inspect one target
t0 = sample.get_target(0)
print(f"First target: z = {t0.get_properties('redshift'):.2f}, field = {t0.get_spectrograph_field()}")
First target: z = 0.20, field = ('narrow', {'spatial_shape': [58, 58], 'spatial_scale': 0.04})

2. Set up the sample to reach a given SNR#

setup_to_snr() configures the detector readout mode of each target so that the requested mean SNR per resolution element is reached in the given wavelength window (here rest-frame).

%%time
# Aim for SNR=10 per resolution element in rest-frame 4000-7000 A
_ = sample.setup_to_snr(
    snr=10,
    per_resolution=True,
    lbda_range=[4000, 7000],
    frame="rest",
)
CPU times: user 2.11 s, sys: 742 ms, total: 2.86 s
Wall time: 2.86 s

3. Summary statistics#

get_summary_stats() returns a DataFrame with the exposure time, the data volume and the detector readout configuration of each target.

stats = sample.get_summary_stats()
stats.index = np.round(redshifts, 2)
stats.index.name = "redshift"
stats
volume exptime nramps ngroups nframes_per_group ndrops
redshift
0.20 0.369099 124.52 1 11 4 0
0.37 0.771752 260.36 1 23 4 0
0.54 0.603980 407.52 1 18 8 0
0.71 0.838861 566.00 1 25 8 0
0.89 1.073742 724.48 1 32 8 0
1.06 1.308623 882.96 1 39 8 0
1.23 2.113929 1426.32 1 63 8 0
1.40 3.623879 2445.12 2 54 8 0

Notice how the readout mode (ngroups, nframes_per_group) and the number of ramps (nramps) increase with redshift, as the source gets fainter and longer exposures are needed.


4. Exposure time vs. redshift#

The cost of observing a SN Ia grows steeply with redshift.

exptimes_h = sample.get_exposure_time(units="hour")

fig, ax = plt.subplots(figsize=(6, 4))
ax.plot(redshifts, exptimes_h, "o-", color="steelblue")
ax.set_xlabel("Redshift")
ax.set_ylabel("Exposure time [hours]")
ax.set_title("Required exposure time for SNR=10 (SN Ia, SALT model)")
ax.grid(alpha=0.3)
plt.tight_layout()
../_images/3e394833a2d644bb9f112f9ed34dd714c8f09a3d72e171539e65c4bd060b5038.png

5. Data volume vs. redshift#

Higher-redshift targets require longer or more ramps, which increases the raw data volume of each observation.

data_volumes = sample.get_data_volume(units="GB")

fig, ax = plt.subplots(figsize=(6, 4))
ax.bar(redshifts, data_volumes, width=0.07, color="coral", edgecolor="k", linewidth=0.5)
ax.set_xlabel("Redshift")
ax.set_ylabel("Data volume per observation [GB]")
ax.set_title("Data volume per SN Ia observation")
ax.grid(axis="y", alpha=0.3)
plt.tight_layout()
../_images/a8b7782201bb9b093c3243445d326c0a8bcdb8b39a36ab6bc1269f4a7a30f6f4.png

6. Survey duration estimate#

get_total_surveyduration() scales the exposure times of the sample to estimate how long a larger survey would take. It assumes that the survey targets follow the same distribution as the sample. By default, it assumes a survey of 8000 SN Ia and that 40% of the mission time is spent on it (fraction_survey=0.4).

# How many years would a 2000-target survey take with 40% of the mission time?
duration_yr = sample.get_total_surveyduration(sample_size=2000, fraction_survey=0.4, units="yr")
print(f"Estimated survey duration: {duration_yr:.2f} years")
Estimated survey duration: 0.14 years

7. Visualizing spectra across the sample#

Loop over targets to retrieve and plot individual spectra.

fig, ax = plt.subplots(figsize=(8, 4))

cmap = plt.cm.viridis
for i, target in enumerate(sample.targets):
    lbda, flux, variance = target.get_spectrum(unit="flambda", incl_error=False)
    color = cmap(i / (sample.ntargets - 1))
    z = target.get_properties("redshift")
    ax.plot(lbda / 1e4, flux / flux.max(), lw=0.8, color=color, label=f"z={z:.2f}")

ax.set_xlabel("Wavelength [micron]")
ax.set_ylabel("Normalized flux")
ax.set_title("SN Ia spectra across redshift")
ax.legend(fontsize=7, ncol=2)
ax.set_xlim(0.4, 1.8)
plt.tight_layout()
../_images/bf21a3928b80b101b22021582de116f23d84b157fe61f3549b1f0cff1f50b314.png

As expected, the SN Ia spectral features shift to longer wavelengths at higher redshifts.