FEEPS sunlight contamination removal and 4SC averages of omnidirectional and Pitch-angle distributions for electrons#

author: Apostolos Kolokotronis

[9]:
%matplotlib widget

import matplotlib.pyplot as plt
import numpy as np
from pyrfu import mms
import pyrfu
from pyrfu import pyrf
from pyrfu.plot import plot_line, plot_spectr, make_labels

from copy import deepcopy

pyrfu.plot.set_color_cycle(pal="pyrfu")
[9]:
(['pyrfu:blue',
  'pyrfu:green',
  'pyrfu:red',
  'pyrfu:fg',
  'pyrfu:orange',
  'pyrfu:purple',
  'pyrfu:yellow',
  'pyrfu:lightblue',
  'pyrfu:olive'],
 'hot_white_desaturated')
[10]:
mms.db_init(default="local", local="/data/mms")
tint = ["2017-07-24T12:48:44.00", "2017-07-24T12:50:23.00"]
[10-Feb-26 20:14:54] INFO: Updating MMS data access configuration in /homelocal/apostolosk/envs/mms_projects/lib/python3.12/site-packages/pyrfu/mms/config.json...
[10-Feb-26 20:14:54] INFO: Updating MMS SDC credentials in /homelocal/apostolosk/.config/python_keyring...

Load FGM and FEEPS data from the four MMS spacecraft#

[11]:
b_gsm = [mms.get_data("b_gsm_fgm_brst_l2", tint, mms_id) for mms_id in range(1, 5)]
b_gsm_4sc = pyrf.avg_4sc(b_gsm)

b_bcs = [mms.get_data("b_bcs_fgm_brst_l2", tint, mms_id) for mms_id in range(1, 5)]

dpf_feeps_alle_e = [
    mms.get_feeps_alleyes(f"fluxe_brst_l2", tint, mms_id) for mms_id in range(1, 5)
]
[10-Feb-26 20:14:54] INFO: Loading mms1_fgm_b_gsm_brst_l2...
[10-Feb-26 20:14:54] INFO: Loading mms2_fgm_b_gsm_brst_l2...
[10-Feb-26 20:14:54] INFO: Loading mms3_fgm_b_gsm_brst_l2...
[10-Feb-26 20:14:54] INFO: Loading mms4_fgm_b_gsm_brst_l2...
[10-Feb-26 20:14:54] INFO: Loading mms1_fgm_b_bcs_brst_l2...
[10-Feb-26 20:14:54] INFO: Loading mms2_fgm_b_bcs_brst_l2...
[10-Feb-26 20:14:54] INFO: Loading mms3_fgm_b_bcs_brst_l2...
[10-Feb-26 20:14:54] INFO: Loading mms4_fgm_b_bcs_brst_l2...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_spinsectnum...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_pitch_angle...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_top_intensity_sensorid_1...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_top_intensity_sensorid_2...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_top_intensity_sensorid_3...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_top_intensity_sensorid_4...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_top_intensity_sensorid_5...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_top_intensity_sensorid_9...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_top_intensity_sensorid_10...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_top_intensity_sensorid_11...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_top_intensity_sensorid_12...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_1...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_2...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_3...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_4...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_5...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_9...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_10...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_11...
[10-Feb-26 20:14:54] INFO: Loading mms1_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_12...
[10-Feb-26 20:14:54] INFO: Loading mms2_epd_feeps_brst_l2_electron_spinsectnum...
[10-Feb-26 20:14:54] INFO: Loading mms2_epd_feeps_brst_l2_electron_pitch_angle...
[10-Feb-26 20:14:54] INFO: Loading mms2_epd_feeps_brst_l2_electron_top_intensity_sensorid_1...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_top_intensity_sensorid_2...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_top_intensity_sensorid_3...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_top_intensity_sensorid_4...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_top_intensity_sensorid_5...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_top_intensity_sensorid_9...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_top_intensity_sensorid_10...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_top_intensity_sensorid_11...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_top_intensity_sensorid_12...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_1...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_2...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_3...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_4...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_5...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_9...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_10...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_11...
[10-Feb-26 20:14:55] INFO: Loading mms2_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_12...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_spinsectnum...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_pitch_angle...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_top_intensity_sensorid_1...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_top_intensity_sensorid_2...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_top_intensity_sensorid_3...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_top_intensity_sensorid_4...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_top_intensity_sensorid_5...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_top_intensity_sensorid_9...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_top_intensity_sensorid_10...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_top_intensity_sensorid_11...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_top_intensity_sensorid_12...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_1...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_2...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_3...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_4...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_5...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_9...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_10...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_11...
[10-Feb-26 20:14:55] INFO: Loading mms3_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_12...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_spinsectnum...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_pitch_angle...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_top_intensity_sensorid_1...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_top_intensity_sensorid_2...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_top_intensity_sensorid_3...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_top_intensity_sensorid_4...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_top_intensity_sensorid_5...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_top_intensity_sensorid_9...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_top_intensity_sensorid_10...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_top_intensity_sensorid_11...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_top_intensity_sensorid_12...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_1...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_2...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_3...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_4...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_5...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_9...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_10...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_11...
[10-Feb-26 20:14:55] INFO: Loading mms4_epd_feeps_brst_l2_electron_bottom_intensity_sensorid_12...

