Simple Kicks Formalism - IBS Kicks Based on Analytical Growth Rates

This example shows how to use the SimpleKickIBS class to apply IBS kicks to tracked particles based on analytical growth rates. It implements IBS kicks based on the formalism described in [RJMMW10].

Warning

Please note that this kick formalism is not valid for any machine operating below transition energy. Details are provided in the class’s documentation. The class will raise an error at instantiation if the machine is below transition.

We will demonstrate using an xtrack.Line of the CLIC damping ring, for a positron beam.

import logging
import sys
import warnings

from dataclasses import dataclass
from typing import Self

import matplotlib.pyplot as plt
import numpy as np
import xobjects as xo
import xpart as xp
import xtrack as xt

from xibs.analytical import NagaitsevIBS
from xibs.inputs import BeamParameters, OpticsParameters
from xibs.formulary import _bunch_length, _geom_epsx, _geom_epsy, _sigma_delta
from xibs.kicks import IBSKickCoefficients, SimpleKickIBS

warnings.simplefilter("ignore")  # for this tutorial's clarity
plt.rcParams.update(
    {
        "font.family": "serif",
        "font.size": 20,
        "axes.titlesize": 20,
        "axes.labelsize": 20,
        "xtick.labelsize": 20,
        "ytick.labelsize": 20,
        "legend.fontsize": 15,
        "figure.titlesize": 20,
    }
)

Let’s start by defining the line and particle information, as well as some parameters for later use. Note that bunch_intensity is the actual number of particles in the bunch, and its value influences the analytical IBS growth rates calculation while n_part is the number of generated particles for tracking, which is much lower.

line_file = "lines/chrom-corr_DR.newlattice_2GHz.json"
bunch_intensity = int(4.5e9)
n_part = int(1.5e4)  # 15k particles initially to have a look at the kick effect
sigma_z = 1.58e-3
nemitt_x = 5.66e-7
nemitt_y = 3.7e-9

Setting up line and particles

Let’s now load the line from file, activate the accelerating cavities and create a context for multithreading with OpenMP, since tracking particles is going to take some time:

line = xt.Line.from_json(line_file)
context = xo.ContextCpu(omp_num_threads="auto")
line.particle_ref = xp.Particles(mass0=xp.ELECTRON_MASS_EV, q0=1, p0c=2.86e9)

# ----- Power accelerating cavities ----- #
for cavity in [element for element in line.elements if isinstance(element, xt.Cavity)]:
    cavity.lag = 180  # we are above transition

line.build_tracker(context, extra_headers=["#define XTRACK_MULTIPOLE_NO_SYNRAD"])
line.optimize_for_tracking()
twiss = line.twiss()

