def __init__(self, data, chromatic=False, magformat='multiply'): self.chromatic = chromatic # get column names in input data if isinstance(data, Table): colnames = data.colnames elif isinstance(data, np.ndarray): colnames = data.dtype.names elif isinstance(data, dict): colnames = data.keys() else: raise ValueError('unrecognized data type') if self.chromatic: mapping = alias_map(colnames, MLDATA_ALIASES, required=CHROMATIC_MLDATA_REQUIRED_ALIASES) else: mapping = alias_map(colnames, MLDATA_ALIASES, required=ACHROMATIC_MLDATA_REQUIRED_ALIASES) self.magnification = np.asarray(data[mapping['magnification']]) magform = magformat[:2].lower() if magform not in ['ad', 'mu']: raise RuntimeError("``magformat`` must be ``multiply`` or ``add``") if magform == 'ad': self.magnification = 10**(-0.4 * self.magnification) self.time = np.asarray(data[mapping['time']]) if self.chromatic: self.wavelength = np.asarray(data[mapping['wavelength']]) # ensure columns are equal length if isinstance(data, dict): if not (len(self.time) == len(self.magnification)): raise ValueError("unequal column lengths") if self.chromatic: if not (len(self.time) == len(self.wavelength)): raise ValueError("unequal column lengths")
def test_alias_map(): mapping = utils.alias_map(['A', 'B_', 'foo'], {'a': set(['a', 'a_']), 'b': set(['b', 'b_'])}) assert mapping == {'a': 'A', 'b': 'B_'}
def realize_lcs(observations, model, params, thresh=None, trim_observations=False, scatter=True, snrFunc=None): """***A copy of SNCosmo's function, just to add a SNR function Realize data for a set of SNe given a set of observations. Parameters ---------- observations : `~astropy.table.Table` or `~numpy.ndarray` Table of observations. Must contain the following column names: ``band``, ``time``, ``zp``, ``zpsys``, ``gain``, ``skynoise``. model : `sncosmo.Model` The model to use in the simulation. params : list (or generator) of dict List of parameters to feed to the model for realizing each light curve. thresh : float, optional If given, light curves are skipped (not returned) if none of the data points have signal-to-noise greater than ``thresh``. trim_observations : bool, optional If True, only observations with times between ``model.mintime()`` and ``model.maxtime()`` are included in result table for each SN. Default is False. scatter : bool, optional If True, the ``flux`` value of the realized data is calculated by adding a random number drawn from a Normal Distribution with a standard deviation equal to the ``fluxerror`` of the observation to the bandflux value of the observation calculated from model. Default is True. Returns ------- sne : list of `~astropy.table.Table` Table of realized data for each item in ``params``. Notes ----- ``skynoise`` is the image background contribution to the flux measurement error (in units corresponding to the specified zeropoint and zeropoint system). To get the error on a given measurement, ``skynoise`` is added in quadrature to the photon noise from the source. It is left up to the user to calculate ``skynoise`` as they see fit as the details depend on how photometry is done and possibly how the PSF is is modeled. As a simple example, assuming a Gaussian PSF, and perfect PSF photometry, ``skynoise`` would be ``4 * pi * sigma_PSF * sigma_pixel`` where ``sigma_PSF`` is the standard deviation of the PSF in pixels and ``sigma_pixel`` is the background noise in a single pixel in counts. """ RESULT_COLNAMES = ('time', 'band', 'flux', 'fluxerr', 'zp', 'zpsys') lcs = [] # Copy model so we don't mess up the user's model. model = copy(model) # get observations as a Table if not isinstance(observations, Table): if isinstance(observations, np.ndarray): observations = Table(observations) else: raise ValueError("observations not understood") # map column name aliases colname = alias_map(observations.colnames, OBSERVATIONS_ALIASES, required=OBSERVATIONS_REQUIRED_ALIASES) # result dtype used when there are no observations band_dtype = observations[colname['band']].dtype zpsys_dtype = observations[colname['zpsys']].dtype result_dtype = ('f8', band_dtype, 'f8', 'f8', 'f8', zpsys_dtype) for p in params: model.set(**p) # Select times for output that fall within tmin amd tmax of the model if trim_observations: mask = ((observations[colname['time']] > model.mintime()) & (observations[colname['time']] < model.maxtime())) snobs = observations[mask] else: snobs = observations # explicitly detect no observations and add an empty table if len(snobs) == 0: if thresh is None: lcs.append( Table(names=RESULT_COLNAMES, dtype=result_dtype, meta=p)) continue flux = model.bandflux(snobs[colname['band']], snobs[colname['time']], zp=snobs[colname['zp']], zpsys=snobs[colname['zpsys']]) if snrFunc is not None: fluxerr = flux / snrFunc(-2.5 * np.log10(flux) + snobs[colname['zp']]) else: fluxerr = np.sqrt(snobs[colname['skynoise']]**2 + np.abs(flux) / snobs[colname['gain']]) # Scatter fluxes by the fluxerr # np.atleast_1d is necessary here because of an apparent bug in # np.random.normal: when the inputs are both length 1 arrays, # the output is a Python float! if scatter: flux = np.atleast_1d(np.random.normal(flux, fluxerr)) # Check if any of the fluxes are significant if thresh is not None and not np.any(flux / fluxerr > thresh): continue data = [ snobs[colname['time']], snobs[colname['band']], flux, fluxerr, snobs[colname['zp']], snobs[colname['zpsys']] ] lcs.append(Table(data, names=RESULT_COLNAMES, meta=p)) return lcs