Resistive Wall Wakefields¶
This module provides analytical models for short-range resistive wall wakefields in accelerator beam pipes, based on the approach described in SLAC-PUB-10707.
Physics Background¶
When a relativistic charged particle travels through a conducting beam pipe, it induces image currents in the pipe walls. Due to the finite conductivity of the wall material, these currents penetrate into the conductor and dissipate energy, creating a wakefield that acts on trailing particles.
At high frequencies (short distances), the AC conductivity of metals deviates from DC behavior due to the Drude relaxation time $\tau$:
$$\sigma(\omega) = \frac{\sigma_0}{1 - i\omega\tau}$$
This frequency dependence causes the wakefield to oscillate and decay exponentially behind the source particle.
Characteristic Scales¶
$s_0$: Characteristic length scale where the wake transitions from the $1/\sqrt{z}$ DC behavior to oscillatory AC behavior: $$s_0 = \left( \frac{2a^2}{Z_0 \sigma_0} \right)^{1/3}$$
$W_0$: Characteristic wake amplitude: $W_0 = c Z_0 / (\pi a^2)$ (round geometry)
Classes¶
| Class | Description | Speed | Accuracy |
|---|---|---|---|
ResistiveWallWakefield |
Full impedance model using FFT convolution | Slower | High |
ResistiveWallPseudomode |
Damped sinusoid approximation | Fast (O(N)) | ~10-20% error |
Key Methods¶
| Method | Description |
|---|---|
wake(z) |
Evaluate wakefield $W(z)$ at position(s) $z$ [V/C/m] |
impedance(k) |
Evaluate impedance $Z(k)$ at wavenumber(s) $k$ [Ω/m] |
convolve_density(ρ, dz, plot=False) |
Compute integrated wake from charge density |
particle_kicks(z, weight) |
Compute per-particle momentum kicks [eV/m] |
plot() |
Visualize the wakefield |
Use ParticleGroup.apply_wakefield(wake, length) to apply wakefield kicks to particles.
Sign Convention¶
- $z < 0$: Behind the source (trailing particle feels the wake)
- $z > 0$: Ahead of the source (causality requires $W = 0$)
- Positive $W(z)$ corresponds to energy loss (decelerating field)
- Tail particles lose more energy than head particles
import numpy as np
import matplotlib.pyplot as plt
from pmd_beamphysics import ParticleGroup
from pmd_beamphysics.wakefields import ResistiveWallWakefield, ResistiveWallPseudomode
from pmd_beamphysics.units import epsilon_0
# Create wakefield from material preset
wake = ResistiveWallWakefield.from_material(
"copper-slac-pub-10707",
radius=2.5e-3, # 2.5 mm pipe radius
geometry="flat",
)
wake
ResistiveWallWakefield(radius=0.0025, conductivity=65000000.0, relaxation_time=2.7e-14, geometry='flat', material='copper-slac-pub-10707') → s₀=7.992e-06 m
Visualize the wakefield and impedance¶
wake.plot_impedance()
wake.plot(zmax=200e-6, normalized=True)
Apply to particles¶
Load a particle distribution and visualize the wakefield kicks. Note: particles must be at constant time (use drift_to_t()) for proper wake calculation.
P = ParticleGroup("../data/bmad_particles2.h5")
P.drift_to_t() # Align particles at constant time
P.wakefield_plot(wake)
# Apply wakefield over 10 m of beam pipe (returns a modified copy)
P_after = P.apply_wakefield(wake, length=10.0)
# Mean energy change [keV] - negative means energy loss
(P_after["mean_energy"] - P["mean_energy"]) / 1e3
np.float64(-1376.4140474233627)
Setup¶
# Accurate impedance-based model
wake = ResistiveWallWakefield(
radius=2.5e-3, # Pipe radius [m] (or half-gap for flat)
conductivity=6.5e7, # DC conductivity σ₀ [S/m]
relaxation_time=27e-15, # Drude relaxation time τ [s]
geometry="round", # "round" or "flat"
)
wake
ResistiveWallWakefield(radius=0.0025, conductivity=65000000.0, relaxation_time=2.7e-14, geometry='round', material='copper-slac-pub-10707') → s₀=7.992e-06 m
# Fast pseudomode approximation
wake_fast = ResistiveWallPseudomode(
radius=2.5e-3,
conductivity=6.5e7,
relaxation_time=27e-15,
geometry="round",
)
wake_fast
ResistiveWallPseudomode(radius=0.0025, conductivity=65000000.0, relaxation_time=2.7e-14, geometry='round', material='copper-slac-pub-10707') → s₀=7.992e-06 m, Γ=1.013, k_r=199535.0/m, Q_r=3.55
Material Presets¶
Common materials with known conductivity and relaxation times are available:
# Available materials (SI units)
ResistiveWallWakefield.MATERIALS
{'copper-slac-pub-10707': {'conductivity': 65000000.0,
'relaxation_time': 2.7e-14},
'copper-genesis4': {'conductivity': 58130000.0, 'relaxation_time': 2.7e-14},
'aluminum-genesis4': {'conductivity': 35710000.0, 'relaxation_time': 8e-15},
'aluminum-slac-pub-10707': {'conductivity': 42000000.0,
'relaxation_time': 8e-15},
'aluminum-alloy-6061-t6-20C': {'conductivity': 25000000.0,
'relaxation_time': 8e-15},
'aluminum-alloy-6063-t6-20C': {'conductivity': 30000000.0,
'relaxation_time': 8e-15}}
# Create from material preset
wake_cu = ResistiveWallWakefield.from_material(
"copper-slac-pub-10707",
radius=4.5e-3,
geometry="flat",
)
wake_cu
ResistiveWallWakefield(radius=0.0045, conductivity=65000000.0, relaxation_time=2.7e-14, geometry='flat', material='copper-slac-pub-10707') → s₀=1.183e-05 m
Key material properties (conductivity [S/m], relaxation time [s], characteristic length $s_0$ [m]):
wake_cu.conductivity, wake_cu.relaxation_time, wake_cu.s0
(65000000.0, 2.7e-14, 1.1825977087388154e-05)
# Evaluate at single point (10 µm behind source) [V/C/m]
wake.wake(-10e-6)
-1593077197930949.8
# Evaluate on array
z_arr = np.linspace(-200e-6, 10e-6, 500)
W_arr = wake.wake(z_arr)
plt.figure(figsize=(8, 4))
plt.plot(-z_arr * 1e6, W_arr * 1e-12)
plt.axvline(0, color="r", ls="--", label="Source position")
plt.xlabel(r"Distance behind source $|z|$ (µm)")
plt.ylabel(r"$W(z)$ (V/pC/m)")
plt.title("Resistive Wall Wakefield")
plt.legend()
plt.xlim(-20, 200)
(-20.0, 200.0)
Impedance and Wakefield plots¶
The longitudinal impedance as a function of wavenumber $k$:
wake.plot_impedance()
wake.plot(zmax=200e-6)
Round vs Flat Geometry¶
The flat (parallel plate) geometry has a higher wake amplitude due to the different boundary conditions:
params = dict(radius=2.5e-3, conductivity=6.5e7, relaxation_time=27e-15)
wake_round = ResistiveWallWakefield(**params, geometry="round")
wake_flat = ResistiveWallWakefield(**params, geometry="flat")
z = np.linspace(-200e-6, 0, 300)
plt.figure(figsize=(8, 4))
plt.plot(-z * 1e6, wake_round.wake(z) * 1e-12, label="Round")
plt.plot(-z * 1e6, wake_flat.wake(z) * 1e-12, "--", label="Flat")
plt.xlabel(r"Distance behind source $|z|$ (µm)")
plt.ylabel(r"$W(z)$ (V/pC/m)")
plt.title("Round vs Flat Geometry")
plt.legend()
<matplotlib.legend.Legend at 0x7f5507c0b620>
Convolution with Charge Density¶
The convolve_density method computes the integrated wake potential from a charge density distribution:
$$V(z) = \int_{z}^{\infty} \rho(z') \, W(z - z') \, dz'$$
# Create a Gaussian bunch density
n_bins = 500
dz = 1e-6 # 1 µm spacing
z = np.arange(n_bins) * dz
Q_total = 100e-12 # 100 pC
sigma_z = 10e-6 # 10 µm RMS
z0 = z[n_bins // 2] # Center
density = (
Q_total / (sigma_z * np.sqrt(2 * np.pi)) * np.exp(-0.5 * ((z - z0) / sigma_z) ** 2)
)
# Total charge [pC]
np.sum(density) * dz * 1e12
np.float64(100.00000000000001)
# Compute integrated wake with built-in plotting
wake.convolve_density(density, dz, plot=True)
array([-3.24323297e+002, -3.27984816e+002, -3.33798033e+002,
-3.41724783e+002, -3.51633980e+002, -3.63301633e+002,
-3.76414898e+002, -3.90580211e+002, -4.05335407e+002,
-4.20165551e+002, -4.34522010e+002, -4.47844147e+002,
-4.59582857e+002, -4.69225029e+002, -4.76317952e+002,
-4.80492586e+002, -4.81484635e+002, -4.79152370e+002,
-4.73490234e+002, -4.64637393e+002, -4.52880540e+002,
-4.38650489e+002, -4.22512330e+002, -4.05149181e+002,
-3.87339835e+002, -3.69930915e+002, -3.53804401e+002,
-3.39841649e+002, -3.28885242e+002, -3.21700204e+002,
-3.18936205e+002, -3.21092450e+002, -3.28486950e+002,
-3.41231749e+002, -3.59215560e+002, -3.82095002e+002,
-4.09295345e+002, -4.40021308e+002, -4.73278050e+002,
-5.07902082e+002, -5.42601348e+002, -5.76003306e+002,
-6.06709434e+002, -6.33354155e+002, -6.54665949e+002,
-6.69528096e+002, -6.77036440e+002, -6.76551463e+002,
-6.67742086e+002, -6.50618801e+002, -6.25554035e+002,
-5.93288107e+002, -5.54919632e+002, -5.11879841e+002,
-4.65890953e+002, -4.18909424e+002, -3.73055612e+002,
-3.30532067e+002, -2.93533295e+002, -2.64150378e+002,
-2.44274256e+002, -2.35501783e+002, -2.39048790e+002,
-2.55674324e+002, -2.85620053e+002, -3.28568345e+002,
-3.83622002e+002, -4.49307823e+002, -5.23605306e+002,
-6.04000771e+002, -6.87566110e+002, -7.71060265e+002,
-8.51050415e+002, -9.24048843e+002, -9.86660511e+002,
-1.03573561e+003, -1.06852078e+003, -1.08280243e+003,
-1.07703538e+003, -1.05045044e+003, -1.00313503e+003,
-9.36081565e+002, -8.51199696e+002, -7.51289623e+002,
-6.39975235e+002, -5.21597574e+002, -4.01070782e+002,
-2.83704492e+002, -1.74998304e+002, -8.04155243e+001,
-5.14471091e+000, 4.61414171e+001, 6.95194317e+001,
6.20304466e+001, 2.18667480e+001, -5.14800106e+001,
-1.57117799e+002, -2.92707632e+002, -4.54476742e+002,
-6.37294585e+002, -8.34812204e+002, -1.03966279e+003,
-1.24371846e+003, -1.43839565e+003, -1.61499876e+003,
-1.76508956e+003, -1.88086806e+003, -1.95554880e+003,
-1.98371633e+003, -1.96164296e+003, -1.88755291e+003,
-1.76181811e+003, -1.58707297e+003, -1.36823837e+003,
-1.11244809e+003, -8.28875187e+002, -5.28459444e+002,
-2.23541927e+002, 7.25833788e+001, 3.46186424e+002,
5.83668276e+002, 7.72157154e+002, 9.00109288e+002,
9.57888914e+002, 9.38301031e+002, 8.37051011e+002,
6.53106727e+002, 3.88941585e+002, 5.06407146e+001,
-3.52142614e+002, -8.06387587e+002, -1.29606433e+003,
-1.80264440e+003, -2.30575040e+003, -2.78392523e+003,
-3.21549477e+003, -3.57949257e+003, -3.85661021e+003,
-4.03013379e+003, -4.08682528e+003, -4.01770710e+003,
-3.81871027e+003, -3.49114968e+003, -3.04199540e+003,
-2.48391594e+003, -1.83507763e+003, -1.11869404e+003,
-3.62329601e+002, 4.03027299e+002, 1.14409577e+003,
1.82660190e+003, 2.41670025e+003, 2.88246335e+003,
3.19537890e+003, 3.33179035e+003, 3.27421511e+003,
3.01247590e+003, 2.54458499e+003, 1.87732794e+003,
1.02650336e+003, 1.67873238e+001, -1.11879446e+003,
-2.33978790e+003, -3.59960922e+003, -4.84718789e+003,
-6.02890379e+003, -7.09075255e+003, -7.98065951e+003,
-8.65085110e+003, -9.06018460e+003, -9.17633303e+003,
-8.97772173e+003, -8.45511794e+003, -7.61278322e+003,
-6.46911254e+003, -5.05670097e+003, -3.42180027e+003,
-1.62315181e+003, 2.69791731e+002, 2.17921610e+003,
4.02179984e+003, 5.71198048e+003, 7.16551642e+003,
8.30320825e+003, 9.05462844e+003, 9.36169839e+003,
9.18194893e+003, 8.49130383e+003, 7.28623691e+003,
5.58517136e+003, 3.42901443e+003, 8.80751821e+002,
-1.97593801e+003, -5.03904940e+003, -8.19155217e+003,
-1.13055459e+004, -1.42471375e+004, -1.68818763e+004,
-1.90805440e+004, -2.07250722e+004, -2.17143389e+004,
-2.19695859e+004, -2.14392012e+004, -2.01026194e+004,
-1.79731204e+004, -1.50993385e+004, -1.15653388e+004,
-7.48917269e+003, -3.01988449e+003, 1.66699327e+003,
6.37534963e+003, 1.08956361e+004, 1.50129999e+004,
1.85161140e+004, 2.12063568e+004, 2.29069638e+004,
2.34717443e+004, 2.27929588e+004, 2.08079627e+004,
1.75042532e+004, 1.29226109e+004, 7.15809476e+003,
3.58733017e+002, -7.27814579e+003, -1.55118937e+004,
-2.40666733e+004, -3.26414636e+004, -4.09215635e+004,
-4.85911205e+004, -5.53461405e+004, -6.09073815e+004,
-6.50325162e+004, -6.75269550e+004, -6.82527696e+004,
-6.71352324e+004, -6.41665986e+004, -5.94068926e+004,
-5.29816140e+004, -4.50764477e+004, -3.59292251e+004,
-2.58195412e+004, -1.50565656e+004, -3.96568954e+003,
7.12528562e+003, 1.78996910e+004, 2.80645647e+004,
3.73616271e+004, 4.55761385e+004, 5.25432590e+004,
5.81516978e+004, 6.23446053e+004, 6.51178306e+004,
6.65158197e+004, 6.66255494e+004, 6.55689851e+004,
6.34945998e+004, 6.05685027e+004, 5.69656968e+004,
5.28619256e+004, 4.84264839e+004, 4.38162626e+004,
3.91711923e+004, 3.46111409e+004, 3.02342239e+004,
2.61164075e+004, 2.23122189e+004, 1.88563452e+004,
1.57658797e+004, 1.30429781e+004, 1.06777050e+004,
8.65088021e+003, 6.93677066e+003, 5.50551595e+003,
4.32521348e+003, 3.36362680e+003, 2.58951158e+003,
1.97357810e+003, 1.48912671e+003, 1.11240384e+003,
8.22730835e+002, 6.02458465e+002, 4.36796156e+002,
3.13559593e+002, 2.22872883e+002, 1.56854018e+002,
1.09304998e+002, 7.54213608e+001, 5.15302350e+001,
3.48615124e+001, 2.33533476e+001, 1.54907681e+001,
1.01746476e+001, 6.61743707e+000, 4.26172493e+000,
2.71773461e+000, 1.71615054e+000, 1.07307703e+000,
6.64406552e-001, 4.07347038e-001, 2.47299305e-001,
1.48665027e-001, 8.84956918e-002, 5.21629998e-002,
3.04459652e-002, 1.75963773e-002, 1.00703051e-002,
5.70672965e-003, 3.20226075e-003, 1.77930414e-003,
9.78964937e-004, 5.33343314e-004, 2.87719063e-004,
1.53692177e-004, 8.12934383e-005, 4.25773938e-005,
2.20811531e-005, 1.13392086e-005, 5.76582284e-006,
2.90306406e-006, 1.44733126e-006, 7.14487256e-007,
3.49249248e-007, 1.69040258e-007, 8.10135612e-008,
3.84447857e-008, 1.80646085e-008, 8.40485647e-009,
3.87205879e-009, 1.76629298e-009, 7.97796219e-010,
3.56803344e-010, 1.58005738e-010, 6.92824282e-011,
3.00800774e-011, 1.29312266e-011, 5.50433361e-012,
2.31992188e-012, 9.68155029e-013, 4.00054114e-013,
1.63679478e-013, 6.63087366e-014, 2.65979210e-014,
1.05639039e-014, 4.15432438e-015, 1.61761438e-015,
6.23659650e-016, 2.38077187e-016, 8.99880511e-017,
3.36781550e-017, 1.24797987e-017, 4.57891075e-018,
1.66345696e-018, 5.98349916e-019, 2.13104527e-019,
7.51490324e-020, 2.62389772e-020, 9.07115780e-021,
3.10506276e-021, 1.05237233e-021, 3.53150137e-022,
1.17338305e-022, 3.86020419e-023, 1.25739099e-023,
4.05526824e-024, 1.29496367e-024, 4.09434022e-025,
1.28173490e-025, 3.97283046e-026, 1.21923945e-026,
3.70479978e-027, 1.11462008e-027, 3.32028221e-028,
9.79284272e-029, 2.85974853e-029, 8.26859728e-030,
2.36712077e-030, 6.70954747e-031, 1.88299836e-031,
5.23227088e-032, 1.43950685e-032, 3.92121059e-033,
1.05757018e-033, 2.82410288e-034, 7.46678777e-035,
1.95464898e-035, 5.06623259e-036, 1.30011735e-036,
3.30339708e-037, 8.31035384e-038, 2.06994345e-038,
5.10478432e-039, 1.24645325e-039, 3.01338073e-040,
7.21292086e-041, 1.70941384e-041, 4.01108530e-042,
9.31869202e-043, 2.14351314e-043, 4.88174655e-044,
1.10078360e-044, 2.45757101e-045, 5.43234538e-046,
1.18890077e-046, 2.57620582e-047, 5.52703214e-048,
1.17403133e-048, 2.46912700e-049, 5.14141901e-050,
1.05998126e-050, 2.16365882e-051, 4.37274924e-052,
8.74974430e-053, 1.73344850e-053, 3.40017385e-054,
6.60337103e-055, 1.26970983e-055, 2.41722614e-056,
4.55621184e-057, 8.50284329e-058, 1.57107855e-058,
2.87412003e-059, 5.20577031e-060, 9.33550950e-061,
1.65753916e-061, 2.91381614e-062, 5.07145877e-063,
8.73928483e-064, 1.49104573e-064, 2.51870871e-065,
4.21246859e-066, 6.97536598e-067, 1.14358581e-067,
1.85627277e-068, 2.98322464e-069, 4.74680177e-070,
7.47802754e-071, 1.16638977e-071, 1.80123708e-072,
2.75402858e-073, 4.16904184e-074, 6.24847692e-075,
9.27218427e-076, 1.36225926e-076, 1.98155978e-077,
2.85380404e-078, 4.06921416e-079, 5.74468533e-080,
8.02954823e-081, 1.11118154e-081, 1.52246684e-082,
2.06528246e-083, 2.77382936e-084, 3.68848965e-085,
4.85607990e-086, 6.32982036e-087, 8.16892928e-088,
1.04377517e-088, 1.32043414e-089, 1.65384336e-090,
2.05087757e-091, 2.51798256e-092, 3.06078775e-093,
3.68367337e-094, 4.38931058e-095, 5.17819857e-096,
6.04822828e-097, 6.99430604e-098, 8.00807199e-099,
9.07774721e-100, 1.01881394e-100, 1.13208296e-101,
1.24545520e-102, 1.35657669e-103, 1.46294124e-104,
1.56198050e-105, 1.65116484e-106, 1.72810954e-107,
1.79068014e-108, 1.83709068e-109, 1.86598826e-110,
1.87651858e-111, 1.86836786e-112, 1.84177843e-113,
1.79753669e-114, 1.73693452e-115, 1.66170662e-116,
1.57394803e-117, 1.47601740e-118, 1.37043216e-119,
1.25976198e-120, 1.14652694e-121, 1.03310572e-122,
9.21658262e-124, 8.14066063e-125, 7.11891465e-126,
6.16354782e-127, 5.28309963e-128, 4.48009876e-129,
3.72416859e-130, 2.66809449e-131])
# Load particles
P = ParticleGroup("../data/bmad_particles2.h5")
P.drift_to_t() # Align at constant time
P
<ParticleGroup with 100000 particles at 0x7f5507b4c190>
# Compute kicks [eV/m] and visualize
kicks = wake.particle_kicks(P.z, P.weight)
P.wakefield_plot(wake)
Applying to Particles¶
Use ParticleGroup.apply_wakefield() to apply the kicks:
Apply wakefield over 10 m (returns a copy by default):
P_after = P.apply_wakefield(wake, length=10.0)
# Change in mean pz [MeV/c]
(P_after["mean_pz"] - P["mean_pz"]) * 1e-6
np.float64(-1.16122149968338)
Or modify in-place with inplace=True:
P_copy = P.copy()
P_copy.apply_wakefield(wake, length=10.0, inplace=True)
ParticleGroup Wakefield Plot¶
A convenience method for visualizing the wakefield effect on a particle distribution:
# Larger figure for detail
P.wakefield_plot(wake, figsize=(12, 4))
Comparing Models: Accurate vs Fast¶
Two implementations are available with different speed/accuracy trade-offs:
ResistiveWallWakefield: Uses FFT-based impedance integration. More accurate, especially near the first oscillation peak.ResistiveWallPseudomode: Uses a damped sinusoid fit from polynomial approximations. Very fast (O(N) for particle kicks), with ~10-20% error.
# Create both models with same parameters
params = dict(
radius=2.5e-3,
conductivity=6.5e7,
relaxation_time=27e-15,
geometry="round",
)
wake_accurate = ResistiveWallWakefield(**params)
wake_fast = ResistiveWallPseudomode(**params)
wake_accurate
ResistiveWallWakefield(radius=0.0025, conductivity=65000000.0, relaxation_time=2.7e-14, geometry='round', material='copper-slac-pub-10707') → s₀=7.992e-06 m
wake_fast
ResistiveWallPseudomode(radius=0.0025, conductivity=65000000.0, relaxation_time=2.7e-14, geometry='round', material='copper-slac-pub-10707') → s₀=7.992e-06 m, Γ=1.013, k_r=199535.0/m, Q_r=3.55
# Compare wake functions
z = np.linspace(-200e-6, 0, 300)
W_accurate = wake_accurate.wake(z)
W_fast = wake_fast.wake(z)
fig, axes = plt.subplots(2, 1, figsize=(10, 6), sharex=True)
ax = axes[0]
ax.plot(-z * 1e6, W_accurate * 1e-12, label="ResistiveWallWakefield (accurate)")
ax.plot(-z * 1e6, W_fast * 1e-12, "--", label="ResistiveWallPseudomode (fast)")
ax.set_ylabel(r"$W(z)$ (V/pC/m)")
ax.legend()
ax.set_title("Wakefield Comparison")
ax = axes[1]
rel_diff = (W_fast - W_accurate) / np.abs(W_accurate).max() * 100
ax.plot(-z * 1e6, rel_diff)
ax.set_xlabel(r"Distance behind source $|z|$ (µm)")
ax.set_ylabel("Relative difference (%)")
ax.set_title("Pseudomode Error")
ax.axhline(0, color="k", lw=0.5)
plt.tight_layout()
Performance Comparison¶
%%timeit -n 10 -r 3
# Accurate model: convolve_density
_ = wake_accurate.convolve_density(density, dz)
2.2 ms ± 78.3 μs per loop (mean ± std. dev. of 3 runs, 10 loops each)
%%timeit -n 100 -r 3
# Fast model: convolve_density
_ = wake_fast.convolve_density(density, dz)
160 μs ± 6.2 μs per loop (mean ± std. dev. of 3 runs, 100 loops each)
Pseudomode Properties¶
ResistiveWallPseudomode models the wakefield as a damped sinusoid:
$$W(z) = A \cdot e^{d \cdot z} \cdot \sin(k_r z + \phi)$$
The parameters are computed from polynomial fits to SLAC-PUB-10707 data:
Characteristic scales ($s_0$ [m], $\Gamma$, $k_r$ [1/m], $Q_r$):
wake_fast.s0, wake_fast.Gamma, wake_fast.kr, wake_fast.Qr
(7.991997937835282e-06, 1.012812619442749, np.float64(199535.03396550965), np.float64(3.5452850664129434))
# Access the underlying pseudomode(s) - each has amplitude A, decay d, wavenumber k, phase phi
wake_fast.modes
[Pseudomode(A=5752033143156176.0, d=np.float64(28140.9012572571), k=np.float64(199535.03396550965), phi=1.5707963267948966)]
Bmad Export¶
ResistiveWallPseudomode can export to Bmad format for use in external tracking codes:
wake_fast.to_bmad()
'! AC Resistive wall wakefield\n! Adapted from SLAC-PUB-10707\n! Material : copper-slac-pub-10707\n! Conductivity : 65000000.0 S/m\n! Relaxation time : 2.7e-14 s\n! Geometry : round\n! Radius : 0.0025 m\n! s₀ : 7.991997937835282e-06 m\n! Γ : 1.012812619442749 \n! sr_wake = \n{z_scale=1, amp_scale=1, scale_with_length=True, z_max=100,\nlongitudinal = {5752033143156176.0, 28140.9012572571, 199535.03396550965, 0.25, none}}\n'
Validation Against SLAC-PUB-10707¶
Compare our implementation against digitized data from the original paper:
def validate_against_slacpub(figure_num):
"""Compare against digitized SLAC-PUB-10707 figures."""
geometry = "round" if figure_num == 4 else "flat"
radius = 2.5e-3
# Load digitized data
data = np.loadtxt(
f"../data/SLAC-PUB-10707-digitized-Fig{figure_num}-AC-Cu.csv", delimiter=","
)
z_ref = data[:, 0] * 1e-6 # µm → m
# Convert from paper's CGS units
W_ref = data[:, 1] * 4 / radius**2 / (4 * np.pi * epsilon_0)
# Create our model
wake = ResistiveWallWakefield.from_material(
"copper-slac-pub-10707",
radius=radius,
geometry=geometry,
)
z = np.linspace(0, 300e-6, 300)
W = wake.wake(-z)
plt.figure(figsize=(8, 4))
plt.plot(z * 1e6, W * 1e-12, label="ResistiveWallWakefield")
plt.plot(
z_ref * 1e6, W_ref * 1e-12, "o", ms=4, label=f"SLAC-PUB-10707 Fig. {figure_num}"
)
plt.xlabel(r"Distance behind source $|z|$ (µm)")
plt.ylabel(r"$W(z)$ (V/pC/m)")
plt.title(f"Validation: {geometry.title()} Copper Pipe")
plt.legend()
validate_against_slacpub(4) # Round geometry
validate_against_slacpub(8) # Flat geometry
Summary¶
| Feature | ResistiveWallWakefield |
ResistiveWallPseudomode |
|---|---|---|
| Method | FFT-based impedance | Damped sinusoid fit |
| Accuracy | High | Good (~10-20% error) |
| Speed | Moderate | Very fast |
| Impedance access | ✓ | ✓ (analytical) |
| Bmad export | ✗ | ✓ |
| Best for | General use, validation | Production tracking |
Both classes share the same API:
wake(z)- Evaluate wakefieldimpedance(k)- Evaluate impedanceconvolve_density(density, dz, plot=False)- Density convolutionparticle_kicks(z, weight)- Per-particle kicksplot()- Visualize wakefieldplot_impedance()- Visualize impedancefrom_material()- Create from preset
Use ParticleGroup.apply_wakefield(wake, length) to apply kicks to particles.