Estimating source photometry#
In addition to the theoretical predictions of locations, kinematics and stellar distributions of sources, you may also wish to estimate the photometry of sources in your simulation. This tutorial will show you how to do this!
Learning Goals#
By the end of this tutorial you should know how to:
Compute the photometry of sources in a
PopulationApply dust map extinction to the photometry of sources
Use custom distances for non-Milky Way simulations
Beware - extra dependencies required here!
You’ll need to have installed the extra dependencies of cogsworth to estimate photometry of sources. Check out the installation page for more details on how to do this! (You’ll probably just need to run pip install cogsworth[extras])
[1]:
import cogsworth
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import astropy.units as u
[2]:
# this all just makes plots look nice
%config InlineBackend.figure_format = 'retina'
plt.rc('font', family='serif')
plt.rcParams['text.usetex'] = False
fs = 24
# update various fontsizes to match
params = {'figure.figsize': (12, 8),
'legend.fontsize': fs,
'axes.labelsize': fs,
'xtick.labelsize': 0.9 * fs,
'ytick.labelsize': 0.9 * fs,
'axes.linewidth': 1.1,
'xtick.major.size': 7,
'xtick.minor.size': 4,
'ytick.major.size': 7,
'ytick.minor.size': 4}
plt.rcParams.update(params)
pd.options.display.max_columns = 999
Population photometry#
There are a couple of components that go into estimating the photometry of sources in a simulated Population:
Effective temperature, surface gravity and metallicity of each system (to get bolometric luminosity)
Distance to each source (to convert absolute to apparent magnitudes)
Dust extinction for each source (to apply to apparent magnitudes)
The information for the bolometric luminosity is contained entirely in each COSMIC output bpp table and is calculated using the MIST isochrones. For the dust extinction we use the Bayestar2019 dust map by default and the distances can either be inputted or calculated assuming a Milky Way setup and that the observer is on Earth.
Calculating photometry is fairly simple given an evolved population - let’s try this out.
[3]:
p = cogsworth.pop.Population(2500, use_default_BSE_settings=True)
p.create_population()
Run for 2500 binaries
Sampled 3348 binaries
[1e-02s] Sample initial binaries
[0.7s] Evolve binaries (run COSMIC)
Integrating orbits: 100%|██████████| 3356/3356 [00:04<00:00, 740.76it/s]
[6.0s] Integrate galactic orbits (run gala)
Overall: 6.8s
[4]:
p.get_observables(filters=["Gaia_G_EDR3", "Gaia_BP_EDR3", "Gaia_RP_EDR3"],
ignore_extinction=False, assume_mw_galactocentric=True,
silence_bounds_warning=True);
This populates the p.observables table with:
The assumed extinction for each source
Bolometric magnitudes
Magnitudes in each filter
For the G filter we also output whether the secondary star is brighter (and the observed temperature and surface gravity based on this)
[5]:
p.observables
[5]:
| Av_1 | Av_2 | M_abs_1 | m_app_1 | M_abs_2 | m_app_2 | Gaia_G_EDR3_app_1 | Gaia_G_EDR3_app_2 | teff_obs | log_g_obs | secondary_brighter | Gaia_G_EDR3_abs_1 | Gaia_G_EDR3_abs_2 | Gaia_BP_EDR3_app_1 | Gaia_BP_EDR3_app_2 | Gaia_BP_EDR3_abs_1 | Gaia_BP_EDR3_abs_2 | Gaia_RP_EDR3_app_1 | Gaia_RP_EDR3_app_2 | Gaia_RP_EDR3_abs_1 | Gaia_RP_EDR3_abs_2 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 3.333000 | 6.0 | 10.273020 | 24.902600 | 11.526079 | 26.155659 | 27.786380 | inf | 3237.409615 | 4.972553 | False | 11.143872 | inf | 30.708783 | inf | 12.813383 | inf | 26.322171 | inf | 9.896075 | inf |
| 1 | 5.148000 | 6.0 | 9.205454 | 22.937900 | 9.818399 | 23.550844 | 26.270199 | inf | 3613.601960 | 4.930603 | False | 9.420474 | inf | 29.252073 | inf | 10.581822 | inf | 24.805402 | inf | 8.355534 | inf |
| 2 | 6.000000 | 6.0 | 8.145901 | 22.038654 | 9.927306 | 23.820058 | 25.977614 | inf | 3783.218240 | 4.783519 | False | 8.630722 | inf | 29.164091 | inf | 9.733847 | inf | 24.485698 | inf | 7.586910 | inf |
| 3 | 3.531000 | 6.0 | 9.034814 | 23.516264 | 9.175367 | 23.656817 | 26.155724 | inf | 3560.665698 | 4.889564 | False | 9.186757 | inf | 28.833339 | inf | 10.477703 | inf | 24.728263 | inf | 8.066396 | inf |
| 4 | 6.000000 | 6.0 | 8.854705 | 23.529008 | 10.092647 | 24.766949 | 27.398663 | inf | 3622.894135 | 4.876995 | False | 9.338264 | inf | 30.762635 | inf | 10.567622 | inf | 25.887019 | inf | 8.239965 | inf |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 3343 | 4.983000 | 6.0 | 7.376696 | 21.760561 | 11.144605 | 25.528469 | 25.230796 | inf | 4036.222484 | 4.671091 | False | 7.779006 | inf | 27.678958 | inf | 8.641159 | inf | 23.844498 | inf | 6.865615 | inf |
| 3344 | 3.892272 | 6.0 | 5.245240 | 19.613696 | 7.726504 | 22.094961 | 22.267580 | inf | 5440.884504 | 4.484475 | False | 5.094196 | inf | 23.747926 | inf | 5.495327 | inf | 21.088685 | inf | 4.520856 | inf |
| 3345 | 6.000000 | 6.0 | 6.553501 | 21.096736 | 10.502945 | 25.046180 | 25.025406 | inf | 4745.956610 | 4.644648 | False | 6.597684 | inf | 27.353759 | inf | 7.112288 | inf | 23.646858 | inf | 5.921349 | inf |
| 3346 | 6.000000 | 6.0 | 8.678926 | 23.146658 | 11.734802 | 26.202534 | 27.174966 | inf | 3742.484484 | 4.876034 | False | 9.177240 | inf | 30.142328 | inf | 10.161111 | inf | 25.706517 | inf | 8.191595 | inf |
| 3347 | 1.122000 | 6.0 | 9.119755 | 23.239831 | 12.498029 | 26.618105 | 24.660818 | inf | 3601.924721 | 4.914646 | False | 9.807927 | inf | 26.193570 | inf | 10.979074 | inf | 23.468550 | inf | 8.737116 | inf |
3348 rows × 21 columns
We can use cogsworth to immediately transform this into a colour-magnitude diagram coloured by stellar type to see how things look. You can see that there’s a clear main sequence with some evolved massive stars and lots of white dwarfs.
[6]:
cogsworth.plot.plot_cmd(p, m_filter="Gaia_G_EDR3", c_filter_1="Gaia_BP_EDR3", c_filter_2="Gaia_RP_EDR3", s=5);
Custom distances#
When computing photometry you can either assume a Milky Way galactocentric frame (as above) to automatically work out the distance to each system, or you can manually specify the distance to your sources.
Let’s try this for an example where we also turn off extinction entirely.
[7]:
p.get_observables(filters=["Gaia_G_EDR3", "Gaia_BP_EDR3", "Gaia_RP_EDR3"], ignore_extinction=True,
assume_mw_galactocentric=False, distances=np.ones(len(p.final_pos)) * u.kpc,
silence_bounds_warning=True)
[7]:
| Av_1 | Av_2 | M_abs_1 | m_app_1 | M_abs_2 | m_app_2 | Gaia_G_EDR3_app_1 | Gaia_G_EDR3_app_2 | teff_obs | log_g_obs | secondary_brighter | Gaia_G_EDR3_abs_1 | Gaia_G_EDR3_abs_2 | Gaia_BP_EDR3_app_1 | Gaia_BP_EDR3_app_2 | Gaia_BP_EDR3_abs_1 | Gaia_BP_EDR3_abs_2 | Gaia_RP_EDR3_app_1 | Gaia_RP_EDR3_app_2 | Gaia_RP_EDR3_abs_1 | Gaia_RP_EDR3_abs_2 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 0.0 | 0.0 | 10.273020 | 20.273020 | 11.526079 | 21.526079 | 21.143872 | inf | 3237.409615 | 4.972553 | False | 11.143872 | inf | 22.813383 | inf | 12.813383 | inf | 19.896075 | inf | 9.896075 | inf |
| 1 | 0.0 | 0.0 | 9.205454 | 19.205454 | 9.818399 | 19.818399 | 19.420474 | inf | 3613.601960 | 4.930603 | False | 9.420474 | inf | 20.581822 | inf | 10.581822 | inf | 18.355534 | inf | 8.355534 | inf |
| 2 | 0.0 | 0.0 | 8.145901 | 18.145901 | 9.927306 | 19.927306 | 18.630722 | inf | 3783.218240 | 4.783519 | False | 8.630722 | inf | 19.733847 | inf | 9.733847 | inf | 17.586910 | inf | 7.586910 | inf |
| 3 | 0.0 | 0.0 | 9.034814 | 19.034814 | 9.175367 | 19.175367 | 19.186757 | inf | 3560.665698 | 4.889564 | False | 9.186757 | inf | 20.477703 | inf | 10.477703 | inf | 18.066396 | inf | 8.066396 | inf |
| 4 | 0.0 | 0.0 | 8.854705 | 18.854705 | 10.092647 | 20.092647 | 19.338264 | inf | 3622.894135 | 4.876995 | False | 9.338264 | inf | 20.567622 | inf | 10.567622 | inf | 18.239965 | inf | 8.239965 | inf |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 3343 | 0.0 | 0.0 | 7.376696 | 17.376696 | 11.144605 | 21.144605 | 17.779006 | inf | 4036.222484 | 4.671091 | False | 7.779006 | inf | 18.641159 | inf | 8.641159 | inf | 16.865615 | inf | 6.865615 | inf |
| 3344 | 0.0 | 0.0 | 5.245240 | 15.245240 | 7.726504 | 17.726504 | 15.094196 | inf | 5440.884504 | 4.484475 | False | 5.094196 | inf | 15.495327 | inf | 5.495327 | inf | 14.520856 | inf | 4.520856 | inf |
| 3345 | 0.0 | 0.0 | 6.553501 | 16.553501 | 10.502945 | 20.502945 | 16.597684 | inf | 4745.956610 | 4.644648 | False | 6.597684 | inf | 17.112288 | inf | 7.112288 | inf | 15.921349 | inf | 5.921349 | inf |
| 3346 | 0.0 | 0.0 | 8.678926 | 18.678926 | 11.734802 | 21.734802 | 19.177240 | inf | 3742.484484 | 4.876034 | False | 9.177240 | inf | 20.161111 | inf | 10.161111 | inf | 18.191595 | inf | 8.191595 | inf |
| 3347 | 0.0 | 0.0 | 9.119755 | 19.119755 | 12.498029 | 22.498029 | 19.807927 | inf | 3601.924721 | 4.914646 | False | 9.807927 | inf | 20.979074 | inf | 10.979074 | inf | 18.737116 | inf | 8.737116 | inf |
3348 rows × 21 columns
You’ll notice this runs a lot faster now that we don’t need to query a dustmap or transform any coordinates!
Wrap-up#
Now you know all about how to transform your intrinsic population into an observable sample, nice job! Keep reading the next tutorial to learn about how you can use these observable to tell whether a given system is observable by Gaia.
Note
This tutorial was generated from a Jupyter notebook that can be found here.