Source code for scisalt.PWFA.ions1d
import os as _os
_on_rtd = _os.environ.get('READTHEDOCS', None) == 'True'
if not _on_rtd:
import scipy.constants as _spc
import scipy.special as _spsp
import numpy as _np
import scipy as _sp
# ============================
# Constants
# ============================
e = _spc.e # Elementary charge
c = _spc.speed_of_light # Speed of light
e0 = _spc.epsilon_0 # Vacuum permittivity
amu = _spc.atomic_mass # AMU in kg
from .ions import Ions as _Ions
import logging as _logging
_logger = _logging.getLogger(__name__)
[docs]class Ions1D(_Ions):
"""
.. versionadded:: 1.7
A class to facilitate calculating ion motion in PWFA ion columns due to planar, infinitely-long gaussian beams.
"""
def __init__(self, species, N_e, sig_x, sig_y, sig_xi, r0_big=None, n_samp=1000, order=5, rtol=None, atol=None):
# ============================
# Experiment Parameters
# ============================
super().__init__(dims=1, species=species, N_e=N_e, sig_xi=sig_xi, rtol=rtol, atol=atol)
if sig_x < sig_y:
raise ValueError('Assumption that (sig_x = {}) > (sig_y = {}) not met.'.format(sig_x, sig_y))
else:
self._sig_x = sig_x # Transverse R.M.S. width
self._sig_y = sig_y # Transverse R.M.S. width
[docs] def lambda_large(self, y0):
"""
The wavelength for large (:math:`r_0 < \\sigma_r`) oscillations.
"""
return 4*_np.sqrt(2*y0/self.k)
# ============================
# Omega for large r(0)
# ============================
# def omega_big(self, r0):
# omega_r0 = _np.sqrt(_np.pi*self.k/2)
# return omega_r0/_np.sqrt(r0)
# ============================
# Scale factor function
# ============================
[docs] def h(self, q):
return 1
@property
def sig_x(self):
return self._sig_x
@property
def sig_y(self):
return self._sig_y
@property
def nb0(self):
return self.N_e / (4*_np.sqrt(3) * _np.pi * self.sig_x * self.sig_y * self.sig_xi)
@property
def k(self):
"""
Driving force term: :math:`r'' = -k \\left( \\frac{1-e^{-r^2/2{\\sigma_r}^2}}{r} \\right)`
"""
try:
return self._k
except AttributeError:
self._k = _np.sqrt(_np.pi/8) * e**2 * self.nb0 * self.sig_y / ( e0 * self.m * c**2)
return self._k
@property
def k_small(self):
"""
Small-angle driving force term: :math:`r'' = -k_{small} r`.
Note: :math:`k_{small} = \\frac{k}{2{\\sigma_r^2}}`
"""
return self.k * _np.sqrt(2/_np.pi) / self.sig_y
# return e**2 * self.nb0 / (2 * e0 * self.m * c**2)
# ============================
# Force equation
# ============================
def _yp2(self, y):
return -self.k*_spsp.erf(y/(_np.sqrt(2)*self.sig_y))
# ============================
# Derivative of force eq.
# ============================
def _delyp2(self, y):
return - (self.k * _np.sqrt(2/_np.pi)/self.sig_y) * _np.exp(-y**2/(2*self.sig_y**2))
# ============================
# First-order ODE system
# ============================
def _func(self, y, t):
return [self._yp2(y[1]), y[0]]
# ============================
# Gradient for 1st-order ODE
# ============================
def _gradient(self, y, t):
return [[0, self._delyp2(y[1])], [1, 0]]
@property
def q_label(self):
return 'y'