Fast implementation of 2D DFT#
The two-dimensional direct fourier transform is used in GVEC, and we use dimension-by-dimension operators storing 1D matrices only for a fast transform, and making use of matrix-matrix multiplication.
Representation of variables in Fourier modes#
A variable in the poloidal and toroidal angle periodic directions \((\thet, \zeta)\) is represented as a truncated real-valued Fourier series of either only cosine, only sine, or both. The mode number in poloidal direction is \(0 \leq m \leq \mmax\) and in toroidal direction \(-\nmax \leq n \leq \nmax\). It is possible to have a periodicity of the toroidal direction, the ‘number of field periods’ \(\nfp\), such that \(\zeta \in [0, 2\pi / \nfp]\).
The full 2D Fourier representation is split into the following parts:
with the coefficients of cosine \(x^c_{mn}\) and sine \(x^s_{mn}\). One can choose to use the full series, with or without the zero mode, or only the cosine series with or without the zero mode, or only the sine series.
Depending on this choice, the 2D matrix of \((m, n)\) coefficients would not be filled. Therefore, only the non-zero coefficients are stored as a contiguous one-dimensional array, along with an array of same size containing the associated \(m\) and \(n \nfp\) mode numbers. Since only one single 1D array is used independent of the different choices, the start and end positions of the sine and cosine coefficients are also stored.
Transform to real space#
A sampling in real space is necessary, since we need to take integrals of nonlinear products of these variables (and of their derivatives in \(\thet\) and \(\zeta\)). For periodic functions, the trapezoidal rule on an equidistant set of points is known to be spectrally accurate. We need to choose the same point positions for evaluating cosine and sine series,
with the number of points \((\mnyq, \nnyq)\). The number of integration points is typically chosen to be a factor 4 of the maximum mode number, due to the nonlinearity of the integral. (To guarantee the orthogonality of the product of two Fourier basis functions with maximum mode number \(N\), the minimum number of integration points is \(2N+1\).)
The transform then reads
where the sum over mode indices is not specified yet. This operation can be made more efficient by splitting the sum into the two directions. First,
which can be grouped as
Since the result for all points \((i,j)\) is a matrix of size \([\mnyq \times \nnyq]\), one can write the operation in two consecutive matrix-matrix multiplications:
The Fourier coefficients \(x^c_{mn}\) and \(x^s_{mn}\) must be copied into a matrix of size \([\mtotal \times \ntotal]\), with \(\mtotal = f(\mmax+1)\), \(\ntotal = (2\nmax+1)\), where \(f=1\) if sine or cosine series only, and \(f=2\) if the full series. The few non-existing coefficients are set to zero.
The first matrix-matrix product has a left matrix of size \([2\mnyq \times \mtotal]\). This matrix is precomputed.
The result is a matrix of size \([2\mnyq \times \ntotal]\). It can be recast into a matrix of size \([\mnyq \times 2\ntotal]\), without any additional copy. (The result can be seen as a 3D array of size \([\mnyq \times 2 \times \ntotal]\), allowing both matrix shapes.)
The second matrix-matrix product has a right matrix of size \([2\ntotal \times \nnyq]\). This matrix is also precomputed.
The result is a 2D matrix of size \([\mnyq \times \nnyq]\).
Note that for the computation of a derivative in \(\thet\) or \(\zeta\) direction, the corresponding derivative matrices are precomputed and used instead:
The total number of multiplications for the two matrix-matrix products is \((2\mnyq\, \mtotal\, \ntotal) + (\mnyq\, 2\ntotal\, \nnyq) = 2\mnyq\, \ntotal(\mtotal + \nnyq)\).
Transform to Fourier space#
The integrand \(g(\thet, \zeta)\) is projected onto the Fourier coefficients via integration. Using the orthogonality of the Fourier basis, one can write
Note that the normalization factor is not included here.
The integral is approximated by the sum over the integration points with a constant integration weight (not included here). With the integrand evaluated at the points \(g_{ij} = g(\thet_i, \zeta_j)\), we get
or for the generalized full series
Using the abbreviations \(\mtotal = f(\mmax+1)\), \(\ntotal = (2\nmax+1)\), where \(f=1\) if sine or cosine series only, and \(f=2\) if the full series, the two matrix-matrix products are:
For the first matrix-matrix product, the left matrix holds \(g_{ij}\) and has size \([\mnyq \times \nnyq]\); the right matrix is of size \([\nnyq \times 2\ntotal]\). This matrix is precomputed.
The result is a matrix of size \([\mnyq \times 2\ntotal]\), which can be recast into a matrix of size \([2\mnyq \times \ntotal]\).
The second matrix-matrix product has a left matrix of size \([\mtotal \times 2\mnyq]\). This matrix is precomputed.
The result is the Fourier mode coefficients in a matrix of size \([\mtotal \times \ntotal]\), which must be copied to the 1D array data structure introduced in the first section.
Note that the precomputed matrices are the transposed versions of those used in the previous section. The projection can also be used with the \(\thet\), \(\zeta\) derivatives of the Fourier basis, using again precomputed matrices.
The total number of multiplications for the two matrix-matrix products is