CSR Zeuthen Benchmark - Comparing to Bmad¶
This tries to replicate the Zeuthern CSR benchmark according to: https://journals.aps.org/prab/abstract/10.1103/PhysRevSTAB.16.060703
In [ ]:
Copied!
import matplotlib.pyplot as plt
import numpy as np
from pmd_beamphysics import ParticleGroup
from pytao import Tao
from scipy.constants import c
import impact.z as IZ
from impact.z.interfaces.bmad import plot_impactz_and_tao_stats
from impact.tests.z.conftest import bmad_files
import matplotlib.pyplot as plt
import numpy as np
from pmd_beamphysics import ParticleGroup
from pytao import Tao
from scipy.constants import c
import impact.z as IZ
from impact.z.interfaces.bmad import plot_impactz_and_tao_stats
from impact.tests.z.conftest import bmad_files
Parameters¶
In [ ]:
Copied!
TRACK_START = "BEGINNING"
TRACK_END = "END"
NX = 32
NY = 32
NZ = 128
N_PARTICLE = 100_000
DS_STEP = 0.01
CSR_ON = True
DRIFT_CSR_ON = True
BMAD_SC_ON = False
CHIRP = -36  # 1/m
TRACK_START = "BEGINNING"
TRACK_END = "END"
NX = 32
NY = 32
NZ = 128
N_PARTICLE = 100_000
DS_STEP = 0.01
CSR_ON = True
DRIFT_CSR_ON = True
BMAD_SC_ON = False
CHIRP = -36  # 1/m
Tao¶
In [ ]:
Copied!
# Gaussian
def set_gaussian(
    tao,
    n_particle=N_PARTICLE,
    a_norm_emit=1.0e-6,
    b_norm_emit=1.0e-6,
    bunch_charge=1e-9,
    sig_pz0=2e-6,
    sig_z=200e-6,
    center_pz=0,
    chirp=0,  # 1/m
):
    sig_pz = np.hypot(sig_pz0, chirp * sig_z)
    cmds = [
        f"set beam_init n_particle = {n_particle}",
        "set beam_init random_engine = quasi",
        "set beam_init saved_at = MARKER::*, BEGINNING, END",
        f"set beam_init a_norm_emit = {a_norm_emit}",
        f"set beam_init b_norm_emit = {b_norm_emit}",
        f"set beam_init bunch_charge = {bunch_charge}",
        f"set beam_init sig_pz = {sig_pz}",
        f"set beam_init sig_z = {sig_z}",
        f"set beam_init dpz_dz = {chirp}",
        f"set beam_init center(6) = {center_pz}",
    ]
    tao.cmds(cmds)
    tao.cmd("set global lattice_calc_on = T")
def get_particles(tao, ele_id):
    return ParticleGroup(data=tao.bunch_data(ele_id))
# Gaussian
def set_gaussian(
    tao,
    n_particle=N_PARTICLE,
    a_norm_emit=1.0e-6,
    b_norm_emit=1.0e-6,
    bunch_charge=1e-9,
    sig_pz0=2e-6,
    sig_z=200e-6,
    center_pz=0,
    chirp=0,  # 1/m
):
    sig_pz = np.hypot(sig_pz0, chirp * sig_z)
    cmds = [
        f"set beam_init n_particle = {n_particle}",
        "set beam_init random_engine = quasi",
        "set beam_init saved_at = MARKER::*, BEGINNING, END",
        f"set beam_init a_norm_emit = {a_norm_emit}",
        f"set beam_init b_norm_emit = {b_norm_emit}",
        f"set beam_init bunch_charge = {bunch_charge}",
        f"set beam_init sig_pz = {sig_pz}",
        f"set beam_init sig_z = {sig_z}",
        f"set beam_init dpz_dz = {chirp}",
        f"set beam_init center(6) = {center_pz}",
    ]
    tao.cmds(cmds)
    tao.cmd("set global lattice_calc_on = T")
def get_particles(tao, ele_id):
    return ParticleGroup(data=tao.bunch_data(ele_id))