particles = xp.generate_matched_gaussian_bunch(
    num_particles=n_part,
    total_intensity_particles=bunch_intensity,
    nemitt_x=nemitt_x,
    nemitt_y=nemitt_y,
    sigma_z=sigma_z,
    line=line,
)
Loading line from dict:   0%|          | 0/71796 [00:00<?, ?it/s]
Loading line from dict:   4%|▍         | 2997/71796 [00:00<00:02, 29967.89it/s]
Loading line from dict:   8%|▊         | 6074/71796 [00:00<00:02, 30428.92it/s]
Loading line from dict:  13%|█▎        | 9206/71796 [00:00<00:02, 30814.74it/s]
Loading line from dict:  17%|█▋        | 12303/71796 [00:00<00:01, 30874.94it/s]
Loading line from dict:  22%|██▏       | 15482/71796 [00:00<00:01, 31192.98it/s]
Loading line from dict:  26%|██▌       | 18602/71796 [00:00<00:02, 24592.11it/s]
Loading line from dict:  30%|███       | 21814/71796 [00:00<00:01, 26672.38it/s]
Loading line from dict:  35%|███▍      | 25034/71796 [00:00<00:01, 28229.72it/s]
Loading line from dict:  39%|███▉      | 27990/71796 [00:00<00:01, 28209.14it/s]
Loading line from dict:  43%|████▎     | 30903/71796 [00:01<00:01, 27583.39it/s]
Loading line from dict:  47%|████▋     | 33725/71796 [00:01<00:01, 27141.95it/s]
Loading line from dict:  51%|█████     | 36483/71796 [00:01<00:01, 27154.10it/s]
Loading line from dict:  55%|█████▌    | 39596/71796 [00:01<00:01, 28291.61it/s]
Loading line from dict:  60%|█████▉    | 42744/71796 [00:01<00:00, 29220.71it/s]
Loading line from dict:  64%|██████▍   | 45888/71796 [00:01<00:00, 29869.69it/s]
Loading line from dict:  68%|██████▊   | 49148/71796 [00:01<00:00, 30671.41it/s]
Loading line from dict:  73%|███████▎  | 52300/71796 [00:01<00:00, 30921.67it/s]
Loading line from dict:  77%|███████▋  | 55564/71796 [00:01<00:00, 31427.72it/s]
Loading line from dict:  82%|████████▏ | 58714/71796 [00:02<00:00, 23848.36it/s]
Loading line from dict:  86%|████████▌ | 61803/71796 [00:02<00:00, 25568.48it/s]
Loading line from dict:  90%|████████▉ | 64593/71796 [00:02<00:00, 25779.02it/s]
Loading line from dict:  94%|█████████▍| 67337/71796 [00:02<00:00, 25908.80it/s]
Loading line from dict:  98%|█████████▊| 70059/71796 [00:02<00:00, 26266.83it/s]
Loading line from dict: 100%|██████████| 71796/71796 [00:02<00:00, 27769.08it/s]
Done loading line from dict.
Compiling ContextCpu kernels...
Done compiling ContextCpu kernels.
Disable xdeps expressions
Remove markers
Remove inactive multipoles
Merge consecutive multipoles
Remove redundant apertures
Remove zero length drifts
Merge consecutive drifts
Use simple bends
Use simple quadrupoles
Rebuild tracker data
Compiling ContextCpu kernels...
Done compiling ContextCpu kernels.

Generating the IBS Kick Class

Just as all user-facing classes in xibs, the SimpleKickIBS [RJMMW10] class is instantiated by providing a BeamParameters and an OpticsParameters objects:

# Let's make sure we get logging output to demonstrate
logging.basicConfig(
    level=logging.INFO,
    stream=sys.stdout,
    format="[%(asctime)s] [%(levelname)s] - %(module)s.%(funcName)s:%(lineno)d - %(message)s",
    datefmt="%H:%M:%S",
)

beamparams = BeamParameters.from_line(line, n_part=bunch_intensity)
opticsparams = OpticsParameters.from_line(line)
IBS = SimpleKickIBS(beamparams, opticsparams)
Compiling ContextCpu kernels...
Done compiling ContextCpu kernels.

When creating a SimpleKickIBS instance, it will automatically assign itself an analytical class (from xibs.analytical) to compute the growth rates when needed, and log its choice to the user. In our case, it chose a NagaitsevIBS class since it did not detect any vertical dispersion in the lattice. It is always possible to manually assign a chosen formalism class instead by setting the analytical_ibs (see the API reference for more details).

print(IBS.analytical_ibs)
assert isinstance(IBS.analytical_ibs, NagaitsevIBS)  # True
NagaitsevIBS object for analytical IBS calculations.
IBS growth rates computed: False

Computing and Applying IBS Kicks

Since calculating the IBS kicks requires computing the analytical growth rates, which is computationally expensive and not necessary every turn, this functionality is distinct from the application of the kicks to the particles. The so-called “kick coefficients” are computed directly from the particle distribution by calling the dedicated method. They are returned as an IBSKickCoefficients object, but also stored internally in the IBS object and will be updated internally each time they are computed.

kick_coefficients = IBS.compute_kick_coefficients(particles)
print(kick_coefficients)
print(IBS.kick_coefficients)
IBSKickCoefficients(Kx=8.814567556163397e-09, Ky=4.398453407256656e-10, Kz=LinkedArrayCpu(2.10559134e-06))
IBSKickCoefficients(Kx=8.814567556163397e-09, Ky=4.398453407256656e-10, Kz=LinkedArrayCpu(2.10559134e-06))

We can also manually set arbitrary values of kick coefficients through the kick_coefficients attribute. We will do so here with unrealistically high values in order to demonstrate the effect of the kick. The kick is applied to the particles with the apply_ibs_kick method, which applies momentum effect on each plane, weigthed by the line density of the bunch.

