Beamline
The pieces of beamtracer.beamline in a row, from the grid to the port: a group of aimed
beamlets, the charge states the neutralizer leaves, the residual-ion dump, the duct with
its gas and the stray field, and where the power goes. At the end the neutrals at the port
are written out as a dataset, in the tokamak’s frame, for the deposition in the plasma
(freeofcharge).
The numbers are illustrative: a group of 5 by 16 beamlets of 1 MeV deuterium, 5 mrad each, curved to 7 m; the cross sections are placeholders, as plasmacoll holds no beam stripping data.
import matplotlib.pyplot as pltimport numpy as npimport plasma_plots as ppfrom plasmacoll import ChargeStateTransitionsfrom scipy import constantsfrom simgeometry import Cuboid
from beamtracer.beamline import ( ApertureGrid, Duct, FieldRegion, Markers, beamline_summary, box_field, gaussian_beam, profile_field,)
mass = 2.014 * constants.atomic_massspeed = np.sqrt(2 * 1e6 * constants.e / mass)beamlet = gaussian_beam(1500, power=2e5, speed=speed, width=2e-3, divergence=5e-3)grid = ApertureGrid.regular(16, 5, (0.020, 0.022), focal_length=(7.0, 7.0))beam = grid.markers(beamlet)
# the neutralizer: 60 % neutrals, 20 % each of D- and D+beam.state = np.random.default_rng(0).choice(3, size=len(beam), p=[0.2, 0.6, 0.2])print(f"{len(beam)} markers, {beam.total_power / 1e6:.0f} MW")120000 markers, 16 MWThe dump and the duct
Section titled “The dump and the duct”The dump: two plates 0.12 m apart from 0.5 to 2.3 m, 20 kV between them, across the narrow side of the group. The duct: a rectangular channel of four plates from 2.3 to 6 m, its bore 0.12 by 0.24 m, with 1e18 m⁻³ of D and a vertical stray field rising from 5 to 50 mT towards the port at 6 m.
dump_start, dump_end, gap = 0.5, 2.3, 0.12dump_length = dump_end - dump_startplates = [ Cuboid( ((dump_start + dump_end) / 2, s * (gap / 2 + 0.005), 0), (dump_length, 0.01, 0.5), ) for s in (1, -1)]dump = FieldRegion( plates, exit=dump_end, mass=mass, electric=box_field( (0, 20e3 / gap, 0), (dump_start, -gap / 2, -0.25), (dump_end, gap / 2, 0.25) ), step=2e-3,).trace(beam)
port, half_y, half_z, wall = 6.0, 0.06, 0.12, 0.05middle, duct_length = (dump_end + port) / 2, port - dump_endduct_walls = [ Cuboid( (middle, s * (half_y + wall / 2), 0), (duct_length, wall, 2 * (half_z + wall)) ) for s in (1, -1)] + [ Cuboid((middle, 0, s * (half_z + wall / 2)), (duct_length, 2 * half_y, wall)) for s in (1, -1)]transitions = ChargeStateTransitions({"D2": {("0", "+"): 1.5e-20}})duct = Duct(duct_walls, port=port, transitions=transitions, gas={"D2": 1e18}).trace( dump.exit)stray = profile_field([dump_end, port], [[0, 0], [0, 0], [0.005, 0.05]])bent = FieldRegion(duct_walls, exit=port, mass=mass, magnetic=stray).trace( duct.reionized)
summary = beamline_summary( Markers.concatenate([duct.port, bent.exit]), {"dump": dump.wall, "duct": duct.wall, "duct, reionized": bent.wall}, power=beam.total_power, work=dump.work + bent.work, # what the dump field gave the ions halo_radius=0.05, footprint_extent=(-0.1, 0.1, -0.15, 0.15), bins=50,)summary<xarray.Dataset> Size: 21kB
Dimensions: (part: 3, y: 50, z: 50)
Coordinates:
* part (part) <U15 180B 'dump' 'duct' 'duct, reionized'
* y (y) float64 400B -0.098 -0.094 -0.09 ... 0.09 0.094 0.098
* z (z) float64 400B -0.147 -0.141 -0.135 ... 0.135 0.141 0.147
Data variables:
power_in float64 8B 1.6e+07
power_work float64 8B 6.386e+04
power_port float64 8B 8.488e+06
power_on (part) float64 24B 6.461e+06 7.123e+05 4.021e+05
transmission float64 8B 0.5305
power_halo float64 8B 1.958e+06
footprint (y, z) float64 20kB 0.0 0.0 0.0 0.0 0.0 ... 0.0 0.0 0.0 0.0Where the power goes
Section titled “Where the power goes”parts = ["port"] + list(summary.part.values)values = [float(summary.power_port)] + list(summary.power_on.values)with pp.figure(figsize=(7, 3.2)) as fig: ax = fig[0] ax.barh(parts[::-1], np.array(values[::-1]) / 1e6, color="#2a78d6") for k, v in enumerate(values[::-1]): ax.text( v / 1e6, k, f" {v / 1e6:.1f} MW ({v / float(summary.power_in):.0%})", va="center", ) ax.set_xlabel("power [MW]") ax.set_xlim(0, 1.3 * max(values) / 1e6)
The dump takes the 40 % of the beam that left the neutralizer charged. Of the neutrals the duct walls take the tails of the beam, the gas reionizes a few percent, and the stray field turns those onto the walls near the port. The rest, about half the beam, reaches the port.
The footprint at the port
Section titled “The footprint at the port”The group converges towards its focus at 7 m, 1 m behind the port. The power density at the
port, clipped at m by the duct, with a circle of 5 cm round the beam’s
centre; the power beyond it is power_halo. Here that is the Gaussian tail of the
beamlets: the halo the experiments see, a broad component of some 30 mrad, appears
only if the beamlet carries one.
footprint = (summary.footprint / 1e6).assign_attrs(units="MW/m²")result = footprint.plasma.plot.slice( x="y", y="z", equal_aspect=True, colorbar_label="MW/m²")ax = plt.gca()ax.add_patch(plt.Circle((0, 0), 0.05, fill=False, color="white", ls="--"))print( f"beyond 5 cm: {float(summary.power_halo) / float(summary.power_port):.1%} of the port power")beyond 5 cm: 23.1% of the port power
Handing the neutrals on
Section titled “Handing the neutrals on”The neutrals through the port, placed in the tokamak’s frame (here the port at
m, the beam aimed at a tangency radius of 5.3 m, horizontal), as a dataset with
their weight in atoms per second, which freeofcharge’s NeutralBeam.from_dataset reads:
at_port = Markers.concatenate([duct.port, bent.exit]).drift_to(port)neutral = at_port.subset(at_port.state == at_port.states.index("0"))tangency, start = 5.3, 9.0along = np.sqrt(start**2 - tangency**2)origin = (tangency, -along, 0.0)direction = (0.0, 1.0, 0.0)markers = neutral.placed(origin, direction, x=port).to_dataset(mass)markers<xarray.Dataset> Size: 4MB
Dimensions: (component: 3, marker: 62950)
Coordinates:
* component (component) <U1 12B 'x' 'y' 'z'
Dimensions without coordinates: marker
Data variables:
position (component, marker) float64 2MB 5.317 5.246 ... 0.02505 0.01953
velocity (component, marker) float64 2MB -3.69e+04 ... -2.352e+05
power (marker) float64 504kB 133.3 133.3 133.3 ... 133.3 133.3 133.3
weight (marker) float64 504kB 8.322e+14 8.322e+14 ... 8.322e+14
state (marker) <U1 252kB '0' '0' '0' '0' '0' ... '0' '0' '0' '0' '0'
Attributes:
mass: 3.3443256848048796e-27
states: ['-', '0', '+']