Orbital classify

Orbit classification based on the resonance angle behaviour.

[1]:
import numpy as np
import matplotlib.pyplot as plt

import galport

Load coordinates, velocities and actions for the set of orbits

[20]:
xv_act = np.load('data/xv_act_0.npy')

t = np.arange(0, 600.125, 0.125)
xv = xv_act[:,:,0:6]
act = xv_act[:,:,6:9]

theta_p = np.load('data/phi_p.npy')
Omega_p = np.load('data/omega_p.npy')

Find actions, angles and frequencies of the every orbit and Orbit classification at time \(t=400\)

[26]:
%%time

OT = galport.OrbitTools(t=t, xv=xv, act=act)
data = OT.calculate_actions(secular=True)
otype_ILR = OT.classify_orbits(t_out=400, family='ILR', theta_p=theta_p)
otype_vILR = OT.classify_orbits(t_out=400, family='vILR', theta_p=theta_p)
otype_uha = OT.classify_orbits(t_out=400, family='uha', theta_p=theta_p)
CPU times: user 2.33 s, sys: 6.88 ms, total: 2.34 s
Wall time: 2.34 s

Equivalent code for classification (calling the OrbitClassifier module directly)

[38]:
ang = OT.angles
# or
ang = data[:, :, 3:6] # if dJdt=False
# ang = OT.data[:, :, 6:9] # if dJdt=True

OC = galport.OrbitClassifier(t, angles=ang, theta_p=theta_p, time_resolution=5.)
# Not all angles are used in the classification; they are taken with a time step of `time_resolution`
result = OC(t_out=400, family='ILR', time_around_res=False, amplitude_res=False)
# or, you can also set the resonant angle yourself
OC = galport.OrbitClassifier(t, angles=2*(ang[:,:,2] - theta_p) - ang[:,:,0], time_resolution=5.)
result = OC(t_out=400, time_around_res=True, amplitude_res=False)

List of types

0: Not classify

1: Increasing angle

2: Decreasing angle

3: Resonance around \(0\)

4: Resonance around \(\pi\)

5: Passage through \(0\) from \(\Omega_{res} > 0\) to \(\Omega_{res} < 0\)

6: Passage through \(\pi\) from \(\Omega_{res} > 0\) to \(\Omega_{res} < 0\)

7: Passage through \(0\) from \(\Omega_{res} < 0\) to \(\Omega_{res} > 0\)

8: Passage through \(\pi\) from \(\Omega_{res} < 0\) to \(\Omega_{res} > 0\)

Additional variables

time_around_res: returns the resonance entry and exit times for resonant orbits

amplitude_res: returns the maximum libration amplitude of the resonant angle

Secular actions and frequencies

[36]:
t0 = 400
n_t = int(t0)*8

JR_sec = data[:,n_t,9]
Jz_sec = data[:,n_t,10]
Lz_sec = data[:,n_t,11]

kappa_sec = data[:,n_t,12]
omegaz_sec = data[:,n_t,13]
Omega_sec = data[:,n_t,14] - Omega_p[n_t]

Plot different orbital types on the plane \(2(\Omega - \Omega_p)/\kappa\) vs \(\omega_z\kappa\)

[37]:
fig, ax = plt.subplots(figsize=(5,5))
ax.set_aspect('equal')

ax.grid(zorder=-1)

test_bar = (otype_ILR > 2)
test_notbarnotdisk = (otype_ILR <= 2) & (otype_uha == 1) & (otype_vILR == 2)
test_disk = ~test_bar & ~test_notbarnotdisk


ax.scatter(
    2*Omega_sec[test_bar]/kappa_sec[test_bar],
    omegaz_sec[test_bar]/kappa_sec[test_bar],
    s=3, label='Bar'
)

ax.scatter(
    2*Omega_sec[test_disk]/kappa_sec[test_disk],
    omegaz_sec[test_disk]/kappa_sec[test_disk],
    s=3, label='Disk'
)

ax.scatter(
    2*Omega_sec[test_notbarnotdisk]/kappa_sec[test_notbarnotdisk],
    omegaz_sec[test_notbarnotdisk]/kappa_sec[test_notbarnotdisk],
    s=3, label='not Bar, not Disk'
)


ax.set_xlabel('$2(\\Omega - \\Omega_p)/\\kappa$')
ax.set_ylabel('$\\omega_z\\kappa$')
ax.legend(
    loc='upper center',
    bbox_to_anchor=(0.5, 1.15),
    ncols=3
)

ax.set_xlim(-1.0,1.5)
ax.set_ylim(0.5, 2)

plt.show()
../_images/notebooks_classify_12_0.png