In this example we calculate the convolution functions f(G) for a simple wave function composed by sine functions.
- As expressed above, the goal of this example is to demonstrate that the code can correctly read in a grid and set of wavefunctions. Then fourier transform these wavefunctions, compute the real-space convolution functions f(r) and then fourier transform them back to g-space f(G) (see M. J. Rayson and P. R. Briddon, Physical Review B 77, 035119 (2008) for more information.) The convolution functions are defined as below.
- The file
./Export/grid.txt
contains a typical list of g-vectors borrowed from the NV-Diamond example. The form is rather simple as each line represents a new 3D g-vector of only integer value (units of reciprocal lattice).
less ./Export/grid.txt
- In this example, we will build two artificial wavefunctions out of simple combinations of sine functions as described below. In this way the exact solution at each step of the calculation is known or can be easily computed by other means.
- Since the starting point of the ZFS calculation is reciprocal space wavefunctions these wavefunctions need to begin in G-space.
- Run the python script
./Scripts/generate_wfc.py
to generate the above wfc's.
cd ./Export
../Scripts/generate_wfc.py
- Open the generated wavefunctions alongside
./grid.txt
to confirm that they are of the correct form. (Recall:gt
can be used to switch between tabs when usingvim -p
. Also,:qall!
can be used to close all tabs without saving.)
vim -p ./grid.txt ./wfc_1.txt
cd ..
- The file
./zfs.in
contains an input file for the zfs code. Here we set the optionverbosity = "high"
in order to view extra information from the calculation. Only use this option when the calculation is small as in this case or it will take forever!
vim ./zfs.in
- Run the zfs calculation.
../../bin/zfs.x -i ./zfs.in > ./zfs.out
- The output file
./zfs.out
is not of interest here, instead we are interested in the contents of the dump directory./zfs.dump/
.
cd ./zfs.dump/
-
The contents of this folder are as follows:
grid.txt
contains list of g-space vectors. Identical to../Export/grid.txt
wfc1.txt
andwfc2.txt
contains the complex g-space wavefunctions. Identical to../Export/wfc1.txt
and../Export/wfc2.txt
wfc1_r.txt
andwfc2_r.txt
contains the complex real-space wavefunctions.f1_r.txt
,f2_r.txt
andf3_r.txt
contain the real-space convolution functions defined above (step 0).f1_g.txt
,f2_g.txt
andf3_g.txt
contain the g-space convolution functions defined above (step 0).*-og.txt
files are counterparts of the aforementioned files which explicitly show how the wfc are defined over the grid (og). For example the head ofwfc1-og.txt
shows that wfc1 has a weight of i/2 at G = [0, 0, 1] which agrees with its form written in step 3.
0 0 0 : 0.000000E+000 , 0.000000E+000 0 0 1 : 0.000000E+000 , 0.500000E+000 0 0 2 : 0.000000E+000 , 0.000000E+000
-
While the input grid
grid.txt
contains negative g-vectors, the indices of the wavefunctions must remain positive. As such negative frequencies must be wrapped to positive ones as described below. See FFTW documentation for more info.
- In this case the FFT grid is 13x13x13 and so n = 6. Now open
./wfc1-og.txt
and confirm that it has the correct form compared to the above expession under step 3. In this case we can use a simple grep command to extract non-zero elements.
grep -v "0\.000000E+000 , 0\.000000E+000" ./wfc1-og.txt | less
- Following the rule -i = 2n+1-i (with n = 6), we can understand the transformation between negative and positive g-vectors and comparing with wfc1(G) with its above expression it does indeed have the correct form as it is described below. (You can also confirm that wfc2 is of the correct form by checking
wfc2-og.txt
.)
G_x G_y G_z Re(wfc) Im(wfc)
---------------------------------------------
0 0 1 : 0.000000E+000 , 0.500000E+000 | This corresponds to G = [ 0, 0, 1]
0 0 12 : 0.000000E+000 , -0.500000E+000 | This corresponds to G = [ 0, 0, -1]
0 2 0 : 0.000000E+000 , 0.500000E+000 | This corresponds to G = [ 0, 2, 0]
0 11 0 : 0.000000E+000 , -0.500000E+000 | This corresponds to G = [ 0, -2, 0]
1 0 0 : 0.000000E+000 , 0.500000E+000 | This corresponds to G = [ 1, 0, 0]
3 0 0 : 0.000000E+000 , 0.500000E+000 | This corresponds to G = [ 3, 0, 0]
10 0 0 : 0.000000E+000 , -0.500000E+000 | This corresponds to G = [ -3, 0, 0]
12 0 0 : 0.000000E+000 , -0.500000E+000 | This corresponds to G = [ -1, 0, 0]
- Now that we have confirmed the form of wfc(G), we will confirm the form in real-space wfc(r) by plotting. Since the wavefunctions are 3-dimensional we will plot 1-dimensional cuts of the wavefunction wfc(x,0,0), wfc(0,y,0) and wfc(0,0,z). Exit
zfs.dump
and run the script./Scripts/plot.sh -w
to plot the real space wavefunctions.
cd ..
./Scripts/plot.sh -w
- View the generated eps files with a document viewer such as
okular
. The solid lines correspond to the exact solution to wfc(r) and the points are the ones generated by the output ofzfs.x
.
okular ./wfc1_r.eps ./wfc2_r.eps
- Next we turn our focus to the convolution functions f(r) defined above. According to their definition, the expected solution of the functions f(r) should be defined as below.
- Plot the convolution functions using the script
./Scripts/plot.sh -f
and view the generated eps files with a document viewer.
./Scripts/plot.sh -f
okular ./f1_r.eps ./f2_r.eps ./f3_r.eps
- Next, we want to confirm that the form of f(G) is correct. In order to better understand the expected output of f(G), the expanded forms of f(r) are included in the pdf
./fft_algebra.pdf
alongside other formulas in this document. While the forms of f(r) written in this pdf are extremely lengthy, they should help to more easily understand the final forms of f(G). (The mathematica document./Scripts/algebra.nb
contains more notes on their derivation.)
okular ./fft_algebra.pdf
- According to
./fft_algebra.pdf
all of the f(G) functions are purely real. Usingawk
we can quickly confirm that the imaginary part of f(G) is zero everywhere. (The imaginary part of f(G) corresponds to the 7th column ofzfs.dump/f*_G-og.txt
.)
awk 'sqrt($7^2) > 1e-10 {print $0}' ./zfs.dump/f*_G-og.txt
- Meanwhile the real part of f(G) is non-zero at several spots. (The real part of f(G) corresponds to the 5th column of
zfs.dump/f*_G-og.txt
.). Use the helper script./Scripts/awk_f_G.sh
to check out the non-zero elements of the convolution functions f(G).
./Scripts/awk_f_G.sh | less
- Careful inspection of the previous output alongside the exact form of the convolution functions
./fft_algebra.pdf
reveals that the calculated f(G) are all correct.
- Lastly, we can calculate the convolution functions f(G) using direct convolution which avoids any FFT's but is dramatically slower. First enter the directory
./zfs.dump
and move the contents to a new directory to avoid overwriting them.
cd ./zfs.dump
mkdir fft
mv *.txt fft
cd ..
- The input
./zfs-conv.in
has the optiondirect_flag = .true.
. Run this calculation to produce output with the direct convolution method.
../../bin/zfs.x -i ./zfs-conv.in > ./zfs-conv.out
- First, we can see that both methods produce the same result.
grep ! ./zfs.out ./zfs-conv.out
./zfs.out: ! ZFS = ********** GHz = 82.620453 cm-1
./zfs-conv.out: ! ZFS = ********** GHz = 82.620453 cm-1
- Finally, we can plot each convolution function and see they are exactly the same using the script
Script/f_g.gnu
.
./Scripts/f_g.gnu
okular fft-vs-con_*.eps
Tryout the NV-Diamond Example