Generating Fluence Maps

In order to compute dose, an idealised fluence distribution needs to be created.

This is usually done by generating fluence maps on a set of bixels from beam-limiting devices, such as the jaws or MLC.

Beam-Limiting Devices

Beam-limiting devices contain information on the position and shape of the aperture. These typically relate to physical devices on the treatment machine, such as a Multi-Leaf Collimator. Each have specialised methods by which they interact with bixels and assign an amount of fluence going through the bixel.

Jaws

Jaws are rectangular field shapes, which are simply constructed by specifying the position of the left, right, top and bottom jaws, or a fieldsize:

julia> jaws = Jaws(-10., 10., -15., 15.)Jaws{Float64}([-10.0, 10.0], [-15.0, 15.0])
julia> jaws = Jaws(100.)Jaws{Float64}([-50.0, 50.0], [-50.0, 50.0])

They have methods for accessing the positions, and the area,

julia> getx(jaws)2-element StaticArraysCore.SVector{2, Float64} with indices SOneTo(2):
 -50.0
  50.0
julia> gety(jaws)2-element StaticArraysCore.SVector{2, Float64} with indices SOneTo(2): -50.0 50.0
julia> getarea(jaws)10000.0

Multi-Leaf Collimator

Multi-Leac Collimator (MLC) contain the position of the leaves (in the x direction), and the position of the leaf edges (the y direction).

They are constructed by specifying leaf positions in a 2xn matrix and leaf edges in an n+1 length vector,

julia> mlcx = [-10. -12. -23.
                10.   4. -10.];
julia> mlcy = -5.:5.:10;
julia> mlc = MultiLeafCollimator(mlcx, mlcy)2x3 MultiLeafCollimator 1: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 3: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

They can also be constructed by just specifying the edges, which sets the leaf positions to zero,

MultiLeafCollimator(mlcy)

Indexing and iterating through the MLC will produce a subset of the MLC, either returning Jaws or another MultiLeafCollimator,

julia> mlc[1]Jaws{Float64}([-10.0, 10.0], [-5.0, 0.0])
julia> mlc[2:3]2x2 MultiLeafCollimator 1: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
julia> @view mlc[1:2]2x2 MultiLeafCollimator 1: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

Leaf positions can be accessed using the getpositions,

julia> getpositions(mlc)2×3 Matrix{Float64}:
 -10.0  -12.0  -23.0
  10.0    4.0  -10.0
julia> getpositions(mlc, 1)2-element Vector{Float64}: -10.0 10.0

Edge positions can be accessed using getedges,

julia> getedges(mlc)-5.0:5.0:10.0
julia> getedges(mlc, 1)-5.0:5.0:0.0
julia> getedges(mlc, 1:2)-5.0:5.0:5.0

Leaf positions can be set with setpositions! and closed with closeleaves!

julia> closeleaves!(mlc)2x3 MultiLeafCollimator
  1: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  2: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
  3: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
julia> mlc[1] = -5., 25(-5.0, 25)
julia> mlc2x3 MultiLeafCollimator 1: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 3: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░
julia> mlcx = [-10. -12. -23. 10. 4. -10.];
julia> setpositions!(mlc, mlcx)2x3 MultiLeafCollimator 1: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 2: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 3: ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░

Bixels

Bixels are 2D elements denoting a rectangular section of the isoplane. Their position and width is in the IEC BLD coordinate system, scaled to the isoplane.

Bixels are constructed by specifing their position and width,

julia> bixel = Bixel(0., 0., 1., 1.)Bixel{Float64}([0.0, 0.0], [1.0, 1.0])

A collection of bixels is used to build a fluence map. Most functions will take a list of bixels (AbstractVector{Bixel}). These can either be constructed using the Bixel constructor, or using one of the following bixel methods.

Bixel Grids

Bixel grids store bixels in a rectilinear grid, and behave as Matrix{Bixel}. They can be constructed by supplying a vector or range of bixel edge positions,

julia> bixels = BixelGrid(-10.:5.:10, -10.:5.:10.)4×4 BixelGrid{StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}, StepRangeLen{Float64, Base.TwicePrecision{Float64}, Base.TwicePrecision{Float64}, Int64}}:
 Bixel{Float64}([-7.5, -7.5], [5.0, 5.0])  …  Bixel{Float64}([-7.5, 7.5], [5.0, 5.0])
 Bixel{Float64}([-2.5, -7.5], [5.0, 5.0])     Bixel{Float64}([-2.5, 7.5], [5.0, 5.0])
 Bixel{Float64}([2.5, -7.5], [5.0, 5.0])      Bixel{Float64}([2.5, 7.5], [5.0, 5.0])
 Bixel{Float64}([7.5, -7.5], [5.0, 5.0])      Bixel{Float64}([7.5, 7.5], [5.0, 5.0])

Bixel grids can also be constructed using the information from beam-limiting devices,

julia> jaws = Jaws([-5., 5.], [-10., 10.])Jaws{Float64}([-5.0, 5.0], [-10.0, 10.0])
julia> bixels = BixelGrid(jaws, 5., 5.)2×4 BixelGrid{Vector{Float64}, Vector{Float64}}: Bixel{Float64}([-2.5, -7.5], [5.0, 5.0]) … Bixel{Float64}([-2.5, 7.5], [5.0, 5.0]) Bixel{Float64}([2.5, -7.5], [5.0, 5.0]) Bixel{Float64}([2.5, 7.5], [5.0, 5.0])

They behave like matrices: they can be indexed (both linear and Cartesian indexing), and iterate through each bixel,

julia> bixels[3]Bixel{Float64}([-2.5, -2.5], [5.0, 5.0])
julia> bixels[1, 2]Bixel{Float64}([-2.5, -2.5], [5.0, 5.0])

They also have specialised methods for accessing grid axes,

julia> getaxes(bixels)([-5.0, 0.0, 5.0], [-10.0, -5.0, 0.0, 5.0, 10.0])
julia> getaxes(bixels, 1)3-element Vector{Float64}: -5.0 0.0 5.0

Bixels from Beam-Limiting Devices

In addition to the creation of bixel grids from beam-limiting devices, bixels_from_bld creates a vector of bixels that span the open aperture of the beam-limiting device, e.g.

julia> bixels = bixels_from_bld(mlc, jaws)4-element Vector{Bixel{Float64}}:
 Bixel{Float64}([-2.5, -2.5], [5.0, 5.0])
 Bixel{Float64}([2.5, -2.5], [5.0, 5.0])
 Bixel{Float64}([-2.5, 2.5], [5.0, 5.0])
 Bixel{Float64}([2.0, 2.5], [4.0, 5.0])

Fluence

Fluence is computed using the fluence method with bixel and beam-limiting devices as inputs,

julia> fluence(bixel, jaws)1.0

These are available for all beam-limiting devices.

Fluence maps similarly are generated fluence and fluence! methods,

julia> Ψ = fluence(bixels, mlc)4-element Vector{Float64}:
 1.0
 1.0
 1.0
 1.0
julia> fluence!(Ψ, bixels, mlc)4-element Vector{Float64}: 1.0 1.0 1.0 1.0

They can also be computed in-place with fluence!, avoiding memory allocation.