squishyplanet 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ from .oblate_system import OblateSystem
2
+
3
+ __all__ = ["OblateSystem"]
4
+ __url__ = "https://github.com/ben-cassese/squishyplanet"
5
+ __license__ = "MIT"
6
+ __description__ = "Modeling transits of non-spherical exoplanets"
7
+ __version__ = "0.1.0"
@@ -0,0 +1,177 @@
1
+ import jax
2
+
3
+ jax.config.update("jax_enable_x64", True)
4
+
5
+ import jax.numpy as jnp
6
+
7
+
8
+ # for illustration only- not actually doing this 2D integral anywhere
9
+ def profile(
10
+ x,
11
+ y,
12
+ p_xx,
13
+ p_xy,
14
+ p_xz,
15
+ p_x0,
16
+ p_yy,
17
+ p_yz,
18
+ p_y0,
19
+ p_zz,
20
+ p_z0,
21
+ p_00,
22
+ x_c,
23
+ y_c,
24
+ z_c,
25
+ **kwargs,
26
+ ):
27
+ return (
28
+ -(
29
+ x_c
30
+ * (
31
+ p_x0
32
+ + 2 * p_xx * x
33
+ + p_xy * y
34
+ - (
35
+ p_xz
36
+ * (
37
+ p_z0
38
+ + p_xz * x
39
+ + p_yz * y
40
+ - jnp.sqrt(
41
+ (p_z0 + p_xz * x + p_yz * y) ** 2
42
+ - 4
43
+ * p_zz
44
+ * (
45
+ -1
46
+ + p_00
47
+ + p_x0 * x
48
+ + p_xx * x**2
49
+ + p_y0 * y
50
+ + p_xy * x * y
51
+ + p_yy * y**2
52
+ )
53
+ )
54
+ )
55
+ )
56
+ / (2.0 * p_zz)
57
+ )
58
+ )
59
+ - (
60
+ p_y0
61
+ + p_xy * x
62
+ + 2 * p_yy * y
63
+ - (
64
+ p_yz
65
+ * (
66
+ p_z0
67
+ + p_xz * x
68
+ + p_yz * y
69
+ - jnp.sqrt(
70
+ (p_z0 + p_xz * x + p_yz * y) ** 2
71
+ - 4
72
+ * p_zz
73
+ * (
74
+ -1
75
+ + p_00
76
+ + p_x0 * x
77
+ + p_xx * x**2
78
+ + p_y0 * y
79
+ + p_xy * x * y
80
+ + p_yy * y**2
81
+ )
82
+ )
83
+ )
84
+ )
85
+ / (2.0 * p_zz)
86
+ )
87
+ * y_c
88
+ - jnp.sqrt(
89
+ (p_z0 + p_xz * x + p_yz * y) ** 2
90
+ - 4
91
+ * p_zz
92
+ * (
93
+ -1
94
+ + p_00
95
+ + p_x0 * x
96
+ + p_xx * x**2
97
+ + p_y0 * y
98
+ + p_xy * x * y
99
+ + p_yy * y**2
100
+ )
101
+ )
102
+ * z_c
103
+ ) / (
104
+ jnp.sqrt(
105
+ (p_z0 + p_xz * x + p_yz * y) ** 2
106
+ - 4
107
+ * p_zz
108
+ * (
109
+ -1
110
+ + p_00
111
+ + p_x0 * x
112
+ + p_xx * x**2
113
+ + p_y0 * y
114
+ + p_xy * x * y
115
+ + p_yy * y**2
116
+ )
117
+ + (
118
+ p_x0
119
+ + 2 * p_xx * x
120
+ + p_xy * y
121
+ - (
122
+ p_xz
123
+ * (
124
+ p_z0
125
+ + p_xz * x
126
+ + p_yz * y
127
+ - jnp.sqrt(
128
+ (p_z0 + p_xz * x + p_yz * y) ** 2
129
+ - 4
130
+ * p_zz
131
+ * (
132
+ -1
133
+ + p_00
134
+ + p_x0 * x
135
+ + p_xx * x**2
136
+ + p_y0 * y
137
+ + p_xy * x * y
138
+ + p_yy * y**2
139
+ )
140
+ )
141
+ )
142
+ )
143
+ / (2.0 * p_zz)
144
+ )
145
+ ** 2
146
+ + (
147
+ p_y0
148
+ + p_xy * x
149
+ + 2 * p_yy * y
150
+ - (
151
+ p_yz
152
+ * (
153
+ p_z0
154
+ + p_xz * x
155
+ + p_yz * y
156
+ - jnp.sqrt(
157
+ (p_z0 + p_xz * x + p_yz * y) ** 2
158
+ - 4
159
+ * p_zz
160
+ * (
161
+ -1
162
+ + p_00
163
+ + p_x0 * x
164
+ + p_xx * x**2
165
+ + p_y0 * y
166
+ + p_xy * x * y
167
+ + p_yy * y**2
168
+ )
169
+ )
170
+ )
171
+ )
172
+ / (2.0 * p_zz)
173
+ )
174
+ ** 2
175
+ )
176
+ * jnp.sqrt(x_c**2 + y_c**2 + z_c**2)
177
+ )
@@ -0,0 +1,265 @@
1
+ # this is a quadratic-only limb darkening model, uses a different method than the
2
+ # Agol, Luger, Foreman-Mackey 2020 scheme. Should produce the same results as the
3
+ # more generate polynomial_limb_darkened_transit.py model when all terms > u2 are 0
4
+
5
+ import jax
6
+
7
+ jax.config.update("jax_enable_x64", True)
8
+ import jax.numpy as jnp
9
+
10
+ from quadax import quadgk
11
+
12
+ from squishyplanet.engine.planet_3d import planet_3d_coeffs
13
+ from squishyplanet.engine.planet_2d import planet_2d_coeffs
14
+ from squishyplanet.engine.parametric_ellipse import (
15
+ poly_to_parametric,
16
+ cartesian_intersection_to_parametric_angle,
17
+ )
18
+ from squishyplanet.engine.skypos import skypos
19
+ from squishyplanet.engine.star_planet_intersection import single_intersection_points
20
+ from squishyplanet.engine.kepler import kepler
21
+
22
+
23
+ @jax.jit
24
+ def planet_flux_integrand(alpha, u1, u2, c_x1, c_x2, c_x3, c_y1, c_y2, c_y3, **kwargs):
25
+ return (
26
+ (
27
+ c_x3 * (-(jnp.sin(alpha) * c_y1) + jnp.cos(alpha) * c_y2)
28
+ - c_x2 * (c_y1 + jnp.cos(alpha) * c_y3)
29
+ + c_x1 * (c_y2 + jnp.sin(alpha) * c_y3)
30
+ )
31
+ * (
32
+ 3
33
+ * u2
34
+ * (
35
+ (jnp.cos(alpha) * c_x1 + jnp.sin(alpha) * c_x2 + c_x3) ** 2
36
+ + (jnp.cos(alpha) * c_y1 + jnp.sin(alpha) * c_y2 + c_y3) ** 2
37
+ )
38
+ ** 2
39
+ - 4
40
+ * (u1 + 2 * u2)
41
+ * (
42
+ -1
43
+ + jnp.sqrt(
44
+ 1
45
+ - (jnp.cos(alpha) * c_x1 + jnp.sin(alpha) * c_x2 + c_x3) ** 2
46
+ - (jnp.cos(alpha) * c_y1 + jnp.sin(alpha) * c_y2 + c_y3) ** 2
47
+ )
48
+ )
49
+ + 2
50
+ * (
51
+ (jnp.cos(alpha) * c_x1 + jnp.sin(alpha) * c_x2 + c_x3) ** 2
52
+ + (jnp.cos(alpha) * c_y1 + jnp.sin(alpha) * c_y2 + c_y3) ** 2
53
+ )
54
+ * (
55
+ 3
56
+ - 3 * u1
57
+ - 6 * u2
58
+ + 2
59
+ * u1
60
+ * jnp.sqrt(
61
+ 1
62
+ - (jnp.cos(alpha) * c_x1 + jnp.sin(alpha) * c_x2 + c_x3) ** 2
63
+ - (jnp.cos(alpha) * c_y1 + jnp.sin(alpha) * c_y2 + c_y3) ** 2
64
+ )
65
+ + 4
66
+ * u2
67
+ * jnp.sqrt(
68
+ 1
69
+ - (jnp.cos(alpha) * c_x1 + jnp.sin(alpha) * c_x2 + c_x3) ** 2
70
+ - (jnp.cos(alpha) * c_y1 + jnp.sin(alpha) * c_y2 + c_y3) ** 2
71
+ )
72
+ )
73
+ )
74
+ ) / (
75
+ 12.0
76
+ * (
77
+ (jnp.cos(alpha) * c_x1 + jnp.sin(alpha) * c_x2 + c_x3) ** 2
78
+ + (jnp.cos(alpha) * c_y1 + jnp.sin(alpha) * c_y2 + c_y3) ** 2
79
+ )
80
+ )
81
+
82
+
83
+ # @jax.jit
84
+ def star_flux_integral(alpha1, alpha2, u1, u2, c_x1, c_x2, c_x3, c_y1, c_y2, c_y3):
85
+ x1 = c_x1 * jnp.cos(alpha1) + c_x2 * jnp.sin(alpha1) + c_x3
86
+ y1 = c_y1 * jnp.cos(alpha1) + c_y2 * jnp.sin(alpha1) + c_y3
87
+ theta1 = jnp.arctan2(y1, x1)
88
+ theta1 = jnp.where(theta1 < 0, theta1 + 2 * jnp.pi, theta1)
89
+
90
+ x2 = c_x1 * jnp.cos(alpha2) + c_x2 * jnp.sin(alpha2) + c_x3
91
+ y2 = c_y1 * jnp.cos(alpha2) + c_y2 * jnp.sin(alpha2) + c_y3
92
+ theta2 = jnp.arctan2(y2, x2)
93
+ theta2 = jnp.where(theta2 < 0, theta2 + 2 * jnp.pi, theta2)
94
+
95
+ # print(theta1, theta2)
96
+ # jax.debug.print("theta1 {x}", x=theta1)
97
+ # jax.debug.print("theta2 {x}", x=theta2)
98
+
99
+ delta = jnp.abs(jnp.arctan2(jnp.sin(theta1 - theta2), jnp.cos(theta1 - theta2)))
100
+ # jax.debug.print("weird delta {x}", x=delta)
101
+
102
+ # jax.debug.print("star contribution {x}", x=delta / (2 * jnp.pi))
103
+ return delta / (2 * jnp.pi)
104
+
105
+
106
+ def limb_darkening_profile(x, y, u1, u2, **kwargs):
107
+ return (
108
+ 1
109
+ + u1 * (-1 + jnp.sqrt(1 - x**2 - y**2))
110
+ - u2 * (-1 + jnp.sqrt(1 - x**2 - y**2)) ** 2
111
+ )
112
+
113
+
114
+ @jax.jit
115
+ def lightcurve(state, times):
116
+ epsabs = epsrel = 1e-12
117
+
118
+ # kepler's eq for times -> f
119
+ time_deltas = times - state["t_peri"]
120
+ mean_anomalies = 2 * jnp.pi * time_deltas / state["period"]
121
+ f = kepler(mean_anomalies, state["e"])
122
+ state["f"] = f
123
+ # state["f"] = times
124
+
125
+ three = planet_3d_coeffs(**state)
126
+ two = planet_2d_coeffs(**three)
127
+ para = poly_to_parametric(**two)
128
+
129
+ fluxes = jnp.ones_like(times)
130
+
131
+ positions = skypos(**state)
132
+
133
+ possibly_in_transit = (
134
+ (jnp.abs(positions[0, :]) < 1.0 + state["r"])
135
+ * (jnp.abs(positions[1, :]) < 1.0 + state["r"])
136
+ * (positions[2, :] > 0)
137
+ )
138
+
139
+ # not thrilled with this implementation- you can feed all of para straight to
140
+ # quadgk without scanning or vmapping, which makes me think you should be able to
141
+ # feed it just all of the entries where it's transiting. but, I kept running into
142
+ # ConcretizationTypeError issues, so this is the workaround for now
143
+ def fully_transiting(X):
144
+ para, _, _ = X
145
+ func = jax.tree_util.Partial(
146
+ planet_flux_integrand, u1=state["u1"], u2=state["u2"], **para
147
+ )
148
+ blocked_flux = quadgk(func, [0, 2 * jnp.pi], epsabs=epsabs, epsrel=epsrel)[0]
149
+ blocked_flux = blocked_flux / (
150
+ -(1 / 6) * jnp.pi * (-6 + 2 * state["u1"] + state["u2"])
151
+ )
152
+ return blocked_flux
153
+
154
+ def partially_transiting(X):
155
+ # return jnp.nan
156
+ para, xs, ys = X
157
+ alphas = cartesian_intersection_to_parametric_angle(xs, ys, **para)
158
+ alphas = jnp.where(xs != 999, alphas, 2 * jnp.pi)
159
+ alphas = jnp.where(alphas < 0, alphas + 2 * jnp.pi, alphas)
160
+ alphas = jnp.where(alphas > 2 * jnp.pi, alphas - 2 * jnp.pi, alphas)
161
+ alphas = jnp.sort(alphas)
162
+ # jax.debug.print("xs {x}", x=xs)
163
+ # jax.debug.print("ys {x}", x=ys)
164
+ # jax.debug.print("alphas {x}", x=alphas)
165
+
166
+ test_ang = alphas[0] + (alphas[1] - alphas[0]) / 2
167
+ test_ang = jnp.where(test_ang > 2 * jnp.pi, test_ang - 2 * jnp.pi, test_ang)
168
+ # jax.debug.print("test_ang {x}", x=test_ang)
169
+
170
+ test_val = limb_darkening_profile(
171
+ x=para["c_x1"] * jnp.cos(test_ang)
172
+ + para["c_x2"] * jnp.sin(test_ang)
173
+ + para["c_x3"],
174
+ y=para["c_y1"] * jnp.cos(test_ang)
175
+ + para["c_y2"] * jnp.sin(test_ang)
176
+ + para["c_y3"],
177
+ u1=0.6,
178
+ u2=0.2,
179
+ **para,
180
+ )
181
+ # jax.debug.print("test_val {x}", x=test_val)
182
+
183
+ func = jax.tree_util.Partial(
184
+ planet_flux_integrand, u1=state["u1"], u2=state["u2"], **para
185
+ )
186
+ # full = quadgk(func, [0, 2 * jnp.pi], epsabs=epsabs, epsrel=epsrel)[
187
+ # 0
188
+ # ] / (-(1 / 6) * jnp.pi * (-6 + 2 * state["u1"] + state["u2"]))
189
+
190
+ def testval_is_not_nan(_):
191
+ planet_contribution = quadgk(
192
+ func, [alphas[0], alphas[1]], epsabs=epsabs, epsrel=epsrel
193
+ )[0] / (-(1 / 6) * jnp.pi * (-6 + 2 * state["u1"] + state["u2"]))
194
+ return planet_contribution
195
+
196
+ def testval_is_nan(_):
197
+ # planet_contribution =
198
+
199
+ leg1 = quadgk(func, [alphas[1], 2 * jnp.pi], epsabs=epsabs, epsrel=epsrel)[
200
+ 0
201
+ ]
202
+ leg2 = quadgk(func, [0.0, alphas[0]], epsabs=epsabs, epsrel=epsrel)[0]
203
+ planet_contribution = (leg1 + leg2) / (
204
+ -(1 / 6) * jnp.pi * (-6 + 2 * state["u1"] + state["u2"])
205
+ )
206
+ return planet_contribution
207
+
208
+ planet_contribution = jax.lax.cond(
209
+ jnp.isnan(test_val), testval_is_nan, testval_is_not_nan, ()
210
+ )
211
+ # jax.debug.print("planet_contribution {x}", x=planet_contribution)
212
+ # #jax.debug.print("full {x}", x=full)
213
+ # #jax.debug.print("side1 {x}", x=side1)
214
+ # #jax.debug.print("side2 {x}", x=side2)
215
+
216
+ # planet_contribution = jnp.where(~jnp.isnan(test_val), side2, side1)
217
+ star_contribution = star_flux_integral(
218
+ alpha1=alphas[0],
219
+ alpha2=alphas[1],
220
+ u1=state["u1"],
221
+ u2=state["u2"],
222
+ **para,
223
+ )
224
+
225
+ total_blocked = planet_contribution + star_contribution
226
+
227
+ return total_blocked
228
+
229
+ def transiting(X):
230
+ indv_para, indv_two = X
231
+ (
232
+ xs,
233
+ ys,
234
+ ) = single_intersection_points(**indv_two)
235
+ on_limb = jnp.sum(xs) != 999 * 4
236
+
237
+ return jax.lax.cond(
238
+ on_limb,
239
+ partially_transiting,
240
+ fully_transiting,
241
+ (indv_para, xs, ys),
242
+ )
243
+
244
+ def not_transiting(X):
245
+ return 0.0
246
+
247
+ def scan_func(carry, scan_over):
248
+ indv_para, indv_two, mask = scan_over
249
+ return None, jax.lax.cond(
250
+ mask, transiting, not_transiting, (indv_para, indv_two)
251
+ )
252
+
253
+ # _, transit_fluxes = jax.lax.scan(
254
+ # scan_func, None, (para, two, possibly_in_transit), None
255
+ # )
256
+ # fluxes -= transit_fluxes
257
+ two["rho_xx"] = jnp.ones_like(f) * two["rho_xx"]
258
+ two["rho_xy"] = jnp.ones_like(f) * two["rho_xy"]
259
+ two["rho_yy"] = jnp.ones_like(f) * two["rho_yy"]
260
+
261
+ transit_fluxes = jax.lax.scan(
262
+ scan_func, None, (para, two, possibly_in_transit), None
263
+ )[1]
264
+
265
+ return fluxes - transit_fluxes