asteroid_spinprops 1.1.1__py3-none-any.whl → 1.2.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.
- asteroid_spinprops/ssolib/modelfit.py +140 -69
- asteroid_spinprops/ssolib/utils.py +54 -0
- {asteroid_spinprops-1.1.1.dist-info → asteroid_spinprops-1.2.0.dist-info}/METADATA +1 -1
- {asteroid_spinprops-1.1.1.dist-info → asteroid_spinprops-1.2.0.dist-info}/RECORD +5 -5
- {asteroid_spinprops-1.1.1.dist-info → asteroid_spinprops-1.2.0.dist-info}/WHEEL +0 -0
|
@@ -2,23 +2,27 @@ import numpy as np
|
|
|
2
2
|
import pandas as pd
|
|
3
3
|
|
|
4
4
|
import asteroid_spinprops.ssolib.utils as utils
|
|
5
|
+
import asteroid_spinprops.ssolib.periodest as periodest
|
|
5
6
|
|
|
6
7
|
from fink_utils.sso.spins import (
|
|
7
8
|
estimate_sso_params,
|
|
8
9
|
func_hg1g2_with_spin,
|
|
9
10
|
)
|
|
10
|
-
from asteroid_spinprops.ssolib.periodest import
|
|
11
|
+
from asteroid_spinprops.ssolib.periodest import (
|
|
12
|
+
get_multiterm_period_estimate,
|
|
13
|
+
)
|
|
11
14
|
|
|
12
15
|
|
|
13
16
|
def get_fit_params(
|
|
14
17
|
data,
|
|
15
18
|
flavor,
|
|
16
19
|
shg1g2_constrained=True,
|
|
17
|
-
|
|
20
|
+
period_blind=True,
|
|
21
|
+
pole_blind=True,
|
|
18
22
|
p0=None,
|
|
19
|
-
survey_filter=None,
|
|
20
23
|
alt_spin=False,
|
|
21
24
|
period_in=None,
|
|
25
|
+
period_quality_flag=False,
|
|
22
26
|
terminator=False,
|
|
23
27
|
):
|
|
24
28
|
"""
|
|
@@ -44,12 +48,12 @@ def get_fit_params(
|
|
|
44
48
|
Model type to fit. Must be 'SHG1G2' or 'SOCCA'.
|
|
45
49
|
shg1g2_constrained : bool, optional
|
|
46
50
|
Whether to constrain the SOCCA fit using a prior SHG1G2 solution. Default True.
|
|
47
|
-
|
|
48
|
-
If True, perform a small grid search over initial
|
|
51
|
+
period_blind : bool, optional
|
|
52
|
+
If True, perform a small grid search over initial periods. Default True.
|
|
53
|
+
pole_blind : bool, optional
|
|
54
|
+
If True, perform a grid search over initial poles. Default True.
|
|
49
55
|
p0 : list, optional
|
|
50
56
|
Initial guess parameters for the fit. Required if `shg1g2_constrained=False`.
|
|
51
|
-
survey_filter : str or None, optional
|
|
52
|
-
If 'ZTF' or 'ATLAS', only data from that survey are used. Default None uses all data.
|
|
53
57
|
alt_spin : bool, optional
|
|
54
58
|
For SOCCA constrained fits, use the antipodal spin solution. Default False.
|
|
55
59
|
period_in : float, optional
|
|
@@ -80,36 +84,26 @@ def get_fit_params(
|
|
|
80
84
|
If `flavor` is not 'SHG1G2' or 'SOCCA'.
|
|
81
85
|
"""
|
|
82
86
|
|
|
83
|
-
if survey_filter is None:
|
|
84
|
-
filter_mask = np.array(data["cfid"].values[0]) >= 0
|
|
85
|
-
if survey_filter == "ZTF":
|
|
86
|
-
filter_mask = (np.array(data["cfid"].values[0]) == 1) | (
|
|
87
|
-
np.array(data["cfid"].values[0]) == 2
|
|
88
|
-
)
|
|
89
|
-
if survey_filter == "ATLAS":
|
|
90
|
-
filter_mask = (np.array(data["cfid"].values[0]) == 3) | (
|
|
91
|
-
np.array(data["cfid"].values[0]) == 4
|
|
92
|
-
)
|
|
93
87
|
if flavor == "SHG1G2":
|
|
94
88
|
if p0 is None:
|
|
95
89
|
Afit = estimate_sso_params(
|
|
96
|
-
magpsf_red=data["cmred"].values[0]
|
|
97
|
-
sigmapsf=data["csigmapsf"].values[0]
|
|
98
|
-
phase=np.radians(data["Phase"].values[0]
|
|
99
|
-
filters=data["cfid"].values[0]
|
|
100
|
-
ra=np.radians(data["ra"].values[0]
|
|
101
|
-
dec=np.radians(data["dec"].values[0]
|
|
90
|
+
magpsf_red=data["cmred"].values[0],
|
|
91
|
+
sigmapsf=data["csigmapsf"].values[0],
|
|
92
|
+
phase=np.radians(data["Phase"].values[0]),
|
|
93
|
+
filters=data["cfid"].values[0],
|
|
94
|
+
ra=np.radians(data["ra"].values[0]),
|
|
95
|
+
dec=np.radians(data["dec"].values[0]),
|
|
102
96
|
model="SHG1G2",
|
|
103
97
|
)
|
|
104
98
|
|
|
105
99
|
if p0 is not None:
|
|
106
100
|
Afit = estimate_sso_params(
|
|
107
|
-
magpsf_red=data["cmred"].values[0]
|
|
108
|
-
sigmapsf=data["csigmapsf"].values[0]
|
|
109
|
-
phase=np.radians(data["Phase"].values[0]
|
|
110
|
-
filters=data["cfid"].values[0]
|
|
111
|
-
ra=np.radians(data["ra"].values[0]
|
|
112
|
-
dec=np.radians(data["dec"].values[0]
|
|
101
|
+
magpsf_red=data["cmred"].values[0],
|
|
102
|
+
sigmapsf=data["csigmapsf"].values[0],
|
|
103
|
+
phase=np.radians(data["Phase"].values[0]),
|
|
104
|
+
filters=data["cfid"].values[0],
|
|
105
|
+
ra=np.radians(data["ra"].values[0]),
|
|
106
|
+
dec=np.radians(data["dec"].values[0]),
|
|
113
107
|
model="SHG1G2",
|
|
114
108
|
p0=p0,
|
|
115
109
|
)
|
|
@@ -118,11 +112,15 @@ def get_fit_params(
|
|
|
118
112
|
if flavor == "SOCCA":
|
|
119
113
|
if shg1g2_constrained is True:
|
|
120
114
|
shg1g2_params = get_fit_params(
|
|
121
|
-
data=data, flavor="SHG1G2"
|
|
122
|
-
)
|
|
123
|
-
residuals_dataframe = make_residuals_df(
|
|
124
|
-
data, model_parameters=shg1g2_params
|
|
115
|
+
data=data, flavor="SHG1G2"
|
|
125
116
|
)
|
|
117
|
+
try:
|
|
118
|
+
residuals_dataframe = make_residuals_df(
|
|
119
|
+
data, model_parameters=shg1g2_params
|
|
120
|
+
)
|
|
121
|
+
except Exception:
|
|
122
|
+
SOCCA_opt = {"Failed at period search preliminary steps": 3}
|
|
123
|
+
return SOCCA_opt
|
|
126
124
|
if period_in is None:
|
|
127
125
|
# Period search boundaries (in days)
|
|
128
126
|
pmin, pmax = 5e-2, 1e4
|
|
@@ -132,20 +130,31 @@ def get_fit_params(
|
|
|
132
130
|
residuals_dataframe, p_min=pmin, p_max=pmax, k_free=True
|
|
133
131
|
)
|
|
134
132
|
)
|
|
133
|
+
if period_quality_flag:
|
|
134
|
+
_, Nbs = periodest.perform_residual_resampling(
|
|
135
|
+
resid_df=residuals_dataframe, p_min=pmin, p_max=pmax, k=int(k_val)
|
|
136
|
+
)
|
|
135
137
|
except KeyError:
|
|
136
138
|
# If more than 10 terms are required switch to fast rotator:
|
|
137
139
|
pmin, pmax = 5e-3, 5e-2
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
try:
|
|
141
|
+
p_in, k_val, p_rms, signal_peaks, window_peaks = (
|
|
142
|
+
get_multiterm_period_estimate(
|
|
143
|
+
residuals_dataframe, p_min=pmin, p_max=pmax, k_free=True
|
|
144
|
+
)
|
|
142
145
|
)
|
|
143
|
-
|
|
146
|
+
if period_quality_flag:
|
|
147
|
+
_, Nbs = periodest.perform_residual_resampling(
|
|
148
|
+
resid_df=residuals_dataframe, p_min=pmin, p_max=pmax, k=int(k_val)
|
|
149
|
+
)
|
|
150
|
+
except Exception:
|
|
151
|
+
SOCCA_opt = {"Failed at period search after": 4}
|
|
152
|
+
return SOCCA_opt
|
|
144
153
|
period_sy = p_in
|
|
145
154
|
else:
|
|
146
155
|
period_sy = period_in
|
|
147
156
|
|
|
148
|
-
if
|
|
157
|
+
if period_blind is True:
|
|
149
158
|
rms = []
|
|
150
159
|
model = []
|
|
151
160
|
|
|
@@ -155,9 +164,54 @@ def get_fit_params(
|
|
|
155
164
|
|
|
156
165
|
ra0, dec0 = shg1g2_params["alpha0"], shg1g2_params["delta0"]
|
|
157
166
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
167
|
+
if pole_blind is True:
|
|
168
|
+
ra_init, dec_init = utils.generate_initial_points(
|
|
169
|
+
ra0, dec0, dec_shift=45
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
else:
|
|
173
|
+
rarange = np.arange(0, 360, 10)
|
|
174
|
+
decrange = np.arange(-90, 90, 5)
|
|
175
|
+
rms_landscape = np.ones(shape=(len(rarange), len(decrange)))
|
|
176
|
+
|
|
177
|
+
for i, ra0 in enumerate(rarange):
|
|
178
|
+
for j, dec0 in enumerate(decrange):
|
|
179
|
+
|
|
180
|
+
all_residuals = []
|
|
181
|
+
|
|
182
|
+
for ff in np.unique(data["cfid"].values[0]):
|
|
183
|
+
cond_ff = data["cfid"].values[0] == ff
|
|
184
|
+
|
|
185
|
+
pha = [np.radians(data["Phase"].values[0][cond_ff]),
|
|
186
|
+
np.radians(data["ra"].values[0][cond_ff]),
|
|
187
|
+
np.radians(data["dec"].values[0][cond_ff])]
|
|
188
|
+
|
|
189
|
+
H = shg1g2_params[f"H_{ff}"]
|
|
190
|
+
G1 = shg1g2_params[f"G1_{ff}"]
|
|
191
|
+
G2 = shg1g2_params[f"G2_{ff}"]
|
|
192
|
+
R = shg1g2_params["R"]
|
|
193
|
+
|
|
194
|
+
C = func_hg1g2_with_spin(pha, H, G1, G2, R,
|
|
195
|
+
np.radians(ra0), np.radians(dec0))
|
|
196
|
+
|
|
197
|
+
O = data["cmred"].values[0][cond_ff]
|
|
198
|
+
|
|
199
|
+
all_residuals.append(O - C)
|
|
200
|
+
|
|
201
|
+
all_residuals = np.concatenate(all_residuals)
|
|
202
|
+
rms_landscape[j, i] = np.sqrt(np.mean(all_residuals**2))
|
|
203
|
+
|
|
204
|
+
interp_vals = utils.gaussian_interpolate(rms_landscape, factor=4, sigma=1.0)
|
|
205
|
+
ny, nx = interp_vals.shape
|
|
206
|
+
[rarange[0], rarange[-1], decrange[0], decrange[-1]]
|
|
207
|
+
ra_vals = np.linspace(rarange.min(), rarange.max(), nx)
|
|
208
|
+
dec_vals = np.linspace(decrange.min(), decrange.max(), ny)
|
|
209
|
+
ys, xs = utils.detect_local_minima(interp_vals)
|
|
210
|
+
ra_minima = ra_vals[xs]
|
|
211
|
+
dec_minima = dec_vals[ys]
|
|
212
|
+
|
|
213
|
+
ra_init = ra_minima
|
|
214
|
+
dec_init = dec_minima
|
|
161
215
|
|
|
162
216
|
H_key = next(
|
|
163
217
|
(f"H_{i}" for i in range(1, 7) if f"H_{i}" in shg1g2_params),
|
|
@@ -190,9 +244,26 @@ def get_fit_params(
|
|
|
190
244
|
model.append(SOCCA)
|
|
191
245
|
except Exception:
|
|
192
246
|
continue
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
247
|
+
try:
|
|
248
|
+
rms = np.array(rms)
|
|
249
|
+
SOCCA_opt = model[rms.argmin()]
|
|
250
|
+
if period_quality_flag:
|
|
251
|
+
try:
|
|
252
|
+
DeltaF1 = signal_peaks[1] - signal_peaks[2]
|
|
253
|
+
f_obs = 2 / period_sy
|
|
254
|
+
y_trumpet = utils.trumpet(
|
|
255
|
+
DeltaF1, 1, f_obs
|
|
256
|
+
)
|
|
257
|
+
alias_flag = (DeltaF1 - y_trumpet) * 100
|
|
258
|
+
if alias_flag < 1:
|
|
259
|
+
SOCCA_opt["Period_class"] = "true"
|
|
260
|
+
else:
|
|
261
|
+
SOCCA_opt["Period_class"] = "alias"
|
|
262
|
+
SOCCA_opt["Nbs"] = Nbs
|
|
263
|
+
except Exception:
|
|
264
|
+
SOCCA_opt["Period_class"] = "class_error"
|
|
265
|
+
except Exception:
|
|
266
|
+
SOCCA_opt = {"Failed at SOCCA inversion": 5}
|
|
196
267
|
return SOCCA_opt
|
|
197
268
|
else:
|
|
198
269
|
period_si_t, alt_period_si_t, _ = utils.estimate_sidereal_period(
|
|
@@ -254,14 +325,14 @@ def get_fit_params(
|
|
|
254
325
|
|
|
255
326
|
# Constrained Fit
|
|
256
327
|
Afit = estimate_sso_params(
|
|
257
|
-
data["cmred"].values[0]
|
|
258
|
-
data["csigmapsf"].values[0]
|
|
259
|
-
np.radians(data["Phase"].values[0]
|
|
260
|
-
data["cfid"].values[0]
|
|
261
|
-
ra=np.radians(data["ra"].values[0]
|
|
262
|
-
dec=np.radians(data["dec"].values[0]
|
|
263
|
-
jd=data["cjd"].values[0]
|
|
264
|
-
model="SSHG1G2",
|
|
328
|
+
data["cmred"].values[0],
|
|
329
|
+
data["csigmapsf"].values[0],
|
|
330
|
+
np.radians(data["Phase"].values[0]),
|
|
331
|
+
data["cfid"].values[0],
|
|
332
|
+
ra=np.radians(data["ra"].values[0]),
|
|
333
|
+
dec=np.radians(data["dec"].values[0]),
|
|
334
|
+
jd=data["cjd"].values[0],
|
|
335
|
+
model="SSHG1G2", # We should call this SOCCA
|
|
265
336
|
p0=p0,
|
|
266
337
|
)
|
|
267
338
|
return Afit
|
|
@@ -272,28 +343,28 @@ def get_fit_params(
|
|
|
272
343
|
if p0 is not None:
|
|
273
344
|
if terminator:
|
|
274
345
|
Afit = estimate_sso_params(
|
|
275
|
-
data["cmred"].values[0]
|
|
276
|
-
data["csigmapsf"].values[0]
|
|
277
|
-
np.radians(data["Phase"].values[0]
|
|
278
|
-
data["cfid"].values[0]
|
|
279
|
-
ra=np.radians(data["ra"].values[0]
|
|
280
|
-
dec=np.radians(data["dec"].values[0]
|
|
281
|
-
jd=data["cjd"].values[0]
|
|
346
|
+
data["cmred"].values[0],
|
|
347
|
+
data["csigmapsf"].values[0],
|
|
348
|
+
np.radians(data["Phase"].values[0]),
|
|
349
|
+
data["cfid"].values[0],
|
|
350
|
+
ra=np.radians(data["ra"].values[0]),
|
|
351
|
+
dec=np.radians(data["dec"].values[0]),
|
|
352
|
+
jd=data["cjd"].values[0],
|
|
282
353
|
model="SSHG1G2",
|
|
283
354
|
p0=p0,
|
|
284
355
|
terminator=terminator,
|
|
285
|
-
ra_s=np.radians(data["ra_s"].values[0]
|
|
286
|
-
dec_s=np.radians(data["dec_s"].values[0]
|
|
356
|
+
ra_s=np.radians(data["ra_s"].values[0]),
|
|
357
|
+
dec_s=np.radians(data["dec_s"].values[0]),
|
|
287
358
|
)
|
|
288
359
|
else:
|
|
289
360
|
Afit = estimate_sso_params(
|
|
290
|
-
data["cmred"].values[0]
|
|
291
|
-
data["csigmapsf"].values[0]
|
|
292
|
-
np.radians(data["Phase"].values[0]
|
|
293
|
-
data["cfid"].values[0]
|
|
294
|
-
ra=np.radians(data["ra"].values[0]
|
|
295
|
-
dec=np.radians(data["dec"].values[0]
|
|
296
|
-
jd=data["cjd"].values[0]
|
|
361
|
+
data["cmred"].values[0],
|
|
362
|
+
data["csigmapsf"].values[0],
|
|
363
|
+
np.radians(data["Phase"].values[0]),
|
|
364
|
+
data["cfid"].values[0],
|
|
365
|
+
ra=np.radians(data["ra"].values[0]),
|
|
366
|
+
dec=np.radians(data["dec"].values[0]),
|
|
367
|
+
jd=data["cjd"].values[0],
|
|
297
368
|
model="SSHG1G2",
|
|
298
369
|
p0=p0,
|
|
299
370
|
terminator=terminator,
|
|
@@ -418,3 +418,57 @@ def generate_initial_points(ra, dec, dec_shift=45):
|
|
|
418
418
|
ra_list.append(wrap_longitude(temp_ra + offset))
|
|
419
419
|
dec_list.append(shifted_dec)
|
|
420
420
|
return ra_list, dec_list
|
|
421
|
+
|
|
422
|
+
def gaussian_interpolate(data, factor=4, sigma=1.0):
|
|
423
|
+
"""
|
|
424
|
+
Reproduce matplotlib's `interpolation="gaussian"` effect.
|
|
425
|
+
factor : upsampling factor
|
|
426
|
+
sigma : Gaussian smoothing strength
|
|
427
|
+
"""
|
|
428
|
+
from scipy.ndimage import zoom, gaussian_filter
|
|
429
|
+
|
|
430
|
+
# Step 1: upsample (mimics interpolation grid)
|
|
431
|
+
up = zoom(data, factor, order=1) # bilinear before smoothing
|
|
432
|
+
|
|
433
|
+
# Step 2: apply gaussian smoothing
|
|
434
|
+
smoothed = gaussian_filter(up, sigma=sigma)
|
|
435
|
+
|
|
436
|
+
return smoothed
|
|
437
|
+
|
|
438
|
+
def detect_local_minima(arr):
|
|
439
|
+
import scipy.ndimage.filters as filters
|
|
440
|
+
import scipy.ndimage.morphology as morphology
|
|
441
|
+
|
|
442
|
+
# https://stackoverflow.com/questions/3684484/peak-detection-in-a-2d-array/3689710#3689710
|
|
443
|
+
# https://stackoverflow.com/questions/3986345/how-to-find-the-local-minima-of-a-smooth-multidimensional-array-in-numpy
|
|
444
|
+
"""
|
|
445
|
+
Takes an array and detects the troughs using the local maximum filter.
|
|
446
|
+
Returns a boolean mask of the troughs (i.e. 1 when
|
|
447
|
+
the pixel's value is the neighborhood maximum, 0 otherwise)
|
|
448
|
+
"""
|
|
449
|
+
neighborhood = morphology.generate_binary_structure(len(arr.shape),2)
|
|
450
|
+
|
|
451
|
+
footprint = np.ones((15, 15))
|
|
452
|
+
local_min = filters.minimum_filter(arr, footprint=footprint) == arr
|
|
453
|
+
|
|
454
|
+
background = (arr==0)
|
|
455
|
+
eroded_background = morphology.binary_erosion(
|
|
456
|
+
background, structure=neighborhood, border_value=1)
|
|
457
|
+
|
|
458
|
+
detected_minima = local_min ^ eroded_background
|
|
459
|
+
return np.where(detected_minima)
|
|
460
|
+
|
|
461
|
+
def trumpet(peak_diff_1, f_feat, f_obs):
|
|
462
|
+
if peak_diff_1 > 0 and f_obs > f_feat:
|
|
463
|
+
return 2 * f_feat
|
|
464
|
+
|
|
465
|
+
elif peak_diff_1 < 0 and f_obs < f_feat:
|
|
466
|
+
return -2 * f_obs
|
|
467
|
+
|
|
468
|
+
elif peak_diff_1 > 0 and f_obs < f_feat:
|
|
469
|
+
return 2 * f_obs
|
|
470
|
+
|
|
471
|
+
elif peak_diff_1 < 0 and f_obs > f_feat:
|
|
472
|
+
return -2 * f_feat
|
|
473
|
+
|
|
474
|
+
return 0.0
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
asteroid_spinprops/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
2
|
asteroid_spinprops/ssolib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
asteroid_spinprops/ssolib/dataprep.py,sha256=7PfkVNpLjPERkhr8dZQfN8haFCFmoeXB3fWuTSBLVuc,7848
|
|
4
|
-
asteroid_spinprops/ssolib/modelfit.py,sha256
|
|
4
|
+
asteroid_spinprops/ssolib/modelfit.py,sha256=-EA0Ha0fUh9CPqqh_f9kuoZWTVmSP-XMITUHJ3OsCy4,19698
|
|
5
5
|
asteroid_spinprops/ssolib/periodest.py,sha256=kDWEB0fPRd5paqxNjJmigsocrAd4rydYLwxNh5sMV2U,13216
|
|
6
6
|
asteroid_spinprops/ssolib/ssptools.py,sha256=DlSgYtXenztRAtEV9d4itzp5OZMjkbXkW2yZ_Qumu4U,4490
|
|
7
|
-
asteroid_spinprops/ssolib/utils.py,sha256=
|
|
8
|
-
asteroid_spinprops-1.
|
|
9
|
-
asteroid_spinprops-1.
|
|
10
|
-
asteroid_spinprops-1.
|
|
7
|
+
asteroid_spinprops/ssolib/utils.py,sha256=Cv1TYSaUsWLbeQyrdqou6YnrPv4VijDP6GB9HAXtzKA,12902
|
|
8
|
+
asteroid_spinprops-1.2.0.dist-info/METADATA,sha256=6j5gC6L3ptLRtFfAQIDUQfuY5y6LmLGOiao0VsMdi48,5635
|
|
9
|
+
asteroid_spinprops-1.2.0.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
10
|
+
asteroid_spinprops-1.2.0.dist-info/RECORD,,
|
|
File without changes
|