# kWaveArray

Class definition for k-Wave array.

## Syntax

    karray = kWaveArray
    karray = kWaveArray(...)

## Description

`kWaveArray` is a class definition that allows the definition of multi-element transducer arrays in 1D, 2D, and 3D for use with the k-Wave simulation functions. The individual elements are defined using physical (rather than grid) parameters in Cartesian coordinates, and their representation on the grid is automatically calculated using off-grid sources as described in [1].

There are two key advantages of using the `kWaveArray` class to define arrays. (1) The geometry is stair-case free. (2) The transmit signals (for sources) and receive signals (for sensors) are defined per physical transducer element, rather than per grid point.

No information about the actual simulation grid is stored within objects of the `kWaveArray` class. The idea is that an array can be defined using physical characteristics, and then re-used for many different simulations. Note, some methods do use the grid information for calculations, and therefore take an object of the `kWaveGrid` class as an input. The implementation assumes that the grid sampling is uniform, i.e., dx = dy = dz.

To calculate the off-grid source, first a uniform sampling of Cartesian points is generated covering the element shape (e.g., bowl, disc, etc). For each sample point, a band-limited interpolant is computed corresponding to a point source at that location. These point source responses are summed and scaled to give the source mask for that element. This means the source mask will be non-local, i.e., will extend spatially beyond where the physical source lies on the grid. Note, any Cartesian points that are outside the grid are automatically removed.

In the current version, objects of the `kWaveArray` class cannot be passed directly to the simulation functions in place of the source and sensor structures. Instead, methods of the `kWaveArray` class can be used to automatically create these inputs and process the outputs. See examples for further details.

[1] Wise, E. S., Cox, B. T., Jaros, J., & Treeby, B. E. (2019). Representing arbitrary acoustic source and sensor distributions in Fourier collocation methods. The Journal of the Acoustical Society of America, 146(1), 278-288.

## Optional Inputs

Optional 'string', value pairs that may be used to modify the default computational settings.

Input | Valid Settings | Default | Description
--- | --- | --- | ---
`'BLITolerance'` | (numeric scalar) | `0.05` | Scalar value controlling where the spatial extent of the BLI at each point is trunctated as a portion of the maximum value.
`'BLIType'` | `'sinc'` `'exact'` | `'sinc'` | String controlling the BLI expression that is used for each point source (see `offGridPoints`). `'BLITolerance'` is ignored if `'exact'` is specified.
`'SinglePrecision'` | (Boolean) | `false` | Boolean controlling whether the calculation of the off-grid mask and distributed source are performed in single precision to improve performance.
`'UpsamplingRate'` | (numeric scalar) | `10` | Oversampling used to distribute the off-grid points compared to the equivalent number of on-grid points.

## Outputs

`karray` | `kWaveArray` object which can be used to define source and sensor inputs for the kWave simulation functions.

## Properties

Properties of the kWaveArray class which can be queried, but not directly modified.

Property | Description
--- | ---
`karray.array_transformation` | Position of the array defined by an affine transform. Used to move the array without needing to redefine the individual positions of the elements. Set by using the `setArrayPosition` and `setAffineTransform` methods.
`karray.dim` | Number of spatial dimensions.
`karray.elements` | Structure containing the properties for each element in the array. New elements are added using the `addXXXElement` methods.
`karray.number_elements` | Number of transducer elements in the array.

## Methods

Methods of the kWaveArray class. After creating a kWaveArray object, methods are called using the syntax:

    karray = kWaveArray;
    karray.methodName(inputs);
    output = karray.methodName(inputs);

The first syntax is used for methods with no output, and the second for methods with an output.

### addAnnularArray

Add annular array (3D simulations). The array elements are indexed from the inner-most element outwards. If `radius ~= inf`, then the annular array is positioned over the surface of a bowl.

    addAnnularArray(position, radius, diameters, focus_pos)

