|
10 | 10 | from imap_l3_processing.swe.l3.models import SweConfiguration |
11 | 11 | from imap_l3_processing.swe.quality_flags import SweL3Flags |
12 | 12 |
|
13 | | -def piece_wise_model(x: np.ndarray, b0: float, b1: float, |
14 | | - b2: float, b3: float, b4: float, b5: float) -> np.ndarray: |
15 | | - return np.log(np.piecewise(x, [x <= b2, (x > b2) & (x <= b4), x > b4], |
16 | | - [ |
17 | | - lambda x: b0 * np.exp(-b1 * x), |
18 | | - lambda x: b0 * np.exp(b2 * (b3 - b1)) * np.exp(-b3 * x), |
19 | | - lambda x: b0 * np.exp(b2 * (b3 - b1)) * np.exp(b4 * (b5 - b3)) * np.exp(-b5 * x), |
20 | | - ])) |
21 | | - |
22 | 13 | def mec_breakpoint_finder(energies: np.ndarray, averaged_psd: np.ndarray) -> tuple[float, float, SweL3Flags]: |
23 | 14 | """ |
24 | 15 | Input: |
@@ -165,101 +156,6 @@ def refine_breakpoint_value(energy, psd, breakpoint_value, num_points): |
165 | 156 | return sc_pot_output, ch_break_output, FALLBACK_POTENTIAL_ESTIMATE | BACKUP_SPLINE_UNRESOLVED | POTENTIAL_FIT_UNCONVERGED | BREAKPOINT_FIT_UNCONVERGED |
166 | 157 |
|
167 | 158 |
|
168 | | -def find_breakpoints(energies: np.ndarray, averaged_psd: np.ndarray, latest_spacecraft_potentials: list[float], |
169 | | - latest_core_halo_break_points: list[float], |
170 | | - config: SweConfiguration) -> tuple[ |
171 | | - float, float]: |
172 | | - log_psd = np.log(averaged_psd) |
173 | | - slopes = -np.diff(log_psd) / np.diff(energies) |
174 | | - slope_ratios = slopes[1:] / slopes[:-1] |
175 | | - numb = np.max(np.nonzero(slope_ratios > config['slope_ratio_cutoff_for_potential_calc']), initial=0) |
176 | | - |
177 | | - if not numb: |
178 | | - return latest_spacecraft_potentials[-1], latest_core_halo_break_points[-1] |
179 | | - |
180 | | - energies = energies[:numb] |
181 | | - log_psd = log_psd[:numb] |
182 | | - b1: float = slopes[0] |
183 | | - core_index = np.searchsorted(energies, config["core_energy_for_slope_guess"]) - 1 |
184 | | - halo_index = np.searchsorted(energies, config["halo_energy_for_slope_guess"]) - 1 |
185 | | - b3: float = slopes[core_index] |
186 | | - b5: float = slopes[halo_index] |
187 | | - b0: float = np.exp(log_psd[0] + b1 * energies[0]) |
188 | | - initial_guesses = ( |
189 | | - b0, b1, np.average(latest_spacecraft_potentials), b3, np.average(latest_core_halo_break_points), b5) |
190 | | - |
191 | | - first_min_index = 0 |
192 | | - for i in range(1, len(slope_ratios) - 1): |
193 | | - if slope_ratios[i - 1] > slope_ratios[i] < slope_ratios[i + 1]: |
194 | | - first_min_index = i |
195 | | - break |
196 | | - |
197 | | - last_max_index = 0 |
198 | | - for i in reversed(range(1 + first_min_index, len(slopes) - 1)): |
199 | | - if slopes[i - 1] < slopes[i] > slopes[i + 1]: |
200 | | - last_max_index = i |
201 | | - break |
202 | | - |
203 | | - if last_max_index < config["refit_core_halo_breakpoint_index"]: |
204 | | - delta_b2 = -1.5 |
205 | | - delta_b4 = -10 |
206 | | - else: |
207 | | - delta_b2 = -1.0 |
208 | | - delta_b4 = 10 |
209 | | - return try_curve_fit_until_valid(energies, log_psd, initial_guesses, latest_spacecraft_potentials[-1], |
210 | | - latest_core_halo_break_points[-1], delta_b2, delta_b4) |
211 | | - |
212 | | - |
213 | | -def ls_fit(x, y, initial_b): |
214 | | - b = np.copy(initial_b) |
215 | | - flamda = 0.01 |
216 | | - deltaa = 0.05 * np.abs(b) |
217 | | - nloops = 14 |
218 | | - prev_chisqr = 0.0 |
219 | | - sigmay = None |
220 | | - nterms = len(b) |
221 | | - yfit = np.zeros(len(x)) |
222 | | - sigmaa = np.zeros(nterms) |
223 | | - |
224 | | - for k in range(nloops): |
225 | | - chisqr = curve_fit(6, len(x), x, y, sigmay, b, deltaa, flamda, sigmaa, yfit) |
226 | | - delta_chisqr = np.abs(prev_chisqr - chisqr) |
227 | | - if delta_chisqr < 0.001: |
228 | | - break |
229 | | - prev_chisqr = chisqr |
230 | | - return b |
231 | | - |
232 | | - |
233 | | -def try_curve_fit_until_valid(energies: np.ndarray, log_psd: np.ndarray, initial_guesses: tuple[float, ...], |
234 | | - latest_spacecraft_potential: float, latest_core_halo_breakpoint: float, |
235 | | - delta_b2: float, delta_b4: float) -> tuple[float, float]: |
236 | | - b = ls_fit(energies, log_psd, initial_guesses) |
237 | | - |
238 | | - def bad_fit(b): |
239 | | - return (b[1] <= 0 or |
240 | | - b[3] <= 0 or |
241 | | - b[5] <= 0 or |
242 | | - b[2] >= b[4] or |
243 | | - b[4] <= 15 or |
244 | | - b[2] <= energies[0] or |
245 | | - b[2] >= 20 or |
246 | | - b[2] >= 2 * latest_spacecraft_potential) |
247 | | - |
248 | | - attempt_count = 0 |
249 | | - |
250 | | - modified_guesses = list(initial_guesses) |
251 | | - while bad_fit(b): |
252 | | - if attempt_count < 3: |
253 | | - modified_guesses[2] += delta_b2 |
254 | | - modified_guesses[4] += delta_b4 |
255 | | - b = ls_fit(energies, log_psd, modified_guesses) |
256 | | - attempt_count += 1 |
257 | | - else: |
258 | | - return latest_spacecraft_potential, latest_core_halo_breakpoint |
259 | | - |
260 | | - return b[2], b[4] |
261 | | - |
262 | | - |
263 | 159 | def average_over_look_directions(phase_space_density: np.ndarray, geometric_weights: np.ndarray, |
264 | 160 | minimum_psd_value: float) -> np.ndarray: |
265 | 161 | enforced_minimum_psd = np.maximum(phase_space_density, minimum_psd_value) |
@@ -530,101 +426,3 @@ def swe_rebin_intensity_by_pitch_angle_and_gyrophase(intensity_data: np.ndarray[ |
530 | 426 | averaged_rebinned_intensity_by_pa_and_gyro, averaged_rebinned_intensity_by_pa_only, |
531 | 427 | uncertainties_by_pa_and_gyro, |
532 | 428 | uncertainties_by_pa_only) |
533 | | - |
534 | | - |
535 | | -def curve_fit(nterms: int, npts: int, x, y, sigmay, b, deltaa, flamda, sigmaa, yfit): |
536 | | - nfree = npts - nterms |
537 | | - ngoes = 1 |
538 | | - alpha = np.zeros((nterms, nterms)) |
539 | | - weights = np.ones(npts) |
540 | | - beta = np.zeros(nterms) |
541 | | - b2 = np.zeros(nterms) |
542 | | - array = np.full_like(alpha, np.nan) |
543 | | - invarray = np.full_like(array, np.nan) |
544 | | - |
545 | | - if nfree <= 0: |
546 | | - chisqr = 0.0 |
547 | | - return chisqr |
548 | | - |
549 | | - for i in range(npts): |
550 | | - |
551 | | - deriv = fderiv(nterms, x[i], b, deltaa) |
552 | | - |
553 | | - for j in range(nterms): |
554 | | - beta[j] += weights[i] * (y[i] - functn(nterms, x[i], b)) * deriv[j] |
555 | | - |
556 | | - for k in range(nterms): |
557 | | - alpha[j][k] += weights[i] * deriv[j] * deriv[k] |
558 | | - |
559 | | - for i in range(npts): |
560 | | - yfit[i] = functn(nterms, x[i], b) |
561 | | - |
562 | | - chisq1 = fchisqr(npts, y, weights, nfree, yfit) |
563 | | - chisqr = np.inf |
564 | | - |
565 | | - while ((chisq1 - chisqr < 0.0) and (ngoes <= 5)): |
566 | | - for j in range(nterms): |
567 | | - for k in range(nterms): |
568 | | - array[j][k] = alpha[j][k] / np.sqrt(alpha[j][j] * alpha[k][k]) |
569 | | - array[j][j] = 1 + flamda |
570 | | - |
571 | | - invarray = np.linalg.inv(array) |
572 | | - |
573 | | - for j in range(nterms): |
574 | | - b2[j] = b[j] |
575 | | - |
576 | | - for k in range(nterms): |
577 | | - b2[j] += beta[k] * invarray[j][k] / np.sqrt(alpha[j][j] * alpha[k][k]) |
578 | | - |
579 | | - for i in range(npts): |
580 | | - yfit[i] = functn(nterms, x[i], b2) |
581 | | - |
582 | | - chisqr = fchisqr(npts, y, weights, nfree, yfit) |
583 | | - ngoes += 1 |
584 | | - flamda *= 10.0 |
585 | | - |
586 | | - for j in range(nterms): |
587 | | - b[j] = b2[j] |
588 | | - sigmaa[j] = np.sqrt(invarray[j][j] / alpha[j][j]) |
589 | | - |
590 | | - flamda /= 10.0 |
591 | | - return chisqr |
592 | | - |
593 | | - |
594 | | -def fderiv(nterms, x, b, deltaa): |
595 | | - b2 = np.copy(b) |
596 | | - deriv = np.full(nterms, np.nan) |
597 | | - |
598 | | - for j in range(nterms): |
599 | | - b2[j] = b[j] |
600 | | - |
601 | | - for j in range(nterms): |
602 | | - bj = b2[j] |
603 | | - delta = deltaa[j] |
604 | | - |
605 | | - b2[j] = bj + delta |
606 | | - yfit1 = functn(nterms, x, b2) |
607 | | - |
608 | | - b2[j] = bj - delta |
609 | | - yfit2 = functn(nterms, x, b2) |
610 | | - |
611 | | - deriv[j] = (yfit1 - yfit2) / (2 * delta) |
612 | | - |
613 | | - b2[j] = bj |
614 | | - |
615 | | - return deriv |
616 | | - |
617 | | - |
618 | | -def functn(nterms, x, b): |
619 | | - assert nterms == len(b) |
620 | | - return piece_wise_model(x, *b) |
621 | | - |
622 | | - |
623 | | -def fchisqr(npts, y, weights, free, yfit): |
624 | | - chisq = 0 |
625 | | - if free <= 0: |
626 | | - return 0.0 |
627 | | - for i in range(npts): |
628 | | - chisq += weights[i] * (y[i] - yfit[i]) * (y[i] - yfit[i]) |
629 | | - |
630 | | - return chisq / free |
0 commit comments