In [ ]:
Copied!
tao = Tao(lattice_file=bmad_files / "csr_zeuthen.bmad", plot="mpl")
# tao.cmd('set ele beginning e_tot = 500e6') # TEST
set_gaussian(tao, n_particle=N_PARTICLE, chirp=CHIRP)
tao = Tao(lattice_file=bmad_files / "csr_zeuthen.bmad", plot="mpl")
# tao.cmd('set ele beginning e_tot = 500e6') # TEST
set_gaussian(tao, n_particle=N_PARTICLE, chirp=CHIRP)
In [ ]:
Copied!
tao.cmd("set bmad_com csr_and_space_charge_on = T")
tao.cmd("set space_charge particle_bin_span = 1")
tao.cmd(f"set space_charge n_bin = {NZ}")
tao.cmd(f"set space_charge ds_track_step = {DS_STEP}")
tao.cmd("set bmad_com radiation_damping_on  = T")  # off by default
tao.cmd("set bmad_com radiation_fluctuations_on = T")  # off by default
if BMAD_SC_ON:
    tao.cmd("set ele * space_charge_method = fft_3d")
    tao.cmd(f"set space_charge space_charge_mesh_size = {NX} {NY} {NZ}")
else:
    tao.cmd("set ele * space_charge_method = off")
tao.cmd(f"set ele * DS_STEP = {DS_STEP}")
tao.cmd("set bmad_com csr_and_space_charge_on = T")
tao.cmd("set space_charge particle_bin_span = 1")
tao.cmd(f"set space_charge n_bin = {NZ}")
tao.cmd(f"set space_charge ds_track_step = {DS_STEP}")
tao.cmd("set bmad_com radiation_damping_on  = T")  # off by default
tao.cmd("set bmad_com radiation_fluctuations_on = T")  # off by default
if BMAD_SC_ON:
    tao.cmd("set ele * space_charge_method = fft_3d")
    tao.cmd(f"set space_charge space_charge_mesh_size = {NX} {NY} {NZ}")
else:
    tao.cmd("set ele * space_charge_method = off")
tao.cmd(f"set ele * DS_STEP = {DS_STEP}")
In [ ]:
Copied!
if not CSR_ON:
    tao.cmd("set ele * csr_method = off")
elif DRIFT_CSR_ON:
    tao.cmd("set ele * csr_method = 1_dim")
else:
    tao.cmd("set ele * csr_method = off")
    tao.cmd("set ele sbend::* csr_method = 1_dim")
if not CSR_ON:
    tao.cmd("set ele * csr_method = off")
elif DRIFT_CSR_ON:
    tao.cmd("set ele * csr_method = 1_dim")
else:
    tao.cmd("set ele * csr_method = off")
    tao.cmd("set ele sbend::* csr_method = 1_dim")
In [ ]:
Copied!
%%time
tao.track_beam()
P0 = get_particles(tao, "beginning")
P1 = get_particles(tao, "end")
P1.plot("delta_t", "energy"), P1["sigma_t"] * c * 1e6
%%time
tao.track_beam()
P0 = get_particles(tao, "beginning")
P1 = get_particles(tao, "end")
P1.plot("delta_t", "energy"), P1["sigma_t"] * c * 1e6
In [ ]:
Copied!
sigma_z0 = P0["sigma_t"] * c
sigma_z1 = P1["sigma_t"] * c
sigma_z1 * 1e6
sigma_z0 = P0["sigma_t"] * c
sigma_z1 = P1["sigma_t"] * c
sigma_z1 * 1e6
In [ ]:
Copied!
# Compression factor
sigma_z0 / sigma_z1
# Compression factor
sigma_z0 / sigma_z1
Impact-Z¶
In [ ]:
Copied!
input = IZ.ImpactZInput.from_tao(tao, verbose=False, write_beam_eles="")
I = IZ.ImpactZ(input)
I.initial_particles = P0
input = IZ.ImpactZInput.from_tao(tao, verbose=False, write_beam_eles="")
I = IZ.ImpactZ(input)
I.initial_particles = P0
In [ ]:
Copied!
I.input.integrator_type = 1  # default
I.input.integrator_type = 1  # default
In [ ]:
Copied!
%%time
I.nproc = 0
I.run(verbose=False);
%%time
I.nproc = 0
I.run(verbose=False);
In [ ]:
Copied!
P2 = I.output.particles["final_particles"]
P2.plot("delta_t", "energy")
P2 = I.output.particles["final_particles"]
P2.plot("delta_t", "energy")
Compare¶
In [ ]:
Copied!
plot_impactz_and_tao_stats(I, tao)
plot_impactz_and_tao_stats(I, tao)
In [ ]:
Copied!
def compare(
    xkey="x",
    ykey="y",
    skip=1,
):
    fig, ax = plt.subplots()
    plist = (
        (P1, "Bmad", "X", "blue"),
        (P2, "Impact-Z", ".", "orange"),
    )
    for p, label, marker, color in plist:
        ax.scatter(
            p[xkey][::skip],
            p[ykey][::skip],
            label=label,
            marker=marker,
            alpha=0.5,
            color=color,
        )
    for p, label, marker, color in plist:
        ax.scatter(
            p["mean_" + xkey],
            p["mean_" + ykey],
            marker="+",
            color=color,
            facecolor="black",
        )
    plt.legend()
    ax.set_xlabel(xkey)
    ax.set_ylabel(ykey)
