exovetter 0.0.2__py3-none-any.whl → 0.0.3__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 @@
1
+
@@ -11,7 +11,8 @@ def compute_diff_image_centroids(
11
11
  cube,
12
12
  period_days,
13
13
  epoch,
14
- duration_days,
14
+ duration_days,
15
+ remove_transits,
15
16
  max_oot_shift_pix=1.5,
16
17
  plot=False
17
18
  ):
@@ -38,7 +39,9 @@ def compute_diff_image_centroids(
38
39
  epoch
39
40
  (float) Epoch of transit centre in the same time system as `time`.
40
41
  duration_days
41
- (float) Duration of transit.
42
+ (float) Duration of transit.
43
+ remove_transits
44
+ (list) List of 0 indexed transit integers to not calculate on.
42
45
  max_oot_shift_pix
43
46
  (float) Passed to `fastpsffit.fastGaussianPrfFit()
44
47
 
@@ -73,21 +76,30 @@ def compute_diff_image_centroids(
73
76
 
74
77
  figs = []
75
78
  centroids = []
79
+
76
80
  for i in range(len(transits)):
77
- cin = transits[i]
78
- cents, fig = measure_centroids(
79
- cube,
80
- cin,
81
- max_oot_shift_pix=max_oot_shift_pix,
82
- plot=plot
83
- )
84
- centroids.append(cents)
85
- figs.append(fig)
81
+ if i not in remove_transits:
82
+ cin = transits[i]
83
+ cents, fig = measure_centroids(
84
+ cube,
85
+ cin,
86
+ max_oot_shift_pix=max_oot_shift_pix,
87
+ plot=plot
88
+ )
89
+
90
+ if plot == True:
91
+ fig.suptitle('Transit '+str(i))
92
+
93
+ centroids.append(cents)
94
+ figs.append(fig)
95
+
86
96
  centroids = np.array(centroids)
87
- return centroids, figs
97
+ all_transits = list(np.arange(len(transits)))
98
+ kept_transits = [x for x in all_transits if x not in remove_transits]
99
+ return centroids, figs, kept_transits
88
100
 
89
101
 
90
- def measure_centroid_shift(centroids, plot=False):
102
+ def measure_centroid_shift(centroids, kept_transits, plot=False):
91
103
  """Measure the average offset of the DIC centroids from the OOT centroids.
92
104
 
93
105
  Inputs
@@ -95,6 +107,9 @@ def measure_centroid_shift(centroids, plot=False):
95
107
  centroids
96
108
  (2d np array) Output of :func:`compute_diff_image_centroids`
97
109
 
110
+ kept_transits
111
+ (list) List of 0 indexed transit integers to calculate on.
112
+
98
113
  Returns
99
114
  -----------
100
115
  offset
@@ -122,7 +137,7 @@ def measure_centroid_shift(centroids, plot=False):
122
137
 
123
138
  fig = None
124
139
  if plot:
125
- fig = covar.diagnostic_plot(dcol, drow, flags)
140
+ fig = covar.diagnostic_plot(dcol, drow, kept_transits, flags)
126
141
  return offset_pix, signif, fig
127
142
 
128
143
 
@@ -45,7 +45,7 @@ import numpy as np
45
45
  """
46
46
 
47
47
 
48
- def diagnostic_plot(x, y, flag=None):
48
+ def diagnostic_plot(x, y, kept_transits, flag=None):
49
49
 
50
50
  if flag is None:
51
51
  flag = np.zeros(len(x))
@@ -57,7 +57,8 @@ def diagnostic_plot(x, y, flag=None):
57
57
  mu_y = np.mean(y[~idx])
58
58
  sma, smi = compute_eigen_vectors(x[~idx], y[~idx])
59
59
 
60
- plt.clf()
60
+ # plt.clf()
61
+ plt.figure()
61
62
  plt.gcf().set_size_inches((10, 8))
62
63
  plt.plot(x, y, "ko", mec="w", label="Centroids", zorder=+5)
63
64
  if np.any(idx):
@@ -71,7 +72,8 @@ def diagnostic_plot(x, y, flag=None):
71
72
 
72
73
  # prob = compute_prob_of_points(x, y, sma, smi)
73
74
  for i in range(len(x)):
74
- plt.text(x[i], y[i], " %i" % (i), zorder=+5)
75
+
76
+ plt.text(x[i], y[i], " %i" % (kept_transits[i]), zorder=+5)
75
77
 
76
78
  sigma_a = np.linalg.norm(sma)
77
79
  sigma_b = np.linalg.norm(smi)
exovetter/leo.py ADDED
@@ -0,0 +1,226 @@
1
+ import numpy as np
2
+
3
+ def phasefold(t, per, epo):
4
+ # Phase will span -0.5 to 0.5, with transit centred at phase 0
5
+ phase = np.mod(t - epo, per) / per
6
+ phase[phase > 0.5] -= 1
7
+ return phase
8
+
9
+ def weighted_mean(y, dy):
10
+ w = 1 / dy**2
11
+ mean = np.sum(w * y) / np.sum(w)
12
+ return mean
13
+
14
+ def weighted_std(y, dy):
15
+ w = 1 / dy**2
16
+ N = len(w)
17
+ mean = np.sum(w * y) / np.sum(w)
18
+ std = np.sqrt(np.sum(w * (y - mean) ** 2) / ((N - 1) * np.sum(w) / N))
19
+ return std
20
+
21
+ def weighted_err(y, dy):
22
+ w = 1 / dy**2
23
+ err = 1 / np.sqrt(np.sum(w))
24
+ return err
25
+
26
+ class Leo:
27
+ def __init__(self, time, per, epo, dur, flux, flux_err, frac, max_chases_phase):
28
+ '''
29
+ Parameters
30
+ -----------
31
+ time : array
32
+ Array of times from lc
33
+
34
+ per : float
35
+ Orbital period in days
36
+
37
+ epo : float
38
+ Time of first transit in TESS BJD
39
+
40
+ dur : float
41
+ Transit duration in days
42
+
43
+ flux : array
44
+ Array of flux values from lc
45
+
46
+ flux_err : array
47
+ Array of flux error values from lc
48
+
49
+ frac : float
50
+ fraction of SES for a transit which triggers the chases false alarm statistic (default 0.7)
51
+
52
+ max_chases_phase : float
53
+ maximum to allow the chases search to run on (default 0.1)
54
+
55
+ Attributes
56
+ ------------
57
+ qtran : Transit duration divided by the period of the transit
58
+ phase : Array of phases for the time series spanning -0.5 to 0.5 with transit at 0
59
+ in_tran : Phases in transit
60
+ near_tran : Boolean of cadences within 1 transit duration
61
+ epochs : Number of transits accounting for gaps
62
+ tran_epochs : Epochs of the transits
63
+ N_transit : Length of tran_epochs
64
+ fit_tran : Cadences within 2 transit durations
65
+ zpt : Out-of-transit wieghted mean of the fluxes
66
+ dep : Depth of the transit based on the weighted mean of the in transit points
67
+ '''
68
+ self.time = time
69
+ self.per = per
70
+ self.epo = epo
71
+ self.dur = dur
72
+ self.flux = flux
73
+ self.flux_err = flux_err
74
+ self.qtran = dur / per
75
+
76
+ # Phase spans -0.5 to 0.5 with transit at 0
77
+ self.phase = phasefold(time, per, epo)
78
+ # Cadences in-transit
79
+ self.in_tran = abs(self.phase) < 0.5 * self.qtran
80
+ # Cadences within 1 transit duration
81
+ self.near_tran = abs(self.phase) < self.qtran
82
+ # Actual number of transits accounting for gaps
83
+ self.epochs = np.round((time - epo) / per)
84
+ self.tran_epochs = np.unique(self.epochs[self.in_tran])
85
+ self.N_transit = len(self.tran_epochs)
86
+ # Cadences within 2 transit durations
87
+ self.fit_tran = abs(self.phase) < 2 * self.qtran # can change to a variable other than 2
88
+ # Number of transit datapoints
89
+ self.n_in = np.sum(self.in_tran)
90
+
91
+ # Out-of-transit flux and transit depth
92
+ self.zpt = weighted_mean(self.flux[~self.near_tran], self.flux_err[~self.near_tran])
93
+ self.dep = self.zpt - weighted_mean(self.flux[self.in_tran], self.flux_err[self.in_tran])
94
+
95
+ self.frac = frac
96
+ self.max_chases_phase = max_chases_phase
97
+
98
+
99
+ def get_SES_MES(self):
100
+ N = len(self.time)
101
+ dep_SES = np.zeros(N)
102
+ n_SES = np.zeros(N)
103
+ dep_MES = np.zeros(N)
104
+ n_MES = np.zeros(N)
105
+ N_transit_MES = np.zeros(N)
106
+ bin_flux = np.zeros(N)
107
+ bin_flux_err = np.zeros(N)
108
+ phase = phasefold(self.time, self.per, self.epo)
109
+ phase[phase < 0] += 1
110
+ for i in np.arange(N):
111
+ # Get individual transit depth at this cadence, i.e. only use datapoints close in time
112
+ in_tran = abs(self.time - self.time[i]) < 0.5 * self.dur
113
+ n_SES[i] = np.sum(in_tran)
114
+ dep_SES[i] = self.zpt - weighted_mean(
115
+ self.flux[in_tran], self.flux_err[in_tran]
116
+ )
117
+ # Get overall transit depth at this cadence, i.e. use all datapoints close in phase
118
+ all_tran = (abs(phase - phase[i]) < 0.5 * self.qtran) | (
119
+ abs(phase - phase[i]) > 1 - 0.5 * self.qtran
120
+ )
121
+ n_MES[i] = np.sum(all_tran)
122
+ dep_MES[i] = self.zpt - weighted_mean(
123
+ self.flux[all_tran], self.flux_err[all_tran]
124
+ )
125
+ epochs = np.round((self.time - self.time[i]) / self.per)
126
+ tran_epochs = np.unique(epochs[all_tran])
127
+ N_transit_MES[i] = len(tran_epochs)
128
+ # Get running mean and uncertainty of out-of-transit fluxes, binned over transit timescale
129
+ in_bin = in_tran & ~self.near_tran
130
+ bin_flux[i] = weighted_mean(self.flux[in_bin], self.flux_err[in_bin])
131
+ bin_flux_err[i] = weighted_err(self.flux[in_bin], self.flux_err[in_bin])
132
+ # Estimate white and red noise following Hartman & Bakos (2016)
133
+ mask = ~np.isnan(bin_flux) & ~self.near_tran
134
+ std = weighted_std(self.flux[mask], self.flux_err[mask])
135
+ bin_std = weighted_std(bin_flux[mask], bin_flux_err[mask])
136
+ expected_bin_std = (
137
+ std
138
+ * np.sqrt(np.nanmean(bin_flux_err[mask] ** 2))
139
+ / np.sqrt(np.nanmean(self.flux_err[mask] ** 2))
140
+ )
141
+ self.sig_w = std
142
+ sig_r2 = bin_std**2 - expected_bin_std**2
143
+ self.sig_r = np.sqrt(sig_r2) if sig_r2 > 0 else 0
144
+ # Estimate signal-to-pink-noise following Pont et al. (2006)
145
+ self.err = np.sqrt(
146
+ (self.sig_w**2 / self.n_in) + (self.sig_r**2 / self.N_transit)
147
+ )
148
+ err_SES = np.sqrt((self.sig_w**2 / n_SES) + self.sig_r**2)
149
+ err_MES = np.sqrt((self.sig_w**2 / n_MES) + (self.sig_r**2 / N_transit_MES))
150
+ self.SES_series = dep_SES / err_SES
151
+ self.dep_series = dep_MES
152
+ self.err_series = err_MES
153
+ self.MES_series = dep_MES / err_MES
154
+ self.MES = self.dep / self.err
155
+ Fmin = np.nanmin(-self.dep_series)
156
+ Fmax = np.nanmax(-self.dep_series)
157
+ self.SHP = Fmax / (Fmax - Fmin)
158
+
159
+ def get_chases(self):
160
+ deps = np.zeros(self.N_transit)
161
+ errs = np.zeros(self.N_transit)
162
+ self.SES = np.zeros(self.N_transit)
163
+ self.rubble = np.zeros(self.N_transit)
164
+ self.chases = np.zeros(self.N_transit)
165
+ # self.redchi2 = np.zeros(self.N_transit)
166
+ # Search range for chases metric is between 1.5 durations and n times the period away
167
+ chases_tran = (abs(self.phase) > 1.5 * self.qtran) & (abs(self.phase) < self.max_chases_phase)
168
+
169
+ # Get metrics for each transit event
170
+ for i in range(self.N_transit):
171
+ epoch = self.tran_epochs[i]
172
+ in_epoch = self.in_tran & (self.epochs == epoch)
173
+ # Compute the transit time, depth, and SES for this transit
174
+ transit_time = self.epo + self.per * epoch
175
+ n_in = np.sum(in_epoch)
176
+ dep = self.zpt - weighted_mean(self.flux[in_epoch], self.flux_err[in_epoch])
177
+ err = np.sqrt((self.sig_w**2 / n_in) + self.sig_r**2)
178
+ deps[i], errs[i] = dep, err
179
+ self.SES[i] = dep / err
180
+ # Find the most significant nearby event
181
+ chases_epoch = (chases_tran & (self.epochs == epoch) & (np.abs(self.SES_series) > self.frac * self.SES[i]))
182
+
183
+ if np.any(chases_epoch):
184
+ self.chases[i] = np.min(np.abs(self.time[chases_epoch] - transit_time)) / (
185
+ self.max_chases_phase * self.per
186
+ )
187
+ else:
188
+ self.chases[i] = 1
189
+ # Find how much of the transit falls in gaps
190
+ fit_epoch = self.fit_tran & (self.epochs == epoch)
191
+ n_obs = np.sum(fit_epoch)
192
+ cadence = np.nanmedian(np.diff(self.time[fit_epoch]))
193
+ n_exp = 4 * self.dur / cadence # 4 is used because of the 2 transit duration on either side above in fit_tran
194
+ self.rubble[i] = n_obs / n_exp
195
+ # if ("transit_aic" in tlc.metrics) and ~np.isnan(tlc.metrics["transit_aic"]):
196
+ # tm = TransitModel(
197
+ # tlc.metrics["transit_per"],
198
+ # tlc.metrics["transit_epo"],
199
+ # tlc.metrics["transit_RpRs"],
200
+ # tlc.metrics["transit_aRs"],
201
+ # tlc.metrics["transit_b"],
202
+ # tlc.metrics["transit_u1"],
203
+ # tlc.metrics["transit_u2"],
204
+ # tlc.metrics["transit_zpt"],
205
+ # )
206
+ # resid = tm.residual(
207
+ # tm.params,
208
+ # tlc.time[fit_epoch],
209
+ # tlc.flux[fit_epoch],
210
+ # tlc.flux_err[fit_epoch],
211
+ # )
212
+ # chi2 = np.sum(resid**2)
213
+ # tlc.redchi2[i] = chi2 / (np.sum(fit_epoch) - 6)
214
+ # else:
215
+ # tlc.redchi2[i] = np.nan
216
+ O = self.SES
217
+ E = self.dep / errs
218
+ chi2 = np.sum((O - E) ** 2 / E)
219
+ self.CHI = self.MES / np.sqrt(chi2 / (self.N_transit - 1))
220
+ self.med_chases = np.nanmedian(self.chases)
221
+ self.mean_chases = np.nanmean(self.chases)
222
+ self.max_SES = np.nanmax(self.SES)
223
+ self.DMM = np.nanmean(deps) / np.nanmedian(deps)
224
+
225
+ def plot (self):
226
+ print('Implement plotting')
@@ -2,7 +2,7 @@
2
2
 
3
3
  import astropy
4
4
 
5
- __all__ = ['unpack_lk_version']
5
+ __all__ = ['unpack_lk_version', 'unpack_tpf']
6
6
 
7
7
 
8
8
  def unpack_lk_version(lightcurve, flux_name):
exovetter/lpp.py CHANGED
@@ -325,8 +325,9 @@ def plot_lpp_diagnostic(data, target, norm_lpp):
325
325
  ax2.plot(flux, 'k.', ms=5, label=f"LPP Norm = {norm_lpp:5.3f}")
326
326
  ax2.set_xlabel('Bin Number')
327
327
  ax2.legend()
328
-
329
- plt.draw()
328
+
329
+ fig.tight_layout()
330
+ plt.draw() #Why is this here? MD 2023
330
331
 
331
332
  return fig
332
333
 
exovetter/sweet.py CHANGED
@@ -63,7 +63,8 @@ def sweet(time, flux, period, epoch, duration, plot=False):
63
63
 
64
64
  if plot:
65
65
  import matplotlib.pyplot as plt
66
- plt.clf()
66
+ #plt.clf() # removed MD 2023
67
+ plt.figure(figsize=(7,7)) # Added MD 2023
67
68
 
68
69
  out = []
69
70
  for i, per in enumerate([period * 0.5, period, 2 * period]):
@@ -77,9 +78,11 @@ def sweet(time, flux, period, epoch, duration, plot=False):
77
78
  if plot:
78
79
  srt = np.argsort(phase)
79
80
  plt.subplot(3, 1, i + 1)
80
- plt.plot(phase, flux, 'ko')
81
+ plt.plot(phase, flux, 'ko', ms=2.5)
81
82
  plt.plot(phase[srt], f_obj.get_best_fit_model(phase[srt]), '-')
82
83
  plt.ylabel("P=%g" % (per))
84
+ if i==0:
85
+ plt.title('SWEET: Folded at 1, 1/2, and 1/4 times the period') # Added MD 2023
83
86
 
84
87
  result = np.array(out)
85
88
 
@@ -742,7 +742,8 @@ class TrapezoidFit:
742
742
 
743
743
  """
744
744
  import scipy.optimize as opt
745
- from astropy.utils.compat.context import nullcontext
745
+ # from astropy.utils.compat.context import nullcontext # removed as depricated MD 2023
746
+ from contextlib import nullcontext # added instead of astropy's nullcontext ^
746
747
  from astropy.utils.misc import NumpyRNGContext
747
748
 
748
749
  if options is None:
exovetter/utils.py CHANGED
@@ -5,6 +5,9 @@ import warnings
5
5
 
6
6
  import numpy as np
7
7
 
8
+ __all__ = ['sine', 'estimate_scatter', 'mark_transit_cadences', 'median_detrend',
9
+ 'plateau', 'set_median_flux_to_zero', 'set_median_flux_to_one', 'sigmaClip',
10
+ 'get_mast_tce', 'WqedLSF', 'compute_phases']
8
11
 
9
12
  def sine(x, order, period=1):
10
13
  """Sine function for SWEET vetter."""
@@ -115,7 +118,8 @@ def mark_transit_cadences(time, period_days, epoch_bkjd, duration_days,
115
118
  big_num = sys.float_info.max # A large value that isn't NaN
116
119
  max_diff = 0.5 * duration_days * num_durations
117
120
 
118
- idx = np.zeros_like(time, dtype=np.bool8)
121
+ idx = np.zeros_like(time, dtype=np.bool_)
122
+ # Changed from np.bool8 to np.bool_ for py 3.11
119
123
  for tt in transit_times:
120
124
  diff = time - tt
121
125
  diff[flags] = big_num
@@ -150,25 +154,32 @@ def median_detrend(flux, nPoints):
150
154
 
151
155
 
152
156
  def plateau(array, threshold):
153
- """Find plateaus in an array, i.e continuous regions that exceed threshold
157
+ """Find plateaus in an array, i.e continuous regions that exceed threshold.
154
158
 
155
159
  Given an array of numbers, return a 2d array such that
156
160
  out[:,0] marks the indices where the array crosses threshold from
157
161
  below, and out[:,1] marks the next time the array crosses that
158
162
  same threshold from below.
159
163
 
160
- Inputs:
161
- array (1d numpy array)
162
- threshold (float or array) If threshold is a single number, any point
163
- above that value is above threshold. If it's an array,
164
- it must have the same length as the first argument, and
165
- an array[i] > threshold[i] to be included as a plateau
166
-
167
- Returns:
168
- Numpy 2d array with 2 columns.
164
+ Parameters
165
+ ----------
166
+ array : array_like
167
+ Numpy 1D array
168
+
169
+ threshold : float or array_like
170
+ If threshold is a single number, any point
171
+ above that value is above threshold. If it's an array,
172
+ it must have the same length as the first argument, and
173
+ an array[i] > threshold[i] to be included as a plateau
174
+
175
+ Returns
176
+ -------
177
+ loc : array_like
178
+ Numpy 2d array with 2 columns.
169
179
 
170
180
 
171
- Notes:
181
+ Notes
182
+ -----
172
183
  To find the length of the plateaus, use
173
184
  out[:,1] - out[:,0]
174
185
 
@@ -180,7 +191,7 @@ def plateau(array, threshold):
180
191
  to ensure floating point arithmetic prevents two numbers being
181
192
  exactly equal.
182
193
  """
183
-
194
+
184
195
  arr = array.astype(np.float32)
185
196
  arr = arr - threshold + 1e-12
186
197
  arrPlus = np.roll(arr, 1)
@@ -316,6 +327,7 @@ def get_mast_tce(name):
316
327
  url = planeturl + name + "/properties/"
317
328
 
318
329
  r = requests.get(url = url, headers = header)
330
+
319
331
  if len(r.json()) < 1:
320
332
  print("No TCE Information was returned from MAST.")
321
333
  return []
@@ -330,6 +342,7 @@ def get_mast_tce(name):
330
342
  epoch_offset_str = 'mjd'
331
343
  depth = prop['transit_depth']
332
344
  duration = prop['transit_duration']
345
+ catalog_name = prop['catalog_name']
333
346
  if duration is None:
334
347
  duration = 0
335
348
  durunit = prop['transit_duration_unit']
@@ -341,7 +354,8 @@ def get_mast_tce(name):
341
354
  epoch_offset = const.__dict__['string_to_offset'][epoch_offset_str],
342
355
  depth = depth * const.frac_amp,
343
356
  duration = duration * u.__dict__[durunit],
344
- target = name
357
+ target = name,
358
+ catalog_name = catalog_name
345
359
  )
346
360
 
347
361
  tces.append(atce)
@@ -437,7 +451,8 @@ class WqedLSF:
437
451
  covar = np.linalg.inv(A)
438
452
 
439
453
  wy = self.y / self.s
440
- beta = np.dot(wy, df)
454
+ df = df.T # transposed to work with np.dot updates MD 2023
455
+ beta = np.dot(df, wy) # swapped order in dot product MD 2023
441
456
  params = np.dot(beta, covar)
442
457
 
443
458
  # Store results
@@ -599,8 +614,3 @@ def compute_phases(time, period, epoch, offset=0.5):
599
614
  pmin = np.min(phases)
600
615
 
601
616
  return phases
602
-
603
-
604
-
605
-
606
-
exovetter/version.py CHANGED
@@ -5,4 +5,4 @@ try:
5
5
  from setuptools_scm import get_version
6
6
  version = get_version(root='..', relative_to=__file__)
7
7
  except Exception:
8
- version = '0.0.2'
8
+ version = '0.0.3'