Bethe-Salpeter equation tutorial. Optical absorption (BN): Difference between revisions

From The Yambo Project
Jump to navigation Jump to search
No edit summary
 
(38 intermediate revisions by 3 users not shown)
Line 1: Line 1:
== Optical absorption using the Bethe-Salpeter Equation (BN) ==
In this tutorial we will deal with different aspects of running a Bethe-Salpeter (BSE) calculation for optical absorption spectra using yambopy. We continue using hexagonal boron nitride to illustrate how to perform convergence tests, and how to compare and analyse the results.


'''by H. Miranda'''
Before you start this tutorial, make sure you have run the scf and nscf runs. If you have not, you can calculate the relax <code>-r</code>, scf <code>-s</code> and nscf <code>-n</code> using the <code>gs_bn.py</code> file:
$ python gs_bn.py -r -s -n
Once that is over, you can start the tutorial.


In this tutorial we will deal with different aspects of running a BSE calculation for optical absorption spectra using yambopy:
__FORCETOC__
 
=== BSE convergence of optical spectra ===
 
In this section of the tutorial we will use the <code>bse_conv_bn.py</code> file. To evaluate the Bethe-Salpeter Kernel we need to first calculate the static dielectric screening, and then the screened Coulomb interaction matrix elements. This will be done with the file <code>bse_conv_bn.py</code>.
 
'''a. Static dielectric function'''
 
We begin by converging the static screening. In principle, all parameters can be converged independently one-by-one, and then the you can choose the best values for the final, fully converged result. To converge the static screening you will need:
 
<blockquote>
 
<code>FFTGvecs</code>: number of planewaves to include. Usually we need fewer planewaves than the total used in the self-consistent cycle that generated the ground state in the <code>scf</code> run. Reducing the total number of planewaves used diminishes the amount of memory per process that you are going to need.
 
<code>BndsRnXs</code>: number of bands to calculate the screening. Typically you need many bands (hundreds or even more) for the screening to converge.
 
<code>NGsBlkXs</code>: number of components for the local fields. Averages the value of the dielectric screening over a number of periodic copies of the unit cell. This parameter greatly increases the computational cost of the calculation and thus should be increased slowly.


<blockquote><ol style="list-style-type: decimal;">
<li><p>Relevant parameters for the convergence</p>
<blockquote><ol style="list-style-type: lower-alpha;">
<li>Static dielectric function</li>
<li>Optical absorption spectra</li></ol>
</blockquote></li>
<li>Coulomb truncation convergence</li>
<li>Plot excitonic wavefunctions</li>
<li>Parallel static screening</li></ol>
</blockquote>
</blockquote>
Before you start this tutorial, make sure you did the scf and nscf runs. If you did not, you can calculate the scf <code>-s</code> and nscf <code>-n</code> using the <code>gs_bn.py</code> file:


<source lang="bash">python gs_bn.py -s -n</source>
We create a dictionary with different values for each variable. The python script (<code>bse_conv_bn.py</code>) will then create a reference input file with the first value in each parameter's list. It will then create input files with the other parameters changing independently according to the values specified on the list:
When that is done, you can start the tutorial.


=== Relevant parameters for the convergence ===
#list of variables to optimize the dielectric screening
conv = { 'FFTGvecs': [[10,10,15,20,30],'Ry'],
          'NGsBlkXs': [[1,1,2,3,5,6], 'Ry'],
          'BndsRnXs': [[1,10],[1,10],[1,20],[1,30],[1,40]] }


In this section of the tutorial we will use the <code>bse_conv_bn.py</code> file. To calculate the Bethe-Salpeter Kernel we need to first calculate the static dielectric screening and then the screened coulomb interaction matrix elements. The relevant convergence parameters for these two stages are:
To run a calculation for each of the variables in the dictionary, we first define the <code>run</code> function


'''a. Static dielectric function'''
    def run(filename):
        """
        Function to be called by the optimize function
        """
        path = filename.split('.')[0]
        print(filename, path)
        shell = scheduler()
        shell.add_command('cd %s'%folder)
        shell.add_command('%s mpirun -np %d %s -F %s -J %s -C %s 2> %s.log'%(nohup,threads,yambo,filename,path,path,path))
        shell.add_command('touch %s/done'%path)
        if not os.path.isfile("%s/%s/done"%(folder,path)):
            shell.run()
 
which together with the optimize function in the class <code>YamboIn</code>,
y.optimize(conv,folder='bse_conv',run=run,ref_run=False)
will run and manage the calculations as defined in the dictionary. All calculation files and outputs will be in their respective directories inside the folder <code>bse_conv</code>.


<blockquote><code>FFTGvecs</code>: number of planewaves to include. Can be smaller than the number of planewaves in the self-consistency cycle. A typical good value is around 30 Ry (should always be checked!).
To check which options are available in the script <code>bse_conv_bn.py</code>, run
$ python bse_conv_bn.py
which will give you the following list:


