Reduced Electron Velocity Distribution Function using Monte-Carlo integration#

author: Louis Richard
Example to compute and plot reduced electron distributions from FPI (based on Khotyaintsev et al., 2020, PRL)
[1]:
%matplotlib inline

import matplotlib.pyplot as plt
import numpy as np

from pyrfu import mms, pyrf
from pyrfu.plot import plot_spectr, plot_line

mms.db_init("/Volumes/mms")
Load IGRF coefficients ...

Load data#

Define time interval and spacecraft index#

[2]:
tint = ["2015-12-02T01:14:55.500", "2015-12-02T01:14:56.600"]

tint_long = ["2015-12-02T01:14:45.500", "2015-12-02T01:15:02.500"]
mms_id = 4

Get magnetic field in spacecraft coordinates#

[3]:
b_dmpa = mms.get_data("b_dmpa_fgm_brst_l2", tint_long, mms_id)
[09-Jun-23 09:48:37] INFO: Loading mms4_fgm_b_dmpa_brst_l2...

Get electric field in spacecraft coordinates and spacecraft potential#

[4]:
e_dsl = mms.get_data("e_dsl_edp_brst_l2", tint_long, mms_id)
scpot = mms.get_data("v_edp_brst_l2", tint_long, mms_id)
[09-Jun-23 09:48:37] INFO: Loading mms4_edp_dce_dsl_brst_l2...
[09-Jun-23 09:48:37] INFO: Loading mms4_edp_scpot_brst_l2...

Get the electron velocity distribution skymap and the uncertainties (\(\delta f / f = 1/\sqrt{n}\) with \(n\) the number of counts)#

[5]:
vdf_e = mms.get_data("pde_fpi_brst_l2", tint_long, mms_id)
vdf_e_err = mms.get_data("pderre_fpi_brst_l2", tint_long, mms_id)
[09-Jun-23 09:48:38] INFO: Loading mms4_des_dist_brst...
[09-Jun-23 09:48:38] WARNING: /usr/local/lib/python3.10/site-packages/pyrfu/mms/get_dist.py:68: UserWarning: Epoch_plus_var/Epoch_minus_var units are not clear, assume s
  warnings.warn(message)

[09-Jun-23 09:48:41] INFO: Loading mms4_des_disterr_brst...

Ignore phase-space density for one count level (also makes function faster)#

[6]:
vdf_e.data.data[vdf_e.data.data < 1.1 * vdf_e_err.data.data] = 0.0

Define coordinates#

[7]:
x_hat = pyrf.resample(b_dmpa, vdf_e)
x_hat.data /= pyrf.norm(pyrf.resample(b_dmpa, vdf_e)).data[:, np.newaxis]
y_hat = pyrf.resample(pyrf.cross(e_dsl, b_dmpa), vdf_e)
y_hat.data /= pyrf.norm(y_hat).data[:, np.newaxis]
z_hat = pyrf.cross(x_hat, y_hat)
[09-Jun-23 09:48:45] INFO: Using averages in resample
[09-Jun-23 09:48:45] INFO: Using averages in resample
[09-Jun-23 09:48:45] INFO: Using averages in resample

Create transformation matrix#

[8]:
xyz = np.transpose(np.stack([x_hat.data, y_hat.data, z_hat.data]), [1, 2, 0])
xyz = pyrf.ts_tensor_xyz(vdf_e.time.data, xyz)

Define velocity grid parallel to the magnetic field#

[9]:
vpara_lim = np.array([-10e3, 10e3], dtype=np.float64)
vg_para = np.linspace(vpara_lim[0], vpara_lim[1], 100)

Reduce distribution along the magnetic field#

[10]:
f1dpara = mms.reduce(
    vdf_e, dim="1d", xyz=xyz, n_mc=200, vg=vg_para * 1e3, sc_pot=scpot, lower_e_lim=30.0
)
f1dpara = f1dpara.assign_coords(vx=f1dpara.vx.data / 1e3)
[09-Jun-23 09:48:45] INFO: Using averages in resample
100%|█████████████████████| 566/566 [00:20<00:00, 27.97it/s]

Plot#

[11]:
f, axs = plt.subplots(2, sharex="all", figsize=(9, 6.5))
f.subplots_adjust(hspace=0.0, left=0.15, right=0.85, bottom=0.08, top=0.95)
plot_line(axs[0], b_dmpa)
plot_line(axs[0], pyrf.norm(b_dmpa), color="k")
axs[0].set_ylim([-5, 40])
axs[0].set_ylabel("$B_{dmpa}~[\mathrm{nT}]$")

