Repository navigation
DriftKineticMaxwellCoupling: implicit energy-conserving drift-kinetic Maxwell step with polarization and quasi-neutral limit; model DriftKineticMaxwell - #823
Merged
Conversation
…ling Semi-discrete systems in struphy operator notation (M1, M2, C, Lambda^1, W), Crank-Nicolson update, SchurSolver blocks A, B, C, resulting Schur system, accumulated matrix/vector and conserved quantities. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…w docs] The Schur operator M1 + dt^2/4 C^T M2 C is preconditioned by StiffnessPreconditioner(curl, mid=dt^2/4 M2, stab=M1), built per time step in prepare_time_step. Iterations hardly depend on dt (hollow cylinder 16x16x4, p=3: 39 instead of 411 PCG iterations at dt = 100 h); for dt <~ h the mass preconditioner stays slightly better. algo="explicit" falls back to the mass preconditioner, solver="cg" drops the preconditioner with a warning. New test file test_maxwell_weak_ampere.py. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nt preconditioners and solvers [preview docs] - remove the iteration-count sentence from the MaxwellWeakAmpere docstring - full-path cross-references (Derham.curl, SchurSolver, prepare_time_step, kernel functions in their *_kernels modules); feectools inverse as literal - dev-feec.rst: new sections Preconditioners (struphy.feec.preconditioner, struphy.polar.kron_2dx1d, multigrid preconditioner) and Linear solvers (schur_solver, solver) - dev-pic.rst: vlasov_maxwell accumulation kernel, new Pusher kernels section with push_v_with_efield Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The raw docstrings of SchurSolver, SchurSolverFull and SchurSolverFull3 used \\text and \\right (a LaTeX line break in a raw string), which broke the math; rewritten with pmatrix. Markdown links replaced by plain names, and the SchurSolver Parameters section now matches the signature (precond, solver_params). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n [preview docs] Lynch, Rice and Thomas (1964) for the method, Sangalli and Tani (2016) for preconditioning spline stiffness matrices; short description of the generalized eigendecomposition used by KroneckerSumSolver. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nalization [preview docs] Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
One implicit Crank-Nicolson step for Ampere (kappa dE/dt), Faraday and the electric force on the markers, with free coefficients c1, c2, c3. The Schur system kappa M1 + dt^2/4 (c1 C^T M2 C + c2 c3 A) is solved for the mid-point field; it stays well-posed for kappa -> 0 (no displacement current), where the plasma term absorbs the kernel of the curl-curl operator. Exact energy conservation (full-f), stiffness preconditioner with stabilization kappa + dt^2 c2 c3 n_ref / 4. The model subclasses VlasovMaxwellOneSpecies (propagator linking factored out), adds the scalar en_H and a Weibel example for kappa = 1 and 0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…general coefficients - assert no polar splines in VlasovMaxwellCoupling (the V1 buffers are tensor-product) - test_discrete_equations: residual of the CN Ampere and Faraday laws with c1, c2, c3 all different (catches wrong coefficients in the Schur system or the pusher) - energy test also with explicit coefficients; test the solver rebuild on dt change with a given n_ref; drop the tautological e^n-independence test Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… other 6D couplings With matrix_free=True the vector j and the fillings w_p G^-1 per marker are accumulated by vlasov_maxwell_mf (AccumulatorVector) and the particle matrix in the Schur operator is a MarkerOperator (marker_operator_symm_dot). Tests: same steps as assembled (test_matrix_free_6d.py, kappa = 1 and 0), discrete equations with both variants. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…lasovMaxwellCoupling Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…kappa = 0 At kappa = 0 the constraint holds at the mid-point only, so its residual r = c1 C^T M2 b - c2 j(V) changes sign in every step: initial data that violate it (e.g. marker noise in J) oscillate with period 2 without damping. The option initial_projection (default True) replaces (b, V) once in the setup (first prepare_time_step) by the mid-point state of one step, an implicit Euler step of dt/2 that satisfies the constraint exactly and does not depend on dt. Tests: sign flip without, vanishing residual, dt independence and stationary steps with the projection; step-level tests at kappa = 0 run without it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
e_mid of the projection is an impulse of size 1/dt; writing it into e replaced the initial Poisson field in the saved t = 0 data (en_E, gauss_error). At kappa = 0, e does not enter the steps, so only b and V are projected. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… kappa = 0 1D-3V Weibel instability (k = 1.25, 1e5 full-f markers, dt = 1, t = 160) in the quasi-neutral limit; the growth rate fitted to the magnetic energy for 80 < t < 150 must be within 15 % of the analytical 0.02784 (fitted: 0.02506, the same on 1, 2 and 4 ranks). About 45 s serial, 25 s on 2 or 4 ranks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… with the same loading With pseudo-random markers the fitted growth rate is noise-limited (about 10 % low, 7 % seed-to-seed scatter at 1e5 markers). With the antithetic Sobol loading the current noise mostly cancels: gamma = 0.02770 / 0.02784 / 0.02782 on 1 / 2 / 4 ranks against the analytical 0.02784, so the tolerance is now 5 %. Review fixes: comments on the kappa = 0 dispersion relation and on reading the scalars, rmtree(ignore_errors=True). The example (Tend = 400) now gives gamma = 0.02805 (kappa = 1) and 0.02758 (kappa = 0). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eed, 7 % tolerance Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…nergy-conserving drift-kinetic Maxwell coupling with quasi-neutral limit - New propagator DriftKineticMaxwellCoupling: Maxwell's equations, the parallel acceleration of the guiding centers and (option exb_current) the E x B current in one Crank-Nicolson step at frozen positions; Schur system for e_mid with M_E = kappa M1 + chi M1gyro (linearized polarization current, chi = alpha^2 by default), parallel response A_par (rank one per marker) and antisymmetric E x B operator S. Exact energy conservation, well-posed for kappa = 0 (requires kappa + chi > 0). Assembled (dk_maxwell) and matrix-free (dk_maxwell_mf with marker_operator_rank1_dot / marker_operator_asym_dot) particle matrices, push_vpar_with_efield kernel, StiffnessPreconditioner, initial projection for kappa = 0. - PushGuidingCenterBxEstar / PushGuidingCenterParallel accept either phi (unchanged) or a new e_field (Hcurl): E* = E - eps mu grad|B| (gauge-invariant formulation). - New model DriftKineticMaxwell: coupling -> push_para -> push_bxe(e_field). - Tests: energy conservation, assembled = matrix-free, operator symmetries, discrete equations, initial projection, options; e_field = -grad phi reproduces the phi path; verification against the cold-slab dispersion relation (inertial Alfven wave, with and without E x B current, kappa = 0 and 1). - Example inertial_alfven_wave. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…t algorithm; no CuPy; docs - With e_field (no potential), the discrete-gradient correction removes the work of E along the step (O(1) inconsistency, e.g. no E x B drift across grad|B|): a discrete-gradient algo now falls back to "explicit" with a warning (keeps the default options working); the phi path is unchanged. Test with the default algo added. - DriftKineticMaxwellCoupling asserts a non-CuPy backend (no CUDA kernels yet); document that chi uses the MHD-equilibrium density and the missing f0 response with the control variate. - Example: explicit E* x b0 push. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…to drift-kinetic-maxwell
…parameters lam and kappa - species equation parameters lam = Omega_c x / c (new) and kappa = Omega_p x / c (redefined, was Omega_p t, unused); overridable in the model constructor - VlasovMaxwellQuasiNeutral: velocity scale cyclotron, constructor arguments lam, kappa; Ampere's law lam^2 dE/dt = curl B - kappa^2/eps J; doc_normalization directly below doc_pde; energies en_E, en_B, en_f and en_tot = conserved energy of the coupling step; Gauss' law lam^2 div E = c2 rho for the initial Poisson solve, which is skipped for lam = 0 (gauss_error NaN) - VlasovMaxwellCoupling: option kappa (displacement current) renamed to lam (with coefficient lam^2); defaults lam = eps lam_s, c2 = kappa_s^2 eps, c3 = 1/eps, i.e. lam_s, kappa_s^2, 1 for the cyclotron and 1, alpha^2/eps, 1/eps for the light velocity scale; energies() parts - docs: lam and kappa under Equation parameters; cyclotron velocity without the spurious 2 pi; models/AGENTS.md: doc_normalization directly below doc_pde - example, verification test (gamma unchanged, 0.02770) and tests with lam, kappa Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…sign of lam, AGENTS.md Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e paragraph under Equation parameters, fix the energy reference in VlasovMaxwellCoupling The :meth: reference to VlasovMaxwellCoupling.energy was inside a math block (not rendered); moved into the text. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…to drift-kinetic-maxwell
…rs lam and kappa; safeguard the parallel force - Model DriftKineticMaxwell: velocity scale "thermal" (as DriftKineticElectrostaticAdiabatic), default BaseUnits(kBT=1.0) (also in the generated default parameter file), constructor overrides epsilon, lam, kappa; Ampere's law eps^2 lam^2 dE/dt + eps^2 kappa^2 n0/|B0|^2 dE_perp/dt = curl B - eps kappa^2 J_gc; doc_pde with doc_normalization below it (thermal scale only); scalars en_E, en_B, en_f from the parts of the conserved energy. - DriftKineticMaxwellCoupling: option kappa (displacement current) renamed to lam (coefficient lam^2), defaults lam = eps lam_s, chi = (eps kappa_s)^2, c2 = kappa_s^2 eps, c3 = 1/eps (#821 equation parameters); energies(); the internal E x B scaling of the assembled matrix renamed to skew (also in the dk_maxwell kernel). - The parallel electric force b* . E / eps is applied only by the coupling step: the model asserts that push_para gets no potential and no electric field, doc_discretization lists which propagator applies each term, and a test checks that push_para leaves v_par unchanged for E != 0 (and changes it when given E). - Tests, verification test and example in the new parameters (eps = 0.5, lam = 2, kappa = 6 give the same coefficients as before; same frequencies). Raw docstrings for the pusher constructors (LaTeX). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
spossann
marked this pull request as ready for review
October 23, 2026 14:05
# Conflicts: # doc/sections/subsections/propagators-avail.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Model docs (preview): https://struphy-hub.github.io/docs-preview/pr-828/sections/subsections/models-all.html#struphy.models.DriftKineticMaxwell
The drift-kinetic counterpart of
VlasovMaxwellCoupling/VlasovMaxwellQuasiNeutral(#821, merged).What
DriftKineticMaxwellCoupling: Maxwell's equations, the parallel acceleration of the guiding centers (dk_maxwell) or matrix-free (dk_maxwell_mfwith the existing rank-1/antisymmetricMarkerOperatorkernels).StiffnessPreconditioner. Initial projection forM1gyro. It is needed because the drift-kinetic ordering removes the polarization drift. Without it the quasi-neutral limitlam**2 + chi > 0is asserted.PushGuidingCenterBxEstar/PushGuidingCenterParallelaccept eitherphi(the existing path, unchanged) or a newe_field(Hcurl), withe_fieldonly the explicit algorithm is consistent: the discrete gradients conserve a Hamiltonian with a potential, and without one they would remove the work of E along the step.algotherefore falls back toexplicitwith a warning, so the default options keep working.DriftKineticMaxwell, splittingcoupling→push_para→push_bxe(e_field), and the exampleinertial_alfven_wave.DriftKineticElectrostaticAdiabatic, using the equation parametersand the parallel force has the coefficient
lam(coefficientepsilon,lamandkappaas overrides. The default isBaseUnits(kBT=1.0), also in the generated params file.push_paranophiand noe_field, and asserts this.doc_discretizationlists which propagator applies each term.push_paraleavesAnalysis note with the derivations (dispersion relation, well-posedness, energy across the splitting):
notes/drift_kinetic_maxwell.md(local, not in the repo).Energy across the splitting
The coupling step conserves energy exactly. The full splitting does so only when$\nabla|B_0| = 0$ and either $\mu = 0$ or $\tilde B_\parallel = 0$ . Two pieces are missing:
Both are follow-ups.
Tests
Separate review agents went over the branch before each push. The one real finding (discrete gradients with
e_field) is fixed in the second commit; the review of the normalization commit found no bugs.test_drift_kinetic_maxwell_coupling.py(1 and 4 ranks):test_push_guiding_center_e_field.py:e_field = -grad phireproduces thephipath exactly, for both pushers, also with the default (discrete-gradient)algothrough the fallback.test_verif_DriftKineticMaxwell.py: frequency of the cold-slab shear mode against the roots of the dispersion determinant:The deviations are marker noise (−0.35 % with 4× more markers). Energy over the whole splitting is conserved to$10^{-9}$ .
Generic model test, example test run, kernel catalog counts bumped. Regression: the generic tests of
DriftKineticElectrostaticAdiabatic,GuidingCenter,ToyDrift,LinearMHDDriftkineticCCandtest_current_coupling_5d.pypass.🤖 Generated with Claude Code