<code>BndsRnXs</code>: number of bands to calculate the screening. A very high number of bands is needed.
usage: bse_conv_bn.py [-h] [-r] [-a] [-p] [-e] [-b] [-u] [-t THREADS]
Test the yambopy script.
optional arguments:
  -h, --help            show this help message and exit
  -r, --run            run BSE convergence calculation
  -a, --analyse        analyse results data
  -p, --plot            plot the results
  -e, --epsilon        converge epsilon parameters
  -b, --bse            converge bse parameters
  -u, --nohup          run the commands with nohup
  -t THREADS, --threads THREADS
                        number of threads to use


<code>NGsBlkXs</code>: number of components for the local fields. Averages the value of the dielectric screening over a number of periodic copies of the unit cell. This parameter increases greatly increases the cost of the calculation and hence should be increased slowly. A typical good value is 2 Ry.
So, in order to converge the screening, you will need to run
</blockquote>
To run the convergence we create a dictionary with different values for the variables. The python script (<code>bse_conv_bn.py</code>) will create a reference input file with the first value of each parameter. Then it will create input files with the other parameters changing according to the values specified in the list.


<source lang="python">#list of variables to optimize the dielectric screening
$ python bse_conv_bn.py -r -e
conv = { 'FFTGvecs': [[10,15,20,30],'Ry'],
        'NGsBlkXs': [[1,2,3,5,6], 'Ry'],
        'BndsRnXs': [[1,10],[1,20],[1,30],[1,40]] }</source>
To run the convergence with the static dielectric function do:


<source lang="bash">python bse_conv_bn.py -r -e</source>
By default we have set up the total number of threads to be equal to 2. If you have access to more, you can change this using the <code>-t</code> option shown on the list, followed by the number of threads you want to use.  
As you can see, the python script is running all the calculations changing the value of the input variables. You are free to open the <code>bse_conv_bn.py</code> file and modify it accoridng to your own needs. Using the optimal parameters, you can run a calculation and save the dielectric screening databases <code>ndb.em1s*</code> to re-use them in the subsequent calculations. For that you can copy these files to the SAVE folder. <code>yambo</code> will only re-calculate any database if it does not find it or some parameter has changed.


Once the calculations are done you can plot the static dielectric function as a function of q points using the following commands:
As you can see, the python script is running all the calculations changing the value of the input variables. You are free to open the <code>bse_conv_bn.py</code> file and modify it according to your own needs. Using the optimal parameters, you can run a calculation and save the dielectric screening databases <code>ndb.em1s*</code> to re-use them in the subsequent calculations. For that you can copy these files to the SAVE folder. <code>yambo</code> will only re-calculate any database if it does not find it or some parameter has changed.


<source lang="bash">yambopy plotem1s bse_conv/FFTGvecs* bse_conv/reference
Once the calculations are done you can plot resulting optical spectra by running
yambopy plotem1s bse_conv/BndsRnXs* bse_conv/reference
$ python bse_conv_bn.py -a
yambopy plotem1s bse_conv/NGsBlkXs* bse_conv/reference</source>
followed by
[[Image:figures/bse_bn_FFTGvecs.png|image]]
$ python bse_conv_bn.py -p -e
It will search the folder <code>bse_conv</code> for all calculations you performed previously and plot the results, using the class <code>YamboAnalyser</code> class.


[[Image:figures/bse_bn_BndsRnXs.png|image]]
[[File:Bse-hbn-conv-2.png|x750px|Yambo tutorial image]]


[[Image:figures/bse_bn_NGsBlkXs.png|image]]
Alternatively, you can obtain the same plots by running the the following script:  
$ python plot-bse-conv.py
If you want to, you can now add new entries to the lists in the dictionary, specially the <code>BndsRnXs</code>, which usually requires a lot of bands to achieve convergence. Keep its number less than or equal to the number of bands in the nscf calculation, re-run the script and plot the results again. You can also test if some variables can converge with smaller values than the ones you used up to now.  


You are at this point invited to add new entries to the list of <code>BndsRnXs</code> in the convergence dictionary (keep it bellow or equal to the number of bands in the nscf calculation) re-run the script and plot the results again.
'''b. Screened Coulomb interaction'''


'''b. Optical absorption spectra'''
Up to this point we have been focused on the variables which control the static screening. We still need to deal with the variables which setup the Bethe-Salpeter auxiliary Hamiltonian matrix. Once this matrix is set up, <code>yambo</code> will use a diagonalisation algorithm to obtain the excitonic states and energies. The relevant variables for this process are:


Once you obtained a converged dielectric screening function you can calculate the Bethe-Salpeter auxiliary Hamiltonian and obtain the excitonic states and energies diagonalizing it or calculating the optical absorption spectra with a recursive technique like the Haydock method. Recall the relevant parameters for convergence:
<blockquote><code>BSEBands</code>: number of bands to generate the transitions. Should be as small as possible as the size of the BSE auxiliary hamiltonian has (in the resonant approximation) dimensions <code>Nk*Nv*Nc</code>.  


