Source code for struphy.models.vlasov
import copy
from struphy import BaseUnits
from struphy.io.options import LiteralOptions
from struphy.models.base import StruphyModel
from struphy.models.scalars import KineticEnergyPIC, Scalars
from struphy.models.species import (
ParticleSpecies,
)
from struphy.models.variables import PICVariable
from struphy.propagators.push_eta import PushEta
from struphy.propagators.push_vxb import PushVxB
[docs]
class Vlasov(StruphyModel):
"""Vlasov equation for a single species in a static background magnetic field.
Parameters
----------
base_units: BaseUnits
Base units for normalization (default: BaseUnits())
charge_number: int
Charge number (in units of the positive elementary charge) of the species (default: 1)
mass_number: float
Mass number (in units of Proton mass) of the species (default: 1.0)
"""
@classmethod
def model_type(cls) -> LiteralOptions.ModelTypes:
return "Kinetic"
## species
class KineticIons(ParticleSpecies):
def __init__(
self,
charge_number: int = 1,
mass_number: float = 1.0,
):
self.var = PICVariable(space="Particles6D")
self.init_variables(
charge_number=charge_number,
mass_number=mass_number,
)
## propagators
class Propagators:
def __init__(self):
self.push_vxb = PushVxB()
self.push_eta = PushEta()
## abstract methods
def __init__(
self,
base_units: BaseUnits = BaseUnits(),
charge_number: int = 1,
mass_number: float = 1.0,
):
# 0. store input parameters
self.params = copy.deepcopy(locals())
# 1. instantiate all species
self.kinetic_ions = self.KineticIons(
charge_number,
mass_number,
)
# 2. derive units (must be done after instantiating species to access charge and mass numbers)
self.setup_equation_params(base_units=base_units)
# 3. instantiate all propagators
self.propagators = self.Propagators()
# 4. assign variables to propagators
self.propagators.push_vxb.variables.ions = self.kinetic_ions.var
self.propagators.push_eta.variables.var = self.kinetic_ions.var
# 5. define scalars to be tracked during simulation
kinetic_energy = KineticEnergyPIC(self.kinetic_ions.var)
self.scalars = Scalars(kinetic_energy=kinetic_energy)
@property
def bulk_species(self):
return self.kinetic_ions
@property
def velocity_scale(self):
return "cyclotron"
def post_allocate(self):
pass
[docs]
@classmethod
def doc_pde(cls):
r"""**PDEs solved by model:**
Vlasov equation:
.. math::
\frac{\partial f}{\partial t} + \mathbf{v} \cdot \nabla f + \left( \mathbf{v} \times \mathbf{B}_0 \right) \cdot \frac{\partial f}{\partial \mathbf{v}} = 0
"""
[docs]
@classmethod
def doc_normalization(cls):
r"""The characteristic speed is the cyclotron scale
.. math::
\hat v = \hat\Omega_c \hat x.
"""
[docs]
@classmethod
def doc_scalar_quantities(cls):
r"""**The following scalars are tracked during simulation:**
- Particle kinetic energy: ``kinetic_energy``"""
[docs]
@classmethod
def doc_discretization(cls):
"""Time integration is performed by the following propagators (in sequence):
1. :class:`~struphy.propagators.push_vxb.PushVxB`
2. :class:`~struphy.propagators.push_eta.PushEta`
"""
doc = rf"""**1. push_vxb.PushVxB:**
{PushVxB.__doc__}
**2. push_eta.PushEta:**
{PushEta.__doc__}
"""
return doc
[docs]
@classmethod
def doc_long_description(cls):
r"""Vlasov is the simplest kinetic test-particle model in the 6D hierarchy.
It evolves particles in a static magnetic background without electric or
magnetic self-consistency."""
[docs]
@classmethod
def doc_examples(cls):
r"""Create and initialize a Vlasov test-particle model:
.. code-block:: python
from struphy.models import Vlasov
model = Vlasov()
model.kinetic_ions.var
"""
[docs]
@classmethod
def doc_use_cases(cls):
r"""This model is appropriate for:
- test-particle motion in prescribed magnetic fields
- verification of the Boris-like VxB and PushEta splitting
- reduced kinetic transport studies without field feedback"""
[docs]
@classmethod
def doc_cannot_be_used_for(cls):
r"""This model is not suitable for:
- self-consistent electrostatic or electromagnetic coupling
- collisional kinetic dynamics
- guiding-center reduction studies
- fluid or MHD-scale closures"""