Discontinuous Galerkin time-domain solver for Maxwell's equations built on Trixi.jl.
Started at the JuliaCon 2026 hackathon to learn Trixi.jl.
- Maxwell's curl equations for
(E, H)in normalized units (c = Z = 1, relativeepsilon,mu, normalized conductivitysigma) - Materials: homogeneous, or piecewise constant per element carried as passive state components, with one-sided interface fluxes on the
DGSEMmeshes - Dispersive media: Drude and Lorentz poles through polarization currents, homogeneous or per element, with a gold preset (Vial et al. 2005)
- Boundaries: perfect electric and magnetic conductors, Silver-Mueller, incident fields, uniaxial perfectly matched layer
- Sources: Hertzian dipole with analytic reference field, plane waves with Gaussian or modulated signals, total-field/scattered-field injection on tetrahedra and
P4estMesh, quadrature-projected sources forDGSEM - Gambit and Gmsh reader, quadratic Gmsh tetrahedra give curved elements
- VTK output writer with ParaView scripts in
utils/paraview/, point probes (tetrahedra and allDGSEMmeshes), divergence diagnostics - On-the-fly Fourier transform on surfaces (tetrahedra): scattering, extinction and absorption cross sections on the TF/SF surface, transmittance and reflectance on detector planes, with Mie series and Airy formula as references
- Solvers:
DGMultion tetrahedra,DGSEMonTreeMesh,StructuredMesh,P4estMeshandT8codeMesh
Elixirs can be found in examples/, grouped by mesh type.
The tetrahedral ones in examples/dgmulti_3d/ use meshes from nodal-dg, MIDG2 and OpenSEMBA.
The DGSEM mesh types all run the cavity; P4estMesh additionally covers the Fresnel, dipole, PML and TF/SF cases, also with adaptive refinement.
Currently they cover
- PEC cavity
- periodic plane wave
- a lossy cavity
- Silver-Mueller absorption
- Fresnel half space (tetrahedra and
P4estMesh) - dielectric sphere, and its scattering cross section against Mie theory on curved tetrahedra
- dielectric slab transmittance and reflectance against the Airy formula, also with Drude and Lorentz poles
- cavity filled with a Drude medium, and a Drude sphere against Mie theory
- extinction spectrum of a 40 nm gold nanosphere against Mie theory
- dipole in free space (tetrahedra and
P4estMeshwith adaptive refinement) and in a PML box (tetrahedra andP4estMesh) - TF/SF injection without scatterer (tetrahedra and
P4estMesh) and scattering off a PEC sphere
The package currently needs Trixi.jl main, which includes 3D DGMulti slicing. In a Julia 1.13 or newer session:
using Pkg
Pkg.add(url = "https://github.com/trixi-framework/Trixi.jl.git", rev = "main")
Pkg.develop(path = "path/to/MaxwellTrixi.jl")
Pkg.instantiate()Reading Gmsh files needs the optional Gmsh package, which activates the MaxwellTrixiGmshExt extension:
Pkg.add("Gmsh")An ODE integrator is required to run elixirs, for example OrdinaryDiffEqLowStorageRK.
using Trixi, MaxwellTrixi, OrdinaryDiffEqLowStorageRK
trixi_include(pkgdir(MaxwellTrixi, "examples", "dgmulti_3d", "elixir_maxwell_3d_cavity.jl"))Keyword arguments of trixi_include override the variables of the elixir, for example tspan = (0.0, 0.5) or polydeg = 2.
Elixirs on imported meshes download their mesh on first use.
Each file in utils/visualize/ matches its simulation elixir's filename and
contains that scenario's slice settings, figure layout, overlays and video
configuration. The P4est dipole uses the subdirectory p4est_3d_dgsem/ to
distinguish it from the DGMulti dipole. common.jl contains shared sampling,
interpolation and plot-update routines.
Prepare the environment with Julia 1.13, then run a scenario:
julia --project=utils/visualize -e 'using Pkg; Pkg.instantiate()'
# One figure or video:
julia --project=utils/visualize utils/visualize/elixir_maxwell_3d_pml.jl
julia --project=utils/visualize utils/visualize/elixir_maxwell_3d_pml.jl --video
# AMR dipole:
julia --project=utils/visualize utils/visualize/p4est_3d_dgsem/elixir_maxwell_3d_dipole.jl --video
# All figures or all videos:
julia --project=utils/visualize utils/visualize/make_all.jl
julia --project=utils/visualize utils/visualize/make_all.jl --videoPNGs go to docs/figures/ and MP4s to docs/videos/. Scenarios cover cavity,
Fresnel, TF/SF, dipole, AMR dipole, PML, dielectric sphere, PEC sphere,
periodic waves and Mie scattering.
Video simulations run for twice the span of the original animations, while still figures retain their original snapshot times. Videos use 241 frames at 30 fps by default, fixed color scales and left-aligned updating titles. Dipole videos update both the field slice and the numerical versus analytical line probe. The P4est video captures the current mesh with each frame, so refinement and coarsening remain synchronized with the field and probe. PML retains the side-by-side comparison and narrow scale of its static figure; the stronger outgoing pulse saturates that scale, highlighting late reflections. Imported tetrahedra use affine geometry for the current native slicer. TF/SF outlines mark total field inside and scattered field outside.
Use trixi_include to override a visualization's assignments, just as with a
simulation elixir. For example, for a short preview in julia --project=utils/visualize:
using Trixi
trixi_include("utils/visualize/elixir_maxwell_3d_pml.jl";
video = true, frame_times = [0.0, 0.05, 0.1],
simulation_overrides = (; polydeg = 1, cells_per_dimension = (2, 2, 2)))ParaView is used only for inspecting the full 3D adaptive mesh of the P4est
dipole. Its single view combines a clipped half-domain element mesh with a
Hy slice near y = 0, so you can rotate the view and follow refinement
throughout the volume. The CairoMakie figure and GLMakie video show the same
simulation as a 2D slice with the analytical probe comparison.
The script opens at the first saved frame (t = 0). Press Play to animate;
change the clipping plane in the pipeline to inspect another part of the mesh.
Set PV_TIME to choose another initial time (rounded to the nearest saved
frame), or DIPOLE_PVD and DIPOLE_MESH_PVD for other collection paths.
The dipole elixir uses Trixi.jl's own SaveSolutionCallback and is converted with Trixi2Vtk.jl. The P4est dipole carries the callback; after running it,
using Trixi2Vtk
trixi2vtk(joinpath("out", "solution_*.h5"), output_directory = joinpath("out", "vtk"),
pvd = "dipole")
trixi2vtk(joinpath("out", "solution_*.h5"), output_directory = joinpath("out", "vtk_mesh"),
nvisnodes = 2, pvd = "dipole")The first call preserves the high-order field representation in Lagrange hexahedra; the second uses only the element corners for a linear mesh overlay. Then paraview --script=utils/paraview/dipole_p4est.py.
For a headless preview after conversion:
PV_SCREENSHOT=/tmp/amr.png pvpython utils/paraview/dipole_p4est.pyThe dipole elixir saves the initial and final states and every 100 accepted steps. The number of frames and their sizes depend on CFL stepping and the evolving adaptive mesh. trixi_include(elixir; save_solution_interval = 20) samples the animation more frequently. The explicit pvd names above keep the script paths independent of the saved step numbers.
Trixi2Vtk v0.3.33 reads 3D P4estMesh refinement levels using the 2D p4est struct layout, so its converted levels cell data are unreliable. The dipole script uses element edges to show refinement geometrically instead of coloring by these levels. Check whether this converter issue has been fixed when using another version.
The documentation explains the implementation together with its equations, examples and videos. To build it locally from the repository root:
julia --project=docs -e 'using Pkg; Pkg.instantiate()'
julia --threads=1 --project=docs docs/make.jl
python3 -m http.server 8000 --bind 127.0.0.1 --directory docs/buildOpen http://127.0.0.1:8000. The site uses figures from docs/figures/, replacing
them with matching videos from docs/videos/ when available. GitHub Actions
publishes the documentation with figures by default; video rendering is opt-in.
See Building and publishing for editing and build instructions.
using Pkg
Pkg.test("MaxwellTrixi")Full-resolution validation runs go to the Otus cluster through a GitLab mirror, started from Actions → HPC. See Building and publishing for the cases, the result files and the setup.
The figures below are produced by the scenario scripts in utils/visualize/ using
CairoMakie. Run julia --project=utils/visualize utils/visualize/make_all.jl to regenerate
them, or add --video for their GLMakie animations.
PEC cavity with exact TM mode.
Gaussian pulse hitting a dielectric half space at x = 0 with exact Fresnel solution.
Total-field/scattered-field box in free space.
Hertzian dipole on tetrahedra. Point probe compares with the exact solution.
The source is narrower than the elements around it can resolve, which shows in the slice.
The same dipole with DGSEM on P4estMesh and adaptive mesh refinement, starting from the same 8 x 8 x 8 cells. The elements around the source are refined twice, the pulse is followed with one refinement level and by t = 2 fills the box.
Comparison of UPML with Silver-Mueller boundary condition.
Dielectric sphere (epsilon = 2.25) scattering with gaussian plane-wave intiatial condition.
PEC sphere in a box. The box is used to inject gaussian plane-wave with TF/SF.
Following the Trixi.jl style, using utils/trixi-format.jl from Trixi.jl.
MIT, see LICENSE.







