asteroid_spinprops 1.1.2__py3-none-any.whl → 1.2.1__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 +74 -5
- asteroid_spinprops/ssolib/periodest.py +4 -3
- asteroid_spinprops/ssolib/utils.py +70 -0
- {asteroid_spinprops-1.1.2.dist-info → asteroid_spinprops-1.2.1.dist-info}/METADATA +26 -11
- asteroid_spinprops-1.2.1.dist-info/RECORD +10 -0
- asteroid_spinprops-1.1.2.dist-info/RECORD +0 -10
- {asteroid_spinprops-1.1.2.dist-info → asteroid_spinprops-1.2.1.dist-info}/WHEEL +0 -0
|
@@ -2,6 +2,7 @@ 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,
|
|
@@ -21,6 +22,7 @@ def get_fit_params(
|
|
|
21
22
|
p0=None,
|
|
22
23
|
alt_spin=False,
|
|
23
24
|
period_in=None,
|
|
25
|
+
period_quality_flag=False,
|
|
24
26
|
terminator=False,
|
|
25
27
|
):
|
|
26
28
|
"""
|
|
@@ -49,13 +51,16 @@ def get_fit_params(
|
|
|
49
51
|
period_blind : bool, optional
|
|
50
52
|
If True, perform a small grid search over initial periods. Default True.
|
|
51
53
|
pole_blind : bool, optional
|
|
52
|
-
If True, perform a grid search over initial poles. Default True.
|
|
54
|
+
If True, perform a grid search over 12 initial poles all over a sphere. Default True.
|
|
55
|
+
If False, produce the sHG1G2 rms error landscape and initialize SOCCA poles on its local minima
|
|
53
56
|
p0 : list, optional
|
|
54
57
|
Initial guess parameters for the fit. Required if `shg1g2_constrained=False`.
|
|
55
58
|
alt_spin : bool, optional
|
|
56
59
|
For SOCCA constrained fits, use the antipodal spin solution. Default False.
|
|
57
60
|
period_in : float, optional
|
|
58
61
|
Input synodic period (days) to override automatic estimation. Default None.
|
|
62
|
+
period_quality_flag : bool, optional
|
|
63
|
+
Provide bootstrap score, alias/true (0/1) flags and period fit rms for the period estimates
|
|
59
64
|
terminator : bool, optional
|
|
60
65
|
If True, include self-shading in the fit. Default False.
|
|
61
66
|
|
|
@@ -128,6 +133,10 @@ def get_fit_params(
|
|
|
128
133
|
residuals_dataframe, p_min=pmin, p_max=pmax, k_free=True
|
|
129
134
|
)
|
|
130
135
|
)
|
|
136
|
+
if period_quality_flag:
|
|
137
|
+
_, Nbs = periodest.perform_residual_resampling(
|
|
138
|
+
resid_df=residuals_dataframe, p_min=pmin, p_max=pmax, k=int(k_val)
|
|
139
|
+
)
|
|
131
140
|
except KeyError:
|
|
132
141
|
# If more than 10 terms are required switch to fast rotator:
|
|
133
142
|
pmin, pmax = 5e-3, 5e-2
|
|
@@ -137,6 +146,10 @@ def get_fit_params(
|
|
|
137
146
|
residuals_dataframe, p_min=pmin, p_max=pmax, k_free=True
|
|
138
147
|
)
|
|
139
148
|
)
|
|
149
|
+
if period_quality_flag:
|
|
150
|
+
_, Nbs = periodest.perform_residual_resampling(
|
|
151
|
+
resid_df=residuals_dataframe, p_min=pmin, p_max=pmax, k=int(k_val)
|
|
152
|
+
)
|
|
140
153
|
except Exception:
|
|
141
154
|
SOCCA_opt = {"Failed at period search after": 4}
|
|
142
155
|
return SOCCA_opt
|
|
@@ -153,16 +166,55 @@ def get_fit_params(
|
|
|
153
166
|
)
|
|
154
167
|
|
|
155
168
|
ra0, dec0 = shg1g2_params["alpha0"], shg1g2_params["delta0"]
|
|
156
|
-
|
|
169
|
+
|
|
157
170
|
if pole_blind is True:
|
|
158
171
|
ra_init, dec_init = utils.generate_initial_points(
|
|
159
172
|
ra0, dec0, dec_shift=45
|
|
160
173
|
)
|
|
161
174
|
|
|
162
175
|
else:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
176
|
+
rarange = np.arange(0, 360, 10)
|
|
177
|
+
decrange = np.arange(-90, 90, 5)
|
|
178
|
+
rms_landscape = np.ones(shape=(len(rarange), len(decrange)))
|
|
179
|
+
|
|
180
|
+
for i, ra0 in enumerate(rarange):
|
|
181
|
+
for j, dec0 in enumerate(decrange):
|
|
182
|
+
|
|
183
|
+
all_residuals = []
|
|
184
|
+
|
|
185
|
+
for ff in np.unique(data["cfid"].values[0]):
|
|
186
|
+
cond_ff = data["cfid"].values[0] == ff
|
|
187
|
+
|
|
188
|
+
pha = [np.radians(data["Phase"].values[0][cond_ff]),
|
|
189
|
+
np.radians(data["ra"].values[0][cond_ff]),
|
|
190
|
+
np.radians(data["dec"].values[0][cond_ff])]
|
|
191
|
+
|
|
192
|
+
H = shg1g2_params[f"H_{ff}"]
|
|
193
|
+
G1 = shg1g2_params[f"G1_{ff}"]
|
|
194
|
+
G2 = shg1g2_params[f"G2_{ff}"]
|
|
195
|
+
R = shg1g2_params["R"]
|
|
196
|
+
|
|
197
|
+
C = func_hg1g2_with_spin(pha, H, G1, G2, R,
|
|
198
|
+
np.radians(ra0), np.radians(dec0))
|
|
199
|
+
|
|
200
|
+
Obs = data["cmred"].values[0][cond_ff]
|
|
201
|
+
|
|
202
|
+
all_residuals.append(Obs - C)
|
|
203
|
+
|
|
204
|
+
all_residuals = np.concatenate(all_residuals)
|
|
205
|
+
rms_landscape[j, i] = np.sqrt(np.mean(all_residuals**2))
|
|
206
|
+
|
|
207
|
+
interp_vals = utils.gaussian_interpolate(rms_landscape, factor=4, sigma=1.0)
|
|
208
|
+
ny, nx = interp_vals.shape
|
|
209
|
+
[rarange[0], rarange[-1], decrange[0], decrange[-1]]
|
|
210
|
+
ra_vals = np.linspace(rarange.min(), rarange.max(), nx)
|
|
211
|
+
dec_vals = np.linspace(decrange.min(), decrange.max(), ny)
|
|
212
|
+
ys, xs = utils.detect_local_minima(interp_vals)
|
|
213
|
+
ra_minima = ra_vals[xs]
|
|
214
|
+
dec_minima = dec_vals[ys]
|
|
215
|
+
|
|
216
|
+
ra_init = ra_minima
|
|
217
|
+
dec_init = dec_minima
|
|
166
218
|
|
|
167
219
|
H_key = next(
|
|
168
220
|
(f"H_{i}" for i in range(1, 7) if f"H_{i}" in shg1g2_params),
|
|
@@ -198,6 +250,23 @@ def get_fit_params(
|
|
|
198
250
|
try:
|
|
199
251
|
rms = np.array(rms)
|
|
200
252
|
SOCCA_opt = model[rms.argmin()]
|
|
253
|
+
if period_quality_flag:
|
|
254
|
+
try:
|
|
255
|
+
DeltaF1 = signal_peaks[1] - signal_peaks[2]
|
|
256
|
+
f_obs = 2 / period_sy
|
|
257
|
+
y_trumpet = utils.trumpet(
|
|
258
|
+
DeltaF1, 1, f_obs
|
|
259
|
+
)
|
|
260
|
+
alias_flag = (DeltaF1 - y_trumpet) * 100
|
|
261
|
+
if alias_flag < 1:
|
|
262
|
+
SOCCA_opt["Period_class"] = 1 # True
|
|
263
|
+
else:
|
|
264
|
+
SOCCA_opt["Period_class"] = 0 # Alias
|
|
265
|
+
SOCCA_opt["Nbs"] = Nbs
|
|
266
|
+
except Exception:
|
|
267
|
+
SOCCA_opt["Period_class"] = -1 # Classification error
|
|
268
|
+
SOCCA_opt["prms"] = p_rms
|
|
269
|
+
SOCCA_opt["k_terms"] = k_val
|
|
201
270
|
except Exception:
|
|
202
271
|
SOCCA_opt = {"Failed at SOCCA inversion": 5}
|
|
203
272
|
return SOCCA_opt
|
|
@@ -123,7 +123,7 @@ def get_period_estimate(residuals_dataframe, p_min=0.03, p_max=2):
|
|
|
123
123
|
|
|
124
124
|
|
|
125
125
|
def get_multiterm_period_estimate(
|
|
126
|
-
residuals_dataframe, p_min=0.03, p_max=2, k_free=True, k_val=None
|
|
126
|
+
residuals_dataframe, p_min=0.03, p_max=2, k_free=True, k_val=None, k_max=4
|
|
127
127
|
):
|
|
128
128
|
"""
|
|
129
129
|
Estimate the period of a multiband time series using a multiband Lomb-Scargle model.
|
|
@@ -148,7 +148,8 @@ def get_multiterm_period_estimate(
|
|
|
148
148
|
If True, automatically scan multiple base-term complexities to choose optimal model. Default True.
|
|
149
149
|
k_val : int, optional
|
|
150
150
|
Fixed number of base terms to use if `k_free=False`. Default None.
|
|
151
|
-
|
|
151
|
+
k_max : int
|
|
152
|
+
Maximum number of terms to use for the multiterm LS periodogram. Default 4
|
|
152
153
|
Returns
|
|
153
154
|
-------
|
|
154
155
|
tuple
|
|
@@ -175,7 +176,7 @@ def get_multiterm_period_estimate(
|
|
|
175
176
|
residuals = np.zeros(len(residuals_dataframe["filters"].values))
|
|
176
177
|
bands = np.unique(residuals_dataframe["filters"].values)
|
|
177
178
|
if k_free:
|
|
178
|
-
for k in range(1,
|
|
179
|
+
for k in range(1, k_max + 1):
|
|
179
180
|
model = LombScargleMultiband(
|
|
180
181
|
residuals_dataframe["jd"].values,
|
|
181
182
|
residuals_dataframe["residuals"].values,
|
|
@@ -418,3 +418,73 @@ 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
|
+
"""
|
|
463
|
+
Implementation of the alias/true flagging algorithm.
|
|
464
|
+
|
|
465
|
+
Parameters
|
|
466
|
+
----------
|
|
467
|
+
peak_diff_1 : float
|
|
468
|
+
The difference between the 2nd and 3rd highest peaks of the periodogram
|
|
469
|
+
f_feat : float
|
|
470
|
+
The feature frequency.
|
|
471
|
+
f_obs : float
|
|
472
|
+
The 1st highest peak if the periodogram
|
|
473
|
+
Returns
|
|
474
|
+
-------
|
|
475
|
+
The assumed true frequency peak difference value if the peak is true,
|
|
476
|
+
otherwise zero.
|
|
477
|
+
"""
|
|
478
|
+
if peak_diff_1 > 0 and f_obs > f_feat:
|
|
479
|
+
return 2 * f_feat
|
|
480
|
+
|
|
481
|
+
elif peak_diff_1 < 0 and f_obs < f_feat:
|
|
482
|
+
return -2 * f_obs
|
|
483
|
+
|
|
484
|
+
elif peak_diff_1 > 0 and f_obs < f_feat:
|
|
485
|
+
return 2 * f_obs
|
|
486
|
+
|
|
487
|
+
elif peak_diff_1 < 0 and f_obs > f_feat:
|
|
488
|
+
return -2 * f_feat
|
|
489
|
+
|
|
490
|
+
return 0.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: asteroid_spinprops
|
|
3
|
-
Version: 1.1
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Summary: Collection of tools used for fitting sHG1G2 and SOCCA photometric models to sparse asteroid photometry
|
|
5
5
|
License: MIT
|
|
6
6
|
Author: Odysseas
|
|
@@ -111,13 +111,13 @@ pdf["jd_ltc"] = pdf["cjd"] - pdf["Observer_SSO_distance_column"] / c_kmday # li
|
|
|
111
111
|
|
|
112
112
|
Your input DataFrame must therefore include:
|
|
113
113
|
|
|
114
|
-
- time of observation
|
|
115
|
-
- PSF magnitude and uncertainty
|
|
116
|
-
- filter ID
|
|
117
|
-
- RA, Dec
|
|
118
|
-
- phase angle
|
|
119
|
-
- heliocentric distance
|
|
120
|
-
- observer-centric distance
|
|
114
|
+
- time of observation (JD)
|
|
115
|
+
- PSF magnitude and uncertainty
|
|
116
|
+
- filter ID
|
|
117
|
+
- RA, Dec (Degrees)
|
|
118
|
+
- phase angle
|
|
119
|
+
- heliocentric distance (AU)
|
|
120
|
+
- observer-centric distance (AU)
|
|
121
121
|
|
|
122
122
|
The preprocessing step renames these fields to the Fink schema, computes reduced magnitudes, and applies the light-time correction to the observation timestamps.
|
|
123
123
|
|
|
@@ -138,10 +138,23 @@ numeric_filter = inv + 1
|
|
|
138
138
|
pdf_s["cfid"].values[0] = numeric_filter
|
|
139
139
|
|
|
140
140
|
# --- Data cleaning and filtering ---
|
|
141
|
-
clean_data, errorbar_rejects = dataprep.errorbar_filtering(data=pdf_s, mlimit=0.7928)
|
|
141
|
+
clean_data, errorbar_rejects = dataprep.errorbar_filtering(data=pdf_s, mlimit=0.7928) # mag limit from the LCDB
|
|
142
142
|
clean_data, projection_rejects = dataprep.projection_filtering(data=clean_data)
|
|
143
143
|
clean_data, iterative_rejects = dataprep.iterative_filtering(data=clean_data)
|
|
144
144
|
|
|
145
|
+
# --- Fit SOCCA ---
|
|
146
|
+
SOCCA_params = modelfit.get_fit_params(
|
|
147
|
+
data=clean_data,
|
|
148
|
+
flavor="SOCCA",
|
|
149
|
+
shg1g2_constrained=True,
|
|
150
|
+
pole_blind=False,
|
|
151
|
+
period_blind=True,
|
|
152
|
+
period_in=None,
|
|
153
|
+
period_quality_flag=True
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
## --- Or step-by-step --- ##
|
|
145
158
|
# --- Fit SHG1G2 model ---
|
|
146
159
|
shg1g2_params = modelfit.get_fit_params(
|
|
147
160
|
data=clean_data,
|
|
@@ -167,13 +180,15 @@ _, Nbs = periodest.perform_residual_resampling(
|
|
|
167
180
|
k=int(k_val)
|
|
168
181
|
)
|
|
169
182
|
|
|
170
|
-
# --- Fit
|
|
183
|
+
# --- Fit SOCCA model ---
|
|
171
184
|
SOCCA_params = modelfit.get_fit_params(
|
|
172
185
|
data=clean_data,
|
|
173
186
|
flavor="SSHG1G2",
|
|
174
187
|
shg1g2_constrained=True,
|
|
175
|
-
|
|
188
|
+
period_blind=False,
|
|
189
|
+
pole_blind=False,
|
|
176
190
|
period_in=p_in,
|
|
191
|
+
period_quality_flag=False
|
|
177
192
|
)
|
|
178
193
|
```
|
|
179
194
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
asteroid_spinprops/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
asteroid_spinprops/ssolib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
+
asteroid_spinprops/ssolib/dataprep.py,sha256=7PfkVNpLjPERkhr8dZQfN8haFCFmoeXB3fWuTSBLVuc,7848
|
|
4
|
+
asteroid_spinprops/ssolib/modelfit.py,sha256=DIp033AxpLKwFQRrRPnnFv3nHWiZ06YsfBnMv94ANvo,20084
|
|
5
|
+
asteroid_spinprops/ssolib/periodest.py,sha256=iql2HhpxN6dFwRmNchomv2m4M_JPULNrHLz5fvc5zYY,13330
|
|
6
|
+
asteroid_spinprops/ssolib/ssptools.py,sha256=DlSgYtXenztRAtEV9d4itzp5OZMjkbXkW2yZ_Qumu4U,4490
|
|
7
|
+
asteroid_spinprops/ssolib/utils.py,sha256=Na0EAgCCeBrvY3-KwfU7PuaoDGLjw5jGP9oi19lY490,13344
|
|
8
|
+
asteroid_spinprops-1.2.1.dist-info/METADATA,sha256=0Zt7MUH5q6uCMKlAOAkK6I7b1_wFKIsV1Qk7Cw4u0_c,6143
|
|
9
|
+
asteroid_spinprops-1.2.1.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
10
|
+
asteroid_spinprops-1.2.1.dist-info/RECORD,,
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
asteroid_spinprops/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
asteroid_spinprops/ssolib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
|
-
asteroid_spinprops/ssolib/dataprep.py,sha256=7PfkVNpLjPERkhr8dZQfN8haFCFmoeXB3fWuTSBLVuc,7848
|
|
4
|
-
asteroid_spinprops/ssolib/modelfit.py,sha256=CM2fc_OKPrajK3Dadiny4JEkY1KuwCXqoarTx8SnYpM,16473
|
|
5
|
-
asteroid_spinprops/ssolib/periodest.py,sha256=kDWEB0fPRd5paqxNjJmigsocrAd4rydYLwxNh5sMV2U,13216
|
|
6
|
-
asteroid_spinprops/ssolib/ssptools.py,sha256=DlSgYtXenztRAtEV9d4itzp5OZMjkbXkW2yZ_Qumu4U,4490
|
|
7
|
-
asteroid_spinprops/ssolib/utils.py,sha256=BUeIP0DZ45tg5qAQML8LBM8Gbj1vn2d3PGzvgQvfq_I,11094
|
|
8
|
-
asteroid_spinprops-1.1.2.dist-info/METADATA,sha256=Ea_ijdxK1WerLh0BSULWUQp3sn054ghbk35Chg_Ggz4,5635
|
|
9
|
-
asteroid_spinprops-1.1.2.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
10
|
-
asteroid_spinprops-1.1.2.dist-info/RECORD,,
|
|
File without changes
|