exovetter 0.0.2__py3-none-any.whl → 0.0.4__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,227 @@
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
+
85
+ self.tran_epochs = np.unique(self.epochs[self.in_tran])
86
+ self.N_transit = len(self.tran_epochs)
87
+ # Cadences within 2 transit durations
88
+ self.fit_tran = abs(self.phase) < 2 * self.qtran # can change to a variable other than 2
89
+ # Number of transit datapoints
90
+ self.n_in = np.sum(self.in_tran)
91
+
92
+ # Out-of-transit flux and transit depth
93
+ self.zpt = weighted_mean(self.flux[~self.near_tran], self.flux_err[~self.near_tran])
94
+ self.dep = self.zpt - weighted_mean(self.flux[self.in_tran], self.flux_err[self.in_tran])
95
+
96
+ self.frac = frac
97
+ self.max_chases_phase = max_chases_phase
98
+
99
+
100
+ def get_SES_MES(self):
101
+ N = len(self.time)
102
+ dep_SES = np.zeros(N)
103
+ n_SES = np.zeros(N)
104
+ dep_MES = np.zeros(N)
105
+ n_MES = np.zeros(N)
106
+ N_transit_MES = np.zeros(N)
107
+ bin_flux = np.zeros(N)
108
+ bin_flux_err = np.zeros(N)
109
+ phase = phasefold(self.time, self.per, self.epo)
110
+ phase[phase < 0] += 1
111
+ for i in np.arange(N):
112
+ # Get individual transit depth at this cadence, i.e. only use datapoints close in time
113
+ in_tran = abs(self.time - self.time[i]) < 0.5 * self.dur
114
+ n_SES[i] = np.sum(in_tran)
115
+ dep_SES[i] = self.zpt - weighted_mean(
116
+ self.flux[in_tran], self.flux_err[in_tran]
117
+ )
118
+ # Get overall transit depth at this cadence, i.e. use all datapoints close in phase
119
+ all_tran = (abs(phase - phase[i]) < 0.5 * self.qtran) | (
120
+ abs(phase - phase[i]) > 1 - 0.5 * self.qtran
121
+ )
122
+ n_MES[i] = np.sum(all_tran)
123
+ dep_MES[i] = self.zpt - weighted_mean(
124
+ self.flux[all_tran], self.flux_err[all_tran]
125
+ )
126
+ epochs = np.round((self.time - self.time[i]) / self.per)
127
+ tran_epochs = np.unique(epochs[all_tran])
128
+ N_transit_MES[i] = len(tran_epochs)
129
+ # Get running mean and uncertainty of out-of-transit fluxes, binned over transit timescale
130
+ in_bin = in_tran & ~self.near_tran
131
+ bin_flux[i] = weighted_mean(self.flux[in_bin], self.flux_err[in_bin])
132
+ bin_flux_err[i] = weighted_err(self.flux[in_bin], self.flux_err[in_bin]).value
133
+ # Estimate white and red noise following Hartman & Bakos (2016)
134
+ mask = ~np.isnan(bin_flux) & ~self.near_tran
135
+ std = weighted_std(self.flux[mask], self.flux_err[mask])
136
+ bin_std = weighted_std(bin_flux[mask], bin_flux_err[mask])
137
+ expected_bin_std = (
138
+ std
139
+ * np.sqrt(np.nanmean(bin_flux_err[mask] ** 2))
140
+ / np.sqrt(np.nanmean(self.flux_err[mask] ** 2))
141
+ ).value
142
+ self.sig_w = std
143
+ sig_r2 = bin_std**2 - expected_bin_std**2
144
+ self.sig_r = np.sqrt(sig_r2) if sig_r2 > 0 else 0
145
+ # Estimate signal-to-pink-noise following Pont et al. (2006)
146
+ self.err = np.sqrt(
147
+ (self.sig_w**2 / self.n_in) + (self.sig_r**2 / self.N_transit)
148
+ )
149
+ err_SES = np.sqrt((self.sig_w**2 / n_SES) + self.sig_r**2)
150
+ err_MES = np.sqrt((self.sig_w**2 / n_MES) + (self.sig_r**2 / N_transit_MES))
151
+ self.SES_series = dep_SES / err_SES
152
+ self.dep_series = dep_MES
153
+ self.err_series = err_MES
154
+ self.MES_series = dep_MES / err_MES
155
+ self.MES = self.dep / self.err
156
+ Fmin = np.nanmin(-self.dep_series)
157
+ Fmax = np.nanmax(-self.dep_series)
158
+ self.SHP = Fmax / (Fmax - Fmin)
159
+
160
+ def get_chases(self):
161
+ deps = np.zeros(self.N_transit)
162
+ errs = np.zeros(self.N_transit)
163
+ self.SES = np.zeros(self.N_transit)
164
+ self.rubble = np.zeros(self.N_transit)
165
+ self.chases = np.zeros(self.N_transit)
166
+ # self.redchi2 = np.zeros(self.N_transit)
167
+ # Search range for chases metric is between 1.5 durations and n times the period away
168
+ chases_tran = (abs(self.phase) > 1.5 * self.qtran) & (abs(self.phase) < self.max_chases_phase)
169
+
170
+ # Get metrics for each transit event
171
+ for i in range(self.N_transit):
172
+ epoch = self.tran_epochs[i]
173
+ in_epoch = self.in_tran & (self.epochs == epoch)
174
+ # Compute the transit time, depth, and SES for this transit
175
+ transit_time = self.epo + self.per * epoch
176
+ n_in = np.sum(in_epoch)
177
+ dep = self.zpt - weighted_mean(self.flux[in_epoch], self.flux_err[in_epoch])
178
+ err = np.sqrt((self.sig_w**2 / n_in) + self.sig_r**2)
179
+ deps[i], errs[i] = dep, err
180
+ self.SES[i] = dep / err
181
+ # Find the most significant nearby event
182
+ chases_epoch = (chases_tran & (self.epochs == epoch) & (np.abs(self.SES_series) > self.frac * self.SES[i]))
183
+
184
+ if np.any(chases_epoch):
185
+ self.chases[i] = np.min(np.abs(self.time[chases_epoch] - transit_time)) / (
186
+ self.max_chases_phase * self.per
187
+ )
188
+ else:
189
+ self.chases[i] = 1
190
+ # Find how much of the transit falls in gaps
191
+ fit_epoch = self.fit_tran & (self.epochs == epoch)
192
+ n_obs = np.sum(fit_epoch)
193
+ cadence = np.nanmedian(np.diff(self.time[fit_epoch]))
194
+ n_exp = 4 * self.dur / cadence # 4 is used because of the 2 transit duration on either side above in fit_tran
195
+ self.rubble[i] = n_obs / n_exp
196
+ # if ("transit_aic" in tlc.metrics) and ~np.isnan(tlc.metrics["transit_aic"]):
197
+ # tm = TransitModel(
198
+ # tlc.metrics["transit_per"],
199
+ # tlc.metrics["transit_epo"],
200
+ # tlc.metrics["transit_RpRs"],
201
+ # tlc.metrics["transit_aRs"],
202
+ # tlc.metrics["transit_b"],
203
+ # tlc.metrics["transit_u1"],
204
+ # tlc.metrics["transit_u2"],
205
+ # tlc.metrics["transit_zpt"],
206
+ # )
207
+ # resid = tm.residual(
208
+ # tm.params,
209
+ # tlc.time[fit_epoch],
210
+ # tlc.flux[fit_epoch],
211
+ # tlc.flux_err[fit_epoch],
212
+ # )
213
+ # chi2 = np.sum(resid**2)
214
+ # tlc.redchi2[i] = chi2 / (np.sum(fit_epoch) - 6)
215
+ # else:
216
+ # tlc.redchi2[i] = np.nan
217
+ O = self.SES
218
+ E = self.dep / errs
219
+ chi2 = np.sum((O - E) ** 2 / E)
220
+ self.CHI = self.MES / np.sqrt(chi2 / (self.N_transit - 1))
221
+ self.med_chases = np.nanmedian(self.chases)
222
+ self.mean_chases = np.nanmean(self.chases)
223
+ self.max_SES = np.nanmax(self.SES)
224
+ self.DMM = np.nanmean(deps) / np.nanmedian(deps)
225
+
226
+ def plot (self):
227
+ print('Plotting not yet implemented')
@@ -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,7 +325,8 @@ 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
-
328
+
329
+ fig.tight_layout()
329
330
  plt.draw()