`position` | Centre of rear surface `[bx, by, bz]` [m].
`radius` | Radius of curvature of a bowl on which the annular array lies [m].
`diameters` | 2 x `num_elements` array containing pairs of inner and outer aperture diameter (diameter of opening) [m].
`focus_pos` | Any point on the beam axis `[fx, fy, fz]` [m].

### addAnnularElement

Add annular element (3D simulations). Setting `diameter = [0, diameter]` will generate a bowl the same as `addBowlElement`.

    addAnnularElement(position, radius, diameters, focus_pos)

`position` | Centre of rear surface `[bx, by, bz]` [m].
`radius` | Radius of curvature of a bowl on which the annular element lies [m].
`diameters` | 2 element array containing inner and outer aperture diameter (diameter of opening) [m].
`focus_pos` | Any point on the beam axis `[fx, fy, fz]` [m].

### addArcElement

Add arc-shaped element to the array (2D simulations).

    addArcElement(position, radius, diameters, focus_pos)

`position` | Centre of rear surface (arc midpoint) `[bx, by]` [m].
`radius` | Radius of curvature of arc [m].
`diameters` | Diameter of arc opening [m].
`focus_pos` | Any point on the beam axis `[fx, fy]` [m].

### addBowlElement

Add bowl-shaped element to the array (3D simulations).

    addBowlElement(position, radius, diameter, focus_pos)

`position` | Centre of rear surface `[bx, by, bz]` [m].
`radius` | Radius of curvature of bowl [m].
`diameters` | Diameter of bowl opening [m].
`focus_pos` | Any point on the beam axis `[fx, fy, fz]` [m].

### addCustomElement

Add custom volume, area, or line element to the array defined as a series of integration points (see [1]; 1D/2D/3D simulations). Note, for custom elements, the integration point density should be sufficient relative the intended grid spacing (see [1]).

    addCustomElement(integration_points, measure, element_dim, label)

`integration_points` | 1 x num_points (1D), 2 x num_points (2D) or 3 x num_points (3D) array of Cartesian coordinates [m].
`measure` | Length [m] of line element, area [m^2] of area element, or volume [m^3] of volume element corresponding to `element_dim`.
`element_dim` | Integer specifying whether the custom element is a line element (1), area element (2), or volume element (3). Note, `element_dim` does not need to match the dimension of the simulation, i.e., it is possible to use a line element in a 3D array.
`label` | String identifier.

### addDiscElement

Add disc-shaped element to the array (2D/3D simulations).

    addDiscElement(position, diameter, focus_pos)

`position` | Centre of disc surface `[bx, by]` or `[bx, by, bz]` [m].
`diameter` | Diameter of the disc [m].
`focus_pos` | Any point on beam axis `[fx, fy, fz]` (not used for 2D simulations) [m].

### addRectElement

Add rectangular element to the array (2D/3D simulations). The rectangle is created in the x-y plane and then rotated.

    addRectElement(position, Lx, Ly, theta)

`position` | Centre of rect `[bx, by]` or `[bx, by, bz]` [m].
`Lx` | Height of rect (along x-axis before rotation) [m].
`Ly` | Width of rect (along y-axis before rotation) [m].
`theta` | Either a scalar (2D) or three element vector (3D) `[tx, ty, tz]` specifying the orientation of the rectangle [deg]. In 3D, the rotations are specified about x-y'-z'' (intrinsic rotations) or z-y-x (extrinsic rotations). All rotations are counter-clockwise. Can be set to [] if no rotation.

### addLineElement

Add line element to the array (1D/2D/3D simulations).

    addLineElement(start_point, end_point)

`start_point` | Start coordinate for the line given as a one (1D), two (2D), or three (3D) element vector [m].
`end_point` | End coordinate for the line given as a one (1D), two (2D), or three (3D) element vector [m].

### combineSensorData

When using `array.getArrayBinaryMask` to define a sensor mask for the k-Wave simulation functions, the returned sensor data is defined for every grid point that forms part of the array. This method combines the sensor data with the appropriate weights and returns a single time series (or value) for each physical array element (rather than each grid point). The data is returned in the same order as the transducer elements were added to the array.

    combined_sensor_data = combineSensorData(kgrid, sensor_data)

