Skip to content
Projects

beamtracer.optimization.einzel

An einzel lens: the first design problem of beamtracer.optimization.

Three coaxial tubes in cylindrical (x, r) geometry, the beam axis along x: the outer two at 0 V, the middle one at the design voltage V, inside a grounded wall. Each tube is electrode_length long and thickness thick, its bore at the radius aperture, with half-round ends (made of a simgeometry rectangle and two circles: a sharp corner has a singular surface field, which grows as the mesh is refined); gap separates neighbouring tubes. A parallel beam of ions (protons by default) is launched upstream with energy and radius beam_radius, and the objective is its rms size at the target plane downstream. Trajectories that hit an electrode are lost, and the lost fraction of the current is a constraint, as is the largest field on the electrode surfaces.

EinzelLens holds the fixed settings and makes the beamtracer.optimization.Design and beamtracer.optimization.Problem of any of the parameters voltage, gap and aperture; EinzelLens.thin_lens_voltage and EinzelLens.paraxial_voltage are the voltages that focus at the target in the thin-lens and paraxial approximations.

The thin lens

For a paraxial ray in an axially symmetric electrostatic field, with ϕ(z)\phi(z) the potential relative to the ions’ zero of kinetic energy (the kinetic energy per charge, ϕ=T/q=T0/q−V(z)\phi = T / q = T_0 / q - V(z) on the axis for ions starting at V=0V = 0), the ray equation (non-relativistic)