330
331
 
331
332
  return fig
exovetter/sweet.py CHANGED
@@ -1,4 +1,4 @@
1
- """Module ot handle SWEET vetter."""
1
+ """Module to handle SWEET vetter."""
2
2
 
3
3
  import os
4
4
 
@@ -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() # swapped with a plt.figure below MD 2023
67
+ plt.figure(figsize=(7,7))
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/2, 1, and 2 times the period')
83
86
 
84
87
  result = np.array(out)
85
88
 
exovetter/tce.py CHANGED
@@ -130,9 +130,15 @@ class Tce(dict):
130
130
  """
131
131
  if 'epoch' not in self or 'epoch_offset' not in self:
132
132
  raise KeyError('epoch and epoch_offset must be defined first')
133
+
134
+ # Puts into bjd
133
135
  epoch = self["epoch"] - self["epoch_offset"]
136
+
134
137
  if offset is not None:
138
+ # Transforms from bjd into desired time system
135
139
  epoch = epoch + offset
140
+
141
+
136
142
  return epoch
137
143
 
138
144
  def validate(self):
@@ -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', 'first_epoch']
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
@@ -600,7 +615,32 @@ def compute_phases(time, period, epoch, offset=0.5):
600
615
 
601
616
  return phases
602
617
 
618
+ def first_epoch(epoch, period, lc):
619
+ """Returns epoch of the first time in a lc file. Usefull when pulling down TCEs from MAST.
603
620
 
604
-
605
-
606
-
621
+ Parameters
622
+ ----------
623
+ epoch : float
624
+ Time of transit from a given TCE in same time system as lc.
625
+
626
+ period : float
627
+ Period in units of time.
628
+
629
+ lc : lightkurve object
630
+ lightkurve object with flux and time.
631
+
632
+ Returns
633
+ -------
634
+ first_epoch : float
635
+ Epoch of first transit in lc in the time system of the lc.
636
+
637
+ """
638
+ # TODO might need to assert epoch.value is within lc.time.value
639
+ if epoch.value >= lc.time.value[0]:
640
+ N = np.floor((epoch.value-lc.time.value[0])/period.value)
641
+ first_epoch = epoch - N*period
642
+ else:
643
+ N = np.ceil((lc.time.value[0]-epoch.value)/period.value)
644
+ first_epoch = epoch + N*period
645
+
646
+ return first_epoch
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.4'