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.
Files changed (43) hide show
  1. vsh_tools/basis.py +169 -0
  2. vsh_tools/bootstrap.py +185 -0
  3. vsh_tools/deprecated/__init__.py +8 -0
  4. vsh_tools/deprecated/auto_elim.py +276 -0
  5. vsh_tools/deprecated/bootstrap_sample.py +275 -0
  6. vsh_tools/deprecated/generate_test_data.py +67 -0
  7. vsh_tools/deprecated/glide_func.py +446 -0
  8. vsh_tools/deprecated/implementation_comparison.py +100 -0
  9. vsh_tools/deprecated/matrix_calc.py +817 -0
  10. vsh_tools/deprecated/pmt_convert.py +406 -0
  11. vsh_tools/deprecated/rgq_func.py +110 -0
  12. vsh_tools/deprecated/ssh_expansion.py +101 -0
  13. vsh_tools/deprecated/stats_func.py +261 -0
  14. vsh_tools/deprecated/test.py +40 -0
  15. vsh_tools/deprecated/tune_para.py +108 -0
  16. vsh_tools/deprecated/vec_sph_harm.py +178 -0
  17. vsh_tools/deprecated/verify_code.py +71 -0
  18. vsh_tools/deprecated/vsh_aux_info.py +51 -0
  19. vsh_tools/deprecated/vsh_expansion.py +162 -0
  20. vsh_tools/deprecated/vsh_expension.py +165 -0
  21. vsh_tools/deprecated/vsh_fit.py +491 -0
  22. vsh_tools/deprecated/vsh_power.py +171 -0
  23. vsh_tools/deprecated/vsh_significance.py +339 -0
  24. vsh_tools/deprecated/vsh_stat.py +126 -0
  25. vsh_tools/deprecated/vsh_stats.py +126 -0
  26. vsh_tools/diagnostics.py +45 -0
  27. vsh_tools/fit.py +351 -0
  28. vsh_tools/glide.py +209 -0
  29. vsh_tools/legacy.py +787 -0
  30. vsh_tools/linalg.py +448 -0
  31. vsh_tools/metrics.py +166 -0
  32. vsh_tools/outliers.py +199 -0
  33. vsh_tools/power.py +195 -0
  34. vsh_tools/rgq.py +363 -0
  35. vsh_tools/scalar_basis.py +121 -0
  36. vsh_tools/significance.py +233 -0
  37. vsh_tools/stats.py +173 -0
  38. vsh_tools/transforms.py +357 -0
  39. vsh_tools-0.1.0.dist-info/METADATA +73 -0
  40. vsh_tools-0.1.0.dist-info/RECORD +43 -0
  41. vsh_tools-0.1.0.dist-info/WHEEL +5 -0
  42. vsh_tools-0.1.0.dist-info/licenses/LICENSE +21 -0
  43. 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,8 @@
1
+ #!/usr/bin/env ipython
2
+ # -*- coding: utf-8 -*-
3
+ # File name: __init__.py
4
+ """
5
+ Created on Mon Feb 7 14:52:46 2022
6
+
7
+ @author: Neo(niu.liu@nju.edu.cn)
8
+ """
@@ -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 --------------------------------