r′′+ϕ′2ϕr′+ϕ′′4ϕr=0r'' + \frac{\phi'}{2\phi} r' + \frac{\phi''}{4\phi} r = 0

becomes, with the Picht transformation R=rϕ1/4R = r \phi^{1/4},

R′′+316(ϕ′ϕ)2R=0.R'' + \frac{3}{16} \left(\frac{\phi'}{\phi}\right)^2 R = 0 .

An einzel lens has the same potential on both sides, so RR and rr agree up to a constant outside it; for a weak (thin) lens RR hardly changes inside it, and a parallel ray of radius rr leaves with the slope −r/f-r/f,

1f=316∫(ϕ′ϕ)2dz.\frac{1}{f} = \frac{3}{16} \int \left(\frac{\phi'}{\phi}\right)^2 dz .

See the paraxial ray equation and electrostatic lenses in M. Reiser, Theory and Design of Charged Particle Beams (Wiley, 2nd ed. 2008), S. Humphries, Principles of Charged Particle Acceleration (Wiley, 1986) and H. Wollnik, Optics of Charged Particles (Academic Press, 1987). It holds for paraxial rays and a lens short compared with f; ϕ\phi must stay positive (no ion is reflected). Here the thin lens sits at the middle electrode, and its focal length is the distance to the target. EinzelLens.paraxial_voltage integrates the Picht equation through the whole lens instead (thick, still paraxial and without aberrations).

Attributes

NameDescription
ELECTRODESNo description.
NEGLIGIBLE_CURRENTNo description.
PARAMETERSNo description.

Classes

NameDescription
EinzelLensThe fixed settings of an einzel lens, and its problems.

ELECTRODESattributemodule attribute#

ELECTRODES = ('upstream', 'middle', 'downstream')

NEGLIGIBLE_CURRENTattributemodule attribute#

NEGLIGIBLE_CURRENT = 1e-09

PARAMETERSattributemodule attribute#

PARAMETERS = {'voltage': 'V', 'gap': 'm', 'aperture': 'm'}

EinzelLensclassdataclass#

class EinzelLens(energy: float = unit_field('eV', 30000.0), charge: float = 1.0, mass: float = unit_field('u', 1.007276), current: float = unit_field('A', 0.0), beam_radius: float = unit_field('m', 0.002), num_particles: int = 60, start: float = unit_field('m', 0.001), voltage: float = unit_field('V', 15000.0), gap: float = unit_field('m', 0.004), aperture: float = unit_field('m', 0.005), electrode_length: float = unit_field('m', 0.008), thickness: float = unit_field('m', 0.002), rounded: bool = True, centre: float = unit_field('m', 0.04), target: float = unit_field('m', 0.12), length: float = unit_field('m', 0.13), radius: float = unit_field('m', 0.02), cell: float = unit_field('m', 0.0005), field_limit: float = unit_field('V/m', 5000000.0), max_loss: float = 0.0, iterations: int = 40, tolerance: float = unit_field('V', 0.01), relaxation: float = 0.8, save_trajectories: int = 0)

The fixed settings of an einzel lens, and its problems.

The beam: ions of energy [eV], charge [e] and mass [u] (protons by default), current [A] (0: no space charge), launched parallel at x = start [m] from the axis to beam_radius [m] as num_particles rings. The lens: three tubes of electrode_length [m] and thickness [m], the middle one centred on x = centre [m], with half-round ends if rounded; voltage [V], gap [m] and aperture [m] are the values of the parameters not being optimized. The domain is length by radius [m] with nodes every cell [m], grounded at both ends and on the outer wall; the objective is the rms size (sqrt(<r^2> / 2), the projected rms) at x = target [m]. The constraints are the largest surface field over field_limit [V/m], minus 1, and the lost fraction of the current minus max_loss. iterations, tolerance [V] and relaxation set the Vlasov iteration with space charge; without it, one field solve and trace is the solution. Every physical setting takes an astropy Quantity or a string too ("30 keV", 4 * u.mm), converted to these units.

A frozen dataclass, so it pickles: its config and evaluate are the build and evaluation functions of its problems, also in a process pool.

Numerics

ray

r′′+ϕ′2ϕr′+ϕ′′4ϕr=0,ϕ(z)=Eq−V(z)r'' + \frac{\phi'}{2\phi} r' + \frac{\phi''}{4\phi} r = 0, \qquad \phi(z) = \frac{E}{q} - V(z)

objective

spot=12⟨r2⟩ at the target plane\text{spot} = \sqrt{\tfrac12 \langle r^2 \rangle} \ \text{at the target plane}

constraints

loss=max⁡(0,1−T)−max_loss,surface_field=max⁡∣Esurface∣field_limit−1\text{loss} = \max(0, 1 - T) - \text{max\_loss}, \qquad \text{surface\_field} = \frac{\max |E_\mathrm{surface}|}{\text{field\_limit}} - 1

transmission

T=ItargetπJRb2T = \frac{I_\mathrm{target}}{\pi J R_b^2}

Assumptions:

  • three coaxial tubes, the outer two grounded, the middle one at VV, inside a grounded wall; a parallel beam launched upstream as rings
  • without space charge (current 0) one field solve and trace is the solution, with 10−910^{-9} A as the weight of the moments
  • a design that does not fit is a failed evaluation

References:

Built from:

  • beamtracer.model.BeamOptics
  • beamtracer.optimization.einzel.EinzelLens.thin_lens_focal_length
  • beamtracer.optimization.einzel.EinzelLens.paraxial_focus

apertureattributeclass attributeinstance attribute#

aperture: float = unit_field('m', 0.005)

beam_radiusattributeclass attributeinstance attribute#

beam_radius: float = unit_field('m', 0.002)

cellattributeclass attributeinstance attribute#

cell: float = unit_field('m', 0.0005)

centreattributeclass attributeinstance attribute#

centre: float = unit_field('m', 0.04)

chargeattributeclass attributeinstance attribute#

charge: float = 1.0

currentattributeclass attributeinstance attribute#

current: float = unit_field('A', 0.0)

electrode_lengthattributeclass attributeinstance attribute#

electrode_length: float = unit_field('m', 0.008)

energyattributeclass attributeinstance attribute#

energy: float = unit_field('eV', 30000.0)

field_limitattributeclass attributeinstance attribute#

field_limit: float = unit_field('V/m', 5000000.0)

gapattributeclass attributeinstance attribute#

gap: float = unit_field('m', 0.004)

iterationsattributeclass attributeinstance attribute#

iterations: int = 40

lengthattributeclass attributeinstance attribute#

length: float = unit_field('m', 0.13)

massattributeclass attributeinstance attribute#

mass: float = unit_field('u', 1.007276)

max_lossattributeclass attributeinstance attribute#

max_loss: float = 0.0

num_particlesattributeclass attributeinstance attribute#

num_particles: int = 60

radiusattributeclass attributeinstance attribute#

radius: float = unit_field('m', 0.02)

relaxationattributeclass attributeinstance attribute#

relaxation: float = 0.8

roundedattributeclass attributeinstance attribute#

rounded: bool = True

save_trajectoriesattributeclass attributeinstance attribute#

save_trajectories: int = 0

startattributeclass attributeinstance attribute#

start: float = unit_field('m', 0.001)

targetattributeclass attributeinstance attribute#

target: float = unit_field('m', 0.12)

thicknessattributeclass attributeinstance attribute#

thickness: float = unit_field('m', 0.002)

toleranceattributeclass attributeinstance attribute#

tolerance: float = unit_field('V', 0.01)

voltageattributeclass attributeinstance attribute#

voltage: float = unit_field('V', 15000.0)

axis_potentialmethod#

def axis_potential(values: Mapping[str, float] | None = None) -> tuple[np.ndarray, np.ndarray]

Return x [m] and the potential on the axis per volt on the middle tube.

From a field solve at the given geometry (without space charge, the potential is proportional to the voltage).

configmethod#

def config(values: Mapping[str, float] | None = None) -> BeamOpticsConfig

Return the beamtracer.BeamOpticsConfig of the lens.

values are some of voltage, gap and aperture; the others are the lens defaults. Raises ValueError for a lens that does not fit between the start of the beam and the target.

designmethod#

def design(bounds: Mapping[str, tuple[float, float]]) -> Design

Return the design of the parameters in bounds ({name: (lower, upper)}).

electrodemethod#

def electrode(name: str, x: float, aperture: float, voltage: float) -> dict[str, ObjectParams]

Return the objects of a tube centred on x with its bore at aperture.

A tube of electrode_length along the axis and thickness across it. With rounded its ends are half circles (a rectangle and two circles, named name, name.upstream and name.downstream); without, it is a rectangle with sharp corners, whose surface field is singular.

electrode_positionsmethod#

def electrode_positions(gap: float) -> tuple[float, float, float]

Return the centres [m] of the three tubes.

evaluatemethod#

def evaluate(config: BeamOpticsConfig) -> Evaluation

Solve the lens of a configuration and return its measure.

kinetic_potentialmethod#

def kinetic_potential(potential: np.ndarray) -> np.ndarray

Return the ions’ kinetic energy per charge, phi [V], at a potential [V].

measuremethod#

def measure(model: BeamOptics) -> Evaluation

Return the beamtracer.optimization.Evaluation of a solved lens.

The objective spot is the rms size [m] at the target; the constraints are surface_field (the largest surface field over field_limit, minus 1) and loss (the lost fraction of the current minus max_loss). The diagnostics hold the field [V/m], the transmission, the rms, 90 % and largest radii [m] at the target, and the Vlasov iterations.

paraxial_focusmethod#

def paraxial_focus(x: np.ndarray, potential: np.ndarray) -> float

Return where a paraxial ray, parallel at the start, crosses the axis [m].

The Picht equation integrated through the on-axis potential [V] (a thick lens, without aberrations); a ray still away from the axis at the end of the domain is continued straight (the field is zero there). inf if it diverges.

Numerics

picht

R′′+316(ϕ′ϕ)2R=0,R=r ϕ1/4,R(z0)=1, R′(z0)=0R'' + \frac{3}{16}\left(\frac{\phi'}{\phi}\right)^2 R = 0, \qquad R = r\, \phi^{1/4}, \qquad R(z_0) = 1, \ R'(z_0) = 0

focus

zf:R(zf)=0,or zend−R/R′ for a ray still off the axis at the endz_f: R(z_f) = 0, \qquad \text{or } z_\mathrm{end} - R / R' \text{ for a ray still off the axis at the end}

Assumptions:

  • a thick lens, paraxial and without aberrations; integrated by solve_ivp (rtol 10−910^{-9}, at most one cell per step), the coefficient interpolated linearly
  • ∞\infty for a diverging ray; the voltage that focuses on the target by Brent’s method

paraxial_voltagemethod#

def paraxial_voltage(values: Mapping[str, float] | None = None) -> float

Return the voltage whose paraxial (thick-lens) focus is on the target.

Like thin_lens_voltage, but integrating the paraxial ray through the lens (paraxial_focus).

problemmethod#

def problem(bounds: Mapping[str, tuple[float, float]], constraints: tuple[str, ...] = ('loss')) -> Problem

Return the problem of minimizing the spot over the parameters in bounds.

constraints are some of "loss" and "surface_field".

solvemethod#

def solve(values: Mapping[str, float] | None = None) -> BeamOptics

Return the solved beamtracer.BeamOptics of the lens.

surface_fieldsmethodstaticmethod#

def surface_fields(model: BeamOptics) -> dict[str, float]

Return the largest surface field [V/m] of each tube (of all its objects).

thin_lens_focal_lengthmethod#

def thin_lens_focal_length(x: np.ndarray, potential: np.ndarray) -> float

Return the thin-lens focal length [m] of the on-axis potential [V].

1 / f = (3 / 16) int (phi' / phi)^2 dx (see the module docstring), with phi the kinetic energy per charge; infinite without a lens, NaN if an ion is stopped.

Numerics

thin lens

1f=316∫(ϕ′ϕ)2dz\frac{1}{f} = \frac{3}{16} \int \left(\frac{\phi'}{\phi}\right)^2 dz

discrete

ϕ′ by central differences,∫ by the trapezoidal rule on the mesh nodes\phi' \text{ by central differences}, \qquad \int \text{ by the trapezoidal rule on the mesh nodes}

voltage

1f(V)=1xtarget−xcentre(Brent’s method)\frac{1}{f(V)} = \frac{1}{x_\mathrm{target} - x_\mathrm{centre}} \quad \text{(Brent's method)}

Order of accuracy: 2.

Assumptions:

  • paraxial rays, a lens short compared with ff, the same potential on both sides; ϕ>0\phi > 0 everywhere (NaN if an ion is stopped), ∞\infty without a lens
  • the on-axis potential per volt from one vacuum solve at 1 kV on the middle tube
  • the order is in the node spacing

thin_lens_voltagemethod#

def thin_lens_voltage(values: Mapping[str, float] | None = None) -> float

Return the voltage whose thin lens at the middle tube focuses on the target.

The voltage has the sign of the lens default voltage (for positive ions, positive decelerates them and negative accelerates them), at the geometry of values. Raises ValueError if no voltage of that sign focuses there.

valuesmethod#

def values(values: Mapping[str, float] | None = None) -> dict[str, float]

Return all three parameters: values, the defaults for the others.