Available Propagators#
Field solvers#
- class struphy.propagators.poisson_solve.PoissonSolve(rho: FEECVariable | ParticlesToGrid | Callable | list | None = None, rho_coeffs: float | list | None = None, delta_f_source: bool = False)[source]#
Bases:
ImplicitDiffusionWeak discretization of the (stabilized) Poisson equation: find \(\phi \in H^1\) such that
\[\epsilon \int_\Omega \psi\, \phi\,\textrm d \mathbf x + \int_\Omega \nabla \psi^\top \, \nabla \phi \,\textrm d \mathbf x = \sum_i \int_\Omega \psi\, \rho_i(\mathbf x)\,\textrm d \mathbf x \qquad \forall \ \psi \in H^1\,,\]where \(\epsilon \in \mathbb R\) is a stabilization parameter. Boundary terms from integration by parts are assumed to vanish. The equation is discretized as
\[\left( \epsilon\,\mathbb S + \mathbb G^\top \mathbb M^1 \mathbb G \right)\, \boldsymbol{\phi} = \sum_i(\Lambda^0, \rho_i )_{L^2}\,,\]where \(\mathbb M^1\) is the \(H(\textnormal{curl})\)-mass matrix and \(\mathbb S\) is a stabilization matrix.
- class Options(stab_eps: float = 0.0, stab_mat: Literal['M0', 'M0ad', 'Id'] = 'M0', stab_average: str | None = None, diffusion_mat: Literal['M1', 'M1perp', 'M1para', 'M1gyro'] = 'M1', x0: StencilVector | None = None, solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MultiGrid', 'StiffnessPreconditioner', 'MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] | None = None, precond_params: dict | None = None, multigrid: MultiGridOptions | None = None, solver_params: SolverParameters | None = None, enforce_compatibility: bool = True, filter_params: dict[PICVariable | SPHVariable, FilterParameters] | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
Poisson.- Parameters:
stab_eps (float, default=0.0) – Stabilization weight used for the mass-like term in the Poisson operator. Internally mapped to
sigma_1 = stab_epsin the parentImplicitDiffusionformulation.stab_mat ({"M0", "M0ad", "Id"}, default="M0") –
Stabilization matrix multiplied by
stab_eps."M0": standard weighted 0-form mass operator."M0ad": adiabatic-electron weighted 0-form mass operator."Id": identity operator.
stab_average (str, default=None) – If not None, the stabilization becomes
stab_mat @ (I - A), whereAis thestab_mat-weighted average over the given logical directions, e.g."eta3"or"eta2 eta3", seeImplicitDiffusion.diffusion_mat ({"M1", "M1perp", "M1gyro"}, defaults="M1") –
Diffusion matrix.
"M1": standard weighted 1-form mass operator."M1perp": weighted 1-form mass operator perpendicular to magnetic field."M1para": weighted 1-form mass operator parallele to magnetic field."M1gyro": weighted 1-form mass operator used in gyrokinetic model.
x0 (StencilVector, default=None) – Initial guess for the iterative linear solver.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Name of the symmetric iterative solver passed to
psydac.linalg.solvers.inverse().precond (LiteralOptions.OptsDiffusionPrecond, default=None) – Name of the preconditioner, see
ImplicitDiffusion("MultiGrid"for geometric multigrid,"StiffnessPreconditioner"for the Kronecker approximation of the Laplacian,"MassMatrixPreconditioner"for the Kronecker approximation ofM0). Requiressolver="pcg".precond_params (dict, default=None) – Keyword arguments passed to the constructor of the mass-matrix or stiffness preconditioner, see
ImplicitDiffusion.multigrid (MultiGridOptions, default=None) – Options of the multigrid preconditioner (if
precond="MultiGrid"). For periodic or Neumann boundary conditions withstab_eps = 0, useMultiGridOptions(nullspace="constants").solver_params (SolverParameters, default=None) – Iterative-solver controls (for example
tol,maxiter,verbose,info,recycle). IfNone, defaults toSolverParameters().enforce_compatibility (bool, default=True) –
If
Trueand the Poisson operator is singular (no Dirichlet boundary conditions and no polar splines, e.g. fully periodic), the constant (“net charge”) component of the right-hand side is removed before the solve,\[\mathbf b \leftarrow \mathbf b - \frac{\mathbf 1^\top \mathbf b}{\mathbf 1^\top \mathbb M^0 \mathbf 1}\, \mathbb M^0 \mathbf 1\,,\]such that the discrete compatibility condition \(\mathbf 1^\top \mathbf b = 0\) holds (\(\mathbf 1\) is the coefficient vector of the constant function). This is equivalent to a uniform neutralizing background; for particle sources, the net charge is Monte-Carlo noise. Without it, the constant mode of
phibecomes(net charge) / stab_eps, andgrad philoses digits to cancellation.stab_epsthen only fixes the constant ofphi. Set toFalseifstab_eps * stab_matis a physical term (e.g. adiabatic electrons withstab_mat="M0ad") and not just a stabilization.filter_params (dict[PICVariable | SPHVariable, FilterParameters], default=None) – If not None, specifies a filter to the accumulation of a specific variable.
Notes
Poisson.OptionsreusesImplicitDiffusioninternals by enforcingsigma_2 = 0.0,sigma_3 = 1.0,divide_by_dt = Falseanddiffusion_mat = "M1"in__post_init__.
- class struphy.propagators.gyrokinetic_poisson_solve.GyrokineticPoissonSolve(rho: FEECVariable | ParticlesToGrid | Callable | list | None = None, rho_coeffs: float | list | None = None, epsilon: float = 1.0, Z: int = 1, diagnostic: FEECVariable | None = None)[source]#
Bases:
ImplicitDiffusionWeak discretization of the Poisson equation, with adiabatic response of electrons.
Find \(\phi \in H^1\) such that
\[\frac{1}{Z\epsilon^2} \int_\Omega \frac{n_0}{T_0} \psi\, (\phi - \langle \phi \rangle)\,\textrm d \mathbf x + \int_\Omega \frac{n_0}{|B_0|^2} \nabla \psi^\top \,\mathbb D \nabla \phi \,\textrm d \mathbf x = \frac{1}{\epsilon} \sum_i c_i \int_\Omega \psi\, \rho_i(\mathbf x)\,\textrm d \mathbf x \qquad \forall \ \psi \in H^1\,,\]where \(c_i\) are the
rho_coeffs, \(\mathbb D\) can be an anisotropic (gyrokinetic) diffusion operator (or the identity; the weight \(n_0/|B_0|^2\) is that ofdiffusion_mat="M1gyro","M1"and"M1perp"are unweighted), \(\epsilon \in \mathbb R\) is the gyrokinetic ratio defined in units, Z the charge number of ions and \(\langle \phi \rangle\) the (weighted) average over \(\eta_3\) or over \(\eta_2, \eta_3\) (which_geometry, or zero forwhich_geometry=None): the electrons do not respond to the flux-surface averaged potential. For a full-f particle source (no control variate) the neutralizing background \(-n_0\) is added to \(\rho_i\) automatically (seeImplicitDiffusion). Boundary terms from integration by parts are assumed to vanish.The equation is discretized as
\[\left( \frac{1}{Z\epsilon^2}\,\mathbb M^0_{\textnormal{ad},T} (\mathbb I - \mathbb A) + \mathbb G^\top \mathbb M^1_\textnormal{gyro} \mathbb G \right)\, \boldsymbol\phi^{n+1} = \frac{1}{\epsilon} \sum_i c_i (\Lambda^0, \rho_i )_{L^2}\,,\]where \(\mathbb M^0_{\textnormal{ad},T}\) is the mass matrix weighted with \(n_0/T_0\) (
stab_mat, default"M0ad_withT"), \(\mathbb A\) the weighted average projection and \(\mathbb M^1_\textnormal{gyro}\) the gyrokinetic mass matrix (diffusion_mat).- Parameters:
rho (FEECVariable or Callable or ParticlesToGrid or list) – (List of) right-hand side source term(s) of the Poisson problem (optional, can be set with a setter later). See
ImplicitDiffusionfor the accepted entries; particle sources are passed asParticlesToGrid.rho_coeffs (float or list) – Multiplicative coefficient(s) applied to
rho(default 1.0 for all sources).epsilon (float) – Gyrokinetic parameter.
Z (int) – Charge number of the ions.
diagnostic (FEECVariable, optional) – If given, the L2 projection of the right-hand side is stored in it.
- class Options(stab_mat: Literal['M0', 'M0ad', 'Id', 'M0ad_withT'] = 'M0ad_withT', diffusion_mat: Literal['M1', 'M1perp', 'M1gyro'] = 'M1gyro', which_geometry: Literal['cylindrical', 'toroidal', None] = 'cylindrical', x0: StencilVector | None = None, solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MultiGrid', 'StiffnessPreconditioner', 'MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] | None = None, precond_params: dict | None = None, multigrid: MultiGridOptions | None = None, solver_params: SolverParameters | None = None, filter_params: dict[PICVariable | SPHVariable, FilterParameters] | None = None, enforce_compatibility: bool = False)[source]#
Bases:
objectConfiguration options for
GyrokineticPoissonSolve.- Parameters:
stab_mat ({"M0", "M0ad", "Id", "M0ad_withT"}, default="M0ad_withT") –
Mass matrix of the adiabatic term, multiplied by
1 / (Z * epsilon**2)."M0": standard weighted 0-form mass operator."M0ad": adiabatic-electron weighted 0-form mass operator (weight \(n_0\))."Id": identity operator."M0ad_withT": adiabatic-electron weighted 0-form mass operator (weight \(n_0/T_0\)).
which_geometry ({"cylindrical", "toroidal", None}, default="cylindrical") – Geometry of the problem, determines the meaning of <phi> in the stabilization term: the average over
eta3(cylindrical) or overeta2andeta3(toroidal), i.e.stab_averageofImplicitDiffusionis"eta3"or"eta2 eta3". WithNonethere is no average, \(\langle \phi \rangle = 0\): the electrons respond adiabatically to all modes (e.g. for a comparison with the full-orbit model in a slab, seeAdiabaticPoissonSolve). The average is weighted withstab_mat(for equilibrium density and temperature constant on the averaged surfaces, the flux-surface average with the volume element), which keeps the operator symmetric (WeightedAverageProjection).diffusion_mat ({"M1", "M1perp", "M1gyro"}, default="M1gyro") – Diffusion matrix.
x0 (StencilVector, default=None) – Initial guess for the iterative linear solver.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Name of the symmetric iterative solver passed to
psydac.linalg.solvers.inverse().precond (LiteralOptions.OptsDiffusionPrecond, default=None) – Name of the preconditioner, see
ImplicitDiffusion. Requiressolver="pcg"."StiffnessPreconditioner"approximates the operator withdiffusion_mat,stab_matand the average;"MultiGrid"is the geometric multigrid V-cycle, with the average re-built on every coarse level.precond_params (dict, default=None) – Keyword arguments passed to the constructor of the mass-matrix or stiffness preconditioner, see
ImplicitDiffusion.multigrid (MultiGridOptions, default=None) – Options of the multigrid preconditioner (if
precond="MultiGrid"). IfNone, defaults toMultiGridOptions().solver_params (SolverParameters, default=None) – Iterative-solver controls (for example
tol,maxiter,verbose,info,recycle). IfNone, defaults toSolverParameters().filter_params (dict[PICVariable | SPHVariable, FilterParameters], default=None) – If not None, specifies a filter to the accumulation of a specific variable.
Notes
GyrokineticPoissonSolvereusesImplicitDiffusioninternals by enforcingsigma_1 = 1 / (Z * epsilon**2),sigma_2 = 0.0,sigma_3 = 1 / epsilonanddivide_by_dt = Falseinallocate.- property stab_average: str | None#
Directions averaged in the stabilization, see
which_geometry.
- class struphy.propagators.adiabatic_poisson_solve.AdiabaticPoissonSolve(rho: FEECVariable | ParticlesToGrid | Callable | list | None = None, rho_coeffs: float | list | None = None, epsilon: float = 1.0, alpha: float = 1.0, Z: int = 1, diagnostic: FEECVariable | None = None)[source]#
Bases:
ImplicitDiffusionWeak discretization of the Poisson equation for full-orbit ions with adiabatic electrons.
Find \(\phi \in H^1\) such that
\[\frac{1}{Z\varepsilon^2} \int_\Omega \frac{n_0}{T_0}\, \psi\, (\phi - \langle \phi \rangle)\,\textrm d \mathbf x + \frac{1}{\alpha^2} \int_\Omega \nabla \psi \cdot \nabla \phi \,\textrm d \mathbf x = \frac{1}{\varepsilon} \int_\Omega \psi\, (n_i - n_0)\,\textrm d \mathbf x \qquad \forall \ \psi \in H^1\,,\]where \(n_i = \int f \,\textrm d^3 \mathbf v\) is the ion density, \(n_0\) and \(T_0\) are the equilibrium ion density and electron temperature, \(Z\) is the ion charge number, \(\varepsilon = 1/(\hat \Omega_c \hat t)\) and \(\alpha = \hat \omega_p / \hat \Omega_c\). The first term is the linearized Boltzmann response of the electrons. The optional flux-surface average \(\langle \phi \rangle\) (
which_geometry) is the zonal correction ofGyrokineticPoissonSolve: electrons do not respond to the averaged potential. The second term is the vacuum (Debye) term; it is dropped in the quasi-neutral limit \(\alpha \to \infty\) (quasi_neutral=True), where the equation becomes a weighted \(L^2\) projection. Boundary terms from integration by parts are assumed to vanish.With
polarization=True(quasi-neutral only), the polarization density of guiding centers is added as inGyrokineticPoissonSolve,\[\frac{1}{Z\varepsilon^2} \int_\Omega \frac{n_0}{T_0}\, \psi\, (\phi - \langle \phi \rangle)\,\textrm d \mathbf x + \int_\Omega \frac{n_0}{|\mathbf B_0|^2} \nabla_\perp \psi \cdot \nabla_\perp \phi \,\textrm d \mathbf x = \frac{1}{\varepsilon} \int_\Omega \psi\, (n_i - n_0)\,\textrm d \mathbf x\,,\]discretized with the gyrokinetic mass matrix \(\mathbb M^1_\textnormal{gyro}\) in place of \(\alpha^{-2} \mathbb M^1\). This is needed when \(n_i\) is the density of the marker positions and these are guiding-center positions whose density does not contain the polarization density (e.g. classical Pauli particles with the control variate, see
VlasovPoissonAdiabaticPauli).In the quasi-neutral limit the ion-acoustic branch \(\omega = k c_s\) has no Debye cutoff, so an explicit particle push needs \(\Delta t\, c_s k_\textnormal{max} \lesssim 2\), with \(k_\textnormal{max} \approx \pi / h\) for the smallest cell size \(h\); refining the grid requires a smaller time step.
The equation is discretized as (multiplied by \(\alpha^2\) if
quasi_neutral=False)\[\left( \frac{1}{Z\varepsilon^2}\,\mathbb M^0_{\textnormal{ad},T} (\mathbb I - \mathbb A) + \frac{1}{\alpha^2} \mathbb G^\top \mathbb M^1 \mathbb G \right)\, \boldsymbol\phi = \frac{1}{\varepsilon} (\Lambda^0, n_i - n_0 )_{L^2}\,,\]where \(\mathbb M^0_{\textnormal{ad},T}\) is the mass matrix weighted with \(n_0/T_0\) (
stab_mat="M0ad_withT") and \(\mathbb A\) is the weighted average projection (\(\mathbb A = 0\) ifwhich_geometry=None). The particle density is deposited with the marker weights; the background \(n_0\) is subtracted either by the control variate or, for full-f markers, by an explicit background source (seeImplicitDiffusion).- class Options(quasi_neutral: bool = True, polarization: bool = False, which_geometry: Literal['cylindrical', 'toroidal', None] | None = None, stab_mat: Literal['M0', 'M0ad', 'M0ad_withT', 'Id'] = 'M0ad_withT', x0: StencilVector | None = None, solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MultiGrid', 'StiffnessPreconditioner', 'MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] | None = None, precond_params: dict | None = None, multigrid: MultiGridOptions | None = None, solver_params: SolverParameters | None = None, filter_params: dict[PICVariable | SPHVariable, FilterParameters] | None = None, enforce_compatibility: bool = False)[source]#
Bases:
OptionsBaseConfiguration options for
AdiabaticPoissonSolve.- Parameters:
quasi_neutral (bool, default=True) – If
True, drop the vacuum term \(\alpha^{-2}\Delta \phi\) (limit \(\alpha \to \infty\)); the system is then a weighted mass-matrix solve (unlesspolarization=True).polarization (bool, default=False) – If
True, add the polarization term \(-\nabla \cdot (n_0 |\mathbf B_0|^{-2} \nabla_\perp \phi)\) (diffusion matrix"M1gyro"). Requiresquasi_neutral=True. Only for markers at guiding-center positions whose deposited density lacks the polarization density (classical Pauli particles with the control variate); for full-orbit ions or full-f Pauli particles the polarization density is counted twice.which_geometry ({None, "cylindrical", "toroidal"}, default=None) – Zonal correction \(\phi - \langle \phi \rangle\) in the adiabatic electron response: no average (
None), average overeta3("cylindrical") or overeta2andeta3("toroidal"), as inGyrokineticPoissonSolve. Requiresquasi_neutral=Falseorpolarization=True: without a diffusion term the averaged potential is undetermined.stab_mat ({"M0", "M0ad", "M0ad_withT", "Id"}, default="M0ad_withT") – Mass matrix of the adiabatic term (
"M0ad_withT"has the weight \(n_0/T_0\)).x0 (StencilVector, default=None) – Initial guess for the iterative linear solver.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Name of the symmetric iterative solver.
precond (LiteralOptions.OptsDiffusionPrecond, default=None) – Name of the preconditioner, see
ImplicitDiffusion."MultiGrid"and"StiffnessPreconditioner"need a diffusion term (quasi_neutral=Falseorpolarization=True).precond_params (dict, default=None) – Keyword arguments passed to the constructor of the preconditioner, see
ImplicitDiffusion.multigrid (MultiGridOptions, default=None) – Options of the multigrid preconditioner (if
precond="MultiGrid").solver_params (SolverParameters, default=None) – Iterative-solver controls. If
None, defaults toSolverParameters().filter_params (dict[PICVariable | SPHVariable, FilterParameters], default=None) – If not None, specifies a filter to the accumulation of a specific variable.
Notes
AdiabaticPoissonSolvereusesImplicitDiffusionwithsigma_2 = 0,divide_by_dt = False,sigma_1 = 1 / (Z * epsilon**2),sigma_3 = 1 / epsilonanddiffusion_mat=None(quasi-neutral), orsigma_1 = alpha**2 / (Z * epsilon**2),sigma_3 = alpha**2 / epsilonanddiffusion_mat="M1", or, withpolarization=True,sigma_1 = 1 / (Z * epsilon**2),sigma_3 = 1 / epsilonanddiffusion_mat="M1gyro".- property stab_average: str | None#
Directions averaged in the adiabatic term, see
which_geometry.
- property diffusion_mat: str | None#
none in the quasi-neutral limit,
"M1gyro"with the polarization term.- Type:
Diffusion matrix of
ImplicitDiffusion
- class struphy.propagators.curl_curl_solve.CurlCurlSolve(j: FEECVariable | tuple[Callable, Callable, Callable] | tuple[AccumulatorVector, Particles] | list | None = None, j_coeffs: float | list | None = None)[source]#
Bases:
PropagatorWeak discretization of the curl-curl problem.
Find \(\mathbf E \in H(\textnormal{curl})\) such that
\[\int_\Omega \nabla \times \mathbf F \cdot \nabla \times \mathbf E\,\textrm d \mathbf x + \sigma \int_\Omega \mathbf F \cdot \mathbf E\,\textrm d \mathbf x = \sum_i \int_\Omega \mathbf F \cdot \mathbf J _i\,\textrm d \mathbf x \qquad \forall \,\mathbf F \in H(\textnormal{curl})\,,\]where \(\mathbf J _i:\Omega \to \mathbb R^3\) are real-valued and \(\sigma > 0\) is a scalar (the operator is symmetric positive definite). Boundary terms from integration by parts are assumed to vanish. The equation is discretized as
\[\left( \mathbb C^\top \cdot \mathbb M^2 \cdot \mathbb C + \sigma \mathbb M^1 \right)\, \boldsymbol e^{n+1} =\sum_i \boldsymbol j_i\,,\]where \(\mathbb M^1\) and \(\mathbb M^2\) are
WeightedMassOperatorsand \(\boldsymbol j_i\) is the vector of coefficients of the projection of \(\mathbf J_i\) into the discrete space \(V^1_h\subset H(\textnormal{curl})\).- class Variables[source]#
Bases:
object- Variables:
e (FEECVariable) – Vector-valued solution in
"Hcurl"space.
- class Options(sigma: float = 1.0, stab_mat: Literal['M1', 'Id'] = 'M1', diffusion_mat: Literal['M2'] = 'M2', x0: StencilVector | None = None, solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MultiGrid', 'HiptmairXu', 'StiffnessPreconditioner', 'MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] | None = None, precond_params: dict | None = None, multigrid: MultiGridOptions | None = None, solver_params: SolverParameters | None = None)[source]#
Bases:
objectConfiguration options for
CurlCurlSolve.- Parameters:
sigma (float, default=1.0) – Coefficient of the stabilization/mass term on the left-hand side,
curl.T @ diffusion_mat @ curl + sigma * stab_mat. Must be positive for the operator to be positive definite (values below1e-14are set to1e-14).stab_mat ({"M1", "Id"}, default="M1") –
Stabilization operator used in the term weighted by
sigma."M1": standard weighted 1-form mass operator."Id": identity operator.
diffusion_mat ("M2" or WeightedMassOperator, default="M2") – Diffusion metric in the bilinear form
curl.T @ diffusion_mat @ curl. You can pass the name of a pre-built operator inmass_opsor a customWeightedMassOperatorcompatible with the codomain ofcurl.x0 (StencilVector, default=None) – Initial guess for the iterative linear solver.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Name of the symmetric iterative solver passed to
psydac.linalg.solvers.inverse().precond (LiteralOptions.OptsCurlCurlPrecond, default=None) –
Name of the preconditioner (requires
solver="pcg"):None: no preconditioner."MassMatrixPreconditioner": Kronecker approximation of the inverse of the 1-form mass matrixM1(seestruphy.feec.preconditioner)."PolarMassMatrixPreconditioner": 2D x 1D Kronecker approximation of the inverse ofM1on polar splines."StiffnessPreconditioner": Kronecker approximation of the inverse ofcurl.T @ diffusion_mat @ curl + sigma * stab_mat(StiffnessPreconditionerwithderivative="curl",mid=diffusion_matandstab=stab_mat): block Jacobi with fast diagonalization, plus a correction on the gradients (kernel_correction, default True). With polar splines, this is aPolarStiffnessPreconditioner."HiptmairXu": auxiliary space preconditioner of Hiptmair and Xu (HiptmairXuPreconditionerwithmid=diffusion_matandstab=stab_mat): smoother plus corrections on the gradients and on vector-valued \(H^1\) splines."MultiGrid": geometric multigrid V-cycle (MultiGridPreconditioner) with the hybrid smoother of Hiptmair (seemultigrid).
precond_params (dict, default=None) – Keyword arguments passed to the constructor of the preconditioner, e.g.
{"kernel_correction": False}for"StiffnessPreconditioner"or{"smoother": "jacobi"}for"HiptmairXu".multigrid (MultiGridOptions, default=None) – Options of the multigrid preconditioner (if
precond="MultiGrid"). IfNone, defaults toMultiGridOptions(hiptmair=True, smoother_precond="jacobi"): without the Hiptmair smoother, multigrid is not robust for the curl-curl operator.solver_params (SolverParameters, default=None) – Iterative-solver controls (for example
tol,maxiter,verbose,info,recycle). IfNone, defaults toSolverParameters().
- property sources: list[Vector | FEECVariable | AccumulatorVector]#
Right-hand side of the equation (sources).
- property coeffs: list[float]#
Same length as self.sources. Coefficients multiplied with sources before solve (default is 1.0).
- property x0#
feectools.linalg.stencil.StencilVector or struphy.polar.basic.PolarVector. First guess of the iterative solver.
- class struphy.propagators.adiabatic_phi.AdiabaticPhi(phi: StencilVector, *, A_mat: WeightedMassOperator = 'M0', rho: StencilVector | tuple | None = None, sigma_1: float = 1.0, x0: StencilVector | None = None, **params)[source]#
Bases:
PropagatorElectrostatic potential for adiabatic electrons, computed from
\[n_e = n_{e0}\,\exp \left( \frac{e \phi}{k_B T_e} \right) \approx n_{e0} \left( 1 + \frac{e \phi}{k_B T_{e0}} \right)\,,\]where \(n_{e0}\) and \(T_{e0}\) denote electron equilibrium density and temperature, respectively.
This is solved in weak form: find \(\phi \in H^1\) such that
\[\int_\Omega \psi\, \frac{n_{e0}(\mathbf x)}{T_{e0}(\mathbf x)}\phi \,\textrm d \mathbf x = \int_\Omega \psi\, (n_{e}(\mathbf x) - n_{e0}(\mathbf x))\,\textrm d \mathbf x \qquad \forall \ \psi \in H^1\,.\]The equation is discretized as
\[\sigma_1 \mathbb M^0_{n/T} \boldsymbol \phi = (\Lambda^0, n_{e} - n_{e0} )_{L^2}\,,\]where \(M^0_{n/T}\) is a
WeightedMassOperatorand \(\sigma_1\) is a normalization parameter.- Parameters:
phi (StencilVector) – FE coefficients of the solution as a discrete 0-form.
A_mat (WeightedMassOperator) – The matrix to invert.
rho (StencilVector or tuple) – Right-hand side FE coefficients of a 0-form (optional, can be set with a setter later). Can be either a) StencilVector or b) 2-tuple. In case b) the first tuple entry must be
AccumulatorVector, and the second entry must beParticles.sigma_1 (float) – Normalization parameter.
x0 (StencilVector) – Initial guess for the iterative solver (optional, can be set with a setter later).
**params (dict) – Parameters for the iterative solver (see
__init__for details).
- property rho#
Right-hand side FE coefficients of a 0-form. Can be either a) StencilVector or b) 2-tuple. In the latter case, the first tuple entry must be
AccumulatorVector, and the second entry must beParticles.
- property x0#
feectools.linalg.stencil.StencilVector or struphy.polar.basic.PolarVector. First guess of the iterative solver.
Linear MHD solvers#
- class struphy.propagators.shear_alfven_propagator.ShearAlfvenPropagator(rho_field: FEECVariable | None = None)[source]#
Bases:
PropagatorShear-Alfvén step of linear MHD (zero-flow equilibrium) with the magnetic field in \(H(\textnormal{div})\).
Continuous problem. Find \(\tilde{\mathbf{U}} \in V_U\) with \(V_U \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\) (
Options.u_space) and \(\tilde{\mathbf{B}} \in H(\textnormal{div})\) such that\[\begin{split}\int_{\Omega} \rho_0\frac{\partial \tilde{\mathbf{U}}}{\partial t} \cdot \mathbf V\,\textnormal d \mathbf x &= \int_{\Omega} \tilde{\mathbf{B}} \cdot \nabla \times (\mathbf{B}_0 \times \mathbf V) \,\textnormal d \mathbf x \qquad \forall \,\mathbf V \in V_U\,, \\[2mm] \frac{\partial \tilde{\mathbf{B}}}{\partial t} &= \nabla\times(\tilde{\mathbf{U}} \times \mathbf{B}_0)\,.\end{split}\]The momentum equation is weak (integration by parts of the Lorentz force), the induction equation holds strongly in \(H(\textnormal{div})\). Essential boundary conditions impose \(\tilde{\mathbf B} \cdot \mathbf n = 0\) (and the corresponding condition on \(\tilde{\mathbf U}\)) strongly.
Semi-discrete problem. With the FE coefficients \(\mathbf u\) of \(\tilde{\mathbf U}_h \in V^\alpha_h\) (\(\alpha = 1, 2, v\) for
u_space"Hcurl","Hdiv","H1vec") and \(\mathbf b\) of \(\tilde{\mathbf B}_h \in V^2_h\),\[\mathbb M^\alpha_{\rho_0} \dot{\mathbf u} = (\mathbb T^\alpha)^\top \mathbb C^\top \mathbb M^2 \mathbf b\,, \qquad \dot{\mathbf b} = - \mathbb C \mathbb T^\alpha \mathbf u\,,\]with the discrete curl \(\mathbb C\) (
curl), the mass matrix \(\mathbb M^2\) (M2), the density-weighted mass matrix \(\mathbb M^\alpha_{\rho_0}\) (M1n,M2n,Mvn) and the basis projection operator \(\mathbb T^\alpha \approx \hat \Pi^1[\mathbf B_0 \times \,\cdot\,]\) (T1,T2,Tv). The induction equation holds exactly in \(V^2_h\), so the discrete divergence of \(\mathbf b\) stays constant, and the system conserves the perturbed energy \(\frac 12 \mathbf u^\top \mathbb M^\alpha_{\rho_0} \mathbf u + \frac 12 \mathbf b^\top \mathbb M^2 \mathbf b\).Time discretization. Selected by
Options.algo:algo="implicit"(default): Crank-Nicolson (implicit mid-point), which conserves the perturbed energy exactly (up to the solver tolerances). The magnetic field is eliminated withSchurSolver(\(x = \mathbf u\), \(y = \mathbf b\)), with the Schur complement\[S = \mathbb M^\alpha_{\rho_0} + \frac{\Delta t^2}{4} (\mathbb T^\alpha)^\top \mathbb C^\top \mathbb M^2 \mathbb C \mathbb T^\alpha\,,\]solved by
Options.solverwith the preconditionerOptions.precond(by default the KroneckerShearAlfvenSchurPreconditioner; also multigrid or mass matrix preconditioners, seeOptions); then \(\mathbf b^{n+1} = \mathbf b^n - \frac{\Delta t}{2} \mathbb C \mathbb T^\alpha (\mathbf u^{n+1} + \mathbf u^n)\).algo="explicit": explicit Runge-Kutta method of the semi-discrete system, with the tableauOptions.butcher(ButcherTableau); each stage inverts \(\mathbb M^\alpha_{\rho_0}\) withOptions.solver.
Perturbative models (constructor argument
rho_field, the density perturbation \(\tilde \rho\), only withu_space="Hdiv"): the inertia is that of the total density, \(\mathbb M^2_{\rho_0} \to \mathbb M^2_\rho\), \(\rho = \rho_0 + \tilde \rho^n\) (VelocityMass, assembled at \(t^n\); the density does not change in this step, so the energy is still conserved exactly). The preconditioners are still built from \(\mathbb M^2_{\rho_0}\).- class Variables[source]#
Bases:
objectContainer for variables advanced by
ShearAlfvenPropagator.- Variables:
u (FEECVariable) – Velocity variable in one of
"Hcurl","Hdiv", or"H1vec".b (FEECVariable) – Magnetic-field variable in
"Hdiv"space.
- class Options(u_space: Literal['Hcurl', 'Hdiv', 'H1vec'] = 'Hdiv', algo: Literal['implicit', 'explicit'] = 'implicit', solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', 'MultiGrid', 'StiffnessPreconditioner', None] = 'StiffnessPreconditioner', exb_correction: bool = True, multigrid: MultiGridOptions | None = None, solver_params: SolverParameters | None = None, butcher: ButcherTableau | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
ShearAlfvenPropagator.- Parameters:
u_space (LiteralOptions.OptsVecSpace, default="Hdiv") – FEEC space used for the velocity variable.
algo ({"implicit", "explicit"}, default="implicit") – Time stepping algorithm.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Symmetric iterative solver used by implicit or explicit operators.
precond (LiteralOptions.OptsShearAlfvenPrecond, default="StiffnessPreconditioner") – Preconditioner of the linear solve: of the mass-matrix block (
"MassMatrixPreconditioner","PolarMassMatrixPreconditioner"), or of the whole Schur operator \(\mathbb M^2_{\rho_0} + \frac{\Delta t^2}{4} \mathbb T^\top \mathbb C^\top \mathbb M^2 \mathbb C \mathbb T\): a geometric multigrid V-cycle ("MultiGrid",MultiGridPreconditioner) or the Kronecker (fast diagonalization) preconditioner ("StiffnessPreconditioner", the default,ShearAlfvenSchurPreconditioner: block Jacobi in the velocity components with the auxiliary space correction of the \(\mathbf E \times \mathbf B\) flows ifexb_correction, see there). Both need the implicit algorithm withsolver="pcg"or"pcg_sr". The Kronecker preconditioner also needsu_space="Hdiv", tensor-product splines (no polar splines) and the global commuting projectors; otherwise"StiffnessPreconditioner"falls back (with a warning) to"MassMatrixPreconditioner"("PolarMassMatrixPreconditioner"with polar splines). The Schur operator depends on the time step, so these preconditioners are built in the setup (prepare_time_step()) and updated if the time step changes. The multigrid preconditioner is experimental: on the TAE example (HollowTorus, 8x32x4) it needs more PCG iterations than the mass matrix preconditioner and is much slower (the smoother does not treat the large kernel of the curl term on 2-forms). The Kronecker preconditioner (withexb_correction) needs about 11 instead of 31 PCG iterations per step there (dt = 0.1), with larger gains for larger time steps.exb_correction (bool, default=True) – For
precond="StiffnessPreconditioner": add the auxiliary space correction \(\mathbb N P_\phi \mathbb N^\top\) (AuxiliarySpacePreconditioner) of the \(\mathbf E \times \mathbf B\) flows \(\mathbf B_0 \times \nabla \phi\), \(\mathbb N = \hat \Pi^2[\mathbf B_0 \times \,\cdot\,] \, \mathbb G\) with \(\phi \in V^0_h\). The block Jacobi preconditioner in the velocity components does not reduce the incompressible shear Alfven modes with small parallel wave number (their velocity components cancel in the compression), which this correction resolves; \(P_\phi\) is the Kronecker (fast diagonalization) preconditioner of \(\mathbb N^\top S \mathbb N\) on \(V^0_h\).multigrid (MultiGridOptions, default=None) – Options of the multigrid preconditioner (if
precond="MultiGrid"); defaults toMultiGridOptions().solver_params (SolverParameters, default=None) – Solver controls; defaults to
SolverParameters().butcher (ButcherTableau, default=None) – Explicit Runge-Kutta tableau when
algo="explicit".
- class struphy.propagators.shear_alfven_b1.ShearAlfvenB1[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\mathbf U \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\) and \(\mathbf B \in H(\textnormal{curl})\) such that
\[\begin{split}&\int_\Omega \rho_0\frac{\partial \tilde{\mathbf{U}}}{\partial t} \cdot \mathbf V\,\textnormal d \mathbf x =\int_\Omega \tilde{\mathbf B } \cdot \nabla \times (\mathbf{B}_0 \times \mathbf V) \,\textnormal d \mathbf x \qquad \forall \,\mathbf V \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\,, \\[2mm] &\int_\Omega \frac{\partial \tilde{\mathbf{B}}}{\partial t} \cdot \mathbf C\,\textnormal d \mathbf x - \int_\Omega \mathbf C \cdot\nabla\times \left( \tilde{\mathbf{U}} \times \mathbf{B}_0 \right) \textrm d \mathbf x = 0 \qquad \forall \, \mathbf C \in H(\textrm{curl})\,.\end{split}\]Time discretization: Crank-Nicolson (implicit mid-point). System size reduction via
SchurSolver:\[\begin{split}\begin{bmatrix} \mathbf u^{n+1} - \mathbf u^n \\ \mathbf b^{n+1} - \mathbf b^n \end{bmatrix} = \frac{\Delta t}{2} \begin{bmatrix} 0 & (\mathbb M^\rho_2)^{-1} \mathcal {T^2}^\top \mathbb C \mathbb M_1^{-1}\\ - \mathbb M_1^{-1} \mathbb C^\top \mathcal {T^2} (\mathbb M^\rho_2)^{-1} & 0 \end{bmatrix} \begin{bmatrix} {\mathbb M^\rho_2}(\mathbf u^{n+1} + \mathbf u^n) \\ \mathbb M_1(\mathbf b^{n+1} + \mathbf b^n) \end{bmatrix} ,\end{split}\]where \(\mathbb M^\rho_2\) is a weighted mass matrix in 2-space, the weight being \(\rho_0\), the MHD equilibirum density.
- class Variables[source]#
Bases:
objectContainer for variables advanced by
ShearAlfvenB1.- Variables:
u (FEECVariable) – Velocity variable in
"Hdiv"space.b (FEECVariable) – Magnetic-field variable in
"Hcurl"space.
- class Options(solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, solver_M1: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond_M1: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params_M1: SolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
ShearAlfvenB1.- Parameters:
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Solver used for the Schur-reduced system.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner for
M2nblock.solver_params (SolverParameters, default=None) – Solver controls for the Schur solve.
solver_M1 (LiteralOptions.OptsSymmSolver, default="pcg") – Solver used for inverting
M1.precond_M1 (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner for
M1inversion.solver_params_M1 (SolverParameters, default=None) – Solver controls for
M1inversion.
- class struphy.propagators.shear_alfven_curl_curl_propagator.ShearAlfvenCurlCurlPropagator(projected: bool = False)[source]#
Bases:
PropagatorShear-Alfvén step of linear MHD (zero-flow equilibrium) with the magnetic field in \(H(\textnormal{curl})\).
Continuous problem. Find \(\tilde{\mathbf U} \in H(\textnormal{div})\) and \(\tilde{\mathbf B} \in H(\textnormal{curl})\) such that
\[\begin{split}&\int_\Omega \rho_0\frac{\partial \tilde{\mathbf{U}}}{\partial t} \cdot \mathbf V\,\textnormal d \mathbf x =\int_\Omega \left[(\nabla \times \tilde{\mathbf B}) \times \mathbf B_0\right] \cdot \mathbf V \,\textnormal d \mathbf x \qquad \forall \,\mathbf V \in H(\textnormal{div})\,, \\[2mm] &\int_\Omega \frac{\partial \tilde{\mathbf{B}}}{\partial t} \cdot \mathbf C\,\textnormal d \mathbf x = - \int_\Omega \left[(\nabla \times \mathbf C) \times \mathbf B_0\right] \cdot \tilde{\mathbf{U}} \,\textrm d \mathbf x \qquad \forall \, \mathbf C \in H(\textrm{curl})\,.\end{split}\]The momentum equation is the \(\rho_0\)-weighted \(L^2\) projection of the Lorentz force onto \(H(\textnormal{div})\); the induction equation is weak, its boundary term is dropped, which imposes \(\tilde{\mathbf U} \cdot \mathbf n = 0\) weakly if \(\mathbf B_0 \cdot \mathbf n = 0\), since \(\mathbf n \times (\tilde{\mathbf U} \times \mathbf B_0) = \tilde{\mathbf U} (\mathbf B_0 \cdot \mathbf n) - \mathbf B_0 (\tilde{\mathbf U} \cdot \mathbf n)\) (free boundary conditions of the de Rham sequence; essential ones set the tangential components of \(\tilde{\mathbf B}\) to zero on the boundary).
Semi-discrete problem. With the FE coefficients \(\mathbf u \in \mathbb R^{N_2}\) of \(\tilde{\mathbf U}_h \in V^2_h\) and \(\mathbf b \in \mathbb R^{N_1}\) of \(\tilde{\mathbf B}_h \in V^1_h\),
\[\mathbb M^2_{\rho_0} \dot{\mathbf u} = - \mathbb M^2_{B_0} \mathbb C \mathbf b\,, \qquad \mathbb M^1 \dot{\mathbf b} = - \mathbb C^\top \mathbb M^2_{B_0} \mathbf u\,,\]with the discrete curl \(\mathbb C\) (
curl), the mass matrix \(\mathbb M^1\) (M1), the mass matrix of 2-forms weighted with the density \(\mathbb M^2_{\rho_0}\) (M2n) and the antisymmetric mass matrix \(\mathbb M^2_{B_0}\) (M2B) of \(\int (\mathbf B_0 \times \mathbf W) \cdot \mathbf V \,\textnormal d \mathbf x\). This is the semi-discrete system ofShearAlfvenB1. It conserves the perturbed energy \(\frac 12 \mathbf u^\top \mathbb M^2_{\rho_0} \mathbf u + \frac 12 \mathbf b^\top \mathbb M^1 \mathbf b\).Time discretization. Selected by
Options.algoand the constructor argumentprojected:algo="implicit",projected=True: Crank-Nicolson (implicit mid-point) of the semi-discrete system, which conserves the perturbed energy exactly (up to the solver tolerances). The velocity is eliminated withSchurSolver(\(x = \mathbf b\), \(y = \mathbf u\)), with the Schur complement\[S = \mathbb M^1 + \frac{\Delta t^2}{4} \mathbb C^\top (\mathbb M^2_{B_0})^\top (\mathbb M^2_{\rho_0})^{-1} \mathbb M^2_{B_0} \mathbb C\,,\]where \((\mathbb M^2_{\rho_0})^{-1}\) is an inner iterative solve (
solver_M2n,precond_M2n). The curl-curl operator with \(\mathbb M^2_\perp\) below is not spectrally equivalent to \(S\) (it overestimates \(S\) on the fields whose Lorentz force is removed by the projection), soprecond="HiptmairXu"does not pay off here;precond="auto"(default) selects the mass matrix preconditioner.algo="implicit",projected=False(default): Crank-Nicolson without the projection in the curl-curl term: the velocity update \(\tilde{\mathbf U}^{n+1} = \tilde{\mathbf U}^n + \frac{\Delta t}{2 \rho_0} (\nabla \times (\tilde{\mathbf B}^{n+1} + \tilde{\mathbf B}^n)) \times \mathbf B_0\) is inserted into the weak induction equation before the projection, which gives the curl-curl system\[\left(\mathbb M^1 + \frac{\Delta t^2}{4} \mathbb C^\top \mathbb M^2_\perp \mathbb C\right) \mathbf b^{n+1} = \left(\mathbb M^1 - \frac{\Delta t^2}{4} \mathbb C^\top \mathbb M^2_\perp \mathbb C\right) \mathbf b^n - \Delta t \, \mathbb C^\top \mathbb M^2_{B_0} \mathbf u^n\,,\]with the mass matrix \(\mathbb M^2_\perp\) (
M2gyro) of \(\int \frac{1}{\rho_0} (\mathbf B_0 \times \mathbf W) \cdot (\mathbf B_0 \times \mathbf V) \,\textnormal d \mathbf x\), whose weight \(\frac{|\mathbf B_0|^2}{\rho_0} (I - \mathbf b_0 \mathbf b_0^\top)\), \(\mathbf b_0 = \mathbf B_0 / |\mathbf B_0|\), acts perpendicular to \(\mathbf B_0\) (here \(\mathbf W = \nabla \times \tilde{\mathbf B}\), \(\mathbf V = \nabla \times \mathbf C\)). The velocity is then updated with the projection, \(\mathbf u^{n+1} = \mathbf u^n - \frac{\Delta t}{2} (\mathbb M^2_{\rho_0})^{-1} \mathbb M^2_{B_0} \mathbb C (\mathbf b^{n+1} + \mathbf b^n)\). The perturbed energy does not increase, but is not exactly conserved (the projection error of the Lorentz force enters): numerical damping that vanishes with the resolution along \(\mathbf B_0\). The system is solved bySchurSolverwith \(x = \mathbf b\), \(y = \mathbf u\). Withprecond="HiptmairXu"(selected by the default"auto"), it is preconditioned byHiptmairXuPreconditionerfor the curl-curl operator (scaled by \(\sigma = 4/\Delta t^2\); TAE example 8x32x4: 37 instead of 706 PCG iterations per step for \(\Delta t = 2\)), optionally (aux_correction) plus the field-aligned auxiliary space corrections ofAuxiliarySpacePreconditioner: perpendicular perturbations \(\mathbf B_0 \times \nabla \psi\) and parallel perturbations \(\phi \mathbf B_0\) with \(\psi, \phi \in V^0_h\), whose operators are approximated by the KroneckerStiffnessPreconditioner.algo="explicit": explicit Runge-Kutta method of the semi-discrete system, with the tableauOptions.butcher(ButcherTableau);projectedhas no effect.
- Parameters:
projected (bool, default=False) – Whether the curl-curl term of the implicit step contains the projection of the Lorentz force (exactly energy conserving step, see above).
- class Variables[source]#
Bases:
objectContainer for variables advanced by
ShearAlfvenCurlCurlPropagator.- Variables:
u (FEECVariable) – Velocity variable in
"Hdiv"space.b (FEECVariable) – Magnetic-field variable in
"Hcurl"space.
- class Options(algo: Literal['implicit', 'explicit'] = 'implicit', solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['auto', 'HiptmairXu', 'MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'auto', aux_correction: bool = False, hx_aux_weight: float = 1.0, solver_params: SolverParameters | None = None, solver_M2n: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond_M2n: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params_M2n: SolverParameters | None = None, butcher: ButcherTableau | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
ShearAlfvenCurlCurlPropagator.- Parameters:
algo ({"implicit", "explicit"}, default="implicit") – Time stepping algorithm.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Solver of the Schur system in
b(implicit) or ofM1(explicit).precond (LiteralOptions.OptsShearAlfvenCurlCurlPrecond, default="auto") – Preconditioner of the Schur system: Hiptmair-Xu for the curl-curl operator with
M2gyroin the middle ("HiptmairXu", implicit algorithm withsolver="pcg"only, no polar splines; it depends on the time step, so it is built in the setup (prepare_time_step()) and rebuilt when the time step changes), or a preconditioner of the mass matrixM1("MassMatrixPreconditioner","PolarMassMatrixPreconditioner"). Withalgo="explicit","HiptmairXu"is replaced by"MassMatrixPreconditioner"(forM1)."auto"selects"HiptmairXu"for the implicit algorithm withprojected=False,solver="pcg"or"pcg_sr"and tensor-product splines, and the mass matrix preconditioner otherwise ("PolarMassMatrixPreconditioner"with polar splines).aux_correction (bool, default=False) – With Hiptmair-Xu (
precond="HiptmairXu", or"auto"resolving to it): add the field-aligned auxiliary space corrections of the perturbations \(\mathbf B_0 \times \nabla \psi\) and \(\phi \mathbf B_0\) (\(\psi, \phi \in V^0_h\)), which the Hiptmair-Xu preconditioner of the anisotropic curl-curl operator reduces only slowly. They lower the condition number (TAE geometry, 4x12x3, dt = 1: 34 to 19) but hardly the PCG iterations (tilted slab 16x16x8, dt = 2: 148 to 125; TAE example 8x32x4: unchanged), at about twice the cost per iteration.hx_aux_weight (float, default=1.0) – Factor of the vector auxiliary space correction of the Hiptmair-Xu preconditioner (
aux_weightofHiptmairXuPreconditioner, 0 switches it off;M2gyrovanishes along the background field, so the isotropic vector Laplacian of the correction does not match the curl-curl term).solver_params (SolverParameters, default=None) – Solver controls of the Schur system; defaults to
SolverParameters().solver_M2n (LiteralOptions.OptsSymmSolver, default="pcg") – Solver of the inner
M2ninversion.precond_M2n (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner of the inner
M2ninversion.solver_params_M2n (SolverParameters, default=None) – Solver controls of the inner
M2ninversion; defaults toSolverParameters(tol=1e-14)(the outer Krylov solver needs an accurate inner inverse).butcher (ButcherTableau, default=None) – Explicit Runge-Kutta tableau when
algo="explicit".
- prepare_time_step(dt: float)[source]#
Hiptmair-Xu preconditioner of S ~ dt^2/4 (C^T M2gyro C + 4/dt^2 M1) for the time step
dt: built in the setup, rebuilt when dt changes (sigma = 4/dt^2 with the unscaled M2gyro in the middle, as the vector auxiliary space correction of Hiptmair-Xu assumes a unit-scale curl-curl term).
- class struphy.propagators.magnetosonic.Magnetosonic(b_field: FEECVariable | None = None)[source]#
Bases:
PropagatorMagnetosonic step of linear MHD (zero-flow equilibrium): density, velocity and pressure perturbations at a fixed magnetic field perturbation.
Continuous problem. Find \(\tilde{\rho} \in L^2\), \(\tilde{\mathbf U} \in V_U\) with \(V_U \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\) (
Options.u_space) and \(\tilde p \in L^2\) such that\[\begin{split}\frac{\partial \tilde{\rho}}{\partial t} + \nabla\cdot(\rho_0 \tilde{\mathbf{U}}) &= 0\,, \\[2mm] \int_{\Omega} \rho_0\frac{\partial \tilde{\mathbf{U}}}{\partial t} \cdot \mathbf V\,\textrm{d}\mathbf x - \int_{\Omega} \tilde p\,\nabla\cdot\mathbf V\,\textrm{d}\mathbf x &= \int_{\Omega} (\nabla\times\mathbf{B}_0)\times\tilde{\mathbf{B}}\cdot\mathbf V\,\textrm{d}\mathbf x \qquad \forall\,\mathbf V \in V_U\,, \\[2mm] \frac{\partial \tilde p}{\partial t} + \nabla\cdot(p_0 \tilde{\mathbf{U}}) + \frac{2}{3}\,p_0\nabla\cdot\tilde{\mathbf{U}} &= 0\,.\end{split}\]The continuity and pressure equations hold strongly in \(L^2\), the momentum equation is weak (integration by parts of the pressure gradient; with free boundary conditions this imposes \(\tilde p = 0\) weakly). For \(V_U = H(\textnormal{curl})\) or \((H^1)^3\), the divergence acts on the projection of the velocity (and of \(\mathbf V\)) onto \(H(\textnormal{div})\) (operator \(\mathbb U^\alpha\) below). The magnetic field perturbation \(\tilde{\mathbf B}\) is not updated (it enters only the Lorentz force); it is the constructor argument
b_field, either a 2-form (\(H(\textnormal{div})\), default) or a 1-form (\(H(\textnormal{curl})\), only for \(V_U = H(\textnormal{div})\)).Semi-discrete problem. With the FE coefficients \(\mathbf n\), \(\mathbf p\) of \(\tilde \rho_h, \tilde p_h \in V^3_h\), \(\mathbf u\) of \(\tilde{\mathbf U}_h \in V^\alpha_h\) (\(\alpha = 1, 2, v\) for
u_space"Hcurl","Hdiv","H1vec") and \(\mathbf b\) of \(\tilde{\mathbf B}_h \in V^\beta_h\) (\(\beta = 2\) or \(1\)),\[\dot{\mathbf n} = - \mathbb D \mathbb Q^\alpha \mathbf u\,, \qquad \mathbb M^\alpha_{\rho_0} \dot{\mathbf u} = (\mathbb D \mathbb U^\alpha)^\top \mathbb M^3 \mathbf p + \mathbb M^{\alpha\beta}_{J_0} \mathbf b\,, \qquad \dot{\mathbf p} = - \left(\mathbb D \mathbb S^\alpha + \frac 23 \mathbb K^3 \mathbb D \mathbb U^\alpha\right) \mathbf u\,,\]with the discrete divergence \(\mathbb D\) (
div), the mass matrix \(\mathbb M^3\) (M3), the density-weighted mass matrix \(\mathbb M^\alpha_{\rho_0}\) (M1n,M2n,Mvn), the current mass matrix \(\mathbb M^{\alpha\beta}_{J_0}\) of \(\int (\mathbf J_0 \times \mathbf W) \cdot \mathbf V \,\textnormal d \mathbf x\), \(\mathbf J_0 = \nabla \times \mathbf B_0\) (M1J,M2J,MvJ;M21Jfor the 1-form \(\tilde{\mathbf B}\)), and the basis projection operators (BasisProjectionOperators) \(\mathbb Q^\alpha \approx \hat \Pi^2[\rho_0 \,\cdot\,]\) (Q1,Q2,Qv), \(\mathbb S^\alpha \approx \hat \Pi^2[p_0 \,\cdot\,]\) (S1,S2,Sv), \(\mathbb K^3 \approx \hat \Pi^3[p_0 \,\cdot\,]\) (K3) and \(\mathbb U^\alpha \approx \hat \Pi^2[\,\cdot\,]\) (U1,Uv; the identity for \(\alpha = 2\)).Time discretization. Crank-Nicolson (implicit mid-point), the only option. The pressure is eliminated with
SchurSolver(\(x = \mathbf u\), \(y = \mathbf p\)), with the Schur complement\[S = \mathbb M^\alpha_{\rho_0} + \frac{\Delta t^2}{4} (\mathbb D \mathbb U^\alpha)^\top \mathbb M^3 \left(\mathbb D \mathbb S^\alpha + \frac 23 \mathbb K^3 \mathbb D \mathbb U^\alpha\right)\,,\]solved by
Options.solver(non-symmetric, default"pbicgstab") with the mass matrix preconditionerOptions.precondof \(\mathbb M^\alpha_{\rho_0}\); then \(\mathbf p^{n+1}\) and \(\mathbf n^{n+1}\) follow from the mid-point velocity \((\mathbf u^{n+1} + \mathbf u^n)/2\).- class Variables[source]#
Bases:
objectContainer for variables advanced by
Magnetosonic.- Variables:
n (FEECVariable) – Density perturbation variable in
"L2"space.u (FEECVariable) – Velocity perturbation variable in one of
"Hcurl","Hdiv", or"H1vec".p (FEECVariable) – Pressure perturbation variable in
"L2"space.
- class Options(u_space: Literal['Hcurl', 'Hdiv', 'H1vec'] = 'Hdiv', solver: Literal['pbicgstab', 'bicgstab', 'gmres'] = 'pbicgstab', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
Magnetosonic.- Parameters:
u_space (LiteralOptions.OptsVecSpace, default="Hdiv") – FEEC space used for the velocity variable
u.solver (LiteralOptions.OptsGenSolver, default="pbicgstab") – Iterative solver used by
SchurSolver.precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner applied to the mass matrix block associated with
u_space.solver_params (SolverParameters, default=None) – Iterative-solver controls. If
None, defaults toSolverParameters().
- class struphy.propagators.sound_wave_linear.SoundWaveLinear[source]#
Bases:
PropagatorEnergy conserving sound wave step of linear MHD (zero-flow equilibrium): velocity and pressure perturbations, with the density perturbation carried along.
Continuous problem. Find \(\tilde{\mathbf U} \in H(\textnormal{div})\), \(\tilde p \in L^2\) and \(\tilde \rho \in L^2\) such that
\[\begin{split}\int_\Omega \rho_0 \frac{\partial \tilde{\mathbf U}}{\partial t} \cdot \mathbf V \,\textnormal d \mathbf x - \int_\Omega \tilde p \, \nabla \cdot \mathbf V \,\textnormal d \mathbf x &= 0 \qquad \forall \, \mathbf V \in H(\textnormal{div})\,, \\[2mm] \int_\Omega \frac{1}{p_0} \frac{\partial \tilde p}{\partial t} \, q \,\textnormal d \mathbf x + \gamma \int_\Omega \nabla \cdot \tilde{\mathbf U} \, q \,\textnormal d \mathbf x &= 0 \qquad \forall \, q \in L^2\,, \\[2mm] \frac{\partial \tilde{\rho}}{\partial t} + \nabla\cdot(\rho_0 \tilde{\mathbf{U}}) &= 0\,,\end{split}\]with \(\gamma = 5/3\). The momentum equation is weak (integration by parts of the pressure gradient; with free boundary conditions this imposes \(\tilde p = 0\) weakly), the pressure equation is the \(1/p_0\)-weighted \(L^2\) projection of \(\partial_t \tilde p = - \gamma p_0 \nabla \cdot \tilde{\mathbf U}\) and the continuity equation holds strongly. The first two equations conserve the energy
\[\frac 12 \int_\Omega \rho_0 |\tilde{\mathbf U}|^2 \,\textnormal d \mathbf x + \frac{1}{2\gamma} \int_\Omega \frac{\tilde p^2}{p_0} \,\textnormal d \mathbf x\,.\]The remaining term \(- \tilde{\mathbf U} \cdot \nabla p_0\) of the linear pressure equation is the step
BackgroundPressureGradient.Semi-discrete problem. With the FE coefficients \(\mathbf u\) of \(\tilde{\mathbf U}_h \in V^2_h\) and \(\mathbf p\), \(\mathbf n\) of \(\tilde p_h, \tilde \rho_h \in V^3_h\),
\[\mathbb M^2_{\rho_0} \dot{\mathbf u} = \mathbb D^\top \mathbb M^3 \mathbf p\,, \qquad \mathbb M^3_{1/p_0} \dot{\mathbf p} = - \gamma \, \mathbb M^3 \mathbb D \mathbf u\,, \qquad \dot{\mathbf n} = - \mathbb D \mathbb Q^2 \mathbf u\,,\]with the discrete divergence \(\mathbb D\) (
div), the mass matrix \(\mathbb M^3\) (M3), the weighted mass matrices \(\mathbb M^2_{\rho_0}\) (M2n) and \(\mathbb M^3_{1/p_0}\) (M3p_inv), and the basis projection operator \(\mathbb Q^2 \approx \hat \Pi^2[\rho_0 \,\cdot\,]\) (Q2). It conserves the discrete energy \(\frac 12 \mathbf u^\top \mathbb M^2_{\rho_0} \mathbf u + \frac{1}{2\gamma} \mathbf p^\top \mathbb M^3_{1/p_0} \mathbf p\).Time discretization. Crank-Nicolson (implicit mid-point), the only option, which conserves the discrete energy exactly (up to the solver tolerances). The pressure is eliminated with
SchurSolver(\(x = \mathbf u\), \(y = \mathbf p\)), with the symmetric positive definite Schur complement\[S = \mathbb M^2_{\rho_0} + \frac{\gamma \Delta t^2}{4} \mathbb D^\top \mathbb M^3 (\mathbb M^3_{1/p_0})^{-1} \mathbb M^3 \mathbb D\,,\]solved by
Options.solverwith the mass matrix preconditionerOptions.precondof \(\mathbb M^2_{\rho_0}\), where \((\mathbb M^3_{1/p_0})^{-1}\) is an inner iterative solve (Options.solver_M3,Options.precond_M3). Then\[\mathbf p^{n+1} = \mathbf p^n - \frac{\gamma \Delta t}{2} (\mathbb M^3_{1/p_0})^{-1} \mathbb M^3 \mathbb D (\mathbf u^{n+1} + \mathbf u^n)\,, \qquad \mathbf n^{n+1} = \mathbf n^n - \frac{\Delta t}{2} \mathbb D \mathbb Q^2 (\mathbf u^{n+1} + \mathbf u^n)\,.\]- class Variables[source]#
Bases:
objectContainer for variables advanced by
SoundWaveLinear.- Variables:
n (FEECVariable) – Density perturbation variable in
"L2"space.u (FEECVariable) – Velocity perturbation variable in
"Hdiv"space.p (FEECVariable) – Pressure perturbation variable in
"L2"space.
- class Options(solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, solver_M3: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond_M3: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params_M3: SolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
SoundWaveLinear.- Parameters:
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Iterative solver of the (symmetric) Schur complement.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner of the Schur complement, built from \(\mathbb M^2_{\rho_0}\) (
M2n).solver_params (SolverParameters, default=None) – Controls of the Schur complement solve; defaults to
SolverParameters().solver_M3 (LiteralOptions.OptsSymmSolver, default="pcg") – Solver of the inner
M3p_invinversion.precond_M3 (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner of the inner
M3p_invinversion.solver_params_M3 (SolverParameters, default=None) – Controls of the inner
M3p_invinversion; defaults toSolverParameters(tol=1e-14)(the outer Krylov solver and the energy conservation need an accurate inner inverse).
- property gamma: float#
Adiabatic index.
- class struphy.propagators.background_pressure_gradient.BackgroundPressureGradient(u_field: FEECVariable | None = None)[source]#
Bases:
PropagatorAdvection of the background pressure by the velocity perturbation in linear MHD (zero-flow equilibrium): the part of the linear pressure equation that is not in the energy conserving
SoundWaveLinearstep.Continuous problem. Find \(\tilde p \in L^2\) such that
\[\frac{\partial \tilde p}{\partial t} + \tilde{\mathbf U} \cdot \nabla p_0 = 0\,,\]in \(L^2\), at a fixed velocity perturbation \(\tilde{\mathbf U} \in H(\textnormal{div})\) (the constructor argument
u_field) and with the equilibrium pressure \(p_0\). Together with the pressure equation \(\partial_t \tilde p = - \gamma p_0 \nabla \cdot \tilde{\mathbf U}\) ofSoundWaveLinear, this gives the full linear pressure equation \(\partial_t \tilde p + \nabla \cdot (p_0 \tilde{\mathbf U}) + (\gamma - 1) p_0 \nabla \cdot \tilde{\mathbf U} = 0\).Semi-discrete problem. With the FE coefficients \(\mathbf p\) of \(\tilde p_h \in V^3_h\) and \(\mathbf u\) of \(\tilde{\mathbf U}_h \in V^2_h\),
\[\dot{\mathbf p} = - \mathbb G \mathbf u\,, \qquad \mathbb G = \mathbb D \mathbb S^2 - \mathbb K^3 \mathbb D\,,\]with the discrete divergence \(\mathbb D\) (
div) and the basis projection operators (BasisProjectionOperators) \(\mathbb S^2 \approx \hat \Pi^2[p_0 \,\cdot\,]\) (S2) and \(\mathbb K^3 \approx \hat \Pi^3[p_0 \,\cdot\,]\) (K3). By the commuting property \(\mathbb D \hat \Pi^2 = \hat \Pi^3 \nabla \cdot\) of the projectors and the product rule \(\nabla \cdot (p_0 \mathbf U) = \nabla p_0 \cdot \mathbf U + p_0 \nabla \cdot \mathbf U\),\[\mathbb G \mathbf u \approx \hat \Pi^3 \left[ \nabla \cdot (p_0 \tilde{\mathbf U}_h) - p_0 \nabla \cdot \tilde{\mathbf U}_h \right] = \hat \Pi^3 \left[ \tilde{\mathbf U}_h \cdot \nabla p_0 \right]\,,\]up to the quadrature in the projectors (exact for exact integration), so no gradient of \(p_0\) is needed. This is the same operator splitting as in
Magnetosonic, whose pressure operator \(\mathbb D \mathbb S^2 + \frac 23 \mathbb K^3 \mathbb D\) equals \(\mathbb G + \gamma \mathbb K^3 \mathbb D\).Time discretization. Exact in time, the only option, since the right-hand side does not change during the step:
\[\mathbf p^{n+1} = \mathbf p^n - \Delta t \, \mathbb G \mathbf u\,.\]- class Variables[source]#
Bases:
objectContainer for variables advanced by
BackgroundPressureGradient.- Variables:
p (FEECVariable) – Pressure perturbation variable in
"L2"space.
- class Options[source]#
Bases:
OptionsBaseConfiguration options for
BackgroundPressureGradient(none at the moment).
- class struphy.propagators.sound_wave_linear_with_q.SoundWaveLinearWithQ(rho_field: FEECVariable | None = None)[source]#
Bases:
PropagatorEnergy conserving sound wave step of the linear models with the variable \(q = \sqrt p\) (zero-flow equilibrium): velocity perturbation and perturbation of \(q\), with the density perturbation carried along.
Continuous problem. Find \(\tilde{\mathbf U} \in H(\textnormal{div})\), \(\tilde q \in L^2\) and \(\tilde \rho \in L^2\) such that
\[\begin{split}\int_\Omega \rho_0 \frac{\partial \tilde{\mathbf U}}{\partial t} \cdot \mathbf V \,\textnormal d \mathbf x - 2 \int_\Omega q_0 \tilde q \, \nabla \cdot \mathbf V \,\textnormal d \mathbf x &= 0 \qquad \forall \, \mathbf V \in H(\textnormal{div})\,, \\[2mm] \int_\Omega \frac{\partial \tilde q}{\partial t} \, r \,\textnormal d \mathbf x + \frac{\gamma}{2} \int_\Omega q_0 \nabla \cdot \tilde{\mathbf U} \, r \,\textnormal d \mathbf x &= 0 \qquad \forall \, r \in L^2\,, \\[2mm] \frac{\partial \tilde{\rho}}{\partial t} + \nabla\cdot(\rho_0 \tilde{\mathbf{U}}) &= 0\,,\end{split}\]with \(q_0 = \sqrt{p_0}\) and \(\gamma = 5/3\). The momentum equation is the weak form of \(\rho_0 \partial_t \tilde{\mathbf U} = - \nabla \tilde p\) with the linearized pressure perturbation \(\tilde p = 2 q_0 \tilde q\) (integration by parts; with free boundary conditions this imposes \(\tilde q = 0\) weakly), the second equation is the \(L^2\) projection of \(\partial_t \tilde q = - \frac{\gamma}{2} q_0 \nabla \cdot \tilde{\mathbf U}\) and the continuity equation holds strongly. The first two equations conserve the energy
\[\frac 12 \int_\Omega \rho_0 |\tilde{\mathbf U}|^2 \,\textnormal d \mathbf x + \frac{2}{\gamma} \int_\Omega \tilde q^2 \,\textnormal d \mathbf x\,,\]which is the energy \(\frac 12 \int \rho_0 |\tilde{\mathbf U}|^2 + \frac{1}{2\gamma} \int \tilde p^2 / p_0\) of linear MHD written with \(\tilde q\). The remaining term \(- \tilde{\mathbf U} \cdot \nabla q_0\) of the linear equation of \(\tilde q\) is the step
BackgroundQGradient.Semi-discrete problem. With the FE coefficients \(\mathbf u\) of \(\tilde{\mathbf U}_h \in V^2_h\) and \(\mathbf q\), \(\mathbf n\) of \(\tilde q_h, \tilde \rho_h \in V^3_h\),
\[\mathbb M^2_{\rho_0} \dot{\mathbf u} = 2\, \mathbb D^\top \mathbb M^3_{q_0} \mathbf q\,, \qquad \mathbb M^3 \dot{\mathbf q} = - \frac{\gamma}{2} \, \mathbb M^3_{q_0} \mathbb D \mathbf u\,, \qquad \dot{\mathbf n} = - \mathbb D \mathbb Q^2 \mathbf u\,,\]with the discrete divergence \(\mathbb D\) (
div), the mass matrix \(\mathbb M^3\) (M3), the weighted mass matrices \(\mathbb M^2_{\rho_0}\) (M2n) and \(\mathbb M^3_{q_0}\) (M3q0), and the basis projection operator \(\mathbb Q^2 \approx \hat \Pi^2[\rho_0 \,\cdot\,]\) (Q2). The force is the transpose of the operator of the \(\tilde q\) equation (up to the factor \(4/\gamma\)), so the semi-discrete system conserves the discrete energy \(\frac 12 \mathbf u^\top \mathbb M^2_{\rho_0} \mathbf u + \frac{2}{\gamma} \mathbf q^\top \mathbb M^3 \mathbf q\), a positive definite quadratic form, and its spectrum is purely imaginary.Time discretization. Crank-Nicolson (implicit mid-point), the only option, which conserves the discrete energy exactly (up to the solver tolerances); its eigenvalues are on the unit circle. \(\mathbf q\) is eliminated with
SchurSolver(\(x = \mathbf u\), \(y = \mathbf q\)), with the symmetric positive definite Schur complement\[S = \mathbb M^2_{\rho_0} + \frac{\gamma \Delta t^2}{4} \mathbb D^\top \mathbb M^3_{q_0} (\mathbb M^3)^{-1} \mathbb M^3_{q_0} \mathbb D\,,\]solved by
Options.solverwith the mass matrix preconditionerOptions.precondof \(\mathbb M^2_{\rho_0}\), where \((\mathbb M^3)^{-1}\) is an inner iterative solve (Options.solver_M3,Options.precond_M3). Then\[\mathbf q^{n+1} = \mathbf q^n - \frac{\gamma \Delta t}{4} (\mathbb M^3)^{-1} \mathbb M^3_{q_0} \mathbb D (\mathbf u^{n+1} + \mathbf u^n)\,, \qquad \mathbf n^{n+1} = \mathbf n^n - \frac{\Delta t}{2} \mathbb D \mathbb Q^2 (\mathbf u^{n+1} + \mathbf u^n)\,.\]Perturbative models (constructor argument
rho_field, the density perturbation \(\tilde \rho\)): the inertia is that of the total density, \(\rho_0 \to \rho = \rho_0 + \tilde\rho^n\) in the momentum equation, i.e. \(\mathbb M^2_{\rho_0} \to \mathbb M^2_{\rho}\) (VelocityMass, assembled at \(t^n\), with the coefficientsprojected_equil.n3of \(\rho_0\)), and the density is not advanced (the continuity equation is inMomentumAdvection). The step then conserves \(\frac 12 \mathbf u^\top \mathbb M^2_\rho \mathbf u + \frac{2}{\gamma} \mathbf q^\top \mathbb M^3 \mathbf q\) exactly. The preconditioner is still built from \(\mathbb M^2_{\rho_0}\).- class Variables[source]#
Bases:
objectContainer for variables advanced by
SoundWaveLinearWithQ.- Variables:
n (FEECVariable) – Density perturbation variable in
"L2"space.u (FEECVariable) – Velocity perturbation variable in
"Hdiv"space.q (FEECVariable) – Perturbation of \(q = \sqrt p\) in
"L2"space.
- class Options(solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, solver_M3: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond_M3: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params_M3: SolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
SoundWaveLinearWithQ.- Parameters:
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Iterative solver of the (symmetric) Schur complement.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner of the Schur complement, built from \(\mathbb M^2_{\rho_0}\) (
M2n).solver_params (SolverParameters, default=None) – Controls of the Schur complement solve; defaults to
SolverParameters().solver_M3 (LiteralOptions.OptsSymmSolver, default="pcg") – Solver of the inner
M3inversion.precond_M3 (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner of the inner
M3inversion.solver_params_M3 (SolverParameters, default=None) – Controls of the inner
M3inversion; defaults toSolverParameters(tol=1e-14)(the outer Krylov solver and the energy conservation need an accurate inner inverse).
- property gamma: float#
Adiabatic index.
- class struphy.propagators.background_q_gradient.BackgroundQGradient(u_field: FEECVariable | None = None)[source]#
Bases:
PropagatorAdvection of the background \(q_0 = \sqrt{p_0}\) by the velocity perturbation in the linear models with the variable \(q = \sqrt p\) (zero-flow equilibrium): the part of the linear equation of \(\tilde q\) that is not in the energy conserving
SoundWaveLinearWithQstep.Continuous problem. Find \(\tilde q \in L^2\) such that
\[\frac{\partial \tilde q}{\partial t} + \tilde{\mathbf U} \cdot \nabla q_0 = 0\,,\]in \(L^2\), at a fixed velocity perturbation \(\tilde{\mathbf U} \in H(\textnormal{div})\) (the constructor argument
u_field). Together with the equation \(\partial_t \tilde q = - \frac{\gamma}{2} q_0 \nabla \cdot \tilde{\mathbf U}\) ofSoundWaveLinearWithQ, this gives the full linear equation \(\partial_t \tilde q + \nabla \cdot (q_0 \tilde{\mathbf U}) + (\frac{\gamma}{2} - 1) q_0 \nabla \cdot \tilde{\mathbf U} = 0\), the linearization of \(\partial_t q + \mathbf U \cdot \nabla q + \frac{\gamma}{2} q \nabla \cdot \mathbf U = 0\).Semi-discrete problem. With the FE coefficients \(\mathbf q\) of \(\tilde q_h \in V^3_h\) and \(\mathbf u\) of \(\tilde{\mathbf U}_h \in V^2_h\),
\[\dot{\mathbf q} = - \mathbb G_q \mathbf u\,, \qquad \mathbb G_q = \mathbb D \mathbb S^2_q - \mathbb K^3_q \mathbb D\,,\]with the discrete divergence \(\mathbb D\) (
div) and the basis projection operators (BasisProjectionOperators) \(\mathbb S^2_q \approx \hat \Pi^2[q_0 \,\cdot\,]\) (S2q) and \(\mathbb K^3_q \approx \hat \Pi^3[q_0 \,\cdot\,]\) (K3q). By the commuting property \(\mathbb D \hat \Pi^2 = \hat \Pi^3 \nabla \cdot\) of the projectors and the product rule,\[\mathbb G_q \mathbf u \approx \hat \Pi^3 \left[ \nabla \cdot (q_0 \tilde{\mathbf U}_h) - q_0 \nabla \cdot \tilde{\mathbf U}_h \right] = \hat \Pi^3 \left[ \tilde{\mathbf U}_h \cdot \nabla q_0 \right]\,,\]up to the quadrature in the projectors, so no gradient of \(q_0\) is needed (as in
BackgroundPressureGradient, with \(q_0\) instead of \(p_0\)).Time discretization. Exact in time, the only option, since the right-hand side does not change during the step:
\[\mathbf q^{n+1} = \mathbf q^n - \Delta t \, \mathbb G_q \mathbf u\,.\]- class Variables[source]#
Bases:
objectContainer for variables advanced by
BackgroundQGradient.- Variables:
q (FEECVariable) – Perturbation of \(q = \sqrt p\) in
"L2"space.
- class Options[source]#
Bases:
OptionsBaseConfiguration options for
BackgroundQGradient(none at the moment).
- class struphy.propagators.sound_wave_nonlinear_with_q.SoundWaveNonlinearWithQ(rho_field: FEECVariable | None = None)[source]#
Bases:
PropagatorEnergy conserving sound wave step of the terms that are nonlinear in the perturbation of \(q = \sqrt p\), in the perturbative models with the variable \(q\) (zero-flow equilibrium, \(q = q_0 + \tilde q\)).
Continuous problem. Find \(\tilde{\mathbf U} \in H(\textnormal{div})\) and \(\tilde q \in L^2\) such that
\[\begin{split}\int_\Omega \rho \frac{\partial \tilde{\mathbf U}}{\partial t} \cdot \mathbf V \,\textnormal d \mathbf x - \frac{2}{\gamma - 1} \int_\Omega \tilde q \, \mathcal S_{\tilde q}(\mathbf V) \,\textnormal d \mathbf x &= 0 \qquad \forall \, \mathbf V \in H(\textnormal{div})\,, \\[2mm] \frac{\partial \tilde q}{\partial t} + \mathcal S_{\tilde q}(\tilde{\mathbf U}) &= 0\,,\end{split}\]with the transport operator \(\mathcal S_s(\mathbf V) = \nabla \cdot (s \mathbf V) + (\frac{\gamma}{2} - 1) s \nabla \cdot \mathbf V\), the total density \(\rho = \rho_0 + \tilde \rho\) (fixed in this step) and \(\gamma = 5/3\). Since \(\int \tilde q \, \nabla \cdot (\tilde q \mathbf V) = \frac 12 \int \tilde q^2 \nabla \cdot \mathbf V\) (for \(\mathbf V \cdot \mathbf n = 0\) on the boundary), the force is \(\int \tilde q^2 \nabla \cdot \mathbf V\), the weak form of \(- \nabla \tilde q^2\), and the second equation is \(\partial_t \tilde q + \tilde{\mathbf U} \cdot \nabla \tilde q + \frac{\gamma}{2} \tilde q \nabla \cdot \tilde{\mathbf U} = 0\): the terms quadratic in the perturbations of \(\rho \partial_t \mathbf U = - \nabla q^2\) and \(\partial_t q + \mathbf U \cdot \nabla q + \frac{\gamma}{2} q \nabla \cdot \mathbf U = 0\). The step conserves the energy
\[\frac 12 \int_\Omega \rho |\tilde{\mathbf U}|^2 \,\textnormal d \mathbf x + \frac{1}{\gamma - 1} \int_\Omega \tilde q^2 \,\textnormal d \mathbf x\,.\]Semi-discrete problem. With the FE coefficients \(\mathbf u\) of \(\tilde{\mathbf U}_h \in V^2_h\) and \(\mathbf q\) of \(\tilde q_h \in V^3_h\),
\[\mathbb M^2_\rho \dot{\mathbf u} = \frac{2}{\gamma - 1} \mathbb S[\mathbf q]^\top \mathbb M^3 \mathbf q\,, \qquad \dot{\mathbf q} = - \mathbb S[\mathbf q] \mathbf u\,,\]with \(\mathbb S[\mathbf w] = \mathbb D \hat \Pi^2[\hat w^3 \vec{\boldsymbol\Lambda}^2 / \sqrt g] + (\frac{\gamma}{2} - 1) \hat \Pi^3[\hat w^3 \Lambda^3 / \sqrt g] \mathbb D\) (
Pressure_transport_operatorwith the adiabatic index \(\gamma / 2\)), the mass matrix \(\mathbb M^3\) (M3) and the mass matrix \(\mathbb M^2_\rho\) of the total density (VelocityMass, with the constructor argumentrho_field; without it \(\mathbb M^2_{\rho_0}\)). The force is the transpose of the operator of the \(\mathbf q\) equation, so the discrete energy \(\frac 12 \mathbf u^\top \mathbb M^2_\rho \mathbf u + \frac{1}{\gamma - 1} \mathbf q^\top \mathbb M^3 \mathbf q\) (a positive definite quadratic form) is conserved for any weight in \(\mathbb S\).Time discretization. Crank-Nicolson in \((\mathbf u, \mathbf q)\) with the weight \(\mathbf w\) of \(\mathbb S\) fixed in the step (
Options.weights):\[\mathbb M^2_\rho \frac{\mathbf u^{n+1} - \mathbf u^n}{\Delta t} = \frac{2}{\gamma - 1} \mathbb S[\mathbf w]^\top \mathbb M^3 \mathbf q^{n+\frac12}\,, \qquad \frac{\mathbf q^{n+1} - \mathbf q^n}{\Delta t} = - \mathbb S[\mathbf w] \mathbf u^{n+\frac12}\,,\]which conserves the discrete energy exactly (up to the solver tolerances): with
"frozen"(default) \(\mathbf w = \mathbf q^n\), one linear solve per step (first order in time); with"midpoint"\(\mathbf w = \mathbf q^{n+\frac12}\), the implicit midpoint rule (second order), solved by Picard iteration (Options.nonlin_solver) with one linear solve per iteration. The step is a linear map with the fixed weight, the force being the transpose of the \(\mathbf q\) operator, so it does not damp or amplify the waves. Each linear solve eliminates \(\mathbf q\) withSchurSolver, with the symmetric positive definite Schur complement\[S = \mathbb M^2_\rho + \frac{\Delta t^2}{2(\gamma - 1)} \mathbb S[\mathbf w]^\top \mathbb M^3 \mathbb S[\mathbf w]\,,\]solved by
Options.solverwith the mass matrix preconditionerOptions.precondof \(\mathbb M^2_{\rho_0}\).- class Variables[source]#
Bases:
objectContainer for variables advanced by
SoundWaveNonlinearWithQ.- Variables:
u (FEECVariable) – Velocity perturbation variable in
"Hdiv"space.q (FEECVariable) – Perturbation of \(q = \sqrt p\) in
"L2"space.
- class Options(weights: Literal['frozen', 'midpoint'] = 'frozen', solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, nonlin_solver: NonlinearSolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
SoundWaveNonlinearWithQ.- Parameters:
weights (LiteralOptions.OptsTimeWeights, default="frozen") – Weight of the transport operator: \(\mathbf q^n\) (
"frozen", one linear solve) or \(\mathbf q^{n+1/2}\) ("midpoint", Picard iteration).solver (LiteralOptions.OptsSymmSolver, default="pcg") – Iterative solver of the (symmetric) Schur complement.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner of the Schur complement, built from \(\mathbb M^2_{\rho_0}\) (
M2n).solver_params (SolverParameters, default=None) – Controls of the Schur complement solve; defaults to
SolverParameters(tol=1e-13).nonlin_solver (NonlinearSolverParameters, default=None) – Controls of the Picard iteration with
weights="midpoint"(tol: relative change of the velocity); defaults toNonlinearSolverParameters(tol=1e-12).
- property gamma: float#
Adiabatic index.
- class struphy.propagators.shear_alfven_nonlinear.ShearAlfvenNonlinear(rho_field: FEECVariable | None = None)[source]#
Bases:
PropagatorEnergy conserving shear-Alfvén step of the terms that are nonlinear in the magnetic field perturbation, in the perturbative MHD models (zero-flow equilibrium, \(\mathbf B = \mathbf B_0 + \tilde{\mathbf B}\)), with the velocity and the magnetic field in \(H(\textnormal{div})\).
Continuous problem. Find \(\tilde{\mathbf U} \in H(\textnormal{div})\) and \(\tilde{\mathbf B} \in H(\textnormal{div})\) such that
\[\begin{split}\int_{\Omega} \rho \frac{\partial \tilde{\mathbf{U}}}{\partial t} \cdot \mathbf V\,\textnormal d \mathbf x &= \int_{\Omega} \tilde{\mathbf{B}} \cdot \nabla \times (\tilde{\mathbf B} \times \mathbf V) \,\textnormal d \mathbf x \qquad \forall \,\mathbf V \in H(\textnormal{div})\,, \\[2mm] \frac{\partial \tilde{\mathbf{B}}}{\partial t} &= \nabla\times(\tilde{\mathbf{U}} \times \tilde{\mathbf{B}})\,,\end{split}\]with the total density \(\rho = \rho_0 + \tilde \rho\) (fixed in this step): the terms \((\nabla \times \tilde{\mathbf B}) \times \tilde{\mathbf B}\) of the Lorentz force (weak form) and \(\nabla \times (\tilde{\mathbf U} \times \tilde{\mathbf B})\) of the induction equation, which are quadratic in the perturbations. This is the step of
ShearAlfvenPropagatorwith the weight \(\mathbf B_0 \to \tilde{\mathbf B}\); together with it andBackgroundCurrentit gives the Lorentz force \((\nabla \times \mathbf B) \times \mathbf B - (\nabla \times \mathbf B_0) \times \mathbf B_0\) and the induction equation \(\partial_t \tilde{\mathbf B} = \nabla \times (\tilde{\mathbf U} \times \mathbf B)\). The step conserves the energy \(\frac 12 \int \rho |\tilde{\mathbf U}|^2 + \frac 12 \int |\tilde{\mathbf B}|^2\).Semi-discrete problem. With the FE coefficients \(\mathbf u\) of \(\tilde{\mathbf U}_h \in V^2_h\) and \(\mathbf b\) of \(\tilde{\mathbf B}_h \in V^2_h\),
\[\mathbb M^2_\rho \dot{\mathbf u} = \mathbb T[\mathbf b]^\top \mathbb M^2 \mathbf b\,, \qquad \dot{\mathbf b} = - \mathbb T[\mathbf b] \mathbf u\,,\]with \(\mathbb T[\mathbf w] = \mathbb C \hat \Pi^1[(\hat{\mathbf w}^2 \times \vec{\boldsymbol\Lambda}^2) / \sqrt g]\) (
Hdiv0_transport_operator, as \(\mathbb C \mathbb T^2\) ofShearAlfvenPropagatorwith \(\mathbf B_0 \to \mathbf w\)), the mass matrix \(\mathbb M^2\) (M2) and the mass matrix \(\mathbb M^2_\rho\) of the total density (VelocityMass, with the constructor argumentrho_field; without it \(\mathbb M^2_{\rho_0}\)). The induction equation holds strongly in \(V^2_h\) (the divergence of \(\mathbf b\) is conserved), and the energy \(\frac 12 \mathbf u^\top \mathbb M^2_\rho \mathbf u + \frac 12 \mathbf b^\top \mathbb M^2 \mathbf b\) is conserved for any weight in \(\mathbb T\).Time discretization. Crank-Nicolson in \((\mathbf u, \mathbf b)\) with the weight \(\mathbf w\) of \(\mathbb T\) fixed in the step (
Options.weights):\[\mathbb M^2_\rho \frac{\mathbf u^{n+1} - \mathbf u^n}{\Delta t} = \mathbb T[\mathbf w]^\top \mathbb M^2 \mathbf b^{n+\frac12}\,, \qquad \frac{\mathbf b^{n+1} - \mathbf b^n}{\Delta t} = - \mathbb T[\mathbf w] \mathbf u^{n+\frac12}\,,\]which conserves the discrete energy exactly (up to the solver tolerances): with
"frozen"(default) \(\mathbf w = \mathbf b^n\), one linear solve per step (first order in time); with"midpoint"\(\mathbf w = \mathbf b^{n+\frac12}\), the implicit midpoint rule (second order), solved by Picard iteration (Options.nonlin_solver). Each linear solve eliminates \(\mathbf b\) withSchurSolver, with the symmetric positive definite Schur complement\[S = \mathbb M^2_\rho + \frac{\Delta t^2}{4} \mathbb T[\mathbf w]^\top \mathbb M^2 \mathbb T[\mathbf w]\,,\]solved by
Options.solverwith the mass matrix preconditionerOptions.precondof \(\mathbb M^2_{\rho_0}\).- class Variables[source]#
Bases:
objectContainer for variables advanced by
ShearAlfvenNonlinear.- Variables:
u (FEECVariable) – Velocity perturbation variable in
"Hdiv"space.b (FEECVariable) – Magnetic field perturbation in
"Hdiv"space.
- class Options(weights: Literal['frozen', 'midpoint'] = 'frozen', solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, nonlin_solver: NonlinearSolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
ShearAlfvenNonlinear.- Parameters:
weights (LiteralOptions.OptsTimeWeights, default="frozen") – Weight of the transport operator: \(\mathbf b^n\) (
"frozen", one linear solve) or \(\mathbf b^{n+1/2}\) ("midpoint", Picard iteration).solver (LiteralOptions.OptsSymmSolver, default="pcg") – Iterative solver of the (symmetric) Schur complement.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner of the Schur complement, built from \(\mathbb M^2_{\rho_0}\) (
M2n).solver_params (SolverParameters, default=None) – Controls of the Schur complement solve; defaults to
SolverParameters(tol=1e-13).nonlin_solver (NonlinearSolverParameters, default=None) – Controls of the Picard iteration with
weights="midpoint"(tol: relative change of the velocity); defaults toNonlinearSolverParameters(tol=1e-12).
- class struphy.propagators.momentum_advection.MomentumAdvection[source]#
Bases:
PropagatorAdvection of mass and momentum in the perturbative fluid models (zero-flow equilibrium), with the velocity in \(H(\textnormal{div})\): the density perturbation and the velocity are transported by the velocity, conserving the mass and the kinetic energy \(\frac 12 \int \rho |\tilde{\mathbf U}|^2\) of the total density \(\rho = \rho_0 + \tilde \rho\).
Continuous problem. Find \(\tilde \rho \in L^2\) and \(\tilde{\mathbf U} \in H(\textnormal{div})\) such that
\[\begin{split}\frac{\partial \tilde \rho}{\partial t} + \nabla \cdot (\rho \tilde{\mathbf U}) &= 0\,, \\[2mm] \int_\Omega \rho \frac{\partial \tilde{\mathbf U}}{\partial t} \cdot \mathbf V \,\textnormal d \mathbf x + \int_\Omega \rho \, \mathbf V \cdot (\boldsymbol \omega \times \tilde{\mathbf U}) \,\textnormal d \mathbf x - \int_\Omega \nabla \cdot (\rho \mathbf V) \, \frac{|\tilde{\mathbf U}|^2}{2} \,\textnormal d \mathbf x &= 0 \qquad \forall \, \mathbf V \in H(\textnormal{div})\,,\end{split}\]with the vorticity \(\boldsymbol \omega = \nabla \times \tilde{\mathbf U}\) (in the weak sense). This is \(\rho (\partial_t \tilde{\mathbf U} + \tilde{\mathbf U} \cdot \nabla \tilde{\mathbf U}) = 0\) in rotational form, \(\tilde{\mathbf U} \cdot \nabla \tilde{\mathbf U} = \boldsymbol \omega \times \tilde{\mathbf U} + \nabla \frac{|\tilde{\mathbf U}|^2}{2}\), with the last term integrated by parts (the boundary term vanishes for \(\tilde{\mathbf U} \cdot \mathbf n = 0\)). The vorticity term does no work, and the work of the last term is the change of the kinetic energy by the transport of the density, such that \(\frac 12 \int \rho |\tilde{\mathbf U}|^2\) and \(\int \tilde \rho\) are conserved.
Semi-discrete problem. With the FE coefficients \(\mathbf n\) of \(\tilde \rho_h \in V^3_h\), \(\boldsymbol \rho = \boldsymbol \rho_0 + \mathbf n\) of the total density (\(\boldsymbol \rho_0\) the coefficients of the projected equilibrium density 3-form,
projected_equil.n3) and \(\mathbf u\) of \(\tilde{\mathbf U}_h \in V^2_h\), the kinetic energy is \(E = \frac 12 \mathbf u^\top \mathbb M^2_\rho \mathbf u = \frac 12 \boldsymbol \rho^\top k(\mathbf u)\), with the density-weighted mass matrix \(\mathbb M^2_\rho\) and the linear form \(k(\mathbf u)_{ijk} = \int \Lambda^3_{ijk} |\tilde{\mathbf U}_h|^2 \frac{1}{\sqrt g}\,\textnormal d \mathbf x\) (HdivDensityOperators, same quadrature, so that the identity is exact). With the transport operator \(\mathbb L[\mathbf w] \mathbf u = \mathbb D \hat \Pi^2[\hat w^3 \mathbf u]\) (L2_transport_operator) and the antisymmetric matrix \(\mathbb W[\mathbf w, \boldsymbol \omega]\) of \(\int w\, \mathbf v \cdot (\boldsymbol \omega \times \mathbf u)\) (HdivDensityOperators), with the weak curl \(\boldsymbol \omega = (\mathbb M^1)^{-1} \mathbb C^\top \mathbb M^2 \mathbf u\),\[\dot{\mathbf n} = - \mathbb L[\boldsymbol \rho] \mathbf u\,, \qquad \mathbb M^2_\rho \dot{\mathbf u} + \mathbb W[\boldsymbol \rho, \boldsymbol \omega] \mathbf u - \frac 12 \mathbb L[\boldsymbol \rho]^\top k(\mathbf u) = 0\,,\]which conserves \(E\), since \(\dot E = \frac 12 \dot{\mathbf n}^\top k(\mathbf u) + \mathbf u^\top \mathbb M^2_\rho \dot{\mathbf u}\) and \(\mathbb W\) is antisymmetric.
Time discretization. A discrete gradient of \(E\) in \((\mathbf n, \mathbf u)\):
\[\begin{split}\frac{\mathbf n^{n+1} - \mathbf n^n}{\Delta t} &= - \mathbb L[\mathbf w] \, \mathbf u^{n+\frac12}\,, \\[2mm] \mathbb M^2_{\bar \rho} \frac{\mathbf u^{n+1} - \mathbf u^n}{\Delta t} + \mathbb W[\mathbf w, \boldsymbol \omega^w] \, \mathbf u^{n+\frac12} - \frac 12 \mathbb L[\mathbf w]^\top \frac{k(\mathbf u^n) + k(\mathbf u^{n+1})}{2} &= 0\,,\end{split}\]with \(\bar{\boldsymbol \rho} = \boldsymbol \rho_0 + (\mathbf n^n + \mathbf n^{n+1})/2\) and \(\mathbf u^{n+\frac12} = (\mathbf u^n + \mathbf u^{n+1})/2\). Since \(E^{n+1} - E^n = \frac 12 (\mathbf n^{n+1} - \mathbf n^n)^\top \frac{k(\mathbf u^n) + k(\mathbf u^{n+1})}{2} + (\mathbf u^{n+1} - \mathbf u^n)^\top \mathbb M^2_{\bar \rho} \mathbf u^{n+\frac12}\) exactly, the kinetic energy is conserved exactly for any weight \(\mathbf w\) and vorticity \(\boldsymbol \omega^w\) (up to the tolerances of the iteration), and the mass because \(\int \mathbb D \,\cdot = 0\). The option
weightssets \(\mathbf w\) and \(\boldsymbol \omega^w\):"frozen"(default) \(\boldsymbol \rho_0 + \mathbf n^n\) and the vorticity of \(\mathbf u^n\) (first order in time),"midpoint"\(\bar{\boldsymbol \rho}\) and the vorticity of \(\mathbf u^{n+\frac12}\) (second order). The kinetic energy is cubic in \((\mathbf n, \mathbf u)\), so the system is nonlinear for both options; it is solved by Picard iteration (Options.nonlin_solver): with the current iterate, \(\mathbf n^{n+1}\) from the first equation, then \(\mathbf u^{n+1}\) from the linear system with the non-symmetric matrix \(\mathbb M^2_{\bar \rho} + \frac{\Delta t}{2} \mathbb W\) (Options.solver, preconditioned with the mass matrix preconditioner of \(\mathbb M^2_{\rho_0}\)).- class Variables[source]#
Bases:
objectContainer for variables advanced by
MomentumAdvection.- Variables:
n (FEECVariable) – Density perturbation variable in
"L2"space.u (FEECVariable) – Velocity variable in
"Hdiv"space.
- class Options(weights: Literal['frozen', 'midpoint'] = 'frozen', solver: Literal['pbicgstab', 'bicgstab', 'gmres'] = 'pbicgstab', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, solver_M1: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', solver_params_M1: SolverParameters | None = None, nonlin_solver: NonlinearSolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
MomentumAdvection.- Parameters:
weights (LiteralOptions.OptsTimeWeights, default="frozen") – Density weight of the transport and vorticity of the vorticity term: at \(t^n\) (
"frozen") or at \(t^{n+1/2}\) ("midpoint", updated in every Picard iteration).solver (LiteralOptions.OptsGenSolver, default="pbicgstab") – Solver of the (non-symmetric) linear system of the velocity.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner of the linear system, built from \(\mathbb M^2_{\rho_0}\) (
M2n).solver_params (SolverParameters, default=None) – Controls of the linear solve; defaults to
SolverParameters(tol=1e-13).solver_M1 (LiteralOptions.OptsSymmSolver, default="pcg") – Solver of the inner
M1inversion of the weak curl.solver_params_M1 (SolverParameters, default=None) – Controls of the inner
M1inversion; defaults toSolverParameters(tol=1e-14).nonlin_solver (NonlinearSolverParameters, default=None) – Controls of the Picard iteration (
tol: relative change of the velocity); defaults toNonlinearSolverParameters(tol=1e-12).
- class struphy.propagators.background_current.BackgroundCurrent(b_field: FEECVariable | None = None, rho_field: FEECVariable | None = None)[source]#
Bases:
PropagatorForce of the equilibrium current on the magnetic field perturbation in linear MHD (zero-flow equilibrium): the part of the Lorentz force that is not in the energy conserving shear-Alfvén step (
ShearAlfvenPropagator,ShearAlfvenCurlCurlPropagator).Continuous problem. Find \(\tilde{\mathbf U} \in H(\textnormal{div})\) such that
\[\int_\Omega \rho_0 \frac{\partial \tilde{\mathbf U}}{\partial t} \cdot \mathbf V \,\textnormal d \mathbf x = \int_\Omega \left[ (\nabla \times \mathbf B_0) \times \tilde{\mathbf B} \right] \cdot \mathbf V \,\textnormal d \mathbf x \qquad \forall \, \mathbf V \in H(\textnormal{div})\,,\]at a fixed magnetic field perturbation \(\tilde{\mathbf B}\) (the constructor argument
b_field), either in \(H(\textnormal{div})\) or in \(H(\textnormal{curl})\), with the equilibrium density \(\rho_0\) and magnetic field \(\mathbf B_0\). This is the \(\rho_0\)-weighted \(L^2\) projection of \(\rho_0 \partial_t \tilde{\mathbf U} = \mathbf J_0 \times \tilde{\mathbf B}\), \(\mathbf J_0 = \nabla \times \mathbf B_0\).Semi-discrete problem. With the FE coefficients \(\mathbf u\) of \(\tilde{\mathbf U}_h \in V^2_h\) and \(\mathbf b\) of \(\tilde{\mathbf B}_h \in V^\beta_h\) (\(\beta = 2\) or \(1\)),
\[\mathbb M^2_{\rho_0} \dot{\mathbf u} = \mathbb M_{J_0} \mathbf b\,,\]with the density-weighted mass matrix \(\mathbb M^2_{\rho_0}\) (
M2n) and the current mass matrix \(\mathbb M_{J_0}\) of \(\int (\mathbf J_0 \times \mathbf W) \cdot \mathbf V \,\textnormal d \mathbf x\) , i.e. \(\mathbb M_{J_0} = \mathbb M^2_{J_0}\) (M2J) for \(\beta = 2\) and the mixed \(\mathbb M_{J_0} = \mathbb M^{21}_{J_0}\) (M21J) for \(\beta = 1\).Time discretization. Exact in time, the only option, since the right-hand side does not change during the step:
\[\mathbf u^{n+1} = \mathbf u^n + \Delta t \, (\mathbb M^2_{\rho_0})^{-1} \mathbb M_{J_0} \mathbf b\,,\]where \((\mathbb M^2_{\rho_0})^{-1}\) is applied with
Options.solverandOptions.precond.Perturbative models (constructor argument
rho_field, the density perturbation \(\tilde \rho\)): the inertia is that of the total density, \(\mathbb M^2_{\rho_0} \to \mathbb M^2_\rho\), \(\rho = \rho_0 + \tilde \rho^n\) (VelocityMass, assembled at \(t^n\); the step is still exact in time, since the density does not change in it).- class Variables[source]#
Bases:
objectContainer for variables advanced by
BackgroundCurrent.- Variables:
u (FEECVariable) – Velocity perturbation variable in
"Hdiv"space.
- class Options(solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
BackgroundCurrent.- Parameters:
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Iterative solver of the
M2ninversion.precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner of the
M2ninversion.solver_params (SolverParameters, default=None) – Solver controls; defaults to
SolverParameters().
- class struphy.propagators.magnetosonic_uniform.MagnetosonicUniform[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\tilde \rho \in L^2, \tilde{\mathbf U} \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}, \tilde p \in L^2\) such that
\[ \begin{align}\begin{aligned}&\frac{\partial \tilde \rho}{\partial t}+\nabla\cdot(\rho_0 \tilde{\mathbf{U}})=0\,,\\\int \rho_0&\frac{\partial \tilde{\mathbf{U}}}{\partial t} \cdot \mathbf V\,\textrm d \mathbf x - \int \tilde p\, \nabla \cdot \mathbf V \,\textrm d \mathbf x = 0 \qquad \forall \ \mathbf V \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\,,\\&\frac{\partial \tilde p}{\partial t} + \frac{5}{3}\,p_0\nabla\cdot \tilde{\mathbf{U}}=0\,.\end{aligned}\end{align} \]Time discretization: Crank-Nicolson (implicit mid-point). System size reduction via
SchurSolver:\[\begin{split}\begin{bmatrix} \mathbf u^{n+1} - \mathbf u^n \\ \mathbf p^{n+1}_i - \mathbf p^n_i \end{bmatrix} = \frac{\Delta t}{2} \begin{bmatrix} 0 & (\mathbb M^\rho_2)^{-1} \mathbb D^\top \mathbb M_3 \\ - \gamma \mathcal K^3 \mathbb D & 0 \end{bmatrix} \begin{bmatrix} (\mathbf u^{n+1} + \mathbf u^n) \\ (\mathbf p^{n+1}_i + \mathbf p^n_i) \end{bmatrix} ,\end{split}\]where \(\mathbb M^\rho_2\) is a weighted mass matrix in 2-space, the weight being the MHD equilibirum density \(\rho_0\). Furthermore, \(\mathcal K^3\) is the basis projection operator given by :
\[\mathcal{K}^3_{ijk,mno} := \hat{\Pi}^3_{ijk} \left[ \frac{\hat{p}^3_{\text{eq}}}{\sqrt{g}}\Lambda^3_{mno} \right] \,.\]The solution of the above system is based on the Schur complement.
Decoupled density update:
\[\boldsymbol{\rho}^{n+1} = \boldsymbol{\rho}^n - \frac{\Delta t}{2} \mathcal Q \mathbb D (\mathbf u^{n+1} + \mathbf u^n) \,.\]- Parameters:
n (feectools.linalg.stencil.StencilVector) – FE coefficients of a discrete 3-form.
u (feectools.linalg.block.BlockVector) – FE coefficients of MHD velocity 2-form.
p (feectools.linalg.stencil.StencilVector) –
FE coefficients of a discrete 3-form.
- **paramsdict
Solver- and/or other parameters for this splitting step.
- class Variables[source]#
Bases:
objectContainer for variables advanced by
MagnetosonicUniform.- Variables:
n (FEECVariable) – Density perturbation variable in
"L2"space.u (FEECVariable) – Velocity perturbation variable in one of
"Hcurl","Hdiv", or"H1vec".p (FEECVariable) – Pressure perturbation variable in
"L2"space.
- class Options(solver: Literal['pbicgstab', 'bicgstab', 'gmres'] = 'pbicgstab', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
MagnetosonicUniform.- Parameters:
solver (LiteralOptions.OptsGenSolver, default="pbicgstab") – Iterative solver used by
SchurSolver.precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner applied to the
M2nblock.solver_params (SolverParameters, default=None) – Iterative-solver controls. If
None, defaults toSolverParameters().
- class struphy.propagators.faraday_extended.FaradayExtended(*args, **kwargs)[source]#
Bases:
PropagatorEquations: Faraday’s law
\[\begin{split}\begin{align*} & \frac{\partial {\mathbf A}}{\partial t} = - \frac{\nabla \times (\nabla \times {\mathbf A} + {\mathbf B}_0) }{n} \times (\nabla \times {\mathbf A} + {\mathbf B}_0) - \frac{\int ({\mathbf A} - {\mathbf p}f \mathrm{d}{\mathbf p})}{n} \times (\nabla \times {\mathbf A} + {\mathbf B}_0), \\ & n = \int f \mathrm{d}{\mathbf p}. \end{align*}\end{split}\]Mid-point rule:
\[\begin{split}\begin{align*} & \left[ \mathbb{M}_1 - \frac{\Delta t}{2} \mathbb{F}(\hat{n}^0_h, {\mathbf a}^{n+\frac{1}{2}}) \mathbb{M}_1^{-1} (\mathbb{P}_1^\top \mathbb{W} \mathbb{P}_1 + \mathbb{C}^\top \mathbb{M}_2 \mathbb{C} ) \right] {\mathbf a}^{n+1} \\ & = \mathbb{M}_1 {\mathbf a}^n + \frac{\Delta t}{2} \mathbb{F}(\hat{n}^0_h, {\mathbf a}^{n+\frac{1}{2}}) \mathbb{M}_1^{-1} (\mathbb{P}_1^\top \mathbb{W} \mathbb{P}_1 + \mathbb{C}^\top \mathbb{M}_2 \mathbb{C} ) {\mathbf a}^{n+1} \\ & - \Delta t \mathbb{F}(\hat{n}^0_h, {\mathbf a}^{n+\frac{1}{2}}) \mathbb{M}_1^{-1} \mathbb{P}_1^\top \mathbb{W} {\mathbf P}^n\\ & + \Delta t \mathbb{F}(\hat{n}^0_h, {\mathbf a}^{n+\frac{1}{2}}) \mathbb{M}_1^{-1} \mathbb{C}^\top \mathbb{M}_2 {\mathbf b}_0\\ & \mathbb{F}_{ij} = - \int \frac{1}{\hat{n}^0_h \sqrt{g}} G (\nabla \times {\mathbf A} + {\mathbf B}_0) \cdot (\Lambda^1_i \times \Lambda^1_j) \mathrm{d}{\boldsymbol \eta}. \end{align*}\end{split}\]- Parameters:
a (feectools.linalg.block.BlockVector) – FE coefficients of vector potential.
**params (dict) – Solver- and/or other parameters for this splitting step.
Note
Not available: this propagator has not been ported to the current Propagator, Accumulator and solver APIs, and the kernels
hybrid_fA_Arelatedandhybrid_curlAare missing.
- class struphy.propagators.hall.Hall(epsilon_from: Species | None = None)[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\mathbf B \in H(\textnormal{curl})\) such that
\[\int_\Omega \frac{\partial \tilde{\mathbf{B}}}{\partial t} \cdot \mathbf C\,\textnormal d \mathbf x + \frac{1}{\varepsilon} \int_\Omega \nabla\times \mathbf C \cdot \left( \frac{\nabla\times \tilde{\mathbf{B}}}{\rho_0}\times \mathbf{B}_0 \right) \textrm d \mathbf x = 0 \qquad \forall \, \mathbf C \in H(\textrm{curl})\]Time discretization: Crank-Nicolson (implicit mid-point):
\[\mathbf b^{n+1} - \mathbf b^n = \frac{\Delta t}{2} \mathbb M_1^{-1} \mathbb C^\top \mathbb M^{\mathcal{T},\rho}_2 \mathbb C (\mathbf b^{n+1} + \mathbf b^n) ,\]where \(\mathbb M^{\mathcal{T},\rho}_2\) is a weighted mass matrix in 2-space, the weight being \(\frac{\mathcal{T}}{\rho_0}\), the MHD equilibirum density \(\rho_0\) as a 0-form, and rotation matrix \(\mathcal{T} \vec v = \vec B^2_{\textnormal{eq}} \times \vec v\,,\). The solution of the above system is based on the Pre-conditioned Biconjugate Gradient Stabilized algortihm (PBiConjugateGradientStab).
- class Variables[source]#
Bases:
objectContainer for variables advanced by
Hall.- Variables:
b (FEECVariable) – Magnetic-field variable in
"Hcurl"space.
- class Options(solver: Literal['pbicgstab', 'bicgstab', 'gmres'] = 'pbicgstab', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
Hall.- Parameters:
solver (LiteralOptions.OptsGenSolver, default="pbicgstab") – General iterative solver used for the linear Hall update.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner applied to the
M1block.solver_params (SolverParameters, default=None) – Iterative-solver controls. If
None, defaults toSolverParameters().
Nonlinear MHD solvers#
- class struphy.propagators.variational_alfven_evolve.VariationalAlfvenEvolve(bt2: FEECVariable | None = None)[source]#
Bases:
PropagatorFEEC discretization of the coupling of the velocity and the magnetic field (shear-Alfvén and fast magnetosonic waves) of the variational MHD models: find \(\mathbf u \in (H^1)^3\) and \(\mathbf B \in H(\textnormal{div})\) such that, with the transport operator \(\mathcal T_{\bar{\mathbf B}}(\mathbf v) = \nabla \times (\bar{\mathbf B} \times \mathbf v)\), depending on the option
model:"full"(full MHD):\[\int_\Omega \partial_t (\rho \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x - \int_\Omega \mathbf B \cdot \mathcal T_{\mathbf B}(\mathbf v)\,\textrm d \mathbf x = 0 \qquad \forall \, \mathbf v \in (H^1)^3\,, \qquad \partial_t \mathbf B + \mathcal T_{\mathbf B}(\mathbf u) = 0 \,.\]"linear"(\(\mathbf B\) is the perturbation of the equilibrium \(\mathbf B_0\)):\[\int_\Omega \partial_t (\rho_0 \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x - \int_\Omega \left(\mathbf B \cdot \mathcal T_{\mathbf B_0}(\mathbf v) + \mathbf B_0 \cdot \mathcal T_{\mathbf B}(\mathbf v)\right)\textrm d \mathbf x = 0\,, \qquad \partial_t \mathbf B + \mathcal T_{\mathbf B_0}(\mathbf u) = 0 \,.\]"perturbative"(\(\mathbf B\) is the perturbation, all nonlinear terms are kept):\[\int_\Omega \partial_t (\rho \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x - \int_\Omega \left(\mathbf B \cdot \mathcal T_{\mathbf B_0 + \mathbf B}(\mathbf v) + \mathbf B_0 \cdot \mathcal T_{\mathbf B}(\mathbf v)\right)\textrm d \mathbf x = 0\,, \qquad \partial_t \mathbf B + \mathcal T_{\mathbf B_0 + \mathbf B}(\mathbf u) = 0 \,.\]
The thermodynamic variable is not changed, so this propagator serves the models in \(p\) and in \(q = \sqrt p\) alike; together with
VariationalSoundEvolveit splits the coupled step ofVariationalQBEvolveinto the shear-Alfvén/fast and the sound waves. The second-order term of the linear and perturbative models comes from the auxiliary variable \(\hat{\mathbf B}\) (bt2), which, if given, is advanced with \(\partial_t \hat{\mathbf B} + \mathcal T_{\mathbf B}(\mathbf u) = 0\); it enters the conserved energy as \(\int \mathbf B_0 \cdot \hat{\mathbf B}\). For"perturbative", \(\rho\) is \(\rho_0\) plus the variable ofVariationalDensityEvolve.Discretization: with \(\mathbb T[\bar{\mathbf B}] = \mathbb C \hat\Pi^1[\hat{\bar{\mathbf B}} \times \vec{\boldsymbol\Lambda}^v]\) (
Hdiv0_transport_operator) and, for"linear",\[\begin{split}\begin{align} &\mathbb M^v[\hat\rho_0] \frac{\mathbf u^{n+1} - \mathbf u^n}{\Delta t} - \mathbb T[\mathbf B_0]^\top \mathbb M^2 \mathbf b^{n+\frac12} - \mathbb T[\bar{\mathbf b}]^\top \mathbb M^2 \mathbf b_0 = 0 \,, \\[2mm] &\frac{\mathbf b^{n+1} - \mathbf b^n}{\Delta t} + \mathbb T[\mathbf B_0]\, \mathbf u^{n+\frac12} = 0 \,, \qquad \frac{\hat{\mathbf b}^{n+1} - \hat{\mathbf b}^n}{\Delta t} + \mathbb T[\bar{\mathbf b}]\, \mathbf u^{n+\frac12} = 0 \,, \end{align}\end{split}\]and correspondingly for the other models. The weight \(\bar{\mathbf b}\) of the transport operators is taken at the same time in all equations, which makes the step energy conserving: at \(t^n\) (first order in time, one Newton step solves the linear system), or at the midpoint \(t^{n+1/2}\) with the option
implicit_weights(second order in time, a Picard iteration). The Newton system is solved withSchurSolverFull; its Schur complement for"linear"is the shear-Alfvén operator \(\mathbb M^v[\hat\rho_0] + \frac{\Delta t^2}{4} \mathbb T[\mathbf B_0]^\top \mathbb M^2 \mathbb T[\mathbf B_0]\), for which the optionschur_precondprovides a Kronecker preconditioner.- class Variables[source]#
Bases:
objectContainer for variables advanced by
VariationalAlfvenEvolve.- Variables:
u (FEECVariable) – Velocity variable in
"H1vec"space.b (FEECVariable) – Magnetic-field variable in
"Hdiv"space.
- class Options(model: Literal['full', 'linear', 'perturbative'] = 'full', solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, nonlin_solver: NonlinearSolverParameters | None = None, implicit_weights: bool | None = None, schur_precond: Literal['MassMatrixPreconditioner', 'StiffnessPreconditioner'] = 'MassMatrixPreconditioner', exb_correction: bool = True)[source]#
Bases:
OptionsBaseConfiguration options for
VariationalAlfvenEvolve.- Parameters:
model ({"full", "linear", "perturbative"}, default="full") – Model variant.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Linear solver of the Schur system.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner used in linear solves.
solver_params (SolverParameters, default=None) – Linear-solver controls. The Newton systems are solved for a normalized right-hand side, so
tolis relative to the norm of the residual.nonlin_solver (NonlinearSolverParameters, default=None) – Nonlinear/Picard iteration controls. The iteration stops when the norm of the residual is below
toltimes the norm of the state at \(t^n\) if that is smaller than one (absolute otherwise), or when it stagnates (with a warning).implicit_weights (bool, default=None) – If
True, the weight of the transport operators is taken at the midpoint \(t^{n+1/2}\) and updated in every Picard iteration (second order in time, several iterations per step); ifFalse, it is taken at \(t^n\) (first order in time, one linear solve). Both conserve the energy.NonemeansTruefor"linear"and"perturbative"andFalsefor"full".schur_precond ({"MassMatrixPreconditioner", "StiffnessPreconditioner"}, default="MassMatrixPreconditioner") – Preconditioner of the Schur system: the mass-matrix preconditioner of the velocity block, or the Kronecker (fast diagonalization) preconditioner of the whole shear-Alfvén Schur operator (
alfven_schur_preconditioner(), built for each time step inprepare_time_step()). The latter needsmodel="linear",solver="pcg", tensor-product splines and the global commuting projectors; otherwise the mass-matrix preconditioner is used (with a warning).exb_correction (bool, default=True) – For
schur_precond="StiffnessPreconditioner": add the auxiliary space correction of the \(\mathbf E \times \mathbf B\) flows \(\mathbf B_0 \times \nabla \phi\).
- class struphy.propagators.variational_density_evolve.VariationalDensityEvolve(s: FEECVariable | None = None, divdiv_alpha: float = 0.0)[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\rho \in L^2\) and \(\mathbf u \in (H^1)^3\) such that
\[\begin{split}&\partial_t \rho + \nabla \cdot ( \tilde{\rho} \mathbf u ) = 0 \,, \\[4mm] &\int_\Omega \partial_t (\rho \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x + \int_\Omega \left(\frac{|\mathbf u|^2}{2} - \frac{\partial(\rho \mathcal U(\rho))}{\partial \rho}\right) \nabla \cdot (\tilde{\rho} \mathbf v) \,\textrm d \mathbf x = 0 \qquad \forall \, \mathbf v \in (H^1)^3\,.\end{split}\]Where \(\tilde{\rho}\) is either \(\rho\) for full-f models, \(\rho_0\) for linear models or \(\rho_0+\rho\) for \(\delta f\) models.
In the case of linear model, the second equation is not updated. The linear models also accept a velocity 2-form, \(\mathbf u \in H(\textnormal{div})\); the density is then advected with \(\mathbb D \mathcal Q^2\) (
Q2, as inMagnetosonic).Velocity in \(H(\textnormal{div})\) for
model="perturbative": \(\mathbf u\) is a 2-form, \(\hat{\mathbf u} = \hat{\mathbf u}^2/\sqrt g\), with vanishing normal component at the boundary. The scheme below is the same, with the mass matrix \(\mathbb M^v[\hat\rho]\) replaced by the 2-form mass matrix of the total density \(\mathbb M^2_\rho\) (HdivDensityOperators), the transport operator \(\mathbb D \hat\Pi^2[\hat\rho^3 \vec{\boldsymbol\Lambda}^2/\sqrt g]\) (L2_transport_operatorwithu_space="Hdiv") and the kinetic energy density \(\hat{\mathbf u}^{2,n} \cdot G \hat{\mathbf u}^{2,n+1}/(2g)\) (KineticEnergyEvaluatorwithu_space="Hdiv"). Not available with polar splines ordivdiv_alpha > 0.On the logical domain:
\[\begin{split}\begin{align} &\partial_t \hat{\rho}^3 + \nabla \cdot ( \hat{\rho}^3 \hat{\mathbf{u}} ) = 0 ~ , \\[4mm] &\int_{\hat{\Omega}} \partial_t ( \hat{\rho}^3 \hat{\mathbf{u}}) \cdot G \hat{\mathbf{v}} \, \textrm d \boldsymbol \eta + \int_{\hat{\Omega}} \left( \frac{| DF \hat{\mathbf{u}} |^2}{2} - \frac{\partial (\hat{\rho}^3 \mathcal U)}{\partial \hat{\rho}^3} \right) \nabla \cdot (\hat{\rho}^3 \hat{\mathbf{v}}) \, \textrm d \boldsymbol \eta = 0 ~ , \\[2mm] \end{align}\end{split}\]where \(\mathcal U\) depends on the chosen model. It is discretized as
\[\begin{split}\begin{align} &\frac{\mathbb M^v[\hat{\rho}_h^{n+1}] \mathbf u^{n+1}- \mathbb M^v[\hat{\rho}_h^n] \mathbf u^n}{\Delta t} + (\mathbb D \hat{\Pi}^{2}[\hat{\rho_h^{n}} \vec{\boldsymbol \Lambda}^v])^\top \hat{l}^3\left(\frac{DF \hat{\mathbf{u}}_h^{n+1} \cdot DF \hat{\mathbf{u}}_h^{n}}{2} - \frac{\hat{\rho}_h^{n+1}\mathcal U(\hat{\rho}_h^{n+1})-\hat{\rho}_h^{n}\mathcal U(\hat{\rho}_h^{n})}{\hat{\rho}_h^{n+1}-\hat{\rho}_h^n} \right) = 0 ~ , \\[2mm] &\frac{\boldsymbol \rho^{n+1}- \boldsymbol \rho^n}{\Delta t} + \mathbb D \hat{\Pi}^{2}[\hat{\rho_h^{n}} \vec{\boldsymbol \Lambda}^v] \mathbf u^{n+1/2} = 0 ~ , \\[2mm] \end{align}\end{split}\]where \(\hat{l}^3(f)\) denotes the vector representing the linear form \(v_h \mapsto \int_{\hat{\Omega}} f(\boldsymbol \eta) v_h(\boldsymbol \eta) d \boldsymbol \eta\), that is the vector with components
\[\hat{l}^3(f)_{ijk}=\int_{\hat{\Omega}} f \Lambda^3_{ijk} \textrm d \boldsymbol \eta\]and the weights in the the
BasisProjectionOperatorand theWeightedMassOperatorare given by\[\hat{\mathbf{u}}_h^{k} = (\mathbf{u}^{k})^\top \vec{\boldsymbol \Lambda}^v \in (V_h^0)^3 \, \text{for k in} \{n, n+1/2, n+1\}, \qquad \hat{\rho}_h^{k} = (\rho^{k})^\top \vec{\boldsymbol \Lambda}^3 \in V_h^3 \, \text{for k in} \{n, n+1/2, n+1\} .\]With a div-div regularization (
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\): the mass matrix \(\mathbb M^v[\hat\rho]\) is replaced by the metric \(\mathbb A[\hat\rho]\) ofkinetic_metric()and \(\alpha \, d^{n+1} d^n / g\) is added to the argument of \(\hat l^3\), with \(d^k = (\mathbb D \mathbb U^v \mathbf u^k)^\top \vec{\boldsymbol \Lambda}^3\). Since \(\mathbb A\) is linear in \(\hat\rho\), the scheme conserves the regularized energy.- class Variables[source]#
Bases:
objectContainer for variables advanced by
VariationalDensityEvolve.- Variables:
rho (FEECVariable) – Density variable in
"L2"space.u (FEECVariable) – Velocity variable in
"H1vec"space, or in"Hdiv"space formodel="linear","linear_q"and"perturbative".
- class Options(model: Literal['pressureless', 'barotropic', 'full', 'full_p', 'full_q', 'linear', 'perturbative', 'linear_q', 'perturbative_q'] = 'barotropic', gamma: float = 1.6666666666666667, solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, nonlin_solver: NonlinearSolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
VariationalDensityEvolve.- Parameters:
model ({"pressureless", "barotropic", "full", "full_p", "full_q", "linear", "perturbative", "linear_q", "perturbative_q"}, default="barotropic") – Density/thermodynamic model variant.
gamma (float, default=5/3) – Adiabatic index.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Linear solver for the mass-matrix solves (the non-symmetric Jacobian is always solved with “pbicgstab”).
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner used in linear solves.
solver_params (SolverParameters, default=None) – Linear-solver controls. The solves are done for normalized right-hand sides, so
tolis relative.nonlin_solver (NonlinearSolverParameters, default=None) – Nonlinear iteration controls. The iteration stops when the norm of the residual is below
toltimes the norm of the state at \(t^n\) if that is smaller than one (absolute otherwise), or when it stagnates (with a warning).
- class struphy.propagators.variational_entropy_evolve.VariationalEntropyEvolve(rho: FEECVariable | None = None, divdiv_alpha: float = 0.0)[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\mathbf u \in (H^1)^3\) and \(s \in L^2\) such that
\[\begin{split}&\int_\Omega \partial_t (\rho \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x - \int_\Omega \frac{\partial(\rho \mathcal U)}{\partial s} \nabla \cdot (s \mathbf v) \,\textrm d \mathbf x = 0 \qquad \forall \, \mathbf v \in (H^1)^3\,, \\[4mm] &\partial_t s + \nabla \cdot ( s \mathbf u ) = 0 \,.\end{split}\]On the logical domain:
\[\begin{split}\begin{align} &\int_{\hat{\Omega}} \partial_t ( \hat{\rho}^3 \hat{\mathbf{u}}) \cdot G \hat{\mathbf{v}} \, \textrm d \boldsymbol \eta - \int_{\hat{\Omega}} \left(\frac{\partial \hat{\rho}^3 \mathcal U}{\partial \hat{s}} \right) \nabla \cdot (\hat{s} \hat{\mathbf{v}}) \, \textrm d \boldsymbol \eta = 0 ~ , \\[2mm] &\partial_t \hat{s} + \nabla \cdot ( \hat{s} \hat{\mathbf{u}} ) = 0 ~ , \end{align}\end{split}\]where \(\mathcal U\) depends on the chosen model. It is discretized as
\[\begin{split}\begin{align} &\mathbb M^v[\hat{\rho}_h^{n}] \frac{ \mathbf u^{n+1}-\mathbf u^n}{\Delta t} - (\mathbb D \hat{\Pi}^{2}[\hat{s_h^{n}} \vec{\boldsymbol \Lambda}^v])^\top \hat{l}^3\left( \frac{\hat{\rho}_h^{n}\mathcal U(\hat{\rho}_h^{n},\hat{s}_h^{n+1})-\hat{\rho}_h^{n}\mathcal U(\hat{\rho}_h^{n},\hat{s}_h^{n})}{\hat{s}_h^{n+1}-\hat{s}_h^n} \right) = 0 ~ , \\[2mm] &\frac{\mathbf s^{n+1}- \mathbf s^n}{\Delta t} + \mathbb D \hat{\Pi}^{2}[\hat{s_h^{n}} \vec{\boldsymbol \Lambda}^v] \mathbf u^{n+1/2} = 0 ~ , \\[2mm] \end{align}\end{split}\]where \(\hat{l}^3(f)\) denotes the vector representing the linear form \(v_h \mapsto \int_{\hat{\Omega}} f(\boldsymbol \eta) v_h(\boldsymbol \eta) d \boldsymbol \eta\), that is the vector with components
\[\hat{l}^3(f)_{ijk}=\int_{\hat{\Omega}} f \Lambda^3_{ijk} \textrm d \boldsymbol \eta\]and the weights in the the
BasisProjectionOperatorand theWeightedMassOperatorare given by\[\hat{\mathbf{u}}_h^{k} = (\mathbf{u}^{k})^\top \vec{\boldsymbol \Lambda}^v \in (V_h^0)^3 \, \text{for k in} \{n, n+1/2, n+1\}, \qquad \hat{s}_h^{k} = (s^{k})^\top \vec{\boldsymbol \Lambda}^3 \in V_h^3 \, \text{for k in} \{n, n+1/2, n+1\} \qquad \hat{\rho}_h^{n} = (\rho^{n})^\top \vec{\boldsymbol \Lambda}^3 \in V_h^3 \.\]With a div-div regularization (
divdiv_alpha\(> 0\)), the mass matrix \(\mathbb M^v[\hat\rho]\) is replaced by the metric \(\mathbb A[\hat\rho]\) of the regularized kinetic energy, seekinetic_metric().- class Variables[source]#
Bases:
objectContainer for variables advanced by
VariationalEntropyEvolve.- Variables:
s (FEECVariable) – Entropy variable in
"L2"space.u (FEECVariable) – Velocity variable in
"H1vec"space.
- class Options(model: Literal['full'] = 'full', gamma: float = 1.6666666666666667, solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, nonlin_solver: NonlinearSolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
VariationalEntropyEvolve.- Parameters:
model ({"full"}, default="full") – Entropy-evolution model selection.
gamma (float, default=5/3) – Adiabatic index.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Linear solver for implicit substeps.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner used in linear solves.
solver_params (SolverParameters, default=None) – Linear-solver controls. The solves are done for normalized right-hand sides, so
tolis relative.nonlin_solver (NonlinearSolverParameters, default=None) – Nonlinear iteration controls. The iteration stops when the norm of the residual is below
toltimes the norm of the state at \(t^n\) if that is smaller than one (absolute otherwise), or when it stagnates (with a warning).
- class struphy.propagators.variational_mag_field_evolve.VariationalMagFieldEvolve(divdiv_alpha: float = 0.0)[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\mathbf u \in (H^1)^3\) and \(\mathbf B \in H(\textnormal{div})\) such that
\[\begin{split}&\int_\Omega \partial_t (\rho \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x - \int_\Omega \mathbf B \cdot \nabla \times (\mathbf \tilde{B} \times \mathbf v) \,\textrm d \mathbf x = 0 \qquad \forall \, \mathbf v \in (H^1)^3\,, \\[4mm] &\partial_t \mathbf B + \nabla \cdot ( \mathbf \tilde{B} \times \mathbf u ) = 0 \,.\end{split}\]Where \(\tilde{\mathbf B}\) is either \(\mathbf B\) for full-f models, \(\mathbf B_0\) for linear models or \(\mathbf B_0+\mathbf B\) for \(\delta f\) models.
On the logical domain:
\[\begin{split}\begin{align} &\int_{\hat{\Omega}} \partial_t ( \hat{\rho}^3 \hat{\mathbf{u}}) \cdot G \hat{\mathbf{v}} \, \textrm d \boldsymbol \eta - \int_{\hat{\Omega}} \hat{\mathbf{B}}^2 \cdot G \,\nabla \times (\hat{\mathbf{B}}^2 \times \hat{\mathbf{v}}) \,\frac{1}{\sqrt g}\, \textrm d \boldsymbol \eta = 0 ~ , \\[2mm] &\partial_t \hat{\mathbf{B}}^2 + \nabla \times (\hat{\mathbf{B}}^2 \times \hat{\mathbf{u}}) = 0 ~ . \end{align}\end{split}\]It is discretized as
\[\begin{split}\begin{align} &\mathbb M^v[\hat{\rho}_h^{n}] \frac{ \mathbf u^{n+1}-\mathbf u^n}{\Delta t} - (\mathbb C \hat{\Pi}^{1}[B_h^{n+1}} \cdot \vec{\boldsymbol \Lambda}^v])^\top \mathbb M^2 B^{n+\frac{1}{2}} \big) = 0 ~ , \\[2mm] &\frac{\mathbf b^{n+1}- \mathbf b^n}{\Delta t} + \mathbb C \hat{\Pi}^{1}[\hat{B_h^{n}} \cdot \vec{\boldsymbol \Lambda}^v]] \mathbf u^{n+1/2} = 0 ~ , \end{align}\end{split}\]where weights in the the
BasisProjectionOperatorand theWeightedMassOperatorare given by\[\hat{\mathbf{B}}_h^{n+1/2} = (\mathbf{b}^{n+1/2})^\top \vec{\boldsymbol \Lambda}^2 \in V_h^2 \, \qquad \hat{\rho}_h^{n} = (\boldsymbol \rho^{n})^\top \vec{\boldsymbol \Lambda}^3 \in V_h^3 \,.\]With a div-div regularization (
divdiv_alpha\(> 0\)), the mass matrix \(\mathbb M^v[\hat\rho]\) is replaced by the metric \(\mathbb A[\hat\rho]\) of the regularized kinetic energy, seekinetic_metric().- class Variables[source]#
Bases:
objectContainer for variables advanced by
VariationalMagFieldEvolve.- Variables:
u (FEECVariable) – Velocity variable in
"H1vec"space.b (FEECVariable) – Magnetic-field variable in
"Hdiv"space.
- class Options(model: Literal['full', 'full_p', 'linear'] = 'full', solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, nonlin_solver: NonlinearSolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
VariationalMagFieldEvolve.- Parameters:
model ({"full", "full_p", "linear"}, default="full") – Magnetic-field evolution model variant.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Linear solver for implicit substeps.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner used in linear solves.
solver_params (SolverParameters, default=None) – Linear-solver controls. The solves are done for normalized right-hand sides, so
tolis relative.nonlin_solver (NonlinearSolverParameters, default=None) – Nonlinear iteration controls. The iteration stops when the norm of the residual is below
toltimes the norm of the state at \(t^n\) if that is smaller than one (absolute otherwise), or when it stagnates (with a warning).
- class struphy.propagators.variational_momentum_advection.VariationalMomentumAdvection(divdiv_alpha: float = 0.0, rho: FEECVariable | None = None)[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\mathbf u \in (H^1)^3\) such that
\[\int_{\Omega} \partial_t ( \rho \mathbf{u}) \cdot \mathbf{v} \,\textrm d \mathbf x - \int_{\Omega} \rho \mathbf{u} \cdot [\mathbf{u}, \mathbf{v}] \, \textrm d \mathbf x = 0 \,.\]On the logical domain:
\[\int_{\hat{\Omega}} \partial_t ( \hat{\rho}^3 \hat{\mathbf{u}}) \cdot G \hat{\mathbf{v}} \,\textrm d \boldsymbol \eta - \int_{\hat{\Omega}} \hat{\rho}^3 \hat{\mathbf{u}} \cdot G [\hat{\mathbf{u}}, \hat{\mathbf{v}}] \, \textrm d \boldsymbol \eta = 0 \,,\]which is discretized as
\[\mathbb M^v[\hat{\rho}_h^n] \frac{\mathbf u^{n+1}- \mathbf u^n}{\Delta t} - \left(\sum_{\mu} (\hat{\Pi}^{0}[\hat{\mathbf u}_h^{n+1/2} \cdot \vec{\boldsymbol \Lambda}^1] \mathbb G P_{\mu} - \hat{\Pi}^0[\hat{\mathbf A}^1_{\mu,h} \cdot \vec{\boldsymbol \Lambda}^v])^\top P_i \right) \mathbb M^v[\hat{\rho}_h^n] \mathbf u^{n} = 0 ~ .\]where \(P_\mu\) stand for the
CoordinateProjectorand the weights in the the twoBasisProjectionOperatorand theWeightedMassOperatorare given by\[\hat{\mathbf{u}}_h^{n+1/2} = (\mathbf{u}^{n+1/2})^\top \vec{\boldsymbol \Lambda}^v \in (V_h^0)^3 \,, \qquad \hat{\mathbf A}^1_{\mu,h} = \nabla P_\mu((\mathbf u^{n+1/2})^\top \vec{\boldsymbol \Lambda}^v)] \in V_h^1\,, \qquad \hat{\rho}_h^{n} = (\rho^{n})^\top \vec{\boldsymbol \Lambda}^3 \in V_h^3 \,.\]With a div-div regularization (
divdiv_alpha\(> 0\)), the mass matrix \(\mathbb M^v[\hat\rho]\) is replaced by the metric \(\mathbb A[\hat\rho]\) of the regularized kinetic energy, seekinetic_metric().Velocity in \(H(\textnormal{div})\) (velocity variable in
"Hdiv"space): find \(\mathbf u \in H_0(\textnormal{div})\) (vanishing normal component at the boundary) such that, for all \(\mathbf v \in H_0(\textnormal{div})\),\[\int_{\Omega} \partial_t ( \rho \mathbf{u}) \cdot \mathbf{v} \,\textrm d \mathbf x - \int_{\Omega} \rho \mathbf{u} \cdot [\mathbf{u}, \mathbf{v}] \, \textrm d \mathbf x = 0 \,, \qquad [\mathbf u, \mathbf v] = \mathbf u \cdot \nabla \mathbf v - \mathbf v \cdot \nabla \mathbf u\,.\]The velocity is a 2-form, \(\hat{\mathbf u}^2_h = (\mathbf u^n)^\top \vec{\boldsymbol\Lambda}^2\), and the bracket is discretized with the vector fields as derivations of an auxiliary space that contains the contravariant velocity components, so that no projection is needed (
HdivBracketOperator, \(\mathbb B[\hat{\mathbf y}]\) with \(\mathbf v^\top \mathbb B[\hat{\mathbf y}] \mathbf u = \langle \mathbf m, \widehat{[A_{\mathbf u}, A_{\mathbf v}]}\rangle\) and \(\hat{\mathbf y} = (\mathbb M^2_{\textnormal{log}})^{-1} \mathbf m\) the representer of the momentum \(\mathbf m\) with the unweighted logical mass matrix of the 2-forms). The semi-discrete problem is\[\mathbb M^2_\rho\, \dot{\mathbf u} = \mathbb B[\hat{\mathbf y}(\mathbb M^2_\rho \mathbf u)]\, \mathbf u \,,\]with the density-weighted mass matrix \(\mathbb M^2_\rho\) (
VelocityMass: \(\mathbb M^2_{\rho_0}\) without the constructor argumentrho, else the total density \(\rho_0 + \tilde\rho\) with the density perturbationrho, frozen during the step). The time discretization is the implicit midpoint rule with the momentum of the bracket at \(t^n\) (bracket_momentum="n", default),\[\left(\mathbb M^2_\rho - \frac{\Delta t}{2} \mathbb B[\hat{\mathbf y}^n]\right) \mathbf u^{n+1} = \left(\mathbb M^2_\rho + \frac{\Delta t}{2} \mathbb B[\hat{\mathbf y}^n]\right) \mathbf u^n \,, \qquad \hat{\mathbf y}^n = \hat{\mathbf y}(\mathbb M^2_\rho \mathbf u^n)\,,\]one linear solve (GMRES, left-preconditioned with \((\mathbb M^2_\rho)^{-1}\)), or at \(t^{n+1/2}\) (
bracket_momentum="n+1/2"), \(\hat{\mathbf y}^{n+1/2} = \hat{\mathbf y}(\mathbb M^2_\rho \mathbf u^{n+1/2})\), a nonlinear system solved by Picard iteration (one such linear solve per iteration) or by Newton iteration (nonlin_solver.type; Jacobian \(\mathbb M^2_\rho - \frac{\Delta t}{2}\mathbb B[\hat{\mathbf y}^{n+1/2}] - \frac{\Delta t}{2} \mathbb D[\mathbf u^{n+1/2}] \mathbb M^2_\rho\) with the derivative \(\mathbb D\) of the bracket with respect to the momentum,HdivBracketMomentumDerivative), followed by one linear solve with the momentum of the last iterate. Since \(\mathbb B\) is skew-symmetric for any momentum, both conserve the kinetic energy \(\frac 12 \mathbf u^\top \mathbb M^2_\rho \mathbf u\) exactly (up to the solver tolerances). Not available with polar splines ordivdiv_alpha > 0.- class Variables[source]#
Bases:
objectContainer for variables advanced by
VariationalMomentumAdvection.- Variables:
u (FEECVariable) – Velocity variable in
"H1vec"or"Hdiv"space.
- class Options(solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, nonlin_solver: NonlinearSolverParameters | None = None, bracket_momentum: Literal['n', 'n+1/2'] = 'n')[source]#
Bases:
OptionsBaseConfiguration options for
VariationalMomentumAdvection.- Parameters:
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Linear solver for mass-matrix related solves (the non-symmetric Jacobian is always solved with “gmres”).
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner used in linear solves.
solver_params (SolverParameters, default=None) – Linear-solver controls.
nonlin_solver (NonlinearSolverParameters, default=None) – Nonlinear iteration controls (Picard/Newton). For a velocity in
"Hdiv", onlytype,tol,maxiterandinfoof the iteration withbracket_momentum="n+1/2".bracket_momentum ({"n", "n+1/2"}, default="n") – Velocity in
"Hdiv": time of the momentum in the bracket, \(t^n\) (one linear solve) or \(t^{n+1/2}\) (Picard or Newton iteration,nonlin_solver.type). Must be"n"for a velocity in"H1vec".
- class struphy.propagators.variational_pb_evolve.VariationalPBEvolve(div_u: FEECVariable | None = None, u2: FEECVariable | None = None, pt3: FEECVariable | None = None, bt2: FEECVariable | None = None, rho: FEECVariable | None = None)[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\mathbf u \in (H^1)^3\), \(p \in L^2\) and \(\mathbf B \in H(\textnormal{div})\) such that
\[\begin{split}&\int_\Omega \partial_t (\rho \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x - \int_\Omega \mathbf B \cdot \nabla \times (\tilde{\mathbf B} \times \mathbf v) - \int_\Omega \frac{1}{\gamma -1} (\nabla \cdot (\tilde{p} \mathbf v))\,\textrm d \mathbf x = 0 \qquad \forall \, \mathbf v \in (H^1)^3\,, \\[4mm] &\partial_t \mathbf B + \nabla \cdot ( \tilde{\mathbf B} \times \mathbf u ) = 0 \,, \\[4mm] &\partial_t p + \nabla \cdot(\tilde{p} \mathbf u) + (\gamma - 1) \tilde{p} \nabla \cdot u = 0 \,.\end{split}\]Where \(\tilde{\mathbf B}\) (resp. \(\tilde{p}\)) is either \(\mathbf B\) (resp. \(p\)) for full-f models, \(\mathbf B_0\) (resp. \(p_0\)) for linear models or \(\mathbf B_0+\mathbf B\) (resp. \(p_0+p\)) for \(\delta f\) models.
On the logical domain:
\[\begin{split}\begin{align} &\int_{\hat{\Omega}} \partial_t ( \hat{\rho}^3 \hat{\mathbf{u}}) \cdot G \hat{\mathbf{v}} \, \textrm d \boldsymbol \eta - \int_{\hat{\Omega}} \hat{\mathbf{B}}^2 \cdot G \,\nabla \times (\hat{\mathbf{B}}^2 \times \hat{\mathbf{v}}) \,\frac{1}{\sqrt g}\, - \frac{g}{\gamma -1} \nabla \cdot (\hat{p} \hat{v}) \textrm d \boldsymbol \eta = 0 ~ , \\[2mm] &\partial_t \hat{\mathbf{B}}^2 + \nabla \times (\hat{\mathbf{B}}^2 \times \hat{\mathbf{u}}) = 0 ~ , \\[2mm] &\partial_t \hat{p} + \nabla \cdot (\hat{p} \hat{u}) + (\gamma - 1 ) \hat{p} \nabla \cdot G \hat{u} = 0 \, \end{align}\end{split}\]It is discretized as
\[\begin{split}\begin{align} &\mathbb M^v[\hat{\rho}_h^{n}] \frac{ \mathbf u^{n+1}-\mathbf u^n}{\Delta t} - (\mathbb C \hat{\Pi}^{1}[\hat{\mathbf B_h^{n}} \cdot \vec{\boldsymbol \Lambda}^v])^\top \mathbb M^2 \mathbf B^{n+\frac{1}{2}} - (\mathbb D \hat{\Pi}^{2}[\hat{p_h^{n}} \cdot \vec{\boldsymbol \Lambda}^v])^\top \hat{l}^3(\frac{g}{\gamma-1}) = 0 ~ , \\[2mm] &\frac{\mathbf b^{n+1}- \mathbf b^n}{\Delta t} + \mathbb C \hat{\Pi}^{1}[\hat{\mathbf B_h^{n}} \cdot \vec{\boldsymbol \Lambda}^v]] \mathbf u^{n+1/2} = 0 ~ , \\[2mm] &\frac{\mathbf p^{n+1}- \mathbf p^n}{\Delta t} + \big(\mathbb D \hat{\Pi}^{2}[\hat{p_h^{n}} \cdot \vec{\boldsymbol \Lambda}^v]] + (\gamma - 1)\hat{\Pi}^{3}[\hat{p_h^{n}} \cdot \vec{\boldsymbol \Lambda}^3] \mathbb D \mathcal{U}^v \big) \mathbf u^{n+1/2}= 0 ~ , \\[2mm] \end{align}\end{split}\]with
\[\hat{l}^3(f)_{ijk}=\int_{\hat{\Omega}} f \Lambda^3_{ijk} \textrm d \boldsymbol \eta\]where weights in the the
BasisProjectionOperatorand theWeightedMassOperatorare given by\[\hat{\mathbf{B}}_h^{n} = (\mathbf{b}^{n})^\top \vec{\boldsymbol \Lambda}^2 \in V_h^2 \, \qquad \hat{\rho}_h^{n} = (\boldsymbol \rho^{n})^\top \vec{\boldsymbol \Lambda}^3 \in V_h^3 \, \qquad \hat{p}_h^{n} = (\boldsymbol p^{n})^\top \vec{\boldsymbol \Lambda}^3 \in V_h^3 \,.\]and \(\mathcal{U}^v\) is
BasisProjectionOperators.The weights of the transport operators are taken at \(t^n\) (in all equations, which conserves the energy); the step is therefore only first-order accurate in time.
Velocity in \(H(\textnormal{div})\) (
model="perturbative"): \(\mathbf u\) is a 2-form with vanishing normal component at the boundary. The scheme is the same, with the mass matrix of the total density \(\mathbb M^2_\rho\) (VelocityMasswith the constructor argumentrho, assembled at the beginning of the step) instead of \(\mathbb M^v[\hat\rho_h^n]\), and the transport operatorsHdiv0_transport_operatorandPressure_transport_operatorwithu_space="Hdiv"(\(\hat\Pi^1[(\hat{\mathbf B}^2 \times \vec{\boldsymbol\Lambda}^2)/\sqrt g]\), \(\hat\Pi^2[\hat p^3 \vec{\boldsymbol\Lambda}^2/\sqrt g]\) and \(\mathbb D\) without \(\mathcal U^v\)). Not available with polar splines.- class Variables[source]#
Bases:
objectContainer for variables advanced by
VariationalPBEvolve.- Variables:
p (FEECVariable) – Pressure variable in
"L2"space.u (FEECVariable) – Velocity variable in
"H1vec"space, or in"Hdiv"space formodel="perturbative".b (FEECVariable) – Magnetic-field variable in
"Hdiv"space.
- class Options(model: Literal['full_p', 'linear', 'perturbative'] = 'full_p', gamma: float = 1.6666666666666667, solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, nonlin_solver: NonlinearSolverParameters | None = None, schur_precond: Literal['MassMatrixPreconditioner', 'StiffnessPreconditioner'] = 'MassMatrixPreconditioner', exb_correction: bool = True)[source]#
Bases:
OptionsBaseConfiguration options for
VariationalPBEvolve.- Parameters:
model ({"full_p", "linear", "perturbative"}, default="full_p") – Pressure-magnetic coupled model variant.
gamma (float, default=5/3) – Adiabatic index.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Linear solver for implicit substeps.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner used in linear solves.
solver_params (SolverParameters, default=None) – Linear-solver controls. The Newton systems are solved for a normalized right-hand side, so
tolis relative to the norm of the residual.nonlin_solver (NonlinearSolverParameters, default=None) – Nonlinear iteration controls. The iteration stops when the norm of the residual is below
toltimes the norm of the state at \(t^n\) if that is smaller than one (absolute otherwise), or when it stagnates (with a warning).schur_precond ({"MassMatrixPreconditioner", "StiffnessPreconditioner"}, default="MassMatrixPreconditioner") – Preconditioner of the Schur system of the Newton step: the mass-matrix preconditioner of the velocity block, or the Kronecker (fast diagonalization) preconditioner of the whole shear-Alfven Schur operator \(\mathbb M^v[\rho_0] + \frac{\Delta t^2}{4} \mathbb T_0^\top \mathbb M^2 \mathbb T_0\) (
alfven_schur_preconditioner(), built from the equilibrium for each time step inprepare_time_step(); formodel="perturbative"it approximates the Schur operator of the Newton step). The latter needsmodel="linear"or"perturbative",solver="pcg", tensor-product splines and the global commuting projectors; otherwise the mass-matrix preconditioner is used (with a warning).exb_correction (bool, default=True) – For
schur_precond="StiffnessPreconditioner": add the auxiliary space correction of the \(\mathbf E \times \mathbf B\) flows \(\mathbf B_0 \times \nabla \phi\) (the incompressible shear Alfven modes with small parallel wave number, which block Jacobi does not reduce).
- class struphy.propagators.variational_qb_evolve.VariationalQBEvolve(div_u: FEECVariable | None = None, u2: FEECVariable | None = None, qt3: FEECVariable | None = None, bt2: FEECVariable | None = None)[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\mathbf u \in (H^1)^3\), \(q \in L^2\) and \(\mathbf B \in H(\textnormal{div})\) such that, with the transport operators
\[\mathcal T_{\bar{\mathbf B}}(\mathbf v) = \nabla \times (\bar{\mathbf B} \times \mathbf v)\,, \qquad \mathcal S_{\bar q}(\mathbf v) = \nabla \cdot (\bar q \mathbf v) + \left(\frac \gamma 2 - 1\right) \bar q\, \nabla \cdot \mathbf v\,,\]the following holds, depending on the option
model:"full_q"(full MHD in \(q = \sqrt p\)):\[\begin{split}&\int_\Omega \partial_t (\rho \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x - \int_\Omega \mathbf B \cdot \mathcal T_{\mathbf B}(\mathbf v)\,\textrm d \mathbf x - \int_\Omega \frac{2 q}{\gamma -1} \mathcal S_q(\mathbf v)\,\textrm d \mathbf x = 0 \qquad \forall \, \mathbf v \in (H^1)^3\,, \\[2mm] &\partial_t \mathbf B + \mathcal T_{\mathbf B}(\mathbf u) = 0 \,, \qquad \partial_t q + \mathcal S_q(\mathbf u) = 0 \,.\end{split}\]"linear_q"(\(\mathbf B\) and \(q\) are the perturbations of the equilibrium \(\mathbf B_0\), \(q_0\)):\[\begin{split}&\int_\Omega \partial_t (\rho_0 \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x - \int_\Omega \left(\mathbf B \cdot \mathcal T_{\mathbf B_0}(\mathbf v) + \mathbf B_0 \cdot \mathcal T_{\mathbf B}(\mathbf v)\right)\textrm d \mathbf x - \int_\Omega \frac{2}{\gamma -1} \left(q\, \mathcal S_{q_0}(\mathbf v) + q_0\, \mathcal S_q(\mathbf v)\right)\textrm d \mathbf x = 0\,, \\[2mm] &\partial_t \mathbf B + \mathcal T_{\mathbf B_0}(\mathbf u) = 0 \,, \qquad \partial_t q + \mathcal S_{q_0}(\mathbf u) = 0 \,.\end{split}\]"perturbative_q"(\(\mathbf B\) and \(q\) are the perturbations, all nonlinear terms are kept):\[\begin{split}&\int_\Omega \partial_t (\rho \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x - \int_\Omega \left(\mathbf B \cdot \mathcal T_{\mathbf B_0 + \mathbf B}(\mathbf v) + \mathbf B_0 \cdot \mathcal T_{\mathbf B}(\mathbf v)\right)\textrm d \mathbf x - \int_\Omega \frac{2}{\gamma -1} \left(q\, \mathcal S_{q_0 + q}(\mathbf v) + q_0\, \mathcal S_q(\mathbf v)\right)\textrm d \mathbf x = 0\,, \\[2mm] &\partial_t \mathbf B + \mathcal T_{\mathbf B_0 + \mathbf B}(\mathbf u) = 0 \,, \qquad \partial_t q + \mathcal S_{q_0 + q}(\mathbf u) = 0 \,.\end{split}\]
The second-order terms come from the auxiliary variables \(\hat{\mathbf B}\) (
bt2) and \(\hat q\) (qt3), which, if given, are advanced with \(\partial_t \hat{\mathbf B} + \mathcal T_{\mathbf B}(\mathbf u) = 0\) and \(\partial_t \hat q + \mathcal S_q(\mathbf u) = 0\). They enter the conserved energy of the linear and perturbative models as \(\int \mathbf B_0 \cdot \hat{\mathbf B} + 2 q_0 \hat q / (\gamma - 1)\). For"full_q"the mass density \(\rho\) is the variable ofVariationalDensityEvolve, for"perturbative_q"it is \(\rho_0\) plus that variable.The scheme below is written for
"full_q"; for the other models the operators and linear forms are replaced by the sums above. Below, hats denote pullbacks to the logical domain (not the auxiliary variables). The weights of the transport operators are taken at the same time in the momentum, induction and pressure equations, which makes the step energy conserving: at \(t^n\) as written below (first order in time), or at the midpoint \(t^{n+1/2}\) with the optionimplicit_weights(second order in time, the default for"linear_q"and"perturbative_q").On the logical domain:
\[\begin{split}\begin{align} &\int_{\hat{\Omega}} \partial_t ( \hat{\rho}^3 \hat{\mathbf{u}}) \cdot G \hat{\mathbf{v}} \, \textrm d \boldsymbol \eta - \int_{\hat{\Omega}} \hat{\mathbf{B}}^2 \cdot G \,\nabla \times (\hat{\mathbf{B}}^2 \times \hat{\mathbf{v}}) \,\frac{1}{\sqrt g}\, - \frac{2 \hat{q}}{\gamma -1} \left(\nabla \cdot (\hat{q} \hat{\mathbf{v}}) + (\gamma/2 - 1) \hat{q} \nabla \cdot \hat{\mathbf{v}}\right) \textrm d \boldsymbol \eta = 0 ~ , \\[2mm] &\partial_t \hat{\mathbf{B}}^2 + \nabla \times (\hat{\mathbf{B}}^2 \times \hat{\mathbf{u}}) = 0 ~ , \\[2mm] &\partial_t \hat{q} + \nabla \cdot (\hat{q} \hat{u}) + (\gamma/2 - 1 ) \hat{q} \nabla \cdot G \hat{u} = 0 \, \end{align}\end{split}\]It is discretized as
\[\begin{split}\begin{align} &\mathbb M^v[\hat{\rho}_h^{n}] \frac{ \mathbf u^{n+1}-\mathbf u^n}{\Delta t} - (\mathbb C \hat{\Pi}^{1}[\hat{\mathbf B_h^{n}} \cdot \vec{\boldsymbol \Lambda}^v])^\top \mathbb M^2 \mathbf B^{n+\frac{1}{2}}- \Big(\big(\mathbb D \hat{\Pi}^{2}[\hat{q_h^n} \cdot \vec{\boldsymbol \Lambda}^v]] + (\gamma/2 - 1)\hat{\Pi}^{3}[\hat{q_h^n} \cdot \vec{\boldsymbol \Lambda}^3] \mathbb D \mathcal{U}^v \big) \mathbf v \Big)^\top \hat{l}^3(\frac{2q^{n+\frac{1}{2}}}{\gamma-1}) = 0 ~ , \\[2mm] &\frac{\mathbf b^{n+1}- \mathbf b^n}{\Delta t} + \mathbb C \hat{\Pi}^{1}[\hat{\mathbf B_h^{n}} \cdot \vec{\boldsymbol \Lambda}^v]] \mathbf u^{n+1/2} = 0 ~ , \\[2mm] &\frac{\mathbf q^{n+1}- \mathbf q^n}{\Delta t} + \big(\mathbb D \hat{\Pi}^{2}[\hat{q_h^n} \cdot \vec{\boldsymbol \Lambda}^v]] + (\gamma/2 - 1)\hat{\Pi}^{3}[\hat{q_h^n} \cdot \vec{\boldsymbol \Lambda}^3] \mathbb D \mathcal{U}^v \big) \mathbf u^{n+1/2}= 0 ~ , \\[2mm] \end{align}\end{split}\]with
\[\hat{l}^3(f)_{ijk}=\int_{\hat{\Omega}} f \Lambda^3_{ijk} \textrm d \boldsymbol \eta\]where weights in the the
BasisProjectionOperatorand theWeightedMassOperatorare given by\[\hat{\mathbf{B}}_h^{n} = (\mathbf{b}^{n})^\top \vec{\boldsymbol \Lambda}^2 \in V_h^2 \, \qquad \hat{\rho}_h^{n} = (\boldsymbol \rho^{n})^\top \vec{\boldsymbol \Lambda}^3 \in V_h^3 \, \qquad \hat{q}_h^{n} = (\boldsymbol q^{n})^\top \vec{\boldsymbol \Lambda}^3 \in V_h^3 \,.\]and \(\mathcal{U}^v\) is
BasisProjectionOperators.- class Variables[source]#
Bases:
objectContainer for variables advanced by
VariationalQBEvolve.- Variables:
q (FEECVariable) – Thermal-pressure-like scalar in
"L2"space.u (FEECVariable) – Velocity variable in
"H1vec"space.b (FEECVariable) – Magnetic-field variable in
"Hdiv"space.
- class Options(model: Literal['full_q', 'linear_q', 'perturbative_q'] = 'full_q', gamma: float = 1.6666666666666667, solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, nonlin_solver: NonlinearSolverParameters | None = None, implicit_weights: bool | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
VariationalQBEvolve.- Parameters:
model ({"full_q", "linear_q", "perturbative_q"}, default="full_q") – Coupled q-B evolution model variant.
gamma (float, default=5/3) – Adiabatic index.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Linear solver for implicit substeps.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner used in linear solves.
solver_params (SolverParameters, default=None) – Linear-solver controls. The Newton systems are solved for a normalized right-hand side, so
tolis relative to the norm of the residual.nonlin_solver (NonlinearSolverParameters, default=None) – Nonlinear/Picard iteration controls. The iteration stops when the norm of the residual is below
toltimes the norm of the state at \(t^n\) if that is smaller than one (absolute otherwise), or when it stagnates (with a warning).implicit_weights (bool, default=None) – If
True, the weights of the transport operators (\(\mathbf B\) and \(q\)) are taken at the midpoint \(t^{n+1/2}\) and updated in every Picard iteration, which makes the step second-order accurate in time. IfFalse, they are taken at \(t^n\); the step is then only first-order accurate (e.g. for sound waves). Both conserve the energy.NonemeansTruefor"linear_q"and"perturbative_q", andFalsefor"full_q". WithTrue, the Jacobian of the Picard iteration does not contain the dependence of the operators on their weights, so a step needs several iterations (typically 2 to 10, more for large time steps or amplitudes) instead of one, each with a Schur solve;Falseis the cheaper first-order choice.
- class struphy.propagators.variational_linear_base.VariationalLinearEvolve[source]#
Bases:
PropagatorBase class of the propagators of the linear variational MHD models that advance the velocity \(\mathbf u\) and one other variable \(x\) (magnetic field, \(q\) or pressure) with a single linear solve per time step, without Picard or Newton iteration.
The linear models are discretized as
\[\mathbb M^v[\hat\rho_0] \frac{\mathbf u^{n+1} - \mathbf u^n}{\Delta t} + c\, \mathbb A_0^\top \mathbb M_x \mathbf x^{n+\frac12} + \mathbf f(\mathbf x^n) = 0 \,, \qquad \frac{\mathbf x^{n+1} - \mathbf x^n}{\Delta t} + \mathbb A_0\, \mathbf u^{n+\frac12} = 0 \,,\]with the transport operator \(\mathbb A_0\) weighted by the equilibrium, constant in time. The term \(\mathbf f(\mathbf x^n)\) comes from the transport operator weighted by the perturbation; it is taken at \(t^n\), the same weight that advances the auxiliary second-order variable of the energy, which makes the step energy conserving (first order in time). The system is linear in \((\mathbf u^{n+1}, \mathbf x^{n+1})\) and solved in increment form, \((\mathbf u^{n+1}, \mathbf x^{n+1}) = (\mathbf u^n, \mathbf x^n) - \mathbb J^{-1} \mathbf F(\mathbf u^n, \mathbf x^n)\) with the residual \(\mathbf F\) and the constant matrix
\[\begin{split}\mathbb J = \begin{pmatrix} \mathbb M^v[\hat\rho_0] & \frac{c \Delta t}{2} \mathbb A_0^\top \mathbb M_x \\ \frac{\Delta t}{2} \mathbb A_0 & \mathbb I \end{pmatrix} \,,\end{split}\]by
SchurSolverFull. The subclasses define the variables, the options and the operators.With midpoint weights (option
weights="midpoint"of the subclasses), the term \(\mathbf f\) is taken at \(\mathbf x^{n+\frac12}\). It is linear in its weight, \(\mathbf f(\mathbf x) = \mathbb R \mathbf x\), with a constant operator \(\mathbb R\) (the second-order term in weak form, built from mass matrices and derivatives), so the step is still one linear solve, with \(c \mathbb A_0^\top \mathbb M_x \to c \mathbb A_0^\top \mathbb M_x + \mathbb R\) in \(\mathbb J\) (the residual is the same). The auxiliary variable is advanced with the weight \(\mathbf x^{n+\frac12}\) too, which keeps the step energy conserving. This is the implicit midpoint rule of the linear system: second order in time and without the numerical damping (or growth) of the frozen weight \(\mathbf x^n\); the Schur complement is not symmetric.The velocity can also be a 2-form, \(\mathbf u \in H(\textnormal{div})\) (velocity variable in
"Hdiv"space); then \(\mathbb M^v[\hat\rho_0]\) is replaced byM2n, as inLinearMHD.
- class struphy.propagators.variational_linear_alfven_evolve.VariationalLinearAlfvenEvolve(at1: FEECVariable | None = None, bt2: FEECVariable | None = None)[source]#
Bases:
VariationalLinearEvolveFEEC discretization of the coupling of the velocity and the magnetic field (shear-Alfvén and fast magnetosonic waves) of the linear variational MHD models, with one linear solve per time step: find \(\mathbf u \in (H^1)^3\) and \(\mathbf B \in H(\textnormal{div})\) (the perturbation of the equilibrium \(\mathbf B_0\)) such that, with the transport operator \(\mathcal T_{\bar{\mathbf B}}(\mathbf v) = \nabla \times (\bar{\mathbf B} \times \mathbf v)\),
\[\int_\Omega \partial_t (\rho_0 \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x - \int_\Omega \left(\mathbf B \cdot \mathcal T_{\mathbf B_0}(\mathbf v) + \mathbf B_0 \cdot \mathcal T_{\mathbf B}(\mathbf v)\right)\textrm d \mathbf x = 0 \qquad \forall \, \mathbf v \in (H^1)^3\,, \qquad \partial_t \mathbf B + \mathcal T_{\mathbf B_0}(\mathbf u) = 0 \,.\]The thermodynamic variable is not changed, so this propagator serves the linear models in \(p\) and in \(q = \sqrt p\) alike; together with
VariationalLinearSoundEvolveit splits the linear MHD step into the shear-Alfvén/fast and the sound waves. The second-order term comes from the auxiliary second-order field \(\hat{\mathbf B} = \nabla \times \hat{\mathbf A}\), with \(\partial_t \hat{\mathbf A} = - \mathbf B \times \mathbf u\) (at1), which enters the conserved energy as \(\int \mathbf B_0 \cdot \hat{\mathbf B} = \int \mathbf J_0 \cdot \hat{\mathbf A}\), with the equilibrium current \(\mathbf J_0 = \nabla \times \mathbf B_0\) (the boundary term vanishes, since \(\mathbf u \cdot \mathbf n = \mathbf B \cdot \mathbf n = 0\) gives \(\hat{\mathbf A} \times \mathbf n = 0\)). Its variation gives the force \(\int \mathbf B_0 \cdot \mathcal T_{\mathbf B}(\mathbf v) = \int \mathbf v \cdot (\mathbf J_0 \times \mathbf B)\).Discretization: the equilibrium term with \(\mathbb T[\bar{\mathbf B}] = \mathbb C \hat\Pi^1[\hat{\bar{\mathbf B}} \times \vec{\boldsymbol\Lambda}^v]\) (
Hdiv0_transport_operator), and the second-order term with the equilibrium current \(\mathbf j_0\) of the dual complex (no boundary conditions, seeequilibrium_current()) and the weighted mass matrix \(\mathbb W\) of \(\int \mathbf v \cdot (\mathbf j_0 \times \mathbf B)\):\[\begin{split}\begin{align} &\mathbb M^v[\hat\rho_0] \frac{\mathbf u^{n+1} - \mathbf u^n}{\Delta t} - \mathbb T[\mathbf B_0]^\top \mathbb M^2 \mathbf b^{n+\frac12} - \mathbb W \mathbf b^w = 0 \,, \qquad \frac{\mathbf b^{n+1} - \mathbf b^n}{\Delta t} + \mathbb T[\mathbf B_0]\, \mathbf u^{n+\frac12} = 0 \,, \\[2mm] &\frac{\hat{\mathbf a}^{n+1} - \hat{\mathbf a}^n}{\Delta t} + (\mathbb M^1_{\textnormal{free}})^{-1} \mathbf y(\mathbf b^w \times \mathbf u^{n+\frac12}) = 0 \,, \qquad \mathbf y(\mathbf w)_k = \int_\Omega \mathbf w \cdot \vec{\boldsymbol\Lambda}^1_k \,\textrm d \mathbf x \,, \end{align}\end{split}\]where \(\mathbb M^1_{\textnormal{free}}\) and \(\mathbf y\) are taken over all basis functions of \(V^1_h\), without the boundary conditions. The energy \(\mathbf j_0^\top \mathbb M^1_{\textnormal{free}} \hat{\mathbf a}\) then changes by exactly \(-\Delta t\, \mathbf j_0 \cdot \mathbf y = -\Delta t\, (\mathbf u^{n+\frac12})^\top \mathbb W \mathbf b^w\), the work of the force, which makes the step energy conserving (both forms are evaluated at the same quadrature points). The second-order field \(\hat{\mathbf b} = \mathbb C \hat{\mathbf a}\) (
bt2) is a diagnostic.Why the current of the dual complex: the magnetic energy is \(\frac12 \int \beta \wedge \star \beta\) for the 2-form \(\beta\), and its second-order term is \(\int d\hat\alpha \wedge H_0 = \int \hat\alpha \wedge dH_0\) with \(H_0 = \star \beta_0\) (metric-free integration by parts, the boundary term vanishes with \(\hat{\mathbf A} \times \mathbf n = 0\)). The current \(J_0 = dH_0\) is the exterior derivative of the Hodge dual of the field, a quantity of the dual complex (\(H\), \(J\)), which has no wall conditions. The vector potential, the electric field and the magnetic field live in the primal complex with the wall conditions. Writing the energy as \(\mathbf b_0^\top \mathbb M^2 \hat{\mathbf b}\), with \(\hat{\mathbf a}\) subject to the boundary conditions, instead makes the current the weak curl \(\mathbb M_1^{-1} \mathbb C^\top \mathbb M_2 \mathbf b_0\), the codifferential of the primal complex: its tangential components vanish at a wall where those of \(\mathbf J_0\) do not, a boundary layer one element wide that does not shrink in amplitude and causes a spurious instability localized at the wall. Both energies are the same in the continuum.
The option
weightssets \(\mathbf b^w\). With"midpoint"(default), \(\mathbf b^w = \mathbf b^{n+\frac12}\): one linear solve (VariationalLinearEvolve) with the (not symmetric) Schur complement \(\mathbb M^v[\hat\rho_0] + \frac{\Delta t^2}{4} (\mathbb T[\mathbf B_0]^\top \mathbb M^2 + \mathbb W)\, \mathbb T[\mathbf B_0]\); the step is the implicit midpoint rule, energy conserving and second order in time. With"frozen", \(\mathbf b^w = \mathbf b^n\) (first order in time): the Schur complement is the shear-Alfvén operator \(\mathbb M^v[\hat\rho_0] + \frac{\Delta t^2}{4} \mathbb T[\mathbf B_0]^\top \mathbb M^2 \mathbb T[\mathbf B_0]\), symmetric and constant in time; the optionschur_precondprovides a Kronecker preconditioner of it (alfven_schur_preconditioner()).The velocity can also be a 2-form, \(\mathbf u \in H(\textnormal{div})\) (velocity variable in
"Hdiv"space). The scheme is the same, with the operators ofShearAlfvenPropagatorfor the equilibrium terms, \(\mathbb M^v[\hat\rho_0] \to \mathbb M^{2,n}\) and \(\mathbb T[\mathbf B_0] \to \mathbb C\, \mathcal T^2\) (T2). The energy is conserved in the same way.- class Variables[source]#
Bases:
objectContainer for variables advanced by
VariationalLinearAlfvenEvolve.- Variables:
u (FEECVariable) – Velocity variable in
"H1vec"or"Hdiv"space.b (FEECVariable) – Magnetic-field perturbation in
"Hdiv"space.
- class Options(weights: Literal['frozen', 'midpoint'] = 'midpoint', solver: Literal['pcg', 'pcg_sr', 'cg', 'pbicgstab', 'rpbicgstab'] | None = None, precond: Literal['MassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, info: bool = False, relative_tol: bool = True, schur_precond: Literal['MassMatrixPreconditioner', 'StiffnessPreconditioner'] = 'MassMatrixPreconditioner', exb_correction: bool = True)[source]#
Bases:
OptionsBaseConfiguration options for
VariationalLinearAlfvenEvolve.- Parameters:
weights ({"frozen", "midpoint"}, default="midpoint") – Weight of the second-order term: \(\mathbf b^n\) (first order in time) or \(\mathbf b^{n+1/2}\) (second order).
solver ({"pcg", "pcg_sr", "cg", "pbicgstab", "rpbicgstab"}, default=None) – Linear solver of the Schur system; None gives
"pcg"forweights="frozen"and"rpbicgstab"(BiCGStab with right preconditioning, seeSchurSolverFull) forweights="midpoint", whose Schur complement is not symmetric (only"rpbicgstab"or"pbicgstab").precond ({"MassMatrixPreconditioner", None}, default="MassMatrixPreconditioner") – Preconditioner of the Schur system (of its velocity block).
solver_params (SolverParameters, default=None) – Linear-solver controls. The system is solved for a normalized right-hand side, so
tolis relative to the norm of the residual at \(t^n\).info (bool, default=False) – Log the information of the linear solver in every step.
relative_tol (bool, default=True) – Whether the tolerance of
solver_paramsis relative to the norm of the right-hand side (the residual at \(t^n\), normalized before the solve), or absolute, as in the non-variational propagators (e.g.ShearAlfvenPropagator). The energy is conserved up to the residual of the linear solve.schur_precond ({"MassMatrixPreconditioner", "StiffnessPreconditioner"}, default="MassMatrixPreconditioner") – Preconditioner of the Schur system: the one of
precond(velocity block), or the Kronecker (fast diagonalization) preconditioner of the whole shear-Alfven Schur operator (alfven_schur_preconditioner(), rebuilt when the time step changes). The latter needssolver="pcg"(or"rpbicgstab","pbicgstab"), tensor-product splines and the global commuting projectors; otherwiseprecondis used (with a warning).exb_correction (bool, default=True) – For
schur_precond="StiffnessPreconditioner": add the auxiliary space correction of the \(\mathbf E \times \mathbf B\) flows \(\mathbf B_0 \times \nabla \phi\).
- equilibrium_current(b0)[source]#
The equilibrium current \(\mathbf j_0 \in V^1_h\) of the second-order term, through the dual complex:
\[\mathbb M^1_{\textnormal{free}}\, \mathbf h_0 = \mathbb K \mathbf b_0 \,, \qquad \mathbf J_0 = \mathbb C\, \mathbf h_0 \,, \qquad \mathbb M^1_{\textnormal{free}}\, \mathbf j_0 = \mathbb K \mathbf J_0 \,,\]with the metric-free pairing \(\mathbb K_{ij} = \int_{\hat\Omega} \vec{\boldsymbol\Lambda}^1_i \cdot \vec{\boldsymbol\Lambda}^2_j \,\textrm d \boldsymbol\eta\) of 1-forms and 2-forms, all operators over the full spaces (without boundary conditions). \(\mathbf h_0\) is the field as a 1-form (Hodge star of the 2-form \(\mathbf b_0\)) and \(\mathbf J_0\) its curl, exactly divergence free. Collective: call on all ranks.
- class struphy.propagators.variational_linear_sound_evolve.VariationalLinearSoundEvolve(div_u: FEECVariable | None = None, u2: FEECVariable | None = None, pt3: FEECVariable | None = None)[source]#
Bases:
VariationalLinearEvolveFEEC discretization of the coupling of the velocity and the thermodynamic variable (sound waves) of the linear variational MHD models, with one linear solve per time step: find \(\mathbf u \in (H^1)^3\) and \(s \in L^2\) (the perturbation of \(q = \sqrt p\) or of \(p\)) such that, with the transport operator \(\mathcal S^\kappa_{\bar s}(\mathbf v) = \nabla \cdot (\bar s \mathbf v) + (\kappa - 1) \bar s\, \nabla \cdot \mathbf v\), depending on the option
model:"linear_q"(\(s = q\), \(\kappa = \gamma/2\)):\[\int_\Omega \partial_t (\rho_0 \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x - \int_\Omega \frac{2}{\gamma -1} \left(q\, \mathcal S^\kappa_{q_0}(\mathbf v) + q_0\, \mathcal S^\kappa_q(\mathbf v)\right)\textrm d \mathbf x = 0 \qquad \forall \, \mathbf v \in (H^1)^3\,, \qquad \partial_t q + \mathcal S^\kappa_{q_0}(\mathbf u) = 0 \,.\]"linear_p"(\(s = p\), \(\kappa = \gamma\)):\[\int_\Omega \partial_t (\rho_0 \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x - \int_\Omega \frac{1}{\gamma -1} \mathcal S^\kappa_p(\mathbf v)\,\textrm d \mathbf x = 0 \qquad \forall \, \mathbf v \in (H^1)^3\,, \qquad \partial_t p + \mathcal S^\kappa_{p_0}(\mathbf u) = 0 \,.\]
The magnetic field is not changed; together with
VariationalLinearAlfvenEvolvethis splits the linear MHD step into the shear-Alfvén/fast and the sound waves. The second-order term of the energy comes from the auxiliary variable \(\hat s\) (pt3), which, if given, is advanced with \(\partial_t \hat s + \mathcal S^\kappa_s(\mathbf u) = 0\); it enters the conserved energy as \(\int 2 q_0 \hat q / (\gamma - 1)\), respectively \(\int \hat p / (\gamma - 1)\).Discretization: with \(\mathbb S[\bar s] = \mathbb D \hat\Pi^2[\hat{\bar s} \vec{\boldsymbol\Lambda}^v] + (\kappa - 1) \hat\Pi^3[\hat{\bar s}/\sqrt g\, \vec{\boldsymbol\Lambda}^3] \mathbb D \mathcal U^v\) (
Pressure_transport_operator),"linear_q":\[\begin{split}\begin{align} &\mathbb M^v[\hat\rho_0] \frac{\mathbf u^{n+1} - \mathbf u^n}{\Delta t} - \frac{2}{\gamma - 1}\left(\mathbb S[q_0]^\top \mathbb M^3 \mathbf q^{n+\frac12} + \mathbb S[\mathbf q^n]^\top \mathbb M^3 \mathbf q_0\right) = 0 \,, \\[2mm] &\frac{\mathbf q^{n+1} - \mathbf q^n}{\Delta t} + \mathbb S[q_0]\, \mathbf u^{n+\frac12} = 0 \,, \qquad \frac{\hat{\mathbf q}^{n+1} - \hat{\mathbf q}^n}{\Delta t} + \mathbb S[\mathbf q^n]\, \mathbf u^{n+\frac12} = 0 \,, \end{align}\end{split}\]solved with one linear solve (
VariationalLinearEvolve); the Schur complement \(\mathbb M^v[\hat\rho_0] + \frac{\Delta t^2}{2(\gamma - 1)} \mathbb S[q_0]^\top \mathbb M^3 \mathbb S[q_0]\) is constant in time and the sound waves are implicit."linear_p": only the integral of \(\hat p\) enters the energy, and for \(\mathbf u \cdot \mathbf n = 0\) on the boundary \(\int \mathcal S^\gamma_p(\mathbf v) = (\gamma - 1) \int p\, \nabla \cdot \mathbf v\). The second-order term is therefore discretized in this weak form, with the divergence \(\mathbb D \mathcal U^v\) of the velocity and the mass matrix \(\mathbb M^3\):\[\begin{split}\begin{align} &\mathbb M^v[\hat\rho_0] \frac{\mathbf u^{n+1} - \mathbf u^n}{\Delta t} - (\mathbb D \mathcal U^v)^\top \mathbb M^3 \mathbf p^w = 0 \,, \qquad \frac{\mathbf p^{n+1} - \mathbf p^n}{\Delta t} + \mathbb S[p_0]\, \mathbf u^{n+\frac12} = 0 \,, \\[2mm] &\frac{\hat{\mathbf p}^{n+1} - \hat{\mathbf p}^n}{\Delta t} + (\gamma - 1)\, \frac{(\mathbb D \mathcal U^v \mathbf u^{n+\frac12}) \odot (\mathbb M^3 \mathbf p^w)} {\hat l^3(1)} = 0 \,, \end{align}\end{split}\]with \(\hat l^3(1)_{ijk} = \int_{\hat\Omega} \Lambda^3_{ijk} \,\textrm d \boldsymbol\eta\) and elementwise product and division: the auxiliary variable is advanced with \(\partial_t \hat p + (\gamma - 1) p\, \nabla \cdot \mathbf u = 0\) (lumped \(L^2\) projection), and its energy \(\hat l^3(1) \cdot \hat{\mathbf p} / (\gamma - 1)\) changes by exactly the work \(-\Delta t\, (\mathbb D \mathcal U^v \mathbf u^{n+\frac12})^\top \mathbb M^3 \mathbf p^w\) of the force. The pressure force is the one of
Magnetosonic.The option
weightssets \(\mathbf p^w\). With"midpoint"(default), \(\mathbf p^w = \mathbf p^{n+\frac12}\): one linear solve with the (not symmetric) Schur complement \(\mathbb M^v[\hat\rho_0] + \frac{\Delta t^2}{4} (\mathbb D \mathcal U^v)^\top \mathbb M^3 \mathbb S[p_0]\); the step is the implicit midpoint rule, energy conserving and second order in time, and the sound waves are implicit. With"frozen", \(\mathbf p^w = \mathbf p^n\): the velocity is advanced with one mass-matrix solve, then the pressure. The sound waves are then explicit and not stable for any \(\Delta t\): the amplitude of a sound wave of frequency \(\omega_s\) grows by \(|\lambda|^2 = 1 + (\Delta t\, \omega_s)^2 / 2\) per step (first order in time).
For
"linear_q"the weight \(\mathbf q^n\) at \(t^n\) is the same in the momentum equation and in the update of \(\hat{\mathbf q}\), which makes the step energy conserving; it is first order in time, stable for \(\gamma \leq 2\), with a numerical damping of order \(\Delta t\, \omega_s^2\) for \(\gamma < 2\) (onlyweights="frozen").The velocity can also be a 2-form, \(\mathbf u \in H(\textnormal{div})\) (velocity variable in
"Hdiv"space). The scheme is the same, with \(\mathbb M^v[\hat\rho_0] \to \mathbb M^{2,n}\), \(\mathcal U^v \to \mathbb I\) and \(\mathbb S[\bar s] = \mathbb D \hat\Pi^2[\hat{\bar s} \vec{\boldsymbol\Lambda}^2/\sqrt g] + (\kappa - 1) \hat\Pi^3[\hat{\bar s}/\sqrt g\, \vec{\boldsymbol\Lambda}^3] \mathbb D\); for"linear_p"the equilibrium operator is \(\mathbb D \mathcal S^2 + (\gamma - 1) \mathcal K^3 \mathbb D\) ofMagnetosonic. The energy is conserved in the same way.- class Variables[source]#
Bases:
objectContainer for variables advanced by
VariationalLinearSoundEvolve.- Variables:
u (FEECVariable) – Velocity variable in
"H1vec"or"Hdiv"space.s (FEECVariable) – Perturbation of \(q = \sqrt p\) (
"linear_q") or of the pressure ("linear_p") in"L2"space.
- class Options(model: Literal['linear_q', 'linear_p'] = 'linear_q', gamma: float = 1.6666666666666667, weights: Literal['frozen', 'midpoint'] | None = None, solver: Literal['pcg', 'pcg_sr', 'cg', 'pbicgstab', 'rpbicgstab'] | None = None, precond: Literal['MassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, info: bool = False, relative_tol: bool = True)[source]#
Bases:
OptionsBaseConfiguration options for
VariationalLinearSoundEvolve.- Parameters:
model ({"linear_q", "linear_p"}, default="linear_q") – Thermodynamic variable: \(q = \sqrt p\) or \(p\).
gamma (float, default=5/3) – Adiabatic index.
weights ({"frozen", "midpoint"}, default=None) – Weight of the second-order term: \(\mathbf s^n\) (first order in time) or \(\mathbf s^{n+1/2}\) (second order, only for
"linear_p"). None gives"midpoint"for"linear_p"and"frozen"for"linear_q".solver ({"pcg", "pcg_sr", "cg", "pbicgstab", "rpbicgstab"}, default=None) – Linear solver of the Schur system (
"linear_q", or"linear_p"with midpoint weights) or of the mass matrix ("linear_p", frozen weight, not"rpbicgstab"); None gives"pcg"forweights="frozen"and"rpbicgstab"(BiCGStab with right preconditioning, seeSchurSolverFull) forweights="midpoint", whose Schur complement is not symmetric (only"rpbicgstab"or"pbicgstab").precond ({"MassMatrixPreconditioner", None}, default="MassMatrixPreconditioner") – Preconditioner of the linear solve (of the velocity block).
solver_params (SolverParameters, default=None) – Linear-solver controls. The system is solved for a normalized right-hand side, so
tolis relative to its norm.info (bool, default=False) – Log the information of the linear solver in every step.
relative_tol (bool, default=True) – Whether the tolerance of
solver_paramsis relative to the norm of the right-hand side (the residual at \(t^n\), normalized before the solve), or absolute, as in the non-variational propagators (e.g.ShearAlfvenPropagator). The energy is conserved up to the residual of the linear solve.
- class struphy.propagators.variational_resistivity.VariationalResistivity(rho: FEECVariable | None = None, pt3: FEECVariable | None = None)[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(s \in L^2\) and \(\mathbf B \in H(\textrm{div})\) such that
\[\begin{split}&\int_\Omega \partial_t \mathbf B \cdot \mathbf v \,\textrm d \mathbf x + \int_\Omega (\eta + \eta_a(\mathbf x)) \nabla \times \mathbf B \cdot \nabla \times \mathbf v \,\textrm d \mathbf x = 0 \qquad \forall \, \mathbf v \in H(\textrm{div}) \,, \\[4mm] &\int_\Omega \frac{\partial \mathcal U}{\partial s} \partial_t s \, q\,\textrm d \mathbf x - \int_\Omega (\eta + \eta_a(\mathbf x)) |\nabla \times \mathbf B|^2 \, q \,\textrm d \mathbf x = 0 \qquad \forall \, q \in L^2\, \text{if using } s, \\[4mm] &\int_\Omega \frac{1}{\gamma - 1} \partial_t p \, q\,\textrm d \mathbf x - \int_\Omega (\eta + \eta_a(\mathbf x)) |\nabla \times \mathbf B|^2 \, q \,\textrm d \mathbf x = 0 \qquad \forall \, q \in L^2\, \text{if using } p.\end{split}\]With \(\eta_a(\mathbf x) = \eta_a |\nabla \times \mathbf B(\mathbf x)|\)
On the logical domain:
\[\begin{split}\begin{align} &\partial_t \hat{\boldsymbol B} - \eta \Delta \hat{\boldsymbol B} = 0 ~ , \\[2mm] &\int_{\hat{\Omega}} \partial_t (\hat{\rho} \mathcal U(\hat{\rho}, \hat{s})) \hat{w} \,\frac{1}{\sqrt g}\, \textrm d \boldsymbol \eta - \int_{\hat{\Omega}} (\eta + \eta_a(\mathbf \eta)) |DF^{-T}\tilde{\nabla} \times \hat{\boldsymbol B}|^2 \hat{w} \, \textrm d \boldsymbol \eta = 0 ~ , \text{if using } s, \\[2mm] &\int_{\hat{\Omega}} \partial_t (\frac{1}{\gamma -1} \hat{p} ) \hat{w} \,\frac{1}{\sqrt g}\, \textrm d \boldsymbol \eta - \int_{\hat{\Omega}} (\eta + \eta_a(\mathbf \eta)) |DF^{-T}\tilde{\nabla} \times \hat{\boldsymbol B}|^2 \hat{w} \, \textrm d \boldsymbol \eta = 0 ~, \text{if using } p. \end{align}\end{split}\]It is discretized as
\[\begin{split}\begin{align} &\frac{\mathbf B^{n+1}-\mathbf B^n}{\Delta t} + \, \mathbb C \mathbb M_1^{-1} (\eta M_1 + \eta_a M_1[|\nabla \times \mathbf B|]) M_1^{-1} \mathbb C^T \mathbb M_2 \mathbf B^{n+1} = 0 ~ , \\[2mm] &\frac{P^{3}(\rho e(s^{n+1})- P^{3}(\rho e(s^{n}))}{\Delta t} - P^3((\eta + \eta_a(\mathbf x)) DF^{-T} \tilde{\mathbb C} \frac{ \mathbf B^{n+1}+\mathbf B^n}{2} \cdot DF^{-T} \tilde{\mathbb C} \mathbf B^{n+1}) = 0 ~ , \text{if using } s, \\[2mm] &\frac{1}{\gamma -1}\frac{p^{n+1}- p^{n}}{\Delta t} - P^3((\eta + \eta_a(\mathbf x)) DF^{-T} \tilde{\mathbb C} \frac{ \mathbf B^{n+1}+\mathbf B^n}{2} \cdot DF^{-T} \tilde{\mathbb C} \mathbf B^{n+1}) = 0 ~ , \text{if using } p. \end{align}\end{split}\]where $P^3$ denotes the $L^2$ projection in the last space of the de Rham sequence.
- class Variables[source]#
Bases:
objectContainer for variables advanced by
VariationalResistivity.- Variables:
s (FEECVariable) – Thermodynamic scalar variable in
"L2"space.b (FEECVariable) – Magnetic-field variable in
"Hdiv"space.
- class Options(model: Literal['full', 'full_p', 'full_q', 'linear_p', 'linear_q', 'perturbative_q'] = 'full', gamma: float = 1.6666666666666667, solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, nonlin_solver: NonlinearSolverParameters | None = None, linearize_current: bool = False, eta: float = 0.0, eta_a: float = 0.0, fast: bool = False)[source]#
Bases:
OptionsBaseConfiguration options for
VariationalResistivity.- Parameters:
model ({"full", "full_p", "full_q", "linear_p", "linear_q", "perturbative_q"}, default="full") – Thermodynamic model variant.
gamma (float, default=5/3) – Adiabatic index.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Linear solver for implicit subproblems.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner used in linear solves.
solver_params (SolverParameters, default=None) – Linear-solver controls. The solves are done for normalized right-hand sides, so
tolis relative.nonlin_solver (NonlinearSolverParameters, default=None) – Nonlinear iteration controls. The iteration stops when the norm of the residual is below
toltimes the norm of the state at \(t^n\) if that is smaller than one (absolute otherwise), or when it stagnates (with a warning).linearize_current (bool, default=False) – If
True, linearize current terms around background fields.eta (float, default=0.0) – Physical resistivity coefficient.
eta_a (float, default=0.0) – Artificial-resistivity coefficient.
fast (bool, default=False) – If
Trueandmodel="linear_q", skip the thermodynamic (energy balance) update.
- class struphy.propagators.variational_sound_evolve.VariationalSoundEvolve(div_u: FEECVariable | None = None, u2: FEECVariable | None = None, qt3: FEECVariable | None = None)[source]#
Bases:
PropagatorFEEC discretization of the coupling of the velocity and \(q = \sqrt p\) (sound waves) of the variational MHD models in \(q\): find \(\mathbf u \in (H^1)^3\) and \(q \in L^2\) such that, with the transport operator \(\mathcal S_{\bar q}(\mathbf v) = \nabla \cdot (\bar q \mathbf v) + \left(\frac \gamma 2 - 1\right) \bar q\, \nabla \cdot \mathbf v\), depending on the option
model:"full_q"(full MHD):\[\int_\Omega \partial_t (\rho \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x - \int_\Omega \frac{2 q}{\gamma -1} \mathcal S_q(\mathbf v)\,\textrm d \mathbf x = 0 \qquad \forall \, \mathbf v \in (H^1)^3\,, \qquad \partial_t q + \mathcal S_q(\mathbf u) = 0 \,.\]"linear_q"(\(q\) is the perturbation of the equilibrium \(q_0\)):\[\int_\Omega \partial_t (\rho_0 \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x - \int_\Omega \frac{2}{\gamma -1} \left(q\, \mathcal S_{q_0}(\mathbf v) + q_0\, \mathcal S_q(\mathbf v)\right)\textrm d \mathbf x = 0\,, \qquad \partial_t q + \mathcal S_{q_0}(\mathbf u) = 0 \,.\]"perturbative_q"(\(q\) is the perturbation, all nonlinear terms are kept):\[\int_\Omega \partial_t (\rho \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x - \int_\Omega \frac{2}{\gamma -1} \left(q\, \mathcal S_{q_0 + q}(\mathbf v) + q_0\, \mathcal S_q(\mathbf v)\right)\textrm d \mathbf x = 0\,, \qquad \partial_t q + \mathcal S_{q_0 + q}(\mathbf u) = 0 \,.\]
The magnetic field is not changed; together with
VariationalAlfvenEvolvethis splits the coupled step ofVariationalQBEvolveinto the shear-Alfvén/fast and the sound waves. The second-order term of the linear and perturbative models comes from the auxiliary variable \(\hat q\) (qt3), which, if given, is advanced with \(\partial_t \hat q + \mathcal S_q(\mathbf u) = 0\); it enters the conserved energy as \(\int 2 q_0 \hat q / (\gamma - 1)\). For"perturbative_q", \(\rho\) is \(\rho_0\) plus the variable ofVariationalDensityEvolve.Discretization: with \(\mathbb S[\bar q] = \mathbb D \hat\Pi^2[\hat{\bar q} \vec{\boldsymbol\Lambda}^v] + (\gamma/2 - 1) \hat\Pi^3[\hat{\bar q}/\sqrt g\, \vec{\boldsymbol\Lambda}^3] \mathbb D \mathcal U^v\) (
Pressure_transport_operator) and, for"linear_q",\[\begin{split}\begin{align} &\mathbb M^v[\hat\rho_0] \frac{\mathbf u^{n+1} - \mathbf u^n}{\Delta t} - \frac{2}{\gamma - 1}\left(\mathbb S[q_0]^\top \mathbb M^3 \mathbf q^{n+\frac12} + \mathbb S[\bar{\mathbf q}]^\top \mathbb M^3 \mathbf q_0\right) = 0 \,, \\[2mm] &\frac{\mathbf q^{n+1} - \mathbf q^n}{\Delta t} + \mathbb S[q_0]\, \mathbf u^{n+\frac12} = 0 \,, \qquad \frac{\hat{\mathbf q}^{n+1} - \hat{\mathbf q}^n}{\Delta t} + \mathbb S[\bar{\mathbf q}]\, \mathbf u^{n+\frac12} = 0 \,, \end{align}\end{split}\]and correspondingly for the other models. The weight \(\bar{\mathbf q}\) is taken at the same time in all equations, which makes the step energy conserving: at \(t^n\) (first order in time, one Newton step solves the linear system), or at the midpoint \(t^{n+1/2}\) with the option
implicit_weights(second order in time, a Picard iteration). The Newton system is solved withSchurSolverFull; for"linear_q"its Schur complement is \(\mathbb M^v[\hat\rho_0] + \frac{\Delta t^2}{2(\gamma - 1)} \mathbb S[q_0]^\top \mathbb M^3 \mathbb S[q_0]\).- class Variables[source]#
Bases:
objectContainer for variables advanced by
VariationalSoundEvolve.- Variables:
u (FEECVariable) – Velocity variable in
"H1vec"space.q (FEECVariable) – Square root of the pressure in
"L2"space.
- class Options(model: Literal['full_q', 'linear_q', 'perturbative_q'] = 'full_q', gamma: float = 1.6666666666666667, solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, nonlin_solver: NonlinearSolverParameters | None = None, implicit_weights: bool | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
VariationalSoundEvolve.- Parameters:
model ({"full_q", "linear_q", "perturbative_q"}, default="full_q") – Model variant.
gamma (float, default=5/3) – Adiabatic index.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Linear solver of the Schur system.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner used in linear solves.
solver_params (SolverParameters, default=None) – Linear-solver controls. The Newton systems are solved for a normalized right-hand side, so
tolis relative to the norm of the residual.nonlin_solver (NonlinearSolverParameters, default=None) – Nonlinear/Picard iteration controls. The iteration stops when the norm of the residual is below
toltimes the norm of the state at \(t^n\) if that is smaller than one (absolute otherwise), or when it stagnates (with a warning).implicit_weights (bool, default=None) – If
True, the weight of the transport operators is taken at the midpoint \(t^{n+1/2}\) and updated in every Picard iteration (second order in time, several iterations per step); ifFalse, it is taken at \(t^n\) (first order in time, one linear solve). Both conserve the energy.NonemeansTruefor"linear_q"and"perturbative_q"andFalsefor"full_q".
- class struphy.propagators.variational_viscosity.VariationalViscosity(rho: FEECVariable | None = None, pt3: FEECVariable | None = None, divdiv_alpha: float = 0.0)[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(s \in L^2\) and \(\mathbf u \in (H^1)^3\) such that
\[\begin{split}&\int_\Omega \partial_t (\rho \mathbf u) \cdot \mathbf v\,\textrm d \mathbf x + \int_\Omega (\mu + \mu_a(\mathbf x)) \nabla \mathbf u : \nabla \mathbf v \,\textrm d \mathbf x = 0 \qquad \forall \, \mathbf v \in (H^1)^3 \,, \\[4mm] &\int_\Omega \frac{\partial \mathcal U}{\partial s} \partial_t s \, q \,\textrm d \mathbf x - \mu \int_\Omega |\nabla \mathbf u|^2 \, q \,\textrm d \mathbf x = 0 \qquad \forall \, q \in L^2\,\text{if using } s, \\[4mm] &\int_\Omega \frac{1}{\gamma - 1} \partial_t p \, q\,\textrm d \mathbf x - \mu \int_\Omega |\nabla \mathbf u|^2 \, q \,\textrm d \mathbf x = 0 \qquad \forall \, q \in L^2\, \text{if using } p.\end{split}\]With \(\mu_a(\mathbf x) = \mu_a |\nabla \mathbf u(\mathbf x)|\)
On the logical domain:
\[\begin{split}\begin{align} &\int_{\hat{\Omega}} \partial_t ( \hat{\rho}^3 \hat{\mathbf{u}}) \cdot G \hat{\mathbf{v}} \, \textrm d \boldsymbol \eta + \mu \int_{\hat{\Omega}} \nabla (DF \hat{\mathbf{u}}) : \nabla (DF \hat{\mathbf{v}}) \,\frac{1}{\sqrt g}\, \textrm d \boldsymbol \eta = 0 ~ , \\[2mm] &\int_{\hat{\Omega}} \partial_t (\hat{\rho} \hat{e}(\hat{\rho}, \hat{s})) \hat{w} \,\frac{1}{\sqrt g}\, \textrm d \boldsymbol \eta - \int_{\hat{\Omega}} (\mu + \mu_a(\boldsymbol \eta)) \nabla (DF \hat{\mathbf{u}}) : \nabla (DF \hat{\mathbf{u}}) \hat{w} \, \textrm d \boldsymbol \eta = 0 ~ , \text{if using } s, \\[2mm] &\int_{\hat{\Omega}} \partial_t (\frac{1}{\gamma -1} \hat{p} ) \hat{w} \,\frac{1}{\sqrt g}\, \textrm d \boldsymbol \eta - \int_{\hat{\Omega}} (\mu + \mu_a(\boldsymbol \eta)) \nabla (DF \hat{\mathbf{u}}) : \nabla (DF \hat{\mathbf{u}}) \hat{w} \, \textrm d \boldsymbol \eta = 0 ~, \text{if using } p. \end{align}\end{split}\]It is discretized as
\[\begin{split}\begin{align} &\mathbb M^v[\hat{\rho}_h^{n}] \frac{ \mathbf u^{n+1}-\mathbf u^n}{\Delta t} + \sum_\nu (\mathbb G \mathcal{X}^v_\nu)^T (\mu \mathbb M_0 + \mu_a \mathbb M_0[|\nabla u|] \mathbb G \mathcal{X}^v_\nu \mathbf u^{n+1} = 0 ~ , \\[2mm] &\frac{P^{3}(\hat{\rho}_h^{n}\mathcal U(\hat{\rho}_h^{n},\hat{s}_h^{n}))- P^{3}(\hat{\rho}_h^{n}\mathcal U(\hat{\rho}_h^{n},\hat{s}_h^{n+1}))}{\Delta t} - \mu P^3(\sum_\nu DF \mathcal{X}^v_\nu \frac{ \mathbf u^{n+1}+\mathbf u^n}{2} \cdot DF \mathcal{X}^v_\nu \mathbf u^{n+1}) = 0 ~ , \text{if using } s, \\[2mm] &\frac{1}{\gamma -1}\frac{p^{n+1}- p^{n}}{\Delta t} - \mu P^3(\sum_\nu DF \mathcal{X}^v_\nu \frac{ \mathbf u^{n+1}+\mathbf u^n}{2} \cdot DF \mathcal{X}^v_\nu \mathbf u^{n+1}) = 0 ~ , \text{if using } p. \end{align}\end{split}\]where $P^3$ denotes the $L^2$ projection in the last space of the de Rham sequence and the weights in \(\mathbb M_0[|\nabla u|]\) are given by
\[P^0(g \sqrt{\sum_\nu |(\mathbb G \mathcal{X}^v_\nu \mathbb u)^\top \vec{\boldsymbol \Lambda}^0 |^2]})^\top \vec{\boldsymbol \Lambda}^0 ~.\]With a div-div regularization (
divdiv_alpha\(> 0\)), the mass matrix \(\mathbb M^v[\hat\rho]\) is replaced by the metric \(\mathbb A[\hat\rho]\) of the regularized kinetic energy, seekinetic_metric().For
model="linear_p"the viscous heating, which is quadratic in the perturbation, goes into the auxiliary pressure \(\hat p\) (pt3) with the energy density \(\hat p/(\gamma - 1)\): the thermal energy increases by exactly the dissipated kinetic energy \(\Delta t\, \mu\, (\mathbf u^{n+1})^\top \mathbb K\, \mathbf u^{n+1/2}\), where \(\mathbb K\) is the stiffness matrix (seeLinearPressureHeating).Velocity in \(H(\textnormal{div})\) (
model="linear_p"and"full_p"): \(\mathbf u\) is a 2-form, which has no gradient. The viscous term is discretized with its divergence and curl,\[\int_\Omega \mu \left( \nabla \cdot \mathbf u\, \nabla \cdot \mathbf v + \nabla \times \mathbf u \cdot \nabla \times \mathbf v \right) \textrm d \mathbf x \,, \qquad \mathbb K = \mathbb D^\top \mathbb M^3 \mathbb D + \mathbb M^2 \mathbb C (\mathbb M^1)^{-1} \mathbb C^\top \mathbb M^2 \,,\]which equals \(\int \mu \nabla \mathbf u : \nabla \mathbf v\) up to boundary terms. The vorticity is the weak curl \(\boldsymbol\sigma = (\mathbb M^1)^{-1} \mathbb C^\top \mathbb M^2 \mathbf u \in V^1_h\),
\[\int_\Omega \boldsymbol\sigma \cdot \boldsymbol\tau \,\textrm d \mathbf x = \int_\Omega \mathbf u \cdot \nabla \times \boldsymbol\tau \,\textrm d \mathbf x \qquad \forall\, \boldsymbol\tau \in V^1_h \,,\]the same operator as the current in
VariationalResistivity. With the wall condition of \(V^1_h\) (zero tangential trace) this is the Hodge Laplacian of the de Rham complex: the essential condition is \(\mathbf u \cdot \mathbf n = 0\) and the natural one is perfect slip, \((\nabla \times \mathbf u) \times \mathbf n = 0\). The tangential velocity at the wall is free, as in the ideal model, and the viscous term converges to \(\int \mu |\nabla \times \mathbf u|^2\) as the grid is refined. (The strong curl of the interpolated 1-form \(\hat\Pi^1[G \hat{\mathbf u}^2/\sqrt g]\) would remove its tangential trace at the wall instead, a penalty of order \(\mu/h\) on the slip.) The heating uses the same \(\boldsymbol\sigma\), so the thermal energy increases by the dissipated kinetic energy up to the tolerance of the inner \(\mathbb M^1\) solve. The velocity mass matrix is \(\mathbb M^2_{\rho_0}\) (M2n, as in the ideal propagators of the linear model):\[(\mathbb M^2_{\rho_0} + \Delta t\, \mu\, \mathbb K)\, \mathbf u^{n+1} = \mathbb M^2_{\rho_0} \mathbf u^n \,.\]With
model="full_p"(the perturbative model), the mass matrix is that of the total density, \(\mathbb M^2_\rho\) with \(\rho = \rho_0 + \tilde\rho\) (VelocityMass,rhothe density perturbation), and the dissipated energy heats the pressure itself in the same way (its thermal energy \(\int (p_0 + \tilde p)/(\gamma - 1)\) is linear in \(\tilde p\)).The artificial viscosity \(\mu_a\), the damping
alphaand the div-div regularization are not available in this case.- class Variables[source]#
Bases:
objectContainer for variables advanced by
VariationalViscosity.- Variables:
s (FEECVariable) – Thermodynamic scalar variable in
"L2"space.u (FEECVariable) – Velocity variable in
"H1vec"space, or in"Hdiv"space (only formodel="linear_p"and"full_p").
- class Options(model: Literal['full', 'full_p', 'full_q', 'linear_p', 'linear_q', 'perturbative_q'] = 'full', gamma: float = 1.6666666666666667, solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, nonlin_solver: NonlinearSolverParameters | None = None, mu: float = 0.0, mu_a: float = 0.0, alpha: float = 0.0, fast: bool = False)[source]#
Bases:
OptionsBaseConfiguration options for
VariationalViscosity.- Parameters:
model ({"full", "full_p", "full_q", "linear_p", "linear_q", "perturbative_q"}, default="full") – Thermodynamic model variant.
gamma (float, default=5/3) – Adiabatic index.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Linear solver for implicit subproblems.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner used in linear solves.
solver_params (SolverParameters, default=None) – Linear-solver controls. The solves are done for normalized right-hand sides, so
tolis relative.nonlin_solver (NonlinearSolverParameters, default=None) – Nonlinear iteration controls. The iteration stops when the norm of the residual is below
toltimes the norm of the state at \(t^n\) if that is smaller than one (absolute otherwise), or when it stagnates (with a warning).mu (float, default=0.0) – Physical viscosity coefficient.
mu_a (float, default=0.0) – Artificial-viscosity coefficient.
alpha (float, default=0.0) – Optional linear damping/regularization parameter.
fast (bool, default=False) – If
Trueandmodel="linear_q", skip the thermodynamic (energy balance) update.
Cold plasma#
- class struphy.propagators.ohm_cold.OhmCold[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\mathbf j \in H(\textnormal{curl})\) and \(\mathbf E \in H(\textnormal{curl})\) such that
\[\begin{split}\int_\Omega \frac{1}{n_0} \frac{\partial \mathbf j}{\partial t} \cdot \mathbf F \,\textrm d \mathbf x &= \frac{1}{\varepsilon} \int_\Omega \mathbf E \cdot \mathbf F \,\textrm d \mathbf x \qquad \forall \,\mathbf F \in H(\textnormal{curl})\,, \\[2mm] -\frac{\partial \mathbf E}{\partial t} &= \frac{\alpha^2}{\varepsilon} \mathbf j \,,\end{split}\]Time discretization: Crank-Nicolson (implicit mid-point). System size reduction via
SchurSolver, such that\[\begin{split}\begin{bmatrix} \mathbf j^{n+1} - \mathbf j^n \\ \mathbf e^{n+1} - \mathbf e^n \end{bmatrix} = \frac{\Delta t}{2} \begin{bmatrix} 0 & \frac{1}{\varepsilon} \mathbb M_{1/n_0}^{-1} \\ - \frac{1}{\varepsilon} \mathbb M_{1/n_0}^{-1} & 0 \end{bmatrix} \begin{bmatrix} \alpha^2 \mathbb M_{1/n_0} (\mathbf j^{n+1} + \mathbf j^{n}) \\ \mathbb M_1 (\mathbf e^{n+1} + \mathbf e^{n}) \end{bmatrix} \,.\end{split}\]- class Variables[source]#
Bases:
objectContainer for variables advanced by
OhmCold.- Variables:
j (FEECVariable) – Current variable in
"Hcurl"space.e (FEECVariable) – Electric-field variable in
"Hcurl"space.
- class Options(solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
OhmCold.- Parameters:
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Symmetric iterative solver used by
SchurSolver.precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner applied to the weighted mass matrix
M1ninv.solver_params (SolverParameters, default=None) – Iterative-solver controls. If
None, defaults toSolverParameters().
- class struphy.propagators.jxb_cold.JxBCold[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\mathbf j \in H(\textnormal{curl})\) such that
\[\int_\Omega \frac{1}{n_0} \frac{\partial \mathbf j}{\partial t} \cdot \mathbf F \,\textrm d \mathbf x = \frac{1}{\varepsilon} \int_\Omega \frac{1}{n_0} (\mathbf j \times \mathbf B_0) \cdot \mathbf F \,\textrm d \mathbf x \qquad \forall \,\mathbf F \in H(\textnormal{curl})\,,\]Time discretization: Crank-Nicolson (implicit mid-point), such that
\[\mathbb M^1_{1/n_0} \left( \mathbf j^{n+1} - \mathbf j^n \right) = -\frac{\Delta t}{2} \frac{1}{\varepsilon} \mathbb M^1_{B_0/n_0} \left( \mathbf j^{n+1} + \mathbf j^n \right)\,,\]where \(\mathbb M^1_{1/n_0}\) is the 1-form mass matrix weighted with \(1/n_0\) (
M1ninv) and \(\mathbb M^1_{B_0/n_0}\) the skew-symmetric one of \((\mathbf B_0 \times \mathbf j) \cdot \mathbf F / n_0\) (M1Bninv), hence the minus sign.- class Variables[source]#
Bases:
objectContainer for variables advanced by
JxBCold.- Variables:
j (FEECVariable) – Current variable in
"Hcurl"space.
- class Options(solver: Literal['pbicgstab', 'bicgstab', 'gmres'] = 'gmres', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
JxBCold.- Parameters:
solver (LiteralOptions.OptsGenSolver, default="gmres") – Iterative solver used for the linear current update. The system matrix \(\mathbb M^1_{1/n_0} + \frac{\Delta t}{2 \varepsilon} \mathbb M^1_{B_0/n_0}\) is not symmetric (\(\mathbb M^1_{B_0/n_0}\) is skew-symmetric), so a solver for general matrices is needed.
"gmres"(without preconditioner) converges reliably;"pbicgstab"with the mass matrix preconditioner, which neglects the skew-symmetric part, has erratic iteration numbers when that part dominates (\(\Delta t \gtrsim \varepsilon\)), down to no convergence after roundoff-level changes of the data. The symmetric solvers of older parameter files ("pcg", …) are replaced by"gmres", with a warning.precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner built from the weighted mass matrix
M1ninv; only used bysolver="pbicgstab".solver_params (SolverParameters, default=None) – Iterative-solver controls. If
None, defaults toSolverParameters(maxiter=300):"gmres"does not restart and keeps one vector per iteration (plus a Hessenberg matrix of sizemaxiter**2), and needs about 15 to 40 iterations here.
Diffusion#
- class struphy.propagators.implicit_diffusion.ImplicitDiffusion(rho: FEECVariable | ParticlesToGrid | Callable | list | None = None, rho_coeffs: float | list | None = None, diagnostic: FEECVariable | None = None, delta_f_source: bool = False)[source]#
Bases:
PropagatorWeak, implicit discretization of the diffusion (or heat) equation (can be used as a Poisson solver too).
Find \(\phi \in H^1\) such that
\[\int_\Omega \psi\, n_0(\mathbf x)\frac{\partial \phi}{\partial t}\,\textrm d \mathbf x + \int_\Omega \nabla \psi^\top D_0(\mathbf x) \nabla \phi \,\textrm d \mathbf x = \sum_i \int_\Omega \psi\, \rho_i(\mathbf x)\,\textrm d \mathbf x \qquad \forall \ \psi \in H^1\,,\]where \(n_0, \rho_i:\Omega \to \mathbb R\) are real-valued functions and \(D_0:\Omega \to \mathbb R^{3\times 3}\) is a positive diffusion matrix. Boundary terms from integration by parts are assumed to vanish. The equation is discretized as
\[\left( \frac{\sigma_1}{\Delta t} \mathbb M^0_{n_0} + \mathbb G^\top \mathbb M^1_{D_0} \mathbb G \right)\, \boldsymbol\phi^{n+1} = \frac{\sigma_2}{\Delta t} \mathbb M^0_{n_0} \boldsymbol\phi^{n} + \frac{\sigma_3}{\Delta t} \sum_i(\Lambda^0, \rho_i )_{L^2}\,,\]where \(M^0_{n_0}\) and \(M^1_{D_0}\) are
WeightedMassOperatorsand \(\sigma_1, \sigma_2, \sigma_3 \in \mathbb R\) are artificial parameters that can be tuned to change the model (see Notes).- Parameters:
rho (FEECVariable or Callable or ParticlesToGrid or list, default=None) –
Source term(s) \(\rho_i\) on the right-hand side. Several entries can be combined by passing a
list; each is weighted by the corresponding entry ofrho_coeffs. Accepted entries are:None: zero source.FEECVariableinH1: its coefficients are mass-multiplied by \(M^0\) on the fly.Callable\(\rho_i(\mathbf x)\), projected toH1viaL2Projector.ParticlesToGrid, describing aparticle-to-grid deposition. An
AccumulatorVectoris built from it atallocate()and re-accumulated at every call. Itsaccum_spaceselects what is deposited:"H1": the kernel deposits a scalar density directly, used as-is."Hcurl": the kernel deposits a 1-form (vector) quantity \(\mathbf w\), e.g. \(n\mathbf u\); the propagator then applies the weak divergence \(-\mathbb G^\top\) to it before adding it to the right-hand side. This is used in the Chorin-projection scheme for example.
a
listcontaining any mix of the entries above.
rho_coeffs (float or list, default=None) – Multiplicative coefficient(s) for
rhosources. If a scalar is provided, it is applied to a single source. If a sequence is provided, it has one entry per entry ofrho(the neutralising background of a full-f"H1"accumulator gets the coefficient of its accumulator), or one entry per collected source (no background source is collected withdelta_f_source=True, the control variate or delta-f markers). IfNone, all coefficients default to1.0.diagnostic (FEECVariable, default=None) – If not None, updates at each call to the propagator, takes the value of the right-hand side. Otherwise does not provide diagnostic.
delta_f_source (bool, default=False) – Deposit particle sources with the delta-f weights \((f - f_0)/s\) also for full-f markers (without the control variate): the weights are updated before and the full-f weights restored after the deposit, and no background source is added. The Monte-Carlo noise is then that of \(f - f_0\) instead of \(f\), which is much smaller for small perturbations; meant for the initial Poisson solve of full-f models.
Note
The deposited particle density is \(f - f_0\) with the control variate (the weights are updated before each deposit), with delta-f markers (
DeltaFParticles6D) and withdelta_f_source=True. Otherwise (full-f), for"H1"accumulators the neutralizing background \(-n_0\) is added as a separate source with the coefficient of the particle source.Notes
\(\sigma_1=\sigma_2=0\) and \(\sigma_3 = \Delta t\): Poisson solver with a given charge density \(\sum_i\rho_i\).
\(\sigma_2=0\) and \(\sigma_1 = \sigma_3 = \Delta t\) : Poisson with adiabatic electrons.
\(\sigma_1=\sigma_2=1\) and \(\sigma_3 = 0\): Implicit heat equation.
- class Variables[source]#
Bases:
object- Variables:
phi (FEECVariable) – Scalar solution field in H^1 space.
- class Options(sigma_1: float = 1.0, sigma_2: float = 0.0, sigma_3: float = 1.0, divide_by_dt: bool = False, stab_mat: Literal['M0', 'M0ad', 'M0ad_withT', 'Id'] = 'M0', stab_average: str | None = None, diffusion_mat: Literal['M1', 'M1perp', None] = 'M1', x0: StencilVector | None = None, solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MultiGrid', 'StiffnessPreconditioner', 'MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] | None = None, precond_params: dict | None = None, multigrid: MultiGridOptions | None = None, solver_params: SolverParameters | None = None, enforce_compatibility: bool = False, filter_params: dict[PICVariable | SPHVariable, FilterParameters] | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
ImplicitDiffusion.- Parameters:
sigma_1 (float, default=1.0) –
Coefficient multiplying the stabilization/mass contribution on the left-hand side.
With
divide_by_dt=Trueit is interpreted assigma_1 / dtin the assembled linear system.sigma_2 (float, default=0.0) –
Coefficient multiplying the previous solution contribution
stab_mat * phi^non the right-hand side.With
divide_by_dt=Trueit is interpreted assigma_2 / dt.sigma_3 (float, default=1.0) –
Coefficient multiplying the source terms
rhoon the right-hand side.With
divide_by_dt=Trueit is interpreted assigma_3 / dt.divide_by_dt (bool, default=False) – If
True, dividesigma_1,sigma_2andsigma_3by the time step passed to__call__(dt)before assembling the system.stab_mat ({"M0", "M0ad", "M0ad_withT", "Id"}, default="M0") –
Stabilization operator used in the term weighted by
sigma_1andsigma_2."M0": standard weighted 0-form mass operator."M0ad": adiabatic-electron weighted 0-form mass operator (weight \(n_0\))."M0ad_withT": adiabatic-electron weighted 0-form mass operator (weight \(n_0/T_0\))."Id": identity operator.
stab_average (str, default=None) – If not None, the stabilization becomes
stab_mat @ (I - A), whereAis the average over the given logical directions, e.g."eta3"or"eta2 eta3", weighted withstab_mat(WeightedAverageProjection; e.g. the flux-surface average with the volume element for"M0"), such that the operator stays symmetric. Withprecond="MultiGrid", the average is re-built on every coarse level.diffusion_mat ({"M1", "M1perp", None} or WeightedMassOperator, default="M1") – Diffusion metric in the bilinear form
grad.T @ diffusion_mat @ grad. You can pass the name of a pre-built operator inmass_opsor a customWeightedMassOperatorcompatible with the codomain ofgrad. WithNonethere is no diffusion term and the system issigma_1 * stab_matonly (a weighted L2 projection); thenprecondcannot be"MultiGrid"or"StiffnessPreconditioner".x0 (StencilVector, default=None) – Initial guess for the iterative linear solver.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Name of the symmetric iterative solver passed to
psydac.linalg.solvers.inverse().precond (LiteralOptions.OptsDiffusionPrecond, default=None) –
Name of the preconditioner:
None: no preconditioner."MultiGrid": geometric multigrid V-cycle (MultiGridPreconditioner, requiressolver="pcg"or"pcg_sr")."MassMatrixPreconditioner": Kronecker approximation of the inverse of the 0-form mass matrixM0(seestruphy.feec.preconditioner)."PolarMassMatrixPreconditioner": 2D x 1D Kronecker approximation of the inverse ofM0on polar splines."StiffnessPreconditioner": Kronecker approximation of the inverse ofgrad.T @ diffusion_mat @ grad + sigma * stab_mat (I - A)(StiffnessPreconditionerwithmid=diffusion_mat,stab=stab_matandstab_average), withsigma = sigma_1(divided by dt ifdivide_by_dt=True). With polar splines, this is aPolarStiffnessPreconditioner.
precond_params (dict, default=None) – Keyword arguments passed to the constructor of the mass-matrix or stiffness preconditioner, e.g.
{"weights": "unit"}for"StiffnessPreconditioner"or{"dim_reduce": None}for"MassMatrixPreconditioner".multigrid (MultiGridOptions, default=None) – Options of the multigrid preconditioner (if
precond="MultiGrid"). IfNone, defaults toMultiGridOptions(). Setnullspace="constants"for (nearly) singular problems, e.g. a periodic Poisson problem with tinysigma_1; for an exactly singular system (lhs_is_singular) it is set automatically if leftNone.solver_params (SolverParameters, default=None) – Iterative-solver controls (for example
tol,maxiter,verbose,info,recycle). IfNone, defaults toSolverParameters().enforce_compatibility (bool, default=False) –
If
Trueand the diffusion operator is singular (no Dirichlet boundary conditions and no polar splines, i.e. the constant functions are in its kernel), the right-hand side is made compatible before the solve by removing its constant (“net charge”) component,\[\mathbf b \leftarrow \mathbf b - \frac{\mathbf 1^\top \mathbf b}{\mathbf 1^\top \mathbb M^0 \mathbf 1}\, \mathbb M^0 \mathbf 1\,,\]where \(\mathbf 1\) (all ones) is the coefficient vector of the constant function (partition of unity). This is equivalent to adding a uniform neutralizing background. It is meant for (nearly) singular problems where
sigma_1is only a small stabilization; thenphistays of order one instead of growing like(net charge) / sigma_1, which would destroy the accuracy ofgrad phi. Forstab_mat="M0"andsigma_2 = 0it changesphionly by a constant, for anysigma_1.If the diffusion operator is singular and
stab_averageis set (e.g. the quasi-neutrality equation with adiabatic electrons in a fully periodic domain), the stabilizationstab_mat (I - A)vanishes on the constants too, so the whole system is singular (seelhs_is_singular). Then the right-hand side is always made compatible, whateverenforce_compatibilitysays (the constant component of the averaged equation is the net charge, which must vanish), and the undetermined constant ofphiis fixed by \(\int \phi \, \textrm d \mathbf x = 0\) after the solve.filter_params (dict[PICVariable | SPHVariable, FilterParameters], default=None) – If not None, specifies a filter to the accumulation of a specific variable. Keyed by the
pic_variableof the correspondingParticlesToGridsource inrho.
Note
The accumulation kernel for a particle source is not configured here: it is carried by the
ParticlesToGridinstance passed as (part of)rho.
- quadratic_form(phi: StencilVector) float[source]#
The quadratic form of the system matrix (without
dt),\[\boldsymbol \phi^\top \left( \sigma_1\, \mathbb S (\mathbb I - \mathbb A) + \mathbb G^\top \mathbb D\, \mathbb G \right) \boldsymbol \phi\,,\]with
stab_mat\(\mathbb S\), the average \(\mathbb A\) (stab_average) anddiffusion_mat\(\mathbb D\), e.g. twice the field energy of the quasi-neutrality equation. A collective operation: call it on all ranks.
- property operator_is_singular: bool#
True if the diffusion operator
grad.T @ diffusion_mat @ gradis known to be singular with the constant functions (coefficient vector of all ones) in its kernel: no Dirichlet boundary conditions and tensor-product splines. Polar splines are conservatively treated as regular.
- property lhs_is_singular: bool#
True if the whole system matrix
sigma_1 * stab_mat (I - A) + grad.T @ diffusion_mat @ gradis singular, with the constants in its kernel: the diffusion operator is singular (seeoperator_is_singular) and the stabilization removes the averageA(stab_average), which reproduces the constants. Then the right-hand side is made compatible and the mean ofphiis fixed.The kernel can be larger than the constants if
diffusion_matis degenerate ("M1perp","M1gyro") and \(\mathbf B_0\) is not along the averaged directions (e.g.stab_average="eta3"with \(\mathbf B_0\) along \(\eta_1\)); such a setup stays singular. Polar splines are treated as regular, seeoperator_is_singular.
- property sources: list[StencilVector | FEECVariable | AccumulatorVector]#
Right-hand side of the equation (sources).
- property coeffs: list[float]#
Same length as self.sources. Coefficients multiplied with sources before solve (default is 1.0).
- property x0#
feectools.linalg.stencil.StencilVector or struphy.polar.basic.PolarVector. First guess of the iterative solver.
Only particle variables are updated.
- class struphy.propagators.push_deterministic_diffusion.PushDeterministicDiffusion[source]#
Bases:
PropagatorFor each marker \(p\), solves
\[\frac{\textnormal d \mathbf x_p(t)}{\textnormal d t} = - D \, \frac{\nabla u}{ u}\mathbf (\mathbf x_p(t))\,,\]in logical space given by \(\mathbf x = F(\boldsymbol \eta)\):
\[\frac{\textnormal d \boldsymbol \eta_p(t)}{\textnormal d t} = - G\, D \, \frac{\nabla \Pi^0_{L^2}u_h}{\Pi^0_{L^2} u_h}\mathbf (\boldsymbol \eta_p(t))\,, \qquad [\Pi^0_{L^2, ijk} u_h](\boldsymbol \eta_p) = \frac 1N \sum_{p} w_p \boldsymbol \Lambda^0_{ijk}(\boldsymbol \eta_p)\,,\]where \(D>0\) is a positive diffusion coefficient.
Available algorithms:
Explicit from
ButcherTableau
- class Variables[source]#
Bases:
objectContainer for variables advanced by
PushDeterministicDiffusion.- Variables:
var (PICVariable) – Particle variable in
"Particles3D"space whose marker positions are advanced.
- class Options(butcher: ButcherTableau | None = None, bc_type: tuple = ('periodic', 'periodic', 'periodic'), diff_coeff: float = 1.0)[source]#
Bases:
OptionsBaseConfiguration options for
PushDeterministicDiffusion.- Parameters:
butcher (ButcherTableau, default=None) – Butcher tableau used for explicit Runge-Kutta integration. If
None, defaults toButcherTableau().bc_type (tuple, default=("periodic", "periodic", "periodic")) – Boundary-condition types per logical coordinate.
diff_coeff (float, default=1.0) – Positive deterministic diffusion coefficient \(D\).
Only particle variables are updated.
- class struphy.propagators.push_random_diffusion.PushRandomDiffusion[source]#
Bases:
PropagatorFor each marker \(p\), solves
\[\textnormal d \mathbf x_p(t) = \sqrt{2 D} \, \textnormal d \mathbf B_{t}\,,\]where \(D>0\) is a positive diffusion coefficient and \(\textnormal d \mathbf B_{t}\) is a Wiener process,
\[\mathbf B_{t + \Delta t} - \mathbf B_{t} = \sqrt{\Delta t} \,\mathcal N(0;1)\,,\]with \(\mathcal N(0;1)\) denoting the standard normal distribution with mean zero and variance one.
Available algorithms:
forward_euler(1st order)
- class Variables[source]#
Bases:
objectContainer for variables advanced by
PushRandomDiffusion.- Variables:
var (PICVariable) – Particle variable in
"Particles3D"space whose marker positions are advanced.
- class Options(butcher: ButcherTableau | None = None, bc_type: tuple = ('periodic', 'periodic', 'periodic'), diff_coeff: float = 1.0)[source]#
Bases:
OptionsBaseConfiguration options for
PushRandomDiffusion.- Parameters:
butcher (ButcherTableau, default=None) – Butcher tableau used for explicit integration. If
None, defaults toButcherTableau("forward_euler")(Euler-Maruyama). The noise is additive and constant within a step, so multi-stage tableaux give statistically the same result at a higher cost.bc_type (tuple, default=("periodic", "periodic", "periodic")) – Boundary-condition types per logical coordinate.
diff_coeff (float, default=1.0) – Positive diffusion coefficient \(D\) used in the stochastic increment.
Particle pushing#
Only particle variables are updated.
- class struphy.propagators.push_eta.PushEta[source]#
Bases:
PropagatorFor each marker \(p\), solves
\[\frac{\textnormal d \mathbf{x}_p(t)}{\textnormal d t} = \mathbf{v}_p\,,\]in logical space given by \(\mathbf{x} = F(\boldsymbol{\eta})\):
\[\frac{\textnormal d \boldsymbol{\eta}_p(t)}{\textnormal d t} = DF^{-1}(\boldsymbol{\eta}_p(t)) \,\mathbf{v}_p\,,\]for constant \(\mathbf{v}_p\). Available algorithms:
Explicit RK from
ButcherTableau
- class Variables[source]#
Bases:
objectContainer for variables advanced by
PushEta.- Variables:
var (PICVariable or SPHVariable) – Particle variable whose marker positions are advanced.
- class Options(butcher: ButcherTableau | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
PushEta.- Parameters:
butcher (ButcherTableau, default=None) – Butcher tableau used for explicit Runge-Kutta marker pushing. If
None, defaults toButcherTableau().
Only particle variables are updated.
- class struphy.propagators.push_eta_pc.PushEtaPC(u_tilde: FEECVariable | None = None)[source]#
Bases:
PropagatorFor each marker \(p\), solves
\[\frac{\textnormal d \mathbf x_p(t)}{\textnormal d t} = \mathbf v_p + \mathbf U (\mathbf x_p(t))\,,\]for constant \(\mathbf v_p\) and \(\mathbf U\) in logical space given by \(\mathbf x = F(\boldsymbol \eta)\):
\[\frac{\textnormal d \boldsymbol \eta_p(t)}{\textnormal d t} = DF^{-1}(\boldsymbol \eta_p(t)) \,\mathbf v_p + \textnormal{vec}(\hat{\mathbf U}) \,,\]where
\[\textnormal{vec}( \hat{\mathbf U}^{1}) = G^{-1}\hat{\mathbf U}^{1}\,,\qquad \textnormal{vec}( \hat{\mathbf U}^{2}) = \frac{\hat{\mathbf U}^{2}}{\sqrt g}\,, \qquad \textnormal{vec}( \hat{\mathbf U}) = \hat{\mathbf U}\,.\]Available algorithms:
rk4(4th order, default)forward_euler(1st order)heun2(2nd order)rk2(2nd order)heun3(3rd order)
- class Variables[source]#
Bases:
objectContainer for variables advanced by
PushEtaPC.- Variables:
var (PICVariable or SPHVariable) – Particle variable whose marker positions are advanced.
- class Options(butcher: ButcherTableau | None = None, use_perp_model: bool = True, u_space: Literal['Hcurl', 'Hdiv', 'H1vec'] = 'Hdiv')[source]#
Bases:
OptionsBaseConfiguration options for
PushEtaPC.- Parameters:
butcher (ButcherTableau, default=None) – Butcher tableau used for explicit Runge-Kutta marker pushing. If
None, defaults toButcherTableau().use_perp_model (bool, default=True) – Flag forwarded to the particle kernel to select the perpendicular model formulation.
u_space (LiteralOptions.OptsVecSpace, default="Hdiv") – FEEC space used to interpret
u_tildein the pusher kernel.
Only particle variables are updated.
- class struphy.propagators.push_v_in_force_field.PushVinForceField(force_field: FEECVariable | tuple[Callable] | None = None, potential: FEECVariable | Callable | None = None, magnetic_moment: bool = False)[source]#
Bases:
PropagatorPush the velocities according to
\[\frac{\text{d} \mathbf{v}_p}{\text{d} t} = \frac{1}{\varepsilon}\mathbf{F}(\mathbf{x}_p) \,,\]where \(\varepsilon \in \mathbb R\) is a constant species parameter. In logical coordinates, given by \(\mathbf x = F(\boldsymbol \eta)\):
\[\frac{\text{d} \mathbf{v}_p}{\text{d} t} = \frac{1}{\varepsilon}DF^{-\top}\hat{\mathbf F}^1(\boldsymbol \eta_p) \,,\]which is solved analytically. \(\mathbf F\) can optionally be defined through a potential, \(\mathbf F = - \nabla \phi\).
With
magnetic_moment=True, each marker carries a magnetic moment \(\mu_p\) (classical Pauli particle, seePauliParticles6D) and the force is augmented by the magnetic-moment force of the equilibrium field,\[\frac{\text{d} \mathbf{v}_p}{\text{d} t} = \frac{1}{\varepsilon}\mathbf{F}(\mathbf{x}_p) - \mu_p \nabla |\mathbf B_0|(\mathbf{x}_p) \,,\]i.e. \(\mathbf F = -\nabla(\phi + \varepsilon \mu_p |\mathbf B_0|)\), where \(\nabla |\mathbf B_0|\) is the projected 1-form
gradB1of the equilibrium (the same as in the guiding-center pushers).Without force field, potential and magnetic moment the propagator does nothing.
- class Variables[source]#
Bases:
objectContainer for variables advanced by
PushVinForceField.- Variables:
var (PICVariable or SPHVariable) – Particle variable whose velocities are advanced.
- class Options[source]#
Bases:
OptionsBaseConfiguration options for
PushVinForceField.
Only particle variables are updated.
- class struphy.propagators.push_vxb.PushVxB(b2_var: FEECVariable | None = None)[source]#
Bases:
PropagatorFor each marker \(p\), solves
\[\frac{\textnormal d \mathbf{v}_p(t)}{\textnormal d t} = \frac{1}{\varepsilon} \, \mathbf{v}_p(t) \times (\mathbf{B} + \mathbf{B}_{\text{add}}) \,,\]where \(\varepsilon = 1/(\hat{\Omega}_c \hat t)\) is a constant scaling factor, and for rotation vector \(\mathbf{B}\) and optional, additional fixed rotation vector \(\mathbf{B}_{\text{add}}\), both given as a 2-form:
\[\mathbf{B} = \frac{DF\, \hat{\mathbf{B}}^2}{\sqrt{g}}\,.\]Available algorithms:
analyticimplicit.
Without magnetic field (no equilibrium field or a zero one, and no
b2_var) the propagator does nothing.- class Variables[source]#
Bases:
objectContainer for variables advanced by
PushVxB.- Variables:
ions (PICVariable or SPHVariable) – Particle variable whose velocities are advanced.
Only particle variables are updated.
- class struphy.propagators.push_guiding_center_bx_estar.PushGuidingCenterBxEstar(phi: FEECVariable | None = None, b_tilde: FEECVariable | None = None, e_field: FEECVariable | None = None)[source]#
Bases:
PropagatorFor each marker \(p\), solves
\[\frac{\textnormal d \mathbf{X}_p(t)}{\textnormal d t} = \frac{\mathbf{E}^* \times \mathbf{b}_0}{B_{\parallel}^*} (\mathbf{X}_p(t)) \,,\]where
\[\mathbf E^* = -\nabla \phi - \varepsilon \mu_p \nabla |\mathbf B|\,,\qquad \mathbf B^* = \mathbf B + \varepsilon v_\parallel \nabla \times \mathbf{b}_0\,,\qquad B^*_\parallel = \mathbf B^* \cdot \mathbf{b}_0\,,\]Here, \(\mathbf B = \mathbf{B}_0 + \tilde{\mathbf B}\) can be the full magnetic field (equilibrium + perturbation). The electric potential \(\phi\) and/or the magnetic perturbation \(\tilde{\mathbf B}\) can be ignored by passing
None. Instead of the potential, an electric field \(\mathbf E \in H(\textnormal{curl})\) can be passed (e_field, explicit algorithm); then \(\mathbf E^* = \mathbf E - \varepsilon \mu_p \nabla |\mathbf B|\) (gauge-invariant electromagnetic formulation). In logical space this is given by \(\mathbf X = F(\boldsymbol \eta)\):\[\frac{\textnormal d \boldsymbol{\eta}_p(t)}{\textnormal d t} = \frac{\hat{\mathbf E}^{*1} \times \hat{\mathbf b}^1_0}{\sqrt{g}\,\hat{B}_{\parallel}^{*}} (\boldsymbol \eta_p(t)) \,.\]Available algorithms:
Explicit from
ButcherTableaupush_gc_bxEstar_discrete_gradient_1st_order()push_gc_bxEstar_dg_1st_order_newton()push_gc_bxEstar_discrete_gradient_2nd_order()
- class Variables[source]#
Bases:
objectContainer for variables advanced by
PushGuidingCenterBxEstar.- Variables:
ions (PICVariable) – Guiding-center particle variable in
"Particles5D"space.
- class Options(evaluate_e_field: bool = False, algo: Literal['discrete_gradient_2nd_order', 'discrete_gradient_1st_order', 'discrete_gradient_1st_order_newton', 'explicit'] = 'discrete_gradient_1st_order', butcher: ButcherTableau | None = None, maxiter: int = 20, tol: float = 1e-07, mpi_sort: Literal['each', 'last', None] = 'each')[source]#
Bases:
OptionsBaseConfiguration options for
PushGuidingCenterBxEstar.- Parameters:
evaluate_e_field (bool, default=False) – If
True, evaluate and include electric-field contributions in drift-kinetic kernels.algo ({"discrete_gradient_2nd_order", "discrete_gradient_1st_order", "discrete_gradient_1st_order_newton", "explicit"}, default="discrete_gradient_1st_order") – Guiding-center pushing algorithm.
butcher (ButcherTableau, default=None) – Butcher tableau used in explicit mode. If
Noneandalgo="explicit", defaults toButcherTableau().maxiter (int, default=20) – Maximum number of fixed-point or Newton iterations in discrete-gradient modes.
tol (float, default=1e-7) – Convergence tolerance for iterative discrete-gradient updates.
mpi_sort (LiteralOptions.OptsMPIsort, default="each") – MPI sorting policy for particle exchange.
Only particle variables are updated.
- class struphy.propagators.push_guiding_center_parallel.PushGuidingCenterParallel(phi: FEECVariable | None = None, b_tilde: FEECVariable | None = None, e_field: FEECVariable | None = None)[source]#
Bases:
PropagatorFor each marker \(p\), solves
\[\begin{split}\left\{ \begin{aligned} \frac{\textnormal d \mathbf X_p(t)}{\textnormal d t} &= v_{\parallel,p}(t) \frac{\mathbf B^*}{B^*_\parallel}(\mathbf X_p(t)) \,, \\ \frac{\textnormal d v_{\parallel,p}(t)}{\textnormal d t} &= \frac{1}{\varepsilon} \frac{\mathbf B^*}{B^*_\parallel} \cdot \mathbf E^* (\mathbf X_p(t)) \,, \end{aligned} \right.\end{split}\]where
\[\mathbf E^* = -\nabla \phi - \varepsilon \mu_p \nabla |\mathbf B|\,,\qquad \mathbf B^* = \mathbf B + \varepsilon v_\parallel \nabla \times \mathbf b_0\,,\qquad B^*_\parallel = \mathbf B^* \cdot \mathbf b_0\,,\]where \(\mathbf B = \mathbf B_0 + \tilde{\mathbf B}\) can be the full magnetic field (equilibrium + perturbation). The electric potential
phiand/or the magnetic perturbationb_tildecan be ignored by passingNone. Instead of the potential, an electric field \(\mathbf E \in H(\textnormal{curl})\) can be passed (e_field, explicit algorithm); then \(\mathbf E^* = \mathbf E - \varepsilon \mu_p \nabla |\mathbf B|\) (gauge-invariant electromagnetic formulation). In logical space this is given by \(\mathbf X = F(\boldsymbol \eta)\):\[\begin{split}\left\{ \begin{aligned} \frac{\textnormal d \boldsymbol \eta_p(t)}{\textnormal d t} &= v_{\parallel,p}(t) \frac{\hat{\mathbf B}^{*2}}{\hat B^{*3}_\parallel}(\boldsymbol \eta_p(t)) \,, \\ \frac{\textnormal d v_{\parallel,p}(t)}{\textnormal d t} &= \frac{1}{\varepsilon} \frac{\hat{\mathbf B}^{*2}}{\hat B^{*3}_\parallel} \cdot \hat{\mathbf E}^{*1} (\boldsymbol \eta_p(t)) \,. \end{aligned} \right.\end{split}\]Available algorithms:
Explicit from
ButcherTableaupush_gc_Bstar_discrete_gradient_1st_order()push_gc_Bstar_discrete_gradient_1st_order_newton()push_gc_Bstar_discrete_gradient_2nd_order()
- class Variables[source]#
Bases:
objectContainer for variables advanced by
PushGuidingCenterParallel.- Variables:
ions (PICVariable) – Guiding-center particle variable in
"Particles5D"space.
- class Options(evaluate_e_field: bool = False, algo: Literal['discrete_gradient_2nd_order', 'discrete_gradient_1st_order', 'discrete_gradient_1st_order_newton', 'explicit'] = 'discrete_gradient_1st_order', butcher: ButcherTableau | None = None, maxiter: int = 20, tol: float = 1e-07, mpi_sort: Literal['each', 'last', None] = 'each')[source]#
Bases:
OptionsBaseConfiguration options for
PushGuidingCenterParallel.- Parameters:
evaluate_e_field (bool, default=False) – If
True, evaluate and include electric-field contributions in drift-kinetic kernels.algo ({"discrete_gradient_2nd_order", "discrete_gradient_1st_order", "discrete_gradient_1st_order_newton", "explicit"}, default="discrete_gradient_1st_order") – Guiding-center pushing algorithm.
butcher (ButcherTableau, default=None) – Butcher tableau used in explicit mode. If
Noneandalgo="explicit", defaults toButcherTableau().maxiter (int, default=20) – Maximum number of fixed-point or Newton iterations in discrete-gradient modes.
tol (float, default=1e-7) – Convergence tolerance for iterative discrete-gradient updates.
mpi_sort (LiteralOptions.OptsMPIsort, default="each") – MPI sorting policy for particle exchange.
Weights updates (delta-f schemes)#
Particle and FEEC variables are updated.
- class struphy.propagators.efield_weights_coupling.EfieldWeightsCoupling[source]#
Bases:
PropagatorSolves the following substep
\[\begin{split}\begin{align} & \frac{\partial \mathbf{E}}{\partial t} = - \frac{\alpha^2}{\varepsilon} \int \mathbf{v} f_1 \, \text{d} \mathbf{v} \, , \\[2mm] & \frac{\partial f_1}{\partial t} = \frac{1}{v_{\text{th}}^2 \varepsilon} \, \mathbf{E} \cdot \mathbf{v} f_0 \, , \end{align}\end{split}\]which after discretization and in curvilinear coordinates reads
\[\begin{split}\frac{\text{d}}{\text{d} t} w_p &= \frac 1N \frac{f_{0,p}}{s_{0, p}} \frac{1}{v_{\text{th}}^2 \varepsilon} \left[ DF^{-T} (\mathbb{\Lambda}^1)^T \mathbf{e} \right] \cdot \mathbf{v}_p \, , \\[2mm] \frac{\text{d}}{\text{d} t} \mathbb{M}_1 \mathbf{e} &= - \frac{\alpha^2}{\varepsilon} \sum_p w_p \mathbb{\Lambda}^1 \cdot \left( DF^{-1} \mathbf{v}_p \right) \,,\end{split}\]with the weights \(w_p = f_{1,p} / (N s_{0,p})\).
This is solved using the Crank-Nicolson method
\[\begin{split}\begin{bmatrix} \mathbb{M}_1 \left( \mathbf{e}^{n+1} - \mathbf{e}^n \right) \mathbf{W}^{n+1} - \mathbf{W}^n \end{bmatrix} = \frac{\Delta t}{2} \begin{bmatrix} 0 & - \mathbb{E} \\ \mathbb{W} & 0 \end{bmatrix} \begin{bmatrix} \mathbf{e}^{n+1} + \mathbf{e}^n \\ \mathbf{V}^{n+1} + \mathbf{V}^n \end{bmatrix} \, ,\end{split}\]where
\[\begin{split}\mathbb{E} &= \frac{\alpha^2}{\varepsilon} \mathbb{\Lambda}^1 \cdot \left( DF^{-1} \mathbf{v}_p \right) \, , \\[2mm] \mathbb{W} &= \frac 1N \frac{f_{0,p}}{s_{0,p}} \frac{1}{v_\text{th}^2 \varepsilon} \left( DF^{-1} \mathbf{v}_p \right) \cdot \left(\mathbb{\Lambda}^1\right)^T \, ,\end{split}\]based on the
SchurSolver.The accumulation matrix and vector assembled in
Accumulatorare\[BC = \mathbb{E} \mathbb{W} \, , \qquad By_n = \mathbb{E} \mathbf{W} \,.\]- class Variables[source]#
Bases:
objectContainer for variables advanced by
EfieldWeightsCoupling.- Variables:
e (FEECVariable) – Electric-field variable in
"Hcurl"space.ions (PICVariable) – Particle variable in
"Particles6D"or"DeltaFParticles6D"space.
- class Options(alpha: float = 1.0, kappa: float = 1.0, solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, matrix_free: bool = False)[source]#
Bases:
OptionsBaseConfiguration options for
EfieldWeightsCoupling.- Parameters:
alpha (float, default=1.0) – Coupling scaling factor used in the electric-field equation.
kappa (float, default=1.0) – Coupling scaling factor used in the particle-weight update.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Symmetric iterative solver used by
SchurSolver.precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner applied to the electric-field mass matrix block.
solver_params (SolverParameters, default=None) – Iterative-solver controls. If
None, defaults toSolverParameters().matrix_free (bool, default=False) – Apply the particle matrix matrix-free, with its rank-one filling stored per marker (
MarkerOperator), instead of assembling it: one spline evaluation and one deposition per marker and solver iteration instead of the products of all pairs of basis functions per marker. Needs 4 auxiliary marker columns. Ignored on the CuPy backend.
Vlasov-Maxwell coupling#
Particle and FEEC variables are updated.
- class struphy.propagators.vlasov_ampere_coupling.VlasovAmpereCoupling[source]#
Bases:
PropagatorPIC-FEEC discretization of the following equations: find \(\mathbf{E} \in H(\textnormal{curl})\) and \(f\) such that
\[\begin{split}-\int_{\Omega} \frac{\partial \mathbf{E}}{\partial t} \cdot \mathbf{F}\,\textrm d \mathbf x &= \frac{\alpha^2}{\varepsilon} \int_{\Omega} \int_{\mathbb{R}^3} f \mathbf{v} \cdot \mathbf{F} \, \text{d}^3 \mathbf{v} \,\textrm d \mathbf x \qquad \forall \, \mathbf{F} \in H(\textnormal{curl}) \\[2mm] \frac{\partial f}{\partial t} + \frac{1}{\varepsilon}\, \mathbf{E} \cdot \frac{\partial f}{\partial \mathbf{v}} &= 0 .\end{split}\]With \(f\) approximated by \(N\) markers with positions \(\boldsymbol \eta_p\) (logical, fixed in this step), velocities \(\mathbf v_p\) and weights \(w_p\) (including the factor \(1/N\)), and the FE coefficients \(\mathbf e \in \mathbb R^{N_1}\) of \(\mathbf E \in V^1_h\), the semi-discrete system reads
\[\mathbb M_1 \dot{\mathbf e} = - \frac{\alpha^2}{\varepsilon} \, (\mathbf \Lambda^1)^\top \, \overline{DF^{-1}} \, \mathbb W \, \mathbf V \,, \qquad \dot{\mathbf V} = \frac{1}{\varepsilon} \, \overline{DF^{-\top}} \, \mathbf \Lambda^1 \, \mathbf e\,,\]where \(\mathbf V = (\mathbf v_p)_p \in \mathbb R^{3N}\), \(\mathbb W = \textnormal{diag}(w_p)\) (acting on each velocity component), \(\mathbf \Lambda^1 \in \mathbb R^{3N \times N_1}\) holds the \(V^1_h\) basis functions evaluated at the markers, \((\mathbf \Lambda^1 \mathbf e)_p = \hat{\mathbf E}^1(\boldsymbol \eta_p)\), and \(\overline{DF^{-1}}\) is block-diagonal with the inverse Jacobian matrices \(DF^{-1}(\boldsymbol \eta_p)\). It conserves the energy \(\frac 12 \mathbf e^\top \mathbb M_1 \mathbf e + \frac{\alpha^2}{2} \mathbf V^\top \mathbb W \mathbf V\).
Time discretization: Crank-Nicolson (implicit mid-point),
\[\mathbb M_1 \frac{\mathbf e^{n+1} - \mathbf e^n}{\Delta t} = - \frac{\alpha^2}{\varepsilon} \, (\mathbf \Lambda^1)^\top \, \overline{DF^{-1}} \, \mathbb W \, \frac{\mathbf V^{n+1} + \mathbf V^n}{2}\,, \qquad \frac{\mathbf V^{n+1} - \mathbf V^n}{\Delta t} = \frac{1}{\varepsilon} \, \overline{DF^{-\top}} \, \mathbf \Lambda^1 \, \frac{\mathbf e^{n+1} + \mathbf e^n}{2}\,,\]which conserves the energy exactly (up to the solver tolerance; for constant weights, i.e. without control variate). The velocities are eliminated by
SchurSolverwith \(x = \mathbf e\), \(y = \mathbf V\), \(A = \mathbb M_1\), \(B = \frac{\alpha^2}{2 \varepsilon} (\mathbf \Lambda^1)^\top \overline{DF^{-1}} \mathbb W\) and \(C = - \frac{1}{2 \varepsilon} \overline{DF^{-\top}} \mathbf \Lambda^1\), which gives\[\left(\mathbb M_1 + \frac{\Delta t^2 \alpha^2}{4 \varepsilon^2} \mathbb A\right) \mathbf e^{n+1} = \left(\mathbb M_1 - \frac{\Delta t^2 \alpha^2}{4 \varepsilon^2} \mathbb A\right) \mathbf e^n - \Delta t \frac{\alpha^2}{\varepsilon} \, \mathbf j\,,\]with the accumulated matrix and vector (
Accumulator, kernelvlasov_maxwell())\[\mathbb A = (\mathbf \Lambda^1)^\top \, \overline{DF^{-1}} \, \mathbb W \, \overline{DF^{-\top}} \, \mathbf \Lambda^1 = \sum_p w_p \, \mathbf \Lambda^1_p{}^\top G^{-1}(\boldsymbol \eta_p) \, \mathbf \Lambda^1_p\,, \qquad \mathbf j = (\mathbf \Lambda^1)^\top \, \overline{DF^{-1}} \, \mathbb W \, \mathbf V^n\,.\]\(\mathbb A\) is a particle approximation of the \(n\)-weighted mass matrix \(\mathbb M_1\), so the Schur operator is a mass matrix with weight \(1 + \Delta t^2 \alpha^2 n / (4 \varepsilon^2)\), solved with
solverandprecond(built from \(\mathbb M_1\)). The velocities are then updated bypush_v_with_efield()with the mid-point field \(\frac 12 (\mathbf e^{n+1} + \mathbf e^n)\).- class Variables[source]#
Bases:
objectContainer for variables advanced by
VlasovAmpereCoupling.- Variables:
e (FEECVariable) – Electric-field variable in
"Hcurl"space.ions (PICVariable) – Particle variable in
"Particles6D"space.
- class Options(solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, matrix_free: bool = False)[source]#
Bases:
OptionsBaseConfiguration options for
VlasovAmpereCoupling.- Parameters:
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Symmetric iterative solver used by
SchurSolver.precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner for electric-field mass matrix block (with polar splines,
"MassMatrixPreconditioner"is replaced by"PolarMassMatrixPreconditioner").solver_params (SolverParameters, default=None) – Solver controls.
matrix_free (bool, default=False) – Apply the particle matrix matrix-free, with the filling \(w_p G^{-1}(oldsymbol \eta_p)\) stored per marker (
MarkerOperator), instead of assembling it: one spline evaluation and one deposition per marker and solver iteration instead of the products of all pairs of basis functions per marker. Needs 6 auxiliary marker columns. Ignored on the CuPy backend.
- class struphy.propagators.maxwell_weak_ampere.MaxwellWeakAmpere[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\mathbf E \in H(\textnormal{curl})\) and \(\mathbf B \in H(\textnormal{div})\) such that
\[\begin{split}\int_{\Omega} \frac{\partial \mathbf E}{\partial t} \cdot \mathbf F \, \textrm d \mathbf x - \int_{\Omega} \mathbf B \cdot \nabla \times \mathbf F \,\textrm d \mathbf x &= 0\,, \qquad \forall \, \mathbf F \in H(\textnormal{curl}) \\[2mm] \frac{\partial \mathbf B}{\partial t} + \nabla\times\mathbf E &= 0\,.\end{split}\]Ampère’s law holds weakly, Faraday’s law strongly. With the FE coefficients \(\mathbf e \in \mathbb R^{N_1}\) of \(\mathbf E \in V^1_h\) and \(\mathbf b \in \mathbb R^{N_2}\) of \(\mathbf B \in V^2_h\), the semi-discrete system reads
\[\mathbb M_1 \dot{\mathbf e} = \mathbb C^\top \mathbb M_2 \mathbf b\,, \qquad \dot{\mathbf b} = - \mathbb C \mathbf e\,,\]with the mass matrices \(\mathbb M_1\), \(\mathbb M_2\) (
WeightedMassOperators) and the discrete curl \(\mathbb C\) (curl). It conserves the energy \(\frac 12 \mathbf e^\top \mathbb M_1 \mathbf e + \frac 12 \mathbf b^\top \mathbb M_2 \mathbf b\) and the discrete divergence \(\mathbb D \mathbf b\) (since \(\mathbb D \mathbb C = 0\)).implicit: Crank-Nicolson (implicit mid-point),
\[\mathbb M_1 \frac{\mathbf e^{n+1} - \mathbf e^n}{\Delta t} = \mathbb C^\top \mathbb M_2 \frac{\mathbf b^{n+1} + \mathbf b^n}{2}\,, \qquad \frac{\mathbf b^{n+1} - \mathbf b^n}{\Delta t} = - \mathbb C \frac{\mathbf e^{n+1} + \mathbf e^n}{2}\,,\]which conserves both invariants exactly (up to the solver tolerance). The magnetic field is eliminated by
SchurSolverwith \(x = \mathbf e\), \(y = \mathbf b\), \(A = \mathbb M_1\), \(B = -\frac 12 \mathbb C^\top \mathbb M_2\) and \(C = \frac 12 \mathbb C\), which gives the curl-curl system\[\left(\mathbb M_1 + \frac{\Delta t^2}{4} \mathbb C^\top \mathbb M_2 \mathbb C\right) \mathbf e^{n+1} = \left(\mathbb M_1 - \frac{\Delta t^2}{4} \mathbb C^\top \mathbb M_2 \mathbb C\right) \mathbf e^n + \Delta t \, \mathbb C^\top \mathbb M_2 \mathbf b^n\,,\]solved with
solverandprecond. By default (precond="StiffnessPreconditioner") the Schur operator is preconditioned byStiffnessPreconditionerfor \(\mathbb C^\top \left(\frac{\Delta t^2}{4} \mathbb M_2\right) \mathbb C + \mathbb M_1\), built for the current time step (prepare_time_step()). Then \(\mathbf b^{n+1} = \mathbf b^n - \frac{\Delta t}{2} \mathbb C (\mathbf e^{n+1} + \mathbf e^n)\).explicit: Runge-Kutta method from
ButcherTableaufor the system \(\dot{\mathbf e} = \mathbb M_1^{-1} \mathbb C^\top \mathbb M_2 \mathbf b\), \(\dot{\mathbf b} = - \mathbb C \mathbf e\), where \(\mathbb M_1^{-1}\) is applied withsolverandprecondin each stage ("StiffnessPreconditioner"is replaced by the mass matrix preconditioner of \(\mathbb M_1\) here). The time step is limited by the CFL condition of light waves.
- class Variables[source]#
Bases:
objectContainer for Maxwell equation variables.
- Variables:
e (FEECVariable) – Electric field in H(curl) space.
b (FEECVariable) – Magnetic field in H(div) space.
- class Options(algo: Literal['implicit', 'explicit'] = 'implicit', solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['StiffnessPreconditioner', 'MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'StiffnessPreconditioner', solver_params: SolverParameters | None = None, butcher: ButcherTableau | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
MaxwellWeakAmpere.- Parameters:
algo ({"implicit", "explicit"}, default="implicit") –
Time stepping scheme to use for the Maxwell equations.
"implicit": Crank-Nicolson (implicit mid-point) scheme."explicit": explicit Runge-Kutta methods fromButcherTableau.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Name of the symmetric iterative solver passed to
feectools.linalg.solvers.inverse.precond (LiteralOptions.OptsMaxwellPrecond, default="StiffnessPreconditioner") –
Preconditioner of the linear solves.
"StiffnessPreconditioner": Kronecker (fast diagonalization) approximation of the inverse of the Schur operator \(\mathbb M_1 + \frac{\Delta t^2}{4} \mathbb C^\top \mathbb M_2 \mathbb C\) (StiffnessPreconditionerwithderivative="curl"; with polar splines aPolarStiffnessPreconditioner), rebuilt when the time step changes. Foralgo="explicit", the mass matrix preconditioner of \(\mathbb M_1\) is used instead."MassMatrixPreconditioner","PolarMassMatrixPreconditioner": preconditioner of \(\mathbb M_1\), better for small time steps \(\Delta t \lesssim h\), e.g. in CFL-limited PIC runs.None: no preconditioner (set automatically, with a warning, forsolver="cg", which takes no preconditioner).
solver_params (SolverParameters, default=None) – Iterative-solver controls (for example
tol,maxiter,verbose,info,recycle). IfNone, defaults toSolverParameters().butcher (ButcherTableau, default=None) – Butcher tableau for explicit Runge-Kutta methods. Only used when
algo="explicit". IfNone, defaults toButcherTableau().
Notes
System size reduction is performed via
SchurSolverfor implicit time stepping to eliminate the magnetic field and reduce the system to the electric field equation.
Particle and FEEC variables are updated.
- class struphy.propagators.vlasov_maxwell_coupling.VlasovMaxwellCoupling[source]#
Bases:
PropagatorPIC-FEEC discretization of the following equations: find \(\mathbf E \in H(\textnormal{curl})\), \(\mathbf B \in H(\textnormal{div})\) and \(f\) such that
\[\begin{split}\lambda^2 \int_{\Omega} \frac{\partial \mathbf E}{\partial t} \cdot \mathbf F \,\textrm d \mathbf x &= c_1 \int_{\Omega} \mathbf B \cdot \nabla \times \mathbf F \,\textrm d \mathbf x - c_2 \int_{\Omega} \int_{\mathbb{R}^3} f \mathbf{v} \cdot \mathbf{F} \, \text{d}^3 \mathbf{v} \,\textrm d \mathbf x \qquad \forall \, \mathbf F \in H(\textnormal{curl})\,, \\[2mm] \frac{\partial \mathbf B}{\partial t} + \nabla\times\mathbf E &= 0\,, \\[2mm] \frac{\partial f}{\partial t} + c_3\, \mathbf{E} \cdot \frac{\partial f}{\partial \mathbf{v}} &= 0\,,\end{split}\]with \(\lambda \geq 0\), \(c_1 > 0\) and \(c_2 c_3 > 0\). It solves the steps of
MaxwellWeakAmpereandVlasovAmpereCouplingin one implicit step (for \(\lambda = 1\), \(c_1 = 1\), \(c_2 = \alpha^2/\varepsilon\), \(c_3 = 1/\varepsilon\), the light velocity scale). The defaults are the coefficients of the species’ equation parameters, \(\lambda = \varepsilon \lambda_\textrm{s}\), \(c_2 = \kappa_\textrm{s}^2 \varepsilon\), \(c_3 = 1/\varepsilon\), i.e. \(\lambda = \lambda_\textrm{s}\), \(c_2 = \kappa_\textrm{s}^2\), \(c_3 = 1\) for the cyclotron velocity scale. For \(\lambda = 0\) the displacement current vanishes, \(c_1 \nabla \times \mathbf B = c_2 \mathbf J\) (quasi-neutral limit).With the FE coefficients \(\mathbf e \in \mathbb R^{N_1}\) of \(\mathbf E \in V^1_h\) and \(\mathbf b \in \mathbb R^{N_2}\) of \(\mathbf B \in V^2_h\), and \(f\) approximated by \(N\) markers with positions \(\boldsymbol \eta_p\) (logical, fixed in this step), velocities \(\mathbf V = (\mathbf v_p)_p \in \mathbb R^{3N}\) and weights \(\mathbb W = \textnormal{diag}(w_p)\), the semi-discrete system reads
\[\lambda^2\, \mathbb M_1 \dot{\mathbf e} = c_1\, \mathbb C^\top \mathbb M_2 \mathbf b - c_2\, (\mathbf \Lambda^1)^\top \, \overline{DF^{-1}} \, \mathbb W \, \mathbf V \,, \qquad \dot{\mathbf b} = - \mathbb C \mathbf e\,, \qquad \dot{\mathbf V} = c_3\, \overline{DF^{-\top}} \, \mathbf \Lambda^1 \, \mathbf e\,,\]with the notation of
VlasovAmpereCoupling. It conserves the energy (energy())\[H = \frac{\lambda^2}{2}\, \mathbf e^\top \mathbb M_1 \mathbf e + \frac{c_1}{2}\, \mathbf b^\top \mathbb M_2 \mathbf b + \frac{c_2}{2 c_3}\, \mathbf V^\top \mathbb W \mathbf V\,.\]Time discretization: Crank-Nicolson (implicit mid-point), with \(\mathbf e^\textnormal{mid} = \frac 12 (\mathbf e^{n+1} + \mathbf e^n)\) etc.,
\[\lambda^2\, \mathbb M_1 \frac{\mathbf e^{n+1} - \mathbf e^n}{\Delta t} = c_1\, \mathbb C^\top \mathbb M_2 \mathbf b^\textnormal{mid} - c_2\, (\mathbf \Lambda^1)^\top \, \overline{DF^{-1}} \, \mathbb W \, \mathbf V^\textnormal{mid}\,, \qquad \frac{\mathbf b^{n+1} - \mathbf b^n}{\Delta t} = - \mathbb C \mathbf e^\textnormal{mid}\,, \qquad \frac{\mathbf V^{n+1} - \mathbf V^n}{\Delta t} = c_3\, \overline{DF^{-\top}} \, \mathbf \Lambda^1 \, \mathbf e^\textnormal{mid}\,,\]which conserves \(H\) exactly (up to the solver tolerance; for constant weights, i.e. without control variate). Eliminating \(\mathbf b^\textnormal{mid}\) and \(\mathbf V^\textnormal{mid}\) gives the Schur system for the mid-point field
\[\left(\lambda^2\, \mathbb M_1 + \frac{\Delta t^2}{4}\, \mathbb K\right) \mathbf e^\textnormal{mid} = \lambda^2\, \mathbb M_1 \mathbf e^n + \frac{\Delta t}{2} \left( c_1\, \mathbb C^\top \mathbb M_2 \mathbf b^n - c_2\, \mathbf j \right)\,, \qquad \mathbb K = c_1\, \mathbb C^\top \mathbb M_2 \mathbb C + c_2 c_3\, \mathbb A\,,\]with the accumulated matrix \(\mathbb A\) and vector \(\mathbf j\) of
VlasovAmpereCoupling(kernelvlasov_maxwell()). It is solved withsolverandprecond. Then\[\mathbf b^{n+1} = \mathbf b^n - \Delta t\, \mathbb C \mathbf e^\textnormal{mid}\,, \qquad \mathbf e^{n+1} = 2 \mathbf e^\textnormal{mid} - \mathbf e^n \quad (\lambda > 0)\,,\]and the velocities are updated by
push_v_with_efield()with \(\mathbf e^\textnormal{mid}\).Quasi-neutral limit \(\lambda = 0\). The curl-curl operator \(\mathbb C^\top \mathbb M_2 \mathbb C\) is singular on the discrete gradients (and harmonic fields); this kernel is absorbed by the plasma term \(c_2 c_3 \mathbb A\), a particle approximation of the density-weighted mass matrix \(\mathbb M_1\). The Schur operator is therefore positive definite for positive weights (full-f, no control variate) and plasma everywhere in the domain. The scheme then imposes \(c_1\, \mathbb C^\top \mathbb M_2 \mathbf b^\textnormal{mid} = c_2\, (\mathbf \Lambda^1)^\top \overline{DF^{-1}} \mathbb W \mathbf V^\textnormal{mid}\), hence a weakly divergence-free mid-point current (multiply by \(\mathbb G^\top\)); the gradient part of \(\mathbf E\) is its Lagrange multiplier. \(\mathbf e^n\) drops out of the system and only \(\mathbf e^\textnormal{mid}\) is determined: the propagator stores \(\mathbf e^{n+1} = \mathbf e^\textnormal{mid}\) (the Crank-Nicolson update \(2 \mathbf e^\textnormal{mid} - \mathbf e^n\) would carry any error of \(\mathbf e^n\) as an undamped even-odd oscillation). The energy \(H\) (without the electric part) is still conserved exactly.
The constraint holds at the mid-point only: its residual \(\mathbf r^n = c_1 \mathbb C^\top \mathbb M_2 \mathbf b^n - c_2 \mathbf j(\mathbf V^n)\) (linear in \(\mathbf V\) at fixed positions) satisfies \(\mathbf r^{n+1} = - \mathbf r^n\). Initial data that violate the constraint (e.g. by marker noise in \(\mathbf J\)) would thus oscillate with period 2 without damping. With
initial_projection(default), the setup replaces \((\mathbf b^0, \mathbf V^0)\) by the mid-point state of one step, \(\mathbf b^0 - \frac{\Delta t}{2} \mathbb C \mathbf e^\textnormal{mid}\) and \(\mathbf V^0 + \frac{\Delta t}{2} c_3 \overline{DF^{-\top}} \mathbf \Lambda^1 \mathbf e^\textnormal{mid}\) (an implicit Euler step of \(\Delta t / 2\)), which satisfies the constraint exactly and does not depend on \(\Delta t\): a projection onto the constraint. The particle pushers of the splitting change \(\mathbf r\) again by \(O(\Delta t)\) in every step.Preconditioner. With \(\mathbb A \approx \bar n\, \mathbb M_1\), the Schur operator is approximately the stabilized curl-curl operator \(\mathbb C^\top \left(\frac{\Delta t^2 c_1}{4} \mathbb M_2\right) \mathbb C + \left(\lambda^2 + \frac{\Delta t^2 c_2 c_3 \bar n}{4}\right) \mathbb M_1\), preconditioned (default) by
StiffnessPreconditioner, built for the current time step (prepare_time_step()). Its stabilization stays positive for \(\lambda = 0\).- class Variables[source]#
Bases:
objectContainer for variables advanced by
VlasovMaxwellCoupling.- Variables:
e (FEECVariable) – Electric-field variable in
"Hcurl"space.b (FEECVariable) – Magnetic-field variable in
"Hdiv"space.ions (PICVariable) – Particle variable in
"Particles6D"space.
- class Options(lam: float | None = None, c1: float = 1.0, c2: float | None = None, c3: float | None = None, solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['StiffnessPreconditioner', 'MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'StiffnessPreconditioner', solver_params: SolverParameters | None = None, n_ref: float | None = None, matrix_free: bool = False, initial_projection: bool = True)[source]#
Bases:
OptionsBaseConfiguration options for
VlasovMaxwellCoupling.- Parameters:
lam (float, default=None) – \(\lambda \geq 0\), with the coefficient \(\lambda^2\) of the displacement current \(\partial_t \mathbf E\);
lam=0.0is the quasi-neutral limit (needs full-f weights, no control variate). IfNone, \(\varepsilon \lambda_\textrm{s}\) of the species (lamandepsilonof its equation parameters).c1 (float, default=1.0) – Coefficient \(c_1 > 0\) of \(\nabla \times \mathbf B\) in Ampère’s law.
c2 (float, default=None) – Coefficient of the current density in Ampère’s law. If
None, \(\kappa_\textrm{s}^2 \varepsilon\) of the species (kappaandepsilonof its equation parameters).c3 (float, default=None) – Coefficient of the electric force in the Vlasov equation (\(c_2 c_3 > 0\)). If
None, \(1/\varepsilon\) of the species.solver (LiteralOptions.OptsSymmSolver, default="pcg") – Symmetric iterative solver of the Schur system, passed to
feectools.linalg.solvers.inverse.precond (LiteralOptions.OptsMaxwellPrecond, default="StiffnessPreconditioner") –
Preconditioner of the Schur system.
"StiffnessPreconditioner":StiffnessPreconditionerwithderivative="curl"for the stabilized curl-curl approximation of the Schur operator (see the class docstring), rebuilt when the time step changes."MassMatrixPreconditioner","PolarMassMatrixPreconditioner": preconditioner of \(\mathbb M_1\), sufficient for \(\Delta t \lesssim h\) (with polar splines,"MassMatrixPreconditioner"is replaced by"PolarMassMatrixPreconditioner").None: no preconditioner (set automatically, with a warning, forsolver="cg").
solver_params (SolverParameters, default=None) – Solver controls. If
None, defaults toSolverParameters().n_ref (float, default=None) – Reference density \(\bar n\) in the stiffness preconditioner. If
None, the Rayleigh quotient \(\mathbf 1^\top \mathbb A \mathbf 1 / \mathbf 1^\top \mathbb M_1 \mathbf 1\) of the accumulated matrix at the setup (a density average).matrix_free (bool, default=False) – Apply the particle matrix \(\mathbb A\) matrix-free, with the filling \(w_p G^{-1}(\boldsymbol \eta_p)\) stored per marker (
MarkerOperator), instead of assembling it: one spline evaluation and one deposition per marker and solver iteration instead of the products of all pairs of basis functions per marker. Needs 6 auxiliary marker columns. Ignored on the CuPy backend.initial_projection (bool, default=True) – Only for
lam=0: in the setup (firstprepare_time_step()), replace \((\mathbf b, \mathbf V)\) by the mid-point state of one step, which satisfies \(c_1 \nabla \times \mathbf B = c_2 \mathbf J\) exactly (see the class docstring). Without it, initial data that violate the constraint lead to an undamped period-2 oscillation of \(\mathbf b\) and \(\mathbf V\). Not applied to a restored (restart) state.
- prepare_time_step(dt: float)[source]#
Schur operator \(\lambda^2 \mathbb M_1 + \frac{\Delta t^2}{4} \mathbb K\), its preconditioner and solver for the time step
dt: built in the setup, rebuilt when dt changes. The particle matrix enters by reference (assembled, or matrix-free reading the fillings stored in the markers), so it is updated by the accumulation in every step. Forlam=0(andinitial_projection), the first call also projects the state onto the constraint \(c_1 \mathbb C^\top \mathbb M_2 \mathbf b = c_2 \mathbf j\).
- energies() tuple[float, float, float][source]#
The three parts \(\frac{\lambda^2}{2} \mathbf e^\top \mathbb M_1 \mathbf e\), \(\frac{c_1}{2} \mathbf b^\top \mathbb M_2 \mathbf b\) and \(\frac{c_2}{2 c_3} \mathbf V^\top \mathbb W \mathbf V\) of the energy
energy()(summed over all MPI ranks; a collective operation).
Particle and FEEC variables are updated.
- class struphy.propagators.drift_kinetic_maxwell_coupling.DriftKineticMaxwellCoupling[source]#
Bases:
PropagatorPIC-FEEC discretization of the following equations: find \(\mathbf E \in H(\textnormal{curl})\), \(\tilde{\mathbf B} \in H(\textnormal{div})\) and the guiding-center distribution \(f(\mathbf X, v_\parallel, \mu)\) (positions fixed in this step) such that
\[\begin{split}\lambda^2 \int_{\Omega} \frac{\partial \mathbf E}{\partial t} \cdot \mathbf F \,\textrm d \mathbf x + \chi \int_{\Omega} \frac{n_0}{|B_0|^2} \frac{\partial \mathbf E_\perp}{\partial t} \cdot \mathbf F_\perp \,\textrm d \mathbf x &= c_1 \int_{\Omega} \tilde{\mathbf B} \cdot \nabla \times \mathbf F \,\textrm d \mathbf x - c_2 \int_{\Omega} \iint f^\textnormal{vol} \left( v_\parallel \frac{\mathbf B^*}{B^*_\parallel} + \frac{\mathbf E \times \mathbf b_0}{B^*_\parallel} \right) \cdot \mathbf F \,\textrm d v_\parallel \textrm d \mu \,\textrm d \mathbf x \qquad \forall \, \mathbf F \in H(\textnormal{curl})\,, \\[2mm] \frac{\partial \tilde{\mathbf B}}{\partial t} + \nabla\times\mathbf E &= 0\,, \\[2mm] \frac{\partial f}{\partial t} + c_3\, \frac{\mathbf B^*}{B^*_\parallel} \cdot \mathbf E\, \frac{\partial f}{\partial v_\parallel} &= 0\,,\end{split}\]with \(\mathbf E_\perp = \mathbf E - (\mathbf b_0 \cdot \mathbf E) \mathbf b_0\), the phase-space density \(f^\textnormal{vol} = B^*_\parallel f\) (the marker weights) and, as in
PushGuidingCenterParallel,\[\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^*\,,\]with the total magnetic field in the first term (parallel streaming along the perturbed field lines) and the curvature term of the equilibrium.
The current is the guiding-center current \(\iint f^\textnormal{vol} \dot{\mathbf X}\) without the magnetic drifts (which move the positions): parallel streaming, including the curvature drift in \(\mathbf B^*\), and (option
exb_current) the \(E \times B\) drift. The coefficients are \(\lambda \geq 0\), \(\chi \geq 0\) with \(\lambda^2 + \chi > 0\), \(c_1 > 0\) and \(c_2 c_3 > 0\). The defaults are the coefficients of the species’ equation parameters, \(\lambda = \varepsilon \lambda_\textrm{s}\), \(\chi = (\varepsilon \kappa_\textrm{s})^2\), \(c_1 = 1\), \(c_2 = \kappa_\textrm{s}^2 \varepsilon\), \(c_3 = 1/\varepsilon\), i.e. for the thermal velocity scale ofDriftKineticMaxwell\(\lambda = \hat v_\textrm{th}/c\), \(\chi = \hat v_\textrm{th}^2/\hat v_A^2\). The \(\chi\) term is the (linearized, long-wavelength) polarization current of the guiding centers, which the drift-kinetic ordering removes from \(\dot{\mathbf X}\): without it, the perpendicular electrostatic field is undetermined for \(\lambda = 0\) (flute modes), and with only the displacement current the shear Alfvén wave travels at the speed of light.With the FE coefficients \(\mathbf e \in \mathbb R^{N_1}\) of \(\mathbf E \in V^1_h\) and \(\mathbf b \in \mathbb R^{N_2}\) of \(\tilde{\mathbf B} \in V^2_h\), and \(N\) markers with logical positions \(\boldsymbol \eta_p\) (fixed), parallel velocities \(v_{\parallel,p}\) and weights \(w_p\), the semi-discrete system reads
\[\mathbb M_E \dot{\mathbf e} = c_1\, \mathbb C^\top \mathbb M_2 \mathbf b - c_2 \left( \mathbf j(\mathbf V) + \mathbb S\, \mathbf e \right) \,, \qquad \dot{\mathbf b} = - \mathbb C \mathbf e\,, \qquad \dot v_{\parallel,p} = c_3\, \mathbf a_p \cdot \mathbf \Lambda^1_p \mathbf e\,,\]with \(\mathbb M_E = \lambda^2\, \mathbb M_1 + \chi\, \mathbb M_1^\perp\) (\(\mathbb M_1^\perp\) =
M1gyro), \(\mathbf a_p = \hat{\mathbf B}^{*2} / \hat B^{*3}_\parallel (\boldsymbol \eta_p)\), \(\mathbf \Lambda^1_p\) the basis of \(V^1_h\) at \(\boldsymbol \eta_p\), and\[\mathbf j(\mathbf V) = \sum_p w_p v_{\parallel,p}\, (\mathbf \Lambda^1_p)^\top \mathbf a_p\,, \qquad \mathbb A_\parallel = \sum_p w_p\, (\mathbf \Lambda^1_p)^\top \mathbf a_p \mathbf a_p^\top \mathbf \Lambda^1_p\,, \qquad \mathbf f^\top \mathbb S\, \mathbf e = \sum_p \frac{w_p}{\hat B^{*3}_\parallel(\boldsymbol \eta_p)}\, (\mathbf \Lambda^1_p \mathbf f) \cdot \big( (\mathbf \Lambda^1_p \mathbf e) \times \hat{\mathbf b}^1_0 (\boldsymbol \eta_p) \big)\,.\]\(\mathbb A_\parallel\) is symmetric positive semi-definite (the parallel response), \(\mathbb S\) is antisymmetric (the \(E \times B\) current does no work). The system conserves the energy
\[H = \frac{1}{2}\, \mathbf e^\top \mathbb M_E \mathbf e + \frac{c_1}{2}\, \mathbf b^\top \mathbb M_2 \mathbf b + \frac{c_2}{c_3} \sum_p w_p \left( \frac{v_{\parallel,p}^2}{2} + \mu_p |B_0|(\boldsymbol \eta_p) \right) \qquad (:meth:`~struphy.propagators.drift_kinetic_maxwell_coupling.DriftKineticMaxwellCoupling.energy`)\,.\]Time discretization: Crank-Nicolson (implicit mid-point), with \(\mathbf a_p\) and \(\mathbb S\) evaluated at the beginning of the step, which conserves \(H\) exactly (up to the solver tolerance; for constant weights). Eliminating \(\mathbf b^\textnormal{mid}\) and \(\mathbf V^\textnormal{mid}\) gives the Schur system for the mid-point field
\[\left( \mathbb M_E + \frac{\Delta t^2}{4} \left( c_1\, \mathbb C^\top \mathbb M_2 \mathbb C + c_2 c_3\, \mathbb A_\parallel \right) + \frac{\Delta t}{2}\, c_2\, \mathbb S \right) \mathbf e^\textnormal{mid} = \mathbb M_E \mathbf e^n + \frac{\Delta t}{2} \left( c_1\, \mathbb C^\top \mathbb M_2 \mathbf b^n - c_2\, \mathbf j(\mathbf V^n) \right)\,,\]accumulated by
dk_maxwell()(or matrix-free) and solved withsolverandprecond(non-symmetric with the \(E \times B\) current). Then\[\mathbf b^{n+1} = \mathbf b^n - \Delta t\, \mathbb C \mathbf e^\textnormal{mid}\,, \qquad v_{\parallel,p}^{n+1} = v_{\parallel,p}^n + \Delta t\, c_3\, \mathbf a_p \cdot \mathbf \Lambda^1_p \mathbf e^\textnormal{mid}\,, \qquad \mathbf e^{n+1} = 2 \mathbf e^\textnormal{mid} - \mathbf e^n\,,\]with the velocity update by
push_vpar_with_efield().Quasi-neutral limit \(\lambda = 0\). The parallel part of \(\mathbf e\) is then determined by \(\mathbb A_\parallel\) alone (which needs positive weights, i.e. full-f, and markers everywhere), the perpendicular part by \(\chi\, \mathbb M_1^\perp\) and the curl-curl operator. The perpendicular field still carries the energy \(\frac 12 \mathbf e^\top \mathbb M_E \mathbf e\), so the Crank-Nicolson update is kept (storing \(\mathbf e^\textnormal{mid}\) would dissipate it). The parallel part of the stored \(\mathbf e^{n+1}\) is not determined by the scheme and may oscillate from step to step; it does not feed back, since the steps see \(\mathbf e^n\) only through \(\mathbb M_E \mathbf e^n\) and the \(E \times B\) push only through \(\mathbf E_\perp\). With
initial_projection(default), the setup replaces \((\mathbf b^0, \mathbf V^0)\) by the mid-point state of one step, which removes the undamped period-2 oscillation of the constraint residual.Preconditioner. With \(\mathbb A_\parallel \approx \bar n\, \mathbb M_1^\parallel\) (
M1para), the symmetric part of the Schur operator is approximately \(\mathbb C^\top \left(\frac{\Delta t^2 c_1}{4} \mathbb M_2\right) \mathbb C + \mathbb M_E + \frac{\Delta t^2 c_2 c_3 \bar n}{4} \mathbb M_1^\parallel\), preconditioned (default) byStiffnessPreconditioner, built for the current time step (prepare_time_step()).- class Variables[source]#
Bases:
objectContainer for variables advanced by
DriftKineticMaxwellCoupling.- Variables:
e (FEECVariable) – Electric-field variable in
"Hcurl"space.b (FEECVariable) – Magnetic-field perturbation in
"Hdiv"space.ions (PICVariable) – Guiding-center particle variable in
"Particles5D"space.
- class Options(lam: float | None = None, chi: float | None = None, c1: float = 1.0, c2: float | None = None, c3: float | None = None, exb_current: bool = True, solver: Literal['pbicgstab', 'bicgstab', 'gmres', 'pcg', 'cg'] = 'pbicgstab', precond: Literal['StiffnessPreconditioner', 'MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'StiffnessPreconditioner', solver_params: SolverParameters | None = None, n_ref: float | None = None, matrix_free: bool = False, initial_projection: bool = True)[source]#
Bases:
OptionsBaseConfiguration options for
DriftKineticMaxwellCoupling.- Parameters:
lam (float, default=None) – The displacement current has the coefficient \(\lambda^2\) (
lam\(= \lambda \geq 0\));lam=0.0is the quasi-neutral limit (needschi > 0and full-f weights, no control variate). IfNone, \(\varepsilon \lambda_\textrm{s}\) of the species (lamandepsilonof its equation parameters).chi (float, default=None) – Coefficient \(\chi \geq 0\) of the polarization current \(\frac{n_0}{|B_0|^2} \partial_t \mathbf E_\perp\). If
None, \((\varepsilon \kappa_\textrm{s})^2\) of the species (the polarization of the guiding centers themselves); a larger value mimics the polarization of a heavier species.lam**2 + chimust be positive. The density \(n_0\) is the one of the MHD equilibrium (M1gyro), not of the kinetic background: choose them consistently.c1 (float, default=1.0) – Coefficient \(c_1 > 0\) of \(\nabla \times \tilde{\mathbf B}\) in Ampère’s law.
c2 (float, default=None) – Coefficient of the current density in Ampère’s law. If
None, \(\kappa_\textrm{s}^2 \varepsilon\) of the species (kappaandepsilonof its equation parameters).c3 (float, default=None) – Coefficient of the parallel electric force (\(c_2 c_3 > 0\)). If
None, \(1/\varepsilon\) of the species.exb_current (bool, default=True) – Include the \(E \times B\) current (antisymmetric operator \(\mathbb S\)). The Schur system is then non-symmetric and needs a general solver. Switch it off if the \(E \times B\) current is compensated (e.g. by other species).
solver (OptsSolver, default="pbicgstab") – Iterative solver of the Schur system, passed to
feectools.linalg.solvers.inverse. The symmetric solvers"pcg"and"cg"needexb_current=False.precond (LiteralOptions.OptsMaxwellPrecond, default="StiffnessPreconditioner") –
Preconditioner of the Schur system (only for
"pcg"and"pbicgstab"; set toNoneotherwise)."StiffnessPreconditioner":StiffnessPreconditionerwithderivative="curl"for the stabilized curl-curl approximation of the Schur operator (see the class docstring), rebuilt when the time step changes."MassMatrixPreconditioner","PolarMassMatrixPreconditioner": preconditioner of \(\mathbb M_1\) (with polar splines,"MassMatrixPreconditioner"is replaced by"PolarMassMatrixPreconditioner").None: no preconditioner.
solver_params (SolverParameters, default=None) – Solver controls. If
None, defaults toSolverParameters().n_ref (float, default=None) – Reference density \(\bar n\) in the stiffness preconditioner. If
None, the Rayleigh quotient \(\mathbf 1^\top \mathbb A_\parallel \mathbf 1 / \mathbf 1^\top \mathbb M_1^\parallel \mathbf 1\) at the setup (a density average).matrix_free (bool, default=False) – Apply the particle matrices \(\mathbb A_\parallel\) and \(\mathbb S\) matrix-free, with their fillings stored per marker (
MarkerOperator), instead of assembling them. Needs 7 auxiliary marker columns.initial_projection (bool, default=True) – Only for
lam=0: in the setup (firstprepare_time_step()), replace \((\mathbf b, \mathbf V)\) by the mid-point state of one step, which satisfies the constraint of the quasi-neutral limit exactly. Not applied to a restored (restart) state.
- prepare_time_step(dt: float)[source]#
Schur operator, its preconditioner and solver for the time step
dt: built in the setup, rebuilt when dt changes. The particle matrices enter by reference (assembled, or matrix-free reading the fillings stored in the markers), so they are updated by the accumulation in every step. Forlam=0(andinitial_projection), the first call also projects the state onto the constraint.
- energies() tuple[float, float, float][source]#
The three parts \(\frac{1}{2} \mathbf e^\top \mathbb M_E \mathbf e\) (displacement and polarization), \(\frac{c_1}{2} \mathbf b^\top \mathbb M_2 \mathbf b\) and \(\frac{c_2}{c_3} \sum_p w_p (v_{\parallel,p}^2/2 + \mu_p |B_0|(\boldsymbol \eta_p))\) of the energy
energy()(summed over all MPI ranks; a collective operation).
- energy() float[source]#
The energy \(H = \frac{1}{2} \mathbf e^\top \mathbb M_E \mathbf e + \frac{c_1}{2} \mathbf b^\top \mathbb M_2 \mathbf b + \frac{c_2}{c_3} \sum_p w_p (v_{\parallel,p}^2/2 + \mu_p |B_0|(\boldsymbol \eta_p))\) conserved by the propagator (summed over all MPI ranks; a collective operation).
MHD-kinetic coupling#
Particle and FEEC variables are updated.
- class struphy.propagators.current_coupling_5d_curlb.CurrentCoupling5DCurlb(b_tilde: FEECVariable | None = None)[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\mathbf U \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\) and \(\mathbf B \in H(\textnormal{div})\) such that
\[\begin{split}\left\{ \begin{aligned} \int \rho_0 &\frac{\partial \tilde{\mathbf U}}{\partial t} \cdot \mathbf V\, \textnormal{d} \mathbf x = - \frac{A_\textnormal{h}}{A_b} \iint \frac{f^\text{vol}}{B^*_\parallel} v_\parallel^2 (\nabla \times \mathbf b_0) \times \mathbf B \cdot \mathbf V \, \textnormal{d} \mathbf x \textnormal{d} v_\parallel \textnormal{d} \mu \quad \forall \,\mathbf V \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\,, \\ &\frac{\partial f}{\partial t} = - \frac{1}{B^*_\parallel} v_\parallel (\nabla \times \mathbf b_0) \cdot (\mathbf B \times \tilde{\mathbf U}) \nabla_{v_\parallel}f \,. \end{aligned} \right.\end{split}\]Time discretization: Crank-Nicolson (implicit mid-point). System size reduction via
SchurSolver:\[\begin{split}\begin{bmatrix} \mathbf u^{n+1} - \mathbf u^n \\ V_\parallel^{n+1} - V_\parallel^n \end{bmatrix} = \frac{\Delta t}{2} \begin{bmatrix} 0 & - (\mathbb{M}^{2,n})^{-1} \left\{ \mathbb{L}^2 \frac{1}{\bar{\sqrt{g}}} \right\}\cdot_\text{vector} \left\{\bar{b}^{\nabla \times}_0 (\bar{B}^\times_f)^\top \bar{V}_\parallel \frac{1}{\bar{\sqrt{g}}}\right\} \frac{1}{\bar B^{*0}_\parallel}) \\ \frac{1}{\bar B^{*0}_\parallel} \left\{\bar{b}^{\nabla \times}_0 (\bar{B}^\times_f)^\top \bar{V}_\parallel \frac{1}{\bar{\sqrt{g}}}\right\}\, \cdot_\text{vector} \left\{\frac{1}{\bar{\sqrt{g}}}(\mathbb{L}²)^\top\right\} (\mathbb{M}^{2,n})^{-1} & 0 \end{bmatrix} \begin{bmatrix} (\mathbb{M}^{2,n})^{-1} (\mathbf u^{n+1} + \mathbf u^n) \\ \frac{A_\textnormal{h}}{A_b} W (V_\parallel^{n+1} + V_\parallel^n) \end{bmatrix} \,.\end{split}\]For the detail explanation of the notations, see 2022_DriftKineticCurrentCoupling.
- class Variables[source]#
Bases:
objectContainer for variables advanced by
CurrentCoupling5DCurlb.- Variables:
u (FEECVariable) – Fluid-like FEEC variable in one of
"Hcurl","Hdiv", or"H1vec".energetic_ions (PICVariable) – Energetic-ion particle variable in
"Particles5D"space.
- class Options(ep_scale: float | None = None, u_space: Literal['Hcurl', 'Hdiv', 'H1vec'] = 'Hdiv', solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, filter_params: FilterParameters | None = None, matrix_free: bool = True)[source]#
Bases:
OptionsBaseConfiguration options for
CurrentCoupling5DCurlb.- Parameters:
ep_scale (float, default=None) – Factor of the energetic-particle contributions (accumulation): the mass ratio \(A_\textnormal{h}/A_\textnormal{b}\) of the energetic ions and the MHD bulk if
None.u_space (LiteralOptions.OptsVecSpace, default="Hdiv") – FEEC space used for the unknown
uvariable. Only"Hdiv"and"H1vec"are supported.solver (LiteralOptions.OptsSymmSolver, default="pcg") – Symmetric iterative solver used by
SchurSolver.precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner used for the FEEC mass matrix block.
solver_params (SolverParameters, default=None) – Iterative-solver controls. If
None, defaults toSolverParameters().filter_params (FilterParameters, default=None) – Filter of the accumulated (particle-to-grid) vectors; defaults to
FilterParameters()(no filter). The three-point (binomial) filterFilterParameters(use_filter="three_point")reduces the noise, but the propagator then no longer conserves the energy exactly (the filter is applied to the accumulated vectors only, not to the fields seen by the particles).matrix_free (bool, default=True) – Apply the accumulated matrix \(\sum_p c_p (\vec \Lambda_p \cdot \mathbf e_p) (\vec \Lambda_p \cdot \mathbf e_p)^\top\) (rank one per marker) in each iteration of the solver from data stored per marker (
MarkerOperator), instead of assembling it: one spline evaluation and one deposition per marker and iteration instead of the 6 blocks of size \(n_\textnormal{basis}^2\) per marker (much cheaper for cubic splines). Same result up to the solver tolerance.
- class struphy.propagators.current_coupling_5d_density.CurrentCoupling5DDensity(energetic_ions: PICVariable | None = None, b_tilde: FEECVariable | None = None)[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\mathbf U \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\) and \(\mathbf B \in H(\textnormal{div})\) such that
\[\int \rho_0 \frac{\partial \tilde{\mathbf U}}{\partial t} \cdot \mathbf V \, \textnormal{d} \mathbf{x} = \frac{A_\textnormal{h}}{A_b} \frac{1}{\epsilon} \iiint f^\text{vol} \left(1 - \frac{B_\parallel}{B^*_\parallel}\right) \tilde{\mathbf U} \times \mathbf B_f \cdot \mathbf V \, \textnormal{d} \mathbf{x} \textnormal{d} v_\parallel \textnormal{d} \mu \quad \forall \,\mathbf V \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\,,\]FE coefficients update:
\[\mathbf u^{n+1} - \mathbf u^n = -\frac{A_\textnormal{h}}{A_b} \frac{1}{\epsilon} \mathbb{L}²{\mathbb{B}}^\times_f \mathbb{N}(1/g) \mathbb{W} \mathbb{N}\left(1- \frac{\hat B^0_\parallel}{\hat B^{*0} _\parallel}\right) (\mathbb{L}²)^\top \frac{\Delta t}{2} \cdot (\mathbf u^{n+1} + \mathbf u^n) \,.\]For the detail explanation of the notations, see 2022_DriftKineticCurrentCoupling.
- class Variables[source]#
Bases:
objectContainer for variables advanced by
CurrentCoupling5DDensity.- Variables:
u (FEECVariable) – Fluid-like FEEC variable in one of
"Hcurl","Hdiv", or"H1vec".
- class Options(ep_scale: float | None = None, u_space: Literal['Hcurl', 'Hdiv', 'H1vec'] = 'Hdiv', solver: Literal['pbicgstab', 'bicgstab', 'gmres'] = 'pbicgstab', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, filter_params: FilterParameters | None = None, matrix_free: bool = True)[source]#
Bases:
OptionsBaseConfiguration options for
CurrentCoupling5DDensity.- Parameters:
ep_scale (float, default=None) – Factor of the energetic-particle contributions (accumulation): the mass ratio \(A_\textnormal{h}/A_\textnormal{b}\) of the energetic ions and the MHD bulk if
None.u_space (LiteralOptions.OptsVecSpace, default="Hdiv") – FEEC space used for the unknown
uvariable.solver (LiteralOptions.OptsGenSolver, default="pbicgstab") – General iterative solver used for the linear system.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner applied to the FEEC mass matrix.
solver_params (SolverParameters, default=None) – Iterative-solver controls. If
None, defaults toSolverParameters().filter_params (FilterParameters, default=None) – Filter of the accumulated (particle-to-grid) vectors; defaults to
FilterParameters()(no filter). The three-point (binomial) filterFilterParameters(use_filter="three_point")reduces the noise, but the propagator then no longer conserves the energy exactly (the filter is applied to the accumulated vectors only, not to the fields seen by the particles).matrix_free (bool, default=True) – Apply the particle matrix \(\sum_p \vec \Lambda_p F_p \vec \Lambda_p^\top\) (antisymmetric 3x3 \(F_p\)) in each iteration of the solver from data stored per marker (
MarkerOperator) instead of assembling it: one spline evaluation and one deposition per marker and iteration instead of 3 blocks of size \(n_\textnormal{basis}^2\) per marker. Same result up to the solver tolerance.
Particle and FEEC variables are updated.
- class struphy.propagators.current_coupling_5d_gradb.CurrentCoupling5DGradB(b_tilde: FEECVariable | None = None)[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\mathbf U \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\) and \(\mathbf B \in H(\textnormal{div})\) such that
\[\begin{split}\left\{ \begin{aligned} \int \rho_0 &\frac{\partial \tilde{\mathbf U}}{\partial t} \cdot \mathbf V \, \textnormal{d} \mathbf x = - \frac{A_\textnormal{h}}{A_b} \iint \mu \frac{f^\text{vol}}{B^*_\parallel} (\mathbf b_0 \times \nabla B_\parallel) \times \mathbf B \cdot \mathbf V \,\textnormal{d} \mathbf x \textnormal{d} v_\parallel \textnormal{d} \mu \quad \forall \,\mathbf V \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\,, \\ &\frac{\partial f}{\partial t} = \frac{1}{B^*_\parallel} \left[ \mathbf b_0 \times (\tilde{\mathbf U} \times \mathbf B) \right] \cdot \nabla f\,. \end{aligned} \right.\end{split}\]Time discretization: Explicit (‘rk4’, ‘forward_euler’, ‘heun2’, ‘rk2’, ‘heun3’).
\[\begin{split}\begin{bmatrix} \dot{\mathbf u}\\ \dot{\mathbf H} \end{bmatrix} = \begin{bmatrix} 0 & (\mathbb{M}^{2,n})^{-1} \mathbb{L}² \frac{1}{\bar{\sqrt{g}}} \frac{1}{\bar B^{*0}_\parallel}\bar{B}^\times_f \bar{G}^{-1} \bar{b}^\times_0 \bar{G}^{-1} \\ -\bar{G}^{-1} \bar{b}^\times_0 \bar{G}^{-1} \bar{B}^\times_f \frac{1}{\bar B^{*0}_\parallel} \frac{1}{\bar{\sqrt{g}}} (\mathbb{L}²)^\top (\mathbb{M}^{2,n})^{-1} & 0 \end{bmatrix} \begin{bmatrix} \mathbb M^{2,n} \mathbf u \\ \frac{A_\textnormal{h}}{A_b} \bar M \bar W \overline{\nabla B}_\parallel \end{bmatrix} \,.\end{split}\]For the detail explanation of the notations, see 2022_DriftKineticCurrentCoupling.
- class Variables[source]#
Bases:
objectContainer for variables advanced by
CurrentCoupling5DGradB.- Variables:
u (FEECVariable) – Fluid-like FEEC variable in one of
"Hcurl","Hdiv", or"H1vec".energetic_ions (PICVariable) – Energetic-ion particle variable in
"Particles5D"space.
- class Options(ep_scale: float | None = None, algo: Literal['discrete_gradient', 'explicit'] = 'explicit', butcher: ButcherTableau | None = None, u_space: Literal['Hcurl', 'Hdiv', 'H1vec'] = 'Hdiv', solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, filter_params: FilterParameters | None = None, dg_solver_params: DiscreteGradientSolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
CurrentCoupling5DGradB.- Parameters:
ep_scale (float, default=None) – Factor of the energetic-particle contributions (accumulation): the mass ratio \(A_\textnormal{h}/A_\textnormal{b}\) of the energetic ions and the MHD bulk if
None.algo ({"discrete_gradient", "explicit"}, default="explicit") –
Time integration algorithm.
"explicit": explicit Runge-Kutta update withbutcher."discrete_gradient": nonlinear discrete-gradient update.
butcher (ButcherTableau, default=None) – Butcher tableau used in explicit mode. If
Noneandalgo="explicit", defaults toButcherTableau().u_space (LiteralOptions.OptsVecSpace, default="Hdiv") – FEEC space used for the unknown
uvariable."Hdiv"and"H1vec"are supported foralgo="explicit", only"Hdiv"foralgo="discrete_gradient".solver (LiteralOptions.OptsSymmSolver, default="pcg") – Symmetric iterative solver used for mass-matrix inversions.
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner used with the mass matrix.
solver_params (SolverParameters, default=None) – Iterative-solver controls. If
None, defaults toSolverParameters().filter_params (FilterParameters, default=None) – Filter of the accumulated (particle-to-grid) vectors; defaults to
FilterParameters()(no filter). The three-point (binomial) filterFilterParameters(use_filter="three_point")reduces the noise, but the propagator then no longer conserves the energy exactly (the filter is applied to the accumulated vectors only, not to the fields seen by the particles).dg_solver_params (DiscreteGradientSolverParameters, default=None) – Nonlinear solver controls for discrete-gradient mode. If
Noneandalgo="discrete_gradient", defaults toDiscreteGradientSolverParameters().
Particle and FEEC variables are updated.
- class struphy.propagators.current_coupling_6d_current.CurrentCoupling6DCurrent(b_tilde: FEECVariable | None = None)[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\tilde{\mathbf{U}} \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\) and \(f\) such that
\[\begin{split}\int_\Omega \rho_0 &\frac{\partial \tilde{\mathbf{U}}}{\partial t} \cdot \mathbf V \,\textrm d \mathbf x = - \frac{A_\textnormal{h}}{A_\textnormal{b}} \frac{1}{\varepsilon} \int_\Omega n_\textnormal{h}\mathbf{u}_\textnormal{h} \times(\mathbf{B}_0+\tilde{\mathbf{B}}) \cdot \mathbf V \,\textrm d \mathbf x \qquad \forall \, \mathbf V \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\,, \\[2mm] &\frac{\partial f_\textnormal{h}}{\partial t} + \frac{1}{\varepsilon} \Big[(\mathbf{B}_0+\tilde{\mathbf{B}})\times\tilde{\mathbf{U}} \Big] \cdot \frac{\partial f_\textnormal{h}}{\partial \mathbf{v}} =0\,, \\[2mm] &n_\textnormal{h}\mathbf{u}_\textnormal{h}=\int_{\mathbb{R}^3}f_\textnormal{h}\mathbf{v}\,\textnormal{d}^3 \mathbf v\,.\end{split}\]Time discretization: Crank-Nicolson (implicit mid-point). System size reduction via
SchurSolver.- class Variables[source]#
Bases:
objectContainer for variables advanced by
CurrentCoupling6DCurrent.- Variables:
ions (PICVariable) – Energetic-ion particle variable in
"Particles6D"space.u (FEECVariable) – Fluid-like FEEC variable in one of
"Hcurl","Hdiv", or"H1vec".
- class Options(u_space: Literal['Hcurl', 'Hdiv', 'H1vec'] = 'Hdiv', solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, filter_params: FilterParameters | None = None, boundary_cut: tuple = (0.0, 0.0, 0.0), matrix_free: bool = False)[source]#
Bases:
OptionsBaseConfiguration options for
CurrentCoupling6DCurrent.- Parameters:
u_space (LiteralOptions.OptsVecSpace, default="Hdiv") – FEEC space used for the unknown
uvariable.solver (LiteralOptions.OptsSymmSolver, default="pcg") – Symmetric iterative solver used by
SchurSolver.precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner applied to the FEEC mass matrix block.
solver_params (SolverParameters, default=None) – Iterative-solver controls. If
None, defaults toSolverParameters().filter_params (FilterParameters, default=None) – Particle-to-grid filtering parameters used by the accumulator.
boundary_cut (tuple, default=(0.0, 0.0, 0.0)) – Boundary clipping parameters forwarded to accumulation/pushing kernels.
matrix_free (bool, default=False) – Apply the particle matrix matrix-free, with its symmetric filling stored per marker (
MarkerOperator), instead of assembling it: one spline evaluation and one deposition per marker and solver iteration instead of the products of all pairs of basis functions per marker. Needs 6 auxiliary marker columns. Ignored on the CuPy backend.
- class struphy.propagators.current_coupling_6d_density.CurrentCoupling6DDensity(energetic_ions: PICVariable | None = None, b_tilde: FEECVariable | None = None)[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\tilde{\mathbf{U}} \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\) such that
\[\begin{split}&\int_\Omega \rho_0 \frac{\partial \tilde{\mathbf{U}}}{\partial t} \cdot \mathbf V \,\textrm d \mathbf x = \frac{A_\textnormal{h}}{A_\textnormal{b}} \frac{1}{\varepsilon} \int_\Omega n_\textnormal{h}\tilde{\mathbf{U}} \times(\mathbf{B}_0+\tilde{\mathbf{B}}) \cdot \mathbf V \,\textrm d \mathbf x \qquad \forall \, \mathbf V \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\,, \\[2mm] &n_\textnormal{h}=\int_{\mathbb{R}^3}f_\textnormal{h}\,\textnormal{d}^3 \mathbf v\,.\end{split}\]Time discretization: Crank-Nicolson (implicit mid-point).
- class Variables[source]#
Bases:
objectContainer for variables advanced by
CurrentCoupling6DDensity.- Variables:
u (FEECVariable) – Fluid-like FEEC variable in one of
"Hcurl","Hdiv", or"H1vec".
- class Options(u_space: Literal['Hcurl', 'Hdiv', 'H1vec'] = 'Hdiv', solver: Literal['pbicgstab', 'bicgstab', 'gmres'] = 'pbicgstab', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, filter_params: FilterParameters | None = None, boundary_cut: tuple = (0.0, 0.0, 0.0), matrix_free: bool = False)[source]#
Bases:
OptionsBaseConfiguration options for
CurrentCoupling6DDensity.- Parameters:
u_space (LiteralOptions.OptsVecSpace, default="Hdiv") – FEEC space used for the unknown
uvariable.solver (LiteralOptions.OptsGenSolver, default="pbicgstab") – Iterative solver for the linear system, which is not symmetric (the density matrix is antisymmetric).
precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner applied to the FEEC mass matrix.
solver_params (SolverParameters, default=None) – Iterative-solver controls. If
None, defaults toSolverParameters().filter_params (FilterParameters, default=None) – Particle-to-grid filtering parameters used by the accumulator.
boundary_cut (tuple, default=(0.0, 0.0, 0.0)) – Boundary clipping parameters forwarded to the accumulation kernel.
matrix_free (bool, default=False) – Apply the density matrix matrix-free, with its antisymmetric filling stored per marker (
MarkerOperator), instead of assembling it: one spline evaluation and one deposition per marker and solver iteration instead of the products of all pairs of basis functions per marker. Needs 3 auxiliary marker columns. Ignored on the CuPy backend.
- class struphy.propagators.shear_alfven_current_coupling_5d.ShearAlfvenCurrentCoupling5D(energetic_ions: PICVariable | None = None)[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\mathbf U \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\) and \(\mathbf B \in H(\textnormal{div})\) such that
\[\begin{split}\left\{ \begin{aligned} \int \rho_0 &\frac{\partial \tilde{\mathbf U}}{\partial t} \cdot \mathbf V \, \textnormal{d} \mathbf{x} = \int \left(\tilde{\mathbf B} - \frac{A_\textnormal{h}}{A_b} \iint f^\text{vol} \mu \mathbf{b}_0\textnormal{d} \mu \textnormal{d} v_\parallel \right) \cdot \nabla \times (\tilde{\mathbf B} \times \mathbf V) \, \textnormal{d} \mathbf{x} \quad \forall \, \mathbf V \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\,, \,, \\ &\frac{\partial \tilde{\mathbf B}}{\partial t} = - \nabla \times (\tilde{\mathbf B} \times \tilde{\mathbf U}) \,. \end{aligned} \right.\end{split}\]Time discretization: Crank-Nicolson (implicit mid-point). System size reduction via
SchurSolver:\[\begin{split}\begin{bmatrix} \mathbf u^{n+1} - \mathbf u^n \\ \mathbf b^{n+1} - \mathbf b^n \end{bmatrix} = \frac{\Delta t}{2} \,. \begin{bmatrix} 0 & (\mathbb M^{2,n})^{-1} \mathcal {T^2}^\top \mathbb C^\top \\ - \mathbb C \mathcal {T^2} (\mathbb M^{2,n})^{-1} & 0 \end{bmatrix} \begin{bmatrix} {\mathbb M^{2,n}}(\mathbf u^{n+1} + \mathbf u^n) \\ \mathbb M_2(\mathbf b^{n+1} + \mathbf b^n) + \sum_k^{N_p} \omega_k \mu_k \hat{\mathbf b}¹_0 (\boldsymbol \eta_k) \cdot \left(\frac{1}{\sqrt{g(\boldsymbol \eta_k)}} \vec \Lambda² (\boldsymbol \eta_k) \right) \end{bmatrix} \,,\end{split}\]where \(\mathcal{T}^2 = \hat \Pi^1 \left[\frac{\mathbf B^2}{\sqrt{g}} \times \vec \Lambda^2\right]\) with the total field \(\mathbf B = \mathbf B_0 + \tilde{\mathbf B}\) (updated in each step) if
nonlinear, else the equilibrium field \(\mathbf B_0\), and \(\mathbb M^{2,n}\) is aWeightedMassOperatorsbeing weighted with \(\rho_\text{eq}\), the MHD equilibirum density.- class Variables[source]#
Bases:
objectContainer for variables advanced by
ShearAlfvenCurrentCoupling5D.- Variables:
u (FEECVariable) – Velocity variable in one of
"Hcurl","Hdiv", or"H1vec".b (FEECVariable) – Magnetic-field variable in
"Hdiv"space.
- class Options(ep_scale: float | None = None, u_space: Literal['Hcurl', 'Hdiv', 'H1vec'] = 'Hdiv', algo: Literal['implicit', 'explicit'] = 'implicit', solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', 'MultiGrid', 'StiffnessPreconditioner', None] = 'StiffnessPreconditioner', exb_correction: bool = True, solver_params: SolverParameters | None = None, filter_params: FilterParameters | None = None, butcher: ButcherTableau | None = None, nonlinear: bool = True)[source]#
Bases:
OptionsBaseConfiguration options for
ShearAlfvenCurrentCoupling5D.- Parameters:
ep_scale (float, default=None) – Factor of the energetic-particle contributions (accumulation): the mass ratio \(A_\textnormal{h}/A_\textnormal{b}\) of the energetic ions and the MHD bulk if
None.u_space (LiteralOptions.OptsVecSpace, default="Hdiv") – FEEC space used for velocity variable
u.algo ({"implicit", "explicit"}, default="implicit") – Time stepping algorithm.
solver (LiteralOptions.OptsSymmSolver, default="pcg") – Symmetric iterative solver.
precond (LiteralOptions.OptsShearAlfvenPrecond, default="StiffnessPreconditioner") – Preconditioner of the linear solve: of the mass-matrix block (
"MassMatrixPreconditioner","PolarMassMatrixPreconditioner") or the Kronecker (fast diagonalization) preconditioner of the whole Schur operator ("StiffnessPreconditioner", the default,ShearAlfvenSchurPreconditioner, built from the equilibrium part \(\mathbb T\) of the basis projection operator; withnonlinear=Trueit approximates the Schur operator with \(\mathbb T + \mathbb T^B\), since \(|\tilde{\mathbf B}| \ll |\mathbf B_0|\)). The Kronecker preconditioner needsalgo="implicit",solver="pcg"or"pcg_sr",u_space="Hdiv", tensor-product splines (no polar splines) and the global commuting projectors; otherwise it falls back (with a warning) to"MassMatrixPreconditioner"("PolarMassMatrixPreconditioner"with polar splines)."MultiGrid"is not available here.exb_correction (bool, default=True) – For
precond="StiffnessPreconditioner": add the auxiliary space correction of the \(\mathbf E \times \mathbf B\) flows, seeShearAlfvenPropagator.solver_params (SolverParameters, default=None) – Solver controls.
filter_params (FilterParameters, default=None) – Filter of the accumulated (particle-to-grid) vectors; defaults to
FilterParameters()(no filter). The three-point (binomial) filterFilterParameters(use_filter="three_point")reduces the noise, but the propagator then no longer conserves the energy exactly (the filter is applied to the accumulated vectors only, not to the fields seen by the particles).butcher (ButcherTableau, default=None) – Explicit Runge-Kutta tableau for
algo="explicit".nonlinear (bool, default=True) – If
True, the basis projection operator \(\mathcal T^2\) uses the total magnetic field \(\mathbf B_0 + \tilde{\mathbf B}\) (one operator, re-weighted in each step), else the equilibrium field \(\mathbf B_0\) only.
Particle and FEEC variables are updated.
- class struphy.propagators.pressure_coupling_6d.PressureCoupling6D[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\tilde{\mathbf{U}} \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\) and \(f\) such that
\[\begin{split}\int_\Omega \rho_0 &\frac{\partial \tilde{\mathbf{U}}}{\partial t} \cdot \mathbf V \,\textrm d \mathbf x = - \frac{A_\textnormal{h}}{A_\textnormal{b}} \nabla \cdot \tilde{\mathbb{P}}_{\textnormal{h},\perp} \cdot \mathbf V \,\textrm d \mathbf x \qquad \forall \, \mathbf V \in \{H(\textnormal{curl}), H(\textnormal{div}), (H^1)^3\}\,, \\[2mm] &\frac{\partial f_\textnormal{h}}{\partial t} - \left(\nabla \tilde{\mathbf U}_\perp \cdot \mathbf{v} \right) \cdot \frac{\partial f_\textnormal{h}}{\partial \mathbf{v}} =0\,, \\[2mm] &\tilde{\mathbb{P}}_{\textnormal{h},\perp} = \int_{\mathbb{R}^3}f_\textnormal{h}\mathbf{v}_\perp \mathbf{v}_\perp^\top \,\textnormal{d}^3 \mathbf v\,.\end{split}\]Time discretization: Crank-Nicolson (implicit mid-point). System size reduction via
SchurSolver:\[\begin{split}\begin{bmatrix} u^{n+1} - u^n \\ V^{n+1} - V^n \end{bmatrix} = \frac{\Delta t}{2} \begin{bmatrix} 0 & (\mathbb M^n)^{-1} V^\top (\bar {\mathcal X})^\top \mathbb G^\top (\bar {\mathbf \Lambda}^1)^\top \bar {DF}^{-1} \\ - {DF}^{-\top} \bar {\mathbf \Lambda}^1 \mathbb G \bar {\mathcal X} V (\mathbb M^n)^{-1} & 0 \end{bmatrix} \begin{bmatrix} {\mathbb M^n}(u^{n+1} + u^n) \\ \bar W (V^{n+1} + V^{n} \end{bmatrix} \,.\end{split}\]- class Variables[source]#
Bases:
objectContainer for variables advanced by
PressureCoupling6D.- Variables:
u (FEECVariable) – Fluid-like FEEC variable in one of
"Hcurl","Hdiv", or"H1vec".energetic_ions (PICVariable) – Energetic-ion particle variable in
"Particles6D"space.
- class Options(ep_scale: float = 1.0, u_space: Literal['Hcurl', 'Hdiv', 'H1vec'] = 'Hdiv', solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None, filter_params: FilterParameters | None = None, use_perp_model: bool = True, matrix_free: bool = True)[source]#
Bases:
OptionsBaseConfiguration options for
PressureCoupling6D.- Parameters:
ep_scale (float, default=1.0) – Scaling factor applied to energetic-particle pressure coupling terms.
u_space (LiteralOptions.OptsVecSpace, default="Hdiv") – FEEC space used for the unknown
uvariable.solver (LiteralOptions.OptsSymmSolver, default="pcg") – Symmetric iterative solver used by
SchurSolver.precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner applied to the mass matrix block for
u.solver_params (SolverParameters, default=None) – Iterative-solver controls. If
None, defaults toSolverParameters().filter_params (FilterParameters, default=None) – Particle-to-grid filtering parameters used by the accumulator. If
None, defaults toFilterParameters().use_perp_model (bool, default=True) – If
True, use the perpendicular pressure-coupling model; otherwise use the full-model kernels.matrix_free (bool, default=True) – Apply the 3x3 block particle matrix matrix-free, with its filling \(w_p G^{-1}(oldsymbol \eta_p)\) stored per marker (
MarkerOperatorwithn_blocks=3), instead of assembling its six symmetric blocks: per marker and solver iteration, one evaluation of each block and one deposition into each block instead of the products of all pairs of basis functions per marker. Needs 6 auxiliary marker columns. Ignored on the CuPy backend.
- class GT_MAT_G(derham, MAT, transposed=False)[source]#
Bases:
LinearOperatorClass for defining LinearOperator corresponding to \(G^\top (\text{MAT}) G \in \mathbb{R}^{3N^0 \times 3N^0}\) where \(\text{MAT} = V^\top (\bar {\mathbf \Lambda}^1)^\top \bar{DF}^{-1} \bar{W} \bar{DF}^{-\top} \bar{\mathbf \Lambda}^1 V \in \mathbb{R}^{3N^1 \times 3N^1}\).
- Parameters:
derham (struphy.feec.psydac_derham.Derham) – Discrete de Rham sequence on the logical unit cube.
MAT (list | MarkerOperator) – 3x3 nested list of the accumulated pressure terms (six of them distinct), or the matrix-free
MarkerOperatorof the whole 3x3 block matrix (n_blocks=3).
- property domain#
The domain of the linear operator - an element of Vectorspace
- property codomain#
The codomain of the linear operator - an element of Vectorspace
- property dtype#
The data type of the coefficients of the linear operator, upon convertion to matrix.
SPH#
Only particle variables are updated.
- class struphy.propagators.push_v_in_sph_pressure.PushVinSPHpressure[source]#
Bases:
PropagatorFor each marker \(p\), solves
\[\frac{\textnormal d \mathbf v_p(t)}{\textnormal d t} = \mathbf g - \sum_{i=1}^N w_i \left( \frac{\partial \mathcal U}{\partial \rho}(\boldsymbol \eta_p) + \frac{\partial \mathcal U}{\partial \rho}(\boldsymbol \eta_i) \right) DF^{-\top}\nabla W_h(\boldsymbol \eta_p - \boldsymbol \eta_i) \,,\]where \(\mathbf g\) is a constant acceleration and the second term corresponds to the pressure gradient. Here, \(\mathcal U(\rho)\) denotes the internal energy per unit mass as a function of the mass density \(\rho\) and \(DF^{-\top}\) denotes the inverse transpose Jacobian arising in the pull back of the gradient of the smoothing kernel \(W_h\) chosen from
sph_smoothing_kernels. Two choices of the internal energy are implemented:Isothermal closure: \(\mathcal U(\rho) = \kappa \, \ln(\rho)\), where \(\kappa\) is constant.
Polytropic closure: \(\mathcal U(\rho) = \kappa \, \rho^{\gamma - 1} / (\gamma - 1)\), where \(\kappa\) is the polytropic constant and \(\gamma = C_p / C_v\) is the polytropic index.
Time stepping is forward Euler.
- class Variables[source]#
Bases:
objectContainer for variables advanced by
PushVinSPHpressure.- Variables:
fluid (SPHVariable) – SPH particle variable in
"ParticlesSPH"space.
- class Options(kernel_type: Literal['trigonometric_1d', 'gaussian_1d', 'linear_1d', 'trigonometric_2d', 'gaussian_2d', 'linear_2d', 'trigonometric_3d', 'gaussian_3d', 'linear_isotropic_3d', 'linear_3d'] = 'gaussian_2d', kernel_width: tuple | None = None, algo: Literal['forward_euler'] = 'forward_euler', gravity: tuple = (0.0, 0.0, 0.0), kappa: float = 1.0, thermodynamics: Literal['isothermal', 'polytropic'] = 'isothermal')[source]#
Bases:
OptionsBaseConfiguration options for
PushVinSPHpressure.- Parameters:
kernel_type (LiteralOptions.OptsKernel, default="gaussian_2d") – Smoothing kernel used for density and pressure-force evaluation.
kernel_width (tuple, default=None) – Kernel widths per logical direction. If
None, defaults to(1 / n_i)based on sorting boxes.algo ({"forward_euler"}, default="forward_euler") – Time stepping algorithm for velocity pushing.
gravity (tuple, default=(0.0, 0.0, 0.0)) – Constant gravity vector added in the SPH pressure push.
kappa (float, default=1.0) – Coefficient in the internal energy function.
thermodynamics ({"isothermal", "polytropic"}, default="isothermal") – Thermodynamic closure selecting the SPH pressure kernel.
Only particle variables are updated.
- class struphy.propagators.push_v_in_viscous_potential.PushVinViscousPotential[source]#
Bases:
PropagatorFor each marker \(p\), solves
\[\frac{\textnormal d \mathbf v_p(t)}{\textnormal d t} = \kappa_p \sum_{i=1}^N w_i \left( \frac{1}{\rho^{N,h}(\boldsymbol \eta_p)} + \frac{1}{\rho^{N,h}(\boldsymbol \eta_i)} \right) DF^{-\top}\nabla W_h(\boldsymbol \eta_p - \boldsymbol \eta_i) \,,\]where \(DF^{-\top}\) denotes the inverse transpose Jacobian, and with the smoothed density
\[\rho^{N,h}(\boldsymbol \eta) = \frac 1N \sum_{j=1}^N w_j \, W_h(\boldsymbol \eta - \boldsymbol \eta_j)\,,\]where \(W_h(\boldsymbol \eta)\) is a smoothing kernel from
sph_smoothing_kernels. Time stepping:Explicit from
ButcherTableau
- class Variables[source]#
Bases:
objectContainer for variables advanced by
PushVinViscousPotential.- Variables:
fluid (SPHVariable) – SPH particle variable in
"ParticlesSPH"space.
- class Options(kernel_type: Literal['trigonometric_1d', 'gaussian_1d', 'linear_1d', 'trigonometric_2d', 'gaussian_2d', 'linear_2d', 'trigonometric_3d', 'gaussian_3d', 'linear_isotropic_3d', 'linear_3d'] = 'gaussian_2d', kernel_width: tuple | None = None, algo: Literal['forward_euler'] = 'forward_euler', mu: float = 1.0)[source]#
Bases:
OptionsBaseConfiguration options for
PushVinViscousPotential.- Parameters:
kernel_type (LiteralOptions.OptsKernel, default="gaussian_2d") – Smoothing kernel used for SPH evaluations.
kernel_width (tuple, default=None) – Kernel widths per logical direction. If
None, defaults to(1 / n_i)based on sorting boxes.algo ({"forward_euler"}, default="forward_euler") – Time stepping algorithm for the viscous potential push.
mu (float, default=1.0) – Dynamic viscosity coefficient used by the viscosity tensor kernel. Must be non-negative.
Miscellaneous#
- class struphy.propagators.hasegawa_wakatani_step.HasegawaWakataniStep(phi: FEECVariable | None = None)[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \((n, \omega) \in H^1 \times H^1\) such that
\[\begin{split}&\int_\Omega\frac{\partial n}{\partial t} m \,\textrm d \mathbf x = \int_\Omega C(x, y)(\phi - n) \, m \,\textrm d \mathbf x - \int_\Omega \phi [n, m] \,\textrm d \mathbf x - \kappa \int_\Omega \partial_y \phi \,m \,\textrm d \mathbf x - \nu \int_\Omega \nabla n \cdot \nabla m \,\textrm d \mathbf x \qquad \forall m \in H^1\,, \\[2mm] &\int_\Omega\frac{\partial \omega}{\partial t} \psi \,\textrm d \mathbf x = \int_\Omega C(x, y)(\phi - n) \, \psi \,\textrm d \mathbf x - \int_\Omega \phi [\omega, \psi] \,\textrm d \mathbf x - \nu \int_\Omega \nabla \omega \cdot \nabla \psi \,\textrm d \mathbf x \qquad \forall \psi \in H^1\,,\end{split}\]where \(\phi \in H^1\) is a given stream function, \(C = C(x, y)\), \(\kappa\) and \(\nu\) are constants and \([a, b] = \partial_x a \partial_y b - \partial_y a \partial_x b\).
Time discretization: explicit Runge-Kutta, see
ODEsolverFEEC.- Parameters:
n0 (StencilVector) – The density.
omega0 (StencilVector) – The stream function.
phi (SplineFuncion) – The potential.
c_fun (str) – Defines the function c(x,y) in front of (phi - n).
kappa (float) – Equation parameters.
nu (float) – Equation parameters.
algo (str) – See
ButcherTableaufor available algorithms.M0_solver (dict) – Solver parameters for M0 inversion.
- class Variables[source]#
Bases:
objectContainer for variables advanced by
HasegawaWakataniStep.- Variables:
n (FEECVariable) – Density variable in
"H1"space.omega (FEECVariable) – Vorticity variable in
"H1"space.
- class Options(c_fun: Literal['const'] = 'const', coupling: float = 1.0, kappa: float = 1.0, nu: float = 0.01, butcher: ButcherTableau | None = None, solver: Literal['pcg', 'pcg_sr', 'cg'] = 'pcg', precond: Literal['MassMatrixPreconditioner', 'PolarMassMatrixPreconditioner', None] = 'MassMatrixPreconditioner', solver_params: SolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
HasegawaWakataniStep.- Parameters:
c_fun ({"const"}, default="const") – Choice of coupling profile \(C(x,y)\) used in the model.
coupling (float, default=1.0) – Constant value of \(C\) when
c_fun="const".kappa (float, default=1.0) – Constant multiplying the background-gradient drift term.
nu (float, default=0.01) – Diffusion coefficient in density and vorticity equations.
butcher (ButcherTableau, default=None) – Butcher tableau for explicit Runge-Kutta integration. If
None, defaults toButcherTableau().solver (LiteralOptions.OptsSymmSolver, default="pcg") – Symmetric iterative solver used for
M0inversions.precond (LiteralOptions.OptsMassPrecond, default="MassMatrixPreconditioner") – Preconditioner used with
M0inversions.solver_params (SolverParameters, default=None) – Iterative-solver controls. If
None, defaults toSolverParameters().
- class struphy.propagators.time_dependent_source.TimeDependentSource[source]#
Bases:
PropagatorPropagates a source term \(S(t) \in V_h^n\) of the form
\[S(t) = \sum_{ijk} c_{ijk} \Lambda^n_{ijk} * h(\omega t)\,,\]where \(h(\omega t)\) is one of the functions in Notes.
Notes
\(h(\omega t) = \cos(\omega t)\) (default)
\(h(\omega t) = \sin(\omega t)\)
- class Variables[source]#
Bases:
objectContainer for variables advanced by
TimeDependentSource.- Variables:
source (FEECVariable) – Source coefficient field in any FEEC space (
"H1","Hcurl","Hdiv","L2"or"H1vec").
- class Options(omega: float = 6.283185307179586, hfun: Literal['cos', 'sin'] = 'cos')[source]#
Bases:
OptionsBaseConfiguration options for
TimeDependentSource.- Parameters:
omega (float, default=2*pi) – Angular frequency of the time-dependent modulation.
hfun ({"cos", "sin"}, default="cos") – Time modulation function applied to initial coefficients.
- class struphy.propagators.two_fluid_quasi_neutral_full.TwoFluidQuasiNeutralFull[source]#
Bases:
PropagatorFEEC discretization of the following equations: find \(\mathbf u \in H(\textnormal{div})\), \(\mathbf u_e \in H(\textnormal{div})\) and \(\mathbf \phi \in L^2\) such that
\[\begin{split}\int_{\Omega} \partial_t \mathbf{u}\cdot \mathbf{v} \, \textrm d\mathbf{x} &= \int_{\Omega} \phi \nabla \! \cdot \! \mathbf{v} \, \textrm d\mathbf{x} + \int_{\Omega} \mathbf{u}\! \times \! \mathbf{B}_0 \cdot \mathbf{v} \, \textrm d\mathbf{x} + \nu \int_{\Omega} \nabla \mathbf{u}\! : \! \nabla \mathbf{v} \, \textrm d\mathbf{x} + \int_{\Omega} f \mathbf{v} \, \textrm d\mathbf{x} \qquad \forall \, \mathbf{v} \in H(\textrm{div}) \,. \\[2mm] 0 &= - \int_{\Omega} \phi \nabla \! \cdot \! \mathbf{v_e} \, \textrm d\mathbf{x} - \int_{\Omega} \mathbf{u_e} \! \times \! \mathbf{B}_0 \cdot \mathbf{v_e} \, \textrm d\mathbf{x} + \nu_e \int_{\Omega} \nabla \mathbf{u_e} \!: \! \nabla \mathbf{v_e} \, \textrm d\mathbf{x} + \int_{\Omega} f_e \mathbf{v_e} \, \textrm d\mathbf{x} \qquad \forall \ \mathbf{v_e} \in H(\textrm{div}) \,. \\[2mm] 0 &= \int_{\Omega} \psi \nabla \cdot (\mathbf{u}-\mathbf{u_e}) \, \textrm d\mathbf{x} \qquad \forall \, \psi \in L^2 \,.\end{split}\]Time discretization: fully implicit.
- class Variables[source]#
Bases:
objectContainer for variables advanced by
TwoFluidQuasiNeutralFull.- Variables:
u (FEECVariable or None) – Ion velocity variable in
"Hdiv"space.ue (FEECVariable or None) – Electron velocity variable in
"Hdiv"space.phi (FEECVariable or None) – Electrostatic potential variable in
"L2"space.
- class Options(nu: float = 1.0, nu_e: float = 1.0, eps_norm: float = 0.001, boundary_data_u: dict[tuple[int, int], Callable] | None = None, boundary_data_ue: dict[tuple[int, int], Callable] | None = None, source_u: Callable | None = None, source_ue: Callable | None = None, stab_sigma: float | None = None, solver: Literal['pbicgstab', 'bicgstab', 'gmres'] = 'gmres', solver_params: SolverParameters | None = None)[source]#
Bases:
OptionsBaseConfiguration options for
TwoFluidQuasiNeutralFull.- Parameters:
nu (float, default=1.0) – Ion viscosity coefficient.
nu_e (float, default=1.0) – Electron viscosity coefficient.
eps_norm (float, default=1e-3) – Normalization/scaling parameter in Lorentz coupling terms.
boundary_data_u (dict[tuple[int, int], Callable] or None, default=None) – Inhomogeneous Dirichlet data for ion velocity faces.
boundary_data_ue (dict[tuple[int, int], Callable] or None, default=None) – Inhomogeneous Dirichlet data for electron velocity faces.
source_u (Callable or None, default=None) – Source term for ion momentum equation.
source_ue (Callable or None, default=None) – Source term for electron momentum equation.
stab_sigma (float or None, default=None) – Optional stabilization coefficient for electron block.
solver (LiteralOptions.OptsGenSolver, default="gmres") – Linear/saddle-point solver used for the global system.
solver_params (SolverParameters or None, default=None) – Solver controls.