Skip to content

Commit f3b0a64

Browse files
author
Menlo Innovations - CAVA Project
committed
Harrison - PMCL/EYEA - SWE: Test in new breakpoint fitting algorithm and incorporate Quality Flags
1 parent d29fedb commit f3b0a64

8 files changed

Lines changed: 157 additions & 599 deletions

File tree

imap_l3_processing/cdf/config/imap_swe_l3_variable_attrs.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1671,18 +1671,18 @@ inst_az:
16711671
bin.
16721672
DATA_TYPE: CDF_REAL4
16731673
RECORD_VARYING: NRV
1674-
swp_flags:
1675-
NAME: swp_flags
1674+
swe_flags:
1675+
NAME: swe_flags
16761676
DATA_TYPE: CDF_UINT2
1677-
CATDESC: Quality flags for SWAPI L3a
1677+
CATDESC: Quality flags for SWE L3
16781678
DEPEND_0: epoch
16791679
VAR_TYPE: support_data
16801680
RECORD_VARYING: RV
1681-
FIELDNAM: Quality Flags for SWAPI L3a
1681+
FIELDNAM: Quality Flags for SWE L3
16821682
FORMAT: I5
16831683
UNITS: ' '
16841684
VALIDMIN: 0
16851685
VALIDMAX: 65534
16861686
FILLVAL: 65535
1687-
LABLAXIS: Quality flags for SWAPI L3a
1687+
LABLAXIS: Quality flags for SWE L3
16881688

imap_l3_processing/swe/l3/models.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@
8989
CORE_TEMPERATURE_TENSOR_INTEGRATED_CDF_VAR_NAME = "core_temperature_tensor_integrated"
9090
HALO_TEMPERATURE_TENSOR_INTEGRATED_CDF_VAR_NAME = "halo_temperature_tensor_integrated"
9191
TOTAL_TEMPERATURE_TENSOR_INTEGRATED_CDF_VAR_NAME = "total_temperature_tensor_integrated"
92-
SWP_FLAGS_VAR_NAME = "swp_flags"
92+
SWE_FLAGS_VAR_NAME = "swe_flags"
9393
INTEGRATED_LABEL = "integrated_label"
9494
TEMPERATURE_TENSOR_LABEL = "temperature_tensor_label"
9595
RTN_LABEL = "rtn_label"
@@ -221,7 +221,7 @@ class SweL3Data(DataProduct):
221221
raw_psd_by_phi_rebinned: np.ndarray
222222
raw_psd_by_theta_rebinned: np.ndarray
223223
# other
224-
swp_flags: np.ndarray
224+
swe_flags: np.ndarray
225225

226226
def to_data_product_variables(self) -> list[DataProductVariable]:
227227
return [
@@ -381,7 +381,7 @@ def to_data_product_variables(self) -> list[DataProductVariable]:
381381
value=self.moment_data.halo_temperature_tensor_integrated),
382382
DataProductVariable(TOTAL_TEMPERATURE_TENSOR_INTEGRATED_CDF_VAR_NAME,
383383
value=self.moment_data.total_temperature_tensor_integrated),
384-
DataProductVariable(SWP_FLAGS_VAR_NAME, value=self.swp_flags),
384+
DataProductVariable(SWE_FLAGS_VAR_NAME, value=self.swe_flags),
385385
DataProductVariable("raw_1d_psd_rebinned", value=self.raw_1d_psd_rebinned),
386386
DataProductVariable("raw_psd_by_theta_rebinned", value=self.raw_psd_by_theta_rebinned),
387387
DataProductVariable("raw_psd_by_phi_rebinned", value=self.raw_psd_by_phi_rebinned),

imap_l3_processing/swe/l3/science/pitch_calculations.py

Lines changed: 0 additions & 202 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,6 @@
1010
from imap_l3_processing.swe.l3.models import SweConfiguration
1111
from imap_l3_processing.swe.quality_flags import SweL3Flags
1212

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-
2213
def mec_breakpoint_finder(energies: np.ndarray, averaged_psd: np.ndarray) -> tuple[float, float, SweL3Flags]:
2314
"""
2415
Input:
@@ -165,101 +156,6 @@ def refine_breakpoint_value(energy, psd, breakpoint_value, num_points):
165156
return sc_pot_output, ch_break_output, FALLBACK_POTENTIAL_ESTIMATE | BACKUP_SPLINE_UNRESOLVED | POTENTIAL_FIT_UNCONVERGED | BREAKPOINT_FIT_UNCONVERGED
166157

167158

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-
263159
def average_over_look_directions(phase_space_density: np.ndarray, geometric_weights: np.ndarray,
264160
minimum_psd_value: float) -> np.ndarray:
265161
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[
530426
averaged_rebinned_intensity_by_pa_and_gyro, averaged_rebinned_intensity_by_pa_only,
531427
uncertainties_by_pa_and_gyro,
532428
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

Comments
 (0)