#!/usr/bin/env python
# -*- coding: utf-8 -*-
# 3rd party imports
import numpy as np
import xarray as xr
from scipy import constants, optimize
# Local imports
from ..pyrf.resample import resample
from ..pyrf.trace import trace
from ..pyrf.ts_scalar import ts_scalar
from ..pyrf.ts_tensor_xyz import ts_tensor_xyz
__author__ = "Louis Richard"
__email__ = "louisr@irfu.se"
__copyright__ = "Copyright 2020-2023"
__license__ = "MIT"
__version__ = "2.4.2"
__status__ = "Prototype"
def _f_one_pop(x, *args):
i_e, i_aspoc, sc_pot_r = args
return np.nansum(
np.abs(
i_e.data + i_aspoc.data - (x[0] * np.exp(-sc_pot_r.data / x[1])),
),
)
def _f_two_pop(x, *args):
i_e, i_aspoc, sc_pot_r = args
return np.nansum(
np.abs(
i_e.data
+ i_aspoc.data
- (x[0] * np.exp(-sc_pot_r.data / x[1]))
+ (x[2] * np.exp(-sc_pot_r.data / x[3])),
),
)
[docs]def scpot2ne(sc_pot, n_e, t_e, i_aspoc: xr.DataArray = None):
r"""Compute number density from spacecraft potential. Function uses number
density and electron temperature to compute electron thermal current. A
best fit of the photoelectron current to the thermal current is used to
determine the photoelectron currents and temperatures. These are then used
to construct a new number density from the spacecraft potential.
Parameters
----------
sc_pot : xarray.DataArray
Time series of the spacecraft potential.
n_e : xarray.DataArray
Time series of the electron number density.
t_e : xarray.DataArray
Time series of the electron temperature. Function accepts scalar
temperature or tensor (scalar temperature is used in either case).
i_aspoc : xarray.DataArray, Optional
Time series of the ASPOC current in :math:`\\mu` A.
Returns
-------
n_esc : xarray.DataArray
Time series of the number density estimated from SCpot, at the same
resolution as ``sc_pot``.
i_ph0 : float
Value of the photoelectron currents ( :math:`\\mu` A) of the first
population.
t_ph0 : float
Value of the temperature (eV) of the first population.
i_ph1 : float
Value of the photoelectron currents ( :math:`\\mu` A) of the second
population.
t_ph1 : float
Value of the temperature (eV) of the second population.
Notes
-----
Usual assumptions are made for thermal and photoelectron current, vis.,
planar geometry for photoelectrons and spherical geometry for thermal
electrons.
Currently the calculation neglects the ion thermal current, secondary
electrons, and other sources of current.
ASPOC on does not work very well.
"""
if i_aspoc is not None:
i_aspoc = resample(i_aspoc, n_e)
else:
i_aspoc = ts_scalar(n_e.time.data, np.zeros(len(n_e)))
# Check format of electron temperature (tensor)
assert t_e.ndim == 3 and t_e.shape[1:] == (3, 3), "te must be timeseries of tensor"
t_e = trace(ts_tensor_xyz(t_e.time.data, t_e.data)) / 3
# Define constants
m_e = constants.electron_mass
q_e = constants.elementary_charge
s_surf = 34
v_eth = np.sqrt(2 * q_e * t_e.data / m_e)
sc_pot_r = resample(sc_pot, n_e)
# Thermal current in muA
i_e = n_e.data * v_eth * (1 + sc_pot_r.data / t_e.data)
i_e *= 1e12 * q_e * s_surf / (2 * np.sqrt(np.pi))
# First a simple fit of Iph to Ie using 1 photoelectron population
opt_p1 = optimize.fmin(
_f_one_pop,
x0=[500.0, 3.0],
args=(i_e, i_aspoc, sc_pot_r),
maxfun=5000,
)
# Fit of Iph to Ie for two photoelectron populations
opt_p2 = optimize.fmin(
_f_two_pop,
x0=[opt_p1[0], opt_p1[1], 10.0, 10.0],
args=(i_e, i_aspoc, sc_pot_r),
maxfun=5000,
)
i_ph0, t_ph0, i_ph1, t_ph1 = opt_p2
v_eth = np.sqrt(2 * q_e * resample(t_e, sc_pot).data / m_e)
n_esc = i_ph0 * np.exp(-sc_pot.data / t_ph0) + i_ph1 * np.exp(
-sc_pot.data / t_ph1,
)
n_esc /= s_surf * q_e * v_eth * (1 + sc_pot.data / resample(t_e, sc_pot).data)
n_esc *= 2 * np.sqrt(np.pi) * 1e-12
n_esc = ts_scalar(sc_pot.time.data, n_esc)
return n_esc, i_ph0, t_ph0, i_ph1, t_ph1