# Small kick in horizontal, none in vertical, strong in longitudinal
IBS.kick_coefficients = IBSKickCoefficients(5e-7, 0, 3e-4)
particles2 = particles.copy()  # let's apply on a copy of the particles
IBS.apply_ibs_kick(particles2)

We can have a look at the effect on the particles (see the xsuite user guide for more)

fig, (axx, axy, axz) = plt.subplots(1, 3, figsize=(15, 5))

axx.plot(1e4 * particles2.x, 1e4 * particles2.px, ".", label="After Kick")
axx.plot(1e4 * particles.x, 1e4 * particles.px, ".", label="Before Kick")

axy.plot(1e6 * particles2.y, 1e6 * particles2.py, ".", label="After Kick")
axy.plot(1e6 * particles.y, 1e6 * particles.py, ".", label="Before Kick")

axz.plot(1e3 * particles2.zeta, 1e3 * particles2.delta, ".", label="After Kick")
axz.plot(1e3 * particles.zeta, 1e3 * particles.delta, ".", label="Before Kick")

axx.set_xlabel(r"$x$ [$10^{-4}$m]")
axx.set_ylabel(r"$p_x$ [$10^{-4}$]")
axy.set_xlabel(r"$y$ [$10^{-6}$m]")
axy.set_ylabel(r"$p_y$ [$10^{-6}$]")
axz.set_xlabel(r"$z$ [$10^{-3}$]")
axz.set_ylabel(r"$\delta$ [$10^{-3}$]")

for axis in (axx, axy, axz):
    axis.yaxis.set_major_locator(plt.MaxNLocator(3))
    axis.legend()

plt.tight_layout()
plt.show()
demo simple kicks

Applying IBS Kicks in Tracking

Let’s now include this computation and application of IBS kicks in a tracking simulation. We will first re-initialize the particle distribution with less individual particles to speed up the tracking, and re-initialize IBS classes. We will also track the analytical evolution of relevant quantities - as done in the Nagaitsev example - for comparison.

IBS = SimpleKickIBS(beamparams, opticsparams)
NIBS = NagaitsevIBS(beamparams, opticsparams)

# Re-create particles with less elements as tracking takes a while
n_part = int(1.5e3)  # 1500 particles should be enough for this example
particles = xp.generate_matched_gaussian_bunch(
    num_particles=n_part,
    total_intensity_particles=bunch_intensity,
    nemitt_x=nemitt_x,
    nemitt_y=nemitt_y,
    sigma_z=sigma_z,
    line=line,
)

# Define some parameters for the tracking
nturns = 750  # number of turns to loop for
ibs_step = 50  # frequency at which to re-compute the growth rates & kick coefficients in [turns]

We will also set up a dataclass conveniently to store the results:

@dataclass
class Records:
    epsilon_x: np.ndarray
    epsilon_y: np.ndarray
    sigma_delta: np.ndarray
    bunch_length: np.ndarray

    def update_at_turn(self, turn: int, parts: xp.Particles, twiss: xt.TwissTable):
        self.epsilon_x[turn] = _geom_epsx(parts, twiss.betx[0], twiss.dx[0])
        self.epsilon_y[turn] = _geom_epsy(parts, twiss.bety[0], twiss.dy[0])
        self.sigma_delta[turn] = _sigma_delta(parts)
        self.bunch_length[turn] = _bunch_length(parts)

    @classmethod
    def init_zeroes(cls, n_turns: int) -> Self:  # noqa: F821
        return cls(
            epsilon_x=np.zeros(n_turns, dtype=float),
            epsilon_y=np.zeros(n_turns, dtype=float),
            sigma_delta=np.zeros(n_turns, dtype=float),
            bunch_length=np.zeros(n_turns, dtype=float),
        )


# Initialize the dataclasses & store the initial values
kicked_tbt = Records.init_zeroes(nturns)
analytical_tbt = Records.init_zeroes(nturns)

kicked_tbt.update_at_turn(0, particles, twiss)
analytical_tbt.update_at_turn(0, particles, twiss)

