Symmetries in Fourier space
From version 0.8, AtomicSymmetries.jl provided the possibility to apply symmetries to vector and matrices directly in Fourier space. This is implemented now for force-constant dynamical matrices and vectors (displacements, forces, ...).
A vector is transformed from real to q-space with the following convention:
\[\tilde v_k(\vec q) = \frac{1}{\sqrt{N_q}} \sum_{R} e^{-i 2\pi \vec R\cdot \vec q} v_k(\vec R)\]
\[v_k(\vec R) = \frac{1}{\sqrt{N_q}} \sum_{R} e^{i 2\pi \vec R\cdot \vec q} \tilde v_k(\vec q)\]
Note the sign of the Fourier and the normalization prefactor. This convention allows for correctly transforming the matrices, however, it introduces a size inconsistency on the vectors. If we have a periodic vector in the cell, its $q$ fourier transformed counterpart will be $\sqrt {N_q}$ times the value in the primitive cell. So be carefull when extracting $\Gamma$ point data from periodic vectors.
With this convention, we recover the standard rule for the matrices.
\[\tilde \Phi_{ab}(\vec q) = \sum_{\vec R} e^{2\pi i \vec q\cdot \vec R}\Phi_{a;b + \vec R}\]
\[\Phi_{ab} = \frac{1}{N_q} \sum_{\vec q} \tilde\Phi_{ab}(\vec q) e^{2i\pi \vec q\cdot[\vec R(a) - \vec R(b)]}\]
Note that these transformation of matrices and vector are consistent so that matrices and vector written as outer product can be consistently transformed
\[\Phi(\vec R) = \sum_i\sum_{\vec R} \vec v_i(\vec R_1) \otimes \vec v_i(\vec R_1 + \vec R)\]
\[\tilde \Phi(\vec q) = \sum_i \vec {\tilde v}_i(\vec q) \otimes \vec {\tilde v_i}(-\vec q)\]
Notably, this convention introduces two main properties that must be handled with care. The $\Gamma$ value of the fourier transform is not the average over the supercell of the same quantity. If you want to obtain the average, you must divide by $\sqrt {N_q}$ (the number of q-points). If the R_lat is not centered around zero, and the coordinates passed as v_sc are absolute values of positions, then the $\Gamma$ value of the fourier transform will be shifted by a total translation which is the average of the translations of the supercell lattice vectors. This can be avoided by either removing the corner of the supercell from the positions before performing the fourier transform, by centering Rlat around 0, or by removing this translational average a posteriori using the method `shiftposition_origin!`.
Fourier transform
The API to perform the fourier transform occur mainly with vector_r2q!, vector_q2r! which, respectively, trasform a vector from real to q space and vice-versa. Transformation of matrices occur with matrix_r2q!, matrix_q2r!. All these operations are inplace. The matrices are assumed in crystal coordinates, but in this case it should not matter.
To shift the origin for the fourier transformed absolute positions, use the method shift_position_origin! as
The detailed API calls are
AtomicSymmetries.vector_r2q! — Function
vector_r2q!(
v_q :: AbstractArray{Complex{T}, 3},
v_sc :: AbstractMatrix{T},
q_tot :: Matrix{T})
vector_r2q!(v_q :: AbstractArray{Complex{T}, 2},
v_sc :: AbstractVector{T},
q :: Matrix{T},
itau :: Vector{I},
R_lat :: Matrix{T}
) where {T <: AbstractFloat, I <: Integer}Fourier transform a vector from real space and q space.
$\displaystyle v_k(\vec q) = \frac{1}{\sqrt{N_q}} \sum_{R} e^{-i 2\pi \vec R\cdot \vec q} v_k(\vec R)$
It works both on a single vector and on a series of vector. NOTE: In the latter case, the number of configurations must be in the first column. This is not standard, but implemented in this way for performance reasons as it is the most convenient memory rapresentation for vectorizing the average calculation.
Notably, this convention introduces two main properties:
The $\Gamma$ value of the fourier transform is not the average over the supercell of the same quantity. If you want to obtain the average, you must divide by √nq (the number of q-points).
If the R_lat is not centered around zero, and the coordinates passed as v_sc are absolute values of positions, then the $\Gamma$ value of the fourier transform will be shifted by a total translation which is the average of the translations of the supercell lattice vectors.
To avoid this behaviour (which is wrong), you can use the option absolute_positions, which automatically rescales the v_sc to be coordinates relative to the respective cell origin identified by R_lat. Indeed, in this case, R_lat and v_sc must be of the same units, and coordinate types (you cannot mix crystalline and cartesian).
Parameters
v_q: (nconfigs, 3nat, nq) The target vector in Fourier space. Optionally, nconfigs could be omitted if transforming only 1 vectorv_sc: (nconfigs, 3*natsc) The original vector in real space. Optionally, n_configs could be omitted if transforming only 1 vectorq_tot: (3, nq) The list of q vectorsitau: (nat_sc) The correspondance for each atom in the supercell with the atom in the primitive cell.R_lat: (3, nat_sc) The origin coordinates of the supercell in which the atom isabsolute_positions: Bool If true [default false], removes from vsc the value of Rlat.
AtomicSymmetries.vector_q2r! — Function
vector_q2r!(
v_sc :: AbstractMatrix{T},
v_q :: AbstractArray{Complex{T}, 3},
q_tot :: Matrix{T},
itau :: Vector{I},
R_lat :: Matrix{T};
absolute_positions :: Bool = false
) where {T <: AbstractFloat, I <: Integer}
function vector_q2r!(
v_sc :: AbstractVector{T},
v_q :: AbstractMatrix{Complex{T}},
q :: Matrix{T},
itau :: Vector{I},
R_lat :: Matrix{T};
absolute_positions :: Bool = false
) where {T <: AbstractFloat, I <: Integer}Fourier transform a vector from q space to real space.
$\displaystyle v_k(\vec R) = \frac{1}{\sqrt{N_q}} \sum_{R} e^{+i 2\pi \vec R\cdot \vec q} v_k(\vec q)$
It can be applied both to a single vector and in an ensemble. NOTE: In the latter case, the configurations must be stored as the first index. This choice is made for performance reason in computing averages (exploiting vectorization).
Parameters
v_sc: (nconfigs, 3*natsc) The target vector in real space. Optionally, n_configs can be omittedv_q: (nconfigs, nq, 3*nat) The original vector in Fourier space. Optionally, nconfigs can be omittedq_tot: (3, nq) The list of q vectorsitau : (nat_sc)The correspondance for each atom in the supercell with the atom in the primitive cell.R_lat : (3, nat_sc)The origin coordinates of the supercell in which the atom isabsolute_positions: Bool If true, add the absolute position of the cell to the transformed v_sc
AtomicSymmetries.matrix_r2q! — Function
matrix_r2q!(
matrix_q :: Array{Complex{T}, 3},
matrix_r :: AbstractMatrix{T},
q :: Matrix{T},
itau :: Vector{I},
R_lat :: Matrix{T})Fourier transform a matrix from real to q space
\[M_{ab}(\vec q) = \sum_{\vec R} e^{2\pi i \vec q\cdot \vec R}\Phi_{a;b + \vec R}\]
Where $\Phi_{ab}$ is the real space matrix, the $b+\vec R$ indicates the corresponding atom in the supercell displaced by $\vec R$.
Parameters
matrix_q: (3nat, 3nat, nq) The target matrix in Fourier space.matrix_r: (3nat_sc, 3nat) The original matrix in real space (supercell)q_tot: (3, nq) The list of q vectorsitau: (nat_sc) The correspondance for each atom in the supercell with the atom in the primitive cell.R_lat: (3, nat_sc) The origin coordinates of the supercell in which the corresponding atom is
AtomicSymmetries.matrix_q2r! — Function
matrix_q2r!(
matrix_r :: AbstractMatrix{T},
matrix_q :: Array{Complex{T}, 3},
q :: Matrix{T},
itau :: Vector{Int},
R_lat :: Matrix{T})Fourier transform a matrix from q space into r space
$\displaystyle \Phi_{ab} = \frac{1}{N_q} \sum_{\vec q} M_{ab}(\vec q) e^{2i\pi \vec q\cdot[\vec R(a) - \vec R(b)]}$
Where $\Phi_{ab}$ is the real space matrix, $M_{ab}(\vec q)$ is the q space matrix.
Parameters
- matrixr : (3*natsc, 3*nat) The target matrix in real space (supercell). If the second dimension is 3nat_sc, we also apply the translations
- matrix_q : (3nat, 3nat, nq) The original matrix in Fourier space.
- q_tot : (3, nq) The list of q vectors
- itau : (nat_sc) The correspondance for each atom in the supercell with the atom in the primitive cell.
- Rlat : (3, natsc) The origin coordinates of the supercell in which the corresponding atom is
- translations : Vector{Vector{Int}} The itau correspondance for each translational vector. Its size must be equal to the number of q point and contain all possible translations. This can be obtained from the
get_translationssubroutine.
AtomicSymmetries.shift_position_origin! — Function
shift_position_origin!(r_vector::AbstractVector{Complex{T}}, cell::AbstractMatrix{T}, R_lat::AbstractMatrix{T}; buffer) where T
shift_position_origin!(r_vectors::AbstractMatrix{Complex{T}}, cell::AbstractMatrix{T}, R_lat::AbstractMatrix{T}; buffer) where TShifts a set of position vectors (r_vector or r_vectors) such that the average position (center of mass) of the reference lattice (R_lat) is moved to the origin. The function operates in-place. This can be employed after performing a Fourier Transform of absolute positions, to correct for non centered R_lat.
This is typically used to remove the lattice translation from displacement vectors before calculating quantities that are invariant to rigid body translations.
The average position vector of the reference lattice $\mathbf{r}_{\text{avg}}$ is calculated in Cartesian coordinates, and then subtracted from the input vectors $\mathbf{r}$.
\[\mathbf{r}_{\text{avg}} = \frac{1}{N_{\text{atoms}}} \sum_{k=1}^{N_{\text{atoms}}} \mathbf{r}_{\text{lat}, k}\]
The position vectors are then shifted:
\[\mathbf{r}' = \mathbf{r} - \mathbf{r}_{\text{avg}}\]
Arguments
r_vector/r_vectors: The position vector(s) (of typeComplex{T}) to be shifted in-place.cell: The $N_{dims} \times N_{dims}$ matrix defining the unit cell (lattice vectors).R_lat: The reference lattice positions (fractional/lattice coordinates, $N_{dims} \times N_{atoms}$) whose average position determines the shift.buffer: An optional buffer for temporary memory allocation (Bumer.jl)
Details on r_vectors Matrix Method
The matrix method assumes that the input r_vectors has the dimensions $(N_{\text{configs}}, N_{\text{dims}} \times N_{\text{atoms,v}})$, where $N_{\text{configs}}$ is the number of configurations, and $N_{\text{atoms,v}}$ is the number of atoms in the vectors being shifted.
The shift is applied simultaneously to all configurations using broadcasting (@views ... .-=).
Example
An example of usage after the fourier transform
# Define positions_r as a n_configs, 3n_atoms_sc vector
# Perform the fourier transform in q space
# Here, we assume that R_lat and q_points are expressed in crystal coordinates.
# Otherwise, just pass the identity to the cell below.
vector_r2q!(positions_q, positions_r, q_points, itau, R_lat)
# Remove the translations
shift_position_origin!(positions_q, cell, R_lat)Symmetries in Q space
The application of symmetries in Fourier space must also account how points in q space are mapped by the symmetry operations.
For this, the important information about how q points are related by symmetries needs to be computed and stored. This identification is performed by the helper function get_irt_q!, which identifies, for a given symmetry operation, the i->j mapping between q points. Q points mapped into themselves by the same set of symmetry operations form the socalled small-group of $q$, while the set of $q$ points mapped by all the symmetries of a crystal is called the star of the $q$ point. Due to time-inversion symmetry, the dynamical matrix must also satisfy the condition
$D(q) = D^\dagger(-q + G)$
therefore it is necessary also to keep track, for each q point, which one is the corresponding $-q + G$ in the mesh. This mapping is computed by the helper function get_minus_q!. All these information needs to be stored when applying symmetries. Therefore we defined a new Symmetries struct that ihnerits from the GenericSymmetries called SymmetriesQSpace. Note that, to initialize the symmetries in q-space, we must use the symmetries object (Symmetries) evaluated in the primitive cell. The correct initialization of symmetries could be checked with the subroutine check_symmetries, which will spot if a different cell has been employed when initializing the symmetries.
Since the q points must be passed in crystal coordinates, it may be useful to get the reciprocal lattice, which can be done with $get_reciprocal_lattice!$ (see section on crystal coordinates for the API)
Application of symmetries
Applying a symmetry means transforming a vector or a matrix (already in q-space) into a new vector (matrix). If the vector (matrix) is invariant under that transformation, then that transformation belong to the symmetry group.
Notably, the symmetries in the supercell are always the symmetries in the primitive cell times all possible translations operated by the lattice vectors compatible with the chosen supercell. On the contrary, the symmetries in q space are always only equal to the symmetries in the primitive cell. The reason is that translations are automatically incorporated in the q space representation by the block theorem:
\[D(q, q') = D(q)\delta(q - q')\]
This means that applying each symmetry operation in $q$ space is equivalent to averaging the result of the same symmetry operation in the supercell averaging among all possible translations.
The application of a symmetry in q space can be performed by considering how the force-constant matrix transform in real space under a symmetry operation $S$.
\[S[\tilde\Phi_{ab}(\bm q)] = \sum_{\bm R} e^{2\pi i \bm q\cdot \bm R}S^\dagger\Phi_{S\bm a, S(\bm b + \bm R)}S\]
The transformation also changes which atoms are considered. However, we must be careful with the convention adopted for the Fourier transform. In fact, we have that $\bm a$ and $\bm b$ are the positions on the atom in the primitive cell considered. The vectors $S\bm a$ and $S(\bm b + \bm R)$ may not correspond to atoms in the primitive cell, but rather folded in the supercell. To solve this issue, we need to define, for each symmetry operation, which atom in the primitive cell is mapped into which other atom in the primitive cell. This is indicated with $s(a)$ and $s(b)$. Also, we need to consider what is the translation vector $\bm t_{s,a}$ that brings the vector $S\bm a$ inside the primitive cell. With this information, we can rewrite the transformation as
\[\bm t_{s,a} = S\bm a - \bm{s(a)}\]
\[S[\tilde\Phi_{ab}(\bm q)] = \sum_{\bm R} e^{2\pi i \bm q\cdot \bm R}S^\dagger\Phi_{s(a) + \bm t_{s,a}, s(b) + \bm t_{s, b} + S\bm R}S\]
Exploiting the translational invariance, we can remove the $\bm t_{s,a}$ vector from the first index of the supercell force constant matrix, and rewrite the expression as
\[S[\tilde\Phi_{ab}(\bm q)] = \sum_{\bm R} e^{2\pi i \bm q\cdot \bm R}S^\dagger\Phi_{s(a), s(b) + \bm t_{s, b} - \bm t_{s, a} + S\bm R}S\]
By defining $\bm R' = \bm t_{s, b} - \bm t_{s, a} + S\bm R$, we can rewrite the summation in $\bm R'$ as
\[S[\tilde\Phi_{ab}(\bm q)] = \sum_{\bm R'} e^{2\pi i \bm q\cdot S^{-1} (\bm R' + \bm t_{s,a} - \bm t_{s,b})}S^\dagger\Phi_{s(a), s(b) + \bm R'}S\]
Since we work in crystal coordinates and reciprocal vectors, $S^{-1}\neq S^\dagger$. Therefore, we have
\[S[\tilde\Phi_{ab}(\bm q)] = \sum_{\bm R'} e^{2\pi i [(\bm S^{-1})^\dagger\bm q]\cdot(\bm R' + \bm t_{s,a} - \bm t_{s,b})}S^\dagger\Phi_{s(a), s(b) + \bm R'}S\]
Which is equivalent to the Fourier transform of the dynamical matrix at the transformed q-point $(\bm S^{-1})^\dagger\bm q$, times a phase factor. This is how symmetries operates in q space:
\[\bm S_\text{recip} = \left(\bm S^{-1}\right)^\dagger\]
\[S[\tilde\Phi_{ab}(\bm q)] = S^\dagger \tilde\Phi_{s(a)s(b)}(S_\text{recip}\bm q) S e^{2\pi i (S_\text{recip}\bm q)\cdot ( \bm t_{s,a} - \bm t_{s,b})}\]
Note that the $S_\text{recip}q$ vector in the phase factor and in the dynamical matrix can be always folded back into the first Brilluin zone. In fact the dynamical matrix is periodic in the reciprocal vector, while the phase factor is multiplied by a direct lattice vector. Thus, by adding a reciprocal lattice vector $\bm G$ to $S_\text{recip}\bm q$, the phase factor is multiplied by $e^{2\pi i \bm G\cdot ( \bm t_{s,a} - \bm t_{s,b})}$, which is always equal to 1.
The application of symmetries is handled by the general function rotate_vector! and rotate_dynamical_matrix! or rotate_matrix! that works exactly like for real space symmetries, with the same general interface. However, we also provide specific q-space only functions. Note that, while the rotate_* functions works in cartesian space, the following one expects symmetries in real space.
This transformation for each q point is operated by the subroutine apply_symmetry_matrixq!. Both these function modify in-place the first argument, storing the result of the transformation there. Note that, since symmetries are stored in crystalline components, both the vector and the matrix must be in crystalline components. This makes it also important that the $\bm q$ points are provided in crystalline coordinates, to correctly compute the phase factor and the transformed $S\bm q$.
AtomicSymmetries.SymmetriesQSpace — Type
SymmetriesQSpace(symmetries :: Symmetries{T}, q_points :: AbstractMatrix{T}) :: SymmetriesQSpace{T} where T
struct SymmetriesQSpace{T} <: GenericSymmetries where T
symmetries :: Symmetries{T}
irt_q :: Vector{Vector{Int}}
minus_q_index :: Vector{Int}
endThis structure contains the information to perform the symmetrization of a dynamical matrix directly in q space.
Note that the q_points needs to be in crystal coordinates,
and the symmetries must be of the primitive cell.
Parameters
symmetries: The symmetries of the primitive cell (Symmetries{T})q_points: The q points where the symmetries must be applied (in crystal coordinates)irt_q: A vector (one for each symmetry) of the correspondances of q points. For each symmetry can be obtained fromget_irt_q!Q points linked in this way are related by the symmetry operation in reciprocal space, and belong to the same star of q.minus_q_index: A vector containing for eachqthe corresponding $\vec {q'} = -\vec q + \vec G$, where $\vec G$ is a generic reciprocal lattice vector.
AtomicSymmetries.apply_symmetry_vectorq! — Function
apply_symmetry_vectorq!(target_vector :: AbstractMatrix{Complex{T}}, original_vector :: AbstractMatrix{Complex{T}}, symmetry_operation :: AbstractMatrix{U}, irt :: Vector{Int}, irt_q:: AbstractVector{Int})Apply the symmetry on the original vector in q space
Parameters
target_vector: The result (modified inplace) (3n x nq)original_vector: The original vector (3n x nq)symmetry_operation: The 3x3 symmetryirt: The atom-atom association by symmetryirt_q: The q-q association by symmetry
AtomicSymmetries.apply_symmetry_matrixq! — Function
apply_symmetry_matrixq!(target_matrix :: AbstractArray{Complex{T}, 3},
original_matrix :: AbstractArray{Complex{T}, 3},
sym :: AbstractMatrix{U},
irt :: AbstractVector{Int},
irt_q :: AbstractVector{Int},
unit_cell_translations :: AbstractMatrix{T},
; buffer = default_buffer()) where {T, U}Apply the symmetry on the matrix in q space This subroutine assumes the convention that the phase factor is for each supercell, not atoms. In other words, all the atoms coordinates are computed from the same origin of the supercell they are associated with.
Parameters
target_vector: The result (modified inplace) (3n x nq)original_vector: The original vector (3n x nq)symmetry_operation: The 3x3 symmetryirt: The atom-atom association by symmetryirt_q: The q-q association by symmetryunit_cell_translations: The translation vectors to move the transformed atom in the primitive cellbuffer: The Bumper.jl buffer for caching memory allocations [Optional]
AtomicSymmetries.get_irt_q! — Function
get_irt_q!(irt_q :: AbstractVector{Int}, q_points :: AbstractVector{T}, sym_mat :: AbstractMatrix)Get the correspondance $q' = S_\text{recip} q$ on the provided q grid. Always assume everything is in crystal coordinates.
Note that in reciprocal space (crystal coordinates) the symmetry operation is the inverse transpose.
\[S_\text{recip} = (S_\text{direct})^{-T}\]
The provided sym_mat is assumed to be in direct space.
This is needed for the correct application of the symmetries
AtomicSymmetries.get_minus_q! — Function
get_minus_q!(minus_q_index :: AbstractVector{Int}, q_points :: AbstractMatrix{T}; buffer = default_buffer()) where T
get_minus_q!(minus_q_index :: AbstractVector{Int}, q_points :: AbstractMatrix{T}, reciprocal_lattice :: AbstractMatrix{T}; buffer = default_buffer()) where TIdentify for each q point what is the corresponding -q:
$\vec q \longrightarrow -\vec q + \vec G$
where $\vec G$ is a reciprocal vector. Since this is done in crystal coordinates$\vec G$ are all possible integers.
The reciprocal vectors is only needed if the q points are in cartesian coordinates.
Parameters
minus_q_index: The result vector (modified inplace)q_points: The q points (in crystal coordinates if noreciprocal_latticeis provided, cartesian otherwise)cell: The primitive cell (column-based). Only ifq_pointsare in cartesian coordinates. [Optional]reciprocal_lattice: The reciprocal lattice vectors (column-based). Only ifq_pointsare in cartesian coordinates. [Optional]buffer: The Bumper.jl buffer for caching memory allocations [Optional, keyword only]
AtomicSymmetries.check_symmetries — Function
check_symmetries(q_symmetries :: SymmetriesQSpace{T}, n_atoms :: Int) :: BoolCheck if the q_symmetries has been correctly initialized in the primitive cell.
Essentially, this subroutine checks the atomic correspondance by symmetry and spots if there are atoms outside the primitive cell (whose index is above n_atoms).
Parameters
q_symmetries: The symmetries in q spacen_atoms: The number of atoms in the primitive cell
Returns
true if no contraddiction have been detected, false otherwise.
Enforcing symmetries
One of the most useful operation to do is enforce a specific matrix or vector in q-space to satisfy a given symmetry group.
This can be implemented by applying the complete irreducible representation of the symmetry group. Symmetrization of an ent \Phi is obtained as
$\Phi = \frac{1}{N}\sum_{i=1}^N S_i(\Phi)$
where $S_i$ is the symmetry operation. The two functions performing the symmetrization are symmetrize_matrix_q! and symmetrize_vector_q!. Also in this case, the dynamical matrix must be provided in crystalline coordinates.
To symmetrize vector and matrices already provided in cartesian coordinates, we must use the appropriate subroutines symmetrize_vector_cartesian_q! and symmetrize_matrix_cartesian_q!. These two subroutines correctly convert the vector/matrix in crystal coordinates before applying the symmetries, and then convert the symmetrized result back in cartesian space. They are the most used subroutines to perform symmetrization in q-space, the equivalent of symmetrize_vector! and symmetrize_fc! for real space.
The Hermitianity is not automatically imposed by the symmetrization procedure. This allows to symmetrize matrices that are not necessarily hermitian, for example, the cross correlation matrices between different quantities. Hermitianity and time-reversal symmetry can be imposed with the subroutine impose_hermitianity_q!, which enforces the condition. The time-reversal symmetry corresponds to the condition that the original matrix in real space is real-valued.
Here the complete API
AtomicSymmetries.symmetrize_vector_q! — Function
symmetrize_vector_q!(target_gamma :: AbstractVector{T}, original_q :: AbstractArray{Complex{T}, 2}, symmetries :: Symmetries, irt_q :: Vector{Vector{Int}}; buffer = default_buffer() where TImpose the symmetrization of a vector in q space. Since the symmetrization also imposes translational symmetries, the result is always a vector only at gamma.
The symmetrized vector is supposed to be a displacement (so no translations are applied)
NOTE: The provided vector must be in crystal coordinates To symmetrize a vector incartesian coordinates, see the routine symmetrize_vector_cartesian_q!.
Parameters
target_gamma: Then_at * n_dimsoutput symmetrized vector at $\Gamma$original_q: The original vector in q-space of sizenat*n_dims, nqsymmetries: The symmetry groupirt_q: A vector (one for each symmetry) of the correspondances of q points. For each symmetry can be obtained fromget_irt_q!gamma_index: Specify which q vector is $\Gamma$. If not specified, it is assumed to be the first one
AtomicSymmetries.symmetrize_matrix_q! — Function
symmetrize_matrix_q!(target_q :: AbstractArray{Complex{T}, 3}, original_q :: AbstractArray{Complex{T}, 3}, symmetries :: Symmetries, irt_q :: Vector{Vector{Int}}; buffer = default_buffer() where T
symmetrize_matrix_q!(matrix_q :: AbstractArray{Complex{T}, 3}, q_symmetries :: SymmetriesQSpace; buffer = default_buffer()) where T
symmetrize_matrix_q!(target_q :: AbstractArray{T, 3}, original_q :: AbstractArray{Complex{T}, 3}, q_symmetries :: SymmetriesQSpace; buffer = default_buffer()) where TImpose the symmetrization of a dynamical matrix in q space. The matrix must be in crystal coordinates.
Parameters
target_q: The symmetrized matrix of sizen_modes, n_modes, nq(modified in-place).original_q: The original matrix in q-space of sizen_modes, n_modes, nq. It could be the same as target_qsymmetries: The symmetry groupirt_q: A vector (one for each symmetry) of the correspondances of q points. For each symmetry can be obtained fromget_irt_q!unit_cell_translations:: Vector{Matrix{T}} : The translations of the unit cell to bring back the atoms in the primitive cell after the symmetry operation. Each vector elements corresponds to one symmetry operation, then the matrix is a ndims x natoms translation. This is usually the same as the content ofsymmetries.unit_cell_translations.minus_q_index: A vector containing for eachqthe corresponding $\vec {q'} = -\vec q + \vec G$, where $\vec G$ is a generic reciprocal lattice vector.q_points: The vector containing the actual q points
AtomicSymmetries.symmetrize_vector_cartesian_q! — Function
symmetrize_vector_cartesian_q!(vector_q_cart:: AbstractArray{Complex{T}, 2}, cell :: Matrix{T}, symmetries :: SymmetriesQSpace; buffer = default_buffer()) where {T}Perform the symmetrization of a vector (overwriting it) in cartesian coordinates. This is the go-to subroutine for performing symmetrization of vectors in q space.
Parameters
vector_q_cart: in-place symmetrize vector (q-space, cartesian coordinates)cell: 3x3 matrix of the primitive cell (column-based)symmetries: Symmetries in Q spacebuffer: Optional, Bumper stack buffer (caching)
AtomicSymmetries.symmetrize_matrix_cartesian_q! — Function
symmetrize_matrix_cartesian_q!(matrix_q :: AbstractArray{Complex{T}, 3}, cell :: Matrix{T}, q_symmetries :: SymmetriesQSpace; buffer=default_buffer()) where TEnforce the symmetries on the provided matrix (q-space), modifying it in-place. The provided matrix must be in Cartesian Coordinates.
Parameters
matrix_q: The matrix to be symmetrized. Size (nmodes, nmodes, nq)cell: The 3x3 primitive cell (column ordering)q_symmetries: The symmetry group (q-space)buffer: Optional, stack for Bumper to cache allocations.
AtomicSymmetries.impose_hermitianity_q! — Function
impose_hermitianity_q!(matrix_q :: AbstractArray{Complex{T}, 3}, minus_q_index; buffer=default_buffer()) where TImpose the hermitianity and time-reversal symmetry on the dynamical matrix in q space.
Parameters
matrix_q: The dynamical matrix in q space (modified inplace)minus_q_index: A vector containing for eachqthe corresponding $\vec {q'} = -\vec q + \vec G$, where $\vec G$ is a generic reciprocal lattice vector.
Manipulating q points
The fourier transform depends on the knowledge of few vectors: q_points, itau, and R_lat (evenutally translations, for inverse fourier transform into a matrix).
All these properties can be evaluated from the core source. For example, to obtain the supercell to which the q points are commensurate, we can use the get_supercell method as
AtomicSymmetries.get_supercell! — Function
get_supercell(q_points::AbstractMatrix{T}, cell :: AbstractMatrix{T}) :: Vector{Int}
get_supercell(q_points::AbstractMatrix{T}) :: Vector{Int}
get_supercell!(supercell::AbstractVector{I}, q_points::AbstractMatrix{T}, cell :: AbstractMatrix{T}) where {T, I<:Integer}
get_supercell!(supercell::AbstractVector{I}, q_points::AbstractMatrix{T}) where {T, I<:Integer}Calculates the minimum supercell dimensions required to fold a set of commensurate wave vectors (q_points) back to the Gamma point (Γ) of the Brillouin zone.
The resulting supercell dimension $S_i$ for each spatial direction is determined by the reciprocal of the smallest non-zero q-point component in that direction. For commensurate grids, this is mathematically equivalent to:
\[S_i = \text{round} \left( \frac{1}{\min(|q_{i}|)} \right)\]
This function is primarily used when performing calculations in a real-space supercell that is commensurate with the input k-point (or q-point) grid.
Arguments
q_points: A 2D matrix where each column represents a q-point vector, and each row corresponds to a dimension (x, y, z).supercell: An pre-allocated integer vector to store the result (used by theget_supercell!in-place version).cell: The primitive cell. If not provided, the code assumes that q_points are in fractional coordinates.
Important Note on Coordinates
The q_points must be provided in crystal coordinates (fractional coordinates) if cell is not provided.
Example
# 3 dimensions, 2 q-points
q_points = [0.5 0.0;
0.0 0.5;
0.0 0.25]
# The supercell dimensions required are based on (1/0.5, 1/0.5, 1/0.25).
supercell = get_supercell(q_points)
# Result: [2, 2, 4]Analogously, we can get the translations R_lat as
AtomicSymmetries.get_R_lat! — Function
get_R_lat!(R_lat :: Matrix{T}, primitive_coords :: Matrix{T}, supercell_coords :: Matrix{T})Get the R_lat parameter to perform the fourier transform.
Parameters
R_latthe result lattice vectors, modified inplaceprimitive_coords: The coordinates in the primitive cellsupercell_coords: The coordinates in the supercellitau: The correspondence for each atom in the supercell with the respective atom in the primitive cell