Electromagnetic Physics

The electromagnetic domain provides tools for simulating charged particle dynamics in electric and magnetic fields.

Overview

The electromagnetic module implements:

  • Lorentz force dynamics for charged particles

  • Cyclotron motion analysis and exact solutions

  • Magnetic traps (mirror machines, magnetic bottles)

  • E×B drifts in crossed fields

All implementations follow the Lagrangian formulation:

\[L = \frac{1}{2}m\mathbf{v}^2 - q\phi + q\mathbf{v} \cdot \mathbf{A}\]

where \(\phi\) is the scalar potential and \(\mathbf{A}\) is the vector potential.

Quick Start

from mechanics_dsl.domains.electromagnetic import ChargedParticle, CyclotronMotion
import numpy as np

# Create a charged particle (mass=1, charge=1 in natural units)
particle = ChargedParticle(mass=1.0, charge=1.0)

# Set up a uniform magnetic field along z
particle.set_uniform_magnetic_field(Bz=1.0)

# Derive equations of motion
eom = particle.derive_equations_of_motion()
print(eom)  # {'x_ddot': vy, 'y_ddot': -vx, 'z_ddot': 0}

# Analyze cyclotron motion
cyclotron = CyclotronMotion(particle)
omega_c = particle.cyclotron_frequency(B_magnitude=1.0)  # ωc = qB/m
r_L = particle.larmor_radius(v_perp=0.5, B_magnitude=1.0)  # rL = mv⊥/(qB)

Classes

ChargedParticle

class mechanics_dsl.domains.electromagnetic.ChargedParticle(mass: float = 1.0, charge: float = 1.0, name: str = 'charged_particle')[source]

Bases: PhysicsDomain

Dynamics of a charged particle in electromagnetic fields.

Implements the Lorentz force law:

F = q(E + v × B)

And the corresponding Lagrangian:

L = (1/2)m*v² - q*φ + q*v·A

where φ is the scalar potential and A is the vector potential.

Example

>>> particle = ChargedParticle(mass=1.0, charge=1.0)
>>> particle.set_uniform_magnetic_field(Bz=1.0)
>>> eom = particle.derive_equations_of_motion()
set_uniform_electric_field(Ex: float = 0, Ey: float = 0, Ez: float = 0) None[source]

Set a uniform electric field E = (Ex, Ey, Ez).

set_uniform_magnetic_field(Bx: float = 0, By: float = 0, Bz: float = 0) None[source]

Set a uniform magnetic field B = (Bx, By, Bz).

Uses the symmetric gauge: A = (1/2) B × r

set_potentials(phi: Expr, A: Tuple[Expr, Expr, Expr]) None[source]

Set custom scalar and vector potentials.

define_lagrangian() Expr[source]

Lagrangian for charged particle in EM field: L = (1/2)m*v² - q*φ + q*v·A

define_hamiltonian() Expr[source]

Hamiltonian for charged particle in EM field: H = (1/2m)(p - qA)² + qφ

derive_equations_of_motion() Dict[str, Expr][source]

Derive equations of motion from Lorentz force.

Returns:

Dictionary with x_ddot, y_ddot, z_ddot expressions

get_state_variables() List[str][source]

Get state variables: positions and velocities.

get_required_parameters() List[str][source]

Get list of required parameters for this domain.

Override in subclasses to specify required parameters.

Returns:

List of required parameter names

cyclotron_frequency(B_magnitude: float) float[source]

Calculate cyclotron frequency ωc = qB/m.

Parameters:

B_magnitude – Magnitude of magnetic field

Returns:

Cyclotron angular frequency

larmor_radius(v_perp: float, B_magnitude: float) float[source]

Calculate Larmor (gyro) radius rL = mv_⊥/(qB).

Parameters:
  • v_perp – Velocity perpendicular to B

  • B_magnitude – Magnitude of magnetic field

Returns:

Larmor radius

Example: Particle in crossed E×B fields

from mechanics_dsl.domains.electromagnetic import ChargedParticle

particle = ChargedParticle(mass=9.11e-31, charge=1.6e-19)  # Electron

# E along y, B along z → drift along x
particle.set_uniform_electric_field(Ey=1000.0)  # 1000 V/m
particle.set_uniform_magnetic_field(Bz=0.1)     # 0.1 T