<blockquote><code>BSEBands</code>: number of bands to generate the transitions. Should be as small as possible as the size of the BSE auxiliary hamiltonian has (in the resonant approximation) dimensions <code>Nk*Nv*Nc</code>. Another way to converge the number of transitions is using <code>BSEEhEny</code>. This variable selects the number of transitions based on the electron-hole energy difference.
Another way to converge the number of transitions is using <code>BSEEhEny</code>. This variable selects the number of transitions based on the electron-hole energy difference (it's the one we are going to use).


<code>BSENGBlk</code> is the number of blocks for the dielectric screening average over the unit cells. This has a similar meaning as <code>NGsBlkXs</code>.
<code>BSENGBlk</code> is the number of blocks for the dielectric screening average over the unit cells. This uses the static screening computed previously and controlled by the variable <code>NGsBlkXs</code>. So you <code>BSENGBlk</code> cannot be larger than <code>NGsBlkXs</code>.


<code>BSENGexx</code> in the number of exchange components. Relatively cheap to calculate but should be as small as possible to save memory.
<code>BSENGexx</code> in the number of exchange components. Relatively cheap to calculate, but should be kept as small as possible to save memory.


<code>KfnQP_E</code> is the scissor operator for the BSE. The first value is the rigid scissor, the second and third the stretching for the conduction and valence respectively. The optical absorption spectra is obtained in a range of energies given by <code>BEnRange</code> and the number of frequencies in the interval is <code>BEnSteps</code>.
<code>KfnQP_E</code> is the scissor operator for the BSE. The first value is the rigid scissor, the second and third are the stretching for the conduction and valence respectively. The optical absorption spectrum is obtained in a range of energies given by <code>BEnRange</code> and the number of frequencies in the interval is <code>BEnSteps</code>. Alternatively a quasiparticle energy database can be included from a previous GW calculation.
</blockquote>
</blockquote>
For the last variable you will be using a predefined scissor operator. If you want to know how to compute a scissor operator you can go and follow the GW tutorial here [[GW tutorial. Convergence and approximations (BN)]].
The dictionary of convergence in this case is:
The dictionary of convergence in this case is:


<source lang="python">#list of variables to optimize the BSE
#list of variables to optimize the BSE
conv = { 'BSEEhEny': [[[1,10],[1,12],[1,14]],'eV'],
        conv = { 'BSEEhEny': [[[1,10],[1,10],[1,12],[1,14]],'eV'],
        'BSENGBlk': [[0,1,2], 'Ry'],
                  'BSENGBlk': [[0,0,1,2], 'Ry'],
        'BSENGexx': [[10,15,20],'Ry']}</source>
                  'BSENGexx': [[10,10,15,20],'Ry']}
All these variables do not change the dielectric screening, so you can calculate it once and put the database in the <code>SAVE</code> folder to make the calculations faster. To run these <code>BSE</code> part of the calculation do:


<source lang="bash">python bse_conv_bn.py -r -b</source>
All these variables do not change the dielectric screening, so optionally you can calculate it once (using the previous section to determine converged parameters) and put the database in the <code>SAVE</code> folder to make the calculations faster. Otherwise the dielectric screening will be computed for each run (in the case of this tutorial, this is still quite fast). This is a slightly advanced use of <code>yambo</code>, so it is better to leave till you are more familiar with the code and its flow.
Once the calculations are done you can plot the optical absorption spectra:


<source lang="bash">yambopy analysebse bse_conv BSENGBlk
To run the convergence calculations for the BSE Hamiltonian write:
yambopy analysebse bse_conv BSENGexx
yambopy analysebse bse_conv BSEEhEny</source>
[[Image:figures/bse_bn_BSENGBlk_spectra.png|image]]


[[Image:figures/bse_bn_BSENGBlk_excitons.png|image]]
$ python bse_conv_bn.py -r -b
Once the calculations are done you can plot the optical absorption spectra:


[[Image:figures/bse_bn_BSENGexx_spectra.png|image]]
$ python bse_conv_bn.py -a
$ python bse_conv_bn.py -p -b


[[Image:figures/bse_bn_BSENGexx_excitons.png|image]]
[[File:Bse-hbn-conv-1.png|x750px|Yambo tutorial image]]


[[Image:figures/bse_bn_BSEEhEny_spectra.png|image]]
Alternatively, you can obtain the same plots by running the the following script: <source>$ python plot-bse-conv.py </source>
Again, we invite you to change the values in the dictionary to check if the calculation would converge better using larger or smaller parameters.


[[Image:figures/bse_bn_BSEEhEny_excitons.png|image]]
=== Coulomb truncation convergence ===


=== Coulomb truncation convergence ===
'''a. Running for different layer separation distances'''


Here we will check how the dielectric screening changes with vacuum spacing between layers and including a coulomb truncation technique. For that we define a loop where we do a self-consistent ground state calculation, non self-consistent calculation, create the databases and run a <code>yambo</code> BSE calculation for different vacuum spacings.
Here we will check how the dielectric screening changes with vacuum spacing between layers and including a coulomb truncation technique. For that we define a loop where we do a self-consistent ground state calculation, non self-consistent calculation, create the databases and run a <code>yambo</code> BSE calculation for different vacuum spacings.


To analyze the data we will:
To analyze the data we will plot the dielectric screening and check how the different values of the screening change the absorption spectra.
 
In the folder <code>tutorials/bn/</code> you find the python script <code>bse_cutoff_bn.py</code>. This script takes some time to be executed, you can run both variants without the cutoff and with the cutoff <code>-c</code> simultaneously to save time. You can run this script with:
<blockquote># plot the dielectric screening
# check how the different values of the screening change the absorption spectra
</blockquote>
In the folder <code>tutorials/bn/</code> you find the python script <code>bse_cutoff.py</code>. This script takes some time to be executed, you can run both variants without the cutoff and with the cutoff <code>-c</code> simultaneously to save time. You can run this script with:


<source lang="bash">python bse_cutoff.py -r -t4    # without coulomb cutoff
python bse_cutoff_bn.py -r -t4    # without coulomb cutoff
python bse_cutoff.py -r -c -t4 # with coulomb cutoff</source>
python bse_cutoff_bn.py -r -c -t4 # with coulomb cutoff
where <code>-t</code> specifies the number of MPI threads to use. The main loop changes the <code>layer_separation</code> variable using values from a list in the header of the file. In the script you can find how the functions <code>scf</code>, <code>ncf</code> and <code>database</code> are defined.
where <code>-t</code> specifies the number of MPI threads to use. The main loop changes the <code>layer_separation</code> variable using values from a list in the header of the file. In the script you can find how the functions <code>scf</code>, <code>ncf</code> and <code>database</code> are defined.


'''3. Plot the dielectric function'''
'''b. Plot the dielectric function'''


In a similar way as what was done before we can now plot the dielectric function for different layer separations:
In a similar way as what was done before we can now plot the dielectric function for different layer separations:


<source lang="bash">yambopy plotem1s bse_cutoff/*/*    # without coulomb cutoff   
yambopy plotem1s bse_cutoff/*/*    # without coulomb cutoff   
yambopy plotem1s bse_cutoff_cut/*/* # with coulomb cutoff</source>
yambopy plotem1s bse_cutoff_cut/*/* # with coulomb cutoff
[[Image:figures/bn_em1s_cutoff.png|image]]


[[Image:figures/bn_em1s_cutoff_cut.png|image]]
[[File:Bn em1s cutoff cut.png|Yambo tutorial image]]
 
[[File:Bn em1s cutoff.png|Yambo tutorial image]]


In these figures it is clear that the long-range part of the coulomb interaction (q=0 in reciprocal space) is truncated, i. e. it is forced to go to zero.
In these figures it is clear that the long-range part of the coulomb interaction (q=0 in reciprocal space) is truncated, i. e. it is forced to go to zero.


'''2. Plot the absorption'''
'''c. Plot the absorption spectrum'''


You can also plot how the absorption spectra changes with the cutoff using:
You can also plot how the absorption spectra changes with the cutoff using:


<source lang="bash">python bse_cutoff.py -p
python bse_cutoff_bn.py -p
python bse_cutoff.py -p -c</source>
python bse_cutoff_bn.py -p -c
[[Image:figures/bn_bse_cutoff_cut.png|image]]
 
[[File:Bn bse cutoff.png|bn bse cutoff]]
 
[[File:Bn bse cutoff cut.png|bn bse cutoff cut]]
 
As you can see, the spectra is still changing with the vacuum spacing, you should increase the vacuum until convergence. For that you can add larger values to the <code>layer_separations</code> list and run the calculations and analysis again.
 
=== Excitonic wavefunctions in reciprocal space ===
 
In this example we show how to use <code>yambopy</code> to plot the excitonic wavefunction weights that result from a BSE calculation (specifically from the diagonalisation of the excitonic Hamiltonian). The script we will use this time is: <code>bse_bn.py</code>. Be aware the parameters specified for the calculation are not high enough to obtain a converged result.
 
To run the BSE calculation do:
 
python bse_bn.py -r
Alternatively, you can run 
python bse_bn.py -r -c
to add the Coulomb cutoff.
 
Then, the absorption spectrum is readily plotted by running the auxiliary script
python plot-bse.py
[[File:bse_abs_plt.png|Yambo tutorial image]]
 
Afterwards you can run a basic analysis of the excitonic states and store the wavefunctions of the ones that are the most optically active, plotting them in reciprocal space. Plots in real space (not covered here) are also possible using yambopy (by calling ypp).
 
In order to proceed we will use the script <code>plot-excitondb.py</code>.
After running it as
 
python plot-excitondb.py
it will produce three plots.
 
The first one is a representation of the excitonic weights (or rather their modulus) in reciprocal space, band- and kpoint-resolved, for the lowest-bound exciton.
You can see that the electronic transitions contributing to this state originate mostly at symmetry point K (direct band gap) and along the KM line (the bands here have pi-type orbital character).
 
[[File:exc_bands.png|Yambo tutorial image]]
 
The second figure is an interpolated version of the first, with the bands and excitonic contributions appearing smoother.
 
[[File:exc_bands_interp.png|Yambo tutorial image]]
 
 
The final figure is a representation of the excitonic weights, summed over all bands, within the Brillouin zone. The brighter colors indicate where the most important electronic transitions for this excitonic state are located.


[[Image:figures/bn_bse_cutoff.png|image]]
[[File:exc_BZ.png|Yambo tutorial image]]


As you can see, the spectra is still changing with the vaccum spacing, you should increase the vacuum until convergence. For that you can add larger values to the <code>layer_separations</code> list and run the calculations and analysis again.
You can obtain plots for different excitonic states by changing the line


=== Excitonic wavefunctions ===
# List of states to be merged
states = [1,2]
in <code>plot-excitondb.py</code>.


In this example we show how to use the <code>yambopy</code> to plot the excitonic wavefunctions that result from a BSE calculation. The script we will use this time is: <code>bse_bn.py</code>. Be aware the parameters specified for the calculation are not high enough to obtain a converged result. To run the BSE calculation do:
You may also want to test the script with a more converged calculation: to this end you can increase the number of kpoints in the nscf calculation by going to <code>gs_bn.py</code> and changing the line


<source lang="bash">python bse_bn.py -r</source>
kpoints_nscf = [6,6,1]
Afterwards you can run a basic analysis of the excitonic states and store the wavefunctions of the ones that are more optically active and plot their wavefunctions in reciprocal space. Plots in real space are also possible using yambopy (by calling ypp). In the analysis code you have:


<source lang="python">#get the absorption spectra
to your liking (for example to <code>12x12x1</code>).
#'yambo' -&gt; was the jobstring '-J' used when running yambo
#'bse'  -&gt; folder where the job was run
a = YamboBSEAbsorptionSpectra('yambo',path='bse')


# Here we choose which excitons to read
Then, run again the following commands (nscf calculation, regeneration of yambo database, new BSE calculation).
# min_intensity -&gt; choose the excitons that have at least this intensity
# max_energy    -&gt; choose excitons with energy lower than this
# Degen_Step    -&gt; take only excitons that have energies more different than Degen_Step
excitons = a.get_excitons(min_intensity=0.001,max_energy=7,Degen_Step=0.01)


# read the wavefunctions
python gs_bn.py -n
# Cells=[13,13,1]  #number of cell repetitions
rm -r database
# Hole=[0,0,6+.5]  #position of the hole in cartesian coordinates (Bohr units)
python bse_bn.py -r -t 4
# FFTGvecs=10      #number of FFT vecs to use, larger makes the
#                  #image smoother, but takes more time to plot
a.get_wavefunctions(Degen_Step=0.01,repx=range(-1,2),repy=range(-1,2),repz=range(1),
                    Cells=[13,13,1],Hole=[0,0,6+.5], FFTGvecs=10,wf=True)


a.write_json()</source>
As for the plotting, you may want to increase the value of <code>lpratio</code> in <code>plot-excitondb.py</code> to ensure that the interpolation works, as well as reduce the value of <code>scale</code> for a nicer plot in the hexagonal BN.
The class <code>YamboBSEAbsorptionSpectra()</code> reads the absorption spectra obtained with explicit diagonalization of the BSE matrix. <code>yambo</code> if the <code>job_string</code> identifier used when running yambo, <code>bse</code> is the name of the folder where the job was run. The function <code>get_excitons()</code> runs <code>ypp</code> to obtain the exitonic states and their intensities. The function <code>get_wavefunctions()</code> also calls <code>ypp</code> and reads the reciprocal (and optionally real space) space wavefunctions and finally we store all the data in a <code>json</code> file.
The lines to be changed are


This file can then be easily plotted with another python script. To run this part of the code you can do:
exc_bands_inter = yexc.interpolate(save,path,states,lpratio=5,f=None,size=0.5,verbose=True)


<source lang="bash">python bse_bn.py -a  #this will generate absorptionspectra.json
and
yambopy plotexcitons absorptionspectra.json #this will plot it</source>
You can tune the parameters <code>min_intensity</code> and <code>max_energy</code> and obtain more or less excitons. <code>Degen_Step</code> is used to not consider excitons that are degenerate in energy. The reason is that when representing the excitonic wavefunction, degenerate states should be represented together. This value should in general be very small in order to not combine excitons that have energies close to each other but are not exactly degenerate. You should then obtain plots similar (these ones were generated on a 30x30 k-point grid) to the figures presented here:


[[Image:figures/absorption_bn.png|image]]
yexc.plot_exciton_2D_ax(ax,states,mode='hexagon',limfactor=0.8,scale=160)


[[Image:figures/excitons_bn.png|image]]
Then, you can run <code>plot-excitondb.py</code> again.


Again, be aware that this figures serve only to show the kind of representation that can be obtained with <code>yambo</code>, <code>ypp</code> and <code>yambopy</code>. Further convergence tests need to be performed to obtain accurate results, but that is left to the user. You are invited to re-run the nscf loop with more k-points and represent the resulting wavefunctions.
Again, be aware that these figures serve only to show the kind of representation that can be obtained with <code>yambo</code>, <code>ypp</code> and <code>yambopy</code>. Further convergence tests need to be performed to obtain accurate results, but that is left to the user.


You can now visualize these wavefunctions in real space using our online tool: [http://henriquemiranda.github.io/excitonwebsite/ http://henriquemiranda.github.io/excitonwebsite/]
<!--
You can now visualise these wavefunctions in real space using our online tool: [http://henriquemiranda.github.io/excitonwebsite/ http://henriquemiranda.github.io/excitonwebsite/]


For that, go to the website, and in the <code>Excitons</code> section select <code>absorptionspectra.json</code> file using the <code>Custom File</code>. You should see on the right part the absorption spectra and on the left the representation of the wavefunction in real space. Alternatively you can vizualize the individually generated <code>.xsf</code> files using xcrysden.
For that, go to the website, and in the <code>Excitons</code> section select <code>absorptionspectra.json</code> file using the <code>Custom File</code>. You should see on the right part the absorption spectra and on the left the representation of the wavefunction in real space. Alternatively you can vizualise the individually generated <code>.xsf</code> files using xcrysden.
-->


<!--
=== Parallel static screening ===
=== Parallel static screening ===


Line 197: Line 265:
import multiprocessing
import multiprocessing


yambo = &quot;yambo&quot;
yambo = "yambo";
folder = &quot;bse_par&quot;
folder = "bse_par";
nthreads = 2 #create two simultaneous jobs
nthreads = 2 #create two simultaneous jobs


Line 266: Line 334:
You should obtain a plot like this:
You should obtain a plot like this:


[[File:Bethe Salpeter in BN.png|thumb|Yambo tutorial image]]
[[File:Bethe Salpeter in BN.png|Yambo tutorial image]]
-->

Latest revision as of 14:25, 17 April 2024

In this tutorial we will deal with different aspects of running a Bethe-Salpeter (BSE) calculation for optical absorption spectra using yambopy. We continue using hexagonal boron nitride to illustrate how to perform convergence tests, and how to compare and analyse the results.

Before you start this tutorial, make sure you have run the scf and nscf runs. If you have not, you can calculate the relax -r, scf -s and nscf -n using the gs_bn.py file:

$ python gs_bn.py -r -s -n

Once that is over, you can start the tutorial.


BSE convergence of optical spectra

In this section of the tutorial we will use the bse_conv_bn.py file. To evaluate the Bethe-Salpeter Kernel we need to first calculate the static dielectric screening, and then the screened Coulomb interaction matrix elements. This will be done with the file bse_conv_bn.py.

a. Static dielectric function

We begin by converging the static screening. In principle, all parameters can be converged independently one-by-one, and then the you can choose the best values for the final, fully converged result. To converge the static screening you will need:

FFTGvecs: number of planewaves to include. Usually we need fewer planewaves than the total used in the self-consistent cycle that generated the ground state in the scf run. Reducing the total number of planewaves used diminishes the amount of memory per process that you are going to need.

BndsRnXs: number of bands to calculate the screening. Typically you need many bands (hundreds or even more) for the screening to converge.

NGsBlkXs: number of components for the local fields. Averages the value of the dielectric screening over a number of periodic copies of the unit cell. This parameter greatly increases the computational cost of the calculation and thus should be increased slowly.

We create a dictionary with different values for each variable. The python script (bse_conv_bn.py) will then create a reference input file with the first value in each parameter's list. It will then create input files with the other parameters changing independently according to the values specified on the list:

#list of variables to optimize the dielectric screening
conv = { 'FFTGvecs': [[10,10,15,20,30],'Ry'],
         'NGsBlkXs': [[1,1,2,3,5,6], 'Ry'],
         'BndsRnXs': [[1,10],[1,10],[1,20],[1,30],[1,40]] }

To run a calculation for each of the variables in the dictionary, we first define the run function

   def run(filename):
       """
       Function to be called by the optimize function
       """
       path = filename.split('.')[0]
       print(filename, path)
       shell = scheduler()
       shell.add_command('cd %s'%folder)
       shell.add_command('%s mpirun -np %d %s -F %s -J %s -C %s 2> %s.log'%(nohup,threads,yambo,filename,path,path,path))
       shell.add_command('touch %s/done'%path)
       if not os.path.isfile("%s/%s/done"%(folder,path)):
           shell.run()

which together with the optimize function in the class YamboIn,

y.optimize(conv,folder='bse_conv',run=run,ref_run=False) 

will run and manage the calculations as defined in the dictionary. All calculation files and outputs will be in their respective directories inside the folder bse_conv.

To check which options are available in the script bse_conv_bn.py, run

$ python bse_conv_bn.py

which will give you the following list:

usage: bse_conv_bn.py [-h] [-r] [-a] [-p] [-e] [-b] [-u] [-t THREADS]

Test the yambopy script.

optional arguments:
  -h, --help            show this help message and exit
  -r, --run             run BSE convergence calculation
  -a, --analyse         analyse results data
  -p, --plot            plot the results
  -e, --epsilon         converge epsilon parameters
  -b, --bse             converge bse parameters
  -u, --nohup           run the commands with nohup
  -t THREADS, --threads THREADS
                        number of threads to use

So, in order to converge the screening, you will need to run

$ python bse_conv_bn.py -r -e

By default we have set up the total number of threads to be equal to 2. If you have access to more, you can change this using the -t option shown on the list, followed by the number of threads you want to use.

As you can see, the python script is running all the calculations changing the value of the input variables. You are free to open the bse_conv_bn.py file and modify it according to your own needs. Using the optimal parameters, you can run a calculation and save the dielectric screening databases ndb.em1s* to re-use them in the subsequent calculations. For that you can copy these files to the SAVE folder. yambo will only re-calculate any database if it does not find it or some parameter has changed.

Once the calculations are done you can plot resulting optical spectra by running

$ python bse_conv_bn.py -a 

followed by

$ python bse_conv_bn.py -p -e 

It will search the folder bse_conv for all calculations you performed previously and plot the results, using the class YamboAnalyser class.

Yambo tutorial image

Alternatively, you can obtain the same plots by running the the following script:

$ python plot-bse-conv.py 

If you want to, you can now add new entries to the lists in the dictionary, specially the BndsRnXs, which usually requires a lot of bands to achieve convergence. Keep its number less than or equal to the number of bands in the nscf calculation, re-run the script and plot the results again. You can also test if some variables can converge with smaller values than the ones you used up to now.

b. Screened Coulomb interaction

Up to this point we have been focused on the variables which control the static screening. We still need to deal with the variables which setup the Bethe-Salpeter auxiliary Hamiltonian matrix. Once this matrix is set up, yambo will use a diagonalisation algorithm to obtain the excitonic states and energies. The relevant variables for this process are:

BSEBands: number of bands to generate the transitions. Should be as small as possible as the size of the BSE auxiliary hamiltonian has (in the resonant approximation) dimensions Nk*Nv*Nc.

Another way to converge the number of transitions is using BSEEhEny. This variable selects the number of transitions based on the electron-hole energy difference (it's the one we are going to use).

BSENGBlk is the number of blocks for the dielectric screening average over the unit cells. This uses the static screening computed previously and controlled by the variable NGsBlkXs. So you BSENGBlk cannot be larger than NGsBlkXs.

BSENGexx in the number of exchange components. Relatively cheap to calculate, but should be kept as small as possible to save memory.

KfnQP_E is the scissor operator for the BSE. The first value is the rigid scissor, the second and third are the stretching for the conduction and valence respectively. The optical absorption spectrum is obtained in a range of energies given by BEnRange and the number of frequencies in the interval is BEnSteps. Alternatively a quasiparticle energy database can be included from a previous GW calculation.

For the last variable you will be using a predefined scissor operator. If you want to know how to compute a scissor operator you can go and follow the GW tutorial here GW tutorial. Convergence and approximations (BN).

The dictionary of convergence in this case is:

#list of variables to optimize the BSE
        conv = { 'BSEEhEny': [[[1,10],[1,10],[1,12],[1,14]],'eV'],
                 'BSENGBlk': [[0,0,1,2], 'Ry'],
                 'BSENGexx': [[10,10,15,20],'Ry']}

All these variables do not change the dielectric screening, so optionally you can calculate it once (using the previous section to determine converged parameters) and put the database in the SAVE folder to make the calculations faster. Otherwise the dielectric screening will be computed for each run (in the case of this tutorial, this is still quite fast). This is a slightly advanced use of yambo, so it is better to leave till you are more familiar with the code and its flow.

To run the convergence calculations for the BSE Hamiltonian write:

$ python bse_conv_bn.py -r -b

Once the calculations are done you can plot the optical absorption spectra:

$ python bse_conv_bn.py -a
$ python bse_conv_bn.py -p -b

Yambo tutorial image

Alternatively, you can obtain the same plots by running the the following script: <source>$ python plot-bse-conv.py </source> Again, we invite you to change the values in the dictionary to check if the calculation would converge better using larger or smaller parameters.

Coulomb truncation convergence

a. Running for different layer separation distances

Here we will check how the dielectric screening changes with vacuum spacing between layers and including a coulomb truncation technique. For that we define a loop where we do a self-consistent ground state calculation, non self-consistent calculation, create the databases and run a yambo BSE calculation for different vacuum spacings.

To analyze the data we will plot the dielectric screening and check how the different values of the screening change the absorption spectra. In the folder tutorials/bn/ you find the python script bse_cutoff_bn.py. This script takes some time to be executed, you can run both variants without the cutoff and with the cutoff -c simultaneously to save time. You can run this script with:

python bse_cutoff_bn.py -r -t4    # without coulomb cutoff
python bse_cutoff_bn.py -r -c -t4 # with coulomb cutoff

where -t specifies the number of MPI threads to use. The main loop changes the layer_separation variable using values from a list in the header of the file. In the script you can find how the functions scf, ncf and database are defined.

b. Plot the dielectric function

In a similar way as what was done before we can now plot the dielectric function for different layer separations:

yambopy plotem1s bse_cutoff/*/*     # without coulomb cutoff  
yambopy plotem1s bse_cutoff_cut/*/* # with coulomb cutoff

Yambo tutorial image

Yambo tutorial image

In these figures it is clear that the long-range part of the coulomb interaction (q=0 in reciprocal space) is truncated, i. e. it is forced to go to zero.

c. Plot the absorption spectrum

You can also plot how the absorption spectra changes with the cutoff using:

python bse_cutoff_bn.py -p
python bse_cutoff_bn.py -p -c

bn bse cutoff

bn bse cutoff cut

As you can see, the spectra is still changing with the vacuum spacing, you should increase the vacuum until convergence. For that you can add larger values to the layer_separations list and run the calculations and analysis again.

Excitonic wavefunctions in reciprocal space

In this example we show how to use yambopy to plot the excitonic wavefunction weights that result from a BSE calculation (specifically from the diagonalisation of the excitonic Hamiltonian). The script we will use this time is: bse_bn.py. Be aware the parameters specified for the calculation are not high enough to obtain a converged result.

To run the BSE calculation do:

python bse_bn.py -r

Alternatively, you can run

python bse_bn.py -r -c  

to add the Coulomb cutoff.

Then, the absorption spectrum is readily plotted by running the auxiliary script

python plot-bse.py

Yambo tutorial image

Afterwards you can run a basic analysis of the excitonic states and store the wavefunctions of the ones that are the most optically active, plotting them in reciprocal space. Plots in real space (not covered here) are also possible using yambopy (by calling ypp).

In order to proceed we will use the script plot-excitondb.py. After running it as

python plot-excitondb.py

it will produce three plots.

The first one is a representation of the excitonic weights (or rather their modulus) in reciprocal space, band- and kpoint-resolved, for the lowest-bound exciton. You can see that the electronic transitions contributing to this state originate mostly at symmetry point K (direct band gap) and along the KM line (the bands here have pi-type orbital character).

Yambo tutorial image

The second figure is an interpolated version of the first, with the bands and excitonic contributions appearing smoother.

Yambo tutorial image


The final figure is a representation of the excitonic weights, summed over all bands, within the Brillouin zone. The brighter colors indicate where the most important electronic transitions for this excitonic state are located.

Yambo tutorial image

You can obtain plots for different excitonic states by changing the line

# List of states to be merged
states = [1,2]

in plot-excitondb.py.

You may also want to test the script with a more converged calculation: to this end you can increase the number of kpoints in the nscf calculation by going to gs_bn.py and changing the line

kpoints_nscf = [6,6,1]

to your liking (for example to 12x12x1).

Then, run again the following commands (nscf calculation, regeneration of yambo database, new BSE calculation).

python gs_bn.py -n
rm -r database
python bse_bn.py -r -t 4

As for the plotting, you may want to increase the value of lpratio in plot-excitondb.py to ensure that the interpolation works, as well as reduce the value of scale for a nicer plot in the hexagonal BN. The lines to be changed are

exc_bands_inter = yexc.interpolate(save,path,states,lpratio=5,f=None,size=0.5,verbose=True)

and

yexc.plot_exciton_2D_ax(ax,states,mode='hexagon',limfactor=0.8,scale=160)

Then, you can run plot-excitondb.py again.

Again, be aware that these figures serve only to show the kind of representation that can be obtained with yambo, ypp and yambopy. Further convergence tests need to be performed to obtain accurate results, but that is left to the user.