Apply corrections (see mms_feeps example), remove sunlit spin sector-eye pairs and calculate omnidirectional fluxes#

[12]:
dpf_feeps_e_corr = [
    mms.feeps_corrections(deepcopy(dpf_feeps_alle_e[i])) for i in range(4)
]
dpf_feeps_e_corr = [
    mms.feeps_remove_sunlit_sectors(dpf_feeps_e_corr[i], outliers_q=0.99)
    for i in range(4)
]
dpf_feeps_omni_e = [
    mms.feeps_omni(
        dpf_feeps_e_corr[i],
    )
    for i in range(4)
]

Choose angle bin size for PAD and the energy ranges, calculate PAD and average over the 4 spacecraft#

[13]:
n_pabins = 11
bin_size = 180.0 / n_pabins
energy_ranges = [[32.1, 50], [50, 150]]

dpf_feeps_pad_e_energy_ranges = []


for i in range(4):
    dpf_feeps_pad_e_energy_ranges.append(
        [
            mms.feeps_pad(
                dpf_feeps_e_corr[i], b_bcs[i], energy=energy_range, bin_size=bin_size
            )
            for energy_range in energy_ranges
        ]
    )

dpf_feeps_pad_4sc_enranges = [
    mms.feeps_avg_4sc(
        [dpf_feeps_pad_e_energy_ranges[i_sc][i_en] for i_sc in range(4)], flag="pad"
    )
    for i_en in range(len(energy_ranges))
]
[10-Feb-26 20:15:32] WARNING: /homelocal/apostolosk/envs/mms_projects/lib/python3.12/site-packages/pyrfu/mms/feeps_avg_4sc.py:134: RuntimeWarning: invalid value encountered in divide
  b_avg_data / b_nan_denom,

[10-Feb-26 20:15:32] WARNING: /homelocal/apostolosk/envs/mms_projects/lib/python3.12/site-packages/pyrfu/mms/feeps_avg_4sc.py:134: RuntimeWarning: invalid value encountered in divide
  b_avg_data / b_nan_denom,

Convert energies from keV to eV (Useful to plot together with FPI omnidirectional spectrograms)#

[14]:
for i in range(4):
    if "UNITS" not in dpf_feeps_omni_e[i].coords["energy"].attrs.keys():
        dpf_feeps_omni_e[i].coords["energy"].attrs["UNITS"] = "keV"
for i in range(4):
    if dpf_feeps_omni_e[i].coords["energy"].attrs["UNITS"] != "eV":
        dpf_feeps_omni_e[i].coords["energy"] = (
            dpf_feeps_omni_e[i].coords["energy"] * 1e3
        )
        dpf_feeps_omni_e[i].coords["energy"].attrs["UNITS"] = "eV"

Choose energy channel combination over the four spacecraft, for the four-spacecraft omni flux averages#

Each row of energy_channels_sc represents a FEEPS energy channel, while each column corresponds to spacecrafts 1-4.

For this specific energy channel combination used see the supplementary information in Kolokotronis et al. 2026.