axs[1], cax1 = plot_spectr(
    axs[1], f1dpara, cscale="log", clim=[1e-2, 1e0], cmap="Spectral_r"
)
axs[1].set_ylim([-9.9, 9.9])
axs[1].set_ylabel("$v_\\parallel~[\\mathrm{Mm}~\\mathrm{s}^{-1}]$")
cax1.set_ylabel("$F_e~[\\mathrm{s}~\\mathrm{m}^{-4}]$")

axs[-1].set_xlim(pyrf.iso86012datetime64(np.array(tint)))
[11]:
(16771.05203125, 16771.05204398148)
../../_images/examples_01_mms_example_mms_reduced_electron_dist_22_1.png

2D projection of the ion velocity distribution functions#

Define times for 2D projection#

[12]:
t = [
    "2015-12-02T01:15:02.020000000",
    "2015-12-02T01:14:56.440",
    "2015-12-02T01:14:56.380",
    "2015-12-02T01:14:56.290",
    "2015-12-02T01:14:55.810",
    "2015-12-02T01:14:55.600",
]

Reduce ion distributions in 2d plane \((B,E\times B)\)#

[13]:
f2d = []

for t_ in t:
    t_ = pyrf.extend_tint([t_, t_], [-0.06, 0.06])
    tmp = mms.reduce(
        pyrf.time_clip(vdf_e, t_),
        xyz=pyrf.time_clip(xyz, t_),
        dim="2d",
        base="cart",
        n_mc=200 * 5,
        vg=vg_para * 1e3,
    )
    f2d.append(tmp.assign_coords(vx=tmp.vx.data / 1e3, vy=tmp.vy.data / 1e3))
100%|█████████████████████████| 4/4 [00:02<00:00,  1.96it/s]
100%|█████████████████████████| 4/4 [00:02<00:00,  1.94it/s]
100%|█████████████████████████| 4/4 [00:01<00:00,  2.06it/s]
100%|█████████████████████████| 4/4 [00:01<00:00,  2.04it/s]
100%|█████████████████████████| 4/4 [00:02<00:00,  1.86it/s]
100%|█████████████████████████| 4/4 [00:02<00:00,  1.74it/s]

Plot#

[14]:
f, axs = plt.subplots(2, 3, sharex="all", sharey="all", figsize=(9, 6.5))
f.subplots_adjust(wspace=0, hspace=0, left=0.11, right=0.97, bottom=0.1, top=0.85)


axs[0, 0], cax0 = plot_spectr(
    axs[0, 0],
    f2d[0].mean(axis=0),
    cscale="log",
    clim=[1e-10, 3e-7],
    cmap="Spectral_r",
    colorbar="top",
)
axs[0, 0].set_xlim([-1e4, 1e4])
axs[0, 0].set_ylim([-1e4, 1e4])

for f_2d, ax in zip(f2d[1:], axs.flatten()[1:]):
    ax = plot_spectr(
        ax,
        f_2d.mean(axis=0),
        cscale="log",
        clim=[1e-10, 3e-7],
        cmap="Spectral_r",
        colorbar="none",
    )
    ax.set_xlim([-9.9, 9.9])
    ax.set_ylim([-9.9, 9.9])

pos_axs02 = axs[0, 2].get_position()
pos_cax0 = cax0.get_position()
x0 = pos_cax0.x0
y0 = pos_cax0.y0 + 0.03
width = pos_axs02.x0 + pos_axs02.width - pos_cax0.x0
height = 0.02
cax0.set_position([x0, y0, width, height])
cax0.set_xlabel("$F_e~[\\mathrm{s}~\\mathrm{m}^{-5}]$")

f.supxlabel("$v_{\\parallel}~[\\mathrm{Mm}~\\mathrm{s}^{-1}]$")
f.supylabel("$v_{E\\times B}~[\\mathrm{Mm}~\\mathrm{s}^{-1}]$")
[14]:
Text(0.02, 0.5, '$v_{E\\times B}~[\\mathrm{Mm}~\\mathrm{s}^{-1}]$')
../../_images/examples_01_mms_example_mms_reduced_electron_dist_30_1.png