# Let's hide anything below WARNING level for readability
logging.basicConfig(
    level=logging.WARNING,
    stream=sys.stdout,
    format="[%(asctime)s] [%(levelname)s] - %(module)s.%(funcName)s:%(lineno)d - %(message)s",
    datefmt="%H:%M:%S",
    force=True,
)

Now, since xibs is not fully integrated into Xsuite, we will have to manually apply the IBS kick at each turn of tracking, and also manually trigger the turn of tracking. Just like in the analytical examples, we do so in a loop over the turns:

# ----- We loop here now ----- #

for turn in range(1, nturns):
    # ----- Potentially re-compute the IBS growth rates and kick coefficients ----- #
    if (turn % ibs_step == 0) or (turn == 1):
        print(f"Turn {turn:d}: re-computing growth rates and kick coefficients")
        # Compute kick coefficients from the particle distribution at this moment
        IBS.compute_kick_coefficients(particles)
        # Compute analytical values from those at the previous turn
        NIBS.growth_rates(
            analytical_tbt.epsilon_x[turn - 1],
            analytical_tbt.epsilon_y[turn - 1],
            analytical_tbt.sigma_delta[turn - 1],
            analytical_tbt.bunch_length[turn - 1],
        )
    else:
        print(f"Turn {turn:d}")

    # ----- Manually Apply IBS Kick and Track Turn ----- #
    IBS.apply_ibs_kick(particles)
    line.track(particles, num_turns=1)

    # ----- Update records for tracked particles ----- #
    kicked_tbt.update_at_turn(turn, particles, twiss)

    # ----- Compute analytical Emittances from previous turn values & update records----- #
    ana_emit_x, ana_emit_y, ana_sig_delta, ana_bunch_length = NIBS.emittance_evolution(
        analytical_tbt.epsilon_x[turn - 1],
        analytical_tbt.epsilon_y[turn - 1],
        analytical_tbt.sigma_delta[turn - 1],
        analytical_tbt.bunch_length[turn - 1],
    )
    analytical_tbt.epsilon_x[turn] = ana_emit_x
    analytical_tbt.epsilon_y[turn] = ana_emit_y
    analytical_tbt.sigma_delta[turn] = ana_sig_delta
    analytical_tbt.bunch_length[turn] = ana_bunch_length
