Artificial Source Objects

Open In Colab

To generate synthetic images, ArtPop “observes” Source objects. In essence, Source objects are containers that hold the xy pixel positions and stellar mags of the artificial stellar population. As we showed in the Building Stellar Populations and Sampling Spatial Distributions tutorials, ArtPop has the capability to generate these parameters, but you can generate them in any way you want (i.e., independently from ArtPop). You just need to make sure to create xy and mags in the correct format to initialize the Source.

Note: To generate MIST synthetic photometry using ArtPop, MIST isochrone grids are required. The first time you use a MIST grid, ArtPop will download it and save it to your MIST_PATH. If this environment variable is not set, the grid(s) will be saved in ~/.artpop/mist.

[1]:
# Third-party imports
import numpy as np
import matplotlib.pyplot as plt
from astropy import units as u

# Project import
import artpop

# artpop's matplotlib style
plt.style.use(artpop.mpl_style)

# use this random state for reproducibility
rng = np.random.RandomState(9)

Building a Source Object

In this first example, we will show you how to build a Source object from scratch. We’ll assume a uniform distribution of stars at fixed surface brightness. We start by calculating the mean stellar magnitude of an SSP using the MISTIsochrone class:

[2]:
iso = artpop.MISTIsochrone(
    log_age = 9,          # log of age in years
    feh = -1,             # metallicity [Fe/H]
    phot_system = 'LSST', # photometric system(s)
)

# normalize the IMF by number
mean_mag = iso.ssp_mag('LSST_i', norm_type='number')

Next we use the constant_sb_stars_per_pix function to calculate the number of stars per pixel this population would have for a given surface brightness and distance (let’s put the population at 10 Mpc):

[3]:
distance = 10 * u.Mpc
pixel_scale = 0.2

num_stars_per_pix = artpop.constant_sb_stars_per_pix(
    sb = 24,                  # surface brightness
    mean_mag = mean_mag,      # mean stellar magnitude
    distance = distance,      # distance to system
    pixel_scale = pixel_scale # pixel scale in arcsec/pixel
)
print(f'number of stars per pixel = {num_stars_per_pix:.2f}')
number of stars per pixel = 494.43

Let’s say we intend to create an artificial image that is 101 pixels on a side. Then we can calculate the number of pixels, and hence number of stars, in our image.

[4]:
xy_dim = (101, 101)
num_stars = int(np.multiply(*xy_dim) * num_stars_per_pix)
print(f'number of stars = {num_stars:.0e}')
number of stars = 5e+06

Putting the pieces together, we sample num_stars stellar magnitudes and positions to build our Source object:

[5]:
# build SSP with num_stars stars
ssp = artpop.SSP(
    isochrone = iso,       # isochrone object
    num_stars = num_stars, # number of stars
    imf = 'kroupa',        # default imf
    distance = distance,   # distance to system
    random_state = rng,    # random state for reproducibility
)

# sample num_stars positions in a uniform grid
xy = np.vstack([rng.uniform(0, xy_dim[0], num_stars),
                rng.uniform(0, xy_dim[1], num_stars)]).T

# create the artificial source
src = artpop.Source(xy, ssp.mag_table, xy_dim)

Note ssp has an attribute called mag_table, which is an astropy table with stellar magnitudes in the given photometric system(s), which is passed as an argument to Source and stored as the mags attribute:

[6]:
# here are the first 10 rows of the table
# each row corresponds to a single star
src.mags[:10]
[6]:
Table length=10
LSST_uLSST_gLSST_rLSST_iLSST_zLSST_y
float64float64float64float64float64float64
47.5935575786303844.3300592254897642.7824343667992641.8914148552369941.46713343713507541.25933819275674
43.67287956444404541.2274539266189539.9653194708401239.423887931500939.1663117260356839.01244589273298
43.70231166607399641.2543646346786239.9905615553364539.4480838960807239.1900116258116839.0358924533673
46.2901938511232543.42174083925774441.96790864381265641.23411836097583540.88471660919698540.69384323226573
46.20171459890721643.3564081496115541.90883311666541.1851683314976940.8401455512873640.65018191045182
45.3926491559394142.72191381643849441.33492153056991540.6906712409212140.3823602554326740.20509055510217
44.1057958852519141.6203167190079940.33275379644532439.7746060548294639.5091002827780139.35137020065203
45.1735947998100942.538998567562141.169133867959840.5422225364294540.2424024193920640.0694116529263
46.8232751397305543.80497427505565642.3121083900217341.5157794526906141.1366600921155340.939627726304245
44.53637603090895441.996477029030340.67721585585170640.0952214293833939.8180508542726639.655400968864015

The src object contains all the information about the source that we need to simulate an observation, which is described in the Making Artificial Images tutorial.

Here’s a look at the positions and \(i\)-band magnitudes of a random subset of the stars in src:

[7]:
idx = rng.choice(np.arange(src.num_stars), 5000, replace=False)
plt.scatter(src.x[idx], src.y[idx], s=4,
            c=src.mags['LSST_i'][idx], cmap='magma')

cbar = plt.colorbar()
cbar.ax.set_ylabel('$m_i$')
plt.xlabel('$x$ [pixel]')
plt.ylabel('$y$ [pixel]')
plt.title(f'SSP at {distance:.0f}')
plt.minorticks_on();
../_images/tutorials_source_15_0.png

As a sanity check, we can calculate the surface brightness of the population:

[8]:
total_flux = (10**(-0.4 * src.mags['LSST_i'])).sum()
area = np.multiply(*xy_dim) * pixel_scale**2
sb = -2.5 * np.log10(total_flux / area)
print(f'SB = {sb:.2f} mag / arcsec^2')
SB = 24.00 mag / arcsec^2

Helper SSP Source Objects

For convenience, ArtPop provides objects for generating SSP + spatial distribution combinations. Here we generate an SSP with a uniform spatial distribution, similar to above, using the MISTUniformSSP helper class:

[9]:
# let's put the population at 5 Mpc
distance = 5 * u.Mpc

# create ssp distributed uniformly in space
src_uniform = artpop.MISTUniformSSP(
    log_age = 9,          # log of age in years
    feh = -1,             # metallicity [Fe/H]
    phot_system = 'LSST', # photometric system(s)
    distance = distance,  # distance to system
    xy_dim = 101,         # image dimension (101, 101)
    pixel_scale = 0.2,    # pixel scale in arcsec / pixel
    sb = 24,              # surface brightness (SB)
    sb_band='LSST_i',     # bandpass to calculate the SB
    random_state = rng,   # random state for reproducibility
)

# plot positions with symbols colored by i-band mags
idx = rng.choice(np.arange(src_uniform.num_stars), 5000, replace=False)
plt.scatter(src_uniform.x[idx], src_uniform.y[idx], s=4,
            c=src_uniform.mags['LSST_i'][idx], cmap='magma')

cbar = plt.colorbar()
cbar.ax.set_ylabel('$m_i$')
plt.xlabel('$x$ [pixel]')
plt.ylabel('$y$ [pixel]')
plt.title(f'SSP at {distance:.0f}')
plt.minorticks_on();
../_images/tutorials_source_19_0.png

Composite Sources

Similar to creating composite stellar populations, composite Source objects are created intuitively using the + operator. Here we’ll use MISTPlummerSSP to create an SSP with a Plummer spatial distribution and add it to the uniformly distributed population we created above:

[10]:
# create ssp distributed uniformly in space
src_plummer = artpop.MISTPlummerSSP(
    log_age = 10.1,           # log of age in years
    feh = -1.5,               # metallicity [Fe/H]
    scale_radius = 20 * u.pc, # effective radius
    num_stars = 5e5,          # number of stars
    phot_system = 'LSST',     # photometric system(s)
    distance = distance,      # distance to system
    xy_dim = 101,             # image dimension (101, 101)
    pixel_scale = 0.2,        # pixel scale in arcsec / pixel
    random_state = rng,       # random state for reproducibility
)

# add sources together
composite_src = src_uniform + src_plummer

# plot positions with symbols colored by i-band mags
idx = rng.choice(np.arange(composite_src.num_stars), int(1e4), replace=False)
plt.scatter(composite_src.x[idx], composite_src.y[idx], s=2,
            c=composite_src.mags['LSST_i'][idx], cmap='magma')

cbar = plt.colorbar()
cbar.ax.set_ylabel('$m_i$')
plt.xlabel('$x$ [pixel]')
plt.ylabel('$y$ [pixel]')
plt.title(f'SSP at {distance:.0f}')
plt.minorticks_on();
WARNING: 2656 stars outside the image
../_images/tutorials_source_21_1.png