Load the globular cluster catalog

[1]:
import tstrippy
import numpy as np

These data were taken from Bumgardt’s website. I extracted these data and reformated them into a fits file. This file is stored in tstrippy/data/2023-03-28-merged.fits. The file name is the date when I extracted the data from the website and it is merged because I took the columns I needed from the kinematic parameters and the structural parameters.

[2]:
GCdata=tstrippy.Parsers.baumgardtMWGCs().data

The data columns are the:

  • RA: Right Ascension

  • DEC: Declination

  • Rsun: distance to the sun

  • RV: Line-of-Sight velocity (radial velocity)

  • mualpha: \(\mu_{\alpha *}\)=\(\mu_{\alpha}\cos(\delta)\) the proper motion in right ascension

  • mu_delta: \(\mu_{\delta}\) the proper motion in declination

  • rhopmrade: \(\rho_{\alpha,\delta}\), the correlation coefficient between the proper motions of the right ascension and declination

  • Mass: \(M_\odot\) the mass of the globular cluster in solar masses

  • rh_m: the half mass radius of the globular cluster

and each of which have their associated uncertainties.

[3]:
for key in GCdata.keys():
    print(key)
Cluster
RA
DEC
Rsun
ERsun
RV
ERV
mualpha
ERmualpha
mu_delta
ERmu_delta
rhopmrade
Mass
DM
rh_m

Sampling the uncertainties

You can pick a globular cluster and sample its covariance matrix. Note that there are no uncertainties reported the RA and DEC, since their uncertainties are orders of magnitude lower than the other quantities. Additionally, no uncertainty was reported for the half mass radius.

[4]:
targetGC = "NGC5139"
Nsamples = 100
GCobj=tstrippy.Parsers.baumgardtMWGCs()
mean,cov=GCobj.getGCCovarianceMatrix(targetGC)
samples = np.random.multivariate_normal(mean, cov, Nsamples)
RA_all,DEC_all,Rsun_all,RV_all,mualpha_all,mu_delta_all,Mass_all,rh_m_all=samples.T

Here, I’ll make the plot nicer

[5]:
from matplotlib.patches import Ellipse
import matplotlib.pyplot as plt
plt.rcParams.update({
    "text.usetex": True,
    "font.family": "serif",
    "font.serif": ["Computer Modern Roman"],
})
# Function to plot an ellipse
def plot_cov_ellipse(mean, cov, ax, nstd=2, **kwargs):
    """
    Plots an `nstd` sigma error ellipse based on the specified covariance matrix (`cov`)
    and mean (`mean`).
    Skips plotting if cov[0,0] * cov[1,1] is zero.
    color green if there is a covariance
    """

    denom = cov[0, 0] * cov[1, 1]
    if np.isclose(denom, 0):
        return None

    pearson = cov[0, 1] / np.sqrt(denom)
    pearson = np.clip(pearson, -1, 1)

    kwargs.setdefault("color", "green" if not np.isclose(pearson, 0) else "blue")

    ell_radius_x = np.sqrt(1 + pearson)
    ell_radius_y = np.sqrt(1 - pearson)
    ellipse = Ellipse((0, 0), width=ell_radius_x * 2, height=ell_radius_y * 2, **kwargs)

    scale_x = np.sqrt(cov[0, 0]) * nstd
    scale_y = np.sqrt(cov[1, 1]) * nstd
    transf = (
        plt.matplotlib.transforms.Affine2D()
        .rotate_deg(45)
        .scale(scale_x, scale_y)
        .translate(mean[0], mean[1])
    )
    ellipse.set_transform(transf + ax.transData)

    return ax.add_patch(ellipse,)
[ ]:
# Define the arrays
data = np.vstack([RA_all, DEC_all, Rsun_all, RV_all, mualpha_all, mu_delta_all, Mass_all, rh_m_all]).T
labels = [r'$\alpha$ $(\deg)$', '$\delta$ $(\deg)$', r'$R_{\textrm{sun}}$', r'$v_{\textrm{LOS}}$', r'$\mu_{\alpha}\cos(\delta)$ (mas yr$^{-1}$)', r'$\mu_{\delta}$ (mas yr$^{-1}$)', r'Mass $(M_{\odot})$', r'$r_{\textrm{half mass}}$ (kpc)']

# Create the corner plot
fig, axes = plt.subplots(len(labels), len(labels), figsize=(15, 15))

for i in range(len(labels)):
    for j in range(i+1):
        if i == j:
            axes[i, j].hist(data[:, i], bins=30, color='gray')
        else:
            axes[i, j].scatter(data[:, j], data[:, i], s=1)
            if i > j:
                plot_cov_ellipse(mean[[j, i]], cov[[j, i]][:, [j, i]], axes[i, j],alpha=0.5)
        if i < len(labels) - 1:
            axes[i, j].set_xticks([])
        else:
            axes[i, j].set_xlabel(labels[j])
        if j > 0:
            axes[i, j].set_yticks([])
        else:
            axes[i, j].set_ylabel(labels[i])

    for j in range(i+1, len(labels)):
        axes[i, j].axis('off')
fig.tight_layout()
plt.show()
_images/baumgardt_catalog_10_0.png