`combined_sensor_data` | Combined sensor data (one time series per transducer element).
`kgrid` | Grid object returned by `kWaveGrid`.
`sensor_data` | Sensor data returned by simulation functions for a sensor mask given by `getArrayBinaryMask`.

### getArrayBinaryMask

Returns binary mask containing all grid points that form part of the array. Note, as the array elements use off-grid sources (see [1]), this will be non-local to some extent (depending on the values set for `'BLITolerance'` and `'BLIType'`).

    mask = getArrayBinaryMask(kgrid)

`mask` | Binary mask (matrix of 1s and 0s) specifying the grid points that form part of the array.
`kgrid` | Grid object returned by `kWaveGrid`.

### getArrayGridWeights

Returns matrix containing sum of off-grid source weights for each grid point in the domain (defined by kgrid).

    grid_weights = getArrayGridWeights(kgrid)

`grid_weights` | Matrix of grid weights.
`kgrid` | Grid object returned by `kWaveGrid`.

### getDistributedSourceSignal

When defining a source input, a binary source mask (e.g, `source.p_mask`) should be defined using `getArrayBinaryMask`, and the time varying source (e.g., `source.p`) should be defined using this method. This automatically calculates the appropriate weighted source signal for each grid point that forms part of the off-grid source. The source data is assigned in the same order as the transducer elements were added to the array.

    distributed_source = getDistributedSourceSignal(kgrid, source_signal)

`distributed_source` | Source signal used to define time-varying source inputs for the k-Wave simulations functions, e.g, `source.p`.
`kgrid` | Grid object returned by `kWaveGrid`.
`source_signal` | Source signal for each transducer element defined as an array `[number_elements, Nt]`.

### getElementBinaryMask

Returns binary mask containing all grid points that form part of the specified element. Note, as the array elements use off-grid sources (see [1]), this will be non-local (depending on the values set for `'BLITolerance'` and `'BLIType'`).

    mask = getElementBinaryMask(kgrid, element_num)

`mask` | Binary mask (matrix of 1s and 0s) specifying the grid points that form part of the element.
`kgrid` | Grid object returned by `kWaveGrid`.
`element_num` | Element number to return binary mask for.

### getElementGridWeights

Returns matrix containing off-grid source weights for the transducer element defined as element_num (see [1]).

    grid_weights = getElementGridWeights(kgrid, element_num)

`grid_weights` | Matrix of grid weights.
`kgrid` | Grid object returned by `kWaveGrid`.
`element_num` | Element number to return grid weights for.

### getElementPositions

Returns a `[dim, num_elements]` array containing the positions of each array element after applying `array_transformation` (if defined).

    element_pos = getElementPositions()

`element_pos` | Matrix of element positions [m].

### plotArray

Plot the array elements. If `new_figure` is true (the default), a new figure window is created, otherwise the elements are added to the currently active figure window.

    plotArray(new_figure)

`new_figure` | Boolean controlling whether a new figure window is created.

### removeElement

Remove specified element from the array.

    removeElement(element_num)

`element_num` | Element number to remove from the array.

### setAffineTransform

Sets value for `array_transformation` defined as an affine transformation matrix.

    setAffineTransform(affine_transform)

`affine_transform` | Affine transform given as a [3, 3] matrix in 2D or [4, 4] matrix in 3D.

### setArrayPosition

Sets the property `array_transformation` (an affine transform) based on the values for translation and rotation. The translations are given as `[dx, dy]` in 2D and `[dx, dy, dz]` in 3D. The rotations angle/s are given as `[th]` in 2D (counter-clockwise) and `[x_th, y_th, z_th]` in 3D (rotation about x then y' then z'').

    setArrayPosition(translation, rotation)

`translation` | Array translation [m].
`rotation` | Array rotation [degrees].

### setOptionalInputs

Method to define the optional inputs (see Optional Inputs above). Can be called when the kWaveArray object is defined, or later.

    setOptionalInputs(...)

## See Also

`offGridPoints`, `getDeltaBLI`