xsuite / xcoll

Xsuite collimation package
Apache License 2.0
3 stars 15 forks source link

Crystal position across zero and L/R bending #57

Closed ydutheil closed 9 months ago

ydutheil commented 9 months ago

Hi,

I'm observing an inconsistent behavior when placing a bent crystal across zero. By locating element side="left" and jaw_L=-10e-3 I expect it to be across the zero but I do not observe any interaction beyond the zero. As showed bellow

absorber = xc.EverestCrystal(
    material=xc.materials.SiliconCrystal,
    active_length=0.5e-3,
    bending_radius=5,
    xdim=100e-3,
    ydim=50e-3,
    side="left",
    lattice="strip",
    jaw_L=-10e-3,
    align_angle=5e-6,
)

part0 = xt.Particles(p0c=400e9, q0=1, mass0=xt.PROTON_MASS_EV)
part_xs = np.linspace(-50e-3, 50e-3, 50)
part = xp.build_particles(x=part_xs, particle_ref=part0)

part._rng_s1 = np.random.randint(0, 1000**3, size=part._num_active_particles)
part._rng_s2 = np.random.randint(0, 1000**3, size=part._num_active_particles)
part._rng_s3 = np.random.randint(0, 1000**3, size=part._num_active_particles)
part._rng_s4 = np.random.randint(0, 1000**3, size=part._num_active_particles)

absorber.track(part)

fig, ax = plt.subplots(1, 1)

ax.plot(part.x, part.px, 'ro')
ax.plot(part_xs, np.zeros_like(part_xs), 'kx')
ax.axvline(0)

image

The second point, which might be related is that using side="both" I see channeling in 2 different directions plus some absent channeling in the center.

image

freddieknets commented 9 months ago

Hi Yann,

Concerning the first point, this is a feature/bug from the original K2 implementation, where particles on the left jaw are always assumed to have x >= 0 and particles on the right jaw x <= 0. This is not ideal and is on the todo list to adapt into a more generic and user-friendly implementation soon. However, this is not a trivial change (requiring a complete code restructuring), so it might take a while.

While waiting for the full rewrite, you can shift the beam with an xtrack.XYShift element just before the crystal as a temporary workaround and shift it back afterwards. For instance, in your example, a shift of 20mm is enough:

dx = 20e-3

shift = xt.XYShift(dx=-dx)
absorber = xc.EverestCrystal(
    material=xc.materials.SiliconCrystal,
    length=0.5e-3,
    bending_radius=5,
    xdim=100e-3,
    ydim=50e-3,
    side="left",
    lattice="strip",
    jaw_L=-10e-3 + dx,
    align_angle=5e-6,
)
shift_back = xt.XYShift(dx=dx)

part0 = xp.Particles(p0c=400e9, mass0=xt.PROTON_MASS_EV)
part_xs = np.linspace(-50e-3, 50e-3, 50)
part = xp.build_particles(x=part_xs, particle_ref=part0)

part._init_random_number_generator()

shift.track(part)
absorber.track(part)
shift_back.track(part)

fig, ax = plt.subplots(1, 1)

ax.plot(part.x, part.px, 'ro')
ax.plot(part_xs, np.zeros_like(part_xs), 'kx')
ax.axvline(0)

This indeed gives the expected behaviour:

Screenshot 2024-02-26 at 22 26 03

Note also that the way you initialised the seeds for the random generator is not the canonical one as it might introduce spurious correlations between the particles. There is a method, though hidden, to do this for you: part._init_random_number_generator().

For the second point, this is entirely expected, as setting the side to both implies that you put two crystals, one on each side of the beam (as if there were two jaws). But in K2 by default, a crystal deflects outwards, away from the beam. Hence when having both sides the beam is spread out in both directions, exactly as you observed. I probably should make side='both' non-supported for now, as its behaviour tends to confuse people.

What were you trying to achieve with this setup?

ydutheil commented 9 months ago

Hi,

Thanks a lot for your answers and recommendations. I was reproducing channeling and VR maps with an array of crystals but know that I understand this left/right separation I will shift it all away from zero as you suggested. The side='both' also makes sense now.

Thanks, I'll close my issue then. Regards Yann