[15]:
energy_channels_sc = [
    [None, 1, 1, 1],
    [1, 2, 2, 2],
    [2, 3, 3, 3],
    [3, 4, 4, 4],
    [4, 5, 5, 5],
    [5, 6, 6, 6],
    [6, 7, 7, 7],
    [7, 8, 8, 8],
    [None, 9, 9, 9],
    [10, 10, 10, 10],
    [11, 11, 11, 11],
    [12, 12, 12, 12],
    [13, 13, 13, 13],
    [14, 14, 14, 14],
    [15, 15, 15, 15],
]

combined_energies_dict = {
    i: {j: energy_channels_sc[i][j] for j in range(4)}
    for i in range(len(energy_channels_sc))
}

Calculate four-spacecraft average of omnidirectional flux#

[16]:
dpf_feeps_omni_e_avg = mms.feeps_avg_4sc(
    dpf_feeps_omni_e, flag="omni", combined_energies=combined_energies_dict
)
[10-Feb-26 20:15:32] WARNING: /homelocal/apostolosk/envs/mms_projects/lib/python3.12/site-packages/pyrfu/mms/feeps_avg_4sc.py:134: RuntimeWarning: invalid value encountered in divide
  b_avg_data / b_nan_denom,

[17]:
legend_options = dict(frameon=False, loc="upper right", fontsize=14, handletextpad=0.1)


fig, axs = plt.subplots(2 + len(energy_ranges), figsize=(12, 10), sharex="all")
fig.subplots_adjust(hspace=0.0)

plot_line(axs[0], pyrf.norm(b_gsm_4sc), color="black", label="$|B|$")
plot_line(
    axs[0],
    b_gsm_4sc,
    label=["$B_{\mathrm{x}}$", "$B_{\mathrm{y}}$", "$B_{\mathrm{z}}$"],
)
axs[0].legend(
    **legend_options,
    labelcolor="linecolor",
    bbox_to_anchor=(0.6, 1.04),
    handlelength=0.0,
    ncols=4,
)
axs[0].set_ylabel("$B_{\mathrm{GSM}}$ \n [nT]", fontsize=14, labelpad=1)

axs[1], caxs = plot_spectr(
    axs[1], np.log10(dpf_feeps_omni_e_avg), cmap="Spectral_r", yscale="log"
)
caxs.set_ylabel(
    "$\log_{10}$ DPF \n $[\mathrm{cm}^{-2} ~\mathrm{s}^{-1} ~\mathrm{sr}^{-1} ~\mathrm{keV}^{-1}]$",
    x=1,
    y=0.5,
    fontsize=14,
)
axs[1].set_ylabel("$E$ [$eV$]", fontsize=14)


for i_feeps in range(0, len(energy_ranges)):

    plot_id = 2 + i_feeps
    axs[plot_id], caxs = plot_spectr(
        axs[plot_id],
        np.log10(dpf_feeps_pad_4sc_enranges[i_feeps]),
        cmap="Spectral_r",
    )

    axs[plot_id].set_ylabel("$\\theta$" + "\n" + "[$^\\circ$]", fontsize=14)
    if i_feeps == 0 and (len(energy_ranges)) % 2 == 1:
        caxs.set_ylabel(
            "$\log_{10}$ DPF \n $[\mathrm{cm}^{-2} ~\mathrm{s}^{-1} ~\mathrm{sr}^{-1} ~\mathrm{keV}^{-1}]$",
            fontsize=14,
            x=1,
            y=0.5,
        )
    elif i_feeps == 1 and (len(energy_ranges)) % 2 == 0:
        caxs.set_ylabel(
            "$\log_{10}$ DPF \n $[\mathrm{cm}^{-2} ~\mathrm{s}^{-1} ~\mathrm{sr}^{-1} ~\mathrm{keV}^{-1}]$",
            fontsize=14,
            x=1,
            y=1,
        )

    axs[plot_id].set_yticks([45, 90, 135])
    axs[plot_id].set_ylim([0, 180])
    axs[plot_id].text(
        0.05,
        0.05,
        f"{energy_ranges[i_feeps][0]}-{energy_ranges[i_feeps][1]} keV",
        transform=axs[plot_id].transAxes,
        fontsize=14,
        weight="bold",
    )

