Exemple #1
0
def create_Sersic2D(imshape,
                    amplitude=1,
                    x_0=50,
                    y_0=50,
                    radius=25,
                    n=1,
                    ellip=.5,
                    theta=-1):
    """
    Create a galaxy profile defined by a Sersic distribution
        Parameters:
            imshape (numpy.array): image shape given for calculating size of distribution
            amplitude (float): amplitude of the Sersic distribution
            x_0 (float): horizontal position of center
            y_0 (float): vertical position of center
            radius (float): radius of major axis of the distribution
            n (int (0.5, 10) ): Sersic number
            ellip (float (0,1)): eccentricity of the Sersic distribution
            theta (float): orientation angle of the major axis
        Returns:
            numpy.array with galaxy distribution

    """

    x, y = create_meshgrid(imshape)
    mod = Sersic2D(amplitude=amplitude,
                   x_0=x_0,
                   y_0=y_0,
                   r_eff=radius,
                   n=n,
                   ellip=ellip,
                   theta=theta)
    return mod(x, y)
Exemple #2
0
def SersicGalaxy(cell_name,
                 center,
                 r_eff,
                 sersic_n,
                 ellip,
                 angle,
                 num_squares,
                 square_size=1.0,
                 layer=1):
    # We're going to create a brightness profile by randomly dithering
    # num_squares squares of size square_size
    # Create a Cell and add the box
    cell = gds.core.Cell(cell_name)

    for n in range(num_squares):
        # Here we use rejection sampling to generate the 2D Sersic profile
        Reject = True
        while Reject:
            x0 = -6.0 * r_eff + 12.0 * r_eff * rand()
            y0 = -6.0 * r_eff + 12.0 * r_eff * rand()
            if Sersic2D.evaluate(x0, y0, 1.0, r_eff, sersic_n, 0.0, 0.0, ellip,
                                 0.0) > rand():
                Reject = False
        x = center[0] + x0 * cos(angle) + y0 * sin(angle)
        y = center[1] - x0 * sin(angle) + y0 * cos(angle)
        x = round(x / square_size) * square_size
        y = round(y / square_size) * square_size
        pixel = gds.shapes.Rectangle(
            (x - square_size / 2.0, y - square_size / 2.0),
            (x + square_size / 2.0, y + square_size / 2.0),
            layer=layer)
        cell.add(pixel)
    return cell
Exemple #3
0
def fit_sersic_to_stamp(image):
    im = fits.open(image)
    data = im[0].data
    mod = Sersic2D(amplitude=1, r_eff=1, n=3)
    img = mod(data, data.T)

    print(img)

    return img
Exemple #4
0
    def make_galaxy(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Adding smooth galaxy source ...")

        effective_radius = self.config.galaxy_effective_radius
        effective_galaxy_angle = self.config.galaxy_angle + self.effective_rotation_angle

        axial_ratio = self.config.galaxy_axial_ratio
        angle_deg = effective_galaxy_angle.to("deg").value

        # Produce guess values
        initial_sersic_amplitude = self.config.galaxy_central_flux
        initial_sersic_r_eff = effective_radius
        initial_sersic_n = self.config.galaxy_sersic_index
        initial_sersic_x_0 = self.config.galaxy_position.x
        initial_sersic_y_0 = self.config.galaxy_position.y
        initial_sersic_ellip = (axial_ratio - 1.0) / axial_ratio
        initial_sersic_theta = np.deg2rad(angle_deg)

        # Produce sersic model from guess parameters, for time trials
        sersic_x, sersic_y = np.meshgrid(np.arange(self.xsize),
                                         np.arange(self.ysize))
        sersic_model = Sersic2D(amplitude=initial_sersic_amplitude,
                                r_eff=initial_sersic_r_eff,
                                n=initial_sersic_n,
                                x_0=initial_sersic_x_0,
                                y_0=initial_sersic_y_0,
                                ellip=initial_sersic_ellip,
                                theta=initial_sersic_theta)
        sersic_map = sersic_model(sersic_x, sersic_y)

        # Set the galaxy frame
        self.galaxy = Frame(sersic_map)

        # Mask
        self.galaxy[self.rotation_mask] = 0.0

        limit_radius = self.config.galaxy_relative_asymptotic_radius * effective_radius

        # Create galaxy region
        galaxy_center = PixelCoordinate(initial_sersic_x_0, initial_sersic_y_0)
        galaxy_radius = PixelStretch(limit_radius, limit_radius / axial_ratio)
        self.galaxy_region = PixelEllipseRegion(galaxy_center, galaxy_radius,
                                                effective_galaxy_angle)

        # Set galaxy map zero outside certain radius
        self.galaxy[self.galaxy_mask.inverse()] = 0.0

        # Plot
        if self.config.plot: plotting.plot_box(self.galaxy, title="galaxy")
Exemple #5
0
    def fit(self):
        """
        This function ...
        :return:
        """

        sersic_model = Sersic2D(amplitude=sersic_amplitide,
                                r_eff=sersic_r_eff,
                                n=sersic_n,
                                x_0=sersic_x_0,
                                y_0=sersic_y_0,
                                ellip=sersic_ellip,
                                theta=sersic_theta)
        sersic_map = sersic_model(sersic_x, sersic_y)
Exemple #6
0
def make_gals(el, pa, re, sersic, size=(60, 60)):
    x, y = np.meshgrid(np.arange(size[0]), np.arange(size[1]))
    gal_array = []
    for i in range(len(el)):
        mod = Sersic2D(amplitude=1,
                       r_eff=re[i],
                       n=sersic[i],
                       x_0=(size[0] - 1) / 2.0,
                       y_0=(size[1] - 1) / 2.0,
                       ellip=el[i],
                       theta=pa[i])
        img = mod(x, y)
        norm = img / np.max(img)
        gal_array.append(norm)
    return np.array(gal_array)
def SersicGalaxy(boundingcell, cell_name, center, r_eff, sersic_n, ellip, angle, m, square_size=1.0, layer=1): 
    # We're going to create a brightness profile by randomly dithering
    # num_squares squares of size square_size
    # magnitudes are calculated assuming a 10" exposure at 100% light intensity
    # Create a Cell and add the box
    text_step = 2.0 * boundingcell.PixelSizeX 
    num_squares = int(pow(10.0, 0.4 * (27.9 - m)))    
    cell=gds.core.Cell(cell_name)

    nxmin = max(0, int((center[0] - 10.0 * r_eff - boundingcell.xmin) / boundingcell.dx))
    nxmax = min(boundingcell.nx-1, int((center[0] + 10.0 * r_eff - boundingcell.xmin) / boundingcell.dx))    
    nymin = max(0, int((center[1] - 10.0 * r_eff - boundingcell.ymin) / boundingcell.dy))
    nymax = min(boundingcell.ny-1, int((center[1] + 10.0 * r_eff - boundingcell.ymin) / boundingcell.dy))    
    
    for n in range(num_squares):
        # Here we use rejection sampling to generate the 2D Sersic profile
        Reject = True
        while Reject:
            AlreadyFilled = True
            while AlreadyFilled:
                nx = randint(nxmin, nxmax)
                ny = randint(nymin, nymax)            
                AlreadyFilled = boundingcell.data[nx,ny]
            x = boundingcell.x[nx]
            y = boundingcell.y[ny]
            r = sqrt((x-center[0])**2 + (y-center[1])**2)
            #print n, center, nx, ny, x, y, r, Sersic2D.evaluate(x, y, 1.0, r_eff, sersic_n, center[0], center[1], ellip, angle)
            if Sersic2D.evaluate(x, y, 1.0, r_eff, sersic_n, center[0], center[1], ellip, angle) > rand():
                Reject = False
        boundingcell.data[nx,ny] = True
        pixel=gds.shapes.Rectangle((x-boundingcell.dx/2.0, y-boundingcell.dy/2.0), (x+boundingcell.dx/2.0, y+boundingcell.dy/2.0), layer=layer)
        cell.add(pixel)

    # Now we annotate it with non-printing text.
    name = gds.core.Text('Galaxy', (center[0] - 2.0 * text_step, center[1] - 2.0 * text_step), layer=3, magnification=0.002)
    cell.add(name)
    mag = gds.core.Text('Mag = %.2f'%m, (center[0] - 2.0 * text_step, center[1] - 3.0 * text_step), layer=3, magnification=0.002)
    cell.add(mag)
    reff = gds.core.Text('r_eff = %.2f arcseconds'%(r_eff / boundingcell.PixelSizeX * 0.2), (center[0] - 2.0 * text_step, center[1] - 4.0 * text_step), layer=3, magnification=0.002)
    cell.add(reff)
    sersicn = gds.core.Text('n = %.2f'%sersic_n, (center[0] - 2.0 * text_step, center[1] - 5.0 * text_step), layer=3, magnification=0.002)
    cell.add(sersicn)
    ell = gds.core.Text('ellip = %.2f'%ellip, (center[0] - 2.0 * text_step, center[1] - 6.0 * text_step), layer=3, magnification=0.002)
    cell.add(ell)
    rot = gds.core.Text('angle = %.2f degrees'%(angle*180.0/pi), (center[0] - 2.0 * text_step, center[1] - 7.0 * text_step), layer=3, magnification=0.002)
    cell.add(rot)
    #print "Galaxy, m = %f, n = %d"%(m, num_squares)
    return cell
Exemple #8
0
    def intensity(self):
        """
        2D light distribution following a Sersic2D profile

        Returns
        -------
        numpy array
        """
        mod = Sersic2D(x_0=self.x_0,
                       y_0=self.y_0,
                       amplitude=self.amplitude,
                       r_eff=self.r_eff,
                       n=self.n,
                       ellip=self.ellip,
                       theta=self.theta)
        return mod(self.x, self.y)
Exemple #9
0
 def __init__(self,
              psf,
              amplitude=1,
              r_eff=1,
              n=4,
              x_0=0,
              y_0=0,
              ellip=0,
              theta=0 * u.deg,
              **kwargs):
     theta = _check_theta_units(theta)
     super().__init__(amplitude, r_eff, n, x_0, y_0, ellip, theta, **kwargs)
     self.sersic_deconvolved = Sersic2D(amplitude, r_eff, n, x_0, y_0,
                                        ellip, theta, **kwargs)
     psf /= psf.sum()
     self.psf = psf
Exemple #10
0
    def make_galaxies_astropy(self, image, flux, galsize, x, y, ar, pa, n=4):

        for f, s, xi, yi, pai, ari, in zip(flux, galsize, x, y, pa, ar):

            fluxlim = 0.0001 * f  # 0.1
            scale = 1  # arcsec/pixel
            r_e = s  # effective radius
            ellip = ari  # ellipticity
            theta = numpy.deg2rad(pai)  # position angle
            x_cent = 0  # x centroid
            y_cent = 0  # x centroid
            tot_flux = f  # total flux

            s1 = Sersic1D(amplitude=1, r_eff=r_e, n=n)
            r = numpy.arange(0, 1000, scale)
            s1_n = s1(r) / sum(s1(r))
            extent = numpy.where(s1_n * f > fluxlim)[0].max()

            if extent % 2 > 0:
                extent += 1

            ser_model = Sersic2D(r_eff=r_e,
                                 n=n,
                                 ellip=ellip,
                                 theta=theta,
                                 x_0=x_cent,
                                 y_0=y_cent)

            x = numpy.arange(-extent / 1., extent / 1., scale) + x_cent / scale
            y = numpy.arange(-extent / 1., extent / 1., scale) + y_cent / scale

            X, Y = numpy.meshgrid(x, y)

            img = ser_model(X, Y)
            img /= numpy.sum(img)
            img *= tot_flux

            xi, yi = int(xi), int(yi)
            # COLUMNS FIRST -- because FITS are silly
            image[yi - img.shape[1] // 2:yi + img.shape[1] // 2,
                  xi - img.shape[0] // 2:xi + img.shape[0] // 2] += img

        return image
def make_model(n, re, MLs, x0, y0, x, y, dist, edges, lastedge=50):
    ''' This function is where we generate the grid of all possible models for a 
    given galaxy. Given a sersic index "n" and a half-light radius "re",
    we make models of the 2D light distribution of a galaxy with that
    n/re. Then, we apply a range of power-law M/L gradients to turn that light profile 
    into a bunch of theoretical mass profiles. We convolve the 2D mass profiles with 
    the HST F160W PSF to get a set of theoretical 'as-observed' mass profiles, then 
    extract the 1D versions of those mass profiles in annuli. We return the set of both 
    the intrinsic and convolved possible mass profiles for the galaxy.
    "MLs" : list of possible M/L gradients
    "x0, y0" : where to center the galaxy 
    "x, y" : grid of x/y values where we want to evaluate the model
    "edges" : list of the radii of the elliptical apertures we used to calculate the
        M/L gradient. We want to pull out the model M/L at these same radii. In units
        of pixels.
    "dist" : array that has the distance of each pixel in the model from the center
        x0,y0 location. this is used to calculate the actual M/L gradient.
    '''
    
	# make 2D sersic model. re should be given in *pixels*!
	lightModel = Sersic2D(amplitude=1e5, r_eff=re, n=n, x_0=x0, y_0=y0)(x,y)

	# multiply M/L gradients by that light profile to make mass profiles
	massModel = lightModel[:,:, np.newaxis] * \
		dist[:, :, np.newaxis] ** (np.log10(MLs[:-1]) / np.log10(lastedge))
    # get the half-mass radii for all those mass profiles     
	rehalf = np.array([get_rehalf(re, n, ml, edges, lastedge) for ml in MLs])

	# Convolve both the light and mass profiles with the HST PSF
	lightConv = fftconvolve(lightModel, psf, mode='same')
	massConv = np.array([fftconvolve(massModel[:,:,m], psf, mode='same')
		for m in range(len(MLs[:-1]))])
    
    # initialize some arrays that will hold the model light and mass
    # profiles (both in observed & deconvolved space)    
	light = np.zeros((len(edges)))
	mass = np.zeros((len(MLs), len(edges)))
	lightInt = np.zeros((len(edges)))
	massInt = np.zeros((len(MLs), len(edges)))
	edges = np.append(edges, edges[-1]+1)
    
    # measure aperture photometry in annuli for both light and mass
	for an in range(len(edges)-1):
        # make the aperture at this radius
		ap = photutils.CircularAperture((x0,y0), r = edges[an])
        
		# and measure aperture photometry on light profile in that annulus
		light[an] = photutils.aperture_photometry(lightConv, ap)['aperture_sum'][0]
		lightInt[an] = photutils.aperture_photometry(lightModel, ap)['aperture_sum'][0]
        
        # measure aperture photometry for all possible mass maps in that annulus
		for slope_idx in range(len(MLs[:-1])):
			mass[slope_idx, an] = photutils.aperture_photometry(massConv[slope_idx,:,:], \
				ap)['aperture_sum'][0]
			massInt[slope_idx, an] = photutils.aperture_photometry(massModel[:,:,slope_idx], \
				ap)['aperture_sum'][0]
		mass[-1, :] = light
		massInt[-1,:] = lightInt

    # ultimatly we want the profile in *annuli* so just subtract smaller apertures
    # from larger apertures to make the profile in annuli not cumulative distributions
	lightDiff = light - np.insert(light[:-1], 0, 0)
	massDiff = mass - np.insert(mass[:, :-1], 0, np.zeros((len(MLs))), axis=1)
	lightDiffInt = lightInt - np.insert(lightInt[:-1], 0, 0)
	massDiffInt = massInt - np.insert(massInt[:, :-1], 0, np.zeros((len(MLs))), axis=1)
    
    # what we want to return:
    # 1) half-mass radii for each M/L gradient
    # 2) "as-observed" eg convolved M/L profile for each possible M/L gradient
    # 3) intrinsic M/L profile for each possible M/L gradient
    # keep in mind that the "r" axis of these arrays matches our measurements
	return (rehalf, massDiff / lightDiff[np.newaxis, :], massDiffInt / lightDiffInt[np.newaxis, :])
Exemple #12
0
    def make_extended_sources(self):
        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Making extended sources ...")

        # Loop over the filters
        for fltr in self.frames:

            # Get the frame
            frame = self.frames[fltr]

            # Loop over the sources
            for index in range(len(self.extended_source_catalog)):

                principal = self.extended_source_catalog["Principal"][index]

                # Determine flux
                if principal: central_flux = 1e2
                else: central_flux = np.random.uniform(10, 50)

                # Get pixel position
                coordinate = self.extended_source_catalog.get_position(
                    index).to_pixel(frame.wcs)

                initial_sersic_amplitude = central_flux
                initial_sersic_x_0 = coordinate.x
                initial_sersic_y_0 = coordinate.y

                if principal:

                    s4g_name, pa, ellipticity, n, re, mag = catalogs.get_galaxy_s4g_one_component_info(
                        "M81")

                    angle = Angle(pa, "deg")
                    angle_deg = pa

                    effective_radius = re.to(
                        "arcsec").value / frame.average_pixelscale.to(
                            "arcsec").value

                    initial_sersic_n = n
                    initial_sersic_r_eff = effective_radius
                    initial_sersic_ellip = ellipticity
                    initial_sersic_theta = np.deg2rad(angle_deg)

                    # 1 / axial_ratio = 1 - ellipticity
                    axial_ratio = 1. / (1. - ellipticity)

                else:

                    # Get position angle and axes lengths
                    pa = self.extended_source_catalog["Posangle"][index]
                    major = self.extended_source_catalog["Major"][index]
                    minor = self.extended_source_catalog["Minor"][index]
                    axial_ratio = major / minor

                    angle = Angle(pa, "deg")
                    angle_deg = pa

                    effective_radius = major

                    # Produce guess values
                    initial_sersic_r_eff = effective_radius
                    initial_sersic_n = self.config.galaxy_sersic_index
                    initial_sersic_ellip = (axial_ratio - 1.0) / axial_ratio
                    initial_sersic_theta = np.deg2rad(angle_deg)

                # Produce sersic model from guess parameters, for time trials
                sersic_x, sersic_y = np.meshgrid(np.arange(frame.xsize),
                                                 np.arange(frame.ysize))
                sersic_model = Sersic2D(amplitude=initial_sersic_amplitude,
                                        r_eff=initial_sersic_r_eff,
                                        n=initial_sersic_n,
                                        x_0=initial_sersic_x_0,
                                        y_0=initial_sersic_y_0,
                                        ellip=initial_sersic_ellip,
                                        theta=initial_sersic_theta)

                sersic_map = sersic_model(sersic_x, sersic_y)

                # Limit galaxy?
                if self.config.limit_galaxy:

                    limit_radius = self.config.galaxy_relative_asymptotic_radius * effective_radius

                    # Create galaxy region
                    galaxy_center = PixelCoordinate(initial_sersic_x_0,
                                                    initial_sersic_y_0)
                    galaxy_radius = PixelStretch(limit_radius,
                                                 limit_radius / axial_ratio)
                    galaxy_region = PixelEllipseRegion(galaxy_center,
                                                       galaxy_radius, angle)

                    galaxy_mask = galaxy_region.to_mask(
                        frame.xsize, frame.ysize)

                    # Set galaxy map zero outside certain radius
                    sersic_map[galaxy_mask.inverse()] = 0.0

                # Add
                frame += sersic_map

            # mask
            frame[self.rotation_masks[fltr]] = 0.0
    def __init__(self,
                 sp,
                 r_eff,
                 n,
                 theta,
                 ellip,
                 xy_dim,
                 pixel_scale,
                 num_r_eff=10,
                 dx=0,
                 dy=0,
                 labels=None):

        self.sp = sp
        self.mag_limit = sp.mag_limit
        self.mag_limit_band = sp.mag_limit_band
        self.smooth_model = None

        if self.mag_limit is not None and sp.frac_num_sampled < 1.0:
            _r_eff = check_units(r_eff, 'kpc').to('Mpc').value
            _theta = check_units(theta, 'deg').to('radian').value
            _distance = check_units(sp.distance, 'Mpc').to('Mpc').value
            _pixel_scale = check_units(pixel_scale, u.arcsec / u.pixel)

            if _r_eff <= 0:
                raise Exception('Effective radius must be greater than zero.')

            xy_dim = check_xy_dim(xy_dim)
            x_0, y_0 = xy_dim // 2
            x_0 += dx
            y_0 += dy
            self.n = n
            self.ellip = ellip
            self.r_sky = np.arctan2(_r_eff, _distance) * u.radian.to('arcsec')
            self.r_sky *= u.arcsec
            r_pix = self.r_sky.to('pixel', u.pixel_scale(_pixel_scale)).value

            self.smooth_model = Sersic2D(x_0=x_0,
                                         y_0=y_0,
                                         n=n,
                                         r_eff=r_pix,
                                         theta=_theta,
                                         ellip=ellip)

        self.xy_kw = dict(num_stars=sp.num_stars,
                          r_eff=r_eff,
                          n=n,
                          theta=theta,
                          ellip=ellip,
                          distance=sp.distance,
                          xy_dim=xy_dim,
                          num_r_eff=num_r_eff,
                          dx=dx,
                          dy=dy,
                          pixel_scale=pixel_scale,
                          random_state=sp.rng)

        _xy = sersic_xy(**self.xy_kw)

        super(SersicSP, self).__init__(_xy, sp.mag_table, xy_dim, pixel_scale,
                                       labels)
Exemple #14
0
#function: Composite moffat psf for multiple objects
def E2moff_multi((x,y), psftype, given, free):
    out = 0
    count = 0
    for i, psf in enumerate(psftype):
        #add moffat to output for each moffat
        if psf == '3':
            #given is empty, general psf params are all in free
            out+= E2moff((x, y),*free[count:count+7])
            count = count+7
        if psf == '2':
            #given contains [ax,ay,b,theta], free has [A, x0, y0]
            out+= E2moff((x,y),free[count],given[i][0],given[i][1],given[i][2],given[i][3],free[count+1],free[count+2])
            count = count+3
        if psf == '1':
            #given contains [ax,ay,b,theta,x0,y0], free has [A]
            out+= E2moff((x, y),free[count],*given[i])
            count = count+1
        if psf[0] == 's':
            #we need to use sersic profile
            from astropy.modeling.models import Sersic2D
            if psf[1] == 'n':
                #full sersic profile
                out+= Sersic2D(*free[count:count+7])(x,y)
                count = count+7
            else:
                #known sersic n
                out+= Sersic2D(amplitude=free[count],r_eff=free[count+1],n=float(psf[1:]),x_0=free[count+2],y_0=free[count+3],ellip=free[count+4],theta=free[count+5])(x,y)
                count = count+6
    return out
Exemple #15
0
def sersic_xy(num_stars,
              r_eff,
              n,
              theta,
              ellip,
              distance,
              xy_dim,
              pixel_scale=0.2,
              num_r_eff=10,
              random_state=None):
    """
    Sample xy positions from a two-dimensional Sersic distribution. 


    .. note::
        To sample the Sersic distribution, `~artpop.space.sample.xy_from_grid` 
        is used. This means that large sources (and/or large `num_r_eff`) eat 
        up a lot of memory. 

    Parameters
    ----------
    num_stars : int
        Number of stars (i.e., positions) to sample.
    r_eff : float or `~astropy.units.Quantity`
        Effective radius of the source. If a float is given, the units are
        assumed to be `~astropy.units.kpc`. Must be greater than zero.
    n : float
        Sersic index. Must be greater than zero.
    theta : float or `~astropy.units.Quantity`
        Rotation angle, counterclockwise from the positive x-axis. If a float
        is given, the units are assumed to be `degree`.
    ellip : float
        Ellipticity.
    distance : float or `~astropy.units.Quantity`
        Distance to source. If float is given, the units are assumed
        to be `~astropy.units.Mpc`.
    xy_dim : list-like
        Dimensions of the mock image in xy coordinates. If int is given,
        will make the x and y dimensions the same.
    pixel_scale : float or `~astropy.units.Quantity`, optional
        The pixel scale of the mock image. If a float is given, the units will
        be assumed to be `~astropy.units.arcsec` per `~astropy.units.pixels`. 
    num_r_eff : float, optional
        Number of r_eff to sample positions within. This parameter is needed
        because the current Sersic sampling function samples from within a
        discrete grid. Default is 10.
    random_state : `None`, int, list of ints, or `~numpy.random.RandomState`
        If `None`, return the `~numpy.random.RandomState` singleton used by
        ``numpy.random``. If `int`, return a new `~numpy.random.RandomState`
        instance seeded with the `int`.  If `~numpy.random.RandomState`,
        return it. Otherwise raise ``ValueError``.

    Returns
    -------
    xy : `~numpy.ma.MaskedArray`
        Masked numpy array of xy positions. Positions that fall outside the 
        mock image are masked.
    """
    if n <= 0:
        raise Exception('Sersic index n must be greater than zero.')

    xy_dim = check_xy_dim(xy_dim)

    r_eff = check_units(r_eff, 'kpc').to('Mpc').value
    theta = check_units(theta, 'deg').to('radian').value
    distance = check_units(distance, 'Mpc').to('Mpc').value
    pixel_scale = check_units(pixel_scale, u.arcsec / u.pixel)

    if r_eff <= 0:
        raise Exception('Effective radius must be greater than zero.')

    r_pix = np.arctan2(r_eff, distance) * u.radian.to('arcsec') * u.arcsec
    r_pix = r_pix.to('pixel', u.pixel_scale(pixel_scale)).value
    sample_dim = 2 * np.ceil(r_pix * num_r_eff).astype(int) + 1
    x_0, y_0 = sample_dim // 2, sample_dim // 2

    model = Sersic2D(x_0=x_0,
                     y_0=y_0,
                     amplitude=1,
                     r_eff=r_pix,
                     n=n,
                     ellip=ellip,
                     theta=theta)

    xy = xy_from_grid(num_stars,
                      model,
                      xy_dim,
                      sample_dim,
                      random_state=random_state)

    return xy
Exemple #16
0
def sersic_profile(r_eff=100,
                   n=4,
                   ellipticity=0.5,
                   angle=30,
                   normalization="total",
                   width=1024,
                   height=1024,
                   x_offset=0,
                   y_offset=0,
                   oversample=1):
    """
    Returns a 2D array with a normailised sersic profile

    Parameters
    ----------
    r_eff : float
        [pixel] Effective (half-light) radius

    n : float
        Power law index.
        - n=1 for exponential (spiral),
        - n=4 for de Vaucouleurs (elliptical)

    ellipticity : float
        Ellipticity is defined as (a - b)/a. Default = 0.5

    angle : float
        [deg] Default = 30. Rotation anti-clockwise from the x-axis

    normalization : str, optional
        ["half-light", "centre", "total"] Where the profile equals unity
        If normalization equals:
        - "half-light" : the pixels at the half-light radius are set to 1
        - "centre" : the maximum values are set to 1
        - "total" : the image sums to 1

    width, height : int
        [pixel] Dimensions of the image

    x_offset, y_offset : float
        [pixel] The distance between the centre of the profile and the centre
        of the image

    oversample : int
        Factor of oversampling, default factor = 1. If > 1, the model is
        discretized by taking the average of an oversampled grid.

    Returns
    -------
    img : 2D array


    Notes
    -----
    Most units are in [pixel] in this function. This differs from :func:`.galaxy`
    where parameter units are in [arcsec] or [pc]

    """

    from astropy.modeling.models import Sersic2D

    # Silently cast to integer
    os_factor = np.int(oversample)

    if os_factor <= 0:
        raise ValueError("Oversampling factor must be >=1.")

    width_os = os_factor * width
    height_os = os_factor * height
    x, y = np.meshgrid(np.arange(width_os), np.arange(height_os))

    dx = 0.5 * width_os + x_offset * os_factor
    dy = 0.5 * height_os + y_offset * os_factor

    r_eff_os = r_eff * os_factor

    mod = Sersic2D(amplitude=1,
                   r_eff=r_eff_os,
                   n=n,
                   x_0=dx,
                   y_0=dy,
                   ellip=ellipticity,
                   theta=np.deg2rad(angle))
    img_os = mod(x, y)

    # Rebin os_factord image
    img = _rebin(img_os, os_factor)

    if "cen" in normalization.lower():
        img /= np.max(img)
    elif "tot" in normalization.lower():
        img /= np.sum(img)

    return img
Exemple #17
0
def sim_galaxy(patch_size,
               pixel_size,
               gal_type=None,
               gal_params=None,
               duet=None,
               band=None,
               duet_no=None):
    '''
        Return 2D array of a Sersic profile to simulate a galaxy

        Required inputs:
        patch_size = Axis sizes of returned galaxy patch in pixels (15,15)
        pixel_size = Angular size of pixel (6 * ur.arcsec)

        Optional inputs:
        gal_type = String that loads a pre-built 'average' galaxy or allows custom definition
        gal_params = Dictionary of parameters for Sersic model: ...
        duet = Telescope instance
        band = duet.bandpass (defaults to DUET1; deprecated)
        duet_no = integer (1 or 2) for DUET bandpass
    '''
    from astropy.modeling.models import Sersic2D
    from astroduet.utils import duet_abmag_to_fluence, duet_no_from_band

    if duet is None:
        duet = Telescope()
    if band is None:
        band = duet.bandpass1
    if duet_no is None:
        duet_no = duet_no_from_band(band)

    x = np.linspace(-(patch_size[0] // 2), patch_size[0] // 2, patch_size[0])
    y = np.linspace(-(patch_size[1] // 2), patch_size[1] // 2, patch_size[1])
    x, y = np.meshgrid(x, y)

    # Takes either a keyword or Sersic profile parameters
    # Typical galaxy parameters based on Bai et al. (2013)
    # Hardcoded for now, to-do: take distance as an input
    if gal_type == 'spiral':
        # A typical spiral galaxy at 100 Mpc
        surface_mag = 26.2 * u.ABmag  # surface brightness (per arcsec**2)
        surface_rate = duet.fluence_to_rate(
            duet_abmag_to_fluence(surface_mag, duet_no,
                                  duet=duet))  # surface count rate at r_eff
        amplitude = surface_rate * pixel_size.value**2  # surface brightness (per pixel)
        r_eff = 16.5 / pixel_size.value
        n = 1
        theta = 0
        ellip = 0.5
        x_0, y_0 = r_eff, 0
    elif gal_type == 'elliptical':
        # A typical elliptical galaxy at 100 Mpc
        surface_mag = 25.0 * u.ABmag
        surface_rate = duet.fluence_to_rate(
            duet_abmag_to_fluence(surface_mag, duet_no,
                                  duet=duet))  # surface count rate at r_eff
        amplitude = surface_rate * pixel_size.value**2  # surface brightness (per pixel)
        r_eff = 12.5 / pixel_size.value
        n = 4
        theta = 0
        ellip = 0.5
        x_0, y_0 = r_eff, 0
    elif gal_type == 'dwarf':
        # A typical dwarf galaxy at 10 Mpc
        surface_mag = 25.8 * u.ABmag
        surface_rate = duet.fluence_to_rate(
            duet_abmag_to_fluence(surface_mag, duet_no,
                                  duet=duet))  # surface count rate at r_eff
        amplitude = surface_rate * pixel_size.value**2  # surface brightness (per pixel)
        r_eff = 70 / pixel_size
        r_eff = r_eff.value
        n = 4
        theta = 0
        ellip = 0.5
        x_0, y_0 = r_eff, 0
    elif (gal_type == 'custom') | (gal_type == None):
        # Get args from gal_params, default to spiral values
        surface_mag = gal_params.get('magnitude', 26) * u.ABmag
        surface_rate = duet.fluence_to_rate(
            duet_abmag_to_fluence(surface_mag, duet_no,
                                  duet=duet))  # surface count rate at r_eff
        amplitude = surface_rate * pixel_size.value**2  # surface brightness (per pixel)
        r_eff = gal_params.get('r_eff', 16.5 / pixel_size.value)
        n = gal_params.get('n', 1)
        theta = gal_params.get('theta', 0)
        ellip = gal_params.get('ellip', 0.5)
        x_0 = gal_params.get('x_0', 16.5 / pixel_size.value)
        y_0 = gal_params.get('y_0', 0)

    mod = Sersic2D(amplitude=amplitude,
                   r_eff=r_eff,
                   n=n,
                   x_0=x_0,
                   y_0=y_0,
                   ellip=ellip,
                   theta=theta)
    gal = mod(x, y)

    return gal