DC mode (continuous beam)¶
Pre-RFQ Low-Energy Beam Transports (LEBTs) operate on a continuous, unbunched beam — there's no longitudinal structure yet. HELIX has DC-aware code paths in both the envelope and multi-particle solvers to handle this regime correctly.
TL;DR¶
For any pre-RFQ run:
from linac_gen.core.config import BeamConfig
beam_cfg = BeamConfig(
species="H-",
energy=0.030, # 30 keV LEBT exit
frequency=162.5, # downstream RFQ frequency (used for unit conversions)
current=5.0,
n_particles=20_000,
distribution="gaussian", cutoff=4.0,
emit_nx=0.20, emit_ny=0.20,
alpha_x=0.0, beta_x=0.10,
alpha_y=0.0, beta_y=0.10,
emit_z=0.0, # no longitudinal structure
alpha_z=0.0, beta_z=1.0,
continuous=True, # ← critical
dc_energy_spread_keV=0.0,
)
The continuous=True flag triggers DC-aware behaviour throughout
the simulator:
- The longitudinal coordinate is uniformly sampled over one RF period during particle generation (so SC averaging is correct).
- The PIC solver switches to its 2-D analytic / Bassetti-Erskine
/ 2-D PIC kick (per
dc_kernel). - The recorder marks each step with
continuous_at[i] = Trueso consumers know σ_φ / σ_W / ε_z are not physically meaningful.
Choosing a DC kernel¶
SpaceChargeConfig.dc_kernel selects the 2-D transverse kick model:
dc_kernel |
Field model | Speed | Use when |
|---|---|---|---|
"uniform" |
analytic linear uniform-elliptical kick | fastest | matching, fast scans, default |
"gaussian" |
Bassetti-Erskine field of a 2-D Gaussian | medium | rigid-σ MP, per-particle non-linearity |
"pic2d" |
2-D Hockney FFT PIC over actual particle distribution | slowest | most accurate; analogue of TraceWin's PICNIC_2D |
from linac_gen.core.config import SpaceChargeConfig
sc = SpaceChargeConfig(
dc_kernel="gaussian", # or "uniform" / "pic2d"
)
Common gotchas¶
Forget continuous=True and your LEBT explodes
If you forget to set this for a pre-RFQ LEBT, HELIX assumes the beam is bunched and imposes a non-physical longitudinal structure. σ_φ blows up, ε_z grows. Symptoms: looks like SC physics is wrong, but it's just a flag.
PXIE LEBT needs Ki=1 + envelope continuous=True
Two silent failure modes:
1. The PXIE LEBT field maps have Ki=1 (full SC compensation).
Forgetting this gives the wrong forces.
2. The envelope solver also needs continuous=True to skip
longitudinal SC.
Both together: PXIE LEBT mean error 6.93% → 0.84%.
DC-aware envelope solver¶
The Sacherer ODE (linac_gen.tracking.sacherer) is the DC envelope
counterpart. It solves the continuous-beam envelope ODE:
(and similarly for y) with generalised perveance K = qI / (2π ε₀ m c³ (βγ)³). Scope: drifts, hard-edge quadrupoles, 1-D / 3-D solenoid field maps. No acceleration.
from linac_gen.core.lattice import Lattice
from linac_gen.core.particle import H_MINUS
from linac_gen.core.reference import ReferenceParticle
from linac_gen.elements.drift import Drift
from linac_gen.elements.solenoid import Solenoid
from linac_gen.tracking.sacherer import SachererSolver
# LEBT-style solenoid transport line:
lattice = Lattice()
lattice.add(Drift(name="D1", length=200.0))
lattice.add(Solenoid(name="SOL1", length=300.0, field=0.20))
lattice.add(Drift(name="D2", length=400.0))
ref = ReferenceParticle(species=H_MINUS, w_kin=0.033, frequency=162.5)
bg = ref.bg
initial_twiss = dict(emit_x=0.20 / bg, alpha_x=0.0, beta_x=0.5,
emit_y=0.20 / bg, alpha_y=0.0, beta_y=0.5)
solver = SachererSolver(lattice, ref, initial_twiss, current=5.0)
res = solver.run()
The envelope through an RFQ: bunched vs TraceWin-compatible DC¶
EnvelopeSolver with initial["continuous"]=True flips a DC beam to
bunched at the first RF bunching element: it seeds a uniform bunch
(σφ = 180°/√3 ≈ 103.9°), evolves the longitudinal envelope and switches
to bunched space charge. That is the more physical picture — but it is
not what TraceWin's envelope mode computes. TraceWin-env keeps the
beam DC through the RFQ: its genuine ENV+SC export carries no
longitudinal envelope at all (σφ ≡ σ_dp/p ≡ σ_W ≡ 0 in every RFQ row).
With space charge on, the two models therefore diverge inside an RFQ — this is a model-class difference, not an error in either code. Measured on the PXIE 2-solenoid LEBT+RFQ line at 5 mA against the TraceWin ENV+SC export:
| HELIX envelope mode | RFQ σ deviation | full line |
|---|---|---|
| bunched (default) | 27–64 % | 27–32 % |
rfq_dc_envelope=True |
5–11 % | 4.3 % (x) / 3.7 % (y) |
LEBT-only agreement is 0.2 % either way, and the energy ramp is identical in both modes (exit 1955.72 vs TW 1955.77 keV).
from linac_gen.tracking.envelope import EnvelopeSolver
res = EnvelopeSolver(lattice, ref, initial_twiss, current=5.0,
rfq_dc_envelope=True).run() # TW-comparable
Use rfq_dc_envelope=True only for validating against a TraceWin
envelope export; leave the default for physics work. Neither envelope
model is the truth channel for an RFQ — that is multiparticle
tracking. Pinned by tests/rfq/test_env_rfq_dc.py, including a guard
asserting the default mode does not match a TW export (if it ever
starts to, the default's physics changed).
Cross-references¶
- Models — model decision tree.
- Tracking modes → DC modes.
- Worked example: DC LEBT.