tglc 0.6.5__py3-none-any.whl → 0.7.0__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.
tglc/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
- __version__ = "0.6.5"
1
+ __version__ = "0.7.0"
2
2
  __author__ = 'Te Han, Timothy Brandt'
3
- __credits__ = 'University of California, Santa Barbara'
3
+ __credits__ = 'University of California, Santa Barbara'
@@ -0,0 +1,107 @@
1
+ # Adapted from QLP: https://github.com/havijw/tess-time-correction/
2
+
3
+ from pathlib import Path
4
+ from astropy.coordinates import SkyCoord
5
+ from astropy.time import Time, TimeDelta, TimeFromEpoch
6
+ import astropy.units as u
7
+ import numpy as np
8
+ import pandas as pd
9
+
10
+ def get_ephemeris_file_path(sector: int) -> Path:
11
+ """Get the path to the appropriate TESS ephemeris data file for a given sector."""
12
+ ephemeris_data_directory = Path(__file__).resolve().parent / "ephemeris_data"
13
+ if 1 <= sector <= 5:
14
+ return ephemeris_data_directory / "20180720_tess_ephem.csv"
15
+ elif 6 <= sector <= 19:
16
+ return ephemeris_data_directory / "20190101_tess_ephem.csv"
17
+ elif 19 <= sector <= 32:
18
+ return ephemeris_data_directory / "20200101_tess_ephem.csv"
19
+ elif 33 <= sector <= 45:
20
+ return ephemeris_data_directory / "20210101_tess_ephem.csv"
21
+ elif 46 <= sector <= 59:
22
+ return ephemeris_data_directory / "20211215_tess_ephem.csv"
23
+ elif 60 <= sector <= 73:
24
+ return ephemeris_data_directory / "20221201_tess_ephem.csv"
25
+ elif 74 <= sector <= 87:
26
+ return ephemeris_data_directory / "20231201_tess_ephem.csv"
27
+ elif 88 <= sector <= 101:
28
+ return ephemeris_data_directory / "20241201_tess_ephem.csv"
29
+ else:
30
+ raise ValueError(
31
+ f"No spacecraft ephemeris file assigned for sector {sector}."
32
+ )
33
+
34
+ class TESSJD(TimeFromEpoch):
35
+ """
36
+ Define TJD as (JD - 2457000) and reported in units of days.
37
+
38
+ Importing this class registers the `"tjd"` format with `astropy.time`.
39
+ """
40
+
41
+ name = "tjd"
42
+ unit = 1
43
+ epoch_val = 2457000 * u.day
44
+ epoch_val2 = None
45
+ epoch_scale = "tdb"
46
+ epoch_format = "jd"
47
+
48
+
49
+ def apply_barycentric_correction(
50
+ sector: int, tjd: np.typing.ArrayLike, coord: SkyCoord
51
+ ) -> np.ndarray:
52
+ """
53
+ Apply barycentric time correction to timestamps in from a given sector.
54
+
55
+ Uses data from `ephmeris_data/` for TESS spacecraft position.
56
+ Uses vectorized operations so `tjd` and `coord` can be arrays.
57
+
58
+ Parameters
59
+ ----------
60
+ sector : int
61
+ Sector containing the time stamps that need to be corrected
62
+ tjd : ArrayLike
63
+ Timestamps (in days) as recorded on the TESS spacecraft
64
+ coord : SkyCoord
65
+ Sky coordinate(s) of the target star(s) for which correction is being applied
66
+
67
+ Returns
68
+ -------
69
+ btjd : Array
70
+ Barycentric JD, TDB timestamps.
71
+ If `coord` is a scalar, the array shape matches `tjd`.
72
+ Otherwise, an axis is added before axis 0 which corresponds to objects.
73
+ For instance, if `tjd` is a 1D array and `coord` is an array, the
74
+ returned array will have 1 row per coordinate.
75
+ """
76
+ input_times = Time(tjd, format="tjd", scale="tdb")
77
+
78
+ # Linearly interpolate spacecraft position at timestamps
79
+ ephemeris_data_file = get_ephemeris_file_path(sector)
80
+ tess_ephemeris = pd.read_csv(ephemeris_data_file, comment="#")
81
+ tess_spacecraft_x = np.interp(input_times.jd, tess_ephemeris["JDTDB"], tess_ephemeris["X"])
82
+ tess_spacecraft_y = np.interp(input_times.jd, tess_ephemeris["JDTDB"], tess_ephemeris["Y"])
83
+ tess_spacecraft_z = np.interp(input_times.jd, tess_ephemeris["JDTDB"], tess_ephemeris["Z"])
84
+ tess_spacecraft_position = np.array(
85
+ [tess_spacecraft_x, tess_spacecraft_y, tess_spacecraft_z]
86
+ ).T * u.au
87
+
88
+ # Calculate difference in light travel time to TESS vs solar system barycenter
89
+ star_vector = coord.cartesian.xyz
90
+ star_projection = np.dot(tess_spacecraft_position, star_vector).T
91
+ light_travel_time_delta = TimeDelta(
92
+ star_projection.to(u.lightsecond).value * u.second,
93
+ format="jd",
94
+ scale="tdb",
95
+ )
96
+ return (input_times + light_travel_time_delta).tjd
97
+
98
+ if __name__ == "__main__":
99
+ sector = 70
100
+ # Example MID_TJD timestamps from cadences 855745-855747 in camera 1
101
+ tjd = [3208.350463260291, 3208.352778074038, 3208.355092887784]
102
+ # Example values taken for TIC 2761238 and TIC 8939995
103
+ coord = SkyCoord(
104
+ [(356.485772436, -13.4999191877), (354.410412934, -8.16560533748)],
105
+ unit=u.deg,
106
+ )
107
+ print(apply_barycentric_correction(sector, tjd, coord))
tglc/effective_psf.py CHANGED
@@ -191,16 +191,28 @@ def fit_lc(A, source, star_info=None, x=0., y=0., star_num=0, factor=2, psf_size
191
191
  coord = np.arange(size ** 2).reshape(size, size)
192
192
  index = np.array(coord[down:up, left:right]).flatten()
193
193
  A_cut = np.zeros((len(index), np.shape(A)[1]))
194
+ A_target = np.zeros((len(index), np.shape(A)[1]))
194
195
  for i in range(len(index)):
195
196
  A_ = np.zeros(np.shape(A)[-1])
196
197
  star_pos = np.where(star_info_num[0] == index[i])[0]
197
198
  A_[star_info_num[1][star_pos]] = star_info_num[2][star_pos]
199
+ A_target[i] = A_
198
200
  A_cut[i] = A[index[i], :] - A_
199
201
  aperture = np.zeros((len(source.time), len(index)))
200
202
  for j in range(len(source.time)):
201
203
  aperture[j] = np.array(source.flux[j][down:up, left:right]).flatten() - np.dot(A_cut, e_psf[j])
202
204
  aperture = aperture.reshape((len(source.time), up - down, right - left))
203
- # np.save(f'_residual_{source.sector}.npy', aperture)
205
+ target_5x5 = (np.dot(A_target, np.nanmedian(e_psf, axis=0)).reshape(cut_size, cut_size))
206
+ field_stars_5x5 = (np.dot(A_cut, np.nanmedian(e_psf, axis=0)).reshape(cut_size, cut_size))
207
+ if target_5x5.shape != (cut_size, cut_size):
208
+ # Pad with nans to get to 5x5 shape
209
+ # Pad amount in a direction is (expected_num_pix) - (actual_num_pix)
210
+ pad_left = (cut_size // 2) - (x - left)
211
+ pad_right = (cut_size // 2 + 1) - (right - x)
212
+ pad_down = (cut_size // 2) - (y - down)
213
+ pad_up = (cut_size // 2 + 1) - (up - y)
214
+ target_5x5 = np.pad(target_5x5, [(pad_down, pad_up), (pad_left, pad_right)], constant_values=np.nan)
215
+ field_stars_5x5 = np.pad(field_stars_5x5, [(pad_down, pad_up), (pad_left, pad_right)], constant_values=np.nan)
204
216
 
205
217
  # psf_lc
206
218
  over_size = psf_size * factor + 1
@@ -209,7 +221,7 @@ def fit_lc(A, source, star_info=None, x=0., y=0., star_num=0, factor=2, psf_size
209
221
  psf_lc[:] = np.NaN
210
222
  e_psf_1d = np.nanmedian(e_psf[:, :over_size ** 2], axis=0).reshape(over_size, over_size)
211
223
  portion = (36 / 49) * np.nansum(e_psf_1d[8:15, 8:15]) / np.nansum(e_psf_1d) # only valid for factor = 2
212
- return aperture, psf_lc, y - down, x - left, portion
224
+ return aperture, psf_lc, y - down, x - left, portion, target_5x5, field_stars_5x5
213
225
  left_ = left - x + 5
214
226
  right_ = right - x + 5
215
227
  down_ = down - y + 5
@@ -262,7 +274,7 @@ def fit_lc(A, source, star_info=None, x=0., y=0., star_num=0, factor=2, psf_size
262
274
  portion = np.nansum(psf_shape[:, 4:7, 4:7]) / np.nansum(psf_shape)
263
275
  # print(np.nansum(psf_shape[:, 5, 5]) / np.nansum(psf_shape))
264
276
  # np.save(f'toi-5344_psf_{source.sector}.npy', psf_shape)
265
- return aperture, psf_lc, y - down, x - left, portion
277
+ return aperture, psf_lc, y - down, x - left, portion, target_5x5, field_stars_5x5
266
278
 
267
279
 
268
280
  def fit_lc_float_field(A, source, star_info=None, x=np.array([]), y=np.array([]), star_num=0, factor=2, psf_size=11,