Turn 1: re-computing growth rates and kick coefficients
Turn 2
Turn 3
Turn 4
Turn 5
Turn 6
Turn 7
Turn 8
Turn 9
Turn 10
Turn 11
Turn 12
Turn 13
Turn 14
Turn 15
Turn 16
Turn 17
Turn 18
Turn 19
Turn 20
Turn 21
Turn 22
Turn 23
Turn 24
Turn 25
Turn 26
Turn 27
Turn 28
Turn 29
Turn 30
Turn 31
Turn 32
Turn 33
Turn 34
Turn 35
Turn 36
Turn 37
Turn 38
Turn 39
Turn 40
Turn 41
Turn 42
Turn 43
Turn 44
Turn 45
Turn 46
Turn 47
Turn 48
Turn 49
Turn 50: re-computing growth rates and kick coefficients
Turn 51
Turn 52
Turn 53
Turn 54
Turn 55
Turn 56
Turn 57
Turn 58
Turn 59
Turn 60
Turn 61
Turn 62
Turn 63
Turn 64
Turn 65
Turn 66
Turn 67
Turn 68
Turn 69
Turn 70
Turn 71
Turn 72
Turn 73
Turn 74
Turn 75
Turn 76
Turn 77
Turn 78
Turn 79
Turn 80
Turn 81
Turn 82
Turn 83
Turn 84
Turn 85
Turn 86
Turn 87
Turn 88
Turn 89
Turn 90
Turn 91
Turn 92
Turn 93
Turn 94
Turn 95
Turn 96
Turn 97
Turn 98
Turn 99
Turn 100: re-computing growth rates and kick coefficients
Turn 101
Turn 102
Turn 103
Turn 104
Turn 105
Turn 106
Turn 107
Turn 108
Turn 109
Turn 110
Turn 111
Turn 112
Turn 113
Turn 114
Turn 115
Turn 116
Turn 117
Turn 118
Turn 119
Turn 120
Turn 121
Turn 122
Turn 123
Turn 124
Turn 125
Turn 126
Turn 127
Turn 128
Turn 129
Turn 130
Turn 131
Turn 132
Turn 133
Turn 134
Turn 135
Turn 136
Turn 137
Turn 138
Turn 139
Turn 140
Turn 141
Turn 142
Turn 143
Turn 144
Turn 145
Turn 146
Turn 147
Turn 148
Turn 149
Turn 150: re-computing growth rates and kick coefficients
Turn 151
Turn 152
Turn 153
Turn 154
Turn 155
Turn 156
Turn 157
Turn 158
Turn 159
Turn 160
Turn 161
Turn 162
Turn 163
Turn 164
Turn 165
Turn 166
Turn 167
Turn 168
Turn 169
Turn 170
Turn 171
Turn 172
Turn 173
Turn 174
Turn 175
Turn 176
Turn 177
Turn 178
Turn 179
Turn 180
Turn 181
Turn 182
Turn 183
Turn 184
Turn 185
Turn 186
Turn 187
Turn 188
Turn 189
Turn 190
Turn 191
Turn 192
Turn 193
Turn 194
Turn 195
Turn 196
Turn 197
Turn 198
Turn 199
Turn 200: re-computing growth rates and kick coefficients
Turn 201
Turn 202
Turn 203
Turn 204
Turn 205
Turn 206
Turn 207
Turn 208
Turn 209
Turn 210
Turn 211
Turn 212
Turn 213
Turn 214
Turn 215
Turn 216
Turn 217
Turn 218
Turn 219
Turn 220
Turn 221
Turn 222
Turn 223
Turn 224
Turn 225
Turn 226
Turn 227
Turn 228
Turn 229
Turn 230
Turn 231
Turn 232
Turn 233
Turn 234
Turn 235
Turn 236
Turn 237
Turn 238
Turn 239
Turn 240
Turn 241
Turn 242
Turn 243
Turn 244
Turn 245
Turn 246
Turn 247
Turn 248
Turn 249
Turn 250: re-computing growth rates and kick coefficients
Turn 251
Turn 252
Turn 253
Turn 254
Turn 255
Turn 256
Turn 257
Turn 258
Turn 259
Turn 260
Turn 261
Turn 262
Turn 263
Turn 264
Turn 265
Turn 266
Turn 267
Turn 268
Turn 269
Turn 270
Turn 271
Turn 272
Turn 273
Turn 274
Turn 275
Turn 276
Turn 277
Turn 278
Turn 279
Turn 280
Turn 281
Turn 282
Turn 283
Turn 284
Turn 285
Turn 286
Turn 287
Turn 288
Turn 289
Turn 290
Turn 291
Turn 292
Turn 293
Turn 294
Turn 295
Turn 296
Turn 297
Turn 298
Turn 299
Turn 300: re-computing growth rates and kick coefficients
Turn 301
Turn 302
Turn 303
Turn 304
Turn 305
Turn 306
Turn 307
Turn 308
Turn 309
Turn 310
Turn 311
Turn 312
Turn 313
Turn 314
Turn 315
Turn 316
Turn 317
Turn 318
Turn 319
Turn 320
Turn 321
Turn 322
Turn 323
Turn 324
Turn 325
Turn 326
Turn 327
Turn 328
Turn 329
Turn 330
Turn 331
Turn 332
Turn 333
Turn 334
Turn 335
Turn 336
Turn 337
Turn 338
Turn 339
Turn 340
Turn 341
Turn 342
Turn 343
Turn 344
Turn 345
Turn 346
Turn 347
Turn 348
Turn 349
Turn 350: re-computing growth rates and kick coefficients
Turn 351
Turn 352
Turn 353
Turn 354
Turn 355
Turn 356
Turn 357
Turn 358
Turn 359
Turn 360
Turn 361
Turn 362
Turn 363
Turn 364
Turn 365
Turn 366
Turn 367
Turn 368
Turn 369
Turn 370
Turn 371
Turn 372
Turn 373
Turn 374
Turn 375
Turn 376
Turn 377
Turn 378
Turn 379
Turn 380
Turn 381
Turn 382
Turn 383
Turn 384
Turn 385
Turn 386
Turn 387
Turn 388
Turn 389
Turn 390
Turn 391
Turn 392
Turn 393
Turn 394
Turn 395
Turn 396
Turn 397
Turn 398
Turn 399
Turn 400: re-computing growth rates and kick coefficients
Turn 401
Turn 402
Turn 403
Turn 404
Turn 405
Turn 406
Turn 407
Turn 408
Turn 409
Turn 410
Turn 411
Turn 412
Turn 413
Turn 414
Turn 415
Turn 416
Turn 417
Turn 418
Turn 419
Turn 420
Turn 421
Turn 422
Turn 423
Turn 424
Turn 425
Turn 426
Turn 427
Turn 428
Turn 429
Turn 430
Turn 431
Turn 432
Turn 433
Turn 434
Turn 435
Turn 436
Turn 437
Turn 438
Turn 439
Turn 440
Turn 441
Turn 442
Turn 443
Turn 444
Turn 445
Turn 446
Turn 447
Turn 448
Turn 449
Turn 450: re-computing growth rates and kick coefficients
Turn 451
Turn 452
Turn 453
Turn 454
Turn 455
Turn 456
Turn 457
Turn 458
Turn 459
Turn 460
Turn 461
Turn 462
Turn 463
Turn 464
Turn 465
Turn 466
Turn 467
Turn 468
Turn 469
Turn 470
Turn 471
Turn 472
Turn 473
Turn 474
Turn 475
Turn 476
Turn 477
Turn 478
Turn 479
Turn 480
Turn 481
Turn 482
Turn 483
Turn 484
Turn 485
Turn 486
Turn 487
Turn 488
Turn 489
Turn 490
Turn 491
Turn 492
Turn 493
Turn 494
Turn 495
Turn 496
Turn 497
Turn 498
Turn 499
Turn 500: re-computing growth rates and kick coefficients
Turn 501
Turn 502
Turn 503
Turn 504
Turn 505
Turn 506
Turn 507
Turn 508
Turn 509
Turn 510
Turn 511
Turn 512
Turn 513
Turn 514
Turn 515
Turn 516
Turn 517
Turn 518
Turn 519
Turn 520
Turn 521
Turn 522
Turn 523
Turn 524
Turn 525
Turn 526
Turn 527
Turn 528
Turn 529
Turn 530
Turn 531
Turn 532
Turn 533
Turn 534
Turn 535
Turn 536
Turn 537
Turn 538
Turn 539
Turn 540
Turn 541
Turn 542
Turn 543
Turn 544
Turn 545
Turn 546
Turn 547
Turn 548
Turn 549
Turn 550: re-computing growth rates and kick coefficients
Turn 551
Turn 552
Turn 553
Turn 554
Turn 555
Turn 556
Turn 557
Turn 558
Turn 559
Turn 560
Turn 561
Turn 562
Turn 563
Turn 564
Turn 565
Turn 566
Turn 567
Turn 568
Turn 569
Turn 570
Turn 571
Turn 572
Turn 573
Turn 574
Turn 575
Turn 576
Turn 577
Turn 578
Turn 579
Turn 580
Turn 581
Turn 582
Turn 583
Turn 584
Turn 585
Turn 586
Turn 587
Turn 588
Turn 589
Turn 590
Turn 591
Turn 592
Turn 593
Turn 594
Turn 595
Turn 596
Turn 597
Turn 598
Turn 599
Turn 600: re-computing growth rates and kick coefficients
Turn 601
Turn 602
Turn 603
Turn 604
Turn 605
Turn 606
Turn 607
Turn 608
Turn 609
Turn 610
Turn 611
Turn 612
Turn 613
Turn 614
Turn 615
Turn 616
Turn 617
Turn 618
Turn 619
Turn 620
Turn 621
Turn 622
Turn 623
Turn 624
Turn 625
Turn 626
Turn 627
Turn 628
Turn 629
Turn 630
Turn 631
Turn 632
Turn 633
Turn 634
Turn 635
Turn 636
Turn 637
Turn 638
Turn 639
Turn 640
Turn 641
Turn 642
Turn 643
Turn 644
Turn 645
Turn 646
Turn 647
Turn 648
Turn 649
Turn 650: re-computing growth rates and kick coefficients
Turn 651
Turn 652
Turn 653
Turn 654
Turn 655
Turn 656
Turn 657
Turn 658
Turn 659
Turn 660
Turn 661
Turn 662
Turn 663
Turn 664
Turn 665
Turn 666
Turn 667
Turn 668
Turn 669
Turn 670
Turn 671
Turn 672
Turn 673
Turn 674
Turn 675
Turn 676
Turn 677
Turn 678
Turn 679
Turn 680
Turn 681
Turn 682
Turn 683
Turn 684
Turn 685
Turn 686
Turn 687
Turn 688
Turn 689
Turn 690
Turn 691
Turn 692
Turn 693
Turn 694
Turn 695
Turn 696
Turn 697
Turn 698
Turn 699
Turn 700: re-computing growth rates and kick coefficients
Turn 701
Turn 702
Turn 703
Turn 704
Turn 705
Turn 706
Turn 707
Turn 708
Turn 709
Turn 710
Turn 711
Turn 712
Turn 713
Turn 714
Turn 715
Turn 716
Turn 717
Turn 718
Turn 719
Turn 720
Turn 721
Turn 722
Turn 723
Turn 724
Turn 725
Turn 726
Turn 727
Turn 728
Turn 729
Turn 730
Turn 731
Turn 732
Turn 733
Turn 734
Turn 735
Turn 736
Turn 737
Turn 738
Turn 739
Turn 740
Turn 741
Turn 742
Turn 743
Turn 744
Turn 745
Turn 746
Turn 747
Turn 748
Turn 749

