|
1 | 1 | import os |
2 | | - |
3 | 2 | import numpy as np |
4 | | - |
5 | 3 | from .units import ms |
| 4 | +from matplotlib.colors import LinearSegmentedColormap |
| 5 | +from astropy.constants import M_earth, M_sun |
| 6 | +#colormaps |
| 7 | +transgrad = LinearSegmentedColormap.from_list('transmission_gradient', ( |
| 8 | + # Edit this gradient at https://eltos.github.io/gradient/#Random%20gradient%209491=008080-FFF2F2-440154 |
| 9 | + (0.000, (1.000, 0.000, 0.000)), # Bright red (#FF0000) at the low end |
| 10 | + (0.500, (1.000, 1.000, 1.000)), # Bright gray (white) at the middle |
| 11 | + (1.000, (0.000, 0.000, 0.545)))) |
| 12 | + |
| 13 | +# Define color gradients |
| 14 | +star_gradient = LinearSegmentedColormap.from_list( |
| 15 | + "star_gradient", |
| 16 | + [ |
| 17 | + (0.000, (0.000, 0.169, 0.867)), |
| 18 | + (0.050, (0.000, 0.169, 0.867)), |
| 19 | + (0.500, (1.000, 1.000, 1.000)), |
| 20 | + (0.950, (0.867, 0.000, 0.031)), |
| 21 | + (1.000, (0.867, 0.000, 0.031)), |
| 22 | + ], |
| 23 | +) |
| 24 | + |
| 25 | +star_gradient_flux = LinearSegmentedColormap.from_list( |
| 26 | + "star_gradient", |
| 27 | + ( |
| 28 | + # Edit this gradient at https://eltos.github.io/gradient/#Random%20gradient%209491=0:FFFFFF-20:786500-25:806B00-30:887200-35:907900-40:988000-45:A08700-50:A88E00-55:B09500-60:B99C00-65:C1A300-70:CAAA00-75:D2B100-80:DBB900-85:E4C000-90:EDC700-95:F6CE00-100:FFD600 |
| 29 | + (0.000, (0.184, 0.157, 0.000)), |
| 30 | + (0.063, (0.227, 0.192, 0.000)), |
| 31 | + (0.125, (0.271, 0.231, 0.000)), |
| 32 | + (0.188, (0.318, 0.271, 0.000)), |
| 33 | + (0.250, (0.365, 0.310, 0.000)), |
| 34 | + (0.313, (0.412, 0.349, 0.000)), |
| 35 | + (0.375, (0.463, 0.392, 0.000)), |
| 36 | + (0.438, (0.510, 0.435, 0.000)), |
| 37 | + (0.500, (0.561, 0.478, 0.000)), |
| 38 | + (0.563, (0.612, 0.522, 0.000)), |
| 39 | + (0.625, (0.667, 0.565, 0.000)), |
| 40 | + (0.688, (0.718, 0.608, 0.000)), |
| 41 | + (0.750, (0.773, 0.655, 0.000)), |
| 42 | + (0.813, (0.827, 0.698, 0.000)), |
| 43 | + (0.875, (0.886, 0.745, 0.000)), |
| 44 | + (0.938, (0.941, 0.792, 0.000)), |
| 45 | + (1.000, (1.000, 0.839, 0.000)), |
| 46 | + ), |
| 47 | +) |
| 48 | + |
| 49 | +planet_gradient_2 = LinearSegmentedColormap.from_list( |
| 50 | + "planet_gradient_2", |
| 51 | + [ |
| 52 | + (0.000, (1.000, 0.365, 0.051)), |
| 53 | + (0.119, (0.961, 0.510, 0.000)), |
| 54 | + (0.238, (0.918, 0.620, 0.000)), |
| 55 | + (0.357, (0.867, 0.714, 0.000)), |
| 56 | + (0.476, (0.804, 0.800, 0.243)), |
| 57 | + (0.607, (0.686, 0.816, 0.310)), |
| 58 | + (0.738, (0.553, 0.824, 0.408)), |
| 59 | + (0.869, (0.404, 0.827, 0.506)), |
| 60 | + (1.000, (0.224, 0.824, 0.600)), |
| 61 | + ], |
| 62 | +) |
6 | 63 |
|
7 | 64 | c = 299792458.0 * ms |
8 | 65 | sqrt2pi = np.sqrt(2.0 * np.pi) |
9 | 66 |
|
| 67 | +def compute_planet_doppler_shift(sim, psi, absorption_spec): |
| 68 | + from.fast_starspot import doppler_shift |
| 69 | + """ |
| 70 | + Compute Doppler-shifted absorption spectra due to planet's orbital motion. |
| 71 | +
|
| 72 | + Parameters: |
| 73 | + - sim: simulation object containing planet and star parameters. |
| 74 | + - psi_planet: orbital phase array of the planet. |
| 75 | + - absorption_spec: array (or list) of absorption spectra to shift. |
| 76 | + - M_sun: solar mass constant (in appropriate mass units). |
| 77 | + - M_earth: Earth mass constant (in appropriate mass units). |
| 78 | +
|
| 79 | + Returns: |
| 80 | + - absorpt_prest: array of Doppler-shifted absorption spectra. |
| 81 | + """ |
| 82 | + psi_planet=(psi*sim.star.prot/sim.planet.P).value |
| 83 | + Kp = (sim.planet.K) * (M_sun * sim.star.mass) / (sim.planet.Mp * M_earth) |
| 84 | + pshift = (Kp * np.sin(2 * np.pi * psi_planet)).value |
| 85 | + absorpt_prest = np.array([ |
| 86 | + doppler_shift(sim.pixel.wave, absorption_spec[x], -1000 * pshift[x]) |
| 87 | + for x in range(len(pshift)) |
| 88 | + ]) |
| 89 | + return absorpt_prest |
| 90 | + |
| 91 | +def transit_durations(sim): |
| 92 | + """ |
| 93 | + Calculate transit duration and ingress/egress duration for a planet transiting a star. |
| 94 | + Supports both circular and eccentric orbits. |
| 95 | +
|
| 96 | + Parameters: |
| 97 | + P : float |
| 98 | + Orbital period (same time units as desired output) |
| 99 | + a : float |
| 100 | + Semi-major axis in units of stellar radii (a/R_star) |
| 101 | + Rp : float |
| 102 | + Planet radius in units of stellar radii (R_p/R_star) |
| 103 | + ip : float |
| 104 | + Orbital inclination in degrees (90° is edge-on) |
| 105 | + e : float, optional |
| 106 | + Orbital eccentricity (default 0 for circular orbits) |
| 107 | + w : float, optional |
| 108 | + Argument of periastron in degrees (default 90° means periastron at transit) |
| 109 | +
|
| 110 | + Returns: |
| 111 | + T14 : float |
| 112 | + Total transit duration (first to last contact), in same units as P |
| 113 | + tau : float |
| 114 | + Ingress or egress duration |
| 115 | + |
| 116 | + Notes: |
| 117 | + - The formulas follow the analytic approximations described by Seager & Mallén-Ornelas (2003) |
| 118 | + for circular orbits, extended for eccentricity following Kipping (2010): |
| 119 | + https://arxiv.org/abs/1004.3819 |
| 120 | + - The eccentricity correction is applied through the planet's distance at transit |
| 121 | + and the velocity factor. |
| 122 | +
|
| 123 | + References: |
| 124 | + Seager & Mallén-Ornelas (2003), The Astrophysical Journal 585, 1038 |
| 125 | + Kipping (2010), MNRAS, 407, 301, https://arxiv.org/abs/1004.3819 |
| 126 | + """ |
| 127 | + |
| 128 | + a=sim.planet.a.value |
| 129 | + P=1 |
| 130 | + ip=sim.planet.ip.value |
| 131 | + Rp=sim.planet.Rp.value |
| 132 | + e=sim.planet.e |
| 133 | + w=sim.planet.w.value |
| 134 | + |
| 135 | + # Convert degrees to radians for angle calculations |
| 136 | + i = np.radians(ip) |
| 137 | + w_rad = np.radians(w) |
| 138 | + |
| 139 | + # Computes the star-planet distance ratio at mid-transit (normalized by a) |
| 140 | + r_tp = (1 - e**2) / (1 + e * np.sin(w_rad)) |
| 141 | + |
| 142 | + # Impact parameter b (normalized by stellar radius) |
| 143 | + b = a * r_tp * np.cos(i) |
| 144 | + |
| 145 | + # Velocity correction factor accounting for orbital speed at transit |
| 146 | + velocity_factor = np.sqrt(1 - e**2) / (1 + e * np.sin(w_rad)) |
| 147 | + |
| 148 | + # Calculate total transit duration T14 using modified formula: |
| 149 | + # arcsin argument: chord length over the projected orbital distance factor including eccentricity |
| 150 | + arg_total = np.sqrt((1 + Rp)**2 - b**2) / (a * r_tp * np.sin(i)) |
| 151 | + # Clip argument to valid range [-1,1] to avoid NaNs |
| 152 | + arg_total = np.clip(arg_total, -1, 1) |
| 153 | + T14 = (P / np.pi) * velocity_factor * np.arcsin(arg_total) |
| 154 | + |
| 155 | + # Calculate full transit duration T23 (between second and third contacts) |
| 156 | + arg_full = np.sqrt((1 - Rp)**2 - b**2) / (a * r_tp * np.sin(i)) |
| 157 | + arg_full = np.clip(arg_full, -1, 1) |
| 158 | + T23 = (P / np.pi) * velocity_factor * np.arcsin(arg_full) |
| 159 | + |
| 160 | + # Ingress or egress duration is half the difference |
| 161 | + #tau = 0.5 * (T14 - T23) |
| 162 | + |
| 163 | + return T14, T23 |
10 | 164 |
|
11 | 165 | def read_rdb(fname): |
12 | 166 | d = np.loadtxt(fname, skiprows=2) |
|
0 commit comments