tglc 0.6.5__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 +3 -0
- tglc/background_mask/__init__.py +0 -0
- tglc/background_mask/median_mask.fits +0 -0
- tglc/effective_psf.py +484 -0
- tglc/ffi.py +376 -0
- tglc/ffi_cut.py +279 -0
- tglc/lc_plot.py +2662 -0
- tglc/mast.py +116 -0
- tglc/quick_lc.py +526 -0
- tglc/run.py +95 -0
- tglc/source_output.py +85 -0
- tglc/target_lightcurve.py +362 -0
- tglc-0.6.5.dist-info/LICENSE +21 -0
- tglc-0.6.5.dist-info/METADATA +80 -0
- tglc-0.6.5.dist-info/RECORD +17 -0
- tglc-0.6.5.dist-info/WHEEL +5 -0
- tglc-0.6.5.dist-info/top_level.txt +1 -0
tglc/__init__.py
ADDED
|
File without changes
|
|
Binary file
|
tglc/effective_psf.py
ADDED
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from wotan import flatten
|
|
3
|
+
import tglc
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def bilinear(x, y, repeat=1):
|
|
7
|
+
'''
|
|
8
|
+
A bilinear formula
|
|
9
|
+
np.array([1 - x - y + x * y, x - x * y, y - x * y, x * y] * repeat)
|
|
10
|
+
b, d = array[1]
|
|
11
|
+
a, c = array[0]
|
|
12
|
+
:param x: x
|
|
13
|
+
:param y: y
|
|
14
|
+
:param repeat: side length of epsf
|
|
15
|
+
:return: bilinear interpolation
|
|
16
|
+
'''
|
|
17
|
+
return np.array([1 - x - y + x * y, x - x * y, y - x * y, x * y] * repeat)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def get_psf(source, factor=2, psf_size=11, edge_compression=1e-4, c=np.array([0, 0, 0])):
|
|
21
|
+
"""
|
|
22
|
+
Generate matrix for PSF fitting
|
|
23
|
+
:param source: tglc.ffi_cut.Source or tglc.ffi_cut.Source_cut, required
|
|
24
|
+
Source or Source_cut object
|
|
25
|
+
:param factor: int, optional
|
|
26
|
+
effective PSF oversampling factor
|
|
27
|
+
:param psf_size: int, optional
|
|
28
|
+
effective PSF side length
|
|
29
|
+
:param edge_compression: float, optional
|
|
30
|
+
parameter for edge compression
|
|
31
|
+
:param c: np.ndarray, optional
|
|
32
|
+
manual modification of Gaia positions in the format of [x, y, theta]
|
|
33
|
+
:return: A, star_info, over_size, x_round, y_round
|
|
34
|
+
A: 2d matrix for least_square
|
|
35
|
+
star_info: star parameters
|
|
36
|
+
over_size: size of oversampled grid of ePSF
|
|
37
|
+
x_round: star horizontal pixel coordinates rounded
|
|
38
|
+
y_round: star vertical pixel coordinates rounded
|
|
39
|
+
"""
|
|
40
|
+
# even only
|
|
41
|
+
if factor % 2 != 0:
|
|
42
|
+
raise ValueError('Factor must be even.')
|
|
43
|
+
psf_size = psf_size
|
|
44
|
+
half_size = int((psf_size - 1) / 2)
|
|
45
|
+
over_size = psf_size * factor + 1
|
|
46
|
+
size = source.size # TODO: must be even?
|
|
47
|
+
flux_ratio = np.array(source.gaia['tess_flux_ratio'])
|
|
48
|
+
# flux_ratio = 0.9998 * flux_ratio + 0.0002
|
|
49
|
+
# x_shift = np.array(source.gaia[f'sector_{source.sector}_x'])
|
|
50
|
+
# y_shift = np.array(source.gaia[f'sector_{source.sector}_y'])
|
|
51
|
+
|
|
52
|
+
x_shift = np.array(source.gaia[f'sector_{source.sector}_x'])
|
|
53
|
+
y_shift = np.array(source.gaia[f'sector_{source.sector}_y'])
|
|
54
|
+
|
|
55
|
+
# x_shift = (x_ - c[0]) * np.cos(c[2]) - (y_ - c[1]) * np.sin(c[2])
|
|
56
|
+
# y_shift = (x_ - c[0]) * np.sin(c[2]) + (y_ - c[1]) * np.cos(c[2])
|
|
57
|
+
|
|
58
|
+
x_round = np.round(x_shift).astype(int)
|
|
59
|
+
y_round = np.round(y_shift).astype(int)
|
|
60
|
+
|
|
61
|
+
left = np.maximum(0, x_round - half_size)
|
|
62
|
+
right = np.minimum(size, x_round + half_size) + 1
|
|
63
|
+
down = np.maximum(0, y_round - half_size)
|
|
64
|
+
up = np.minimum(size, y_round + half_size) + 1
|
|
65
|
+
x_residual = x_shift % (1 / factor) * factor
|
|
66
|
+
y_residual = y_shift % (1 / factor) * factor
|
|
67
|
+
|
|
68
|
+
x_p = np.arange(size)
|
|
69
|
+
y_p = np.arange(size)
|
|
70
|
+
coord = np.arange(size ** 2).reshape(size, size)
|
|
71
|
+
xx, yy = np.meshgrid((np.arange(size) - (size - 1) / 2), (np.arange(size) - (size - 1) / 2))
|
|
72
|
+
|
|
73
|
+
if type(source) == tglc.ffi.Source:
|
|
74
|
+
bg_dof = 6
|
|
75
|
+
A = np.zeros((size ** 2, over_size ** 2 + bg_dof))
|
|
76
|
+
A[:, -1] = np.ones(size ** 2)
|
|
77
|
+
A[:, -2] = yy.flatten()
|
|
78
|
+
A[:, -3] = xx.flatten()
|
|
79
|
+
A[:, -4] = source.mask.data.flatten()
|
|
80
|
+
A[:, -5] = (source.mask.data * xx).flatten()
|
|
81
|
+
A[:, -6] = (source.mask.data * yy).flatten()
|
|
82
|
+
else:
|
|
83
|
+
bg_dof = 3
|
|
84
|
+
A = np.zeros((size ** 2, over_size ** 2 + bg_dof))
|
|
85
|
+
A[:, -1] = np.ones(size ** 2)
|
|
86
|
+
A[:, -2] = yy.flatten()
|
|
87
|
+
A[:, -3] = xx.flatten()
|
|
88
|
+
star_info = []
|
|
89
|
+
for i in range(len(source.gaia)):
|
|
90
|
+
# if i == 8:
|
|
91
|
+
# continue
|
|
92
|
+
x_psf = factor * (x_p[left[i]:right[i]] - x_round[i] + half_size) + (x_shift[i] % 1) // (1 / factor)
|
|
93
|
+
y_psf = factor * (y_p[down[i]:up[i]] - y_round[i] + half_size) + (y_shift[i] % 1) // (1 / factor)
|
|
94
|
+
x_psf, y_psf = np.meshgrid(x_psf, y_psf) # super slow here
|
|
95
|
+
a = np.array(x_psf + y_psf * over_size, dtype=np.int64).flatten()
|
|
96
|
+
index = coord[down[i]:up[i], left[i]:right[i]]
|
|
97
|
+
A[np.repeat(index, 4), np.array([a, a + 1, a + over_size, a + over_size + 1]).flatten(order='F')] += \
|
|
98
|
+
flux_ratio[i] * bilinear(x_residual[i], y_residual[i], repeat=len(a))
|
|
99
|
+
# star_info.append(
|
|
100
|
+
# (np.repeat(index, 4), np.array([a, a + 1, a + over_size, a + over_size + 1]).flatten(order='F'),
|
|
101
|
+
# flux_ratio[i] * bilinear(x_residual[i], y_residual[i], repeat=len(a))))
|
|
102
|
+
star_info.append(
|
|
103
|
+
(index, a, flux_ratio[i] * bilinear(x_residual[i], y_residual[i])))
|
|
104
|
+
coord_ = np.arange(- psf_size * factor / 2 + 1, psf_size * factor / 2 + 2)
|
|
105
|
+
x_coord, y_coord = np.meshgrid(coord_, coord_)
|
|
106
|
+
variance = psf_size
|
|
107
|
+
dist = (1 - np.exp(- 0.5 * (x_coord ** 4 + y_coord ** 4) / variance ** 4)) * edge_compression # 1e-3
|
|
108
|
+
A_mod = np.diag(dist.flatten())
|
|
109
|
+
A_mod = np.concatenate((A_mod, (np.zeros((over_size ** 2, bg_dof)))), axis=-1)
|
|
110
|
+
A = np.append(A, A_mod, axis=0)
|
|
111
|
+
return A, star_info, over_size, x_round, y_round
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def fit_psf(A, source, over_size, power=0.8, time=0):
|
|
115
|
+
"""
|
|
116
|
+
fit_psf using least_square (improved performance by changing to np.linalg.solve)
|
|
117
|
+
:param A: np.ndarray, required
|
|
118
|
+
2d matrix for least_square
|
|
119
|
+
:param source: tglc.ffi_cut.Source or tglc.ffi_cut.Source_cut, required
|
|
120
|
+
Source or Source_cut object
|
|
121
|
+
:param over_size: int, required
|
|
122
|
+
size of oversampled grid of ePSF
|
|
123
|
+
:param power: float, optional
|
|
124
|
+
power for weighting bright stars' contribution to the fit. 1 means same contribution from all stars,
|
|
125
|
+
<1 means emphasizing dimmer stars
|
|
126
|
+
:param time: int, required
|
|
127
|
+
time index of this ePSF fit
|
|
128
|
+
:return: fit result
|
|
129
|
+
"""
|
|
130
|
+
saturated_index = source.mask.mask.flatten()
|
|
131
|
+
flux = source.flux[time].flatten()
|
|
132
|
+
saturated_index[flux < 0.8 * np.nanmedian(flux)] = True
|
|
133
|
+
|
|
134
|
+
b = np.delete(flux, saturated_index)
|
|
135
|
+
scaler = np.abs(np.delete(flux, saturated_index)) ** power
|
|
136
|
+
b = np.append(b, np.zeros(over_size ** 2))
|
|
137
|
+
scaler = np.append(scaler, np.ones(over_size ** 2))
|
|
138
|
+
|
|
139
|
+
# fit = np.linalg.lstsq(A / scaler[:, np.newaxis], b / scaler, rcond=None)[0]
|
|
140
|
+
a = np.delete(A, np.where(saturated_index), 0) / scaler[:, np.newaxis]
|
|
141
|
+
b = b / scaler
|
|
142
|
+
alpha = np.dot(a.T, a)
|
|
143
|
+
beta = np.dot(a.T, b)
|
|
144
|
+
try:
|
|
145
|
+
fit = np.linalg.solve(alpha, beta)
|
|
146
|
+
except np.linalg.LinAlgError:
|
|
147
|
+
fit = np.full(np.shape(a)[1], np.nan)
|
|
148
|
+
# fit = np.linalg.lstsq(a, b, rcond=None)[0]
|
|
149
|
+
return fit
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def fit_lc(A, source, star_info=None, x=0., y=0., star_num=0, factor=2, psf_size=11, e_psf=None, near_edge=False):
|
|
153
|
+
"""
|
|
154
|
+
Produce matrix for least_square fitting without a certain target
|
|
155
|
+
:param A: np.ndarray, required
|
|
156
|
+
2d matrix for least_square
|
|
157
|
+
:param source: tglc.ffi_cut.Source or tglc.ffi_cut.Source_cut, required
|
|
158
|
+
Source or Source_cut object
|
|
159
|
+
:param star_info: np.ndarray, required
|
|
160
|
+
star parameters
|
|
161
|
+
:param x: float, required
|
|
162
|
+
target horizontal pixel coordinate
|
|
163
|
+
:param y: float, required
|
|
164
|
+
target vertical pixel coordinate
|
|
165
|
+
:param star_num: int, required
|
|
166
|
+
target star index
|
|
167
|
+
:param factor: int, optional
|
|
168
|
+
effective PSF oversampling factor
|
|
169
|
+
:param psf_size: int, optional
|
|
170
|
+
effective PSF side length
|
|
171
|
+
:param e_psf: np.ndarray, required
|
|
172
|
+
effective PSF as a 3d array as a timeseries
|
|
173
|
+
:param near_edge: boolean, required
|
|
174
|
+
whether the star is 2 pixels or closer to the edge of a CCD
|
|
175
|
+
:return: aperture lightcurve, PSF lightcurve, vertical pixel coord, horizontal pixel coord, portion of light in aperture
|
|
176
|
+
"""
|
|
177
|
+
over_size = psf_size * factor + 1
|
|
178
|
+
a = star_info[star_num][1]
|
|
179
|
+
star_info_num = (np.repeat(star_info[star_num][0], 4),
|
|
180
|
+
np.array([a, a + 1, a + over_size, a + over_size + 1]).flatten(order='F'),
|
|
181
|
+
np.tile(star_info[star_num][2], len(a)))
|
|
182
|
+
size = source.size # TODO: must be even?
|
|
183
|
+
# star_position = int(x + source.size * y - 5 * size - 5)
|
|
184
|
+
# aper_lc
|
|
185
|
+
cut_size = 5
|
|
186
|
+
in_frame = np.where(np.invert(np.isnan(source.flux[0])))
|
|
187
|
+
left = np.maximum(np.min(in_frame[1]), x - cut_size // 2)
|
|
188
|
+
right = np.minimum(np.max(in_frame[1]), x + cut_size // 2 + 1)
|
|
189
|
+
down = np.maximum(np.min(in_frame[0]), y - cut_size // 2)
|
|
190
|
+
up = np.minimum(np.max(in_frame[0]), y + cut_size // 2 + 1)
|
|
191
|
+
coord = np.arange(size ** 2).reshape(size, size)
|
|
192
|
+
index = np.array(coord[down:up, left:right]).flatten()
|
|
193
|
+
A_cut = np.zeros((len(index), np.shape(A)[1]))
|
|
194
|
+
for i in range(len(index)):
|
|
195
|
+
A_ = np.zeros(np.shape(A)[-1])
|
|
196
|
+
star_pos = np.where(star_info_num[0] == index[i])[0]
|
|
197
|
+
A_[star_info_num[1][star_pos]] = star_info_num[2][star_pos]
|
|
198
|
+
A_cut[i] = A[index[i], :] - A_
|
|
199
|
+
aperture = np.zeros((len(source.time), len(index)))
|
|
200
|
+
for j in range(len(source.time)):
|
|
201
|
+
aperture[j] = np.array(source.flux[j][down:up, left:right]).flatten() - np.dot(A_cut, e_psf[j])
|
|
202
|
+
aperture = aperture.reshape((len(source.time), up - down, right - left))
|
|
203
|
+
# np.save(f'_residual_{source.sector}.npy', aperture)
|
|
204
|
+
|
|
205
|
+
# psf_lc
|
|
206
|
+
over_size = psf_size * factor + 1
|
|
207
|
+
if near_edge: # TODO: near_edge
|
|
208
|
+
psf_lc = np.zeros(len(source.time))
|
|
209
|
+
psf_lc[:] = np.NaN
|
|
210
|
+
e_psf_1d = np.nanmedian(e_psf[:, :over_size ** 2], axis=0).reshape(over_size, over_size)
|
|
211
|
+
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
|
|
213
|
+
left_ = left - x + 5
|
|
214
|
+
right_ = right - x + 5
|
|
215
|
+
down_ = down - y + 5
|
|
216
|
+
up_ = up - y + 5
|
|
217
|
+
|
|
218
|
+
left_11 = np.maximum(- x + 5, 0)
|
|
219
|
+
right_11 = np.minimum(size - x + 5, 11)
|
|
220
|
+
down_11 = np.maximum(- y + 5, 0)
|
|
221
|
+
up_11 = np.minimum(size - y + 5, 11)
|
|
222
|
+
coord = np.arange(psf_size ** 2).reshape(psf_size, psf_size)
|
|
223
|
+
index = coord[down_11:up_11, left_11:right_11]
|
|
224
|
+
if type(source) == tglc.ffi.Source:
|
|
225
|
+
bg_dof = 6
|
|
226
|
+
else:
|
|
227
|
+
bg_dof = 3
|
|
228
|
+
A = np.zeros((psf_size ** 2, over_size ** 2 + bg_dof))
|
|
229
|
+
A[np.repeat(index, 4), star_info_num[1]] = star_info_num[2]
|
|
230
|
+
psf_shape = np.dot(e_psf, A.T).reshape(len(source.time), psf_size, psf_size)
|
|
231
|
+
psf_sim = psf_shape[:, down_:up_, left_: right_]
|
|
232
|
+
# psf_sim = np.transpose(psf_shape[:, down_:up_, left_: right_], (0, 2, 1))
|
|
233
|
+
|
|
234
|
+
psf_lc = np.zeros(len(source.time))
|
|
235
|
+
A_ = np.zeros((cut_size ** 2, 4))
|
|
236
|
+
xx, yy = np.meshgrid((np.arange(cut_size) - (cut_size - 1) / 2),
|
|
237
|
+
(np.arange(cut_size) - (cut_size - 1) / 2))
|
|
238
|
+
A_[:, -1] = np.ones(cut_size ** 2)
|
|
239
|
+
A_[:, -2] = yy.flatten()
|
|
240
|
+
A_[:, -3] = xx.flatten()
|
|
241
|
+
edge_pixel = np.array([0, 1, 2, 3, 4, 5, 9, 10, 14, 15, 19, 20, 21, 22, 23, 24])
|
|
242
|
+
# edge_pixel = np.array([0, 1, 2, 3, 4, 5, 6,
|
|
243
|
+
# 7, 8, 9, 10, 11, 12, 13,
|
|
244
|
+
# 14, 15, 19, 20,
|
|
245
|
+
# 21, 22, 26, 27,
|
|
246
|
+
# 28, 29, 33, 34,
|
|
247
|
+
# 35, 36, 37, 38, 39, 40, 41,
|
|
248
|
+
# 42, 43, 44, 45, 46, 47, 48])
|
|
249
|
+
med_aperture = np.median(aperture, axis=0).flatten()
|
|
250
|
+
outliers = np.abs(med_aperture[edge_pixel] - np.nanmedian(med_aperture[edge_pixel])) > 1 * np.std(
|
|
251
|
+
med_aperture[edge_pixel])
|
|
252
|
+
epsf_sum = np.sum(np.nanmedian(psf_shape, axis=0))
|
|
253
|
+
for j in range(len(source.time)):
|
|
254
|
+
if np.isnan(psf_sim[j, :, :]).any():
|
|
255
|
+
psf_lc[j] = np.nan
|
|
256
|
+
else:
|
|
257
|
+
aper_flat = aperture[j, :, :].flatten()
|
|
258
|
+
A_[:, 0] = psf_sim[j, :, :].flatten() / epsf_sum
|
|
259
|
+
a = np.delete(A_, edge_pixel[outliers], 0)
|
|
260
|
+
aper_flat = np.delete(aper_flat, edge_pixel[outliers])
|
|
261
|
+
psf_lc[j] = np.linalg.lstsq(a, aper_flat)[0][0]
|
|
262
|
+
portion = np.nansum(psf_shape[:, 4:7, 4:7]) / np.nansum(psf_shape)
|
|
263
|
+
# print(np.nansum(psf_shape[:, 5, 5]) / np.nansum(psf_shape))
|
|
264
|
+
# np.save(f'toi-5344_psf_{source.sector}.npy', psf_shape)
|
|
265
|
+
return aperture, psf_lc, y - down, x - left, portion
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def fit_lc_float_field(A, source, star_info=None, x=np.array([]), y=np.array([]), star_num=0, factor=2, psf_size=11,
|
|
269
|
+
e_psf=None, near_edge=False, prior=0.001):
|
|
270
|
+
"""
|
|
271
|
+
Produce matrix for least_square fitting without a certain target
|
|
272
|
+
:param A: np.ndarray, required
|
|
273
|
+
2d matrix for least_square
|
|
274
|
+
:param source: tglc.ffi_cut.Source or tglc.ffi_cut.Source_cut, required
|
|
275
|
+
Source or Source_cut object
|
|
276
|
+
:param star_info: np.ndarray, required
|
|
277
|
+
star parameters
|
|
278
|
+
:param x: float, required
|
|
279
|
+
target horizontal pixel coordinate
|
|
280
|
+
:param y: float, required
|
|
281
|
+
target vertical pixel coordinate
|
|
282
|
+
:param star_num: int, required
|
|
283
|
+
target star index
|
|
284
|
+
:param factor: int, optional
|
|
285
|
+
effective PSF oversampling factor
|
|
286
|
+
:param psf_size: int, optional
|
|
287
|
+
effective PSF side length
|
|
288
|
+
:param e_psf: np.ndarray, required
|
|
289
|
+
effective PSF as a 3d array as a timeseries
|
|
290
|
+
:param near_edge: boolean, required
|
|
291
|
+
whether the star is 2 pixels or closer to the edge of a CCD
|
|
292
|
+
:return: aperture lightcurve, PSF lightcurve, vertical pixel coord, horizontal pixel coord, portion of light in aperture
|
|
293
|
+
"""
|
|
294
|
+
over_size = psf_size * factor + 1
|
|
295
|
+
a = star_info[star_num][1]
|
|
296
|
+
star_info_num = (np.repeat(star_info[star_num][0], 4),
|
|
297
|
+
np.array([a, a + 1, a + over_size, a + over_size + 1]).flatten(order='F'),
|
|
298
|
+
np.tile(star_info[star_num][2], len(a)))
|
|
299
|
+
size = source.size # TODO: must be even?
|
|
300
|
+
# star_position = int(x + source.size * y - 5 * size - 5)
|
|
301
|
+
# aper_lc
|
|
302
|
+
cut_size = 5
|
|
303
|
+
in_frame = np.where(np.invert(np.isnan(source.flux[0])))
|
|
304
|
+
left = np.maximum(np.min(in_frame[1]), x[star_num] - cut_size // 2)
|
|
305
|
+
right = np.minimum(np.max(in_frame[1]), x[star_num] + cut_size // 2 + 1)
|
|
306
|
+
down = np.maximum(np.min(in_frame[0]), y[star_num] - cut_size // 2)
|
|
307
|
+
up = np.minimum(np.max(in_frame[0]), y[star_num] + cut_size // 2 + 1)
|
|
308
|
+
coord = np.arange(size ** 2).reshape(size, size)
|
|
309
|
+
index = np.array(coord[down:up, left:right]).flatten()
|
|
310
|
+
A_cut = np.zeros((len(index), np.shape(A)[1]))
|
|
311
|
+
for i in range(len(index)):
|
|
312
|
+
A_ = np.zeros(np.shape(A)[-1])
|
|
313
|
+
star_pos = np.where(star_info_num[0] == index[i])[0]
|
|
314
|
+
A_[star_info_num[1][star_pos]] = star_info_num[2][star_pos]
|
|
315
|
+
A_cut[i] = A[index[i], :] - A_
|
|
316
|
+
aperture = np.zeros((len(source.time), len(index)))
|
|
317
|
+
for j in range(len(source.time)):
|
|
318
|
+
aperture[j] = np.array(source.flux[j][down:up, left:right]).flatten() - np.dot(A_cut, e_psf[j])
|
|
319
|
+
aperture = aperture.reshape((len(source.time), up - down, right - left))
|
|
320
|
+
|
|
321
|
+
# psf_lc
|
|
322
|
+
over_size = psf_size * factor + 1
|
|
323
|
+
if near_edge: # TODO: near_edge
|
|
324
|
+
psf_lc = np.zeros(len(source.time))
|
|
325
|
+
psf_lc[:] = np.NaN
|
|
326
|
+
e_psf_1d = np.nanmedian(e_psf[:, :over_size ** 2], axis=0).reshape(over_size, over_size)
|
|
327
|
+
portion = (36 / 49) * np.nansum(e_psf_1d[8:15, 8:15]) / np.nansum(e_psf_1d) # only valid for factor = 2
|
|
328
|
+
return aperture, psf_lc, y[star_num] - down, x[star_num] - left, portion
|
|
329
|
+
# left_ = left - x[star_num] + 5
|
|
330
|
+
# right_ = right - x[star_num] + 5
|
|
331
|
+
# down_ = down - y[star_num] + 5
|
|
332
|
+
# up_ = up - y[star_num] + 5
|
|
333
|
+
if type(source) == tglc.ffi.Source:
|
|
334
|
+
bg_dof = 6
|
|
335
|
+
else:
|
|
336
|
+
bg_dof = 3
|
|
337
|
+
field_star_num = []
|
|
338
|
+
for j in range(len(source.gaia)):
|
|
339
|
+
if np.abs(x[j] - x[star_num]) < 5 and np.abs(y[j] - y[star_num]) < 5:
|
|
340
|
+
field_star_num.append(j)
|
|
341
|
+
|
|
342
|
+
psf_lc = np.zeros(len(source.time))
|
|
343
|
+
A_ = np.zeros((cut_size ** 2 + len(field_star_num), len(field_star_num) + 3))
|
|
344
|
+
xx, yy = np.meshgrid((np.arange(cut_size) - (cut_size - 1) / 2),
|
|
345
|
+
(np.arange(cut_size) - (cut_size - 1) / 2))
|
|
346
|
+
A_[:(cut_size ** 2), -1] = np.ones(cut_size ** 2)
|
|
347
|
+
A_[:(cut_size ** 2), -2] = yy.flatten()
|
|
348
|
+
A_[:(cut_size ** 2), -3] = xx.flatten()
|
|
349
|
+
psf_sim = np.zeros((len(source.time), 11 ** 2 + len(field_star_num), len(field_star_num)))
|
|
350
|
+
for j, star in enumerate(field_star_num):
|
|
351
|
+
a = star_info[star][1]
|
|
352
|
+
star_info_star = (np.repeat(star_info[star][0], 4),
|
|
353
|
+
np.array([a, a + 1, a + over_size, a + over_size + 1]).flatten(order='F'),
|
|
354
|
+
np.tile(star_info[star][2], len(a)))
|
|
355
|
+
delta_x = x[star_num] - x[star]
|
|
356
|
+
delta_y = y[star_num] - y[star]
|
|
357
|
+
# for psf_sim
|
|
358
|
+
left_shift = np.maximum(delta_x, 0)
|
|
359
|
+
right_shift = np.minimum(11 + delta_x, 11)
|
|
360
|
+
down_shift = np.maximum(delta_y, 0)
|
|
361
|
+
up_shift = np.minimum(11 + delta_y, 11)
|
|
362
|
+
# for psf_shape
|
|
363
|
+
left_shift_ = np.maximum(-delta_x, 0)
|
|
364
|
+
right_shift_ = np.minimum(11 - delta_x, 11)
|
|
365
|
+
down_shift_ = np.maximum(-delta_y, 0)
|
|
366
|
+
up_shift_ = np.minimum(11 - delta_y, 11)
|
|
367
|
+
|
|
368
|
+
left_11 = np.maximum(- x[star] + 5, 0)
|
|
369
|
+
right_11 = np.minimum(size - x[star] + 5, 11)
|
|
370
|
+
down_11 = np.maximum(- y[star] + 5, 0)
|
|
371
|
+
up_11 = np.minimum(size - y[star] + 5, 11)
|
|
372
|
+
|
|
373
|
+
coord = np.arange(psf_size ** 2).reshape(psf_size, psf_size)
|
|
374
|
+
index = coord[down_11:up_11, left_11:right_11]
|
|
375
|
+
A = np.zeros((psf_size ** 2, over_size ** 2 + bg_dof))
|
|
376
|
+
A[np.repeat(index, 4), star_info_star[1]] = star_info_star[2]
|
|
377
|
+
psf_shape = np.dot(e_psf, A.T).reshape(len(source.time), psf_size, psf_size)
|
|
378
|
+
epsf_sum = np.sum(np.nanmedian(psf_shape, axis=0))
|
|
379
|
+
psf_sim_index = coord[down_shift:up_shift, left_shift:right_shift].flatten()
|
|
380
|
+
psf_sim[:, psf_sim_index, j] = psf_shape[:, down_shift_:up_shift_, left_shift_:right_shift_].reshape(
|
|
381
|
+
len(source.time), -1) / epsf_sum
|
|
382
|
+
if star != star_num:
|
|
383
|
+
psf_sim[:, 11 ** 2 + j, j] = np.ones(len(source.time)) / (
|
|
384
|
+
prior * 1.5e4 * 10 ** ((10 - source.gaia[star]['tess_mag']) / 2.5))
|
|
385
|
+
else:
|
|
386
|
+
portion = np.nansum(psf_shape[:, 4:7, 4:7]) / np.nansum(psf_shape)
|
|
387
|
+
|
|
388
|
+
star_index = np.where(np.array(field_star_num) == star_num)[0]
|
|
389
|
+
field_star = psf_sim[0, np.arange(11 ** 2).reshape(11, 11)[3:8, 3:8], :].reshape(cut_size ** 2,
|
|
390
|
+
len(field_star_num)) * \
|
|
391
|
+
source.gaia['tess_flux_ratio'][field_star_num]
|
|
392
|
+
field_star[:, star_index] = 0
|
|
393
|
+
for j in range(len(source.time)):
|
|
394
|
+
if np.isnan(psf_sim[j, :, :]).any():
|
|
395
|
+
psf_lc[j] = np.nan
|
|
396
|
+
else:
|
|
397
|
+
aper_flat = aperture[j, :, :].flatten()
|
|
398
|
+
aper_flat = np.append(aper_flat, np.zeros(len(field_star_num) - 1)) # / prior
|
|
399
|
+
aper_flat[cut_size ** 2 + star_index] = 0
|
|
400
|
+
postcards = psf_sim[j, np.arange(11 ** 2).reshape(11, 11)[3:8, 3:8], :].reshape(cut_size ** 2,
|
|
401
|
+
len(field_star_num))
|
|
402
|
+
A_[:cut_size ** 2, :len(field_star_num)] = postcards
|
|
403
|
+
field_star = postcards * source.gaia['tess_flux_ratio'][field_star_num]
|
|
404
|
+
field_star[:, star_index] = 0
|
|
405
|
+
# A_[:(cut_size ** 2), -4] = np.sum(field_star, axis=1)
|
|
406
|
+
A_[cut_size ** 2:, :len(field_star_num)] = psf_sim[j, 11 ** 2:, :].reshape(len(field_star_num),
|
|
407
|
+
len(field_star_num))
|
|
408
|
+
a = np.delete(A_, cut_size ** 2 + star_index, 0)
|
|
409
|
+
psf_lc[j] = np.linalg.lstsq(a, aper_flat)[0][star_index]
|
|
410
|
+
return aperture, psf_lc, y[star_num] - down, x[star_num] - left, portion
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
def bg_mod(source, q=None, aper_lc=None, psf_lc=None, portion=None, star_num=0, near_edge=False):
|
|
414
|
+
'''
|
|
415
|
+
background modification
|
|
416
|
+
:param source: tglc.ffi_cut.Source or tglc.ffi_cut.Source_cut, required
|
|
417
|
+
Source or Source_cut object
|
|
418
|
+
:param q: list, optional
|
|
419
|
+
list of booleans that filter the data points
|
|
420
|
+
:param aper_lc: np.ndarray, required
|
|
421
|
+
aperture light curve
|
|
422
|
+
:param psf_lc: np.ndarray, required
|
|
423
|
+
PSF light curve
|
|
424
|
+
:param portion: float, required
|
|
425
|
+
portion of light in aperture
|
|
426
|
+
:param star_num: int, required,
|
|
427
|
+
star index
|
|
428
|
+
:param near_edge: boolean, required
|
|
429
|
+
whether the star is 2 pixels or closer to the edge of a CCD
|
|
430
|
+
:return: local background, modified aperture light curve, modified PSF light curve
|
|
431
|
+
'''
|
|
432
|
+
bar = 15000 * 10 ** ((source.gaia['tess_mag'][star_num] - 10) / -2.5)
|
|
433
|
+
# print(bar)
|
|
434
|
+
# med_epsf = np.nanmedian(e_psf[:, :23 ** 2].reshape(len(source.time), 23, 23), axis=0)
|
|
435
|
+
# centroid_to_aper_ratio = 4/9 * np.sum(med_epsf[10:13, 10:13]) / np.sum(med_epsf)
|
|
436
|
+
# centroid_to_aper_ratio = np.nanmedian(ratio)
|
|
437
|
+
# flux_bar = aperture_bar * centroid_to_aper_ratio
|
|
438
|
+
# lightcurve = lightcurve + (flux_bar - np.nanmedian(lightcurve[q]))
|
|
439
|
+
aperture_bar = bar * portion
|
|
440
|
+
# print(bar)
|
|
441
|
+
local_bg = np.nanmedian(aper_lc[q]) - aperture_bar
|
|
442
|
+
if np.isnan(local_bg):
|
|
443
|
+
local_bg = 0
|
|
444
|
+
aper_lc = aper_lc - local_bg
|
|
445
|
+
psf_bar = bar
|
|
446
|
+
local_bg = np.nanmedian(psf_lc[q]) - psf_bar
|
|
447
|
+
if np.isnan(local_bg):
|
|
448
|
+
local_bg = 0
|
|
449
|
+
psf_lc = psf_lc - local_bg
|
|
450
|
+
negative_arg_aper = np.where(aper_lc <= 0) # Negative frames
|
|
451
|
+
aper_lc[negative_arg_aper] = np.nan
|
|
452
|
+
negative_arg_psf = np.where(psf_lc <= 0)
|
|
453
|
+
psf_lc[negative_arg_psf] = np.nan
|
|
454
|
+
# removes very large outliers to prevent wotan to freeze
|
|
455
|
+
cal_aper_lc = aper_lc / np.nanmedian(aper_lc)
|
|
456
|
+
cal_aper_lc[np.where(cal_aper_lc > 100)] = np.nan
|
|
457
|
+
if np.isnan(cal_aper_lc).all():
|
|
458
|
+
print('Calibrated aperture flux are not accessible or processed incorrectly. ')
|
|
459
|
+
else:
|
|
460
|
+
_, trend = flatten(source.time, cal_aper_lc - np.nanmin(cal_aper_lc) + 1000,
|
|
461
|
+
window_length=1, method='biweight', return_trend=True)
|
|
462
|
+
cal_aper_lc = (cal_aper_lc - np.nanmin(cal_aper_lc) + 1000 - trend) / np.nanmedian(cal_aper_lc) + 1
|
|
463
|
+
# cal_aper_lc = flatten(source.time, cal_aper_lc, window_length=1, method='biweight',
|
|
464
|
+
# return_trend=False)
|
|
465
|
+
if near_edge:
|
|
466
|
+
cal_psf_lc = psf_lc
|
|
467
|
+
return local_bg, aper_lc, psf_lc, cal_aper_lc, cal_psf_lc
|
|
468
|
+
else:
|
|
469
|
+
cal_psf_lc = psf_lc / np.nanmedian(psf_lc)
|
|
470
|
+
cal_psf_lc[np.where(cal_psf_lc > 100)] = np.nan
|
|
471
|
+
if np.isnan(cal_psf_lc).all():
|
|
472
|
+
print('Calibrated PSF flux are not accessible or processed incorrectly. ')
|
|
473
|
+
else:
|
|
474
|
+
_, trend = flatten(source.time, cal_psf_lc - np.nanmin(cal_psf_lc) + 1000,
|
|
475
|
+
window_length=1, method='biweight', return_trend=True)
|
|
476
|
+
cal_psf_lc = (cal_psf_lc - np.nanmin(cal_psf_lc) + 1000 - trend) / np.nanmedian(cal_psf_lc) + 1
|
|
477
|
+
# cal_psf_lc = flatten(source.time, cal_psf_lc, window_length=1, method='biweight',
|
|
478
|
+
# return_trend=False)
|
|
479
|
+
# aper_mad = 1.4826 * np.nanmedian(np.abs(cal_aper_lc - 1))
|
|
480
|
+
# if aper_mad > 0.02:
|
|
481
|
+
# psf_mad = 1.4826 * np.nanmedian(np.abs(cal_psf_lc - 1))
|
|
482
|
+
# cal_psf_lc /= psf_mad / aper_mad
|
|
483
|
+
# cal_psf_lc += 1 - np.median(cal_psf_lc)
|
|
484
|
+
return local_bg, aper_lc, psf_lc, cal_aper_lc, cal_psf_lc
|