Source code for rail.creation.degradation.spectroscopic_degraders

"""Degraders that emulate spectroscopic effects on photometry"""

import numpy as np
import pandas as pd
from rail.creation.degrader import Degrader


[docs]class LineConfusion(Degrader): """Degrader that simulates emission line confusion. .. code-block:: python Example: degrader = LineConfusion(true_wavelen=3727, wrong_wavelen=5007, frac_wrong=0.05) is a degrader that misidentifies 5% of OII lines (at 3727 angstroms) as OIII lines (at 5007 angstroms), which results in a larger spectroscopic redshift. Note that when selecting the galaxies for which the lines are confused, the degrader ignores galaxies for which this line confusion would result in a negative redshift, which can occur for low redshift galaxies when wrong_wavelen < true_wavelen. Parameters ---------- true_wavelen : positive float The wavelength of the true emission line. Wavelength unit assumed to be the same as wrong_wavelen. wrong_wavelen : positive float The wavelength of the wrong emission line, which is being confused for the correct emission line. Wavelength unit assumed to be the same as true_wavelen. frac_wrong : float between zero and one The fraction of galaxies with confused emission lines. """ name = 'LineConfusion' config_options = Degrader.config_options.copy() config_options.update(true_wavelen=float, wrong_wavelen=float, frac_wrong=float) def __init__(self, args, comm=None): """ """ Degrader.__init__(self, args, comm=comm) # validate parameters if self.config.true_wavelen < 0: raise ValueError("true_wavelen must be positive, not {self.config.true_wavelen}") if self.config.wrong_wavelen < 0: raise ValueError("wrong_wavelen must be positive, not {self.config.wrong_wavelen}") if self.config.frac_wrong < 0 or self.config.frac_wrong > 1: raise ValueError("frac_wrong must be between 0 and 1., not {self.config.wrong_wavelen}")
[docs] def run(self): """ Run method Applies line confusion Notes ----- Get the input data from the data store under this stages 'input' tag Puts the data into the data store under this stages 'output' tag """ data = self.get_data('input') # convert to an array for easy manipulation values, columns = data.values.copy(), data.columns.copy() # get the minimum redshift # if wrong_wavelen < true_wavelen, this is minimum the redshift for # which the confused redshift is still positive zmin = self.config.wrong_wavelen / self.config.true_wavelen - 1 # select the random fraction of galaxies whose lines are confused rng = np.random.default_rng(self.config.seed) idx = rng.choice( np.where(values[:, 0] > zmin)[0], size=int(self.config.frac_wrong * values.shape[0]), replace=False, ) # transform these redshifts values[idx, 0] = ( 1 + values[idx, 0] ) * self.config.true_wavelen / self.config.wrong_wavelen - 1 # return results in a data frame outData = pd.DataFrame(values, columns=columns) self.add_data('output', outData)
[docs]class InvRedshiftIncompleteness(Degrader): """Degrader that simulates incompleteness with a selection function inversely proportional to redshift. The survival probability of this selection function is p(z) = min(1, z_p/z), where z_p is the pivot redshift. Parameters ---------- pivot_redshift : positive float The redshift at which the incompleteness begins. """ name = 'InvRedshiftIncompleteness' config_options = Degrader.config_options.copy() config_options.update(pivot_redshift=float) def __init__(self, args, comm=None): """ """ Degrader.__init__(self, args, comm=comm) if self.config.pivot_redshift < 0: raise ValueError("pivot redshift must be positive, not {self.config.pivot_redshift}")
[docs] def run(self): """ Run method Applies incompleteness Notes ----- Get the input data from the data store under this stages 'input' tag Puts the data into the data store under this stages 'output' tag """ data = self.get_data('input') # calculate survival probability for each galaxy survival_prob = np.clip(self.config.pivot_redshift / data["redshift"], 0, 1) # probabalistically drop galaxies from the data set rng = np.random.default_rng(self.config.seed) mask = rng.random(size=data.shape[0]) <= survival_prob self.add_data('output', data[mask])