예제 #1
0
def transform_bands_msd(msd, transform, fill_value=np.nan):
    """Convert light curve to another band set

    Parameters
    ----------
    msd: MultiStateData
        Initial light curve object, `y` is `10^(-0.4 magn)` as produced by
        `OSCCurve`. All light curves should have the same length
    transform: BandTransformation
        One of `VR_to_gri`, `BR_to_gri`, `BRI_to_gri`, defined in this module
    fill_value: float
        Value to fill corrupt data, i.e. non-positive fluxes

    Returns
    -------
    MultiStateData

    """
    old_dict = {
        band: -2.5 * np.log10(msd.odict[band].y)
        for band in transform.old_bands
    }
    magn = transform(old_dict)
    new_odict = OrderedDict()
    x = msd.odict[transform.old_bands[0]].x
    err = np.full_like(x, np.nan)
    for band in transform.new_bands:
        y = 10**(-0.4 * magn[band])
        y[np.isnan(y)] = fill_value
        new_odict[band] = np.rec.array((x, y, err),
                                       dtype=[('x', float), ('y', float),
                                              ('err', float)])
    new_msd = MultiStateData.from_state_data(new_odict)
    return new_msd
예제 #2
0
    def __call__(self, x=None, fill_error=False):
        """Evaluates the approximation for all the bands.

        Parameters
        ----------
        x: 1-d ndarray or None
            Either evaluate all the bands at the specific x values, or use initial
            ones when the supplied x is equal to None.
        fill_error: bool
            Should the approximation errors be estimated.
        """

        odict = OrderedDict()

        xx = x
        for band in self.bands:

            if x is None:
                xx = self.curve[band]['x']

            recarr = np.recarray(len(xx), [('x', 'd'), ('y', 'd'),
                                           ('err', 'd')])
            recarr['x'] = xx
            recarr['y'] = self.band_approximation(band, xx)
            recarr['err'] = self.band_approximation_error(
                band, xx) if fill_error else np.zeros(len(xx))
            odict[band] = recarr

        return MultiStateData.from_state_data(odict)
예제 #3
0
    def set_error(self, absolute=0, rel=0):
        """Return new SNCurve with set errors for dots without them

        The equation for the error is `err = absolute + rel * y`.
        Upper limits will not be changed.

        Parameters
        ----------
        absolute: float or dict-like[str: float], optional
            Can be dictionary where keys are bands
        rel: float, optional

        Returns
        -------
        SNCurve
        """
        d = dict(self.odict)
        for band, lc in d.items():
            lc = d[band] = lc.copy()
            was_writeable = lc.flags.writeable
            lc.flags.writeable = True
            inf_err_idx = (~(lc['isupperlimit'])) & (~np.isfinite(lc['err']))
            if isinstance(absolute, Real):
                abs_band = absolute
            else:
                abs_band = absolute[band]
            lc.err[inf_err_idx] = abs_band + rel * lc[inf_err_idx].y
            lc.flags.writeable = was_writeable
        msd = MultiStateData.from_state_data(d)
        return SNCurve(msd, self.name, is_binned=self.is_binned, is_filtered=False,
                       additional_attrs=self.__additional_attrs)
예제 #4
0
 def test_msd(self):
     x = np.array([0.])
     err = np.array([0.])
     magnB = 20
     magnR = 20
     magnI = 20
     items = (('B', np.rec.array((x, np.array([10 ** (-0.4 * magnB)]), err),
                                 dtype=[('x', float), ('y', float), ('err', float)])),
              ('R', np.rec.array((x, np.array([10 ** (-0.4 * magnR)]), err),
                                 dtype=[('x', float), ('y', float), ('err', float)])),
              ('I', np.rec.array((x, np.array([10 ** (-0.4 * magnI)]), err),
                                 dtype=[('x', float), ('y', float), ('err', float)])))
     msd = MultiStateData.from_state_data(items)
     new_msd = transform_bands_msd(msd, BRI_to_gri)
     magn = -2.5 * np.log10((new_msd.arrays.y * new_msd.norm))
     assert_allclose(magn, [19.84211267, 20.06126321, 20.32025792])
