streamobs.observed module#
- class streamobs.observed.StreamInjector(survey, primary=None, **kwargs)[source]#
Bases:
objectInject observational effects into stream data for one or more surveys.
A single injector handles both the single- and multi-survey cases: pass one survey or several. The same shared sky placement and a single shared draw of true magnitudes (masses sampled once via the isochrone and interpolated into every survey’s bands) guarantee the same physical star gets consistent magnitudes across surveys. Each survey contributes its own
<survey>_<band>_obs/<survey>_<band>_err/<survey>_flag_observedcolumns, computed with that survey’s own maglim maps and completeness functions. Output columns are always survey-namespaced, even for a single survey.All survey data is loaded once and cached, making multiple injections efficient.
- surveys#
{namespace: Survey}for every survey this injector serves. The namespace is the survey’s{name}_{release}and is the column prefix (lsst_yr5_r_obs,roman_dc2_F158_obs, …).- Type:
dict
- primary#
The survey whose footprint drives the shared sky placement and whose
_save_injected_datais used (also available assurvey). Its namespace string isprimary_namespace.- Type:
- mask_cache#
Cache of previously created HEALPix masks to avoid recomputation.
- Type:
dict (class attribute)
- _last_gc_frame#
The most recently used great circle frame. This allows reusing the same sky location across multiple inject() calls when gc_frame=’last’.
- Type:
GreatCircleICRSFrame or None
Examples
Single survey (columns namespaced
{name}_{release}):>>> injector = StreamInjector('lsst', release='dc2') >>> out = injector.inject(df, bands=['r', 'g']) # -> lsst_dc2_r_obs, ...
Several surveys at once — each spec carries its release; the namespace is derived from it, so bands are keyed by
{name}_{release}:>>> injector = StreamInjector([ ... {'survey': 'lsst', 'release': 'dc2'}, ... {'survey': 'roman', 'release': 'dc2'}, ... ]) >>> out = injector.inject( ... df, ... bands={'lsst_dc2': ['r', 'g'], 'roman_dc2': ['F106', 'F158']}, ... stream_config=scene['stream'], seed=42, ... )
- classmethod clear_mask_cache()[source]#
Clear the mask cache.
This can be useful if you want to free memory or force masks to be recomputed.
Examples
>>> StreamInjector.clear_mask_cache()
- complete_data(data, bands=None, stream_config=None, dist=None, **kwargs)[source]#
Complete the columns the injector needs, filling the rest from the config.
Public helper: give it a (possibly partial) catalog and it returns one with everything the injector requires present — sky coordinates (
ra/dec, converting fromphi1/phi2if needed) and the per-survey true-magnitude columns<survey>_<band>_true. Anything already present is preserved; only missing columns are sampled, usingstream_config(aStreamModelconfig). The stellar masses are drawn once and interpolated into every survey’s bands, so the same physical star is consistent across surveys.This is the same completion
inject()runs internally, exposed so you can build/inspect a completed catalog without injecting noise.- Parameters:
data (str or pandas.DataFrame) – Input catalog (or path). May contain only stream coordinates (
phi1/phi2orra/dec), an all-empty frame of lengthN, or any subset of the target columns.bands (list of str or dict, optional) – Bands whose true-magnitude columns to ensure. A
{survey_name: [bands]}dict selects bands per survey (multi-survey form); a plain list/tuple is the single-survey shorthand. If omitted and there is exactly one survey, defaults to['r', 'g'].stream_config (dict, optional) –
StreamModelconfig used to sample any missing geometry / true magnitudes. Required only when something is missing.dist (float or array-like or None, optional) – Distance modulus to use directly (scalar broadcast or per-row vector) instead of sampling from the config’s
distance_modulusmodel. Lets magnitudes be filled withoutphi1/ a distance model.**kwargs –
rng/seedfor reproducibility, plusgc_frame,mask_type,percentile_threshold,max_iterforwarded tophi_to_radec()when convertingphi1/phi2→ra/dec.
- Returns:
A copy of the input with
ra/decand the requested<survey>_<band>_truecolumns present.- Return type:
pandas.DataFrame
- Raises:
ValueError – If neither (ra, dec) nor (phi1, phi2) are present, or if columns are missing and
stream_configis not provided.
Examples
>>> df = pd.DataFrame({'phi1': [-5, 0, 5], 'phi2': [0, 0, 0]}) >>> out = injector.complete_data(df, bands=['r', 'g'], stream_config=cfg) >>> out = injector.complete_data( ... df, bands={'lsst': ['r', 'g'], 'roman': ['F106', 'F158']}, ... stream_config=cfg, seed=42, ... )
- detect_flag(pix, survey, mag=None, band='r', **kwargs)[source]#
Apply the survey selection to determine detection flags.
For stars (
source_type='stars', the default), uses the survey’s combined completeness (detection × classification) or detection-only efficiency whenperfect_galstarsep=True. For galaxies (source_type='galaxies'), usesget_gal_misclassification_detection()and ignoresperfect_galstarsep.- Parameters:
pix (int or np.ndarray) – HEALPix pixel index/indices.
mag (float or np.ndarray, optional) – Magnitude(s). Default is None.
band (str, optional) – Band to consider for detection. Default is ‘r’.
survey (Survey) – Survey whose efficiency curves to use (required).
**kwargs –
Additional keyword arguments:
- rngnumpy.random.Generator, optional
Random number generator instance.
- seedint, optional
Random seed if rng is not provided.
- source_typestr, optional
'stars'(default) or'galaxies'.- perfect_galstarsepbool, optional
If True and
source_type='stars', uses detection-only efficiency (no classification losses). Ignored for galaxies. Default is False.
- Returns:
Boolean array: True for detected objects, False otherwise.
- Return type:
np.ndarray
- Raises:
ValueError – If magnitude values are not provided.
- static fluxToMag(flux)[source]#
Convert from flux to AB magnitude.
- Parameters:
flux (float or np.ndarray) – Flux in Janskys (Jy).
- Returns:
AB magnitude(s).
- Return type:
float or np.ndarray
- static getFluxError(mag, mag_error)[source]#
Convert magnitude error to flux error.
- Parameters:
mag (float or np.ndarray) – Magnitude(s).
mag_error (float or np.ndarray) – Magnitude error(s).
- Returns:
Flux error in Janskys (Jy).
- Return type:
float or np.ndarray
- inject(data, bands=None, stream_config=None, **kwargs)[source]#
Add observed quantities from every survey into a single catalog.
Applies observational effects (photometric errors, measured magnitudes, detection flags) for each survey this injector serves. A single shared sky placement and a single shared true-magnitude fill (masses sampled once and interpolated into every survey’s bands) ensure the same physical star is consistent across surveys. Output columns are always survey-namespaced (
<survey>_<band>_obsetc.).- Parameters:
data (str or pd.DataFrame) – Input data as DataFrame or path to the file (CSV or Excel). May contain only stream coordinates (
phi1/phi2orra/dec); anything missing is sampled fromstream_config. An all-empty frame of lengthNis accepted (geometry and magnitudes are then sampled forNrows).bands (list of str or dict, optional) – Bands to inject. A
{survey_name: [bands]}dict selects bands per survey (the multi-survey form; keys must match the surveys this injector was built with). A plain list/tuple is the single-survey shorthand, applied to the only survey. If omitted and there is exactly one survey, defaults to['r', 'g'].stream_config (dict, optional) – The
streamsection consumed byStreamModel. Required when any coordinate or true-magnitude column is missing. Its isochrone produces the shared<survey>_<band>_truecolumns.**kwargs –
Additional keyword arguments:
- seedint, optional
Random seed for reproducibility.
- distfloat or array-like, optional
Distance modulus used directly (scalar broadcast or per-row vector) instead of sampling from the config’s
distance_modulusmodel — lets magnitudes be filled withoutphi1.- nsideint, optional
HEALPix nside parameter. Default is 4096.
- detection_mag_cutlist of str, optional
Non-reference bands to apply the explicit SNR>=5 cut to. The reference band (
survey.completeness_band) is never cut here — its SNR cut is already baked into the survey’s selection functions — so the default is every injected band except the reference band. Net effect: every injected band must have SNR >= 5, with the reference band’s cut owned by the curves.- savebool, optional
Whether to save the output data. Default is False.
- folderstr or Path, optional
Output folder path if save=True.
- dust_correctionbool, optional
Whether to apply dust correction to observed magnitudes. Default is True.
- perfect_galstarsepbool, optional
If True, also computes a flag assuming perfect star/galaxy separation (detection efficiency only, no classification losses). Default is False. Only applies when
source_type='stars'.- source_typestr, optional
Type of source being injected. Either
'stars'(default) or'galaxies'. When'galaxies', the detection flag usesget_gal_misclassification()instead of the star completeness function.- verbosebool, optional
Whether to print progress information. Default is True.
- Returns:
DataFrame with shared
ra/decand, per survey:<survey>_<band>_true : True (noiseless) apparent magnitudes
<survey>_<band>_obs : Observed (noisy) magnitudes
<survey>_<band>_err : Reported photometric errors
<survey>_flag_observed : Boolean detection flag (detection and classification efficiencies)
<survey>_flag_perfect_galstarsep : Boolean flag assuming perfect star/galaxy separation (only if perfect_galstarsep=True)
- Return type:
pd.DataFrame
- Raises:
ValueError – If required columns are missing, if
bands(as a dict) references an unknown survey, or if a listbandsis given for a multi-survey injector.
- classmethod list_cached_masks()[source]#
List all cached masks.
- Returns:
List of cache keys (survey_name, mask_types, ebv_threshold)
- Return type:
list of tuples
Examples
>>> StreamInjector.list_cached_masks() [('LSST', ('footprint', 'maglim_r'), None), ('LSST', ('ebv', 'footprint'), 0.2)]
- static magToFlux(mag)[source]#
Convert from AB magnitude to flux.
- Parameters:
mag (float or np.ndarray) – AB magnitude(s).
- Returns:
Flux in Janskys (Jy).
- Return type:
float or np.ndarray
- phi_to_radec(phi1, phi2, gc_frame=None, seed=None, rng=None, mask_type=['footprint'], **kwargs)[source]#
Transform stream coordinates (phi1, phi2) to sky coordinates (RA, Dec).
This method converts stream coordinates to celestial coordinates using a great circle frame. If no frame is provided, it automatically finds one randomly chosen such that a given percentile of the points lie within the mask defined with mask_type.
The frame used (whether provided or generated) is stored in
self._last_gc_framefor potential reuse viagc_frame='last'in subsequent calls.- Parameters:
phi1 (array-like) – Stream coordinates in degrees.
phi2 (array-like) – Stream coordinates in degrees.
gc_frame (gala.coordinates.GreatCircleICRSFrame or 'last', optional) – Great circle coordinate frame. If None, will be automatically determined. If ‘last’, uses the frame from the previous call (stored in self._last_gc_frame).
seed (int, optional) – Random seed for reproducible frame selection.
rng (numpy.random.Generator, optional) – Random number generator instance.
mask_type (list of str, optional) – Types of masks to use for footprint validation. Options: [“footprint”, “maglim_g”, “maglim_r”, “ebv”]. Default is [“footprint”].
**kwargs –
Additional keyword arguments passed to _find_gc_frame():
- percentile_thresholdfloat, optional
Minimum fraction of points that must be in mask. Default is 0.99.
- max_iterint, optional
Maximum number of random trials. Default is 1000.
- Returns:
Sky coordinates in ICRS frame.
- Return type:
astropy.coordinates.SkyCoord
- Raises:
ValueError – If phi1 and phi2 have different lengths or contain invalid values.
RuntimeError – If no suitable great circle frame could be found.
Examples
Convert stream coordinates to sky coordinates:
>>> phi1 = np.linspace(-10, 10, 1000) >>> phi2 = np.zeros_like(phi1) >>> coords = injector.phi_to_radec(phi1, phi2, seed=42)
Reuse the frame from a previous call:
>>> coords2 = injector.phi_to_radec(phi1_2, phi2_2, gc_frame='last')
- plot_stream_in_mask(data, mask_type, ebv_threshold=0.2, **kwargs)[source]#
Plot the stream over the footprint mask.
Creates a visualization showing the stream’s position relative to the survey footprint or other masks.
- Parameters:
data (pd.DataFrame) – Data containing ‘ra’ and ‘dec’ columns.
mask_type (str or list of str) – Type(s) of masks to plot. Options: [“footprint”, “coverage”, “maglim_<band>”, “ebv”].
ebv_threshold (float, optional) – E(B-V) threshold (only used if ‘ebv’ in mask_type). Default is 0.2.
**kwargs –
Additional arguments passed to plotting function:
- output_folderstr, optional
Path to save the figure.
- Returns:
fig (matplotlib.figure.Figure) – The figure object.
ax (matplotlib.axes.Axes) – The axes object.
- Raises:
ValueError – If mask cannot be created from mask_type parameter.
Examples
Plot stream in footprint:
>>> fig, ax = injector.plot_stream_in_mask(data, ['footprint', 'maglim_r'])
Plot with custom E(B-V) threshold:
>>> fig, ax = injector.plot_stream_in_mask( ... data, ['footprint', 'ebv'], ebv_threshold=0.15 ... )
- property primary#
The primary
Survey.Drives the shared sky placement; mask, coordinate and footprint helpers operate on it. Its column namespace is
primary_namespace.
- property primary_namespace#
Column namespace (
{name}_{release}) of the primary survey.
- sample_measured_magnitudes(mag_true, mag_err, **kwargs)[source]#
Sample measured magnitudes from true apparent magnitudes and errors.
This method adds photometric noise to true magnitudes by sampling from a Gaussian distribution in flux space.
- Parameters:
mag_true (float or np.ndarray) – True apparent magnitude(s).
mag_err (float or np.ndarray) – Magnitude error(s).
**kwargs –
Additional keyword arguments:
- rngnumpy.random.Generator, optional
Random number generator instance.
- seedint, optional
Random seed if rng is not provided.
- Returns:
Measured magnitude(s). Returns “BAD_MAG” for objects with negative flux.
- Return type:
np.ndarray or str