compare()
def compare(
    xkey="x",
    ykey="y",
    skip=1,
):
    fig, ax = plt.subplots()
    plist = (
        (P1, "Bmad", "X", "blue"),
        (P2, "Impact-Z", ".", "orange"),
    )
    for p, label, marker, color in plist:
        ax.scatter(
            p[xkey][::skip],
            p[ykey][::skip],
            label=label,
            marker=marker,
            alpha=0.5,
            color=color,
        )
    for p, label, marker, color in plist:
        ax.scatter(
            p["mean_" + xkey],
            p["mean_" + ykey],
            marker="+",
            color=color,
            facecolor="black",
        )
    plt.legend()
    ax.set_xlabel(xkey)
    ax.set_ylabel(ykey)
compare()
In [ ]:
Copied!
P1["mean_x"], P2["mean_x"]
P1["mean_x"], P2["mean_x"]
In [ ]:
Copied!
compare("delta_t", "energy")
compare("delta_t", "energy")
In [ ]:
Copied!
compare("delta_t", "xp")
compare("delta_t", "xp")
In [ ]:
Copied!
energy0 = P0["mean_energy"]
P1["mean_energy"] - energy0, P2["mean_energy"] - energy0
energy0 = P0["mean_energy"]
P1["mean_energy"] - energy0, P2["mean_energy"] - energy0
Impact-Z Lorentz integrator¶
ImpactZInput.integrator_type = 2
Note that this is not expected to work as well because the dipole fringe fields are not able to be specified.
In [ ]:
Copied!
I2 = I.copy()
I2 = I.copy()
In [ ]:
Copied!
I2.input.integrator_type = 2
I2.run()
P3 = I2.output.particles["final_particles"]
I2.input.integrator_type = 2
I2.run()
P3 = I2.output.particles["final_particles"]
In [ ]:
Copied!
stats1 = I.output.stats
stats2 = I2.output.stats
eref = I.output.stats.energy_ref[0]  # should be constant
fig, ax = plt.subplots(1, figsize=(12, 4))
ax.plot(stats1.z, (stats1.mean_energy - eref) / 1e6, label="Impact-T, map")
ax.plot(stats2.z, (stats2.mean_energy - eref) / 1e6, "--", label="Impact-T, nonlinear")
ax.set_xlabel(r"$s$ (m)")
ax.set_ylabel(r"$dE$ (MeV)")
plt.legend()
stats1 = I.output.stats
stats2 = I2.output.stats
eref = I.output.stats.energy_ref[0]  # should be constant
fig, ax = plt.subplots(1, figsize=(12, 4))
ax.plot(stats1.z, (stats1.mean_energy - eref) / 1e6, label="Impact-T, map")
ax.plot(stats2.z, (stats2.mean_energy - eref) / 1e6, "--", label="Impact-T, nonlinear")
ax.set_xlabel(r"$s$ (m)")
ax.set_ylabel(r"$dE$ (MeV)")
plt.legend()
In [ ]:
Copied!
def compare(
    xkey="x",
    ykey="y",
    skip=10,
):
    fig, ax = plt.subplots()
    for p, label, marker in (
        #  (P1, "Bmad", "X"),
        (P2, "Impact-Z map", "x"),
        (P3, "Impact-Z nonlinear", "."),
    ):
        ax.scatter(
            p[xkey][::skip], p[ykey][::skip], label=label, marker=marker, alpha=0.5
        )
    plt.legend()
    ax.set_xlabel(xkey)
    ax.set_ylabel(ykey)
compare("delta_t", "xp")
def compare(
    xkey="x",
    ykey="y",
    skip=10,
):
    fig, ax = plt.subplots()
    for p, label, marker in (
        #  (P1, "Bmad", "X"),
        (P2, "Impact-Z map", "x"),
        (P3, "Impact-Z nonlinear", "."),
    ):
        ax.scatter(
            p[xkey][::skip], p[ykey][::skip], label=label, marker=marker, alpha=0.5
        )
    plt.legend()
    ax.set_xlabel(xkey)
    ax.set_ylabel(ykey)
compare("delta_t", "xp")