예제 #5
0
    def convert_dict(self, d, is_binned=False, is_filtered=False):
        """Convert dict to SNCurve with the same attributes

        Parameters
        ----------
        d: dict-like
            It should has the same format as `.odict`
        is_binned: bool, optional
        is_filtered: bool, optional

        Returns
        -------
        SNCurve
        """
        msd = MultiStateData.from_state_data(d)
        return self.convert_msd(msd, is_binned, is_filtered)
예제 #6
0
    def binned(self, bin_width, discrete_time=False, bands=None):
        """Binned photometry data

        The eges of the bins will be produced by the formula
        `time // bin_width * bin_width`. If upper limit dots are not the
        only dots in the sample, they will be excluded, as the dots with
        infinite errors. If only upper limit dots are presented, the best will
        be used, if only infinite error dots are presented, their mean will be
        used. If any dots with finite errors are presented, then weighed mean
        and corresponding error is calculated. For `discrete_time=True`
        original time errors `err_x` are ignored.

        Parameters
        ----------
        bin_width: float or None, optional
            The width of samples, in the units of photometry time dots
        discrete_time: bool, optional
            If `True`, all time steps between dots will be a multiple of
            `bin_width`, else weighted time moments will be used
        bands: iterable of str or None, optional
            Bands to use. Default is None, SNCurve.bands will be used

        Returns
        -------
        SNCurve

        Raises
        ------
        EmptyPhotometryError
        """
        if bands is None:
            bands = self.bands
        else:
            bands = _transform_to_tuple(bands)
        if set(bands).difference(self.bands):
            raise EmptyPhotometryError(self.name, set(bands) - set(self.bands))
        msd = MultiStateData.from_state_data((band, self._binning(self[band], bin_width, discrete_time))
                                             for band in bands)
        return SNCurve(msd, name=self.name,
                       is_binned=True, is_filtered=self.is_filtered,
                       additional_attrs=self.__additional_attrs)
예제 #7
0
    def setUp(self):
        x = np.arange(-20, 50, 0.5)
        self.sigma = 0.05
        self.fall_time = 20
        self.rise_time = 5
        self.bottom = 0.2
        self.scale = 2.0

        np.random.seed(0)

        odict = OrderedDict()
        recarr = np.recarray(x.shape, [('x', 'd'), ('y', 'd'), ('err', 'd')])
        recarr['x'] = x
        recarr['y'] = self.bottom + self.scale / (np.exp(-x / self.rise_time) + np.exp(x / self.fall_time)) +\
                      np.random.normal(0, self.sigma, x.shape)
        recarr['err'] = np.repeat(self.sigma, x.shape)
        odict['r'] = recarr

        self.msd = MultiStateData.from_state_data(odict)
        self.bf = BazinFitter(self.msd, name='Generated MSD')
        self.bf.fit()
