class GeneralizedGaussianTemporalModel(TemporalModel): r"""A generalized Gaussian temporal profile .. math:: F(t) = exp( - 0.5 * (\frac{ \lvert t - t_{ref} \rvert}{t_rise}) ^ {1 / \eta}) for t < t_ref F(t) = exp( - 0.5 * (\frac{ \lvert t - t_{ref} \rvert}{t_decay}) ^ {1 / \eta}) for t > t_ref Parameters ---------- t_ref : `~astropy.units.Quantity` The time of the pulse's maximum intensity. t_rise : `~astropy.units.Quantity` Rise time constant. t_decay : `~astropy.units.Quantity` Decay time constant. eta : `~astropy.units.Quantity` Inverse pulse sharpness -> higher values implies a more peaked pulse """ tag = ["GeneralizedGaussianTemporalModel", "gengauss"] _t_ref_default = Time("2000-01-01") t_ref = Parameter("t_ref", _t_ref_default.mjd, unit = "day", frozen=False) t_rise = Parameter("t_rise", "1d", frozen=False) t_decay = Parameter("t_decay", "1d", frozen=False) eta = Parameter("eta", 1/2, unit = "", frozen=False) @staticmethod def evaluate(time, t_ref, t_rise, t_decay, eta): val_rise = np.exp( - 0.5 * (np.abs(u.Quantity(time - t_ref,"d")) ** (1/eta)) / (t_rise ** (1/eta))) val_decay = np.exp( - 0.5 * (np.abs(u.Quantity(time - t_ref,"d")) ** (1/eta)) / (t_decay ** (1/eta))) val = np.where(time < t_ref, val_rise, val_decay) return val def integral(self, t_min, t_max, **kwargs): """Evaluate the integrated flux within the given time intervals Parameters ---------- t_min: `~astropy.time.Time` Start times of observation t_max: `~astropy.time.Time` Stop times of observation Returns ------- norm : float Integrated flux norm on the given time intervals """ pars = self.parameters t_rise = pars["t_rise"].quantity t_decay = pars["t_decay"].quantity eta = pars["eta"].quantity t_ref = Time(pars["t_ref"].quantity, format = "mjd") integral = scipy.integrate.quad(self.evaluate, t_min.mjd, t_max.mjd, args=(t_ref.mjd,t_rise,t_decay,eta))[0] return integral / self.time_sum(t_min, t_max).to_value("d")
def covariance_diagonal(): x = Parameter("x", 1, error=0.1) y = Parameter("y", 2, error=0.2) z = Parameter("z", 3, error=0.3) parameters = Parameters([x, y, z]) return Covariance(parameters=parameters)
def __init__(self, radiative_model, distance=1.0 * u.kpc, seed=None): import naima self.radiative_model = radiative_model self._particle_distribution = self.radiative_model.particle_distribution self.distance = u.Quantity(distance) self.seed = seed if isinstance(self._particle_distribution, naima.models.TableModel): param_names = ["amplitude"] else: param_names = self._particle_distribution.param_names parameters = [] for name in param_names: value = getattr(self._particle_distribution, name) parameter = Parameter(name, value) parameters.append(parameter) # In case of a synchrotron radiative model, append B to the fittable parameters if "B" in self.radiative_model.param_names: value = getattr(self.radiative_model, "B") parameter = Parameter("B", value) parameters.append(parameter) super()._init_from_parameters(parameters)
class ExpDecayTemporalModel(TemporalModel): """Temporal model with an exponential decay. Parameters ---------- t0 : Decay time scale t_ref: The reference time in mjd ..math:: F(t) = exp(t - t_ref)/t0 """ tag = "ExponentialDecayTemporalModel" t0 = Parameter("t0", "1 d", frozen=False) t_ref = Parameter("t_ref", 55555, frozen=True) def evaluate(self, time, t0, t_ref): return np.exp(-(time.mjd - t_ref) / t0.to_value("d")) def integral(self, t_min, t_max): pars = self.parameters t0 = pars["t0"].quantity t_ref = pars["t_ref"].quantity val = self.evaluate(t_max, t0, t_ref) - self.evaluate(t_min, t0, t_ref) integ = u.Quantity(-t0 * val) return (integ / self.time_sum(t_min, t_max)).to_value("")
class ShellSpatialModel(SpatialModel): r"""Shell model. For more information see :ref:`shell-spatial-model`. Parameters ---------- lon_0, lat_0 : `~astropy.coordinates.Angle` Center position radius : `~astropy.coordinates.Angle` Inner radius, :math:`r_{in}` width : `~astropy.coordinates.Angle` Shell width frame : {"icrs", "galactic"} Center position coordinate frame See Also -------- Shell2SpatialModel """ tag = ["ShellSpatialModel", "shell"] lon_0 = Parameter("lon_0", "0 deg") lat_0 = Parameter("lat_0", "0 deg", min=-90, max=90) radius = Parameter("radius", "1 deg") width = Parameter("width", "0.2 deg") @property def evaluation_radius(self): r"""Evaluation radius (`~astropy.coordinates.Angle`). Set to :math:`r_\text{out}`. """ return self.radius.quantity + self.width.quantity @staticmethod def evaluate(lon, lat, lon_0, lat_0, radius, width): """Evaluate model.""" sep = angular_separation(lon, lat, lon_0, lat_0) radius_out = radius + width norm = 3 / (2 * np.pi * (radius_out**3 - radius**3)) with np.errstate(invalid="ignore"): # np.where and np.select do not work with quantities, so we use the # workaround with indexing value = np.sqrt(radius_out**2 - sep**2) mask = sep < radius value[mask] = (value - np.sqrt(radius**2 - sep**2))[mask] value[sep > radius_out] = 0 return norm * value def to_region(self, **kwargs): """Model outline (`~regions.CircleAnnulusSkyRegion`).""" return CircleAnnulusSkyRegion( center=self.position, inner_radius=self.radius.quantity, outer_radius=self.radius.quantity + self.width.quantity, **kwargs, )
def __init__(self, r_s=None, rho_s=1 * u.Unit("GeV / cm3")): r_s = self.DEFAULT_SCALE_RADIUS if r_s is None else r_s self.parameters = Parameters([ Parameter("r_s", u.Quantity(r_s)), Parameter("rho_s", u.Quantity(rho_s)) ])
def __init__( self, map, norm=1, tilt=0, reference="1 TeV", meta=None, interp_kwargs=None, name="diffuse", filename=None, ): self.name = name axis = map.geom.get_axis_by_name("energy") if axis.node_type != "center": raise ValueError('Need a map with energy axis node_type="center"') self.map = map self.norm = Parameter("norm", norm) self.tilt = Parameter("tilt", tilt, unit="", frozen=True) self.reference = Parameter("reference", reference, frozen=True) self.meta = {} if meta is None else meta self.filename = filename interp_kwargs = {} if interp_kwargs is None else interp_kwargs interp_kwargs.setdefault("interp", "linear") interp_kwargs.setdefault("fill_value", 0) self._interp_kwargs = interp_kwargs # TODO: onve we have implement a more general and better model caching # remove this again self._cached_value = None self._cached_coordinates = (None, None, None) super().__init__([self.norm, self.tilt, self.reference])
def test_parameters_from_stack(): a = Parameter("a", 1) b = Parameter("b", 2) c = Parameter("c", 3) pars = Parameters([a, b]) + Parameters([]) + Parameters([c]) assert pars.names == ["a", "b", "c"]
class MyModel(Model): x = Parameter("x", 2) y = Parameter("y", 3e2) z = Parameter("z", 4e-2) name = "test" datasets_names = ["test"] type = "model"
def __init__(self, name=""): self.name = name self.parameters = Parameters( [Parameter("x", 2), Parameter("y", 3e2), Parameter("z", 4e-2)]) self.data_shape = (1, )
class GaussianTemporalModel(TemporalModel): """A Gaussian Temporal profile Parameters ---------- t_ref: The reference time in mjd sigma : `~astropy.units.Quantity` """ tag = "GaussianTemporalModel" t_ref = Parameter("t_ref", 55555, frozen=False) sigma = Parameter("sigma", "1 d", frozen=False) def evaluate(self, time, t_ref, sigma): return np.exp(-((time.mjd - t_ref)**2) / (2 * sigma.to_value("d")**2)) def integral(self, t_min, t_max, **kwargs): r"""Integrate Gaussian analytically. Parameters ---------- t_min, t_max : `~astropy.time` Lower and upper bound of integration range """ pars = self.parameters norm = pars["sigma"].quantity * np.sqrt(2 * np.pi) u_min = norm * ((t_min.mjd - pars["t_ref"].quantity) / (np.sqrt(2) * pars["sigma"].quantity)) u_max = norm * ((t_max.mjd - pars["t_ref"].quantity) / (np.sqrt(2) * pars["sigma"].quantity)) integ = 1.0 / 2 * (scipy.special.erf(u_max) - scipy.special.erf(u_min)) unit = getattr(pars["sigma"], "unit") return integ / self.time_sum(t_min, t_max).to_value(unit)
def test_model_class_par_init(): x = Parameter("x", 4, "cm") y = Parameter("y", 10) model = MyModel(x=x, y=y) assert x is model.x assert y is model.y
def test_unique_parameters(): a = Parameter("a", 1) b = Parameter("b", 2) c = Parameter("c", 3) parameters = Parameters([a, b, a, c]) assert parameters.names == ["a", "b", "a", "c"] parameters_unique = parameters.unique_parameters assert parameters_unique.names == ["a", "b", "c"]
def __init__(self, lon_0, lat_0, radius, width, frame="icrs"): self.frame = frame self.lon_0 = Parameter("lon_0", Angle(lon_0)) self.lat_0 = Parameter("lat_0", Angle(lat_0), min=-90, max=90) self.radius = Parameter("radius", Angle(radius)) self.width = Parameter("width", Angle(width)) super().__init__([self.lon_0, self.lat_0, self.radius, self.width])
def __init__(self, table, time_0, phase_0, f0, f1=0, f2=0): self.table = table self.time_0 = Parameter("time_0", time_0) self.phase_0 = Parameter("phase_0", phase_0) self.f0 = Parameter("f0", f0) self.f1 = Parameter("f1", f1) self.f2 = Parameter("f2", f2) super().__init__([self.time_0, self.phase_0, self.f0, self.f1, self.f2])
def __init__(self, r_s=None, alpha=None, rho_s=1 * u.Unit("GeV / cm3")): alpha = self.DEFAULT_ALPHA if alpha is None else alpha r_s = self.DEFAULT_SCALE_RADIUS if r_s is None else r_s self.parameters = Parameters([ Parameter("r_s", u.Quantity(r_s)), Parameter("alpha", u.Quantity(alpha)), Parameter("rho_s", u.Quantity(rho_s)), ])
def __init__(self, lon_0, lat_0, sigma, e=0, phi="0 deg", frame="icrs"): self.frame = frame self.lon_0 = Parameter("lon_0", Angle(lon_0)) self.lat_0 = Parameter("lat_0", Angle(lat_0), min=-90, max=90) self.sigma = Parameter("sigma", Angle(sigma), min=0) self.e = Parameter("e", e, min=0, max=1, frozen=True) self.phi = Parameter("phi", Angle(phi), frozen=True) super().__init__( [self.lon_0, self.lat_0, self.sigma, self.e, self.phi])
def __init__(self, index, amplitude, reference, mean, width): super().__init__( [ Parameter("index", index, min=0), Parameter("amplitude", amplitude, min=0), Parameter("reference", reference, frozen=True), Parameter("mean", mean, min=0), Parameter("width", width, min=0, frozen=True), ] )
def test_parameter_quantity(): par = Parameter("spam", 42, "deg", 10) quantity = par.quantity assert quantity.unit == "deg" assert quantity.value == 420 par.quantity = "70 deg" assert_allclose(par.factor, 7) assert par.scale == 10 assert par.unit == "deg"
class PowerLawTemporalModel(TemporalModel): """Temporal model with a Power Law decay. For more information see :ref:`powerlaw-temporal-model`. Parameters ---------- alpha : float Decay time power t_ref: `~astropy.units.Quantity` The reference time in mjd. Frozen by default, at 2000-01-01. t0: `~astropy.units.Quantity` The scaling time in mjd. Fixed by default, at 1 day. """ tag = ["PowerLawTemporalModel", "powerlaw"] alpha = Parameter("alpha", 1.0, frozen=False) _t_ref_default = Time("2000-01-01") t_ref = Parameter("t_ref", _t_ref_default.mjd, unit="day", frozen=True) t0 = Parameter("t0", "1 d", frozen=True) @staticmethod def evaluate(time, alpha, t_ref, t0=1 * u.day): """Evaluate at given times""" return np.power((time - t_ref) / t0, alpha) def integral(self, t_min, t_max): """Evaluate the integrated flux within the given time intervals Parameters ---------- t_min: `~astropy.time.Time` Start times of observation t_max: `~astropy.time.Time` Stop times of observation Returns ------- norm : float Integrated flux norm on the given time intervals """ pars = self.parameters alpha = pars["alpha"].quantity t0 = pars["t0"].quantity t_ref = Time(pars["t_ref"].quantity, format="mjd") if alpha != -1: value = self.evaluate(t_max, alpha + 1.0, t_ref, t0) - self.evaluate( t_min, alpha + 1.0, t_ref, t0) return t0 / (alpha + 1.0) * value / self.time_sum(t_min, t_max) else: value = np.log((t_max - t_ref) / (t_min - t_ref)) return t0 * value / self.time_sum(t_min, t_max)
def test_get_subcovariance(): a = Parameter("a", 10) b = Parameter("b", 20) c = Parameter("c", 30) pars_0 = Parameters([a, b, c]) pars_0.covariance = np.array([[2, 3, 4], [6, 7, 8], [10, 11, 12]]) pars_1 = Parameters([a, b]) assert_equal(pars_0.get_subcovariance(pars_1), np.array([[2, 3], [6, 7]])) assert_equal(pars_0.get_subcovariance([c]), np.array([[12]]))
class LogParabolaSpectralModel(SpectralModel): r"""Spectral log parabola model. For more information see :ref:`logparabola-spectral-model`. Parameters ---------- amplitude : `~astropy.units.Quantity` :math:`\phi_0` reference : `~astropy.units.Quantity` :math:`E_0` alpha : `~astropy.units.Quantity` :math:`\alpha` beta : `~astropy.units.Quantity` :math:`\beta` """ tag = "LogParabolaSpectralModel" amplitude = Parameter("amplitude", "1e-12 cm-2 s-1 TeV-1") reference = Parameter("reference", "10 TeV", frozen=True) alpha = Parameter("alpha", 2) beta = Parameter("beta", 1) @classmethod def from_log10(cls, amplitude, reference, alpha, beta): """Construct from :math:`log_{10}` parametrization.""" beta_ = beta / np.log(10) return cls(amplitude=amplitude, reference=reference, alpha=alpha, beta=beta_) @staticmethod def evaluate(energy, amplitude, reference, alpha, beta): """Evaluate the model (static function).""" xx = energy / reference exponent = -alpha - beta * np.log(xx) return amplitude * np.power(xx, exponent) @property def e_peak(self): r"""Spectral energy distribution peak energy (`~astropy.units.Quantity`). This is the peak in E^2 x dN/dE and is given by: .. math:: E_{Peak} = E_{0} \exp{ (2 - \alpha) / (2 * \beta)} """ p = self.parameters reference = p["reference"].quantity alpha = p["alpha"].quantity beta = p["beta"].quantity return reference * np.exp((2 - alpha) / (2 * beta))
class ExpCutoffPowerLawSpectralModel(SpectralModel): r"""Spectral exponential cutoff power-law model. For more information see :ref:`exp-cutoff-powerlaw-spectral-model`. Parameters ---------- index : `~astropy.units.Quantity` :math:`\Gamma` amplitude : `~astropy.units.Quantity` :math:`\phi_0` reference : `~astropy.units.Quantity` :math:`E_0` lambda_ : `~astropy.units.Quantity` :math:`\lambda` alpha : `~astropy.units.Quantity` :math:`\alpha` """ tag = "ExpCutoffPowerLawSpectralModel" index = Parameter("index", 1.5) amplitude = Parameter("amplitude", "1e-12 cm-2 s-1 TeV-1") reference = Parameter("reference", "1 TeV", frozen=True) lambda_ = Parameter("lambda_", "0.1 TeV-1") alpha = Parameter("alpha", "1.0", frozen=True) @staticmethod def evaluate(energy, index, amplitude, reference, lambda_, alpha): """Evaluate the model (static function).""" pwl = amplitude * (energy / reference)**(-index) cutoff = np.exp(-np.power(energy * lambda_, alpha)) return pwl * cutoff @property def e_peak(self): r"""Spectral energy distribution peak energy (`~astropy.units.Quantity`). This is the peak in E^2 x dN/dE and is given by: .. math:: E_{Peak} = \left(\frac{2 - \Gamma}{\alpha}\right)^{1/\alpha} / \lambda """ p = self.parameters reference = p["reference"].quantity index = p["index"].quantity lambda_ = p["lambda_"].quantity alpha = p["alpha"].quantity if index >= 2 or lambda_ == 0.0 or alpha == 0.0: return np.nan * reference.unit else: return np.power((2 - index) / alpha, 1 / alpha) / lambda_
class SineTemporalModel(TemporalModel): """Temporal model with a sinusoidal modulation. For more information see :ref:`sine-temporal-model`. Parameters ---------- amp : float Amplitude of the sinusoidal function t_ref: `~astropy.units.Quantity` The reference time in mjd. omega: `~astropy.units.Quantity` Pulsation of the signal. """ tag = ["SineTemporalModel", "sinus"] amp = Parameter("amp", 1.0, frozen=False) omega = Parameter("omega", "1. rad/day", frozen=False) _t_ref_default = Time("2000-01-01") t_ref = Parameter("t_ref", _t_ref_default.mjd, unit="day", frozen=False) @staticmethod def evaluate(time, amp, omega, t_ref): """Evaluate at given times""" return 1.0 + amp * np.sin(omega * (time - t_ref)) def integral(self, t_min, t_max): """Evaluate the integrated flux within the given time intervals Parameters ---------- t_min: `~astropy.time.Time` Start times of observation t_max: `~astropy.time.Time` Stop times of observation Returns ------- norm : float Integrated flux norm on the given time intervals """ pars = self.parameters omega = pars["omega"].quantity.to_value("rad/day") amp = pars["amp"].value t_ref = Time(pars["t_ref"].quantity, format="mjd") value = (t_max - t_min ) - amp / omega * (np.sin(omega * (t_max - t_ref).to_value("day")) - np.sin(omega * (t_min - t_ref).to_value("day"))) return value / self.time_sum(t_min, t_max)
def test_parameter_scale(): # Basic check how scale is used for value, min, max par = Parameter("spam", 42, "deg", 10, 400, 500) assert par.value == 420 assert par.min == 400 assert_allclose(par.factor_min, 40) assert par.max == 500 assert_allclose(par.factor_max, 50) par.value = 70 assert par.scale == 10 assert_allclose(par.factor, 7)
def test_set_subcovariance(): a = Parameter("a", 10) b = Parameter("b", 20) c = Parameter("c", 30) pars_0 = Parameters([a, c, b]) pars_0.covariance = np.zeros((3, 3)) pars_1 = Parameters([a, b]) pars_1.covariance = np.array([[2, 3], [6, 7]]) pars_0.set_subcovariance(pars_1) assert_equal(pars_0.covariance, np.array([[2, 0, 3], [0, 0, 0], [6, 0, 7]]))
class LinearTemporalModel(TemporalModel): """Temporal model with a linear variation. For more information see :ref:`linear-temporal-model`. Parameters ---------- alpha : float Constant term of the baseline flux beta : `~astropy.units.Quantity` Time variation coefficient of the flux t_ref: `~astropy.units.Quantity` The reference time in mjd. Frozen per default, at 2000-01-01. """ tag = ["LinearTemporalModel", "linear"] alpha = Parameter("alpha", 1.0, frozen=False) beta = Parameter("beta", 0.0, unit="d-1", frozen=False) _t_ref_default = Time("2000-01-01") t_ref = Parameter("t_ref", _t_ref_default.mjd, unit="day", frozen=True) @staticmethod def evaluate(time, alpha, beta, t_ref): """Evaluate at given times""" return alpha + beta * (time - t_ref) def integral(self, t_min, t_max): """Evaluate the integrated flux within the given time intervals Parameters ---------- t_min: `~astropy.time.Time` Start times of observation t_max: `~astropy.time.Time` Stop times of observation Returns ------- norm : float Integrated flux norm on the given time intervals """ pars = self.parameters alpha = pars["alpha"] beta = pars["beta"].quantity t_ref = Time(pars["t_ref"].quantity, format="mjd") value = alpha * (t_max - t_min) + beta / 2.0 * ((t_max - t_ref) * (t_max - t_ref) - (t_min - t_ref) * (t_min - t_ref)) return value / self.time_sum(t_min, t_max)
class GaussianTemporalModel(TemporalModel): r"""A Gaussian temporal profile ..math:: F(t) = exp( -0.5 * \frac{ (t - t_{ref})^2 } { \sigma^2 }) Parameters ---------- t_ref: `~astropy.units.Quantity` The reference time in mjd at the peak. sigma : `~astropy.units.Quantity` Width of the gaussian profile. """ tag = ["GaussianTemporalModel", "gauss"] _t_ref_default = Time("2000-01-01") t_ref = Parameter("t_ref", _t_ref_default.mjd, unit="day", frozen=False) sigma = Parameter("sigma", "1 d", frozen=False) @staticmethod def evaluate(time, t_ref, sigma): return np.exp(-((time - t_ref)**2) / (2 * sigma**2)) def integral(self, t_min, t_max, **kwargs): """Evaluate the integrated flux within the given time intervals Parameters ---------- t_min: `~astropy.time.Time` Start times of observation t_max: `~astropy.time.Time` Stop times of observation Returns ------- norm : float Integrated flux norm on the given time intervals """ pars = self.parameters sigma = pars["sigma"].quantity t_ref = Time(pars["t_ref"].quantity, format="mjd") norm = np.sqrt(np.pi / 2) * sigma u_min = (t_min - t_ref) / (np.sqrt(2) * sigma) u_max = (t_max - t_ref) / (np.sqrt(2) * sigma) integral = norm * (scipy.special.erf(u_max) - scipy.special.erf(u_min)) return integral / self.time_sum(t_min, t_max)
class _LogGaussianSpectralModel(SpectralModel): r"""Log Gaussian spectral model with a weird parametrisation. This should not be exposed to end-users as a Gammapy spectral model! See Table 3 in https://ui.adsabs.harvard.edu/abs/2013APh....43..171B """ L = Parameter("L", 1e-12 * u.Unit("cm-2 s-1")) Ep = Parameter("Ep", 0.107 * u.TeV) w = Parameter("w", 0.776) @staticmethod def evaluate(energy, L, Ep, w): return (L / (energy * w * np.sqrt(2 * np.pi)) * np.exp(-((np.log(energy / Ep))**2) / (2 * w**2)))
def test_parameters_from_stack(): a = Parameter("a", 1) b = Parameter("b", 2) c = Parameter("c", 3) pars = Parameters([a, b]) + Parameters([]) + Parameters([c]) assert pars.names == ["a", "b", "c"] pars1 = Parameters.from_values([1, 2], covariance=np.full((2, 2), 2)) pars2 = Parameters.from_values([3, 4, 5], covariance=np.full((3, 3), 3)) pars = pars1 + pars2 assert_allclose(pars.values, [1, 2, 3, 4, 5]) assert_allclose(pars.covariance[0], [2, 2, 0, 0, 0]) assert_allclose(pars.covariance[4], [0, 0, 3, 3, 3])