# Lattices

## Constructors and types

`Electrum.RealBasis`

— Type`Electrum.LatticeBasis{S<:Electrum.BySpace,D,T} <: StaticMatrix{D,D,T}`

Represents the basis vectors of a `D`

-dimensional lattice in real or reciprocal space, depending on `S`

. The units of `LatticeBasis{Electrum.ByRealSpace}`

are bohr, and those of `LatticeBasis{Electrum.ByReciprocalSpace}`

are rad*bohr⁻¹, corresponding to the convention that the dot product of a real space basis vector with a reciprocal space basis vector is 2π.

**Type aliases**

For convenience, the type aliases `RealBasis`

and `ReciprocalBasis`

are defined below:

```
const RealBasis = LatticeBasis{ByRealSpace}
const ReciprocalBasis = LatticeBasis{ByReciprocalSpace}
const AbstractBasis = LatticeBasis{<:BySpace}
```

These type aliases are exported, and in most circumstances code should refer to these types for the sake of readability, not `Electrum.LatticeBasis`

, which is unexported.

**Mathematical operations**

`Electrum.LatticeBasis`

behaves as an ordinary matrix and should support all mathematical operations commonly used, including left division with vectors (`\`

), commonly used in the conversion between Cartesian and fractional (reduced) coordinates.

In most cases, matrix multiplications will convert the result to an ordinary `StaticArray`

or `Array`

. However, right multiplications of an `Electrum.LatticeBasis{S,D}`

with an `SMatrix{D,D,<:Integer}`

are treated as the application of a supercell building operation, and return a new `Electrum.LatticeBasis{S,D}`

instead.

**Conversion**

A `RealBasis`

may be converted to a `ReciprocalBasis`

, or vice versa, using either `convert(T::Electrum.LatticeBasis, b)`

or the constructor `(T::Type{<:Electrum.LatticeBasis})(b)`

. This automatically multiplies or divides by 2π as needed.

The inverse operation `inv`

also performs this conversion. This convention may change in a future update, as the current definition may break other assumptions about matrix inversion.

**Interoperability**

File import and export methods in Electrum and any other software which returns these types must perform unit conversion if the units used by the other software package are different.

**Internals**

In order to avoid the presence of an extraneous type parameter, the backing `vectors`

field of a `LatticeBasis`

is not an `SMatrix{D,D,T}`

(as this is not a concrete type), but an `SVector{D,SVector{D,T}}`

. However, the property `matrix`

is defined so that it returns an `SMatrix{D,D,T}`

. The `vectors`

property is private, and will not be revealed during REPL tab completion.

`Electrum.ReciprocalBasis`

— Type`Electrum.LatticeBasis{S<:Electrum.BySpace,D,T} <: StaticMatrix{D,D,T}`

Represents the basis vectors of a `D`

-dimensional lattice in real or reciprocal space, depending on `S`

. The units of `LatticeBasis{Electrum.ByRealSpace}`

are bohr, and those of `LatticeBasis{Electrum.ByReciprocalSpace}`

are rad*bohr⁻¹, corresponding to the convention that the dot product of a real space basis vector with a reciprocal space basis vector is 2π.

**Type aliases**

For convenience, the type aliases `RealBasis`

and `ReciprocalBasis`

are defined below:

```
const RealBasis = LatticeBasis{ByRealSpace}
const ReciprocalBasis = LatticeBasis{ByReciprocalSpace}
const AbstractBasis = LatticeBasis{<:BySpace}
```

These type aliases are exported, and in most circumstances code should refer to these types for the sake of readability, not `Electrum.LatticeBasis`

, which is unexported.

**Mathematical operations**

`Electrum.LatticeBasis`

behaves as an ordinary matrix and should support all mathematical operations commonly used, including left division with vectors (`\`

), commonly used in the conversion between Cartesian and fractional (reduced) coordinates.

In most cases, matrix multiplications will convert the result to an ordinary `StaticArray`

or `Array`

. However, right multiplications of an `Electrum.LatticeBasis{S,D}`

with an `SMatrix{D,D,<:Integer}`

are treated as the application of a supercell building operation, and return a new `Electrum.LatticeBasis{S,D}`

instead.

**Conversion**

A `RealBasis`

may be converted to a `ReciprocalBasis`

, or vice versa, using either `convert(T::Electrum.LatticeBasis, b)`

or the constructor `(T::Type{<:Electrum.LatticeBasis})(b)`

. This automatically multiplies or divides by 2π as needed.

The inverse operation `inv`

also performs this conversion. This convention may change in a future update, as the current definition may break other assumptions about matrix inversion.

**Interoperability**

File import and export methods in Electrum and any other software which returns these types must perform unit conversion if the units used by the other software package are different.

**Internals**

In order to avoid the presence of an extraneous type parameter, the backing `vectors`

field of a `LatticeBasis`

is not an `SMatrix{D,D,T}`

(as this is not a concrete type), but an `SVector{D,SVector{D,T}}`

. However, the property `matrix`

is defined so that it returns an `SMatrix{D,D,T}`

. The `vectors`

property is private, and will not be revealed during REPL tab completion.

`Electrum.AbstractBasis`

— Type`Electrum.LatticeBasis{S<:Electrum.BySpace,D,T} <: StaticMatrix{D,D,T}`

Represents the basis vectors of a `D`

-dimensional lattice in real or reciprocal space, depending on `S`

. The units of `LatticeBasis{Electrum.ByRealSpace}`

are bohr, and those of `LatticeBasis{Electrum.ByReciprocalSpace}`

are rad*bohr⁻¹, corresponding to the convention that the dot product of a real space basis vector with a reciprocal space basis vector is 2π.

**Type aliases**

For convenience, the type aliases `RealBasis`

and `ReciprocalBasis`

are defined below:

```
const RealBasis = LatticeBasis{ByRealSpace}
const ReciprocalBasis = LatticeBasis{ByReciprocalSpace}
const AbstractBasis = LatticeBasis{<:BySpace}
```

These type aliases are exported, and in most circumstances code should refer to these types for the sake of readability, not `Electrum.LatticeBasis`

, which is unexported.

**Mathematical operations**

`Electrum.LatticeBasis`

behaves as an ordinary matrix and should support all mathematical operations commonly used, including left division with vectors (`\`

), commonly used in the conversion between Cartesian and fractional (reduced) coordinates.

In most cases, matrix multiplications will convert the result to an ordinary `StaticArray`

or `Array`

. However, right multiplications of an `Electrum.LatticeBasis{S,D}`

with an `SMatrix{D,D,<:Integer}`

are treated as the application of a supercell building operation, and return a new `Electrum.LatticeBasis{S,D}`

instead.

**Conversion**

A `RealBasis`

may be converted to a `ReciprocalBasis`

, or vice versa, using either `convert(T::Electrum.LatticeBasis, b)`

or the constructor `(T::Type{<:Electrum.LatticeBasis})(b)`

. This automatically multiplies or divides by 2π as needed.

The inverse operation `inv`

also performs this conversion. This convention may change in a future update, as the current definition may break other assumptions about matrix inversion.

**Interoperability**

File import and export methods in Electrum and any other software which returns these types must perform unit conversion if the units used by the other software package are different.

**Internals**

In order to avoid the presence of an extraneous type parameter, the backing `vectors`

field of a `LatticeBasis`

is not an `SMatrix{D,D,T}`

(as this is not a concrete type), but an `SVector{D,SVector{D,T}}`

. However, the property `matrix`

is defined so that it returns an `SMatrix{D,D,T}`

. The `vectors`

property is private, and will not be revealed during REPL tab completion.

## Methods

`Electrum.eachvertex`

— Function`eachvertex([m::AbstractMatrix], r::AbstractArray...)`

Returns an iterator of `SVector{size(r),eltype(r)}`

objects representing each vertex whose reduced coordinates lie in ranges `r`

. If no matrix is given, the iterator generates all vectors whose coordinates are in corresponding arrays `r`

. A supplied matrix will be left-multiplied with these vectors to generate Cartesian representation of those vectors.

```
eachvertex(b::StaticMatrix)
eachvertex(b::AbstractMatrix)
```

Returns an iterator of `AbstractVector`

objects corresponding to each vertex of the parallelepiped spanned by the column vectors of `b`

.

In the case of `StaticMatrix`

subtypes, this function returns `SVector`

objects, allowing for efficient collection into an `Array{SVector{D},D}`

, preserving the arrangement of the vertices in space. However, for other `AbstractMatrix`

objects whose sizes are not known at compile time, the iterator can only be collected into a `Vector{<:Vector}`

to preserve type stability. We generally recommend working with static matrix types like the provided `RealBasis`

and `ReciprocalBasis`

.

`Electrum.basis`

— Function`basis(x)`

Returns the lattice basis associated with a data structure. By default, this returns `getproperty(x, :basis)`

. This may be implemented for custom data types by either adding a method to `basis()`

or by defining custom `getproperty()`

and `propertynames()`

methods.

Although basis(x) should always return an `Electrum.LatticeBasis`

, the exact return type may vary: not only can the numeric type vary, some data strucutres may store a real space lattice, and others may store a reciprocal space lattice, allowing for properties of the data contained to be inferred. For predictable results, use `convert(T, basis(x))`

where `T`

is the desired type.

`Base.inv`

— Method`inv(b::LatticeBasis{D}) -> SMatrix{D,D}`

Returns the matrix which, when left or right multiplied by `b`

, returns the identity matrix, up to rounding error. Because the result of this calculation is not the dual lattice associated with `b`

, the return type is simply an `SMatrix{D,D}`

.

For the dual space lattice basis vectors, use `dual(x)`

or `dualbasis(x)`

.

`Electrum.dual`

— Function```
dual(b::RealBasis) -> ReciprocalBasis
dual(b::ReciprocalBasis) -> RealBasis
```

Returns the basis of the dual lattice, which is the lattice in dual space whose product with the original lattice is equal to the identity matrix multiplied by 2π.

`Electrum.dualbasis`

— Function`dualbasis(x)`

Returns the dual basis associated with a data structure `x`

; equal to `dual(basis(x))`

.

This function should never be defined directly: instead, `basis(::T)`

should be implemented for a custom type `T`

.

`Electrum.lengths`

— Method`lengths(b::LatticeBasis{S,D}) -> SVector{D}`

Returns the lengths of the basis vectors. The units correspond to the type of the basis vectors: for `RealBasis`

the units are bohr, and for `ReciprocalBasis`

the units are rad*bohr⁻¹.

`Electrum.volume`

— Method`volume(b::LatticeBasis) -> Real`

Returns the volume of a unit cell defined by a matrix. This volume does not carry the sign (negative for cells that do not follow the right hand rule). The units correspond to the type of the basis vectors: for `RealBasis`

the units are bohr³, and for `ReciprocalBasis`

the units are rad³*bohr⁻³.

`Electrum.angles_cos`

— Function`angles_cos(b::LatticeBasis{S,D}) -> SVector{binomial(D,2)}`

Generates the cosines of the unit cell angles.

The angles are generated in the correct order [α, β, γ] for 3-dimensional cells. This is achieved by reversing the output of `Electrum.generate_pairs()`

. For crystals with more spatial dimensions, this may lead to unexpected results.

`Electrum.angles_rad`

— Function`angles_rad(b::LatticeBasis{S,D}) -> SVector{binomial(D,2)}`

Returns the angles (in radians) between each pair of basis vectors.

The angles are generated in the correct order [α, β, γ] for 3-dimensional cells. This is achieved by reversing the output of `Electrum.generate_pairs()`

. For crystals with more spatial dimensions, this may lead to unexpected results.

`Electrum.angles_deg`

— Function`angles_deg(b::LatticeBasis{S,D}) -> SVector{binomial(D,2)}`

Returns the angles (in degrees) between each pair of basis vectors.

The angles are generated in the correct order [α, β, γ] for 3-dimensional cells. This is achieved by reversing the output of `Electrum.generate_pairs()`

. For crystals with more spatial dimensions, this may lead to unexpected results.

`Electrum.gram`

— Function`gram(b::LatticeBasis{S,D}) -> SMatrix{D,D}`

Returns the Gram matrix associated with a set of basis vectors. The entries of this matrix are the dot products associated with all possible combinations of basis vectors.

`Electrum.triangularize`

— Function`triangularize(l::T) where T<:LatticeBasis -> T`

Converts a set of basis vectors to an upper triangular form using QR decomposition.

`triangularize(l::T, sc::AbstractMatrix{<:Integer}) where T<:LatticeBasis -> T`

Converts a set of basis vectors to an upper triangular form using QR decomposition, with an included conversion to a larger supercell. The resulting matrix that describes the basis vectors will have only positive values along the diagonal, and therefore, is always right-handed (regardless of the transformation matrix used).

LAMMPS expects that basis vectors are given in this format.

`Electrum.maxHKLindex`

— Function`Electrum.maxHKLindex(b::LatticeBasis, ecut::Real; prim=true, c = 2)`

Determines the maximum integer values of the reciprocal lattice vectors needed to store data out to a specific energy cutoff for a 3D lattice.

By default, the energy cutoff is assumed to be in units of Hartree, the reciprocal lattice vector lengths are assumed to be in rad*bohr⁻¹, and the value of c is that of the constant (2mₑ/ħ²). In Hartree atomic units, this value is 2 - but for VASP WAVECAR outputs, the value is given in eV⁻¹*angstrom⁻² - see

`Electrum.CVASP`

for more information.The functionality implemented here was taken from WaveTrans: https://www.andrew.cmu.edu/user/feenstra/wavetrans/