class ISO(Fittable1DModel): '''ISO dark mater halo model Inputs: r: array_like. Radius in Kpc. Parameters of ISO model. rho_0: float or int. Central mass density in 10^-3 Msol/pc^3. rc: float or int. Core radius in Kpc. Outputs v: array_like. velocity in km/s. ''' inputs = ('r', ) outputs = ('v', ) rho_0 = Parameter(bounds=(1e-8, 1e3)) rc = Parameter(bounds=(0, 300)) fit_deriv = None @staticmethod def evaluate(r, rho_0, rc): '''ISO dark mater halo function. ''' r = r * u.kpc rho_0 = rho_0 * 10**(-3) * u.M_sun / (u.pc)**3 rc = rc * u.kpc c = (rc / r).value v = np.sqrt(4 * np.pi * G * rho_0 * np.power(rc, 2) * (1.0 - c * np.arctan(1 / c))) v = v.to(u.km / u.s) v = v.value return (v)
class SEDModel(FittableModel): """ Parameters ---------- spectrum : specutils.Spectrum1D filters : wsynphot.FilterSet distance : float distance to the supernova in Mpc fraction : float fraction of luminosity in the spectrum """ inputs = ('luminosity') outputs = ('flux') distance = Parameter() fraction = Parameter(default=1.0) def __init__(self, spectrum, distance, fraction=1.0): assert False # unusable for now super(SEDModel, self).__init__(distance=distance, fraction=fraction) assert spectrum.unit == u.erg / u.s / u.angstrom luminosity_density = spectrum.data * spectrum.unit norm_factor = np.trapz(luminosity_density, spectrum.wavelength) self.normed_luminosity_density = (luminosity_density / norm_factor) def evaluate(self, luminosity, distance, fraction): luminosity_density = ( self.normed_luminosity_density * luminosity * fraction) return luminosity_density / 4 * np.pi * (distance * mpc_to_cm) ** 2
class Paczynski1990Velocity(Fittable1DModel): r"""Distribution by Lyne 1982 and adopted by Paczynski and Faucher. .. math:: f(v) = A\frac{4}{\pi} \frac{1}{v_0 \left[1 + (v / v_0) ^ 2 \right] ^ 2} Reference: https://ui.adsabs.harvard.edu/abs/1990ApJ...348..485P (Formula (3)) Parameters ---------- amplitude : float Value of the integral v_0 : float Velocity parameter (km s^-1) """ amplitude = Parameter() v_0 = Parameter() def __init__(self, amplitude=1, v_0=560, **kwargs): super().__init__(amplitude=amplitude, v_0=v_0, **kwargs) @staticmethod def evaluate(v, amplitude, v_0): """One dimensional Paczynski 1990 velocity model function.""" return amplitude * 4.0 / (np.pi * v_0 * (1 + (v / v_0) ** 2) ** 2)
class Exponential(Fittable1DModel): """Exponential distribution. .. math :: f(z) = A \\exp \\left(- \\frac{|z|}{z_0} \\right) Usually used for height distribution above the Galactic plane, with 0.05 kpc as a commonly used birth height distribution. Parameters ---------- amplitude : float See model formula z_0 : float Scale height of the distribution See Also -------- CaseBattacharya1998, Paczynski1990, YusifovKucuk2004, Lorimer2006, YusifovKucuk2004B, FaucherKaspi2006, Exponential """ amplitude = Parameter() z_0 = Parameter() evolved = False def __init__(self, amplitude=1, z_0=0.05, **kwargs): super(Exponential, self).__init__(amplitude=amplitude, z_0=z_0, **kwargs) @staticmethod def evaluate(z, amplitude, z_0): """Evaluate model.""" return amplitude * np.exp(-np.abs(z) / z_0)
class FLCDM(Fittable1DModel): """ All parameters go here. """ H0 = Parameter(default=1.) Om = Parameter(default=1.) Ol = Parameter(default=1.) @staticmethod def evaluate(x, H0, Om, Ol): c = 2.99792458E+5 return np.log10((c*(1.+x)/H0) * (((1.+x)**3.)*Om + Ol)**-0.5) @staticmethod def fit_deriv(x, H0, Om, Ol): """ Logged DL. These are the derivatives of the DL func for the Lambda CDM model. x - the z that is not part of the integral. """ c = 2.99792458E+5 #d_H0 = -0.434294/H0 #d_H0 = np.ones(x.shape) d_H0 = np.ones(x.shape) * -0.434294/H0 d_Om = -(0.217147*(x + 1)**3)/(Om*(x + 1)**3 + Ol) d_Ol = -0.217147/(Om*(x + 1)**3 + Ol) return [d_H0, d_Om, d_Ol]
class PsfModel(Fittable2DModel): x_0 = Parameter(default=1, min=0) y_0 = Parameter(default=1, min=0) I_0 = Parameter(default=1, min=1e-5) def __init__(self, psf, x_0=x_0.default, y_0=y_0.default, I_0=I_0.default, **kwargs): self.psf = psf super().__init__(x_0, y_0, I_0, **kwargs) return None def evaluate(self, x, y, x_0, y_0, I_0): canvas = np.zeros_like(x) Npsf, Mpsf = self.psf.shape N, M = x.shape if x.shape > self.psf.shape: canvas[:Npsf, :Mpsf] = self.psf else: diffN = Npsf - N diffM = Npsf - N canvas = self.psf[diffN // 2:-diffN // 2, diffM // 2:-diffM // 2] xCen, yCen = barycenter(canvas, np.ones_like(canvas)) dx = (x_0 - xCen) dy = (y_0 - yCen) finalPSF = shift(canvas, (dx, dy), order=2) return I_0 * finalPSF
class single_cog_model(FittableModel): """Generate a single COG model Parameters ---------- logN b input : wrest*f output : redEW reduced EWs """ inputs = ('wrestxf', ) outputs = ('redEW', ) # Free parameters (generally) logN = Parameter() b = Parameter() # Assumes km/s # Fixed parameters @staticmethod def evaluate(wrestf, logN, b): # F(tau0) tau0 = 1.497e-15 * (wrestf) * (10.**logN) / b Ftau0 = intFtau0(tau0) # Finish redEW = 2 * b * Ftau0 / 3e5 return redEW
class mflux_tauevo(FittableModel): """ Mean flux evolution * exp(delta*(lambda/1280-1)), Meant for use with astropy.modeling to correct the fitted Lya forest continuum Abscissa parameter x is the restframe wavelength, and free parameters p0, p1 set the power law. This function NEEDS the quasar redshift zqso to be set input: lambda_r :: Rest wavelength Assumed in Angstroms output: absorbed, normalized flux Parameters: logN,b,z,wrest,f,gamma,fwhm """ inputs = ('lambda_r',) outputs = ('flux',) # Free parameters (generally) p0 = Parameter() p1 = Parameter() # Fixed parameters zqso = Parameter(fixed=True) lamb_piv = Parameter(fixed=True) @staticmethod def evaluate(lambda_r, p0, p1, zqso, lamb_piv): #logN,b,z,wrest,f,gamma,fwhm): zfor = (lambda_r/1216.) * (1. + zqso) - 1. tau = pyteff.lyman_alpha_obs(zfor) #tau = old_taueff_evo(zfor) fmean = np.exp(-1*tau) mfluxtauevo = fmean * mfluxcorr(lambda_r, p0, p1, lamb_piv=lamb_piv) return mfluxtauevo
class Paczynski1990(Fittable1DModel): """Radial distribution of the birth surface density of neutron stars - Paczynski 1990. .. math :: f(r) = A r_{exp}^{-2} \\exp \\left(-\\frac{r}{r_{exp}} \\right) Reference: http://adsabs.harvard.edu/abs/1990ApJ...348..485P (Formula (2)) Parameters ---------- amplitude : float See formula r_exp : float See formula See Also -------- CaseBattacharya1998, YusifovKucuk2004, Lorimer2006, YusifovKucuk2004B, FaucherKaspi2006, Exponential """ amplitude = Parameter() r_exp = Parameter() evolved = False def __init__(self, amplitude=1, r_exp=4.5, **kwargs): super(Paczynski1990, self).__init__(amplitude=amplitude, r_exp=r_exp, **kwargs) @staticmethod def evaluate(r, amplitude, r_exp): """Evaluate model.""" return amplitude * r_exp**-2 * np.exp(-r / r_exp)
class Powerlaw(FittableModel): """ Represents a simple power-law curve The curve is defined as amplitude * (t_offset)**alpha Be wary of using an init_alpha<0, since this results in an asymptote at t=0. NB The curve will always begin at the origin, because maths. (Cannot raise a negative number to a fractional power unless you deal with complex numbers. Also 0.**Y == 0. ) """ inputs = ('t', ) outputs = ('flux', ) init_amp = Parameter() alpha_one = Parameter() t_offset_min = Parameter(default=0.) t0 = Parameter(default=0.) @staticmethod def evaluate(t, init_amp, alpha_one, t_offset_min, t0): if np.ndim(t) == 0: t = np.asarray(t, dtype=np.float).reshape((1, )) t_offset = t - t0 result = np.zeros_like(t_offset) t_valid = t_offset >= t_offset_min result[t_valid] = (init_amp * np.power(t_offset[t_valid], alpha_one)) return result
class FlatLambdaCDM(FIttable1dModel): """ All parameters go here. """ #c = Parameter(default=1.) #H0 = Parameter(default=1.) Om = Parameter(default=0.27) Ol = Parameter(default=1.-0.27) z = Parameter(default=1.) @staticmethod def evaluate(x, z, Om, Ol): H0 = 71. c = 2.99792458E+5 return (c*(1.+x)/H0) * (((1.+z)**3.)*Om + Ol)**-0.5 @staticmethod def fit_deriv(x, z, Om, Ol): """ These are the derivatives of the DL func for the Lambda CDM model. x - the z that is not part of the integral. """ H0 = 71. c = 2.99792458E+5 d_z = (-1.5*Om*(1+z)**2)/((Om*(1+z)**3 + Ol)**1.5) d_Om = -(0.5*(1+z)**3)/(Om*(1+z)**3 + Ol)**1.5 d_Ol = -0.5/(Om*(1+z)**3 + Ol)**1.5 A = (c*(1.+x)/H0) d_z = A * d_z d_Om = A * d_Om d_Ol = A * d_Ol # np.ones(x.shape) return [d_z, d_Om, d_Ol]
class FaucherKaspi2006VelocityMaxwellian(Fittable1DModel): r"""Maxwellian pulsar velocity distribution. .. math:: f(v) = A \sqrt{ \frac{2}{\pi}} \frac{v ^ 2}{\sigma ^ 3 } \exp \left(-\frac{v ^ 2}{2 \sigma ^ 2} \right) Reference: https://ui.adsabs.harvard.edu/abs/2006ApJ...643..332F Parameters ---------- amplitude : float Value of the integral sigma : float Velocity parameter (km s^-1) """ amplitude = Parameter() sigma = Parameter() def __init__(self, amplitude=1, sigma=265, **kwargs): super().__init__(amplitude=amplitude, sigma=sigma, **kwargs) @staticmethod def evaluate(v, amplitude, sigma): """One dimensional velocity model function.""" term1 = np.sqrt(2 / np.pi) * v ** 2 / sigma ** 3 term2 = np.exp(-v ** 2 / (2 * sigma ** 2)) return term1 * term2
class Plummer2D(Fittable2DModel): """ Two-dimensional Plummer surface brightness profile. Parameters ---------- amplitude : float Central surface brightness. scale_radius : float Characteristic scale radius of the mass distribution. x_0 : float, optional x position of the center. y_0 : float, optional y position of the center. """ amplitude = Parameter(default=1) scale_radius = Parameter(default=1) x_0 = Parameter(default=0) y_0 = Parameter(default=0) @classmethod def evaluate(cls, x, y, amplitude, scale_radius, x_0, y_0): """Two-dimensional Plummer profile evaluation function.""" r = np.sqrt((x - x_0)**2 + (y - y_0)**2) return amplitude / (1 + (r / scale_radius)**2)**2
class SymmetricMoffat2D(PSF): """Moffat 2D profile (normalized). The shape is parametrized in terms of FWHM and alpha rather than alpha and gamma for historical reasons.""" amplitude = Parameter(name='amplitude',default=1.) x_0 = Parameter(name='x_0',default=0.) y_0 = Parameter(name='y_0',default=0.) fwhm = Parameter(name='fhwm',default=1.) alpha = Parameter(name='alpha',default=3.) @fwhm.validator def fwhm(self, value): # Remember, the value can be an array if np.any(value <= 0.): raise InputParameterError( "parameter 'fwhm' must be greater than zero ") @alpha.validator def alpha(self, value): # Remember, the value can be an array if np.any(value <= 1.): raise InputParameterError( "parameter 'alpha' must be greater than 1") @staticmethod def _evaluate(x,y,amplitude,x_0,y_0,fwhm,alpha): """The true 'evaluate' method without oversampling""" gamma = 0.5 * fwhm/np.sqrt(2**(1./alpha) - 1.) rr_gg = ((x - x_0) ** 2 + (y - y_0) ** 2) / gamma ** 2 return amplitude * (alpha - 1.)/(np.pi*gamma*gamma) * (1 + rr_gg)**(-alpha)
class FLCDM(Fittable1DModel): """ All parameters go here. """ h = Parameter(default=70.) Om = Parameter(default=0.3) Ol = Parameter(default=0.7) @staticmethod def evaluate(x, H0, Om, Ol): c = 2.99792458E+5 return (c*(1.+x)/H0) * (((1.+x)**3.)*Om + Ol)**-0.5 @staticmethod def fit_deriv(x, H0, Om, Ol): """ h : H0 m : Om L : Ol These are the derivatives of the DL func for the Lambda CDM model. x - the z that is not part of the integral. """ c = 2.99792458E+5 d_H0 = -(c*(x + 1))/(h**2*(Om*(x + 1)**3 + Ol)**0.5) d_Om = -(0.5*c*(x + 1)**4)/(h*(Om*(x + 1)**3 + Ol)**1.5) d_Ol = -(0.5*c*(x + 1))/(h*(Om*(x + 1)**3 + Ol)**1.5) d_z = return [d_h, d_Om, d_Ol, d_z]
class BaseExtRvAfAModel(BaseExtModel): """ Base Extinction R(V)_A, f_A -dependent Model. Do not use. """ RvA = Parameter( description="R_A(V) = A(V)/E(B-V) = " + "total-to-selective extinction of component A", default=3.1, ) fA = Parameter(description="f_A = mixture coefficent of component A", default=1.0) def __init__(self, **kwargs): super().__init__(**kwargs) # set Rv so that extinguishing by Ebv works # equation 11 in Gordon et al. (2016, ApJ, 826, 104) self.Rv = 1.0 / (self.fA / self.RvA + (1 - self.fA) / 2.74) @RvA.validator def RvA(self, value): """ Check that RvA is in the valid range Parameters ---------- value: float RvA value to check Raises ------ InputParameterError Input R_A(V) values outside of defined range """ if not (self.RvA_range[0] <= value <= self.RvA_range[1]): raise InputParameterError("parameter RvA must be between " + str(self.RvA_range[0]) + " and " + str(self.RvA_range[1])) @fA.validator def fA(self, value): """ Check that fA is in the valid range Parameters ---------- value: float fA value to check Raises ------ InputParameterError Input fA values outside of defined range """ if not (self.fA_range[0] <= value <= self.fA_range[1]): raise InputParameterError("parameter fA must be between " + str(self.fA_range[0]) + " and " + str(self.fA_range[1]))
class Exponential1D(Fittable1DModel): """ One dimensional exponential model. Parameters ---------- amplitude : float, optional x_0 : float, optional See Also -------- Exponential1D, Gaussian1D """ amplitude = Parameter(default=1) x_0 = Parameter(default=1) @staticmethod def evaluate(x, amplitude, x_0): return amplitude * np.exp(x / x_0) @staticmethod def fit_deriv(x, amplitude, x_0): d_amplitude = np.exp(x / x_0) d_x_0 = -amplitude * np.exp(x / (x_0**2)) return [d_amplitude, d_x_0] @property def inverse(self): new_amplitude = self.x_0 new_x_0 = self.amplitude return Logarithmic1D(amplitude=new_amplitude, x_0=new_x_0) @x_0.validator def x_0(self, val): if val == 0: raise ValueError("0 is not an allowed value for x_0")
class BaseGaussian1D(Model): """ MGE 1D Gaussian model using astropy Parameters ---------- **kwargs : kwargs Set of free arguments given to MGE_Gaussian1D Return ------ MGE_Gaussian1D """ inputs = ("x") outputs = ("G1D", ) imax1d = Parameter(name="imax1d", default=1.0) sig1d = Parameter(name="sig1d", default=1.0) xcentre1d = Parameter(name="xcentre1d", default=0.0) @staticmethod def evaluate(x, imax1d, sig1d, xcentre1d): return astropy_models.Gaussian1D.evaluate(x, amplitude=imax1d, stddev=sig1d, mean=xcentre1d)
class PSFExModel(Fittable2DModel): """ Warning: does not work. I tried! """ n_inputs = 2 n_outputs = 1 flux = Parameter() x_0 = Parameter() y_0 = Parameter() def __init__(self, psfex_file: str, data_shape: tuple, flux: float, x_0: float, y_0: float): self.psfex_file = psfex_file self.psfex_model = pex.PSFEx(psfex_file) self.data_shape = data_shape super().__init__(flux, x_0, y_0) def evaluate(self, x, y, flux, x_0, y_0): mock = np.zeros(shape=self.data_shape) model_img = self.psfex_model.get_rec(y_0, x_0) y_cen, x_cen = self.psfex_model.get_center(y_0, x_0) model_img /= np.sum(model_img) model_img *= flux mock[0:model_img.shape[0], 0:model_img.shape[1]] += model_img mock = shift(mock, (y_0 - y_cen, x_0 - x_cen)) return mock[int(x), int(y)]
class ExpPhase(DiskIntegratedPhaseFunc): _unit = 'ref' p = Parameter(default=0.1 / u.sr) nu = Parameter(default=0.1 / u.rad) @staticmethod def evaluate(a, p, nu): return p * np.exp(-nu * a)
class Pix2Sky(FittableModel): """ Wrapper to make an astropy.WCS object act like an astropy.modeling.Model object, including having an inverse. """ def __init__(self, wcs, x_offset=0.0, y_offset=0.0, factor=1.0, angle=0.0, direction=1, factor_scale=1.0, angle_scale=1.0, **kwargs): self._wcs = wcs.deepcopy() self._direction = direction self._factor_scale = float(factor_scale) self._angle_scale = float(angle_scale) super(Pix2Sky, self).__init__(x_offset, y_offset, factor, angle, **kwargs) inputs = ('x', 'y') outputs = ('x', 'y') x_offset = Parameter() y_offset = Parameter() factor = Parameter() angle = Parameter() def evaluate(self, x, y, x_offset, y_offset, factor, angle): # x_offset and y_offset are actually arrays in the Model #temp_wcs = self.wcs(x_offset[0], y_offset[0], factor, angle) temp_wcs = self.wcs return temp_wcs.all_pix2world(x, y, 1) if self._direction>0 \ else temp_wcs.all_world2pix(x, y, 1) @property def inverse(self): inv = self.copy() inv._direction = -self._direction return inv @property def wcs(self): """Return the WCS modified by the translation/scaling/rotation""" wcs = self._wcs.deepcopy() x_offset = self.x_offset.value y_offset = self.y_offset.value angle = self.angle.value factor = self.factor.value wcs.wcs.crpix += np.array([x_offset, y_offset]) if factor != self._factor_scale: wcs.wcs.cd *= factor / self._factor_scale if angle != 0.0: m = models.Rotation2D(angle / self._angle_scale) wcs.wcs.cd = m(*wcs.wcs.cd) return wcs
class PSFModelWithFWHM(Fittable2DModel): x_0 = Parameter(default=1) y_0 = Parameter(default=1) flux = Parameter(default=1) fwhm = Parameter(default=5) def __init__(self, fwhm=fwhm.default): super(PSFModelWithFWHM, self).__init__(fwhm=fwhm) def evaluate(self, x, y, x_0, y_0, flux, fwhm): return flux / (fwhm * (x - x_0)**2 * (y - y_0)**2)
class Delta2D(Fittable2DModel): """ Two dimensional delta function . This model can be used for a point source morphology. Parameters ---------- amplitude : float Peak value of the point source x_0 : float x position center of the point source y_0 : float y position center of the point source See Also -------- Shell2D, Sphere2D, astropy.modeling.models.Gaussian2D Notes ----- Model formula: .. math:: f(x, y) = \\cdot \\left \\{ \\begin{array}{ll} A & : x = x_0 \\ \\mathrm{and} \\ y = y_0 \\\\ 0 & : else \\end{array} \\right. The pixel positions x_0 and y_0 are rounded to integers. Subpixel information is lost. """ amplitude = Parameter('amplitude') x_0 = Parameter('x_0') y_0 = Parameter('y_0') def __init__(self, amplitude, x_0, y_0, **constraints): super(Delta2D, self).__init__(amplitude=amplitude, x_0=x_0, y_0=y_0, **constraints) @staticmethod def evaluate(x, y, amplitude, x_0, y_0): """Two dimensional delta model function""" dx = x - x_0 dy = y - y_0 x_mask = np.logical_and(dx > -0.5, dx <= 0.5) y_mask = np.logical_and(dy > -0.5, dy <= 0.5) return np.select([np.logical_and(x_mask, y_mask)], [amplitude])
class GaussianRadial(Fittable1DModel): flux = Parameter(default=1, fixed=False) sigma = Parameter(default=1, fixed=False) sky = Parameter(default=0, fixed=False) @staticmethod def evaluate(x, sigma, flux, sky): return gaussian_r(x, sigma, flux, sky) @property def fwhm(self): return gaussian_fwhm(self.sigma)
class BaseExtRvAfAModel(BaseExtModel): """ Base Extinction R(V)_A, f_A -dependent Model. Do not use. """ RvA = Parameter( description="R_A(V) = A(V)/E(B-V) = " + "total-to-selective extinction of component A", default=3.1, ) fA = Parameter(description="f_A = mixture coefficent of component A", default=1.0) @RvA.validator def RvA(self, value): """ Check that RvA is in the valid range Parameters ---------- value: float RvA value to check Raises ------ InputParameterError Input R_A(V) values outside of defined range """ if not (self.RvA_range[0] <= value <= self.RvA_range[1]): raise InputParameterError("parameter RvA must be between " + str(self.RvA_range[0]) + " and " + str(self.RvA_range[1])) @fA.validator def fA(self, value): """ Check that fA is in the valid range Parameters ---------- value: float fA value to check Raises ------ InputParameterError Input fA values outside of defined range """ if not (self.fA_range[0] <= value <= self.fA_range[1]): raise InputParameterError("parameter fA must be between " + str(self.fA_range[0]) + " and " + str(self.fA_range[1]))
class MoffatRadial(Fittable1DModel): flux = Parameter(default=1, fixed=False) alpha = Parameter(default=1, fixed=False) beta = Parameter(default=1.5, fixed=False) sky = Parameter(default=0, fixed=False) @staticmethod def evaluate(x, flux, alpha, beta, sky): return moffat_r(x, alpha, beta, flux, sky) @property def fwhm(self): return moffat_fwhm(self.alpha, self.beta)
class Test2D(Fittable2DModel): x_0 = Parameter(default=0) y_0 = Parameter(default=0) p = 0 def __init__(self, x_0=x_0.default, y_0=y_0.default, p=0, **kwargs): self.p = p super().__init__(x_0, y_0, **kwargs) def evaluate(self, x, y, x_0, y_0): print(self.p) r = np.sqrt((x - x_0) * (x - x_0) + (y - y_0) * (y - y_0)) return np.exp(-r / 10)
class Star2D(Fittable2DModel): amplitude = Parameter('amplitude', default=1) x_mean = Parameter('x_mean', default=0) y_mean = Parameter('y_mean', default=0) stddev = Parameter('stddev', default=1) saturation = Parameter('saturation', default=1) def __init__(self, amplitude=amplitude.default, x_mean=x_mean.default, y_mean=y_mean.default, stddev=stddev.default, saturation=saturation.default, **kwargs): super(Fittable2DModel, self).__init__(**kwargs) @property def fwhm(self): return self.stddev / 2.335 @staticmethod def evaluate(x, y, amplitude, x_mean, y_mean, stddev, saturation): a = amplitude * np.exp(-(1 / (2. * stddev**2)) * (x - x_mean)**2 - (1 / (2. * stddev**2)) * (y - y_mean)**2) if isinstance(x, float) and isinstance(y, float): if a > saturation: return saturation else: return a else: a[np.where(a >= saturation)] = saturation return a @staticmethod def fit_deriv(x, y, amplitude, x_mean, y_mean, stddev, saturation): d_amplitude = np.exp(-((1 / (2. * stddev**2)) * (x - x_mean)**2 + (1 / (2. * stddev**2)) * (y - y_mean)**2)) d_x_mean = -amplitude * (x - x_mean) / ( stddev**2) * np.exp(-(1 / (2. * stddev**2)) * (x - x_mean)**2 - (1 / (2. * stddev**2)) * (y - y_mean)**2) d_y_mean = -amplitude * (y - y_mean) / ( stddev**2) * np.exp(-(1 / (2. * stddev**2)) * (x - x_mean)**2 - (1 / (2. * stddev**2)) * (y - y_mean)**2) d_stddev = amplitude * ( (x - x_mean)**2 + (y - y_mean)**2) / (stddev**3) * np.exp(-(1 / (2. * stddev**2)) * (x - x_mean)**2 - (1 / (2. * stddev**2)) * (y - y_mean)**2) d_saturation = np.zeros_like(x) return [d_amplitude, d_x_mean, d_y_mean, d_stddev, d_saturation]
class AGaussian1D(Fittable1DModel): inputs = ('x',) outputs = ('y',) amplitude = Parameter() x0 = Parameter() l_sig = Parameter() r_sig = Parameter() @staticmethod def evaluate(x, amplitude, x0, l_sig, r_sig): sig = np.where(x < x0, l_sig, r_sig) f = models.Gaussian1D(amplitude, x0, sig) return f(x)
class LinearPhaseFunc(DiskIntegratedPhaseFunc): """Linear phase function model Examples -------- >>> # Define a linear phase function model with absolute magnitude >>> # H = 5 and slope = 0.04 mag/deg = 2.29 mag/rad >>> import astropy.units as u >>> from sbpy.calib import solar_fluxd >>> from sbpy.photometry import LinearPhaseFunc >>> >>> linear_phasefunc = LinearPhaseFunc(5 * u.mag, 0.04 * u.mag/u.deg, ... radius = 300 * u.km, wfb = 'V') >>> with solar_fluxd.set({'V': -26.77 * u.mag}): ... pha = np.linspace(0, 180, 200) * u.deg ... mag = linear_phasefunc.to_mag(pha) ... ref = linear_phasefunc.to_ref(pha) ... geomalb = linear_phasefunc.geomalb ... phaseint = linear_phasefunc.phaseint ... bondalb = linear_phasefunc.bondalb >>> print('Geometric albedo is {0:.3}'.format(geomalb)) Geometric albedo is 0.0487 >>> print('Bond albedo is {0:.3}'.format(bondalb)) Bond albedo is 0.0179 >>> print('Phase integral is {0:.3}'.format(phaseint)) Phase integral is 0.367 """ _unit = 'mag' H = Parameter(description='Absolute magnitude') S = Parameter(description='Linear slope (mag/deg)') input_units = {'x': u.deg} @staticmethod def evaluate(a, H, S): return H + S * a @staticmethod def fit_deriv(a, H, S): if hasattr(a, '__iter__'): ddh = np.ones_like(a) else: ddh = 1. dds = a return [ddh, dds] def _parameter_units_for_data_units(self, inputs_unit, outputs_unit): return OrderedDict([('H', outputs_unit['y']), ('S', outputs_unit['y']/inputs_unit['x'])])