# E×B drift velocity
v_drift = 1000.0 / 0.1  # = 10,000 m/s in x-direction

CyclotronMotion

class mechanics_dsl.domains.electromagnetic.CyclotronMotion(particle: ChargedParticle)[source]

Bases: object

Analyzes cyclotron motion of charged particles in magnetic fields.

Provides exact solutions for uniform magnetic field and perturbative corrections for non-uniform fields.

exact_trajectory(v0: Tuple[float, float, float], r0: Tuple[float, float, float], B: float, t_array: ndarray) Dict[str, ndarray][source]

Compute exact cyclotron trajectory for uniform B along z.

Parameters:
  • v0 – Initial velocity (vx0, vy0, vz0)

  • r0 – Initial position (x0, y0, z0)

  • B – Magnetic field magnitude (along z)

  • t_array – Time points

Returns:

Dictionary with x, y, z, vx, vy, vz arrays

Example: Computing exact cyclotron trajectory

import numpy as np
from mechanics_dsl.domains.electromagnetic import ChargedParticle, CyclotronMotion

particle = ChargedParticle(mass=1.0, charge=1.0)
cyclotron = CyclotronMotion(particle)

t = np.linspace(0, 10, 1000)
trajectory = cyclotron.exact_trajectory(
    v0=(1.0, 0.0, 0.5),  # Initial velocity
    r0=(0.0, 0.0, 0.0),  # Start at origin
    B=1.0,                # Magnetic field magnitude
    t_array=t
)

# trajectory contains 'x', 'y', 'z', 'vx', 'vy', 'vz', 't'

DipoleTrap

class mechanics_dsl.domains.electromagnetic.DipoleTrap(B0: float, L: float)[source]

Bases: object

Models magnetic dipole traps (mirror machines, magnetic bottles).

Analyzes adiabatic invariants and mirror points.

magnetic_field(z: float) float[source]

Magnetic field magnitude along axis.

B(z) = B0 * (1 + (z/L)²)

mirror_ratio(z_mirror: float) float[source]

Mirror ratio R = B_mirror / B_min.

loss_cone_angle(z_mirror: float) float[source]

Loss cone angle for particles that escape.

sin²(θ_loss) = B_min / B_mirror = 1/R

is_trapped(pitch_angle: float, z_mirror: float) bool[source]

Determine if a particle with given pitch angle is trapped.

Parameters:
  • pitch_angle – Angle between v and B (radians)

  • z_mirror – Position of mirror point

Returns:

True if particle is trapped

Example: Analyzing magnetic mirror confinement

from mechanics_dsl.domains.electromagnetic import DipoleTrap
import numpy as np

# Magnetic mirror with B0=1 T, scale length L=1 m
trap = DipoleTrap(B0=1.0, L=1.0)

# Field at z=1: B(1) = B0(1 + 1) = 2 T
print(trap.magnetic_field(z=1.0))  # 2.0

# Mirror ratio
R = trap.mirror_ratio(z_mirror=1.0)  # R = 2

# Loss cone angle
theta_loss = trap.loss_cone_angle(z_mirror=1.0)
print(f"Loss cone: {np.degrees(theta_loss):.1f}°")  # 45°

# Is a particle with 60° pitch angle trapped?
print(trap.is_trapped(np.radians(60), z_mirror=1.0))  # True

Physical Constants

The module uses SI units by default. Key relationships:

  • Cyclotron frequency: \(\omega_c = \frac{qB}{m}\)

  • Larmor radius: \(r_L = \frac{mv_\perp}{qB}\)

  • E×B drift velocity: \(v_E = \frac{E}{B}\)

  • Magnetic moment: \(\mu = \frac{mv_\perp^2}{2B}\) (adiabatic invariant)

Convenience Functions

from mechanics_dsl.domains.electromagnetic import (
    uniform_crossed_fields,
    calculate_drift_velocity
)

# Create particle in crossed fields quickly
particle = uniform_crossed_fields(E=100.0, B=1.0)

# Calculate E×B drift
v_drift = calculate_drift_velocity(E=100.0, B=1.0)  # 100 m/s

See Also