atcollab / at

Accelerator Toolbox
Apache License 2.0
48 stars 31 forks source link

Addition of Resonator and Resistive Wall functions as direct passmethods #674

Open lcarver opened 11 months ago

lcarver commented 11 months ago

While running tests for #658 , I realised that actually the TRW example is rather slow. One of the reasons for this is likely due to the fact that the function is in python (not directly in C) and the srange has to be specified as an input. When it is like this, on each call to the function you have to first find the appropriate index (binarySearch) and then make the interpolation (getTableWake). For a finely sampled wake or a long wake, this can be a time consuming process.

This is avoided for the BeamLoading module where the longitudinal resonator is directly written in the C (it was done like this to avoid the interpolation around 0 where there is a discontinuity). The Beamloading is therefore fast while the others are slower.

Should we consider adding new PassMethods for each function? This would mean that we make the following additional PassMethods: LongResonatorPass TransResonatorPass TransResWallPass

which may further clutter the atintegrators directory.

swhite2401 commented 11 months ago

You are correct interpolating large tables is slower than the analytical calculation... this would nevertheless require significant changes in the architecture (both python and C) that is fully based on generating tables. We should stick to a single element but the analytical case now has to be handled differently, i.e. without table... this will become tricky when adding wake components become you have to pass individual wake parameters to the C.

lcarver commented 11 months ago

Do you think it makes sense to have both? We keep the python implementation that we have now, which allows for example a resonator to be added to other wakes with the wake table, and then in addition to this we have a direct C implementation which is good for speed and more accurate (no interpolation) but can only be used as a standalone element.

swhite2401 commented 11 months ago

I would like to keep thinks general, maintenance is much easier this way

lcarver commented 11 months ago

I will think about how to implement this cleanly.

lcarver commented 11 months ago

Quick speed benchmarking with the following script:

import numpy
import matplotlib.pyplot as plt
import sys
import at
from at.collective import BeamLoadingElement, add_beamloading, BLMode, Wake
import pickle as pkl
import time

ring = at.load_mat('/machfs/carver/Repo/EBSlattices/AT/S28F.mat', key='LOW_EMIT_RING_INJ')
ring.enable_6d()
ring.set_rf_frequency()
ring.set_rf_voltage(5.5e6)

Nbunches = 992
ring.set_fillpattern(Nbunches)

beta = 2.8
qfactor = 37500/(1+beta) 
rshunt = 145*qfactor*11
phil = 0.0
o6,_ = ring.find_orbit6()

_, fring = at.fast_ring(ring)
cavpts = at.get_refpts(fring, at.RFCavity)

mode = 'RES'
wturns = 30
if mode=='WAKE':
    add_beamloading(fring, qfactor, rshunt, Nslice=1, Nturns=wturns, mode=BLMode.WAKE, VoltGain=0.001, PhaseGain=0.001)
elif mode=='PHASOR':
    add_beamloading(fring, qfactor, rshunt, Nslice=1, mode=BLMode.PHASOR, VoltGain=0.001, PhaseGain=0.001)
else:
    # Define the resonator parameters and current
    srange = Wake.build_srange(0., 0.1, 1.0e-3, 1.0e-1,
                               ring.circumference/Nbunches, ring.circumference*wturns)

    welem = at.LongResonatorElement('wake', fring, srange, ring.get_rf_frequency(), qfactor,
                                 rshunt, Nturns=wturns, Nslice=1)
    fring.append(welem)

Npart = Nbunches

bl_elem = fring[0]

bmon = at.BeamMoments('bmon')
fring.append(bmon)

fring.beam_current = 1e-6
Nturns = 10000

part = numpy.zeros((6, Npart))

tstart = time.time()
fring.track(part, refpts=None, in_place=True, nturns=Nturns)

print(time.time() - tstart)

BLMode = Phasor: tracking time = 3.66s BLMode = Wake: tracking time = 10335.8s LongResonator: tracking time = 16160s