I have a 3D grid in real space of grid spacing $L$ and say 21 grid points in each direction, containing e.g. a charge distribution. This is stored as a numpy array of shape (21, 21, 21)
. The grid points themselves are of course vectors and so are stored as a numpy array of shape (3, 21, 21, 21)
.
I also have a set of plane waves that form a basis for reciprocal space. These are of the form $$ \psi_{\alpha}(\vec r) = \exp\left(i\vec G_\alpha\cdot \vec r\right) $$ such that $|\vec G_\alpha|^2 \le \text{cutoff}$, so as to form a finite set. For reference, all the $\vec G_\alpha$ are vertically stacked to form an $\alpha\times3$ matrix, which I'll call $\mathbb G$. Now we can expand our charge density $n(\vec r)$ in terms of the basis: $$ n(\vec r) = c_\alpha\psi_\alpha(\vec r) $$
This means that the computational representation for the charge density is an ordered list of the $\alpha$ numbers $c_\alpha$, i.e. an $\alpha$-length vector $\vec c$. My current objective is to reconstruct the real-space charge density from $\vec c$.
The way I currently do this is through the following code:
I = np.exp(1j * G @ real_grid)
density_reconstruction = c @ I
In other words, I precompute the tensor $\mathbb I$ of shape (α, 21, 21, 21)
which is essentially the value of each of the basis functions on each of the grid points. I then matrix multiply/contract along the $\alpha$-dimension with $\vec c$ to get a (21, 21, 21)
array - this is the reconstructed charge density. Although this method works, the matrix multiplication in the second step is very expensive.
Question: I have heard that, instead of using a costly matrix multiplication, it is possible to do perform this step using a Fourier transform (in particular, FFT), owing to the form of the plane wave. How can I implement this, e.g. using the scipy.fft
package? How does it work for the case where the number of basis functions $\ne$ the number of real-space grid points?