make_labels(
    axs,
    [0.013, 0.84],
    bbox=dict(
        facecolor="white",
        edgecolor="black",
    ),
    fontsize=14,
)

fig.align_ylabels(axs)
[10-Feb-26 20:15:32] WARNING: <>:11: SyntaxWarning: invalid escape sequence '\m'

[10-Feb-26 20:15:32] WARNING: <>:11: SyntaxWarning: invalid escape sequence '\m'

[10-Feb-26 20:15:32] WARNING: <>:11: SyntaxWarning: invalid escape sequence '\m'

[10-Feb-26 20:15:32] WARNING: <>:20: SyntaxWarning: invalid escape sequence '\m'

[10-Feb-26 20:15:32] WARNING: <>:26: SyntaxWarning: invalid escape sequence '\l'

[10-Feb-26 20:15:32] WARNING: <>:46: SyntaxWarning: invalid escape sequence '\l'

[10-Feb-26 20:15:32] WARNING: <>:53: SyntaxWarning: invalid escape sequence '\l'

[10-Feb-26 20:15:32] WARNING: <>:11: SyntaxWarning: invalid escape sequence '\m'

[10-Feb-26 20:15:32] WARNING: <>:11: SyntaxWarning: invalid escape sequence '\m'

[10-Feb-26 20:15:32] WARNING: <>:11: SyntaxWarning: invalid escape sequence '\m'

[10-Feb-26 20:15:32] WARNING: <>:20: SyntaxWarning: invalid escape sequence '\m'

[10-Feb-26 20:15:32] WARNING: <>:26: SyntaxWarning: invalid escape sequence '\l'

[10-Feb-26 20:15:32] WARNING: <>:46: SyntaxWarning: invalid escape sequence '\l'

[10-Feb-26 20:15:32] WARNING: <>:53: SyntaxWarning: invalid escape sequence '\l'

[10-Feb-26 20:15:32] WARNING: /tmp/ipykernel_333580/174699056.py:11: SyntaxWarning: invalid escape sequence '\m'
  label=["$B_{\mathrm{x}}$", "$B_{\mathrm{y}}$", "$B_{\mathrm{z}}$"],

[10-Feb-26 20:15:32] WARNING: /tmp/ipykernel_333580/174699056.py:11: SyntaxWarning: invalid escape sequence '\m'
  label=["$B_{\mathrm{x}}$", "$B_{\mathrm{y}}$", "$B_{\mathrm{z}}$"],

[10-Feb-26 20:15:32] WARNING: /tmp/ipykernel_333580/174699056.py:11: SyntaxWarning: invalid escape sequence '\m'
  label=["$B_{\mathrm{x}}$", "$B_{\mathrm{y}}$", "$B_{\mathrm{z}}$"],

[10-Feb-26 20:15:32] WARNING: /tmp/ipykernel_333580/174699056.py:20: SyntaxWarning: invalid escape sequence '\m'
  axs[0].set_ylabel("$B_{\mathrm{GSM}}$ \n [nT]", fontsize=14, labelpad=1)

[10-Feb-26 20:15:32] WARNING: /tmp/ipykernel_333580/174699056.py:26: SyntaxWarning: invalid escape sequence '\l'
  "$\log_{10}$ DPF \n $[\mathrm{cm}^{-2} ~\mathrm{s}^{-1} ~\mathrm{sr}^{-1} ~\mathrm{keV}^{-1}]$",

[10-Feb-26 20:15:32] WARNING: /tmp/ipykernel_333580/174699056.py:46: SyntaxWarning: invalid escape sequence '\l'
  "$\log_{10}$ DPF \n $[\mathrm{cm}^{-2} ~\mathrm{s}^{-1} ~\mathrm{sr}^{-1} ~\mathrm{keV}^{-1}]$",

[10-Feb-26 20:15:32] WARNING: /tmp/ipykernel_333580/174699056.py:53: SyntaxWarning: invalid escape sequence '\l'
  "$\log_{10}$ DPF \n $[\mathrm{cm}^{-2} ~\mathrm{s}^{-1} ~\mathrm{sr}^{-1} ~\mathrm{keV}^{-1}]$",

[ ]: