Models#
Full-orbit Vlasov#
- class struphy.models.VlasovAmpereOneSpecies(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), charge_number: int = 1, mass_number: float = 1.0, alpha: float | None = None, epsilon: float | None = None, with_B0: bool = True)[source]#
Bases:
StruphyModelVlasov-Ampère system for a single kinetic species in an electric 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 ion species (default: 1)
mass_number (float) – Mass number (in units or Proton mass) of the ion species (default: 1.0)
alpha (float, optional) – Dimensionless parameter: plasma frequency / cyclotron frequency. If None, computed from units and charge/mass numbers.
epsilon (float, optional) – Normalized cyclotron period: 1 / (cyclotron frequency × time unit). If None, computed from units and charge/mass numbers.
with_B0 (bool) – Whether to include the effect of a background magnetic field B0 (default: True)
- classmethod doc_pde()[source]#
PDEs solved by model:
Vlasov equation:
\[\frac{\partial f}{\partial t} + \mathbf{v} \cdot \nabla f + \frac{1}{\varepsilon} \left( \mathbf{E} + \mathbf{v} \times \mathbf{B}_0 \right) \cdot \frac{\partial f}{\partial \mathbf{v}} = 0\]Ampère’s law:
\[-\frac{\partial \mathbf{E}}{\partial t} = \frac{\alpha^2}{\varepsilon} \int_{\mathbb{R}^3} \mathbf{v} f \, \mathrm{d}^3 \mathbf{v}\]Initial Poisson equation: At \(t=0\), solve weakly for the electric potential \(\phi\):
\[\begin{split}\int_{\Omega} \nabla \psi^{\top} \cdot \nabla \phi \, \mathrm{d} \mathbf{x} = \frac{\alpha^2}{\varepsilon} \int_{\Omega} \int_{\mathbb{R}^3} \psi \, (f - f_0) \, \mathrm{d}^3 \mathbf{v} \, \mathrm{d} \mathbf{x} \qquad \forall \ \psi \in H^1 \\[2mm] \mathbf{E}(t=0) = -\nabla \phi(t=0)\end{split}\]
- classmethod doc_normalization()[source]#
Velocity and field normalizations:
\[\hat{v} = c, \qquad \hat{E} = \hat{B} \hat{v}, \qquad \hat{\phi} = \hat{E} \hat{x}\]Dimensionless parameters:
\[\alpha = \frac{\hat{\omega}_\mathrm{p}}{\hat{\omega}_\mathrm{c}}, \qquad \varepsilon = \frac{1}{\hat{\omega}_\mathrm{c} \hat{t}}\]where
\[\hat{\omega}_\mathrm{p} = \sqrt{\frac{\hat{n} (Ze)^2}{\epsilon_0 (A m_\mathrm{H})}}, \qquad \hat{\omega}_\mathrm{c} = \frac{(Ze) \hat{B}}{(A m_\mathrm{H})}\]For electrons: \(Z = -1\), \(A = 1/1836\).
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Electric field energy: \(E_E = \frac{1}{2} \int |\mathbf{E}|^2 \, \mathrm{d}V\)
Kinetic energy: \(E_f = \frac{\alpha^2}{2N} \sum_p w_p |\mathbf{v}_p|^2\)
Total energy: \(E_\mathrm{tot} = E_E + E_f\)
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
PushVxB(ifwith_B0is True)
- classmethod doc_long_description()[source]#
This model couples the kinetic Vlasov equation for the particle distribution function with Ampère’s law for the electric field evolution. It includes the effect of a static background magnetic field \(\mathbf{B}_0\) and solves the initial Poisson equation to satisfy Gauss’s law at \(t=0\). The model uses a particle-in-cell (PIC) method with finite element exterior calculus (FEEC) for the electromagnetic fields.
All field variables are perturbations around a reference equilibrium distribution. The model enables studies of kinetic instabilities, particle-wave interactions, and nonlinear plasma physics without assuming a fluid approximation.
- classmethod doc_examples()[source]#
Create and initialize a Vlasov-Ampère model:
from struphy.models import VlasovAmpereOneSpecies model = VlasovAmpereOneSpecies() # Access fields model.em_fields.e_field model.em_fields.phi model.kinetic_ions.var # Access tracked scalar quantities model.scalars["electric_energy"] model.scalars["kinetic_energy"] model.scalars["total_energy"]
- classmethod doc_use_cases()[source]#
This model is appropriate for:
studying kinetic instabilities in collisionless plasmas
particle-wave interactions and nonlinear plasma physics
initial value problems with prescribed equilibrium distributions
benchmark problems for kinetic-field coupling schemes
verification of PIC methods in simplified geometries
- class struphy.models.LinearVlasovAmpereOneSpecies(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), charge_number: int = 1, mass_number: float = 1.0, alpha: float | None = None, epsilon: float | None = None, with_B0: bool = True, with_E0: bool = True)[source]#
Bases:
StruphyModelLinearized Vlasov-Ampère equations for one kinetic species around a Maxwellian background.
- 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)
alpha (float, optional) – Dimensionless parameter: plasma frequency / cyclotron frequency. If None, computed from units and charge/mass numbers.
epsilon (float, optional) – Normalized cyclotron period: 1 / (cyclotron frequency × time unit). If None, computed from units and charge/mass numbers.
with_B0 (bool) – Whether to include the effect of a background magnetic field B0 (default: True)
with_E0 (bool) – Whether to include the effect of a background electric field E0 (default: True)
- classmethod doc_pde()[source]#
PDEs solved by model:
Linearized Ampère’s law:
\[\frac{\partial \tilde{\mathbf{E}}}{\partial t} = -\frac{\alpha^2}{\varepsilon} \int_{\mathbb{R}^3} \mathbf{v} \tilde{f} \, \textrm{d}^3 \mathbf{v}\]Linearized Vlasov equation:
\[\frac{\partial \tilde{f}}{\partial t} + \mathbf{v} \cdot \nabla \tilde{f} + \frac{1}{\varepsilon} \left( \mathbf{E}_0 + \mathbf{v} \times \mathbf{B}_0 \right) \cdot \frac{\partial \tilde{f}}{\partial \mathbf{v}} = \frac{1}{v_{\text{th}}^2 \varepsilon} \tilde{\mathbf{E}} \cdot \mathbf{v} f_0\]where \(Z=-1\) and \(A=1/1836\) for electrons. The background distribution function \(f_0\) is a uniform Maxwellian
\[f_0 = \frac{n_0(\mathbf{x})}{\left( \sqrt{2 \pi} v_{\text{th}} \right)^3} \exp \left( - \frac{|\mathbf{v}|^2}{2 v_{\text{th}}^2} \right)\]and the background electric field has to verify the following compatibility condition between with background density
\[\nabla_{\mathbf{x}} \ln (n_0(\mathbf{x})) = \frac{1}{v_{\text{th}}^2 \varepsilon} \mathbf{E}_0\]At initial time the weak Poisson equation is solved once to weakly satisfy Gauss’ law,
\[\begin{split}\int_\Omega \nabla \psi^\top \cdot \nabla \phi \, \textrm{d} \mathbf{x} &= \frac{\alpha^2}{\varepsilon} \int_\Omega \int_{\mathbb{R}^3} \psi \, \tilde{f} \, \text{d}^3 \mathbf{v} \, \textrm{d} \mathbf{x} \qquad \forall \ \psi \in H^1 \\[2mm] \tilde{\mathbf{E}}(t=0) &= -\nabla \phi(t=0)\end{split}\]Moreover, it is assumed that
\[\int_{\mathbb{R}^3} \mathbf{v} f_0 \, \text{d}^3 \mathbf{v} = 0\]
- classmethod doc_normalization()[source]#
The light speed defines the velocity scale:
\[\hat v = c,\qquad \hat E = \hat B \hat v,\qquad \hat\phi = \hat E \hat x.\]The species parameters are \(\alpha=\hat\Omega_p/\hat\Omega_c\) and \(\varepsilon=1/(\hat\Omega_c\hat t)\).
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Electric field energy:
en_EPerturbation particle energy:
en_wTotal energy:
en_tot
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
PushVinForceField(ifwith_E0is True)PushVxB(ifwith_B0is True)
- classmethod doc_long_description()[source]#
LinearVlasovAmpereOneSpecies is the linearized delta-f counterpart of VlasovAmpereOneSpecies. It is intended for weakly perturbed kinetic problems around a prescribed Maxwellian equilibrium and is especially useful for linear instability or damping studies.
- classmethod doc_examples()[source]#
Create and initialize the linear Vlasov-Ampère model:
from struphy.models import LinearVlasovAmpereOneSpecies model = LinearVlasovAmpereOneSpecies() model.em_fields.e_field model.em_fields.phi model.kinetic_ions.var
- class struphy.models.VlasovMaxwellOneSpecies(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), charge_number: int = 1, mass_number: float = 1.0, alpha: float | None = None, epsilon: float | None = None, measure_gauss_law: bool = False)[source]#
Bases:
StruphyModelVlasov-Maxwell equations for one kinetic species.
- 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)
alpha (float, optional) – Dimensionless parameter: plasma frequency / cyclotron frequency. If None, computed from units and charge/mass numbers.
epsilon (float, optional) – Normalized cyclotron period: 1 / (cyclotron frequency × time unit). If None, computed from units and charge/mass numbers.
measure_gauss_law (bool) – Whether to track the Gauss-law error as a scalar quantity (default: False)
- classmethod doc_pde()[source]#
PDEs solved by model:
Vlasov equation:
\[\frac{\partial f}{\partial t} + \mathbf{v} \cdot \nabla f + \frac{1}{\varepsilon} \left( \mathbf{E} + \mathbf{v} \times \left( \mathbf{B} + \mathbf{B}_0 \right) \right) \cdot \frac{\partial f}{\partial \mathbf{v}} = 0\]Ampère’s law:
\[-\frac{\partial \mathbf{E}}{\partial t} + \nabla \times \mathbf{B} = \frac{\alpha^2}{\varepsilon} \int_{\mathbb{R}^3} \mathbf{v} f \, \text{d}^3 \mathbf{v}\]Faraday’s law:
\[\frac{\partial \mathbf{B}}{\partial t} + \nabla \times \mathbf{E} = 0\]where \(Z=-1\) and \(A=1/1836\) for electrons.
At initial time the weak Poisson equation is solved once to weakly satisfy Gauss’ law,
\[\begin{split}\int_{\Omega} \nabla \psi^{\top} \cdot \nabla \phi \, \textrm{d} \mathbf{x} = \frac{\alpha^2}{\varepsilon} \int_{\Omega} \int_{\mathbb{R}^3} \psi \, (f - f_0) \, \text{d}^3 \mathbf{v} \, \textrm{d} \mathbf{x} \qquad \forall \ \psi \in H^1 \\[2mm] \mathbf{E}(t=0) = -\nabla \phi(t=0)\end{split}\]Moreover, it is assumed that
\[\nabla \times \mathbf{B}_0 = \frac{\alpha^2}{\varepsilon} \int_{\mathbb{R}^3} \mathbf{v} f_0 \, \text{d}^3 \mathbf{v}\]where \(\mathbf{B}_0\) is the static equilibrium magnetic field.
- classmethod doc_normalization()[source]#
The model uses the light speed as reference velocity:
\[\hat v = c,\qquad \hat E = \hat B \hat v,\qquad \hat\phi = \hat E \hat x.\]The species parameters are \(\alpha=\hat\Omega_p/\hat\Omega_c\) and \(\varepsilon=1/(\hat\Omega_c\hat t)\).
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Electric field energy:
en_EMagnetic field energy:
en_BParticle kinetic energy:
en_fTotal energy:
en_totOptional Gauss-law diagnostic:
gauss_error
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
VlasovMaxwellOneSpecies is the fully electromagnetic one-species PIC model in Struphy. It evolves particles and fields self-consistently and supports an optional control-variate formulation for the field coupling.
- classmethod doc_examples()[source]#
Create and initialize a Vlasov-Maxwell model:
from struphy.models import VlasovMaxwellOneSpecies model = VlasovMaxwellOneSpecies() model.em_fields.e_field model.em_fields.b_field model.kinetic_ions.var
- class struphy.models.VlasovMaxwellQuasiNeutral(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), charge_number: int = 1, mass_number: float = 1.0, alpha: float | None = None, epsilon: float | None = None, lam: float | None = None, kappa: float | None = None, measure_gauss_law: bool = False)[source]#
Bases:
VlasovMaxwellOneSpeciesVlasov-Maxwell equations for one kinetic species in the cyclotron normalization, with an implicit, energy conserving field-particle coupling that also holds in the quasi-neutral limit (no displacement current).
Same equations as
VlasovMaxwellOneSpecies, normalized with the cyclotron frequency (velocity scale"cyclotron"), so that the coefficients \(\lambda^2\) of the displacement current and \(\kappa^2\) of the plasma current appear in Ampère’s law. Ampère’s law, Faraday’s law and the electric force on the particles are solved together in one Crank-Nicolson step (VlasovMaxwellCoupling);lam=0.0is the quasi-neutral limit.- 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)
alpha (float, optional) – Dimensionless parameter: plasma frequency / cyclotron frequency. If None, computed from units and charge/mass numbers. It does not enter the equations of this model (which use lam and kappa = alpha lam); when overriding lam and kappa, alpha is not adapted.
epsilon (float, optional) – Normalized cyclotron period: 1 / (cyclotron frequency × time unit). If None, computed from units and charge/mass numbers (equal to 1 in this model’s normalization).
lam (float, optional) – Cyclotron frequency × length unit / speed of light (coefficient lam^2 of the displacement current; 0 is the quasi-neutral limit). If None, computed from units and charge/mass numbers.
kappa (float, optional) – Plasma frequency × length unit / speed of light (coefficient kappa^2 of the plasma current). If None, computed from units and charge/mass numbers.
measure_gauss_law (bool) – Whether to track the Gauss-law error as a scalar quantity (default: False; not available for lam = 0)
- classmethod doc_pde()[source]#
PDEs solved by model:
Vlasov equation:
\[\frac{\partial f}{\partial t} + \mathbf{v} \cdot \nabla f + \left( \mathbf{E} + \mathbf{v} \times \left( \mathbf{B} + \mathbf{B}_0 \right) \right) \cdot \frac{\partial f}{\partial \mathbf{v}} = 0\]Ampère’s law:
\[-\lambda^2 \frac{\partial \mathbf{E}}{\partial t} + \nabla \times \mathbf{B} = \kappa^2 \int_{\mathbb{R}^3} \mathbf{v} f \, \text{d}^3 \mathbf{v}\]Faraday’s law:
\[\frac{\partial \mathbf{B}}{\partial t} + \nabla \times \mathbf{E} = 0\]For \(\lambda = 0\) (quasi-neutral limit) Ampère’s law becomes \(\nabla \times \mathbf B = \kappa^2 \int \mathbf v f \, \textrm d^3 \mathbf v\), which implies \(\nabla \cdot \mathbf J = 0\); the electric field is then determined by this constraint, not by its own evolution. The coefficients of the coupling terms can be changed by the options
lam,c1,c2,c3of the propagatorVlasovMaxwellCoupling.At initial time the weak Poisson equation (Gauss’ law \(\lambda^2 \nabla \cdot \mathbf E = \kappa^2 \rho\)) is solved once for \(\lambda > 0\),
\[\begin{split}\lambda^2 \int_{\Omega} \nabla \psi^{\top} \cdot \nabla \phi \, \textrm{d} \mathbf{x} = \kappa^2 \int_{\Omega} \int_{\mathbb{R}^3} \psi \, (f - f_0) \, \text{d}^3 \mathbf{v} \, \textrm{d} \mathbf{x} \qquad \forall \ \psi \in H^1 \\[2mm] \mathbf{E}(t=0) = -\nabla \phi(t=0)\end{split}\](for \(\lambda = 0\) the initial electric field does not enter the time stepping). Moreover, it is assumed that \(\nabla \times \mathbf{B}_0 = \kappa^2 \int_{\mathbb{R}^3} \mathbf{v} f_0 \, \text{d}^3 \mathbf{v}\), where \(\mathbf{B}_0\) is the static equilibrium magnetic field.
- classmethod doc_normalization()[source]#
The model uses the cyclotron velocity of the species as reference velocity:
\[\hat v = \hat\Omega_c \hat x,\qquad \hat t = \frac{1}{\hat\Omega_c},\qquad \hat E = \hat B \hat v,\qquad \hat\phi = \hat E \hat x.\]The species parameters (see Equation parameters) are
\[\lambda = \frac{\hat\Omega_c \hat x}{c}\,,\qquad \kappa = \frac{\hat\Omega_p \hat x}{c} = \alpha \lambda\,,\]where \(c/\hat\Omega_c\) and \(c/\hat\Omega_p\) are the inertial lengths (\(\lambda\) carries the sign of the charge, like \(\hat\Omega_c\); only \(\lambda^2\) enters). The Vlasov equation and Faraday’s law then have no coefficients, and Ampère’s law \(\frac{1}{c^2} \partial_t \mathbf E = \nabla \times \mathbf B - \mu_0 \mathbf J\) becomes \(\lambda^2 \partial_t \mathbf E = \nabla \times \mathbf B - \kappa^2 \mathbf J\). The quasi-neutral limit is \(\lambda \to 0\) at fixed \(\kappa\). With \(\lambda = \kappa = 1\) the equations are those of
VlasovMaxwellOneSpecieswith \(\alpha = 1\), i.e. lengths in units of \(c/\hat\Omega_p\) and \(\hat\Omega_c = \hat\Omega_p\).
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation (the parts of the energy conserved by the coupling step, with the coefficients \(\lambda^2, c_1, c_2, c_3\) of
VlasovMaxwellCoupling):Electric field energy \(\frac{\lambda^2}{2} \int |\mathbf E|^2 \textrm d \mathbf x\):
en_EMagnetic field energy \(\frac{c_1}{2} \int |\mathbf B|^2 \textrm d \mathbf x\):
en_BParticle kinetic energy \(\frac{c_2}{2 c_3} \sum_p w_p |\mathbf v_p|^2\):
en_f(\(\frac{\kappa^2}{2} \sum_p w_p |\mathbf v_p|^2\) for the default coefficients)Total energy:
en_tot(conserved by the coupling step up to the solver tolerance)Optional Gauss-law diagnostic:
gauss_error(NaN for \(\lambda = 0\))
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
VlasovMaxwellQuasiNeutral solves the Vlasov-Maxwell system in the cyclotron normalization, with one implicit step for Maxwell’s equations and the electric force, which conserves the energy exactly and allows time steps larger than the inverse plasma frequency and the light-wave CFL limit. The coefficient \(\lambda^2\) of the displacement current can be set to zero (
lam=0), which gives the quasi-neutral (Darwin-type) limit, in which the electric field is the Lagrange multiplier of a divergence-free current.
- classmethod doc_examples()[source]#
Create a quasi-neutral Vlasov-Maxwell model (lengths in units of the skin depth):
from struphy.models import VlasovMaxwellQuasiNeutral model = VlasovMaxwellQuasiNeutral(lam=0.0, kappa=1.0) model.em_fields.b_field model.kinetic_ions.var
- classmethod doc_use_cases()[source]#
This model is appropriate for:
electromagnetic kinetic simulations with time steps above the plasma period
the quasi-neutral limit of the Vlasov-Maxwell system (
lam=0.0)comparison with
VlasovMaxwellOneSpecies
- class struphy.models.LinearVlasovMaxwellOneSpecies(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), charge_number: int = 1, mass_number: float = 1.0, alpha: float | None = None, epsilon: float | None = None, with_B0: bool = True, with_E0: bool = True)[source]#
Bases:
LinearVlasovAmpereOneSpeciesLinearized Vlasov-Maxwell equations for one kinetic species around a Maxwellian background.
- 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)
alpha (float, optional) – Dimensionless parameter: plasma frequency / cyclotron frequency. If None, computed from units and charge/mass numbers.
epsilon (float, optional) – Normalized cyclotron period: 1 / (cyclotron frequency × time unit). If None, computed from units and charge/mass numbers.
with_B0 (bool) – Whether to include the effect of a background magnetic field B0 (default: True)
with_E0 (bool) – Whether to include the effect of a background electric field E0 (default: True)
- classmethod doc_pde()[source]#
PDEs solved by model:
Linearized Ampère’s law:
\[\frac{\partial \tilde{\mathbf{E}}}{\partial t} = \nabla \times \tilde{\mathbf{B}} - \frac{\alpha^2}{\varepsilon} \int_{\mathbb{R}^3} \mathbf{v} \tilde{f} \, \textrm{d}^3 \mathbf{v}\]Linearized Faraday’s law:
\[\frac{\partial \tilde{\mathbf{B}}}{\partial t} = -\nabla \times \tilde{\mathbf{E}}\]Linearized Vlasov equation:
\[\frac{\partial \tilde{f}}{\partial t} + \mathbf{v} \cdot \nabla \tilde{f} + \frac{1}{\varepsilon} \left( \mathbf{E}_0 + \mathbf{v} \times \mathbf{B}_0 \right) \cdot \frac{\partial \tilde{f}}{\partial \mathbf{v}} = \frac{1}{v_{\text{th}}^2 \varepsilon} \tilde{\mathbf{E}} \cdot \mathbf{v} f_0\]where \(Z=-1\) and \(A=1/1836\) for electrons. The background distribution function \(f_0\) is a uniform Maxwellian
\[f_0 = \frac{n_0(\mathbf{x})}{\left( \sqrt{2 \pi} v_{\text{th}} \right)^3} \exp \left( - \frac{|\mathbf{v}|^2}{2 v_{\text{th}}^2} \right)\]and the background electric field has to verify the following compatibility condition between with background density
\[\nabla_{\mathbf{x}} \ln (n_0(\mathbf{x})) = \frac{1}{v_{\text{th}}^2 \varepsilon} \mathbf{E}_0\]At initial time the weak Poisson equation is solved once to weakly satisfy Gauss’ law,
\[\begin{split}\int_\Omega \nabla \psi^\top \cdot \nabla \phi \, \textrm{d} \mathbf{x} &= \frac{\alpha^2}{\varepsilon} \int_\Omega \int_{\mathbb{R}^3} \psi \, \tilde{f} \, \text{d}^3 \mathbf{v} \, \textrm{d} \mathbf{x} \qquad \forall \ \psi \in H^1 \\[2mm] \tilde{\mathbf{E}}(t=0) &= -\nabla \phi(t=0)\end{split}\]Moreover, it is assumed that
\[\int_{\mathbb{R}^3} \mathbf{v} f_0 \, \text{d}^3 \mathbf{v} = 0\]
- classmethod doc_normalization()[source]#
The normalization matches the linear Vlasov-Ampère model:
\[\hat v = c,\qquad \hat E = \hat B \hat v,\qquad \hat\phi = \hat E \hat x.\]The model retains the same \(\alpha\) and \(\varepsilon\) parameters while adding magnetic perturbation dynamics.
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Electric field energy:
en_EMagnetic field energy:
en_BPerturbation particle energy:
en_wTotal energy:
en_tot
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
PushVinForceField(ifwith_E0is True)PushVxB(ifwith_B0is True)
- classmethod doc_long_description()[source]#
LinearVlasovMaxwellOneSpecies is the electromagnetic extension of the linear delta-f Vlasov model. It is intended for weakly perturbed kinetic electromagnetic waves and instabilities around a prescribed equilibrium.
- classmethod doc_examples()[source]#
Create and initialize the linear Vlasov-Maxwell model:
from struphy.models import LinearVlasovMaxwellOneSpecies model = LinearVlasovMaxwellOneSpecies() model.em_fields.e_field model.em_fields.b_field model.kinetic_ions.var
- class struphy.models.VlasovPoissonAdiabatic(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=1.0), charge_number: int = 1, mass_number: float = 1.0, alpha: float | None = None, epsilon: float | None = None, with_B0: bool = True)[source]#
Bases:
StruphyModelElectrostatic full-orbit (6D) model for a single ion species with adiabatic electrons.
The full-orbit counterpart of
DriftKineticElectrostaticAdiabatic: the ions are not reduced to guiding centers, so the polarization density is not modelled but comes out of the ion orbits. The electrons enter through the adiabatic (Boltzmann) response in the Poisson equation, optionally with the same flux-surface average as in the drift-kinetic model.- Parameters:
base_units (BaseUnits) – Base units for normalization (default:
BaseUnits(kBT=1.0)).charge_number (int) – Charge number (in units of the positive elementary charge) of the ion species (default: 1).
mass_number (float) – Mass number (in units of the proton mass) of the ion species (default: 1.0).
alpha (float, optional) – Ratio of plasma to cyclotron frequency \(\hat \omega_p / \hat \Omega_c\). If
None, computed frombase_unitsand the charge/mass numbers.epsilon (float, optional) – Normalized inverse cyclotron frequency \(1 / (\hat{\Omega}_\mathrm{c} \hat{t})\). If
None, computed frombase_unitsand the charge/mass numbers.with_B0 (bool) – Whether to include the Lorentz force of the background magnetic field (default: True).
- classmethod doc_pde()[source]#
PDEs solved by model:
Vlasov equation:
\[\frac{\partial f}{\partial t} + \mathbf{v} \cdot \nabla f + \frac{1}{\varepsilon} \left( -\nabla \phi + \mathbf{v} \times \mathbf{B}_0 \right) \cdot \frac{\partial f}{\partial \mathbf{v}} = 0\]Poisson equation with adiabatic electrons:
\[-\frac{\varepsilon}{\alpha^2} \Delta \phi + \frac{1}{Z \varepsilon} \frac{n_0}{T_0} \left( \phi - \langle \phi \rangle \right) = \int_{\mathbb{R}^3} f \, \mathrm{d}^3 \mathbf{v} - n_0\]where \(f(\mathbf{x}, \mathbf{v}, t)\) is the ion distribution function, \(n_0\) the equilibrium ion density, \(T_0\) the electron temperature and \(Z\) the ion charge number. The vacuum term \(-\varepsilon \alpha^{-2} \Delta \phi\) is dropped in the quasi-neutral limit \(\alpha \to \infty\) (option
quasi_neutral). The flux-surface average \(\langle \phi \rangle\) (zonal correction, as inDriftKineticElectrostaticAdiabatic) is optional; by default \(\langle \phi \rangle = 0\).
- classmethod doc_normalization()[source]#
The reference speed is the ion thermal speed and the electrostatic fields are scaled accordingly:
\[\hat v = \sqrt{k_B \hat T / m_i},\qquad \hat E = \hat v \hat B,\qquad \hat\phi = \hat E \hat x,\]with temperatures in units of \(k_B \hat T\). The dimensionless parameters are
\[\varepsilon = \frac{1}{\hat\Omega_c\hat t} = \frac{\hat \rho}{\hat x}, \qquad \alpha = \frac{\hat{\omega}_\mathrm{p}}{\hat{\Omega}_\mathrm{c}},\]such that \(\varepsilon / \alpha\) is the normalized Debye length.
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation (their sum is conserved by the model equations):
Field energy:
en_phi\(= \frac{1}{2\alpha^2} \int |\nabla \phi|^2 \, \mathrm d \mathbf x + \frac{1}{2 Z \varepsilon^2} \int \frac{n_0}{T_0} \phi (\phi - \langle \phi \rangle) \, \mathrm d \mathbf x\) (the first term only ifquasi_neutral=False)Kinetic energy:
en_particles\(= \frac{1}{2} \sum_p w_p |\mathbf{v}_p|^2\) (of \(f - f_0\) with the control variate)Total energy:
en_tot
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
PushVxB(ifwith_B0is True)
- classmethod doc_long_description()[source]#
This model is the full-orbit (6D) counterpart of the electrostatic drift-kinetic model
DriftKineticElectrostaticAdiabatic. The ions follow full Lorentz-force orbits in the self-consistent electrostatic field and a static background magnetic field. Electrons are not evolved; they enter through a linearized Boltzmann response in the Poisson equation, optionally without response to the flux-surface-averaged potential (zonal correction), as in the drift-kinetic model. The potential is recomputed from the marker positions at each time step.In the strong-field limit \(\varepsilon \to 0\) with drift ordering, the linear dispersion relation of this model reduces to that of the drift-kinetic model (up to finite-Larmor-radius corrections of order \(k_\perp^2 \rho_i^2\)), which makes it a reference for verifying guiding-center models. The time step must resolve the cyclotron motion, \(\Delta t \lesssim \varepsilon\). In the quasi-neutral limit it must also resolve the ion-acoustic frequency of the smallest cells, \(\Delta t\, c_s \pi / h \lesssim 2\) (there is no Debye cutoff).
- classmethod doc_examples()[source]#
Create and initialize the full-orbit adiabatic-electron model:
from struphy.models import VlasovPoissonAdiabatic model = VlasovPoissonAdiabatic() model.em_fields.phi model.kinetic_ions.var # quasi-neutral (default) or with the vacuum term, optionally with the zonal correction model.propagators.poisson.options = model.propagators.poisson.Options( quasi_neutral=False, which_geometry="cylindrical" )
- classmethod doc_use_cases()[source]#
This model is appropriate for:
electrostatic ion dynamics with adiabatic electrons, including cyclotron and finite-Larmor-radius effects
ion-acoustic waves, ion Bernstein waves, drift waves and slab ITG modes in the full-orbit description
verification of drift-kinetic and gyrokinetic models in the strong-field limit
- classmethod doc_cannot_be_used_for()[source]#
This model is not suitable for:
electromagnetic dynamics with evolving magnetic perturbations
electron kinetic effects beyond the adiabatic closure
long simulations in strongly magnetized plasmas, where resolving the cyclotron motion is too expensive
multi-species kinetic coupling without extending the model
- class struphy.models.VlasovPoissonAdiabaticPauli(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=1.0), charge_number: int = 1, mass_number: float = 1.0, alpha: float | None = None, epsilon: float | None = None, with_poisson: bool = True)[source]#
Bases:
VlasovPoissonAdiabaticGuiding-center ion dynamics with adiabatic electrons, computed from classical Pauli particles.
The ions are full-orbit (6D) markers with a magnetic moment (classical Pauli particles, Xiao and Qin 2021), loaded on the slow manifold without gyration. They follow guiding-center orbits with the full-orbit pushers of
VlasovPoissonAdiabatic, with time steps larger than the cyclotron period. With the control variate (delta-f), the polarization density must be added in the Poisson equation (AdiabaticPoissonSolve.Options(polarization=True)); without the Poisson solve (with_poisson=False) the model computes guiding-center orbits in the equilibrium magnetic field.- Parameters:
base_units (BaseUnits) – Base units for normalization (default:
BaseUnits(kBT=1.0)).charge_number (int) – Charge number (in units of the positive elementary charge) of the ion species (default: 1).
mass_number (float) – Mass number (in units of the proton mass) of the ion species (default: 1.0).
alpha (float, optional) – Ratio of plasma to cyclotron frequency \(\hat \omega_p / \hat \Omega_c\). If
None, computed frombase_unitsand the charge/mass numbers.epsilon (float, optional) – Normalized inverse cyclotron frequency \(1 / (\hat{\Omega}_\mathrm{c} \hat{t})\). If
None, computed frombase_unitsand the charge/mass numbers.with_poisson (bool) – Whether to solve the Poisson equation for the self-consistent potential (default: True); if False, the markers move in the equilibrium magnetic field only (orbit studies).
- classmethod doc_pde()[source]#
PDEs solved by model:
Vlasov equation for classical Pauli particles, with the magnetic moment \(\mu \geq 0\) as a parameter of the distribution function \(f(\mathbf{x}, \mathbf{v}, \mu, t)\):
\[\frac{\partial f}{\partial t} + \mathbf{v} \cdot \nabla f + \left[ \frac{1}{\varepsilon} \left( \mathbf{E}^\dagger + \mathbf{v} \times \mathbf{B}_0 \right) \right] \cdot \frac{\partial f}{\partial \mathbf{v}} = 0\,, \qquad \mathbf{E}^\dagger = -\nabla \left( \phi + \varepsilon \mu |\mathbf{B}_0| \right)\,,\]with \(f\) concentrated on the slow manifold \(\mathbf v \times \mathbf b_0 \approx 0\). Poisson equation with adiabatic electrons:
\[-\frac{\varepsilon}{\alpha^2} \Delta \phi + \frac{1}{Z \varepsilon} \frac{n_0}{T_0} \left( \phi - \langle \phi \rangle \right) = \int_0^\infty \int_{\mathbb{R}^3} f \, \mathrm{d}^3 \mathbf{v}\, \mathrm{d} \mu - n_0\,,\]where \(n_0\) is the equilibrium ion density, \(T_0\) the electron temperature and \(Z\) the ion charge number. On the slow manifold the marker positions are guiding-center positions, so the charge density is the guiding-center density (no gyro-average). With full-f weights the polarization density comes out of the marker orbits (compression by the polarization drift). With the control variate it is removed from the deposited density (see the long description) and is added in the quasi-neutrality equation, as for guiding centers (option
polarizationof the Poisson solve):\[-\varepsilon \nabla \cdot \left( \frac{n_0}{|\mathbf B_0|^2} \nabla_\perp \phi \right) + \frac{1}{Z \varepsilon} \frac{n_0}{T_0} \left( \phi - \langle \phi \rangle \right) = \int_0^\infty \int_{\mathbb{R}^3} (f - f_0) \, \mathrm{d}^3 \mathbf{v}\, \mathrm{d} \mu\,.\]As in
VlasovPoissonAdiabatic, the vacuum term is dropped in the quasi-neutral limit (optionquasi_neutral) and the flux-surface average \(\langle \phi \rangle\) is optional. Withwith_poisson=Falsethe Poisson equation is not solved and \(\phi = 0\).
- classmethod doc_normalization()[source]#
As in
VlasovPoissonAdiabatic: the reference speed is the ion thermal speed,\[\hat v = \sqrt{k_B \hat T / m_i},\qquad \hat E = \hat v \hat B,\qquad \hat\phi = \hat E \hat x,\qquad \hat \mu = \frac{m_i \hat v^2}{\hat B}\,,\]with the dimensionless parameters \(\varepsilon = 1/(\hat\Omega_c\hat t)\) and \(\alpha = \hat{\omega}_\mathrm{p}/\hat{\Omega}_\mathrm{c}\). The normalized magnetic moment is \(\mu = v_\perp^2 / (2 |\mathbf B_0|)\), as in the guiding-center models.
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation (the sum of the energies is conserved by the model equations):
Field energy (only if
with_poisson):en_phi\(= \frac{1}{2\alpha^2} \int |\nabla \phi|^2 \, \mathrm d \mathbf x + \frac{1}{2 Z \varepsilon^2} \int \frac{n_0}{T_0} \phi (\phi - \langle \phi \rangle) \, \mathrm d \mathbf x\) (the first term only ifquasi_neutral=False), plus the polarization energy \(\frac{1}{2} \int \frac{n_0}{|\mathbf B_0|^2} |\nabla_\perp \phi|^2 \, \mathrm d \mathbf x\) withpolarization=TrueKinetic energy:
en_particles\(= \frac{1}{2} \sum_p w_p |\mathbf{v}_p|^2\)Magnetic-moment energy:
en_mu\(= \sum_p w_p \mu_p |\mathbf B_0(\mathbf x_p)|\)Total energy:
en_tot=en_phi + en_particles + en_mu; with the control variate it is conserved only approximately (the E x B kinetic energy of the background is represented by the polarization energy, that of the delta-f weights remains inen_particles)Distance from the slow manifold:
mu_prime_max\(= \max_p |\mathbf v_p \times \mathbf b_0|^2 / (2 |\mathbf B_0|)\), Eq. (7) of Xiao and Qin; it must stay small compared to the magnetic moments.
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
PushVinForceField(withmagnetic_moment=True)
Without the Poisson solve (
with_poisson=False), propagator 2 is left out.
- classmethod doc_long_description()[source]#
Guiding-center dynamics computed with the slow manifold of the classical Pauli particle (J. Xiao and H. Qin, “Slow manifolds of classical Pauli particle enable structure-preserving geometric algorithms for guiding center dynamics”, Comput. Phys. Commun. 265, 107981 (2021)). A classical Pauli particle is a charged particle with an intrinsic magnetic moment \(\mu\), with the Lagrangian \(L = \frac{1}{2}|\dot{\mathbf x}|^2 + \dot{\mathbf x} \cdot \mathbf A - \phi - \mu B\). When its perpendicular velocity vanishes initially, its effective magnetic moment \(\mu' = |\dot{\mathbf x} \times \mathbf b|^2 / (2B)\) is an adiabatic invariant and stays close to zero: the particle moves on a slow manifold, along which its position follows the guiding-center dynamics of a particle with magnetic moment \(\mu\). Unlike the guiding-center Lagrangian, the Lagrangian of the classical Pauli particle is regular, so the full-orbit Boris scheme can be used with time steps larger than the cyclotron period.
The model is the counterpart of
VlasovPoissonAdiabatic(full orbits, with finite-Larmor-radius effects) andDriftKineticElectrostaticAdiabatic(guiding centers with the polarization density in the Poisson equation): the ions have no gyration, hence no finite-Larmor-radius effects, and the polarization drift comes out of the orbits. The magnetic moment of each marker is taken from its drawn perpendicular velocity (PauliParticles6D), such that the markers represent the gyro-average of the initial condition, given in the guiding-center position.Polarization and delta-f. The polarization drift on the slow manifold is compressible, so the phase-space density of the markers is not constant along their orbits. With full-f weights this compression gives the polarization density. The control variate subtracts the background \(f_0\) along the markers, which removes the compression part \(n_0 \nabla \cdot \boldsymbol \xi\) of the polarization density (\(\boldsymbol \xi\) the polarization displacement) from the deposited charge; the advection part \(\boldsymbol \xi \cdot \nabla n_0\) remains in the weights. Without correction, a slab drift wave then has the frequency without polarization and becomes unstable at grid scale. With the guiding-center polarization term in the Poisson equation (
polarization=True, the symmetric operator \(-\nabla \cdot (n_0 |\mathbf B_0|^{-2} \nabla_\perp \phi)\) of the drift-kinetic model, which also contains the advection part), the delta-f slab drift wave has the drift-kinetic frequency, also with time steps of 8 and 16 cyclotron periods. A weak instability remains, which needs the density gradient and is independent of the time step: in the slab example \(\mu'\) stays below \(5 \cdot 10^{-3}\) up to t = 25 and grows by a factor of about 5 per 5 time units and the field energy grows from t = 25 on (about one wave period). A likely cause is that the compression part of the polarization is now instantaneous (field equation), while its advection part follows the marker orbits, including their cyclotron response. Removing only the advection part from the polarization term does not change it. Full-f simulations of small perturbations are dominated by the noise, since the perpendicular ion response is that of cold ions.
- classmethod doc_examples()[source]#
Create and initialize the model:
from struphy.models import VlasovPoissonAdiabaticPauli model = VlasovPoissonAdiabaticPauli(epsilon=0.1) model.em_fields.phi model.kinetic_ions.var # with the control variate (delta-f): polarization density in the quasi-neutrality equation model.propagators.poisson.options = model.propagators.poisson.Options(polarization=True) # guiding-center orbits in the equilibrium magnetic field, without the Poisson solve model = VlasovPoissonAdiabaticPauli(epsilon=0.01, with_poisson=False) # Boris rotation (Cayley transform) for time steps larger than the cyclotron period model.propagators.push_vxb.options = model.propagators.push_vxb.Options(algo="implicit")
- classmethod doc_use_cases()[source]#
This model is appropriate for:
electrostatic guiding-center ion dynamics with adiabatic electrons (delta-f with
polarization=True), with time steps larger than the cyclotron period (drift waves, slab ITG modes, …)guiding-center orbits (banana orbits, grad-B and curvature drifts) in general magnetic fields, with a full-orbit pusher and time steps larger than the cyclotron period (
with_poisson=False)verification of guiding-center pushers
- classmethod doc_cannot_be_used_for()[source]#
This model is not suitable for:
small perturbations with full-f weights, which are dominated by the noise (use the control variate)
finite-Larmor-radius effects (use
VlasovPoissonAdiabatic)magnetic fields without scale separation, where the magnetic moment is not an adiabatic invariant
time-dependent magnetic fields and electromagnetic dynamics
Gyrokinetic#
- class struphy.models.DriftKineticElectrostaticAdiabatic(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=1.0), charge_number: int = 1, mass_number: float = 1.0, epsilon: float | None = None, alpha: float | None = None, use_diagnostic_poisson: bool = False)[source]#
Bases:
StruphyModelElectrostatic drift-kinetic model for a single ion species with adiabatic electrons.
Evolves the guiding-center distribution \(f(\mathbf{X}, v_\parallel, \mu, t)\) under both the \(\mathbf{E}^* \times \mathbf{B}\) drift and parallel streaming along the background magnetic field. Electrons are not evolved kinetically; instead they contribute through an adiabatic response term in the quasi-neutrality (Poisson) equation. The optional control-variate method can be activated for the field solve.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default:
BaseUnits(kBT=1.0)).charge_number (int) – Charge number (in units of the positive elementary charge) of the ion species (default: 1).
mass_number (float) – Mass number (in units of the proton mass) of the ion species (default: 1.0).
epsilon (float, optional) – Normalized inverse cyclotron frequency: \(1 / (\hat{\Omega}_\mathrm{c} \hat{t})\). If
None, computed frombase_unitsand the charge/mass numbers.
- classmethod doc_pde()[source]#
PDEs solved by model:
Drift-kinetic equation:
\[\frac{\partial f}{\partial t} + \left[ v_\parallel \frac{\mathbf{B}^*}{B^*_\parallel} + \frac{\mathbf{E}^* \times \mathbf{b}_0}{B^*_\parallel} \right] \cdot \frac{\partial f}{\partial \mathbf{X}} + \left[ \frac{1}{\varepsilon} \frac{\mathbf{B}^*}{B^*_\parallel} \cdot \mathbf{E}^* \right] \cdot \frac{\partial f}{\partial v_\parallel} = 0\]Poisson equation:
\[-\nabla_\perp \cdot \left( \frac{n_0}{|B_0|^2} \nabla_\perp \phi \right) + \frac{1}{\varepsilon} n_0 \left( 1 + \frac{1}{Z \varepsilon} \frac{1}{T_0} \phi \right) = \frac{1}{\varepsilon} \int f B^*_\parallel \, \textnormal{d} v_\parallel \textnormal{d} \mu\]where \(f(\mathbf{X}, v_\parallel, \mu, t)\) is the guiding center distribution and
\[\mathbf{E}^* = -\nabla \phi - \varepsilon \mu \nabla |B_0|, \qquad \mathbf{B}^* = \mathbf{B}_0 + \varepsilon v_\parallel \nabla \times \mathbf{b}_0, \qquad B^*_\parallel = \mathbf{B}^* \cdot \mathbf{b}_0\]The control variate method can be activated in the Poisson equation; if enabled, the following Poisson equation is solved:
\[-\nabla_\perp \cdot \left( \frac{n_0}{|B_0|^2} \nabla_\perp \phi \right) + \frac{1}{Z \varepsilon^2} \frac{n_0}{T_0} \phi = \frac{1}{\varepsilon} \int (f - f_0) B^*_\parallel \, \textnormal{d} v_\parallel \textnormal{d} \mu\]
- classmethod doc_normalization()[source]#
The reference speed is the ion thermal speed and the electrostatic fields are scaled accordingly:
\[\hat v = \hat v_i,\qquad \hat E = \hat v_i \hat B,\qquad \hat\phi = \hat E \hat x.\]The small parameter is \(\varepsilon = 1/(\hat\Omega_c\hat t)\).
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Field energy:
en_phiGuiding-center particle energy:
en_particlesTotal energy:
en_tot
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
This model is an electrostatic drift-kinetic reduction for strongly magnetized ions in a fixed magnetic equilibrium. Electrons are not evolved kinetically; instead they enter through the adiabatic response in the quasi-neutrality solve. The implementation supports control variates for the field solve.
- classmethod doc_examples()[source]#
Create and initialize the drift-kinetic adiabatic-electron model:
from struphy.models import DriftKineticElectrostaticAdiabatic model = DriftKineticElectrostaticAdiabatic() model.em_fields.phi model.kinetic_ions.var
- classmethod doc_use_cases()[source]#
This model is appropriate for:
electrostatic drift-kinetic ion turbulence studies
strongly magnetized plasmas with adiabatic electrons
guiding-center PIC verification in realistic magnetic geometry
low-frequency regimes where full gyrophase resolution is unnecessary
- classmethod doc_cannot_be_used_for()[source]#
This model is not suitable for:
fully electromagnetic dynamics with evolving magnetic perturbations
electron kinetic effects beyond the adiabatic closure
problems that require resolving full cyclotron motion
multi-species kinetic coupling without extending the model
- class struphy.models.DriftKineticMaxwell(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=1.0), charge_number: int = 1, mass_number: float = 1.0, epsilon: float | None = None, lam: float | None = None, kappa: float | None = None)[source]#
Bases:
StruphyModelElectromagnetic drift-kinetic equation for one guiding-center species coupled to Maxwell’s equations, in the thermal normalization, with an implicit, energy conserving field-particle coupling that also holds in the quasi-neutral limit.
The electric field enters the guiding-center equations through the gauge-invariant \(\mathbf E^* = \mathbf E - \varepsilon \mu \nabla |B|\). Ampère’s law contains the guiding-center current and the (linearized) polarization current; the displacement current has the coefficient \(\lambda^2 \varepsilon^2 = \hat v_\textrm{th}^2/c^2\), option
lamof the coupling propagatorDriftKineticMaxwellCoupling;lam=0.0is the quasi-neutral limit.- Parameters:
base_units (BaseUnits) – Base units for normalization (default:
BaseUnits(kBT=1.0); the thermal energy defines the velocity unit).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)
epsilon (float, optional) – Normalized cyclotron period: 1 / (cyclotron frequency × time unit). If None, computed from units and charge/mass numbers.
lam (float, optional) – Cyclotron frequency × length unit / speed of light. If None, computed from units and charge/mass numbers.
kappa (float, optional) – Plasma frequency × length unit / speed of light. If None, computed from units and charge/mass numbers.
- classmethod doc_pde()[source]#
PDEs solved by model:
Drift-kinetic equation for \(f(\mathbf X, v_\parallel, \mu, t)\):
\[\frac{\partial f}{\partial t} + \frac{v_\parallel \mathbf B^* + \mathbf E^* \times \mathbf b_0}{B^*_\parallel} \cdot \nabla f + \frac{1}{\varepsilon} \frac{\mathbf B^* \cdot \mathbf E^*}{B^*_\parallel} \frac{\partial f}{\partial v_\parallel} = 0\,,\]with
\[\mathbf E^* = \mathbf E - \varepsilon \mu \nabla |\mathbf B|\,,\qquad \mathbf B^* = \mathbf B_0 + \tilde{\mathbf B} + \varepsilon v_\parallel \nabla \times \mathbf b_0\,,\qquad B^*_\parallel = \mathbf b_0 \cdot \mathbf B^*\,,\]where \(|\mathbf B| = \mathbf b_0 \cdot (\mathbf B_0 + \tilde{\mathbf B})\): the total magnetic field in the first term of \(\mathbf B^*\) (parallel streaming along the perturbed field lines), the curvature term with the equilibrium unit vector \(\mathbf b_0\).
Ampère’s law, with the displacement current and the (linearized) polarization current of the guiding centers:
\[\varepsilon^2 \lambda^2 \frac{\partial \mathbf E}{\partial t} + \varepsilon^2 \kappa^2 \frac{n_0}{|\mathbf B_0|^2} \frac{\partial \mathbf E_\perp}{\partial t} = \nabla \times \tilde{\mathbf B} - \varepsilon \kappa^2 \iint f B^*_\parallel \left( v_\parallel \frac{\mathbf B^*}{B^*_\parallel} + \frac{\mathbf E \times \mathbf b_0}{B^*_\parallel} \right) \textrm d v_\parallel \textrm d \mu\,,\]Faraday’s law:
\[\frac{\partial \tilde{\mathbf B}}{\partial t} + \nabla \times \mathbf E = 0\,.\]\(\mathbf B_0\) is a static equilibrium field, \(n_0\) the equilibrium density, and \(\mathbf E_\perp = \mathbf E - (\mathbf b_0 \cdot \mathbf E) \mathbf b_0\). The current in Ampère’s law is the guiding-center current without the magnetic (grad-\(B\)) drift, whose work on \(\mathbf E\) is therefore missing where \(\nabla |B| \neq 0\). The coefficients \(\varepsilon\lambda\) (option
lam, 0 is the quasi-neutral limit), \((\varepsilon\kappa)^2\) (optionchi) and \(\varepsilon\kappa^2\) (optionc2) can be changed inDriftKineticMaxwellCoupling. Without the polarization current the quasi-neutral limit would be singular for flute modes (\(k_\parallel = 0\)).
- classmethod doc_normalization()[source]#
The model uses the thermal velocity of the species as reference velocity:
\[\hat v = \hat v_\textrm{th} = \sqrt{\frac{k_\textrm{B} \hat T}{m}}\,,\qquad \hat t = \frac{\hat x}{\hat v}\,, \qquad \hat E = \hat v \hat B\,,\qquad \hat \mu = \frac{m \hat v^2}{\hat B}\,.\]The species parameters (see Equation parameters) are
\[\varepsilon = \frac{1}{\hat\Omega_c \hat t} = \frac{\hat v}{\hat\Omega_c \hat x}\,,\qquad \lambda = \frac{\hat\Omega_c \hat x}{c}\,,\qquad \kappa = \frac{\hat\Omega_p \hat x}{c}\,.\]The drift-kinetic equation and Faraday’s law then have only the coefficient \(\varepsilon\) (\(\frac{m \hat v}{q \hat B \hat x} = \varepsilon\) in \(\mathbf B^*\) and \(\mathbf E^*\), \(\frac{q \hat B \hat t}{m} = \frac{1}{\varepsilon}\) in the parallel force). Ampère’s law \(\frac{1}{c^2} \partial_t \mathbf E + \mu_0 \frac{n m}{|B|^2} \partial_t \mathbf E_\perp = \nabla \times \mathbf B - \mu_0 q \mathbf J\), multiplied by \(\hat x / \hat B\), has the coefficients
\[\frac{\hat v^2}{c^2} = \varepsilon^2 \lambda^2\,,\qquad \frac{\mu_0 \hat n m \hat v^2}{\hat B^2} = \frac{\hat v^2}{\hat v_A^2} = \varepsilon^2 \kappa^2\,,\qquad \frac{\mu_0 q \hat n \hat v \hat x}{\hat B} = \varepsilon \kappa^2\,.\]Their ratio \(\kappa^2/\lambda^2 = \hat\Omega_p^2/\hat\Omega_c^2 = c^2/\hat v_A^2\) compares the polarization with the vacuum dielectric, and \(1/\kappa\) is the inertial length \(c/\hat\Omega_p\) in units of \(\hat x\). For example, deuterium with \(\hat x = 1\) m, \(\hat B = 5\) T, \(\hat n = 10^{20}\) m-3, \(k_\textrm{B} \hat T = 10\) keV gives \(\varepsilon = 2.9 \cdot 10^{-3}\), \(\lambda = 0.80\), \(\kappa = 31\), i.e. \(\varepsilon^2 \lambda^2 = 5.3 \cdot 10^{-6}\), \(\varepsilon^2 \kappa^2 = 8.0 \cdot 10^{-3}\) and \(\varepsilon \kappa^2 = 2.8\).
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation (the parts of the energy conserved by the coupling step, with the coefficients of
DriftKineticMaxwellCoupling, here the defaults):Electric field energy (displacement and polarization) \(\frac{\varepsilon^2 \lambda^2}{2} \| \mathbf E \|^2 + \frac{\varepsilon^2 \kappa^2}{2} \| \mathbf E_\perp \|^2_{n_0/|B_0|^2}\):
en_EMagnetic field energy \(\frac{1}{2} \| \tilde{\mathbf B} \|^2\):
en_BParticle energy \(\varepsilon^2 \kappa^2 \sum_p w_p (v_{\parallel,p}^2/2 + \mu_p |B_0|)\):
en_fTotal energy:
en_tot(conserved by the coupling step up to the solver tolerance)
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
DriftKineticMaxwell couples one drift-kinetic (guiding-center) species to Maxwell’s equations in the gauge-invariant \(v_\parallel\) formulation (the electric field, not a potential, enters \(\mathbf E^*\)), normalized with the thermal velocity of the species. Maxwell’s equations and the parallel acceleration are solved together in one implicit, energy conserving step, which allows time steps above the plasma period and has no cancellation problem. Since the drift-kinetic ordering removes the polarization drift, its (linearized) polarization current is added to Ampère’s law; with it, the quasi-neutral limit without displacement current is well-posed and gives the inertial Alfvén wave. The guiding centers then stream along the field and drift with \(\mathbf E^* \times \mathbf b_0\). The total energy is conserved exactly by the splitting if \(\nabla |B_0| = 0\) and either \(\mu = 0\) or \(\mathbf b_0 \cdot \tilde{\mathbf B} = 0\).
- classmethod doc_examples()[source]#
Create a drift-kinetic Maxwell model in the quasi-neutral limit:
from struphy.models import DriftKineticMaxwell model = DriftKineticMaxwell() prop = model.propagators.coupling prop.options = prop.Options(lam=0.0)
- classmethod doc_use_cases()[source]#
This model is appropriate for:
electromagnetic drift-kinetic simulations with time steps above the plasma period
shear (inertial) Alfvén waves with kinetic parallel dynamics
the quasi-neutral limit (
lam=0.0) with full-f weights
- classmethod doc_cannot_be_used_for()[source]#
This model is not suitable for:
polar splines (not supported yet by the coupling propagator)
the quasi-neutral limit with control-variate (delta-f) weights or with vacuum regions
finite Larmor radius effects beyond the linearized long-wavelength polarization
exact energy conservation in inhomogeneous \(|B_0|\) (the grad-\(B\) drift current is missing)
- class struphy.models.ToyDrift(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=1.0), charge_number: int = 1, mass_number: float = 1.0, epsilon: float | None = None, alpha: float | None = None)[source]#
Bases:
StruphyModelElectrostatic drift toy model for a single ion species in a given background magnetic field.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default:
BaseUnits(kBT=1.0)).charge_number (int) – Charge number (in units of the positive elementary charge) of the ion species (default: 1).
mass_number (float) – Mass number (in units of the proton mass) of the ion species (default: 1.0).
epsilon (float, optional) – Normalized cyclotron period: \(1 / (\hat{\omega}_\mathrm{c} \hat{t})\). If
None, computed frombase_unitsand the charge/mass numbers.
- classmethod doc_pde()[source]#
PDEs solved by model:
Drift equation:
\[\frac{\partial f}{\partial t} + \frac{\mathbf{E} \times \mathbf{b}_0}{B^{*}_{\parallel}} \cdot \frac{\partial f}{\partial \mathbf{X}} = 0\]Poisson equation:
\[-\nabla \cdot \nabla \phi = \int f B^*_\parallel \, \textnormal{d} v_\parallel \textnormal{d} \mu\]where \(f(\mathbf{X}, v_\parallel, \mu, t)\) is the guiding center distribution and
\[\mathbf{E} = -\nabla \phi, \qquad \mathbf{B}^* = \mathbf{B}_0 + \varepsilon v_\parallel \nabla \times \mathbf{b}_0, \qquad B^*_\parallel = \mathbf{B}^* \cdot \mathbf{b}_0\]The control variate method can be activated in the Poisson equation; if enabled, the following Poisson equation is solved:
\[-\nabla \cdot \nabla \phi = \int (f - f_0) B^*_\parallel \, \textnormal{d} v_\parallel \textnormal{d} \mu\]
- classmethod doc_normalization()[source]#
The reference speed is the ion thermal velocity:
\[\hat v = \hat v_i,\qquad \hat E = \hat v_i \hat B,\qquad \hat\phi = \hat E \hat x.\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Electrostatic field energy:
en_phiParticle kinetic energy:
en_particlesTotal energy:
en_tot
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
ToyDrift is a stripped-down guiding-center model used to isolate the electrostatic drift part of the dynamics. It is intended for algorithm prototyping and reduced verification problems rather than production drift-kinetic studies.
- classmethod doc_examples()[source]#
Create and initialize the toy drift model:
from struphy.models import ToyDrift model = ToyDrift() model.em_fields.phi model.kinetic_ions.var
MHD#
- class struphy.models.LinearMHD(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mass_number: float = 1.0)[source]#
Bases:
StruphyModelLinear ideal MHD with zero-flow equilibrium for magnetohydrodynamic wave propagation.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mass_number (float) – Mass number (in units of Proton mass) of the ion species (default: 1.0)
- classmethod doc_pde()[source]#
PDEs solved by model:
Continuity (mass conservation):
\[\frac{\partial \tilde{\rho}}{\partial t} + \nabla \cdot (\rho_0 \tilde{\mathbf{U}}) = 0\]Momentum (Lorentz force):
\[\rho_0 \frac{\partial \tilde{\mathbf{U}}}{\partial t} + \nabla \tilde{p} = (\nabla \times \tilde{\mathbf{B}}) \times \mathbf{B}_0 + (\nabla \times \mathbf{B}_0) \times \tilde{\mathbf{B}}\]Energy (adiabatic process):
\[\frac{\partial \tilde{p}}{\partial t} + \nabla \cdot (p_0 \tilde{\mathbf{U}}) + \frac{2}{3} p_0 \nabla \cdot \tilde{\mathbf{U}} = 0\]Induction (Faraday’s law):
\[\frac{\partial \tilde{\mathbf{B}}}{\partial t} - \nabla \times (\tilde{\mathbf{U}} \times \mathbf{B}_0) = 0.\]
- classmethod doc_normalization()[source]#
All velocities are normalized by the Alfvén velocity:
\[\hat{U} = \hat{v}_\mathrm{A} = \frac{\hat{B}_0}{\sqrt{\mu_0 \rho_0}}\]The model therefore uses the Alfvén velocity as its characteristic speed scale.
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Kinetic energy (perturbation): \(E_U = \frac{1}{2} \int \rho_0 |\tilde{\mathbf{U}}|^2 \, \mathrm{d}V\)
Magnetic energy (perturbation): \(E_B = \frac{1}{2} \int \frac{|\tilde{\mathbf{B}}|^2}{\mu_0} \, \mathrm{d}V\)
Mean pressure perturbation: \(E_p = \int \frac{\tilde{p}}{\gamma - 1} \, \mathrm{d}V\) (
en_p). This is a linear diagnostic, not an energy.Thermal perturbation energy: \(E_{\mathrm{thermal}} = \frac{1}{2}\int \frac{\tilde{p}^2}{\gamma p_0} \, \mathrm{d}V\) (
en_thermal), with \(\gamma = 5/3\)Total perturbed energy: \(E_{\mathrm{tot}} = E_U + E_B + E_{\mathrm{thermal}}\) (
en_tot)Equilibrium magnetic energy: \(E_{B0} = \frac{1}{2} \int \frac{|\mathbf{B}_0|^2}{\mu_0} \, \mathrm{d}V\)
Equilibrium internal energy: \(E_{p0} = \int \frac{p_0}{\gamma - 1} \, \mathrm{d}V\)
Total magnetic energy: \(E_{B,\mathrm{tot}} = \frac{1}{2} \int \frac{|\mathbf{B}_0 + \tilde{\mathbf{B}}|^2}{\mu_0} \, \mathrm{d}V\)
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
This model simulates small-amplitude perturbations in a magnetized plasma with a static equilibrium magnetic field \(\mathbf{B}_0\) and zero background flow. The model solves the linearized ideal magnetohydrodynamic equations, which couple fluid dynamics (density, velocity, pressure) with magnetic field evolution.
The linear MHD system supports three wave families:
Alfvén waves: incompressible shear waves propagating along \(\mathbf{B}_0\)
Fast magnetosonic waves: compressible waves with phase velocity above the Alfvén speed
Slow magnetosonic waves: compressible waves with phase velocity below the Alfvén speed
All evolved quantities are perturbations around a stationary equilibrium:
\(\tilde{\rho}\) - density perturbation
\(\tilde{\mathbf{U}}\) - velocity perturbation
\(\tilde{p}\) - pressure perturbation
\(\tilde{\mathbf{B}}\) - magnetic field perturbation
The corresponding equilibrium quantities are:
\(\rho_0\) - background density
\(p_0\) - background pressure
\(\mathbf{B}_0\) - background magnetic field
- classmethod doc_examples()[source]#
Create and initialize a linear MHD model:
from struphy.models import LinearMHD model = LinearMHD() # Access fields model.em_fields.b_field model.mhd.density model.mhd.velocity model.mhd.pressure # Access tracked scalar quantities model.scalars["en_U"] model.scalars["en_B"] model.scalars["en_p"]
- classmethod doc_use_cases()[source]#
This model is appropriate for studying linear wave propagation in a static magnetized plasma, including Alfvén and magnetosonic dynamics around a prescribed equilibrium.
Typical use cases include:
verification of linear MHD wave dispersion and mode structure
perturbative studies around static equilibria
benchmark problems for ideal-MHD field-fluid coupling
- class struphy.models.LinearMHDCurlCurl(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mass_number: float = 1.0)[source]#
Bases:
LinearMHDLinear ideal MHD with zero-flow equilibrium, with the magnetic field in \(H(\textnormal{curl})\).
Same equations as
LinearMHD, but with a weak induction equation, such that the implicit shear-Alfvén step leads to a curl-curl system for the magnetic field, which is preconditioned by the Hiptmair-Xu preconditioner (as inShearAlfvenCurlCurl).- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mass_number (float) – Mass number (in units of Proton mass) of the ion species (default: 1.0)
- classmethod doc_pde()[source]#
PDEs solved by model:
Continuity (mass conservation):
\[\frac{\partial \tilde{\rho}}{\partial t} + \nabla \cdot (\rho_0 \tilde{\mathbf{U}}) = 0\]Momentum (Lorentz force):
\[\rho_0 \frac{\partial \tilde{\mathbf{U}}}{\partial t} + \nabla \tilde{p} = (\nabla \times \tilde{\mathbf{B}}) \times \mathbf{B}_0 + (\nabla \times \mathbf{B}_0) \times \tilde{\mathbf{B}}\]Energy (adiabatic process):
\[\frac{\partial \tilde{p}}{\partial t} + \nabla \cdot (p_0 \tilde{\mathbf{U}}) + \frac{2}{3} p_0 \nabla \cdot \tilde{\mathbf{U}} = 0\]Induction (Faraday’s law, solved weakly in \(H(\textnormal{curl})\)):
\[\frac{\partial \tilde{\mathbf{B}}}{\partial t} - \nabla \times (\tilde{\mathbf{U}} \times \mathbf{B}_0) = 0.\]
- classmethod doc_normalization()[source]#
All velocities are normalized by the Alfvén velocity:
\[\hat{U} = \hat{v}_\mathrm{A} = \frac{\hat{B}_0}{\sqrt{\mu_0 \rho_0}}\]The model therefore uses the Alfvén velocity as its characteristic speed scale.
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Kinetic energy (perturbation): \(E_U = \frac{1}{2} \int \rho_0 |\tilde{\mathbf{U}}|^2 \, \mathrm{d}V\)
Magnetic energy (perturbation, magnetic field as a 1-form): \(E_B = \frac{1}{2} \int \frac{|\tilde{\mathbf{B}}|^2}{\mu_0} \, \mathrm{d}V\)
Mean pressure perturbation: \(E_p = \int \frac{\tilde{p}}{\gamma - 1} \, \mathrm{d}V\) (
en_p). This is a linear diagnostic, not an energy.Thermal perturbation energy: \(E_{\mathrm{thermal}} = \frac{1}{2}\int \frac{\tilde{p}^2}{\gamma p_0} \, \mathrm{d}V\) (
en_thermal), with \(\gamma = 5/3\)Total perturbed energy: \(E_{\mathrm{tot}} = E_U + E_B + E_{\mathrm{thermal}}\) (
en_tot). The shear-Alfvén step damps it numerically (the damping vanishes with the resolution along the background field; exactly conserving shear-Alfvén step inLinearMHDCurlCurlwithProj).Equilibrium magnetic energy: \(E_{B0} = \frac{1}{2} \int \frac{|\mathbf{B}_0|^2}{\mu_0} \, \mathrm{d}V\) (with \(\mathbf B_0\) projected as a 1-form)
Equilibrium internal energy: \(E_{p0} = \int \frac{p_0}{\gamma - 1} \, \mathrm{d}V\)
Total magnetic energy: \(E_{B,\mathrm{tot}} = \frac{1}{2} \int \frac{|\mathbf{B}_0 + \tilde{\mathbf{B}}|^2}{\mu_0} \, \mathrm{d}V\)
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
LinearMHDCurlCurl solves the same equations as
LinearMHD, with the perturbed magnetic field in \(H(\textnormal{curl})\) instead of \(H(\textnormal{div})\). The splitting is the same: the shear-Alfvén step ofShearAlfvenCurlCurl(weak induction equation, field-aligned curl-curl system for the magnetic field, Hiptmair-Xu preconditioner by default), followed by the magnetosonic step, in which the force \((\nabla \times \mathbf B_0) \times \tilde{\mathbf B}\) of the 1-form \(\tilde{\mathbf B}\) enters through the mixed mass matrixM21J.The shear-Alfvén step is not exactly energy conserving: it damps the waves numerically, with a damping that vanishes quickly with the resolution along the background field (see
LinearMHDCurlCurlwithProjfor the exactly energy conserving version).Boundary conditions: with essential boundary conditions in the de Rham sequence, the tangential components of the magnetic field (a 1-form) vanish on that boundary, which is unphysical at a wall with \(\mathbf B_0 \cdot \mathbf n = 0\). Free boundary conditions are therefore recommended: they impose \(\tilde{\mathbf U} \cdot \mathbf n = 0\) weakly in the shear-Alfvén step and \(\tilde p = 0\) weakly in the magnetosonic step (natural boundary condition of the pressure term).
All evolved quantities are perturbations around a stationary equilibrium:
\(\tilde{\rho}\) - density perturbation
\(\tilde{\mathbf{U}}\) - velocity perturbation (2-form)
\(\tilde{p}\) - pressure perturbation
\(\tilde{\mathbf{B}}\) - magnetic field perturbation (1-form)
- classmethod doc_examples()[source]#
Create and initialize a linear MHD curl-curl model:
from struphy.models import LinearMHDCurlCurl model = LinearMHDCurlCurl() # Access fields model.em_fields.b_field model.mhd.density model.mhd.velocity model.mhd.pressure # Access tracked scalar quantities model.scalars["en_U"] model.scalars["en_B"] model.scalars["en_tot"]
- classmethod doc_use_cases()[source]#
This model is appropriate for:
linear MHD wave propagation with large time steps (fast curl-curl solve of the shear-Alfvén step)
comparison with
LinearMHD(magnetic field in \(H(\textnormal{div})\))testing curl-curl (Hiptmair-Xu) preconditioning in linear MHD
- classmethod doc_cannot_be_used_for()[source]#
This model is not suitable for:
nonlinear MHD dynamics with finite-amplitude perturbations
equilibria with non-zero background flow
dissipative effects such as resistivity or viscosity
kinetic or particle-field effects beyond the fluid MHD description
polar splines with the Hiptmair-Xu preconditioner
- class struphy.models.LinearMHDCurlCurlwithProj(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mass_number: float = 1.0)[source]#
Bases:
LinearMHDCurlCurlLinear ideal MHD with zero-flow equilibrium, with the magnetic field in \(H(\textnormal{curl})\) and the exactly energy conserving shear-Alfvén step.
Same as
LinearMHDCurlCurl, but the curl-curl term of the implicit shear-Alfvén step contains the \(\rho_0\)-weighted \(L^2\) projection of the Lorentz force onto \(H(\textnormal{div})\) (as inShearAlfvenCurlCurlwithProj), at the price of an inner mass matrix solve in every application of the Schur operator (and no effective curl-curl preconditioner).- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mass_number (float) – Mass number (in units of Proton mass) of the ion species (default: 1.0)
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Kinetic energy (perturbation): \(E_U = \frac{1}{2} \int \rho_0 |\tilde{\mathbf{U}}|^2 \, \mathrm{d}V\)
Magnetic energy (perturbation, magnetic field as a 1-form): \(E_B = \frac{1}{2} \int \frac{|\tilde{\mathbf{B}}|^2}{\mu_0} \, \mathrm{d}V\)
Mean pressure perturbation: \(E_p = \int \frac{\tilde{p}}{\gamma - 1} \, \mathrm{d}V\) (
en_p). This is a linear diagnostic, not an energy.Thermal perturbation energy: \(E_{\mathrm{thermal}} = \frac{1}{2}\int \frac{\tilde{p}^2}{\gamma p_0} \, \mathrm{d}V\) (
en_thermal), with \(\gamma = 5/3\)Total perturbed energy: \(E_{\mathrm{tot}} = E_U + E_B + E_{\mathrm{thermal}}\) (
en_tot), conserved by the shear-Alfvén step up to the solver tolerancesEquilibrium magnetic energy: \(E_{B0} = \frac{1}{2} \int \frac{|\mathbf{B}_0|^2}{\mu_0} \, \mathrm{d}V\) (with \(\mathbf B_0\) projected as a 1-form)
Equilibrium internal energy: \(E_{p0} = \int \frac{p_0}{\gamma - 1} \, \mathrm{d}V\)
Total magnetic energy: \(E_{B,\mathrm{tot}} = \frac{1}{2} \int \frac{|\mathbf{B}_0 + \tilde{\mathbf{B}}|^2}{\mu_0} \, \mathrm{d}V\)
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
LinearMHDCurlCurlwithProj solves the same equations as
LinearMHDCurlCurl(magnetic field in \(H(\textnormal{curl})\), weak induction equation, momentum equation projected onto \(H(\textnormal{div})\)), with the projection also in the curl-curl term of the implicit shear-Alfvén step, such that this step conserves the perturbed energy exactly. The Schur complement then contains the inverse of the density-weighted mass matrix of 2-forms. The magnetosonic step is that ofLinearMHDCurlCurl. Free boundary conditions are recommended for bounded domains (seeLinearMHDCurlCurl).
- classmethod doc_examples()[source]#
Create and initialize the energy conserving linear MHD curl-curl model:
from struphy.models import LinearMHDCurlCurlwithProj model = LinearMHDCurlCurlwithProj() model.em_fields.b_field model.mhd.velocity model.scalars["en_tot"]
- classmethod doc_use_cases()[source]#
This model is appropriate for:
linear MHD wave propagation with exact energy conservation of the shear-Alfvén step
comparison with
LinearMHDCurlCurlandLinearMHD
- classmethod doc_cannot_be_used_for()[source]#
This model is not suitable for:
nonlinear MHD dynamics with finite-amplitude perturbations
equilibria with non-zero background flow
dissipative effects such as resistivity or viscosity
kinetic or particle-field effects beyond the fluid MHD description
large problems where the inner mass matrix solves of the shear-Alfvén step are too expensive (use
LinearMHDCurlCurl)
- classmethod doc_pde()#
PDEs solved by model:
Continuity (mass conservation):
\[\frac{\partial \tilde{\rho}}{\partial t} + \nabla \cdot (\rho_0 \tilde{\mathbf{U}}) = 0\]Momentum (Lorentz force):
\[\rho_0 \frac{\partial \tilde{\mathbf{U}}}{\partial t} + \nabla \tilde{p} = (\nabla \times \tilde{\mathbf{B}}) \times \mathbf{B}_0 + (\nabla \times \mathbf{B}_0) \times \tilde{\mathbf{B}}\]Energy (adiabatic process):
\[\frac{\partial \tilde{p}}{\partial t} + \nabla \cdot (p_0 \tilde{\mathbf{U}}) + \frac{2}{3} p_0 \nabla \cdot \tilde{\mathbf{U}} = 0\]Induction (Faraday’s law, solved weakly in \(H(\textnormal{curl})\)):
\[\frac{\partial \tilde{\mathbf{B}}}{\partial t} - \nabla \times (\tilde{\mathbf{U}} \times \mathbf{B}_0) = 0.\]
- classmethod doc_normalization()#
All velocities are normalized by the Alfvén velocity:
\[\hat{U} = \hat{v}_\mathrm{A} = \frac{\hat{B}_0}{\sqrt{\mu_0 \rho_0}}\]The model therefore uses the Alfvén velocity as its characteristic speed scale.
- class struphy.models.LinearExtendedMHDuniform(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), charge_number: int = 1, mass_number: float = 1.0, epsilon: float | None = None)[source]#
Bases:
StruphyModelLinear extended MHD with zero-flow equilibrium, for uniform background conditions only.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
charge_number (int) – Charge number (in units of the positive elementary charge) of the ion species (default: 1)
mass_number (float) – Mass number (in units of Proton mass) of the ion species (default: 1.0)
epsilon (float, optional) – Normalized cyclotron period: 1 / (cyclotron frequency × time unit). If None, computed from units and charge/mass numbers.
- classmethod doc_pde()[source]#
PDEs solved by model:
Continuity:
\[\frac{\partial \tilde{\rho}}{\partial t} + \nabla \cdot (\rho_0 \tilde{\mathbf{U}}) = 0\]Momentum:
\[\rho_0 \frac{\partial \tilde{\mathbf{U}}}{\partial t} + \nabla \tilde{p} = (\nabla \times \tilde{\mathbf{B}}) \times \mathbf{B}_0\]Pressure:
\[\frac{\partial \tilde{p}}{\partial t} + \frac{5}{3} p_0 \nabla \cdot \tilde{\mathbf{U}} = 0\]Induction:
\[\frac{\partial \tilde{\mathbf{B}}}{\partial t} - \nabla \times \left( \tilde{\mathbf{U}} \times \mathbf{B}_0 - \frac{1}{\varepsilon} \frac{\nabla \times \tilde{\mathbf{B}}}{\rho_0} \times \mathbf{B}_0 \right) = 0\]
- classmethod doc_normalization()[source]#
All velocities are normalized by the Alfvén speed,
\[\hat U = \hat v_A,\]and Hall effects enter through \(\varepsilon = 1/(\hat\Omega_c\hat t)\).
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Flow kinetic energy:
en_UMean pressure perturbation (linear diagnostic):
en_pThermal perturbation energy:
en_thermalMagnetic perturbation energy:
en_BBackground pressure energy:
en_p_eqBackground magnetic energy:
en_B_eqTotal magnetic energy:
en_B_totTotal perturbation energy (kinetic + magnetic + thermal):
en_totMagnetic helicity-like invariant:
helicity
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
LinearExtendedMHDuniform extends the linear MHD model with Hall dynamics for a uniform equilibrium. It is targeted at wave propagation and mode studies where the equilibrium is simple but finite Hall corrections are important.
- classmethod doc_examples()[source]#
Create and initialize a linear extended-MHD model:
from struphy.models import LinearExtendedMHDuniform model = LinearExtendedMHDuniform() model.em_fields.b_field model.mhd.density model.mhd.velocity model.mhd.pressure
- class struphy.models.ShearAlfven(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mass_number: float = 1.0)[source]#
Bases:
StruphyModelShear-Alfvén part of
LinearMHDwith zero-flow equilibrium.- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mass_number (float) – Mass number (in units of Proton mass) of the ion species (default: 1.0)
- classmethod doc_pde()[source]#
PDEs solved by model:
Momentum:
\[\rho_0 \frac{\partial \tilde{\mathbf{U}}}{\partial t} = (\nabla \times \tilde{\mathbf{B}}) \times \mathbf{B}_0\]Induction:
\[\frac{\partial \tilde{\mathbf{B}}}{\partial t} - \nabla \times (\tilde{\mathbf{U}} \times \mathbf{B}_0) = 0\]
- classmethod doc_normalization()[source]#
All velocities are normalized by the Alfvén speed,
\[\hat U = \hat v_A.\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Perturbed kinetic energy:
en_UPerturbed magnetic energy:
en_BBackground magnetic energy:
en_B_eqTotal magnetic energy including background:
en_B_totPerturbation total energy:
en_totTotal energy including background contribution:
en_tot2
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
ShearAlfven is the incompressible magnetic-tension subsystem extracted from linear MHD. It is useful when one wants to isolate Alfvénic propagation from the full linear MHD model.
- classmethod doc_examples()[source]#
Create and initialize a shear-Alfvén model:
from struphy.models import ShearAlfven model = ShearAlfven() model.em_fields.b_field model.mhd.velocity
- class struphy.models.ShearAlfvenCurlCurl(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mass_number: float = 1.0)[source]#
Bases:
StruphyModelShear-Alfvén part of linear MHD with zero-flow equilibrium, with the magnetic field in \(H(\textnormal{curl})\).
Same equations as
ShearAlfven, but with a weak induction equation, such that the implicit time step leads to a curl-curl system for the magnetic field, which is preconditioned by the Hiptmair-Xu preconditioner.- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mass_number (float) – Mass number (in units of Proton mass) of the ion species (default: 1.0)
- classmethod doc_pde()[source]#
PDEs solved by model:
Momentum:
\[\rho_0 \frac{\partial \tilde{\mathbf{U}}}{\partial t} = (\nabla \times \tilde{\mathbf{B}}) \times \mathbf{B}_0\]Induction:
\[\frac{\partial \tilde{\mathbf{B}}}{\partial t} - \nabla \times (\tilde{\mathbf{U}} \times \mathbf{B}_0) = 0\]
- classmethod doc_normalization()[source]#
All velocities are normalized by the Alfvén speed,
\[\hat U = \hat v_A.\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Perturbed kinetic energy:
en_UPerturbed magnetic energy:
en_BBackground magnetic energy:
en_B_eqTotal magnetic energy including background:
en_B_totPerturbation total energy:
en_tot(not increased by the implicit time step: numerical damping that vanishes with the resolution along the background field; exactly conserved inShearAlfvenCurlCurlwithProj)Total energy including background contribution:
en_tot2
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
ShearAlfvenCurlCurl solves the same equations as
ShearAlfven, with the perturbed magnetic field in \(H(\textnormal{curl})\) instead of \(H(\textnormal{div})\). The momentum equation is the \(\rho_0\)-weighted \(L^2\) projection onto \(H(\textnormal{div})\), and the induction equation is weak. In the implicit time step, the velocity update is inserted into the induction equation before the projection, which gives a field-aligned curl-curl system for the magnetic field, preconditioned by the Hiptmair-Xu auxiliary space preconditioner. This step is not exactly energy conserving: it damps the waves numerically, with a damping that vanishes quickly with the resolution along the background field (seeShearAlfvenCurlCurlwithProjfor the exactly energy conserving version). With essential boundary conditions in the de Rham sequence, the tangential components of the magnetic field (a 1-form) vanish on that boundary (stronger than \(\mathbf B \cdot \mathbf n = 0\) of ShearAlfven).
- classmethod doc_examples()[source]#
Create and initialize a shear-Alfvén curl-curl model:
from struphy.models import ShearAlfvenCurlCurl model = ShearAlfvenCurlCurl() model.em_fields.b_field model.mhd.velocity
- class struphy.models.ShearAlfvenCurlCurlwithProj(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mass_number: float = 1.0)[source]#
Bases:
ShearAlfvenCurlCurlShear-Alfvén part of linear MHD with zero-flow equilibrium, with the magnetic field in \(H(\textnormal{curl})\) and the exactly energy conserving time step.
Same as
ShearAlfvenCurlCurl, but the curl-curl term of the implicit time step contains the \(\rho_0\)-weighted \(L^2\) projection of the Lorentz force onto \(H(\textnormal{div})\), which conserves the perturbed energy exactly (up to the solver tolerances), at the price of an inner mass matrix solve in every application of the Schur operator (and no effective curl-curl preconditioner).- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mass_number (float) – Mass number (in units of Proton mass) of the ion species (default: 1.0)
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Perturbed kinetic energy:
en_UPerturbed magnetic energy:
en_BBackground magnetic energy:
en_B_eqTotal magnetic energy including background:
en_B_totPerturbation total energy:
en_tot(conserved by the implicit time step up to the solver tolerances)Total energy including background contribution:
en_tot2
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
ShearAlfvenCurlCurlwithProj solves the same equations as
ShearAlfvenCurlCurl(magnetic field in \(H(\textnormal{curl})\), weak induction equation, momentum equation projected onto \(H(\textnormal{div})\)), with the projection also in the curl-curl term of the implicit time step, such that the perturbed energy is conserved exactly. The Schur complement then contains the inverse of the density-weighted mass matrix of 2-forms.
- classmethod doc_examples()[source]#
Create and initialize the energy conserving shear-Alfvén curl-curl model:
from struphy.models import ShearAlfvenCurlCurlwithProj model = ShearAlfvenCurlCurlwithProj() model.em_fields.b_field model.mhd.velocity
- classmethod doc_use_cases()[source]#
This model is appropriate for:
isolated shear-Alfvén wave propagation with exact energy conservation
comparison with
ShearAlfvenCurlCurl
- classmethod doc_pde()#
PDEs solved by model:
Momentum:
\[\rho_0 \frac{\partial \tilde{\mathbf{U}}}{\partial t} = (\nabla \times \tilde{\mathbf{B}}) \times \mathbf{B}_0\]Induction:
\[\frac{\partial \tilde{\mathbf{B}}}{\partial t} - \nabla \times (\tilde{\mathbf{U}} \times \mathbf{B}_0) = 0\]
- classmethod doc_normalization()#
All velocities are normalized by the Alfvén speed,
\[\hat U = \hat v_A.\]
- classmethod doc_cannot_be_used_for()#
This model is not suitable for:
magnetosonic or compressible wave physics
nonlinear MHD dynamics
kinetic particle coupling
resistive, viscous, or Hall-MHD effects
polar splines with the Hiptmair-Xu preconditioner
- class struphy.models.VariationalMHD(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mass_number: float = 1.0, with_viscosity: bool = True, with_resistivity: bool = True, divdiv_alpha: float = 0.0)[source]#
Bases:
StruphyModelFull (non-linear) visco-resistive MHD equations discretized with a variational method.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mass_number (float) – Mass number (in units of Proton mass) of the fluid species (default: 1.0)
with_viscosity (bool) – Whether to include viscous dissipation (default: True)
with_resistivity (bool) – Whether to include resistive dissipation (default: True)
divdiv_alpha (float) – Coefficient alpha >= 0 of the div-div regularization alpha int rho (div u)^2 dx of the kinetic energy (Hamiltonian regularization, penalizes compressions; default: 0.0), see
kinetic_metric()
- classmethod doc_pde()[source]#
PDEs solved by model:
Continuity:
\[\partial_t \rho + \nabla \cdot (\rho \mathbf{u}) = 0\]Momentum:
\[\partial_t (\rho \mathbf{u}) + \nabla \cdot (\rho \mathbf{u} \otimes \mathbf{u}) + \rho \nabla \frac{(\rho \mathcal{U}(\rho, s))}{\partial \rho} + s \nabla \frac{(\rho \mathcal{U}(\rho, s))}{\partial s} + \mathbf{B} \times \nabla \times \mathbf{B} - \nabla \cdot \left( (\mu + \mu_a(\mathbf{x})) \nabla \mathbf{u} \right) = 0\]Entropy:
\[\partial_t s + \nabla \cdot (s \mathbf{u}) = \frac{1}{T} \left( (\mu + \mu_a(\mathbf{x})) |\nabla \mathbf{u}|^2 + (\eta + \eta_a(\mathbf{x})) |\nabla \times \mathbf{B}|^2 \right)\]Induction:
\[\partial_t \mathbf{B} + \nabla \times (\mathbf{B} \times \mathbf{u}) + \nabla \times (\eta + \eta_a(\mathbf{x})) \nabla \times \mathbf{B} = 0\]where the internal energy per unit mass is \(\mathcal U(\rho) = \rho^{\gamma-1} \exp(s / \rho)\), and \(\mu_a(\mathbf{x})\) and \(\eta_a(\mathbf{x})\) are artificial viscosity and resistivity coefficients.
With
divdiv_alpha\(=\alpha > 0\), the kinetic energy is \(\int \rho |\mathbf u|^2/2 + \alpha \rho (\nabla \cdot \mathbf u)^2 \,\textrm d \mathbf x\) (Hamiltonian regularization of compressions). The equations follow from the same variational principle with the momentum \(\rho \mathbf u - 2 \alpha \nabla (\rho \nabla \cdot \mathbf u)\) instead of \(\rho \mathbf u\), and the density derivative \(\alpha (\nabla \cdot \mathbf u)^2\) of the kinetic energy enters the force like \(|\mathbf u|^2/2\).
- classmethod doc_normalization()[source]#
The model uses Alfvén-speed normalization together with entropy-based thermodynamic units inherited from the chosen internal-energy functional.
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Kinetic energy:
en_UThermodynamic energy:
en_thermoMagnetic energy:
en_magDiv-div part of the kinetic energy (only for
divdiv_alpha > 0):en_divdivTotal energy:
en_totTotal density / entropy:
dens_tot,entr_totDivergence diagnostic:
tot_div_B
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
VariationalViscosity(ifwith_viscosityis True)VariationalResistivity(ifwith_resistivityis True)
- classmethod doc_long_description()[source]#
VariationalMHD is the full nonlinear dissipative MHD model in the entropy formulation. It is the most complete variational MHD model in this family when both viscosity and resistivity are required.
- classmethod doc_examples()[source]#
Create and initialize the full visco-resistive MHD model:
from struphy.models import VariationalMHD model = VariationalMHD() model.mhd.density model.mhd.velocity model.mhd.entropy model.em_fields.b_field
- class struphy.models.VariationalMHD_with_p(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mass_number: float = 1.0, with_viscosity: bool = True, with_resistivity: bool = True)[source]#
Bases:
StruphyModelFull (non-linear) visco-resistive MHD equations, with the pressure variable, discretized with a variational method.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mass_number (float) – Mass number (in units of Proton mass) of the fluid species (default: 1.0)
with_viscosity (bool) – Whether to include viscous dissipation (default: True)
with_resistivity (bool) – Whether to include resistive dissipation (default: True)
- classmethod doc_pde()[source]#
PDEs solved by model:
Continuity:
\[\partial_t \rho + \nabla \cdot (\rho \mathbf{u}) = 0\]Momentum:
\[\partial_t (\rho \mathbf{u}) + \nabla \cdot (\rho \mathbf{u} \otimes \mathbf{u}) + \frac{1}{\gamma - 1} \nabla p + \mathbf{B} \times \nabla \times \mathbf{B} - \nabla \cdot \left( (\mu + \mu_a(\mathbf{x})) \nabla \mathbf{u} \right) = 0\]Pressure:
\[\partial_t p + u \cdot \nabla p + \gamma p \nabla \cdot u = \frac{1}{\gamma - 1} \left( (\mu + \mu_a(\mathbf{x})) |\nabla \mathbf{u}|^2 + (\eta + \eta_a(\mathbf{x})) |\nabla \times \mathbf{B}|^2 \right)\]Induction:
\[\partial_t \mathbf{B} + \nabla \times (\mathbf{B} \times \mathbf{u}) + \nabla \times (\eta + \eta_a(\mathbf{x})) \nabla \times \mathbf{B} = 0\]Here \(\mu_a(\mathbf{x})\) and \(\eta_a(\mathbf{x})\) are artificial viscosity and resistivity coefficients.
- classmethod doc_normalization()[source]#
The characteristic flow speed is the Alfvén speed,
\[\hat u = \hat v_A.\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Kinetic energy:
en_UThermodynamic energy:
en_thermoMagnetic energy:
en_magTotal energy:
en_totTotal density:
dens_totDivergence diagnostic:
tot_div_B
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
VariationalViscosity(ifwith_viscosityis True)VariationalResistivity(ifwith_resistivityis True)
- classmethod doc_long_description()[source]#
This is the pressure-based full nonlinear dissipative MHD model. It is the natural counterpart to the entropy- and q-based full MHD variants when pressure is preferred as primitive thermodynamic variable.
- classmethod doc_examples()[source]#
Create and initialize the pressure-based visco-resistive MHD model:
from struphy.models import VariationalMHD_with_p model = VariationalMHD_with_p() model.mhd.pressure model.em_fields.b_field
- class struphy.models.VariationalMHD_with_q(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mass_number: float = 1.0, with_viscosity: bool = True, with_resistivity: bool = True)[source]#
Bases:
StruphyModelFull (non-linear) visco-resistive MHD equations, with the q variable (square root of the pressure), discretized with a variational method.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mass_number (float) – Mass number (in units of Proton mass) of the fluid species (default: 1.0)
with_viscosity (bool) – Whether to include viscous dissipation (default: True)
with_resistivity (bool) – Whether to include resistive dissipation (default: True)
- classmethod doc_pde()[source]#
PDEs solved by model:
Continuity:
\[\partial_t \rho + \nabla \cdot (\rho \mathbf{u}) = 0\]Momentum:
\[\partial_t (\rho \mathbf{u}) + \nabla \cdot (\rho \mathbf{u} \otimes \mathbf{u}) + \frac{2 q}{\gamma - 1} \nabla q + \mathbf{B} \times \nabla \times \mathbf{B} - \nabla \cdot \left( (\mu + \mu_a(\mathbf{x})) \nabla \mathbf{u} \right) = 0\]Energy-like variable:
\[\partial_t q + \nabla \cdot (q \mathbf{u}) + \left( \frac{\gamma}{2} - 1 \right) q \nabla \cdot \mathbf{u} = \frac{2 q}{\gamma - 1} \left( (\mu + \mu_a(\mathbf{x})) |\nabla \mathbf{u}|^2 + (\eta + \eta_a(\mathbf{x})) |\nabla \times \mathbf{B}|^2 \right)\]Induction:
\[\partial_t \mathbf{B} + \nabla \times (\mathbf{B} \times \mathbf{u}) + \nabla \times (\eta + \eta_a(\mathbf{x})) \nabla \times \mathbf{B} = 0\]Here \(\mu_a(\mathbf{x})\) and \(\eta_a(\mathbf{x})\) are artificial viscosity and resistivity coefficients.
- classmethod doc_normalization()[source]#
The flow variables are normalized with the Alfvén speed:
\[\hat u = \hat v_A.\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Kinetic energy:
en_UThermodynamic q-energy:
en_thermoMagnetic energy:
en_magTotal energy:
en_totTotal density:
dens_totDivergence diagnostic:
tot_div_B
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
VariationalViscosity(ifwith_viscosityis True)VariationalResistivity(ifwith_resistivityis True)
- classmethod doc_long_description()[source]#
This model is the full nonlinear q-based dissipative MHD formulation. It is useful when the square-root pressure variable is preferred in the variational discretization while still retaining full nonlinear magnetic dynamics.
- classmethod doc_examples()[source]#
Create and initialize the q-based visco-resistive MHD model:
from struphy.models import VariationalMHD_with_q model = VariationalMHD_with_q() model.mhd.sqrt_p model.em_fields.b_field
- class struphy.models.VariationalPerturbativeMHD(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mass_number: float = 1.0, linear: bool = False, with_viscosity: bool = True, with_resistivity: bool = True)[source]#
Bases:
StruphyModelPerturbative (delta-f) or linear visco-resistive MHD equations, with the pressure variable, discretized with a variational method.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mass_number (float) – Mass number (in units of Proton mass) of the fluid species (default: 1.0)
linear (bool) – Whether to solve the linearized equations, i.e. to drop the terms that are nonlinear in the perturbations (default: False)
with_viscosity (bool) – Whether to include viscous dissipation (default: True)
with_resistivity (bool) – Whether to include resistive dissipation (default: True)
- classmethod doc_pde()[source]#
PDEs solved by model:
The unknowns are the perturbations \((\tilde\rho, \tilde p, \tilde{\mathbf B})\) with respect to a zero-flow MHD equilibrium \((\rho_0, p_0, \mathbf B_0)\), and the velocity \(\mathbf u\).
Continuity:
\[\partial_t \tilde{\rho} + \nabla \cdot \left( (\tilde{\rho} + \rho_0) \tilde{\mathbf{u}} \right) = 0\]Momentum:
\[\partial_t \left( (\tilde{\rho} + \rho_0) \tilde{\mathbf{u}} \right) + \nabla \cdot \left( (\tilde{\rho} + \rho_0) \tilde{\mathbf{u}} \otimes \tilde{\mathbf{u}} \right) + \nabla \tilde{p} + \mathbf{B}_0 \times \nabla \times \tilde{\mathbf{B}} + \tilde{\mathbf{B}} \times \nabla \times \mathbf{B}_0 + \tilde{\mathbf{B}} \times \nabla \times \tilde{\mathbf{B}} - \nabla \cdot \left( (\mu + \mu_a(\mathbf{x})) \nabla \tilde{\mathbf{u}} \right) = 0\]Pressure:
\[\partial_t \tilde{p} + \tilde{\mathbf{u}} \cdot \nabla (\tilde{p} + p_0) + \gamma (\tilde{p} + p_0) \nabla \cdot \tilde{\mathbf{u}} = (\gamma - 1) \left( (\mu + \mu_a(\mathbf{x})) |\nabla \tilde{\mathbf{u}}|^2 + (\eta + \eta_a(\mathbf{x})) |\nabla \times \tilde{\mathbf{B}}|^2 \right)\]Induction:
\[\partial_t \tilde{\mathbf{B}} + \nabla \times \left( (\tilde{\mathbf{B}} + \mathbf{B}_0) \times \tilde{\mathbf{u}} \right) + \nabla \times (\eta + \eta_a(\mathbf{x})) \nabla \times \tilde{\mathbf{B}} = 0\]Here \(\mu_a(\mathbf{x})\) and \(\eta_a(\mathbf{x})\) are artificial viscosity and resistivity coefficients.
Linear model (
linear=True): the terms that are nonlinear in the perturbations are dropped,\[\begin{split}&\partial_t \tilde{\rho} + \nabla \cdot (\rho_0 \tilde{\mathbf{u}}) = 0\,, \\[2mm] &\partial_t (\rho_0 \tilde{\mathbf{u}}) + \nabla \tilde{p} + \mathbf{B}_0 \times \nabla \times \tilde{\mathbf{B}} + \tilde{\mathbf{B}} \times \nabla \times \mathbf{B}_0 - \nabla \cdot \left( (\mu + \mu_a(\mathbf{x})) \nabla \tilde{\mathbf{u}} \right) = 0\,, \\[2mm] &\partial_t \tilde{p} + \tilde{\mathbf{u}} \cdot \nabla p_0 + \gamma p_0 \nabla \cdot \tilde{\mathbf{u}} = 0\,, \\[2mm] &\partial_t \tilde{\mathbf{B}} + \nabla \times (\mathbf{B}_0 \times \tilde{\mathbf{u}}) + \nabla \times (\eta + \eta_a(\mathbf{x})) \nabla \times \tilde{\mathbf{B}} = 0\,.\end{split}\]In the linear model the viscous and resistive heating is not fed back into the pressure (the dissipative propagators with
model="linear_p"update only \(\tilde{\mathbf{u}}\) and \(\tilde{\mathbf{B}}\)).
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Kinetic energy:
en_UThermal energies:
en_thermo,en_thermo_l1Magnetic energies:
en_mag_1,en_mag_2,en_mag_l1Total energies:
en_tot,en_tot_l1
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
VariationalMomentumAdvection(iflinearis False)VariationalViscosity(ifwith_viscosityis True)VariationalResistivity(ifwith_resistivityis True)
The option
modelof the variational propagators is"perturbative"("full_p"for the dissipative ones), or"linear"("linear_p") iflinearis True.
- classmethod doc_long_description()[source]#
VariationalPerturbativeMHD is the dissipative perturbative MHD model using pressure as thermodynamic variable. It is intended for departures from a background equilibrium that are not strictly linear but still naturally formulated as perturbations; with
linear=Trueit solves linear MHD around the equilibrium.
- classmethod doc_examples()[source]#
Create and initialize the visco-resistive perturbative MHD model and its linear version:
from struphy.models import VariationalPerturbativeMHD model = VariationalPerturbativeMHD() linear_model = VariationalPerturbativeMHD(linear=True) model.mhd.pressure model.em_fields.b_field
- class struphy.models.VariationalPerturbativeMHD_with_q(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mass_number: float = 1.0, linear: bool = False, with_viscosity: bool = True, with_resistivity: bool = True)[source]#
Bases:
StruphyModelPerturbative (delta-f) or linear visco-resistive MHD equations, with the q variable (square root of the pressure), discretized with a variational method.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mass_number (float) – Mass number (in units of Proton mass) of the fluid species (default: 1.0)
linear (bool) – Whether to solve the linearized equations, i.e. to drop the terms that are nonlinear in the perturbations (default: False)
with_viscosity (bool) – Whether to include viscous dissipation (default: True)
with_resistivity (bool) – Whether to include resistive dissipation (default: True)
- classmethod doc_pde()[source]#
PDEs solved by model:
The unknowns are the perturbations \((\tilde\rho, \tilde q, \tilde{\mathbf B})\) with respect to a zero-flow MHD equilibrium \((\rho_0, q_0, \mathbf B_0)\) with \(q_0 = \sqrt{p_0}\), and the velocity \(\mathbf u\). The total quantities are \(\rho = \rho_0 + \tilde\rho\), \(q = q_0 + \tilde q = \sqrt{p}\) and \(\mathbf B = \mathbf B_0 + \tilde{\mathbf B}\).
Continuity:
\[\partial_t \tilde{\rho} + \nabla \cdot \left( (\rho_0 + \tilde{\rho}) \mathbf{u} \right) = 0\]Momentum:
\[\partial_t (\rho \mathbf{u}) + \nabla \cdot (\rho\, \mathbf{u} \otimes \mathbf{u}) + \nabla \left( 2 q_0 \tilde{q} + \tilde{q}^2 \right) = (\nabla \times \tilde{\mathbf{B}}) \times \mathbf{B}_0 + (\nabla \times \mathbf{B}_0) \times \tilde{\mathbf{B}} + (\nabla \times \tilde{\mathbf{B}}) \times \tilde{\mathbf{B}} + \nabla \cdot \left( (\mu + \mu_a(\mathbf{x})) \nabla \mathbf{u} \right)\]Square root of the pressure:
\[\partial_t \tilde{q} + \mathbf{u} \cdot \nabla (q_0 + \tilde{q}) + \frac{\gamma}{2} (q_0 + \tilde{q}) \nabla \cdot \mathbf{u} = 0\]Induction:
\[\partial_t \tilde{\mathbf{B}} - \nabla \times \left( \mathbf{u} \times (\mathbf{B}_0 + \tilde{\mathbf{B}}) \right) + \nabla \times \left( (\eta + \eta_a(\mathbf{x})) \nabla \times \tilde{\mathbf{B}} \right) = 0\]Here \(\mu_a(\mathbf{x})\) and \(\eta_a(\mathbf{x})\) are artificial viscosity and resistivity coefficients. Since \(\nabla(2 q_0 \tilde q + \tilde q^2) = \nabla \tilde p\) with \(\tilde p = p - p_0\), the ideal part (\(\mu = \eta = 0\)) is the perturbative ideal MHD system written in \(q\) instead of \(p\).
Linear model (
linear=True): the terms that are nonlinear in the perturbations are dropped, i.e. \(\rho \to \rho_0\) in the continuity and momentum equations (no \(\mathbf u \cdot \nabla \mathbf u\)), the pressure force is \(\nabla (2 q_0 \tilde q) = 2 q_0 \nabla \tilde q + 2 \tilde q \nabla q_0\), the Lorentz force has no \((\nabla \times \tilde{\mathbf B}) \times \tilde{\mathbf B}\), and \(\tilde q\), \(\tilde{\mathbf B}\) are advected with \(q_0\) and \(\mathbf B_0\) only:\[\partial_t \tilde{q} + \mathbf{u} \cdot \nabla q_0 + \frac{\gamma}{2} q_0 \nabla \cdot \mathbf{u} = 0\,, \qquad \partial_t \tilde{\mathbf{B}} - \nabla \times (\mathbf{u} \times \mathbf{B}_0) + \nabla \times \left( (\eta + \eta_a(\mathbf{x})) \nabla \times \tilde{\mathbf{B}} \right) = 0\,.\]The equations derive from the Lagrangian (with \(\rho_0\) instead of \(\rho\) in the linear model)
\[L = \int_\Omega \left( \rho \frac{|\mathbf{u}|^2}{2} - \frac{\tilde{q}^2 + 2 q_0 \hat{q}}{\gamma - 1} - \frac{|\tilde{\mathbf{B}}|^2}{2} - \mathbf{B}_0 \cdot \hat{\mathbf{B}} \right) \textrm d \mathbf{x}\,,\]with the auxiliary variables \(\hat q\) (
diagnostics.qt3) and \(\hat{\mathbf B}\) (diagnostics.bt2) advected by the perturbations only (in both models),\[\partial_t \hat{q} + \mathbf{u} \cdot \nabla \tilde{q} + \frac{\gamma}{2} \tilde{q} \nabla \cdot \mathbf{u} = \frac{\gamma - 1}{2 q_0} \left( (\mu + \mu_a(\mathbf{x})) |\nabla \mathbf{u}|^2 + (\eta + \eta_a(\mathbf{x})) |\nabla \times \tilde{\mathbf{B}}|^2 \right)\,, \qquad \partial_t \hat{\mathbf{B}} - \nabla \times (\mathbf{u} \times \tilde{\mathbf{B}}) = 0\,.\]They do not feed back on the other unknowns, but they make the energy
\[H = \int_\Omega \left( \rho \frac{|\mathbf{u}|^2}{2} + \frac{\tilde{q}^2 + 2 q_0 \hat{q}}{\gamma - 1} + \frac{|\tilde{\mathbf{B}}|^2}{2} + \mathbf{B}_0 \cdot \hat{\mathbf{B}} \right) \textrm d \mathbf{x}\]a conserved quantity. Note that the heat produced by viscosity and resistivity goes into \(\hat q\), not into \(\tilde q\) (in the linear model, the heating is skipped with the option
fast=Trueof the dissipative propagators).
- classmethod doc_normalization()[source]#
The model uses Alfvén-speed normalization:
\[\hat u = \hat v_A.\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Kinetic energy:
en_U\(= \int \rho |\mathbf u|^2/2\) (\(\rho_0\) in the linear model)Magnetic energies:
en_mag_1\(= \int |\tilde{\mathbf B}|^2/2\),en_mag_2\(= \int \mathbf B_0 \cdot \hat{\mathbf B}\)Thermodynamic energies:
en_thermo_1\(= \int \tilde q^2/(\gamma - 1)\),en_thermo_2\(= \int 2 q_0 \hat q/(\gamma - 1)\)Total energy:
en_tot(conserved)
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
VariationalMomentumAdvection(iflinearis False)VariationalViscosity(ifwith_viscosityis True)VariationalResistivity(ifwith_resistivityis True)
The option
modelof the variational propagators is"perturbative_q", or"linear_q"iflinearis True.
- classmethod doc_long_description()[source]#
MHD written for the perturbations with respect to a zero-flow equilibrium, in the variable \(q = \sqrt{p}\), with optional viscosity and resistivity. By default all nonlinear terms are kept, so the model is equivalent to full MHD, but the equilibrium force balance is subtracted analytically: a zero perturbation stays exactly zero, also when the discrete equilibrium is not in exact force balance. With
linear=Truethe model solves linear MHD around the equilibrium. Using \(q\) makes the Lagrangian quadratic in the perturbed variables, which allows the variational discretization with the auxiliary variables \(\hat q\) and \(\hat{\mathbf B}\). Setwith_viscosity=Falseandwith_resistivity=Falsefor ideal MHD.
- classmethod doc_examples()[source]#
Create the ideal perturbative q-MHD model and its linear version:
from struphy.models import VariationalPerturbativeMHD_with_q model = VariationalPerturbativeMHD_with_q(with_viscosity=False, with_resistivity=False) linear_model = VariationalPerturbativeMHD_with_q(linear=True, with_viscosity=False, with_resistivity=False) model.mhd.sqrt_p model.em_fields.b_field
- classmethod doc_use_cases()[source]#
This model is appropriate for:
nonlinear MHD dynamics around a given (possibly non-uniform) zero-flow equilibrium
linear MHD waves and instabilities (
linear=True)instabilities and waves where the discrete equilibrium force must not drive spurious flows
comparison with
VariationalMHD_with_q(full-f), withVariationalPerturbativeMHD(pressure) and withLinearMHD
Multi-fluid#
- class struphy.models.ColdPlasma(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), charge_number: int = -1, mass_number: float = 0.0005446623093681918, alpha: float | None = None, epsilon: float | None = None)[source]#
Bases:
StruphyModelCold plasma model: electron-fluid current coupled with Maxwell’s equations via a cold-plasma Ohm’s law.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
charge_number (int) – Charge number (in units of the positive elementary charge) of the electron species (default: -1)
mass_number (float) – Mass number (in units of Proton mass) of the electron species (default: 1/1836)
alpha (float, optional) – Dimensionless parameter: plasma frequency / cyclotron frequency. If None, computed from units and charge/mass numbers.
epsilon (float, optional) – Normalized cyclotron period: 1 / (cyclotron frequency × time unit). If None, computed from units and charge/mass numbers.
- classmethod doc_pde()[source]#
PDEs solved by model:
Cold-plasma current:
\[\frac{1}{n_0} \frac{\partial \mathbf{j}}{\partial t} = \frac{1}{\varepsilon} \mathbf{E} + \frac{1}{\varepsilon n_0} \mathbf{j} \times \mathbf{B}_0\]Faraday’s law:
\[\frac{\partial \mathbf{B}}{\partial t} + \nabla \times \mathbf{E} = 0\]Ampère’s law:
\[-\frac{\partial \mathbf{E}}{\partial t} + \nabla \times \mathbf{B} = \frac{\alpha^2}{\varepsilon} \mathbf{j}\]where \((n_0, \mathbf{B}_0)\) denotes an inhomogeneous background.
- classmethod doc_normalization()[source]#
Velocities are normalized with the speed of light and the fields satisfy
\[\hat v = c,\qquad \hat E = c \hat B.\]The dimensionless plasma parameters are the cold-species \(\alpha = \hat\Omega_\mathrm{p}/\hat\Omega_\mathrm{c}\) and \(\varepsilon = 1/(\hat\Omega_\mathrm{c}\hat t)\).
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Electric field energy:
electric_energyMagnetic field energy:
magnetic_energyCold-current energy:
kinetic_energyTotal energy:
total_energy
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
This is a fluid electromagnetic model for a cold plasma response. The electron fluid is represented only through its current, so thermal pressure and kinetic velocity-space effects are omitted. It is useful as a reduced model between vacuum Maxwell and fully kinetic Vlasov-Maxwell dynamics.
- classmethod doc_examples()[source]#
Create and initialize a cold-plasma model:
from struphy.models import ColdPlasma model = ColdPlasma() model.em_fields.e_field model.em_fields.b_field model.electrons.current
- class struphy.models.TwoFluidQuasiNeutralToy(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=1.0), ion_charge_number: int = 1, ion_mass_number: float = 1.0, ion_epsilon: float | None = None, electron_charge_number: int = 1, electron_mass_number: float = 1.0, electron_epsilon: float | None = None)[source]#
Bases:
StruphyModelLinearized, quasi-neutral two-fluid model with zero electron inertia.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits(kBT=1.0))
ion_charge_number (int) – Charge number (in units of the positive elementary charge) of the ion species (default: 1)
ion_mass_number (float) – Mass number (in units of Proton mass) of the ion species (default: 1.0)
ion_epsilon (float, optional) – Normalized cyclotron period of the ion species. If None, computed from units and charge/mass numbers.
electron_charge_number (int) – Charge number (in units of the positive elementary charge) of the electron species (default: 1)
electron_mass_number (float) – Mass number (in units of Proton mass) of the electron species (default: 1.0)
electron_epsilon (float, optional) – Normalized cyclotron period of the electron species. If None, computed from units and charge/mass numbers.
- classmethod doc_pde()[source]#
PDEs solved by model:
Ion momentum:
\[\frac{\partial \mathbf{u}}{\partial t} = -\nabla \phi + \frac{\mathbf{u} \times \mathbf{B}_0}{\varepsilon} + \nu \Delta \mathbf{u} + \mathbf{f}\]Electron momentum:
\[0 = \nabla \phi - \frac{\mathbf{u}_e \times \mathbf{B}_0}{\varepsilon} + \nu_e \Delta \mathbf{u}_e + \mathbf{f}_e\]Quasi-neutrality constraint:
\[\nabla \cdot (\mathbf{u} - \mathbf{u}_e) = 0\]where \(\mathbf{B}_0\) is a static magnetic field and \(\mathbf{f}, \mathbf{f}_e\) are given forcing terms.
- classmethod doc_normalization()[source]#
Thermal-speed scaling is used:
\[\hat u = \hat v_\mathrm{th},\qquad e\hat\phi = m \hat v_\mathrm{th}^2.\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
No default scalar diagnostics are defined by this model.
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
TwoFluidQuasiNeutralToy is a reduced linear two-fluid benchmark with zero electron inertia. It is meant for studying the quasi-neutral solve and the coupled ion/electron velocity response in a simplified setting.
References
[1] Juan Vicente Gutiérrez-Santacreu, Omar Maj, Marco Restelli: Finite element discretization of a Stokes-like model arising in plasma physics, Journal of Computational Physics 2018.
- classmethod doc_examples()[source]#
Create and initialize the quasi-neutral toy model:
from struphy.models import TwoFluidQuasiNeutralToy model = TwoFluidQuasiNeutralToy() model.em_fields.phi model.ions.u model.electrons.u
- class struphy.models.PressureLessSPH(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), charge_number: int = 1, mass_number: float = 1.0, epsilon: float | None = None)[source]#
Bases:
StruphyModelParticle discretization of pressureless Euler flow with external forcing.
- Parameters:
base_units (BaseUnits, optional) – Reference units used to derive model normalization constants.
charge_number (int, optional) – Species charge number (default is 1).
mass_number (float, optional) – Species mass number (default is 1.0).
epsilon (float, optional) – Scaling parameter for force field (default is None).
- classmethod doc_pde()[source]#
PDEs solved by model:
Continuity:
\[\partial_t \rho + \nabla \cdot (\rho \mathbf{u}) = 0\]Momentum:
\[\partial_t (\rho \mathbf{u}) + \nabla \cdot (\rho \mathbf{u} \otimes \mathbf{u}) = \mathbf{F}\]where \(\mathbf{F}\) is an external force.
- classmethod doc_normalization()[source]#
Velocity and field normalizations:
\[\hat{u} = 1\,\textrm{m/s}, \qquad \hat{F} = \frac{m\hat{n}\hat{u}}{\hat{t}}\,.\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
SPH kinetic energy:
en_kin
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
PressureLessSPH is a meshfree particle model for a pressureless fluid. It is primarily useful as a simple SPH benchmark or as a reduced particle transport model in a prescribed force field or potential.
- classmethod doc_examples()[source]#
Create and initialize a pressureless SPH model:
from struphy.models import PressureLessSPH model = PressureLessSPH() model.cold_fluid.var
- class struphy.models.ViscousEulerSPH(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=1.0), charge_number: int = 1, mass_number: float = 1.0, with_B0: bool = True, with_p: bool = True, with_viscosity: bool = True)[source]#
Bases:
StruphyModelEuler equations with viscosity discretized with smoothed particle hydrodynamics (SPH).
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits(kBT=1.0))
charge_number (int) – Charge number (in units of the positive elementary charge) of the fluid species (default: 1)
mass_number (float) – Mass number (in units of Proton mass) of the fluid species (default: 1.0)
with_B0 (bool) – Whether to include the effect of a background magnetic field B0 (default: True)
with_p (bool) – Whether to include pressure forces (default: True)
with_viscosity (bool) – Whether to include viscous dissipation (default: True)
- classmethod doc_pde()[source]#
PDEs solved by model:
Continuity:
\[\partial_t \rho + \nabla \cdot (\rho \mathbf{u}) = 0\]Momentum:
\[\rho (\partial_t \mathbf{u} + \mathbf{u} \cdot \nabla \mathbf{u}) = -\nabla \left( \rho^2 \frac{\partial \mathcal{U}(\rho, S)}{\partial \rho} \right) - \nabla \cdot \boldsymbol{\pi}\]Entropy:
\[\partial_t S + \mathbf{u} \cdot \nabla S = 0\]where \(S\) denotes the entropy per unit mass and \(\boldsymbol{\pi}\) is the viscous stress tensor.
The viscous stress tensor for a Newtonian fluid is given by:
\[\boldsymbol{\sigma} = -\mu \left( \nabla \mathbf{u} + (\nabla \mathbf{u})^T - \frac{2}{3} (\nabla \cdot \mathbf{u}) \mathbf{I} \right)\]where \(\mu\) is the dynamic (shear) viscosity and \(\mathbf{I}\) is the identity tensor.
The internal energy per unit mass can be defined in two ways:
\[\mathrm{isothermal:} \qquad \mathcal{U}(\rho, S) = \kappa(S) \log \rho\]\[\mathrm{polytropic:} \qquad \mathcal{U}(\rho, S) = \kappa(S) \frac{\rho^{\gamma - 1}}{\gamma - 1}\]
- classmethod doc_normalization()[source]#
The characteristic speed is thermal:
\[\hat u = \hat v_\mathrm{th}.\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
SPH kinetic energy:
en_kin
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
PushVxB(ifwith_B0is True)PushVinSPHpressure(ifwith_pis True)PushVinViscousPotential(ifwith_viscosityis True)
- classmethod doc_long_description()[source]#
ViscousEulerSPH is the particle-based SPH fluid model with optional pressure and viscosity contributions. It is intended for meshfree fluid experiments rather than FEEC-based field simulations.
- classmethod doc_examples()[source]#
Create and initialize the viscous Euler SPH model:
from struphy.models import ViscousEulerSPH model = ViscousEulerSPH() model.euler_fluid.var
Bases:
StruphyModelIncompressible Navier-Stokes equations discretized with smoothed particle hydrodynamics (SPH).
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits(kBT=1.0))
charge_number (int) – Charge number (in units of the positive elementary charge) of the fluid species (default: 1)
mass_number (float) – Mass number (in units of Proton mass) of the fluid species (default: 1.0)
with_B0 (bool) – Whether to include the effect of a background magnetic field B0 (default: True)
with_viscosity (bool) – Whether to include viscous dissipation (default: True)
PDEs solved by model:
Incompressible Navier-Stokes:
\[\begin{split}\rho (\partial_t \mathbf{u} + \mathbf{u} \cdot \nabla \mathbf{u}) = -\nabla p - \nabla \cdot \boldsymbol{\sigma} \\[2mm] \nabla \cdot \mathbf{u} = 0\,.\end{split}\]where \(\boldsymbol{\sigma}\) is the viscous stress tensor.
The viscous stress tensor for a Newtonian fluid is given by:
\[\boldsymbol{\sigma} = -\mu \left( \nabla \mathbf{u} + (\nabla \mathbf{u})^T - \frac{2}{3} (\nabla \cdot \mathbf{u}) \mathbf{I} \right)\]where \(\mu\) is the dynamic (shear) viscosity and \(\mathbf{I}\) is the identity tensor.
The characteristic speed is thermal:
\[\hat u = \hat v_\mathrm{th}.\]
The following scalars are tracked during simulation:
SPH kinetic energy:
en_kin
Time integration is performed by the following propagators (in sequence):
PushVxB(ifwith_B0is True)PushVinViscousPotential(ifwith_viscosityis True)
IncompressibleNavierStokesSPH is the particle-based SPH fluid model…
Create and initialize the viscous Euler SPH model:
from struphy.models import ViscousEulerSPH model = ViscousEulerSPH() model.euler_fluid.var
This model is appropriate for:
SPH verification with pressure and viscosity
meshfree compressible-fluid experiments
testing particle-based viscosity and pressure pushers
This model is not suitable for:
FEEC grid-based fluid or MHD formulations
entropy-resolved thermodynamic evolution
kinetic plasma physics
studies that require exact field-based conservation structures
- class struphy.models.HasegawaWakatani(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mass_number: float = 1.0)[source]#
Bases:
StruphyModelHasegawa-Wakatani equations in 2D for drift-wave turbulence.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mass_number (float) – Mass number (in units of Proton mass) of the species (default: 1.0)
- classmethod doc_pde()[source]#
PDEs solved by model:
Density equation:
\[\frac{\partial n}{\partial t} = C (\phi - n) - [\phi, n] - \kappa \, \partial_y \phi + \nu \, \nabla^{2N} n\]Vorticity equation:
\[\frac{\partial \omega}{\partial t} = C (\phi - n) - [\phi, \omega] + \nu \, \nabla^{2N} \omega\]Potential equation:
\[\Delta \phi = \omega\]where \([\phi, n] = \partial_x \phi \partial_y n - \partial_y \phi \partial_x n\), \(C = C(x, y)\) and \(\kappa\) and \(\nu\) are constants (at the moment only \(N=1\) is available).
- classmethod doc_normalization()[source]#
The electrostatic potential is scaled by the thermal-speed unit,
\[\hat u = \hat v_\mathrm{th},\qquad \hat\phi = \hat u \hat x.\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
No default scalar diagnostics are defined by this model.
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
This is a reduced 2D drift-wave turbulence model. The electrostatic potential is recovered from the vorticity at each step, and the coupled density-vorticity dynamics capture resistive drift-wave behavior without resolving full kinetic physics.
- classmethod doc_examples()[source]#
Create and initialize a Hasegawa-Wakatani model:
from struphy.models import HasegawaWakatani model = HasegawaWakatani() model.em_fields.phi model.plasma.density model.plasma.vorticity
- class struphy.models.ViscousFluid(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mass_number: float = 1.0, with_viscosity: bool = True)[source]#
Bases:
StruphyModelFull (non-linear) viscous Navier-Stokes equations discretized with a variational method.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mass_number (float) – Mass number (in units of Proton mass) of the fluid species (default: 1.0)
with_viscosity (bool) – Whether to include viscous dissipation (default: True)
- classmethod doc_pde()[source]#
PDEs solved by model:
Continuity:
\[\partial_t \rho + \nabla \cdot (\rho \mathbf{u}) = 0\]Momentum:
\[\partial_t (\rho \mathbf{u}) + \nabla \cdot (\rho \mathbf{u} \otimes \mathbf{u}) + \rho \nabla \frac{(\rho \mathcal{U}(\rho, s))}{\partial \rho} + s \nabla \frac{(\rho \mathcal{U}(\rho, s))}{\partial s} - \nabla \cdot \left( (\mu + \mu_a(\mathbf{x})) \nabla \mathbf{u} \right) = 0\]Entropy:
\[\partial_t s + \nabla \cdot (s \mathbf{u}) = \frac{1}{T} \left( (\mu + \mu_a(\mathbf{x})) |\nabla \mathbf{u}|^2 \right)\]where the internal energy per unit mass is \(\mathcal U(\rho) = \rho^{\gamma-1} \exp(s / \rho)\). and \(\mu_a(\mathbf{x})\) is an artificial viscosity coefficient.
- classmethod doc_normalization()[source]#
The model uses Alfvén-speed scaling for the velocity and entropy-based thermodynamic units for the internal-energy closure.
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Kinetic energy:
en_UThermodynamic energy:
en_thermoTotal energy:
en_totTotal density / entropy:
dens_tot,entr_tot
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
VariationalViscosity(ifwith_viscosityis True)
- classmethod doc_long_description()[source]#
ViscousFluid is the non-magnetic viscous member of the variational fluid family. It is suited for compressible hydrodynamics with entropy transport and viscous heating but without magnetic effects.
- classmethod doc_examples()[source]#
Create and initialize a viscous-fluid model:
from struphy.models import ViscousFluid model = ViscousFluid() model.fluid.density model.fluid.velocity model.fluid.entropy
- class struphy.models.VariationalBarotropicFluid(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mass_number: float = 1.0)[source]#
Bases:
StruphyModelBarotropic fluid equations discretized with a variational method.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mass_number (float) – Mass number (in units of Proton mass) of the fluid species (default: 1.0)
- classmethod doc_pde()[source]#
PDEs solved by model:
Continuity:
\[\partial_t \rho + \nabla \cdot (\rho \mathbf{u}) = 0\]Momentum:
\[\partial_t (\rho \mathbf{u}) + \nabla \cdot (\rho \mathbf{u} \otimes \mathbf{u}) + \rho \nabla \frac{(\rho \mathcal{U}(\rho))}{\partial \rho} = 0\]where the internal energy per unit mass is \(\mathcal U(\rho) = \rho/2\).
- classmethod doc_normalization()[source]#
The reference flow speed is the Alfvén speed and the internal energy is scaled with density:
\[\hat u = \hat v_A,\qquad \hat{\mathcal U}=\hat\rho/2.\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Kinetic energy:
kinetic_energyThermodynamic energy:
thermo_energyTotal energy:
total_energy
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
This is the simplest variational compressible-fluid model in Struphy. It keeps only density and velocity and uses a barotropic closure rather than a separate entropy or pressure evolution equation.
- classmethod doc_examples()[source]#
Create and initialize a variational barotropic-fluid model:
from struphy.models import VariationalBarotropicFluid model = VariationalBarotropicFluid() model.fluid.density model.fluid.velocity
- class struphy.models.VariationalCompressibleFluid(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mass_number: float = 1.0, divdiv_alpha: float = 0.0)[source]#
Bases:
StruphyModelFully compressible fluid equations discretized with a variational method.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mass_number (float) – Mass number (in units of Proton mass) of the fluid species (default: 1.0)
divdiv_alpha (float) – Coefficient alpha >= 0 of the div-div regularization alpha int rho (div u)^2 dx of the kinetic energy (Hamiltonian regularization, penalizes compressions; default: 0.0), see
kinetic_metric()
- classmethod doc_pde()[source]#
PDEs solved by model:
Continuity:
\[\partial_t \rho + \nabla \cdot (\rho \mathbf{u}) = 0\]Momentum:
\[\partial_t (\rho \mathbf{u}) + \nabla \cdot (\rho \mathbf{u} \otimes \mathbf{u}) + \rho \nabla \frac{(\rho \mathcal{U}(\rho, s))}{\partial \rho} + s \nabla \frac{(\rho \mathcal{U}(\rho, s))}{\partial s} = 0\]Entropy:
\[\partial_t s + \nabla \cdot (s \mathbf{u}) = 0\]where the internal energy per unit mass is \(\mathcal U(\rho) = \rho^{\gamma-1} \exp(s / \rho)\).
With
divdiv_alpha\(=\alpha > 0\), the kinetic energy is \(\int \rho |\mathbf u|^2/2 + \alpha \rho (\nabla \cdot \mathbf u)^2 \,\textrm d \mathbf x\) (Hamiltonian regularization of compressions). The equations follow from the same variational principle with the momentum \(\rho \mathbf u - 2 \alpha \nabla (\rho \nabla \cdot \mathbf u)\) instead of \(\rho \mathbf u\), and the density derivative \(\alpha (\nabla \cdot \mathbf u)^2\) of the kinetic energy enters the force like \(|\mathbf u|^2/2\).
- classmethod doc_normalization()[source]#
The model uses Alfvén-speed scaling for the flow variables together with separate units for internal energy and entropy:
\[\hat u = \hat v_A,\qquad \hat{\mathcal U}=K,\qquad \hat s=\hat\rho C_v.\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Kinetic energy:
en_UThermodynamic energy:
en_thermoDiv-div part of the kinetic energy (only for
divdiv_alpha > 0):en_divdivTotal energy:
en_tot
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
VariationalCompressibleFluid is the entropy-based compressible fluid model in the variational family. It is the natural non-magnetic counterpart to the full variational MHD models.
- classmethod doc_examples()[source]#
Create and initialize a variational compressible-fluid model:
from struphy.models import VariationalCompressibleFluid model = VariationalCompressibleFluid() model.fluid.density model.fluid.velocity model.fluid.entropy
- class struphy.models.VariationalPressurelessFluid(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mass_number: float = 1.0)[source]#
Bases:
StruphyModelPressure-less fluid equations discretized with a variational method.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mass_number (float) – Mass number (in units of Proton mass) of the fluid species (default: 1.0)
- classmethod doc_pde()[source]#
PDEs solved by model:
Continuity:
\[\partial_t \rho + \nabla \cdot (\rho \mathbf{u}) = 0\]Momentum:
\[\partial_t (\rho \mathbf{u}) + \nabla \cdot (\rho \mathbf{u} \otimes \mathbf{u}) = 0\]
- classmethod doc_normalization()[source]#
The flow speed is normalized with the Alfvén speed:
\[\hat u = \hat v_A.\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Kinetic energy:
kinetic_energy
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
This is the pressureless limit of the variational fluid hierarchy. It is intended as a reduced benchmark and as a simple transport model with conservative density and momentum updates.
- classmethod doc_examples()[source]#
Create and initialize a pressureless variational-fluid model:
from struphy.models import VariationalPressurelessFluid model = VariationalPressurelessFluid() model.fluid.density model.fluid.velocity
Fluid-kinetic hybrids#
- class struphy.models.ColdPlasmaVlasov(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), thermal_charge_number: int = -1, thermal_mass_number: float = 0.0005446623093681918, hot_charge_number: int = -1, hot_mass_number: float = 0.0005446623093681918, thermal_alpha: float | None = None, thermal_epsilon: float | None = None, hot_epsilon: float | None = None)[source]#
Bases:
StruphyModelHybrid cold-plasma model: a cold electron fluid coupled with a hot kinetic species and Maxwell’s equations.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
thermal_charge_number (int) – Charge number of the cold (fluid) electron species (default: -1)
thermal_mass_number (float) – Mass number of the cold (fluid) electron species (default: 1/1836)
hot_charge_number (int) – Charge number of the hot (kinetic) electron species (default: -1)
hot_mass_number (float) – Mass number of the hot (kinetic) electron species (default: 1/1836)
thermal_alpha (float, optional) – Dimensionless parameter: cold-species plasma frequency / cyclotron frequency. If None, computed from units and charge/mass numbers.
thermal_epsilon (float, optional) – Normalized cyclotron period of the cold species. If None, computed from units and charge/mass numbers.
hot_epsilon (float, optional) – Normalized cyclotron period of the hot species. If None, computed from units and charge/mass numbers.
- classmethod doc_pde()[source]#
PDEs solved by model:
Hot Vlasov species:
\[\frac{\partial f}{\partial t} + \mathbf{v} \cdot \nabla f + \frac{1}{\varepsilon_\textnormal{h}} \Big[ \mathbf{E} + \mathbf{v} \times \left( \mathbf{B} + \mathbf{B}_0 \right) \Big] \cdot \frac{\partial f}{\partial \mathbf{v}} = 0\]Cold-plasma current:
\[\frac{1}{n_0} \frac{\partial \mathbf{j}_\textnormal{c}}{\partial t} = \frac{1}{\varepsilon_\textnormal{c}} \mathbf{E} + \frac{1}{\varepsilon_\textnormal{c} n_0} \mathbf{j}_\textnormal{c} \times \mathbf{B}_0\]Faraday’s law:
\[\frac{\partial \mathbf{B}}{\partial t} + \nabla \times \mathbf{E} = 0\]Ampère’s law:
\[-\frac{\partial \mathbf{E}}{\partial t} + \nabla \times \mathbf{B} = \frac{\alpha^2}{\varepsilon_\textnormal{h}} \left( \mathbf{j}_\textnormal{c} + \int_{\mathbb{R}^3} \mathbf{v} f \, \text{d}^3 \mathbf{v} \right)\]where \((n_0, \mathbf{B}_0)\) denotes an inhomogeneous background.
At initial time the Poisson equation is solved once to weakly satisfy the Gauss law:
\[\nabla \cdot \mathbf{E} = \nu \frac{\alpha^2}{\varepsilon_\textnormal{h}} \int_{\mathbb{R}^3} f \, \text{d}^3 \mathbf{v}\]
- classmethod doc_normalization()[source]#
Velocities are normalized with the speed of light and the kinetic background is scaled as
\[\hat v = c,\qquad \hat E = c \hat B,\qquad \hat f = \hat n / c^3.\]The model distinguishes cold and hot cyclotron scales through \(\varepsilon_\mathrm{c}\) and \(\varepsilon_\mathrm{h}\).
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Electric field energy:
en_EMagnetic field energy:
en_BCold-current energy:
en_JHot-particle kinetic energy:
en_fTotal energy:
en_tot
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
ColdPlasmaVlasov is an electromagnetic hybrid model that keeps a cold fluid response for the thermal electrons and a kinetic PIC description for a hot species. It is designed for problems where the energetic population matters kinetically but the bulk response can still be treated as cold.
- classmethod doc_examples()[source]#
Create and initialize the cold-plasma plus Vlasov model:
from struphy.models import ColdPlasmaVlasov model = ColdPlasmaVlasov() model.em_fields.e_field model.em_fields.b_field model.thermal_elec.current model.hot_elec.var
- classmethod doc_use_cases()[source]#
This model is appropriate for:
hybrid electromagnetic problems with one hot kinetic species
energetic-particle interaction with a cold background plasma
reduced-cost alternatives to fully kinetic multi-population models
benchmarks of fluid-kinetic current coupling
- class struphy.models.LinearMHDVlasovCC(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mhd_mass_number: float = 1.0, hot_charge_number: int = 1, hot_mass_number: float = 1.0, hot_epsilon: float | None = None)[source]#
Bases:
StruphyModelHybrid linear MHD coupled with energetic ions (6D Vlasov) via the current-coupling scheme.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mhd_mass_number (float) – Mass number (in units of Proton mass) of the MHD bulk species (default: 1.0)
hot_charge_number (int) – Charge number (in units of the positive elementary charge) of the energetic ion species (default: 1)
hot_mass_number (float) – Mass number (in units of Proton mass) of the energetic ion species (default: 1.0)
hot_epsilon (float, optional) – Normalized cyclotron period of the energetic ion species. If None, computed from units and charge/mass numbers.
- classmethod doc_pde()[source]#
PDEs solved by model:
MHD continuity:
\[\frac{\partial \tilde{\rho}}{\partial t} + \nabla \cdot (\rho_0 \tilde{\mathbf{U}}) = 0\]MHD momentum:
\[\rho_0 \frac{\partial \tilde{\mathbf{U}}}{\partial t} + \nabla \tilde{p} = (\nabla \times \tilde{\mathbf{B}}) \times \mathbf{B}_0 + \mathbf{J}_0 \times \tilde{\mathbf{B}} \color{blue} + \frac{A_\textnormal{h}}{A_\textnormal{b}} \frac{1}{\varepsilon} \left( n_\textnormal{h} \tilde{\mathbf{U}} - n_\textnormal{h} \mathbf{u}_\textnormal{h} \right) \times (\mathbf{B}_0 + \tilde{\mathbf{B}}) \color{black}\]MHD pressure:
\[\frac{\partial \tilde{p}}{\partial t} + (\gamma - 1) \nabla \cdot (p_0 \tilde{\mathbf{U}}) + p_0 \nabla \cdot \tilde{\mathbf{U}} = 0\]MHD induction:
\[\frac{\partial \tilde{\mathbf{B}}}{\partial t} = \nabla \times (\tilde{\mathbf{U}} \times \mathbf{B}_0), \qquad \nabla \cdot \tilde{\mathbf{B}} = 0\]Energetic-particle Vlasov equation:
\[\frac{\partial f_\textnormal{h}}{\partial t} + \mathbf{v} \cdot \nabla f_\textnormal{h} + \frac{1}{\varepsilon} \left[ \color{blue} (\mathbf{B}_0 + \tilde{\mathbf{B}}) \times \tilde{\mathbf{U}} \color{black} + \mathbf{v} \times (\mathbf{B}_0 + \tilde{\mathbf{B}}) \right] \cdot \frac{\partial f_\textnormal{h}}{\partial \mathbf{v}} = 0\]Energetic-particle moments:
\[n_\textnormal{h} = \int_{\mathbb{R}^3} f_\textnormal{h} \, \textnormal{d}^3 \mathbf{v}, \qquad n_\textnormal{h} \mathbf{u}_\textnormal{h} = \int_{\mathbb{R}^3} f_\textnormal{h} \mathbf{v} \, \textnormal{d}^3 \mathbf{v}\]where \(\mathbf{J}_0 = \nabla\times\mathbf{B}_0\).
- classmethod doc_normalization()[source]#
Both fluid and particle velocities are normalized with the Alfvén speed,
\[\hat U = \hat v = \hat v_A,\qquad \hat f_h = \hat n / \hat v_A^3.\]The hot-species cyclotron parameter is \(\varepsilon = 1/(\hat\Omega_{c,\mathrm{hot}}\hat t)\).
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
MHD kinetic energy:
en_UThermal pressure energy:
en_pMagnetic energy:
en_BEnergetic-particle energy:
en_fTotal energy:
en_totLost particles:
n_lost_particles
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
LinearMHDVlasovCC is a current-coupling hybrid model for small perturbations around an MHD equilibrium. It is intended for energetic particle effects on linear MHD waves and instabilities without the cost of a fully kinetic background plasma.
- classmethod doc_examples()[source]#
Create and initialize the linear MHD-Vlasov current-coupling model:
from struphy.models import LinearMHDVlasovCC model = LinearMHDVlasovCC() model.em_fields.b_field model.mhd.velocity model.energetic_ions.var
- class struphy.models.LinearMHDVlasovPC(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mhd_mass_number: float = 1.0, hot_charge_number: int = 1, hot_mass_number: float = 1.0, hot_epsilon: float | None = None, turn_off: tuple[str, ...] = (None,))[source]#
Bases:
StruphyModelHybrid linear MHD coupled with energetic ions (6D Vlasov) via the pressure-coupling scheme.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mhd_mass_number (float) – Mass number (in units of Proton mass) of the MHD bulk species (default: 1.0)
hot_charge_number (int) – Charge number (in units of the positive elementary charge) of the energetic ion species (default: 1)
hot_mass_number (float) – Mass number (in units of Proton mass) of the energetic ion species (default: 1.0)
hot_epsilon (float, optional) – Normalized cyclotron period of the energetic ion species. If None, computed from units and charge/mass numbers.
turn_off (tuple[str, ...]) – Names of coupling terms to turn off (default: (None,))
- classmethod doc_pde()[source]#
PDEs solved by model:
MHD continuity:
\[\frac{\partial \tilde{\rho}}{\partial t} + \nabla \cdot (\rho_0 \tilde{\mathbf{U}}) = 0\]MHD momentum:
\[\rho_0 \frac{\partial \tilde{\mathbf{U}}}{\partial t} + \nabla \tilde{p} + \frac{A_\textnormal{h}}{A_\textnormal{b}} \nabla \cdot \tilde{\mathbb{P}}_{\textnormal{h},\perp} = (\nabla \times \tilde{\mathbf{B}}) \times \mathbf{B}_0 + \mathbf{J}_0 \times \tilde{\mathbf{B}}\]\[\mathbf{J}_0 = \nabla \times \mathbf{B}_0\]MHD pressure:
\[\frac{\partial \tilde{p}}{\partial t} + \nabla \cdot (p_0 \tilde{\mathbf{U}}) + \frac{2}{3} p_0 \nabla \cdot \tilde{\mathbf{U}} = 0\]MHD induction:
\[\frac{\partial \tilde{\mathbf{B}}}{\partial t} - \nabla \times (\tilde{\mathbf{U}} \times \mathbf{B}_0) = 0\]Energetic-particle Vlasov equation:
\[\frac{\partial f_\textnormal{h}}{\partial t} + (\mathbf{v} + \tilde{\mathbf{U}}_\perp) \cdot \nabla f_\textnormal{h} + \left[ \frac{1}{\epsilon} \mathbf{v} \times (\mathbf{B}_0 + \tilde{\mathbf{B}}) - \nabla \tilde{\mathbf{U}}_\perp \cdot \mathbf{v} \right] \cdot \frac{\partial f_\textnormal{h}}{\partial \mathbf{v}} = 0\]Perpendicular pressure tensor:
\[\tilde{\mathbb{P}}_{\textnormal{h},\perp} = \int \mathbf{v}_\perp \mathbf{v}_\perp^\top f_\textnormal{h} \, d \mathbf{v}\]
- classmethod doc_normalization()[source]#
Fluid and hot-particle velocities are normalized with the bulk Alfvén speed. The kinetic pressure tensor is scaled consistently with \(A_h m_H \hat n \hat v_A^2\), and the hot cyclotron parameter is \(\varepsilon\).
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
MHD kinetic energy:
en_UThermal pressure energy:
en_pMagnetic energy:
en_BEnergetic-particle kinetic energy:
en_fTotal energy:
en_totLost particles:
n_lost_particles
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
LinearMHDVlasovPC is the pressure-coupling counterpart to the current-coupling hybrid model. It is targeted at linear problems where energetic-particle pressure anisotropy is the relevant feedback channel on the bulk MHD dynamics.
- classmethod doc_examples()[source]#
Create and initialize the linear MHD-Vlasov pressure-coupling model:
from struphy.models import LinearMHDVlasovPC model = LinearMHDVlasovPC() model.em_fields.b_field model.mhd.velocity model.energetic_ions.var
- class struphy.models.LinearMHDDriftkineticCC(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), mhd_mass_number: float = 1.0, hot_charge_number: int = 1, hot_mass_number: float = 1.0, hot_epsilon: float | None = None, turn_off: tuple[str, ...] = (None,))[source]#
Bases:
StruphyModelHybrid linear ideal MHD coupled with energetic ions (5D drift-kinetic) via the current-coupling scheme.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
mhd_mass_number (float) – Mass number (in units of Proton mass) of the MHD bulk species (default: 1.0)
hot_charge_number (int) – Charge number (in units of the positive elementary charge) of the energetic ion species (default: 1)
hot_mass_number (float) – Mass number (in units of Proton mass) of the energetic ion species (default: 1.0)
hot_epsilon (float, optional) – Normalized cyclotron period of the energetic ion species. If None, computed from units and charge/mass numbers.
turn_off (tuple[str, ...]) – Names of coupling terms to turn off (default: (None,))
- classmethod doc_pde()[source]#
PDEs solved by model:
MHD continuity:
\[\frac{\partial \tilde{\rho}}{\partial t} + \nabla \cdot (\rho_0 \tilde{\mathbf{U}}) = 0\]MHD momentum:
\[\rho_0 \frac{\partial \tilde{\mathbf{U}}}{\partial t} + \nabla \tilde{p} = (\nabla \times \tilde{\mathbf{B}}) \times \mathbf{B} + (\nabla \times \mathbf{B}_0) \times \tilde{\mathbf{B}} + \frac{A_\textnormal{h}}{A_\textnormal{b}} \left[ \frac{1}{\epsilon} n_\textnormal{gc} \tilde{\mathbf{U}} - \frac{1}{\epsilon} \mathbf{J}_\textnormal{gc} - \nabla \times \mathbf{M}_\textnormal{gc} \right] \times \mathbf{B}\]MHD pressure:
\[\frac{\partial \tilde{p}}{\partial t} + \nabla \cdot (p_0 \tilde{\mathbf{U}}) + \frac{2}{3} p_0 \nabla \cdot \tilde{\mathbf{U}} = 0\]MHD induction:
\[\frac{\partial \tilde{\mathbf{B}}}{\partial t} - \nabla \times (\tilde{\mathbf{U}} \times \mathbf{B}) = 0\]Energetic-particle drift-kinetic equation:
\[\frac{\partial f_\textnormal{h}}{\partial t} + \frac{1}{B_\parallel^*} \left( v_\parallel \mathbf{B}^* - \mathbf{b}_0 \times \mathbf{E}^* \right) \cdot \nabla f_\textnormal{h} + \frac{1}{\epsilon} \frac{1}{B_\parallel^*} (\mathbf{B}^* \cdot \mathbf{E}^*) \frac{\partial f_\textnormal{h}}{\partial v_\parallel} = 0\]Energetic-particle moments:
\[n_\textnormal{gc} = \int f_\textnormal{h} B_\parallel^* \, \textnormal{d} v_\parallel \textnormal{d} \mu\]\[\mathbf{J}_\textnormal{gc} = \int \frac{f_\textnormal{h}}{B_\parallel^*} \left( v_\parallel \mathbf{B}^* - \mathbf{b}_0 \times \mathbf{E}^* \right) \, \textnormal{d} v_\parallel \textnormal{d} \mu\]\[\mathbf{M}_\textnormal{gc} = -\int f_\textnormal{h} B_\parallel^* \mu \mathbf{b}_0 \, \textnormal{d} v_\parallel \textnormal{d} \mu\]where
\[\begin{split}B^*_\parallel = \mathbf{b}_0 \cdot \mathbf{B}^* \\[2mm] \mathbf{B}^* &= \mathbf{B} + \epsilon v_\parallel \nabla \times \mathbf{b}_0 \\[2mm] \mathbf{E}^* &= -\tilde{\mathbf{U}} \times \mathbf{B} - \epsilon \mu \nabla (\mathbf{b}_0 \cdot \mathbf{B})\end{split}\]
- classmethod doc_normalization()[source]#
The bulk and energetic-particle flow scales are normalized with the bulk Alfvén speed, while the magnetic moment carries its own unit:
\[\hat U = \hat v = \hat v_{A,\mathrm{bulk}},\qquad \hat\mu = A_h m_H \hat v_h^2 / \hat B.\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
MHD kinetic energy:
en_UThermal pressure energy:
en_pMagnetic energy:
en_BParallel energetic-particle energy:
en_fvMagnetic-moment energetic-particle energy:
en_fBTotal energy:
en_totLost particles:
n_lost_particles
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
The default parameter file of this model (
struphy params LinearMHDDriftkineticCC) sets the energy-conserving discrete gradient methods of the reference discretization for the guiding-center pushers:algo="discrete_gradient_2nd_order"(implicit mid-point) for 1 andalgo="discrete_gradient_1st_order_newton"(Itoh-Abe with Newton iterations) for 2. The explicit Runge-Kutta methods (algo="explicit") are cheaper but not energy conserving.
- classmethod doc_long_description()[source]#
LinearMHDDriftkineticCC is the reduced-kinetic hybrid current-coupling model for energetic ions. It is useful when gyrophase averaging is acceptable but energetic-particle feedback on linear MHD still has to be retained.
- classmethod doc_examples()[source]#
Create and initialize the linear MHD plus drift-kinetic CC model:
from struphy.models import LinearMHDDriftkineticCC model = LinearMHDDriftkineticCC() model.em_fields.b_field model.mhd.velocity model.energetic_ions.var
Particle diffusion#
- class struphy.models.DeterministicParticleDiffusion(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None))[source]#
Bases:
StruphyModelDiffusion equation discretized with a deterministic particle method.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
- classmethod doc_pde()[source]#
PDEs solved by model:
Find \(u : \mathbb{R} \times \Omega \to \mathbb{R}^+\) such that
\[\frac{\partial u}{\partial t} + \nabla \cdot \left( \mathbf{F}(u) u \right) = 0, \qquad \mathbf{F}(u) = -\mathbb{D} \frac{\nabla u}{u}\]where \(\mathbb{D} : \Omega \to \mathbb{R}^{3 \times 3}\) is a positive diffusion matrix. At the moment only matrices of the form \(D * Id\) are implemented, where \(D > 0\) is a positive diffusion coefficient.
- classmethod doc_normalization()[source]#
The diffusion coefficient defines the normalization,
\[\hat D = \hat x^2 / \hat t.\]No separate plasma velocity scale is used in this model.
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
No default scalar diagnostics are defined by this model.
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
This is a particle discretization of diffusion where particles follow a deterministic drift derived from the current estimate of the density. It is intended mainly for diffusion-method development and verification rather than plasma dynamics.
- classmethod doc_examples()[source]#
Create and initialize a deterministic diffusion model:
from struphy.models import DeterministicParticleDiffusion model = DeterministicParticleDiffusion() model.hydrogen.var
- class struphy.models.RandomParticleDiffusion(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None))[source]#
Bases:
StruphyModelDiffusion equation discretized with a random particle method, via a Wiener process.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
- classmethod doc_pde()[source]#
PDEs solved by model:
Find \(u : \mathbb{R} \times \Omega \to \mathbb{R}^+\) such that
\[\frac{\partial u}{\partial t} - D \, \Delta u = 0\]where \(D > 0\) is a positive diffusion coefficient.
- classmethod doc_normalization()[source]#
The natural scaling is set by the diffusion coefficient:
\[\hat D = \hat x^2 / \hat t.\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
No default scalar diagnostics are defined by this model.
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
This model is the stochastic counterpart of the deterministic particle diffusion model. It is intended for diffusion-method development and comparisons between random-walk and deterministic transport strategies.
- classmethod doc_examples()[source]#
Create and initialize a random diffusion model:
from struphy.models import RandomParticleDiffusion model = RandomParticleDiffusion() model.hydrogen.var
Pure fields#
- class struphy.models.Poisson(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), with_t_dep_source=False)[source]#
Bases:
StruphyModelWeak discretization of Poisson’s equation with a diffusion matrix, stabilization and an optional time-dependent right-hand side.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
with_t_dep_source (bool) – Whether the right-hand side source term is time-dependent (default: False)
- classmethod doc_pde()[source]#
PDEs solved by model:
Find \(\phi \in H^1\) such that
\[-\nabla \cdot D_0(\mathbf{x}) \nabla \phi + n_0(\mathbf{x}) \phi = \rho(t, \mathbf{x})\]where \(n_0, \rho(t) : \Omega \to \mathbb{R}\) are real-valued functions, \(\rho(t)\) is parametrized by time \(t\), and \(D_0 : \Omega \to \mathbb{R}^{3 \times 3}\) is a positive matrix. Boundary terms from integration by parts are assumed to vanish.
- classmethod doc_normalization()[source]#
The coefficient scaling is
\[\hat D = \hat n / \hat x^2,\qquad \hat\rho = \hat n.\]No dedicated velocity normalization is used.
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
No default scalar diagnostics are defined by this model.
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
TimeDependentSource(ifwith_t_dep_sourceis True)
- classmethod doc_long_description()[source]#
Poisson is the standalone elliptic field-solve model used for weak diffusion/Poisson problems. It is also the building block reused by other models for initial electrostatic solves.
- classmethod doc_examples()[source]#
Create and initialize a Poisson model:
from struphy.models import Poisson model = Poisson() model.em_fields.phi model.em_fields.source
- class struphy.models.CurlCurl(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), with_t_dep_source=False)[source]#
Bases:
StruphyModelWeak discretization of the curl-curl problem with a mass (stabilization) term and an optional time-dependent right-hand side.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
with_t_dep_source (bool) – Whether the right-hand side source term is time-dependent (default: False)
- classmethod doc_pde()[source]#
PDEs solved by model:
Find \(\mathbf E \in H(\textnormal{curl})\) such that
\[\nabla \times \nabla \times \mathbf E + \sigma\, \mathbf E = \mathbf J(t, \mathbf{x})\]where \(\sigma > 0\) is a scalar and \(\mathbf J(t) : \Omega \to \mathbb{R}^3\) is a source parametrized by time \(t\). Boundary terms from integration by parts are assumed to vanish.
- classmethod doc_normalization()[source]#
The coefficient scaling is
\[\hat \sigma = 1 / \hat x^2,\qquad \hat J = \hat E / \hat x^2.\]No dedicated velocity normalization is used.
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
No default scalar diagnostics are defined by this model.
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
TimeDependentSource(ifwith_t_dep_sourceis True)
- classmethod doc_long_description()[source]#
CurlCurl is the standalone elliptic field-solve model for the stabilized curl-curl problem in \(H(\textnormal{curl})\), e.g. from implicit time stepping of Maxwell’s equations or from magnetostatics. It is used to test and profile solvers and preconditioners for this operator, whose large kernel (the gradients) makes standard preconditioners ineffective.
- classmethod doc_examples()[source]#
Create and initialize a CurlCurl model:
from struphy.models import CurlCurl model = CurlCurl() model.em_fields.e_field model.em_fields.source
- class struphy.models.GyrokineticPoisson(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), epsilon: float = 1.0, Z: int = 1, with_t_dep_source=False)[source]#
Bases:
StruphyModelWeak discretization of the gyrokinetic Poisson equation with adiabatic electrons and an optional time-dependent right-hand side.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
epsilon (float) – Gyrokinetic parameter (default: 1.0)
Z (int) – Charge number of the ions (default: 1)
with_t_dep_source (bool) – Whether the right-hand side source term is time-dependent (default: False)
- classmethod doc_pde()[source]#
PDEs solved by model:
Find \(\phi \in H^1\) such that
\[\frac{1}{Z\epsilon^2} \frac{n_0}{T_0} \left( \phi - \langle \phi \rangle \right) - \nabla \cdot \left( \frac{n_0}{|B_0|^2} \nabla_\perp \phi \right) = \frac{1}{\epsilon} \rho(t, \mathbf{x})\,,\]where \(n_0, T_0, |B_0| : \Omega \to \mathbb{R}\) are the equilibrium density, temperature and magnetic field strength, \(\nabla_\perp = (\mathbb{1} - \mathbf b_0 \mathbf b_0^\top) \nabla\) is the gradient perpendicular to the unit vector \(\mathbf b_0 = \mathbf B_0 / |B_0|\), and \(\langle \phi \rangle\) is the \(n_0/T_0\)-weighted average over the toroidal (cylindrical geometry) or the poloidal and toroidal (toroidal geometry) angle. \(\epsilon\) is the gyrokinetic parameter, \(Z\) the charge number of the ions, and the right-hand side \(\rho(t)\) is parametrized by time \(t\). Boundary terms from integration by parts are assumed to vanish.
- classmethod doc_normalization()[source]#
The coefficient scaling is
\[\hat \rho = \hat n.\]No dedicated velocity normalization is used.
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
No default scalar diagnostics are defined by this model.
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
TimeDependentSource(ifwith_t_dep_sourceis True)
- classmethod doc_long_description()[source]#
GyrokineticPoisson is the standalone elliptic field-solve model for the gyrokinetic Poisson equation with adiabatic electrons, the field equation of
DriftKineticElectrostaticAdiabatic. It solves the field equation with a prescribed (possibly time-dependent) source, e.g. for verification and profiling of the solver in a given MHD equilibrium.
- classmethod doc_examples()[source]#
Create and initialize a GyrokineticPoisson model:
from struphy.models import GyrokineticPoisson model = GyrokineticPoisson(epsilon=1.0, Z=1) model.em_fields.phi model.em_fields.source
- class struphy.models.Maxwell(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None))[source]#
Bases:
StruphyModelMaxwell’s equations in vacuum for electromagnetic field evolution.
- Parameters:
base_units (BaseUnits) – Base units for normalization (default: BaseUnits())
- classmethod doc_pde()[source]#
PDEs solved by model:
Ampère’s law (no current):
\[\frac{\partial \mathbf{E}}{\partial t} - \nabla \times \mathbf{B} = 0\]Faraday’s law:
\[\frac{\partial \mathbf{B}}{\partial t} + \nabla \times \mathbf{E} = 0\]
- classmethod doc_normalization()[source]#
Velocity and fields are normalized as:
\[\hat v = c\,,\qquad \hat E = c \hat B\]where \(c\) is the speed of light.
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Electric energy: \(E_E = \frac{1}{2} \int |\mathbf E|^2 \, dV\)
Magnetic energy: \(E_B = \frac{1}{2} \int |\mathbf B|^2 \, dV\)
Total energy: \(E_{total} = E_E + E_B\)
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
This model simulates the propagation of electromagnetic waves in vacuum using Maxwell’s equations without sources. It uses a finite element exterior calculus (FEEC) formulation with the electric field in H(curl) and the magnetic field in H(div) spaces.
Particle tracing#
- class struphy.models.Vlasov(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), charge_number: int = 1, mass_number: float = 1.0)[source]#
Bases:
StruphyModelVlasov 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 doc_pde()[source]#
PDEs solved by model:
Vlasov equation:
\[\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\]
- classmethod doc_normalization()[source]#
The characteristic speed is the cyclotron scale
\[\hat v = \hat\Omega_c \hat x.\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Particle kinetic energy:
kinetic_energy
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
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.
- classmethod doc_examples()[source]#
Create and initialize a Vlasov test-particle model:
from struphy.models import Vlasov model = Vlasov() model.kinetic_ions.var
- class struphy.models.GuidingCenter(base_units: BaseUnits = BaseUnits(x=1.0, B=1.0, n=1.0, kBT=None), charge_number: int = 1, mass_number: float = 1.0, epsilon: float | None = None)[source]#
Bases:
StruphyModelGuiding-center 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)
epsilon (float, optional) – Normalized cyclotron period: 1 / (cyclotron frequency × time unit). If None, computed from units and charge/mass numbers.
- classmethod doc_pde()[source]#
PDEs solved by model:
Guiding-center Vlasov equation:
\[\frac{\partial f}{\partial t} + \left[ v_\parallel \frac{\mathbf{B}^*}{B^*_\parallel} + \frac{\mathbf{E}^* \times \mathbf{b}_0}{B^*_\parallel} \right] \cdot \frac{\partial f}{\partial \mathbf{X}} + \left[ \frac{1}{\epsilon} \frac{\mathbf{B}^*}{B^*_\parallel} \cdot \mathbf{E}^* \right] \cdot \frac{\partial f}{\partial v_\parallel} = 0\]where \(f(\mathbf{X}, v_\parallel, \mu, t)\) is the guiding center distribution and
\[\mathbf{E}^* = -\epsilon \mu \nabla |B_0|, \qquad \mathbf{B}^* = \mathbf{B}_0 + \epsilon v_\parallel \nabla \times \mathbf{b}_0, \qquad B^*_\parallel = \mathbf{B}^* \cdot \mathbf{b}_0\]
- classmethod doc_normalization()[source]#
The reference speed is Alfvénic and the key small parameter is the normalized cyclotron time:
\[\hat v = \hat v_A,\qquad \varepsilon = 1/(\hat\Omega_c \hat t).\]
- classmethod doc_scalar_quantities()[source]#
The following scalars are tracked during simulation:
Parallel kinetic energy:
en_fvMagnetic-moment energy contribution:
en_fBTotal particle energy:
en_totLost markers:
n_lost_particles
- classmethod doc_discretization()[source]#
Time integration is performed by the following propagators (in sequence):
- classmethod doc_long_description()[source]#
GuidingCenter is the reduced kinetic model used when fast gyromotion is averaged out and only guiding-center motion needs to be resolved. It evolves particles in a fixed magnetic background without any self-consistent field solve.
- classmethod doc_examples()[source]#
Create and initialize a guiding-center model:
from struphy.models import GuidingCenter model = GuidingCenter() model.kinetic_ions.var