예제 #8
0
def zero_negative_fluxes(msd, x_peak=None):
    """Convert negative y-values to zero

    Parameters
    ----------
    msd: MultiStateData
        Light curves to process
    x_peak: float or None, optional
         If `x_peak` is `None` than all negative values are zeroed. Otherwise
         the closest (in time, aka `x`) "left" and "right" negative values are
         searched and all values before "left" and after "right" time moment
         are zeroed, even if some of them are positive

    Returns
    -------
    MultistateData

    Examples
    --------
    Zero all negative values:

    >>> import numpy as np
    >>> from multistate_kernel.util import MultiStateData
    >>> from snad.process.util import zero_negative_fluxes
    >>>
    >>> x = np.arange(5, dtype=np.float)
    >>> y = np.array([-3., 5., -2., 1., -77.])
    >>> err = np.zeros_like(y)  # doesn't matter in this case
    >>> lc = np.rec.fromarrays([x, y, err], names='x,y,err')
    >>> msd = MultiStateData.from_state_data({'X': lc})
    >>> zeroed_msd = zero_negative_fluxes(msd)
    >>> print(zeroed_msd.odict['X'].y)
    [0. 5. 0. 1. 0.]

    Zero "wings" around peak:

    >>> import numpy as np
    >>> from multistate_kernel.util import MultiStateData
    >>> from snad.process.util import zero_negative_fluxes
    >>>
    >>> x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], dtype=np.float)
    >>> x_peak = 5.
    >>> y = np.array([1,-1,-2, 1, 2, 3, 2, 1, -1,-2,-3], dtype=np.float)
    >>> err = np.zeros_like(y)  # doesn't matter in this case
    >>> lc = np.rec.fromarrays([x, y, err], names='x,y,err')
    >>> msd = MultiStateData.from_state_data({'X': lc})
    >>> zeroed_msd = zero_negative_fluxes(msd, x_peak=x_peak)
    >>> print(zeroed_msd.odict['X'].y)
    [0. 0. 0. 1. 2. 3. 2. 1. 0. 0. 0.]

    """
    new_odict = OrderedDict()
    for band, lc in msd.odict.items():
        new_odict[band] = lc = copy.deepcopy(lc)
        if x_peak is None:
            idx = lc.y < 0
            lc.y[idx] = 0
            lc.err[idx] = 0
        else:
            for i in range(lc.x.size - 1, -1, -1):
                if lc.x[i] >= x_peak:
                    continue
                if lc.y[i] <= 0:
                    i += 1
                    break
            i_left_nonzero = i
            for i in range(lc.x.size):
                if lc.x[i] <= x_peak:
                    continue
                if lc.y[i] <= 0:
                    i -= 1
                    break
            i_right_nonzero = i
            lc.y[:i_left_nonzero] = 0
            lc.err[:i_left_nonzero] = 0
            lc.y[i_right_nonzero + 1:] = 0
            lc.err[i_right_nonzero + 1:] = 0
    msd = MultiStateData.from_state_data(new_odict)
    return msd
예제 #9
0
    def __init__(self, json_data, bands=None):
        d = dict()

        self._json = json_data
        name = self._json['name']

        if bands is not None:
            bands = _transform_to_tuple(bands)
            bands_set = set(bands)

        add_attrs = {}
        for func, fields in iteritems(self.__additional_value_fields):
            for field in fields:
                add_attrs[field] = tuple(func(x['value']) for x in self._json.get(field, []))
        add_attrs['has_spectra'] = 'spectra' in self._json
        add_attrs['spectrum_count'] = 0
        if add_attrs['has_spectra']:
            add_attrs['spectrum_count'] = len(self._json['spectra'])

        if 'photometry' not in self._json:
            raise NoPhotometryError(name)
        for dot in self._json['photometry']:
            if 'time' in dot and 'band' in dot:
                # Model data, not real observation
                if 'realization' in dot or 'model' in dot:
                    continue

                # Observation of host, not target object
                if 'host' in dot:
                    continue

                if (bands is not None) and (dot.get('band') not in bands_set):
                    continue

                band_curve = d.setdefault(dot['band'], [])

                time = dot['time']
                if isinstance(time, list):
                    time = np.mean([float(t) for t in time])

                if 'e_time' in dot:
                    e_time = float(dot['e_time'])
                    if e_time < 0 or not np.isfinite(e_time):
                        raise BadPhotometryDotError(name, dot, 'e_time')
                else:
                    e_time = np.nan

                magn = float(dot['magnitude'])
                flux = np.power(10, -0.4 * magn)
                if not np.isfinite(flux):
                    raise BadPhotometryDotError(name, dot)

                if 'e_lower_magnitude' in dot and 'e_upper_magnitude' in dot:
                    e_lower_magn = float(dot['e_lower_magnitude'])
                    e_upper_magn = float(dot['e_upper_magnitude'])
                    flux_lower = np.power(10, -0.4 * (magn + e_lower_magn))
                    flux_upper = np.power(10, -0.4 * (magn - e_upper_magn))
                    e_flux = 0.5 * (flux_upper - flux_lower)
                    if e_lower_magn < 0:
                        raise BadPhotometryDotError(name, dot, 'e_lower_magnitude')
                    if e_upper_magn < 0:
                        raise BadPhotometryDotError(name, dot, 'e_upper_magnitude')
                    if not np.isfinite(e_flux):
                        raise BadPhotometryDotError(name, dot)
                elif 'e_magnitude' in dot:
                    e_magn = float(dot['e_magnitude'])
                    e_flux = 0.4 * np.log(10) * flux * e_magn
                    if e_magn < 0:
                        raise BadPhotometryDotError(name, dot, 'e_magnitude')
                    if not np.isfinite(e_flux):
                        raise BadPhotometryDotError(name, dot)
                    if e_magn == 0:
                        e_flux = np.nan
                else:
                    e_flux = np.nan

                band_curve.append((
                    time,
                    e_time,
                    flux,
                    e_flux,
                    dot.get('upperlimit', False),
                ))
        for k, v in iteritems(d):
            v = d[k] = np.rec.fromrecords(v, dtype=self._photometry_dtype)
            if np.any(np.diff(v['x']) < 0):
                logging.info('Original SN {} data for band {} contains unordered dots'.format(name, k))
                v[:] = v[np.argsort(v['x'])]
            v.flags.writeable = False

        if sum(len(v) for v in iteritems(d)) == 0:
            raise EmptyPhotometryError(name, bands)

        if bands is None:
            bands = tuple(sorted(iterkeys(d)))
        else:
            for band in bands:
                if band not in d:
                    raise EmptyPhotometryError(name, (band,))

        msd = MultiStateData.from_state_data((band, d[band]) for band in bands)
        super(OSCCurve, self).__init__(msd, name=name,
                                       is_binned=False, is_filtered=False,
                                       additional_attrs=add_attrs)
