streamobs.observed module#

class streamobs.observed.StreamInjector(survey, primary=None, **kwargs)[source]#

Bases: object

Inject 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_observed columns, 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_data is used (also available as survey). Its namespace string is primary_namespace.

Type:

Survey

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 from phi1/phi2 if needed) and the per-survey true-magnitude columns <survey>_<band>_true. Anything already present is preserved; only missing columns are sampled, using stream_config (a StreamModel config). 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/phi2 or ra/dec), an all-empty frame of length N, 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) – StreamModel config 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_modulus model. Lets magnitudes be filled without phi1 / a distance model.

  • **kwargsrng / seed for reproducibility, plus gc_frame, mask_type, percentile_threshold, max_iter forwarded to phi_to_radec() when converting phi1/phi2ra/dec.

Returns:

A copy of the input with ra/dec and the requested <survey>_<band>_true columns present.

Return type:

pandas.DataFrame

Raises:

ValueError – If neither (ra, dec) nor (phi1, phi2) are present, or if columns are missing and stream_config is 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 when perfect_galstarsep=True. For galaxies (source_type='galaxies'), uses get_gal_misclassification_detection() and ignores perfect_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>_obs etc.).

Parameters:
  • data (str or pd.DataFrame) – Input data as DataFrame or path to the file (CSV or Excel). May contain only stream coordinates (phi1/phi2 or ra/dec); anything missing is sampled from stream_config. An all-empty frame of length N is accepted (geometry and magnitudes are then sampled for N rows).

  • 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 stream section consumed by StreamModel. Required when any coordinate or true-magnitude column is missing. Its isochrone produces the shared <survey>_<band>_true columns.

  • **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_modulus model — lets magnitudes be filled without phi1.

    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 uses get_gal_misclassification() instead of the star completeness function.

    verbosebool, optional

    Whether to print progress information. Default is True.

Returns:

DataFrame with shared ra/dec and, 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 list bands is 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_frame for potential reuse via gc_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

property survey#

Alias for primary — the primary Survey.