Feel free to run this simulation for more turns, with a different frequency of the IBS kick coefficients & growth rates re-computation, or with more particles. After this loop is done running, we can plot the evolutions across turns:

turns = np.arange(nturns, dtype=int)  # array of turns
fig, axs = plt.subplot_mosaic([["epsx", "epsy"], ["sigd", "bl"]], sharex=True, figsize=(15, 7))

# Plot from tracked & kicked particles
axs["epsx"].plot(turns, kicked_tbt.epsilon_x * 1e10, lw=2, label="Simple Kicks")
axs["epsy"].plot(turns, kicked_tbt.epsilon_y * 1e13, lw=2, label="Simple Kicks")
axs["sigd"].plot(turns, kicked_tbt.sigma_delta * 1e3, lw=2, label="Simple Kicks")
axs["bl"].plot(turns, kicked_tbt.bunch_length * 1e3, lw=2, label="Simple Kicks")

# Plot from analytical values
axs["epsx"].plot(turns, analytical_tbt.epsilon_x * 1e10, lw=2.5, label="Analytical")
axs["epsy"].plot(turns, analytical_tbt.epsilon_y * 1e13, lw=2.5, label="Analytical")
axs["sigd"].plot(turns, analytical_tbt.sigma_delta * 1e3, lw=2.5, label="Analytical")
axs["bl"].plot(turns, analytical_tbt.bunch_length * 1e3, lw=2.5, label="Analytical")

