summaryrefslogtreecommitdiff
path: root/main.py
blob: 23a21fc01217162777eecdf7f1bb3490d936765b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import csv
import math

import numpy as np

import atom
import crystal
import density
import eigen


class Settings:
    pass


# Set up the object "s" that contains all settings for the run.
# After this function, "s" is not modified. Adjust settings here.
def initialize():
    s = Settings()

    # Resolution: store functions on an NxNxN grid
    s.Nres = 16

    # Lattice constant of conventional cell, in Bohr radii
    #a = 6.7463 # C
    a = 10.261 # Si
    #a = 10.298 # GaP
    #a = 10.683 # Ge/GaAs
    #a = 11.449 # InAs

    # Crystal structure, choose Cubic{Simple,BodyCentered,FaceCentered}
    s.crystal = crystal.CubicFaceCentered(s.Nres, a)
    # Grid dimensions for k-point sampling of Brillouin zone
    s.bzmesh = (3, 3, 3)

    # Sequence of Brillouin zone symmetry points for band structure plot
    s.bzpath = "LGXWULWXUG"
    # Number of substeps to take between two symmetry points in bzpath
    s.Nbzsub = 10

    # Number of orbitals to use per atom (but not necessarily converge)
    s.Norb = 8
    # Number of total Kohn-Sham eigenstates to converge
    s.Neig = 12

    # Elements and positions of atoms in the unit cell.
    # Positions are expressed in the cell basis (a1,a2,a3).
    atoms = [
        ("Si", (-0.125, -0.125, -0.125)),
        ("Si", ( 0.125,  0.125,  0.125)),
    ]

    # Plane-wave cutoff energy in Hartree
    s.Ecut = 50

    # Finalize the "s" object. Avoid editing below this line.

    # Get the grid point coordinates in direct and Fourier space
    s.rx, s.ry, s.rz, s.dr = s.crystal.get_grid_direct()
    s.qx, s.qy, s.qz, s.dq = s.crystal.get_grid_fourier()
    # Remember to call fftshift before plotting!
    s.qx = np.fft.ifftshift(s.qx)
    s.qy = np.fft.ifftshift(s.qy)
    s.qz = np.fft.ifftshift(s.qz)

    # For convenience
    s.r2 = s.rx**2 + s.ry**2 + s.rz**2
    s.q2 = s.qx**2 + s.qy**2 + s.qz**2

    # Place the atoms on the grid as specified in "atoms"
    s.atoms = []
    basis = s.crystal.a
    for a in atoms:
        pos = a[1][0] * basis[0] + a[1][1] * basis[1] + a[1][2] * basis[2]
        s.atoms.append(atom.Atom(s, a[0], pos))

    return s


def main():
    # Get settings object "s"
    s = initialize()

    # It would be madness to try to converge the electron density
    # for all band structure k-points at the same time, so instead
    # we converge it for a small set and then assume it's fixed.
    rho, Veff = density.get_selfconsistent_density(s)
    print("\nCalculating band structure with fixed electron density")

    # Get list of positions of high-symmetry k-points
    ksteps = s.crystal.get_brillouin_zone_path(s.bzpath)
    Nsteps = len(ksteps) - 1

    # Total distance in k-space, used for first CSV column
    kdistance = 0.0

    # Open output CSV file for writing
    file = open("result.csv", "w", newline="")
    csvw = csv.writer(file, quoting=csv.QUOTE_MINIMAL)

    # Loop over symmetry points, except last one
    for i in range(Nsteps):
        print("Band calculation part {}/{} running...".format(i + 1, Nsteps))

        # Subdivide path between this point and next point
        if i < Nsteps - 1:
            ksubs = np.linspace(ksteps[i], ksteps[i + 1], s.Nbzsub, endpoint=False)
        else: # include endpoint in last iteration
            ksubs = np.linspace(ksteps[i], ksteps[i + 1], s.Nbzsub + 1, endpoint=True)

        # Loop over points on subdivided path
        for j, kp in enumerate(ksubs):
            # Solve Kohn-Sham equation for this k-point, with fixed Veff
            pf = "    ({}/{}) ".format(j + 1, len(ksubs))
            energies, psis_r, psis_q = eigen.solve_kohnsham_equation(s, Veff, kp, pf)

            csvw.writerow([kdistance] + energies[0 : s.Neig])

            # Keep track of how far we moved in k-space
            kd = np.linalg.norm(ksteps[i + 1] - ksteps[i])
            kdistance += kd / s.Nbzsub

    file.close()


if __name__ == "__main__":
    main()