vsh-tools 0.1.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.
- vsh_tools/basis.py +169 -0
- vsh_tools/bootstrap.py +185 -0
- vsh_tools/deprecated/__init__.py +8 -0
- vsh_tools/deprecated/auto_elim.py +276 -0
- vsh_tools/deprecated/bootstrap_sample.py +275 -0
- vsh_tools/deprecated/generate_test_data.py +67 -0
- vsh_tools/deprecated/glide_func.py +446 -0
- vsh_tools/deprecated/implementation_comparison.py +100 -0
- vsh_tools/deprecated/matrix_calc.py +817 -0
- vsh_tools/deprecated/pmt_convert.py +406 -0
- vsh_tools/deprecated/rgq_func.py +110 -0
- vsh_tools/deprecated/ssh_expansion.py +101 -0
- vsh_tools/deprecated/stats_func.py +261 -0
- vsh_tools/deprecated/test.py +40 -0
- vsh_tools/deprecated/tune_para.py +108 -0
- vsh_tools/deprecated/vec_sph_harm.py +178 -0
- vsh_tools/deprecated/verify_code.py +71 -0
- vsh_tools/deprecated/vsh_aux_info.py +51 -0
- vsh_tools/deprecated/vsh_expansion.py +162 -0
- vsh_tools/deprecated/vsh_expension.py +165 -0
- vsh_tools/deprecated/vsh_fit.py +491 -0
- vsh_tools/deprecated/vsh_power.py +171 -0
- vsh_tools/deprecated/vsh_significance.py +339 -0
- vsh_tools/deprecated/vsh_stat.py +126 -0
- vsh_tools/deprecated/vsh_stats.py +126 -0
- vsh_tools/diagnostics.py +45 -0
- vsh_tools/fit.py +351 -0
- vsh_tools/glide.py +209 -0
- vsh_tools/legacy.py +787 -0
- vsh_tools/linalg.py +448 -0
- vsh_tools/metrics.py +166 -0
- vsh_tools/outliers.py +199 -0
- vsh_tools/power.py +195 -0
- vsh_tools/rgq.py +363 -0
- vsh_tools/scalar_basis.py +121 -0
- vsh_tools/significance.py +233 -0
- vsh_tools/stats.py +173 -0
- vsh_tools/transforms.py +357 -0
- vsh_tools-0.1.0.dist-info/METADATA +73 -0
- vsh_tools-0.1.0.dist-info/RECORD +43 -0
- vsh_tools-0.1.0.dist-info/WHEEL +5 -0
- vsh_tools-0.1.0.dist-info/licenses/LICENSE +21 -0
- vsh_tools-0.1.0.dist-info/top_level.txt +1 -0
vsh_tools/basis.py
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File name: basis.py
|
|
4
|
+
"""
|
|
5
|
+
Vector Spherical Harmonics (VSH) basis projections.
|
|
6
|
+
|
|
7
|
+
Reference
|
|
8
|
+
---------
|
|
9
|
+
F. Mignard & S. Klioner, A&A 547, A59 (2012).
|
|
10
|
+
DOI: 10.1051/0004-6361/201219927.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
from numpy import sqrt, pi, sin, cos
|
|
17
|
+
from math import factorial
|
|
18
|
+
from typing import Tuple
|
|
19
|
+
|
|
20
|
+
__all__ = ["vec_sph_harm_proj", "real_vec_sph_harm_proj"]
|
|
21
|
+
|
|
22
|
+
# ----------------------------- FUNCTIONS -----------------------------
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def vec_sph_harm_proj(
|
|
26
|
+
l_max: int,
|
|
27
|
+
ra: np.ndarray,
|
|
28
|
+
dc: np.ndarray,
|
|
29
|
+
sph_type: str = "T",
|
|
30
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
31
|
+
"""
|
|
32
|
+
Compute complex VSH projections T_lm (or S_lm) onto (e_ra, e_dec) at (ra, dec).
|
|
33
|
+
|
|
34
|
+
Parameters
|
|
35
|
+
----------
|
|
36
|
+
l_max : int
|
|
37
|
+
Maximum degree of the harmonics (>= 1).
|
|
38
|
+
ra, dc : array_like of float (radians)
|
|
39
|
+
Right ascension and declination arrays of equal length.
|
|
40
|
+
sph_type : {"T","S"}, optional
|
|
41
|
+
Return toroidal ("T") or spheroidal ("S") projections. Default "T".
|
|
42
|
+
|
|
43
|
+
Returns
|
|
44
|
+
-------
|
|
45
|
+
T_ra_mat, T_dc_mat : complex ndarray, shape (l_max+1, l_max+1, N)
|
|
46
|
+
For each (l, m), the projection of the vector harmonic onto e_ra and e_dec.
|
|
47
|
+
|
|
48
|
+
Notes
|
|
49
|
+
-----
|
|
50
|
+
- m runs 0..l; entries where 0 <= m <= l are filled, others remain zero.
|
|
51
|
+
- S_lm is obtained from T_lm via (S_ra, S_dc) = (-T_dc, T_ra).
|
|
52
|
+
"""
|
|
53
|
+
|
|
54
|
+
# Basic checks / normalization
|
|
55
|
+
try:
|
|
56
|
+
l_max = int(l_max)
|
|
57
|
+
except Exception as e:
|
|
58
|
+
raise ValueError("l_max must be convertible to int.") from e
|
|
59
|
+
if l_max < 1:
|
|
60
|
+
raise ValueError("l_max must be >= 1.")
|
|
61
|
+
|
|
62
|
+
ra = np.asarray(ra, dtype=float)
|
|
63
|
+
dc = np.asarray(dc, dtype=float)
|
|
64
|
+
if ra.shape != dc.shape:
|
|
65
|
+
raise ValueError("ra and dc must have the same shape.")
|
|
66
|
+
N = ra.size
|
|
67
|
+
|
|
68
|
+
x = sin(dc)
|
|
69
|
+
# Guard against tiny negative values from rounding (near poles)
|
|
70
|
+
fac_pol = np.sqrt(np.clip(1.0 - x * x, 0.0, 1.0))
|
|
71
|
+
|
|
72
|
+
# A_lm and B_lm recursion buffers
|
|
73
|
+
A_mat = np.zeros((l_max + 1, l_max + 1, N), dtype=float)
|
|
74
|
+
B_mat = np.zeros((l_max + 1, l_max + 1, N), dtype=float)
|
|
75
|
+
B_mat[1, 1, :] = 1.0 # seed
|
|
76
|
+
B_mat[1, 0, :] = 0.0
|
|
77
|
+
|
|
78
|
+
# Output buffers (complex)
|
|
79
|
+
T_ra_mat = np.zeros((l_max + 1, l_max + 1, N), dtype=complex)
|
|
80
|
+
T_dc_mat = np.zeros((l_max + 1, l_max + 1, N), dtype=complex)
|
|
81
|
+
|
|
82
|
+
# Build B_lm (Eqs. B.13–B.17)
|
|
83
|
+
for l in range(2, l_max + 1):
|
|
84
|
+
for m in range(l, -1, -1): # m = l, l-1, ..., 0
|
|
85
|
+
if m == 0:
|
|
86
|
+
B_mat[l, 0, :] = 0.0
|
|
87
|
+
elif l == m:
|
|
88
|
+
# (2m-1) * m/(m-1) * sqrt(1-x^2) * B_{m-1,m-1}
|
|
89
|
+
B_mat[l, m, :] = fac_pol * \
|
|
90
|
+
(2 * m - 1) * (m / (m - 1)) * B_mat[m - 1, m - 1, :]
|
|
91
|
+
elif l == m + 1:
|
|
92
|
+
# (2m+1) * x * B_{m,m}
|
|
93
|
+
B_mat[l, m, :] = (2 * m + 1) * x * B_mat[m, m, :]
|
|
94
|
+
else:
|
|
95
|
+
# ((2l-1)x B_{l-1,m} - (l-1+m) B_{l-2,m}) / (l-m)
|
|
96
|
+
B_mat[l, m, :] = ((2 * l - 1) * x * B_mat[l - 1, m, :] -
|
|
97
|
+
(l - 1 + m) * B_mat[l - 2, m, :]) / (l - m)
|
|
98
|
+
|
|
99
|
+
# Build A_lm (Eqs. B.18–B.19)
|
|
100
|
+
for l in range(1, l_max + 1):
|
|
101
|
+
for m in range(0, l + 1):
|
|
102
|
+
if m == 0:
|
|
103
|
+
A_mat[l, 0, :] = fac_pol * B_mat[l, 1, :]
|
|
104
|
+
else:
|
|
105
|
+
# (-x*l*B_{l,m} + (l+m)B_{l-1,m}) / m
|
|
106
|
+
A_mat[l, m, :] = (-x * l * B_mat[l, m, :] +
|
|
107
|
+
(l + m) * B_mat[l - 1, m, :]) / m
|
|
108
|
+
|
|
109
|
+
# Project to e_ra, e_dec (Eqs. B.9–B.10)
|
|
110
|
+
for l in range(1, l_max + 1):
|
|
111
|
+
for m in range(0, l + 1):
|
|
112
|
+
# Normalization coefficient
|
|
113
|
+
c = (2 * l + 1) / (l * (l + 1)) / (4 * pi) * \
|
|
114
|
+
factorial(l - m) / factorial(l + m)
|
|
115
|
+
c = (-1) ** m * sqrt(c) * (cos(m * ra) + 1j * sin(m * ra))
|
|
116
|
+
|
|
117
|
+
T_ra_mat[l, m, :] = c * A_mat[l, m, :]
|
|
118
|
+
T_dc_mat[l, m, :] = c * B_mat[l, m, :] * (-1j)
|
|
119
|
+
|
|
120
|
+
if sph_type == "T":
|
|
121
|
+
return T_ra_mat, T_dc_mat
|
|
122
|
+
elif sph_type == "S":
|
|
123
|
+
# S = (-T_dc, T_ra)
|
|
124
|
+
return -T_dc_mat, T_ra_mat
|
|
125
|
+
else:
|
|
126
|
+
raise ValueError("sph_type must be 'T' or 'S'.")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def real_vec_sph_harm_proj(
|
|
130
|
+
l: int,
|
|
131
|
+
m: int,
|
|
132
|
+
T_ra_mat: np.ndarray,
|
|
133
|
+
T_dc_mat: np.ndarray,
|
|
134
|
+
) -> Tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
|
|
135
|
+
"""
|
|
136
|
+
Real-valued VSH components for given (l, m) from complex T_lm projections.
|
|
137
|
+
|
|
138
|
+
Parameters
|
|
139
|
+
----------
|
|
140
|
+
l, m : int
|
|
141
|
+
Degree and order (with 0 <= m <= l).
|
|
142
|
+
T_ra_mat, T_dc_mat : complex ndarray
|
|
143
|
+
Outputs of `vec_sph_harm_proj(...)`.
|
|
144
|
+
|
|
145
|
+
Returns
|
|
146
|
+
-------
|
|
147
|
+
T_ra_r, T_dc_r, T_ra_i, T_dc_i : float ndarray
|
|
148
|
+
Real and imaginary parts (with the conventional sign: imag parts negated
|
|
149
|
+
compared to the raw complex imag) as used in real expansions.
|
|
150
|
+
If m == 0, returns (real(T_ra), real(T_dc), zeros, zeros).
|
|
151
|
+
"""
|
|
152
|
+
if not (0 <= m <= l):
|
|
153
|
+
raise ValueError("Require 0 <= m <= l.")
|
|
154
|
+
|
|
155
|
+
T_ra = T_ra_mat[l, m, :]
|
|
156
|
+
T_dc = T_dc_mat[l, m, :]
|
|
157
|
+
|
|
158
|
+
if m == 0:
|
|
159
|
+
# Only the "real" component is used for m = 0
|
|
160
|
+
return np.real(T_ra), np.real(T_dc), np.zeros_like(T_ra, dtype=float), np.zeros_like(T_dc, dtype=float)
|
|
161
|
+
|
|
162
|
+
# For m > 0, real basis uses:
|
|
163
|
+
# real part unchanged, imag part with opposite sign and factor 2
|
|
164
|
+
T_ra_r = 2.0 * np.real(T_ra)
|
|
165
|
+
T_dc_r = 2.0 * np.real(T_dc)
|
|
166
|
+
T_ra_i = -2.0 * np.imag(T_ra)
|
|
167
|
+
T_dc_i = -2.0 * np.imag(T_dc)
|
|
168
|
+
|
|
169
|
+
return T_ra_r, T_dc_r, T_ra_i, T_dc_i
|
vsh_tools/bootstrap.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File name: bootstrap.py
|
|
4
|
+
"""
|
|
5
|
+
Nonparametric bootstrap for VSH parameter uncertainties.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import numpy as np
|
|
11
|
+
|
|
12
|
+
from .outliers import extract_data
|
|
13
|
+
from .linalg import nor_eq_sol
|
|
14
|
+
from .transforms import convert_ts_to_rgq
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# ----------------------------- utilities -----------------------------
|
|
18
|
+
def bs_freq(x_or_n=None) -> int:
|
|
19
|
+
"""
|
|
20
|
+
Bootstrap resampling frequency N ≈ n * ln(n)^2.
|
|
21
|
+
|
|
22
|
+
Accepts either an array (length n) or an integer n.
|
|
23
|
+
Falls back to N=1000 if None given.
|
|
24
|
+
"""
|
|
25
|
+
print("Decide the frequency of bootstrap resamplings")
|
|
26
|
+
if x_or_n is None:
|
|
27
|
+
print("Since no sample is given, using the default value (1000).")
|
|
28
|
+
return 1000
|
|
29
|
+
|
|
30
|
+
n = int(x_or_n if np.isscalar(x_or_n) else len(x_or_n))
|
|
31
|
+
if n <= 1:
|
|
32
|
+
return 1000
|
|
33
|
+
N = int(n * (np.log(n) ** 2))
|
|
34
|
+
print(f"Sample size n={n}, resampling frequency N ≈ n * ln(n)^2 = {N}")
|
|
35
|
+
return max(N, 1)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def bs_formal_error(x, xi):
|
|
39
|
+
"""
|
|
40
|
+
Estimate mean, std of bootstrap draws, and RMS around the original x.
|
|
41
|
+
"""
|
|
42
|
+
xi = np.asarray(xi, dtype=float)
|
|
43
|
+
xi_mean = float(np.mean(xi))
|
|
44
|
+
xi_std = float(np.std(xi, ddof=1)) if xi.size > 1 else 0.0
|
|
45
|
+
denom = max(xi.size - 1, 1)
|
|
46
|
+
xi_rms = float(np.sqrt(np.sum((xi - x) ** 2) / denom))
|
|
47
|
+
return xi_mean, xi_std, xi_rms
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def bs_resampling(X, Y, samp_size=None):
|
|
51
|
+
"""
|
|
52
|
+
Bootstrap resample paired arrays X, Y with replacement.
|
|
53
|
+
"""
|
|
54
|
+
X = np.asarray(X)
|
|
55
|
+
Y = np.asarray(Y)
|
|
56
|
+
if X.shape[0] != Y.shape[0]:
|
|
57
|
+
raise ValueError("X and Y must have the same length.")
|
|
58
|
+
n = X.shape[0]
|
|
59
|
+
m = int(n if samp_size is None else samp_size)
|
|
60
|
+
idx = np.random.choice(n, size=m, replace=True)
|
|
61
|
+
return X[idx], Y[idx]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def bs_resampling_indx(X, samp_size=None):
|
|
65
|
+
"""
|
|
66
|
+
Return bootstrap indices for X (size given or len(X)), with replacement.
|
|
67
|
+
"""
|
|
68
|
+
n = len(X)
|
|
69
|
+
m = int(n if samp_size is None else samp_size)
|
|
70
|
+
return np.random.choice(n, size=m, replace=True)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# ----------------------------- main -----------------------------
|
|
74
|
+
def bootstrap_resample_4_err(
|
|
75
|
+
mask,
|
|
76
|
+
dra,
|
|
77
|
+
ddc,
|
|
78
|
+
dra_err,
|
|
79
|
+
ddc_err,
|
|
80
|
+
ra_rad,
|
|
81
|
+
dc_rad,
|
|
82
|
+
ra_dc_cor,
|
|
83
|
+
l_max,
|
|
84
|
+
fit_type,
|
|
85
|
+
num_iter,
|
|
86
|
+
output,
|
|
87
|
+
):
|
|
88
|
+
"""
|
|
89
|
+
Estimate formal uncertainty via nonparametric bootstrap.
|
|
90
|
+
|
|
91
|
+
Returns
|
|
92
|
+
-------
|
|
93
|
+
output : dict
|
|
94
|
+
Updated with bootstrap summaries for VSH coeffs and RGQ terms.
|
|
95
|
+
"""
|
|
96
|
+
# Clean sample (if a mask was applied)
|
|
97
|
+
if not np.all(mask):
|
|
98
|
+
dra, ddc, dra_err, ddc_err, ra_rad, dc_rad, ra_dc_cor = extract_data(
|
|
99
|
+
mask, dra, ddc, dra_err, ddc_err, ra_rad, dc_rad, ra_dc_cor
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
# Current estimates (baseline)
|
|
103
|
+
pmt = np.asarray(output["pmt"])
|
|
104
|
+
if "pmt1" in output:
|
|
105
|
+
pmt1 = np.asarray(output["pmt1"])
|
|
106
|
+
degree_key = "pmt1"
|
|
107
|
+
else:
|
|
108
|
+
pmt1 = np.asarray(output["pmt2"])
|
|
109
|
+
degree_key = "pmt2"
|
|
110
|
+
|
|
111
|
+
# Bootstrap sizes
|
|
112
|
+
N_samp = len(dra)
|
|
113
|
+
N_resamp = bs_freq(N_samp)
|
|
114
|
+
N_pmt = pmt.size
|
|
115
|
+
N_pmt1 = pmt1.size
|
|
116
|
+
|
|
117
|
+
pmt_ts_array = np.zeros((N_resamp, N_pmt)) # VSH coeffs
|
|
118
|
+
# RGQ terms (length depends on l_max & fit_type)
|
|
119
|
+
pmt_rgq_array = np.zeros((N_resamp, N_pmt1))
|
|
120
|
+
|
|
121
|
+
for i in range(N_resamp):
|
|
122
|
+
# Indices for a bootstrap sample
|
|
123
|
+
new_idx = bs_resampling_indx(dra)
|
|
124
|
+
|
|
125
|
+
# Slice all arrays consistently (extract_data accepts index arrays too)
|
|
126
|
+
dra1, ddc1, dra_err1, ddc_err1, ra_rad1, dc_rad1, ra_dc_cor1 = extract_data(
|
|
127
|
+
new_idx, dra, ddc, dra_err, ddc_err, ra_rad, dc_rad, ra_dc_cor
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Refit on the resampled data
|
|
131
|
+
pmti, sigi, cor_mati, _, _ = nor_eq_sol(
|
|
132
|
+
dra1, ddc1, dra_err1, ddc_err1, ra_rad1, dc_rad1,
|
|
133
|
+
ra_dc_cor=ra_dc_cor1, l_max=l_max, fit_type=fit_type,
|
|
134
|
+
num_iter=num_iter, calc_res=True
|
|
135
|
+
)
|
|
136
|
+
pmt_ts_array[i, :] = pmti
|
|
137
|
+
|
|
138
|
+
# Convert to rotation/glide/quadrupolar terms
|
|
139
|
+
conv = convert_ts_to_rgq(pmti, sigi, cor_mati, l_max, fit_type)
|
|
140
|
+
rgq = np.asarray(conv["pmt1"] if "pmt1" in conv else conv["pmt2"])
|
|
141
|
+
if rgq.size != N_pmt1:
|
|
142
|
+
# If the pipeline later changes lengths, keep it robust:
|
|
143
|
+
rgq = rgq[:N_pmt1]
|
|
144
|
+
pmt_rgq_array[i, :] = rgq
|
|
145
|
+
|
|
146
|
+
# Bootstrap summaries for VSH coeffs
|
|
147
|
+
pmt_ts_mean = np.zeros(N_pmt)
|
|
148
|
+
pmt_ts_std = np.zeros(N_pmt)
|
|
149
|
+
pmt_ts_rms = np.zeros(N_pmt)
|
|
150
|
+
for i in range(N_pmt):
|
|
151
|
+
meani, stdi, rmsi = bs_formal_error(pmt[i], pmt_ts_array[:, i])
|
|
152
|
+
pmt_ts_mean[i], pmt_ts_std[i], pmt_ts_rms[i] = meani, stdi, rmsi
|
|
153
|
+
|
|
154
|
+
# Bootstrap summaries for RGQ terms
|
|
155
|
+
pmt_rgq_mean = np.zeros(N_pmt1)
|
|
156
|
+
pmt_rgq_std = np.zeros(N_pmt1)
|
|
157
|
+
pmt_rgq_rms = np.zeros(N_pmt1)
|
|
158
|
+
for i in range(N_pmt1):
|
|
159
|
+
meani, stdi, rmsi = bs_formal_error(pmt1[i], pmt_rgq_array[:, i])
|
|
160
|
+
pmt_rgq_mean[i], pmt_rgq_std[i], pmt_rgq_rms[i] = meani, stdi, rmsi
|
|
161
|
+
|
|
162
|
+
# Store results
|
|
163
|
+
output["pmt_bs_mean"] = pmt_ts_mean
|
|
164
|
+
output["pmt_bs_std"] = pmt_ts_std
|
|
165
|
+
output["pmt_bs_rms"] = pmt_ts_rms
|
|
166
|
+
|
|
167
|
+
output[f"{degree_key}_bs_mean"] = pmt_rgq_mean
|
|
168
|
+
output[f"{degree_key}_bs_std"] = pmt_rgq_std
|
|
169
|
+
output[f"{degree_key}_bs_rms"] = pmt_rgq_rms
|
|
170
|
+
|
|
171
|
+
# Notes (keep as a list; caller can print if needed)
|
|
172
|
+
output["note"] = output.get("note", []) + [
|
|
173
|
+
"pmt_bs_mean/std/rms: VSH coefficients from bootstrap sampling",
|
|
174
|
+
f"{degree_key}_bs_mean/std/rms: glide+rotation(+quadrupolar) from bootstrap sampling",
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
return output
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def main():
|
|
181
|
+
print("Have a nice day!")
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
if __name__ == "__main__":
|
|
185
|
+
main()
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# File name: auto_elim.py
|
|
4
|
+
"""
|
|
5
|
+
Created on Thu Dec 24 10:35:28 2020
|
|
6
|
+
|
|
7
|
+
@author: Neo(niu.liu@nju.edu.cn)
|
|
8
|
+
|
|
9
|
+
This script contains code for auto-elimination.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import numpy as np
|
|
13
|
+
|
|
14
|
+
# My progs
|
|
15
|
+
from .stats_func import calc_nor_sep
|
|
16
|
+
from .matrix_calc import (nor_eq_sol_from_cache, nor_eq_sol,
|
|
17
|
+
residual_calc_from_cache,
|
|
18
|
+
cache_mat_calc, rm_cache_mat)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# ----------------------------- Function -----------------------------
|
|
22
|
+
def extract_data(mask, dra, ddc, dra_err, ddc_err, ra_rad, dc_rad, ra_dc_cor=None):
|
|
23
|
+
"""Get a clean sample based on mask
|
|
24
|
+
|
|
25
|
+
Parameters
|
|
26
|
+
----------
|
|
27
|
+
mask : array of boolean
|
|
28
|
+
mask for extract data
|
|
29
|
+
dra/ddc : array of float
|
|
30
|
+
R.A.(*cos(Dec.))/Dec. differences
|
|
31
|
+
dra_err/ddc_err : array of float
|
|
32
|
+
formal uncertainty of dra(*cos(dc_rad))/ddc
|
|
33
|
+
ra_rad/dc_rad : array of float
|
|
34
|
+
Right ascension/Declination in radian
|
|
35
|
+
|
|
36
|
+
Returns
|
|
37
|
+
----------
|
|
38
|
+
dra_new/ddc_new: array of float
|
|
39
|
+
R.A.(*cos(Dec.))/Dec for the clean sample. differences
|
|
40
|
+
dra_err_new/ddc_err_new: array of float
|
|
41
|
+
formal uncertainty of dra(*cos(dc_rad))/ddc for the clean sample
|
|
42
|
+
ra_rad_new/dc_rad_new: array of float
|
|
43
|
+
Right ascension/Declination in radian for the clean sample
|
|
44
|
+
ra_dc_cor_new: array of float
|
|
45
|
+
covariance/correlation coefficient between dra and ddc for the clean sample
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
# Extract the clean sample
|
|
49
|
+
dra_new, ddc_new = dra[mask], ddc[mask]
|
|
50
|
+
dra_err_new, ddc_err_new = dra_err[mask], ddc_err[mask]
|
|
51
|
+
ra_rad_new, dc_rad_new = ra_rad[mask], dc_rad[mask]
|
|
52
|
+
|
|
53
|
+
if ra_dc_cor is None:
|
|
54
|
+
ra_dc_cor_new = ra_dc_cor
|
|
55
|
+
else:
|
|
56
|
+
ra_dc_cor_new = ra_dc_cor[mask]
|
|
57
|
+
|
|
58
|
+
return dra_new, ddc_new, dra_err_new, ddc_err_new, ra_rad_new, dc_rad_new, ra_dc_cor_new
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def elim_on_nor_sep(clip_limit, dra, ddc, dra_err, ddc_err, ra_dc_cor=None):
|
|
62
|
+
"""Get a clean sample based on normalized separation
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
dra/ddc: array of float
|
|
67
|
+
R.A.(*cos(Dec.))/Dec. differences
|
|
68
|
+
dra_err/ddc_err: array of float
|
|
69
|
+
formal uncertainty of dra(*cos(dc_rad))/ddc
|
|
70
|
+
ra_rad/dc_rad: array of float
|
|
71
|
+
Right ascension/Declination in radian
|
|
72
|
+
clip_limit: int ot float
|
|
73
|
+
maximum normalized separation for clipping data
|
|
74
|
+
ra_dc_cor: array of float
|
|
75
|
+
correlation coefficient between dra and ddc, default is None
|
|
76
|
+
|
|
77
|
+
Returns
|
|
78
|
+
----------
|
|
79
|
+
dra_new/ddc_new: array of float
|
|
80
|
+
R.A.(*cos(Dec.))/Dec for the clean sample. differences
|
|
81
|
+
dra_err_new/ddc_err_new: array of float
|
|
82
|
+
formal uncertainty of dra(*cos(dc_rad))/ddc for the clean sample
|
|
83
|
+
ra_rad_new/dc_rad_new: array of float
|
|
84
|
+
Right ascension/Declination in radian for the clean sample
|
|
85
|
+
ra_dc_cor_new: array of float
|
|
86
|
+
covariance/correlation coefficient between dra and ddc for the clean sample
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
# Calculate normalized separation
|
|
90
|
+
nor_sep = calc_nor_sep(dra, dra_err, ddc, ddc_err, ra_dc_cor)
|
|
91
|
+
med_nor_sep = np.median(nor_sep)
|
|
92
|
+
max_nor_sep = clip_limit * med_nor_sep
|
|
93
|
+
|
|
94
|
+
# Constraint on normalized separation
|
|
95
|
+
mask = (nor_sep <= max_nor_sep)
|
|
96
|
+
|
|
97
|
+
return max_nor_sep, mask
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def auto_elim_vsh_fit(clip_limit, dra, ddc, dra_err, ddc_err, ra_rad, dc_rad,
|
|
101
|
+
ra_dc_cor=None, l_max=1, fit_type="full", num_iter=100):
|
|
102
|
+
""" Fit the VSH parameters with auto-elimination
|
|
103
|
+
|
|
104
|
+
Parameters
|
|
105
|
+
----------
|
|
106
|
+
clip_limit: float
|
|
107
|
+
thershold on normalized separation for auto-elimination
|
|
108
|
+
dra/ddc: array of float
|
|
109
|
+
R.A.(*cos(Dec.))/Dec. differences
|
|
110
|
+
dra_err/ddc_err: array of float
|
|
111
|
+
formal uncertainty of dra(*cos(dc_rad))/ddc
|
|
112
|
+
ra_rad/dc_rad: array of float
|
|
113
|
+
Right ascension/Declination in radian
|
|
114
|
+
ra_dc_cor: array of float
|
|
115
|
+
correlation coefficient between dra and ddc, default is None
|
|
116
|
+
l_max: int
|
|
117
|
+
maximum degree
|
|
118
|
+
fit_type: string
|
|
119
|
+
flag to determine which parameters to be fitted
|
|
120
|
+
full for T - and S-vectors both
|
|
121
|
+
T for T-vectors only
|
|
122
|
+
S for S-vectors only
|
|
123
|
+
pos_in_rad: Boolean
|
|
124
|
+
tell if positions are given in radian, mostly False
|
|
125
|
+
num_iter: int
|
|
126
|
+
number of source once processed. 100 should be fine
|
|
127
|
+
|
|
128
|
+
Returns
|
|
129
|
+
----------
|
|
130
|
+
pmt: array of float
|
|
131
|
+
estimation of(d1, d2, d3, r1, r2, r3)
|
|
132
|
+
sig: array of float
|
|
133
|
+
uncertainty of x
|
|
134
|
+
cor_mat: matrix
|
|
135
|
+
matrix of correlation coefficient.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
print("==================== Auto-elimination ====================")
|
|
139
|
+
print(" Nb_iteration Nb_sources Nb_outliers Threshold")
|
|
140
|
+
print(" {:11d} {:9d} {:9d} {:9.3f}".format(0, len(dra), 0, 0))
|
|
141
|
+
|
|
142
|
+
# mask1 = All True
|
|
143
|
+
mask1 = np.full(len(dra), True)
|
|
144
|
+
iter_count = 0
|
|
145
|
+
|
|
146
|
+
# first elimination
|
|
147
|
+
max_nor_sep2, mask2 = elim_on_nor_sep(
|
|
148
|
+
clip_limit, dra, ddc, dra_err, ddc_err, ra_dc_cor)
|
|
149
|
+
|
|
150
|
+
# Generate cache matrix
|
|
151
|
+
suffix_array = cache_mat_calc(
|
|
152
|
+
dra, ddc, dra_err, ddc_err, ra_rad, dc_rad,
|
|
153
|
+
ra_dc_cor=ra_dc_cor, l_max=l_max, fit_type=fit_type, num_iter=num_iter)
|
|
154
|
+
|
|
155
|
+
while np.any(mask1 != mask2):
|
|
156
|
+
# Renew the flags
|
|
157
|
+
max_nor_sep1, mask1 = max_nor_sep2, mask2
|
|
158
|
+
|
|
159
|
+
# Generate a clean sample
|
|
160
|
+
[dra1, ddc1, dra_err1, ddc_err1, ra_rad1, dc_rad1, ra_dc_cor1] = extract_data(
|
|
161
|
+
mask1, dra, ddc, dra_err, ddc_err, ra_rad, dc_rad, ra_dc_cor)
|
|
162
|
+
|
|
163
|
+
iter_count += 1
|
|
164
|
+
print(" {:11d} {:9d} {:9d} {:9.3f}".format(
|
|
165
|
+
iter_count, len(dra1), len(dra)-len(dra1), max_nor_sep1))
|
|
166
|
+
|
|
167
|
+
pmt, sig, cor_mat = nor_eq_sol(
|
|
168
|
+
dra1, ddc1, dra_err1, ddc_err1, ra_rad1, dc_rad1,
|
|
169
|
+
ra_dc_cor=ra_dc_cor1, l_max=l_max, fit_type=fit_type,
|
|
170
|
+
num_iter=num_iter, calc_res=False)
|
|
171
|
+
|
|
172
|
+
# Calculate residuals for all sources
|
|
173
|
+
dra_r, ddc_r = residual_calc_from_cache(
|
|
174
|
+
dra, ddc, pmt, suffix_array)
|
|
175
|
+
|
|
176
|
+
# Re-eliminate the data
|
|
177
|
+
max_nor_sep2, mask2 = elim_on_nor_sep(
|
|
178
|
+
clip_limit, dra_r, ddc_r, dra_err, ddc_err, ra_dc_cor)
|
|
179
|
+
|
|
180
|
+
# Record the mask
|
|
181
|
+
mask = mask1
|
|
182
|
+
|
|
183
|
+
# If no source is classified as outlier
|
|
184
|
+
if iter_count == 0:
|
|
185
|
+
pmt, sig, cor_mat, dra_r, ddc_r = nor_eq_sol_from_cache(
|
|
186
|
+
dra, ddc, suffix_array, num_iter=num_iter)
|
|
187
|
+
|
|
188
|
+
# Remove cache file
|
|
189
|
+
rm_cache_mat(suffix_array)
|
|
190
|
+
|
|
191
|
+
return pmt, sig, cor_mat, dra_r, ddc_r, mask
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def std_elim_vsh_fit(x_limit, dra, ddc, dra_err, ddc_err, ra_rad, dc_rad,
|
|
195
|
+
ra_dc_cor=None, l_max=1, fit_type="full", num_iter=100):
|
|
196
|
+
"""Fit the VSH parameters with standard-elimination
|
|
197
|
+
|
|
198
|
+
Parameters
|
|
199
|
+
----------
|
|
200
|
+
x_limit: float
|
|
201
|
+
thershold on normalized separation for std-elimination
|
|
202
|
+
dra/ddc: array of float
|
|
203
|
+
R.A.(*cos(Dec.))/Dec. differences
|
|
204
|
+
dra_err/ddc_err: array of float
|
|
205
|
+
formal uncertainty of dra(*cos(dc_rad))/ddc
|
|
206
|
+
ra_rad/dc_rad: array of float
|
|
207
|
+
Right ascension/Declination in radian
|
|
208
|
+
ra_dc_cor: array of float
|
|
209
|
+
correlation coefficient between dra and ddc, default is None
|
|
210
|
+
l_max: int
|
|
211
|
+
maximum degree
|
|
212
|
+
fit_type: string
|
|
213
|
+
flag to determine which parameters to be fitted
|
|
214
|
+
full for T - and S-vectors both
|
|
215
|
+
T for T-vectors only
|
|
216
|
+
S for S-vectors only
|
|
217
|
+
pos_in_rad: Boolean
|
|
218
|
+
tell if positions are given in radian, mostly False
|
|
219
|
+
num_iter: int
|
|
220
|
+
number of source once processed. 100 should be fine
|
|
221
|
+
|
|
222
|
+
Returns
|
|
223
|
+
----------
|
|
224
|
+
pmt: array of float
|
|
225
|
+
estimation of(d1, d2, d3, r1, r2, r3)
|
|
226
|
+
sig: array of float
|
|
227
|
+
uncertainty of x
|
|
228
|
+
cor_mat : matrix
|
|
229
|
+
matrix of correlation coefficient.
|
|
230
|
+
mask : array-like of boolean
|
|
231
|
+
flag for the clean sample
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
# Generate cache matrix
|
|
235
|
+
suffix_array = cache_mat_calc(
|
|
236
|
+
dra, ddc, dra_err, ddc_err, ra_rad, dc_rad,
|
|
237
|
+
ra_dc_cor=ra_dc_cor, l_max=l_max, fit_type=fit_type, num_iter=num_iter)
|
|
238
|
+
|
|
239
|
+
# Calculate normalized separation
|
|
240
|
+
nor_sep = calc_nor_sep(dra, dra_err, ddc, ddc_err, ra_dc_cor)
|
|
241
|
+
mask = (nor_sep <= x_limit)
|
|
242
|
+
|
|
243
|
+
# Generate a clean sample
|
|
244
|
+
[dra1, ddc1, dra_err1, ddc_err1, ra_rad1, dc_rad1, ra_dc_cor1] = extract_data(
|
|
245
|
+
mask, dra, ddc, dra_err, ddc_err, ra_rad, dc_rad, ra_dc_cor)
|
|
246
|
+
|
|
247
|
+
print("==================== Standard-elimination ====================")
|
|
248
|
+
print(" Nb_sources Nb_outliers Threshold")
|
|
249
|
+
print(" {:9d} {:9d} {:9.3f}".format(
|
|
250
|
+
len(dra1), len(dra)-len(dra1), x_limit))
|
|
251
|
+
|
|
252
|
+
pmt, sig, cor_mat = nor_eq_sol(
|
|
253
|
+
dra1, ddc1, dra_err1, ddc_err1, ra_rad1, dc_rad1,
|
|
254
|
+
ra_dc_cor=ra_dc_cor1, l_max=l_max, fit_type=fit_type,
|
|
255
|
+
num_iter=num_iter, calc_res=False)
|
|
256
|
+
|
|
257
|
+
# Calculate residuals for all sources
|
|
258
|
+
dra_r, ddc_r = residual_calc_from_cache(
|
|
259
|
+
dra, ddc, pmt, suffix_array)
|
|
260
|
+
|
|
261
|
+
# Remove cache file
|
|
262
|
+
rm_cache_mat(suffix_array)
|
|
263
|
+
|
|
264
|
+
return pmt, sig, cor_mat, dra_r, ddc_r, mask
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
def main():
|
|
268
|
+
"""Maybe add some tests here
|
|
269
|
+
"""
|
|
270
|
+
|
|
271
|
+
print("Have a nice day!")
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
if __name__ == "__main__":
|
|
275
|
+
main()
|
|
276
|
+
# --------------------------------- END --------------------------------
|