A Weibel Instability
Last updated on 2026-04-06 | Edit this page
Estimated time: 30 minutes
Overview
Questions
How does a momentum anisotropy in a plasma generate magnetic fields from scratch?
Objectives
Set up, run, and visualize a 2D Weibel instability simulation with WarpX. Observe the spontaneous generation of magnetic fields from counter-streaming electron beams.
Introduction
The Weibel instability (also known as the current filamentation instability) is a fundamental electromagnetic instability that arises when there is an anisotropy in the momentum distribution of a plasma. Unlike the two-stream instability, which generates electrostatic waves, the Weibel instability generates magnetic fields.
The physical mechanism is as follows: when two electron populations stream past each other, any small magnetic perturbation will deflect the electrons, causing them to bunch into current filaments. These current filaments in turn amplify the magnetic field that caused the bunching, leading to exponential growth until the fields are strong enough to isotropize the distribution.
This instability plays a key role in astrophysical plasmas (e.g. collisionless shocks in gamma-ray bursts) and in laser-plasma interactions.
In this episode, we simulate it in 2D with two counter-streaming electron populations and periodic boundaries.
Setup
Make sure to download the input file.
Whenever you need to prepare an input file, this is where you want to go.
OUTPUT
####################
### MY CONSTANTS ###
####################
# PLASMAS
my_constants.n0 = ... # [m^-3]
my_constants.T0 = ...*q_e # [J]
my_constants.beta0 = ... # [-]
my_constants.omega_pe = sqrt(n0*q_e**2/(m_e*epsilon0)) # [1/s]
my_constants.skin_depth = clight / omega_pe # [m]
# BOX
my_constants.Lx = ...*skin_depth
my_constants.nx = ...
my_constants.dx = Lx/nx
# TIME
my_constants.cfl = ...
my_constants.T = .../omega_pe
my_constants.dt = cfl*dx/(sqrt(2)*clight)
my_constants.nt = floor(T/dt)
##########################
### GENERAL PARAMETERS ###
##########################
stop_time = T
amr.n_cell = nx nx nx
amr.max_level = 0
geometry.dims = 2
geometry.prob_lo = -0.5*Lx -0.5*Lx -0.5*Lx
geometry.prob_hi = 0.5*Lx 0.5*Lx 0.5*Lx
##########################
### BOUNDARY CONDITION ###
##########################
boundary.field_lo = periodic periodic periodic
boundary.field_hi = periodic periodic periodic
boundary.particle_lo = periodic periodic periodic
boundary.particle_hi = periodic periodic periodic
################
### NUMERICS ###
################
warpx.cfl = cfl
algo.maxwell_solver = yee
algo.particle_shape = 3
algo.particle_pusher = boris
warpx.use_filter = 1
#################
### PARTICLES ###
#################
particles.species_names = ele1 ele2
ele1.species_type = electron
ele1.injection_style = NRandomPerCell
ele1.num_particles_per_cell = 80
ele1.profile = constant
ele1.density = n0
ele1.momentum_distribution_type = maxwell_boltzmann
ele1.theta_distribution_type = constant
ele1.theta = T0 / (m_e * clight**2)
ele1.beta_distribution_type = parser
ele1.beta_function(x,y,z) = beta0
ele1.bulk_vel_dir = +y
ele2.species_type = electron
ele2.injection_style = NRandomPerCell
ele2.num_particles_per_cell = 80
ele2.profile = constant
ele2.density = n0
ele2.momentum_distribution_type = maxwell_boltzmann
ele2.theta_distribution_type = constant
ele2.theta = T0 / (m_e * clight**2)
ele2.beta_distribution_type = constant
ele2.beta = beta0
ele2.bulk_vel_dir = -y
###################
### DIAGNOSTICS ###
###################
# FULL
diagnostics.diags_names = fields
fields.intervals = floor(nt/200)
fields.diag_type = Full
fields.write_species = 0
fields.fields_to_plot = Bx Bz
fields.format = openpmd
fields.openpmd_backend = bp
fields.dump_last_timestep = 1
# REDUCED
warpx.reduced_diags_names = FieldEnergy FieldMaximum
FieldEnergy.type = FieldEnergy
FieldEnergy.intervals = 1
FieldMaximum.type = FieldMaximum
FieldMaximum.intervals = 1
Choosing the parameters
You will notice that some parameters in the input file are set to
... (ellipsis). These are left for you to
choose! Designing a good simulation requires making physical and
numerical choices –
this is part of the exercise.
Choose the physical parameters
The physical parameters you need to set are:
| Parameter | Symbol | Description |
|---|---|---|
n0 |
\(n_0\) | Electron density of each beam \([\mathrm{m^{-3}}]\) |
T0 |
\(T_0\) | Thermal temperature of each beam \([\mathrm{eV}]\) |
beta0 |
\(\beta_0 = v_d/c\) | Drift velocity of each beam normalized to \(c\) |
Some hints:
- The Weibel instability requires an anisotropy in the momentum space. Here, the anisotropy comes from the relative drift between the two beams. A mildly relativistic drift (e.g. \(\beta_0 \sim 0.1\)–\(0.5\)) will produce a clear instability.
- The temperature should be small enough that the thermal spread does not wash out the anisotropy. As a rule of thumb, \(v_d \gg v_{\mathrm{th}}\) where \(v_{\mathrm{th}} = \sqrt{T_0/m_e}\).
- A density in the range \(10^{20}\)–\(10^{24}\) m\(^{-3}\) works well; what matters is that lengths and times are measured in skin depths and inverse plasma frequencies.
Choose the numerical parameters
The numerical parameters you need to set are:
| Parameter | Description |
|---|---|
Lx |
Box length (in each direction), expressed in units of the skin depth \(c/\omega_{pe}\) |
nx |
Number of grid cells per direction |
cfl |
CFL number (must be \(< 1/\sqrt{2}\) in 2D for the Yee solver) |
T |
Total simulation time, expressed in units of \(\omega_{pe}^{-1}\) |
Some hints:
- The Weibel filaments have a characteristic size of a few skin depths. The box should be large enough to contain several filaments: \(\sim 10\)–\(30\) skin depths per direction is a good starting point.
- You need to resolve the skin depth on the grid: \(\Delta x \lesssim c/\omega_{pe}\).
- In 2D, the Yee solver CFL condition is \(\mathrm{cfl} < 1/\sqrt{2} \approx 0.7\).
Note how the input file already accounts for this in the
dtformula. - The Weibel instability grows slower than the two-stream instability. You will need a longer simulation: \(\sim 100\)–\(500\,\omega_{pe}^{-1}\).
Notable details
The geometry is 2D (
geometry.dims = 2) with periodic boundary conditions in all directions.Two electron species (
ele1,ele2) are initialized with equal density and temperature but with opposite drift velocities along the \(y\)-axis.The diagnostics record the magnetic field components
BxandBzat regular intervals,
allowing you to visualize the growth of filamentary magnetic structures.Reduced diagnostics (
FieldEnergy,FieldMaximum) track the total field energy and the maximum field amplitude at every timestep. Since the system starts with essentially zero magnetic field (only numerical noise), you can directly observe the exponential growth of the Weibel instability in these quantities.
Run
Create a new folder and copy the input file there, after filling in your chosen parameters.
If you want to speed things up, you can run in parallel:
Depending on your choice of resolution and box size, this simulation
may take from a few minutes to much longer. If it takes too long, try
reducing nx or T.
You should see a standard output flashing out a lot of info.
At the end, you should find in your folder:
- a subfolder called
diags: here is where the code stored the diagnostics - a file called
warpx_used_inputs: this is a summary of the inputs that were used to run the simulation - files
FieldEnergy.txtandFieldMaximum.txtwith the reduced diagnostics
If that’s the case, yey! 💯
If the run went wrong, you may find a Backtrace.0.0 file
which can be useful for debugging purposes. Let me know if the code
fails in any way!
Visualize
With Python 🐍
Now that we have the results, we can analyze them using Python.
We will use the openPMD-viewer library
to grab the data that the simulation produced in openPMD format. Here you can find a
few tutorials on how to use the viewer. If you feel nerdy and/or you
need to deal with the data in parallel workflows, you can use the openPMD-api.
Here are some things you can visualize:
Magnetic field maps: use
get_field('B', 'z')(or'x') to plot 2D maps of the magnetic field at different times. You should see the formation and growth of filamentary structures.Field energy vs time: load
FieldEnergy.txtand plot the total field energy on a semi-log scale as a function of time. You should see a clear exponential growth phase followed by saturation.Filament spacing: from the 2D field maps, estimate the characteristic spacing of the current filaments and compare it to the skin depth \(c/\omega_{pe}\).
With a Jupyter notebook 📓
The notebook includes a magnetic field map (\(B_x\)) and the evolution of the electric and magnetic field energy on a semi-log scale.
You can download the notebook and try it yourself. Remember to either run the notebook from the simulation directory or change the corresponding path in the notebook.
Questions for analysis
How does the magnetic field energy grow in time? Can you identify a linear (exponential) growth phase on a semi-log plot? Estimate the growth rate \(\gamma\) from the slope.
What is the characteristic size of the filaments at saturation? How does it compare to the plasma skin depth?
How does the growth rate depend on \(\beta_0\)? Try at least two different drift velocities and compare the field energy time histories.
What happens if you increase the temperature \(T_0\) while keeping \(\beta_0\) fixed? At what point does the instability shut off?
Compare this simulation to the two-stream instability episode. What are the key differences in the fields that are generated (electrostatic vs electromagnetic)?
These are open-ended questions meant to guide your exploration. The Weibel growth rate in the cold limit scales as \(\gamma \sim \omega_{pe}\,\beta_0\), and the characteristic filament size is of order the skin depth. Finite temperature tends to stabilize short-wavelength modes.
Gallery
💡 The Weibel instability generates magnetic fields from a momentum anisotropy – unlike the two-stream instability, which generates electrostatic fields.
🔬 The instability produces current filaments with a characteristic size of order the plasma skin depth \(c/\omega_{pe}\).
📊 Reduced diagnostics (FieldEnergy,
FieldMaximum) are an efficient way to monitor the
instability growth without storing large field dumps.
⚡ Choosing parameters wisely (\(v_d \gg v_{\mathrm{th}}\), sufficient resolution and box size) is essential for observing the instability clearly.
🔍 The documentation is the first place to look for answers, otherwise check out our issues and discussions and ask there.
📷 To analyze and visualize the simulation results in openPMD format, you can use the openPMD-viewer library for Python.