# Axes parameters
axs["epsx"].set_ylabel(r"$\varepsilon_x$ [$10^{-10}$m]")
axs["epsy"].set_ylabel(r"$\varepsilon_y$ [$10^{-13}$m]")
axs["sigd"].set_ylabel(r"$\sigma_{\delta}$ [$10^{-3}$]")
axs["bl"].set_ylabel(r"Bunch length [mm]")

for axis in (axs["epsy"], axs["bl"]):
    axis.yaxis.set_label_position("right")
    axis.yaxis.tick_right()

for axis in (axs["sigd"], axs["bl"]):
    axis.set_xlabel("Turn Number")

for axis in axs.values():
    axis.yaxis.set_major_locator(plt.MaxNLocator(3))
    axis.legend(loc=9, ncols=4)

fig.align_ylabels((axs["epsx"], axs["sigd"]))
fig.align_ylabels((axs["epsy"], axs["bl"]))

plt.tight_layout()
plt.show()
demo simple kicks

References

The use of the following functions, methods, classes and modules is shown in this example:

  • analytical: NagaitsevIBS, growth_rates, emittance_evolution

  • inputs: BeamParameters, OpticsParameters

  • kicks: IBSKickCoefficients, SimpleKickIBS, apply_ibs_kick, compute_kick_coefficients

Total running time of the script: (2 minutes 10.079 seconds)

Gallery generated by Sphinx-Gallery