# Data grids

The output of many quantum chemistry packages are datasets defined on a spatial grid in real or reciprocal space. Theoretical tools may want to perform mathematical operations on this data, but operating on bare arrays containing the dta requires tracking properties associated with each array, such as the basis vectors of the lattice

`DataGrid`

, `RealDataGrid`

, and `ReciprocalDataGrid`

An `DataGrid{D,B<:Electrum.LatticeBasis,S<:AbstractVector{<:Real},T}`

contains data defined in a crystal lattice of `D`

with basis vectors of type `B`

, a shift parameter of type `S`

, and elements of type `T`

, either in real space (`RealDataGrid`

) or in reciprocal space (`ReciprocalDataGrid`

). These aliases are defined as follows:

```
const RealDataGrid{D,T} = DataGrid{D,RealBasis{D,Float64},SVector{D,Float64},T}
const ReciprocalDataGrid{D,T} = DataGrid{D,ReciprocalBasis{D,Float64},KPoint{D},T}
```

Look closely at the above definition: the shift data type for `RealDataGrid{D}`

is `SVector{D,Float64}`

, but the shift data type for `ReciprocalDataGrid{D}`

is `KPoint{D}`

. When the `DataGrid`

constructor without the shift type parameter is invoked, the basis type is used to infer the appropriate shift type so that a `RealDataGrid`

or `ReciprocalDataGrid`

is constructed.

`DataGrid`

uses zero-based, periodic indexing: the first index of an `AbstractDataGrid{D}`

is `zero(NTuple{D,Int})`

, and indices whose moduli with respect to size along that dimension are identical will reference the same element: for instance, for `g::AbstractDataGrid{3}`

with size `(10, 10, 10)`

, `g[69, 420, 1337] === g[9, 0, 7]`

. Encountering a `BoundsError`

is not possible when indexing an `DataGrid`

.

The basis of an `DataGrid`

can be recovered with `basis(::DataGrid)`

, which will be of the type specified by the type parameter.

## Broadcasting and mathematical operations

Broadcasting is defined for `DataGrid`

with a custom `Base.Broadcast.BroadcastStyle`

subtype:

`Electrum.DataGridStyle{D,B,S} <: Broadcast.AbstractArrayStyle{D}`

This allows `DataGrid`

instances to operated on with dot syntax. However, they must share lattice basis vectors and shift values. If they do not match, an `Electrum.LatticeMismatch`

exception will be thrown.

Although `Base.Broadcast.ArrayStyle`

is usually overridden by other subtypes of `Base.Broadcast.AbstractArrayStyle`

, it does not override `Electrum.DataGridStyle`

. Adding a `DataGrid`

to an `Array`

returns an `Array`

, and adding a `DataGrid`

to other `AbstractArray`

subtypes returns the `AbstractArray`

subtype defined by the `Broadcast.BroadcastStyle`

. In the case of a dimension mismatch, the broadcast style wll be `Broadcast.ArrayConflict`

- the operation will throw a `DimensionMismatch`

.

The `+`

and `-`

operators are defined for `DataGrid`

instances, and they are faster than the broadcasted `.+`

and `.-`

equivalents. As with the broadcasted versions, checks are implemented to ensure that the lattice basis vectors and shifts match.

Similarly, the `*`

, `/`

, and `\`

operators are defined for pairs of `DataGrid`

and `Number`

instances, and again, are faster than their broadcasted equivalents.

### Fourier transforms

The Fourier transform and its inverse are available through an overload of `FFTW.fft()`

and `FFTW.ifft()`

. The transforms are normalized with respect to the basis vectors of the space, so for `g::DataGrid`

, `ifft(fft(g)) ≈ g`

(to within floating point error).

`fft`

and `ifft`

defined for `DataGrid`

generally, but it is critical to note that in the vast majority of cases, you will want to call `fft`

on `RealDataGrid`

instances and `ifft`

on `ReciprocalDataGrid`

instances.