Source code for paintbox.operators

# -*- coding: utf-8 -*-
"""
Miscelaneous classes for combination and modification of other paintbox model
classes.
"""
from __future__ import print_function, division

import numpy as np
import astropy.constants as const
from scipy.ndimage import convolve1d
from spectres import spectres

from paintbox.sed import PaintboxBase

__all__ = ["LOSVDConv", "Resample"]

[docs]class LOSVDConv(PaintboxBase): """ Convolution of a SED model with the LOS Velocity Distribution. This class allows the convolution of a SED model with the line--of-sight velocity distribution. This class requires that the input SED model has a logarithmic-scaled wavelength dispersion. Currently, this operation only supports LOSVDs with two moments (velocity and velocity dispersion). Attributes ---------- velscale: Quantity Velocity scale of the wavelength array. parnames: list Updated list of parameters, including the input SED object parameter names and the LOSVD parameters. wave: numpy.ndarray, astropy.quantities.Quantity Wavelength array. """ def __init__(self, obj, losvdpars=None, vunit="km/s"): """ Parameters ---------- obj: SED model Input paintbox SED model to be convolved with the LOSVD. losvdpars: list Name of the LOSVD parameters appended to the input paranames. Defaults to [V, sigma]. vunit: str Units used for velocity variables. Defaults to km/s. """ self.obj = obj self.wave = obj.wave # Check if velocity scale is constant velscale = np.diff(np.log(self.wave) * const.c.to(vunit)) msg = "LOSVD convolution requires wavelength array with constant " \ "velocity scale." assert np.all(np.isclose(velscale, velscale[0])), msg self.velscale = velscale[0] self._v = self.velscale.value self.losvdpars = ["Vsyst", "sigma"] if losvdpars is None else losvdpars self.parnames = obj.parnames + self.losvdpars self._nparams = len(self.parnames) self._shape = (self._nparams, len(self.wave)) def _kernel_arrays(self, p): """ Produces kernels used in the convolution. """ x0, sigx = p / self._v dx = int(np.ceil(np.max(abs(x0) + 5 * sigx))) n = 2 * dx + 1 x = np.linspace(-dx, dx, n) y = (x - x0) / sigx y2 = np.power(y, 2.) k = np.exp(-0.5 * y2) / (sigx * np.sqrt(2 * np.pi)) return y, k
[docs] def __call__(self, theta): """ Performs convolution of input model with LOSVD.""" z = self.obj(theta[:-2]) y, k = self._kernel_arrays(theta[-2:]) return convolve1d(z, k)
[docs] def gradient(self, theta): """ Gradient of the convolved model. """ p1 = theta[:-2] p2 = theta[-2:] grad = np.zeros(self._shape) model = self.obj(theta[:-2]) modelgrad = self.obj.gradient(p1) y, k = self._kernel_arrays(p2) for i in range(len(modelgrad)): grad[i] = convolve1d(modelgrad[i], k) grad[-2] = convolve1d(model, y * k / p2[1]) grad[-1] = convolve1d(model, (y * y - 1.) * k / p2[1]) return grad
[docs]class Resample(PaintboxBase): """ Resampling of SED model to a new wavelength dispersion. The resample can be performed to arbitrary dispersions based on the 'spectres <https://spectres.readthedocs.io/en/latest/>'_ package. Attributes ---------- parnames: list List of parameter names. wave: numpy.ndarray, astropy.quantities.Quantity Wavelength array. """ def __init__(self, wave, obj): """ Parameters ---------- wave: ndarray, Quantity New wavelength array of the SED model. obj: SED model SED model to be resampled. """ self.obj = obj self._inwave= self.obj.wave self.wave = wave self.parnames = self.obj.parnames self._nparams = len(self.parnames)
[docs] def __call__(self, theta): """ Performs the resampling. """ model = self.obj(theta) rebin = spectres(self.wave, self._inwave, model, fill=0, verbose=False) return rebin
[docs] def gradient(self, theta): """ Calculation the the gradient of the resampled model. """ grads = self.obj.gradient(theta) grads = spectres(self.wave, self._inwave, grads, fill=0, verbose=False) return grads