예제 #10
0
 def multi_state_data(self):
     """Copy photometry data as MultiStateData"""
     return MultiStateData.from_state_data(self.odict)
예제 #11
0
    def filtered(self, with_upper_limits=False, with_inf_e_flux=False, bands=None, sort='default'):
        """Filtered and sorted by bands SNCurve

        Parameters
        ----------
        with_upper_limits: bool, optional
            Include observation point marked as an upper limit
        with_inf_e_flux: bool, optional
            Include observation point with infinity/NaN error
        bands: iterable of str or str or None, optional
            Bands to return. Default is None, SNCurves.bands will be used
        sort: str, optional
            How `bands` will be sorted. Should be one of the following
            strings:

                - 'default' will keep the order of `bands`
                - 'alphabetic' or 'alpha' will sort `bands` alphabetically
                - 'total' will sort `bands` by the total number of photometric
                  points, from maximum to minimum
                - 'filtered' will sort `bands` by the number of returned
                  photometric points from maximum to minimum, e.g. points
                  filtered by `with_upper_limits` and `with_inf_e_flux`
                  arguments

        Returns
        -------
        SNCurve

        Raises
        ------
        EmptyPhotometryError
        """
        if bands is None:
            bands = self.bands
        else:
            bands = _transform_to_tuple(bands)

        if (with_upper_limits
                and with_inf_e_flux
                and (bands is None or bands == self.bands)
                and sort == 'default'):
            return self

        @lru_cache(maxsize=1)
        def fd():
            if with_upper_limits and with_inf_e_flux:  # Little optimization
                return self.odict
            return {band: self[band][(np.logical_not(self[band]['isupperlimit']) + with_upper_limits)
                                     & (np.isfinite(self[band]['err']) + with_inf_e_flux)]
                    for band in bands}

        if sort == 'default':
            pass
        elif sort == 'alphabetic' or sort == 'alpha':
            bands = sorted(bands)
        elif sort == 'total':
            bands = sorted(bands, key=lambda band: self[band].size, reverse=True)
        elif sort == 'filtered':
            bands = sorted(bands, key=lambda band: fd()[band].size, reverse=True)
        else:
            raise ValueError('Argument sort={} is not supported'.format(sort))

        msd = MultiStateData.from_state_data((band, fd()[band]) for band in bands)
        if not msd.arrays.y.size:
            raise EmptyPhotometryError(self.name, bands)
        return SNCurve(msd, name=self.name, is_binned=self.is_binned, is_filtered=True,
                       additional_attrs=self.__additional_attrs)