├── .github └── workflows │ ├── CI.yml │ ├── CompatHelper.yml │ ├── Documentation.yml │ └── TagBot.yml ├── .gitignore ├── LICENSE ├── Project.toml ├── README.md ├── Sample ├── ChernSC.jl ├── Haldane_Model.jl ├── HigherOrderTopo.jl ├── Honeycomb_123NN.jl ├── KitaevChain.jl ├── Square_lattice.jl ├── Triangle_123NN.jl └── Vortex.jl ├── docs ├── Project.toml ├── assets │ ├── Graphene_DOS.png │ ├── Haldane_Chern.png │ ├── Honeycomb_123NN_UC.png │ ├── KitaevChain.png │ ├── Triangle_123NN_FS.png │ ├── Triangle_123NN_UC.png │ ├── Triangle_123NN_bandStructure.png │ └── Triangle_123NN_chi.png ├── make.jl └── src │ ├── BZ.md │ ├── BdGModel.md │ ├── Chern.md │ ├── Figures │ ├── Graphene_DOS.png │ ├── Haldane_Chern.png │ ├── Honeycomb_123NN_UC.png │ ├── KitaevChain.png │ ├── Triangle_123NN_FS.png │ ├── Triangle_123NN_UC.png │ ├── Triangle_123NN_bandStructure.png │ └── Triangle_123NN_chi.png │ ├── Hamiltonian.md │ ├── Lattice.md │ ├── Model.md │ ├── Params.md │ ├── Plot.md │ ├── UnitCell.md │ ├── assets │ ├── Graphene_DOS.png │ ├── Haldane_Chern.png │ ├── Honeycomb_123NN_UC.png │ ├── KitaevChain.png │ ├── Triangle_123NN_FS.png │ ├── Triangle_123NN_UC.png │ ├── Triangle_123NN_bandStructure.png │ └── Triangle_123NN_chi.png │ ├── conductivity.md │ ├── index.md │ └── susceptibility.md ├── src ├── BZ.jl ├── BdGModel.jl ├── Chern.jl ├── Conductivity.jl ├── DesignLattice.jl ├── DesignUnitCell.jl ├── ExpandUnitCell.jl ├── Flux.jl ├── Gauge.jl ├── Hamiltonian.jl ├── Lattice.jl ├── LatticeHamiltonian.jl ├── LatticeModel.jl ├── LatticeSymmetries.jl ├── Model.jl ├── Params.jl ├── Plaquettes.jl ├── Plot.jl ├── SpectralResponse.jl ├── SpinMats.jl ├── Susceptibility.jl ├── TightBindingToolkit.jl ├── UnitCell.jl └── Useful.jl └── test ├── Project.toml └── runtests.jl /.github/workflows/CI.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: 5 | - main 6 | tags: ['*'] 7 | pull_request: 8 | concurrency: 9 | # Skip intermediate builds: always. 10 | # Cancel intermediate builds: only if it is a pull request build. 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: ${{ startsWith(github.ref, 'refs/pull/') }} 13 | jobs: 14 | test: 15 | name: Julia ${{ matrix.version }} - ${{ matrix.os }} - ${{ matrix.arch }} - ${{ github.event_name }} 16 | runs-on: ${{ matrix.os }} 17 | strategy: 18 | fail-fast: false 19 | matrix: 20 | version: 21 | - '1.8' 22 | - '1.7' 23 | - '1.9' 24 | python: [3.8] 25 | os: 26 | - ubuntu-latest 27 | arch: 28 | - x64 29 | env: 30 | PYTHON: "" 31 | steps: 32 | - uses: actions/checkout@v2 33 | - name: Set up 🐍 ${{ matrix.python }} 34 | uses: actions/setup-python@v2 35 | with: 36 | python-version: ${{ matrix.python }} 37 | 38 | - name: Display Python version 39 | run: python -c "import sys; print(sys.version)" 40 | 41 | - name: Set ENV Variables for 🐍 📞 42 | run: echo ENV["PYTHON"] = "${{ env.pythonLocation }}/bin/python" >> $GITHUB_ENV 43 | - name: Install dependencies 🔧 44 | run: | 45 | python -m pip install --upgrade pip 46 | pip install matplotlib 47 | pip install numpy 48 | 49 | # Julia tasks 50 | - uses: julia-actions/setup-julia@v1 51 | with: 52 | version: ${{ matrix.version }} 53 | arch: ${{ matrix.arch }} 54 | - uses: julia-actions/cache@v1 55 | - uses: julia-actions/julia-buildpkg@v1 56 | env: 57 | PYTHON : "${{ env.pythonLocation }}/bin/python" 58 | - uses: julia-actions/julia-runtest@v1 59 | 60 | -------------------------------------------------------------------------------- /.github/workflows/CompatHelper.yml: -------------------------------------------------------------------------------- 1 | name: CompatHelper 2 | on: 3 | schedule: 4 | - cron: 0 0 * * * 5 | workflow_dispatch: 6 | jobs: 7 | CompatHelper: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Pkg.add("CompatHelper") 11 | run: julia -e 'using Pkg; Pkg.add("CompatHelper")' 12 | - name: CompatHelper.main() 13 | env: 14 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 15 | COMPATHELPER_PRIV: ${{ secrets.DOCUMENTER_KEY }} 16 | run: julia -e 'using CompatHelper; CompatHelper.main(; subdirs=["", "docs", "test"])' 17 | -------------------------------------------------------------------------------- /.github/workflows/Documentation.yml: -------------------------------------------------------------------------------- 1 | name: Documentation 2 | 3 | on: 4 | push: 5 | branches: 6 | - main # update to match your development branch (master, main, dev, trunk, ...) 7 | tags: '*' 8 | pull_request: 9 | 10 | jobs: 11 | build: 12 | permissions: 13 | contents: write 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | - uses: julia-actions/setup-julia@v1 18 | with: 19 | version: '1.6' 20 | - name: Install dependencies 21 | run: julia --project=docs/ -e 'using Pkg; Pkg.develop(PackageSpec(path=pwd())); Pkg.instantiate()' 22 | - name: Build and deploy 23 | env: 24 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # If authenticating with GitHub Actions token 25 | DOCUMENTER_KEY: ${{ secrets.DOCUMENTER_KEY }} # If authenticating with SSH deploy key 26 | run: julia --project=docs/ docs/make.jl 27 | -------------------------------------------------------------------------------- /.github/workflows/TagBot.yml: -------------------------------------------------------------------------------- 1 | name: TagBot 2 | on: 3 | issue_comment: 4 | types: 5 | - created 6 | workflow_dispatch: 7 | inputs: 8 | lookback: 9 | default: 3 10 | permissions: 11 | actions: read 12 | checks: read 13 | contents: write 14 | deployments: read 15 | issues: read 16 | discussions: read 17 | packages: read 18 | pages: read 19 | pull-requests: read 20 | repository-projects: read 21 | security-events: read 22 | statuses: read 23 | jobs: 24 | TagBot: 25 | if: github.event_name == 'workflow_dispatch' || github.actor == 'JuliaTagBot' 26 | runs-on: ubuntu-latest 27 | steps: 28 | - uses: JuliaRegistries/TagBot@v1 29 | with: 30 | token: ${{ secrets.GITHUB_TOKEN }} 31 | # Edit the following line to reflect the actual name of the GitHub Secret containing your private key 32 | ssh: ${{ secrets.DOCUMENTER_KEY }} 33 | # ssh: ${{ secrets.NAME_OF_MY_SSH_PRIVATE_KEY_SECRET }} 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /Manifest.toml 2 | /docs/build/ 3 | *.pdf 4 | *.jld2 5 | *.dat 6 | *.npz 7 | Sample/Honeycomb_KaneMele.jl 8 | Sample/HoneycombKekule.jl 9 | Sample/SimpleTightBindingWith12Unitcells.jl 10 | Sample/SquareNeel.jl 11 | Sample/TriangularLattice.jl 12 | Sample/Honeycomb_t1t3_thermal.jl 13 | 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Anjishnu Bose, Sreekar Voleti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Project.toml: -------------------------------------------------------------------------------- 1 | name = "TightBindingToolkit" 2 | uuid = "2233325a-6eb3-486f-aff0-670e0939fa7e" 3 | authors = ["Anjishnu Bose", "Sreekar Voleti", "Samuel Vadnais"] 4 | version = "2.3.4" 5 | 6 | [deps] 7 | Bijections = "e2ed5e7c-b2de-5872-ae92-c73ca462fb04" 8 | FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" 9 | LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" 10 | LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" 11 | Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" 12 | MappedArrays = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900" 13 | Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" 14 | PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee" 15 | Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" 16 | StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91" 17 | TensorCast = "02d47bb6-7ce6-556a-be16-bb1710789e2b" 18 | Tullio = "bc48ee85-29a4-5162-ae0b-a64e1601d4bc" 19 | 20 | [compat] 21 | FFTW = "^1.6" 22 | LaTeXStrings = "1.3" 23 | Logging = "^1.6.7" 24 | MappedArrays = "^0.4" 25 | Plots = "^1.38" 26 | PyPlot = "2.11" 27 | Statistics = "1.0 - 2.0" 28 | StatsBase = "^0.34" 29 | TensorCast = "^0.4" 30 | Tullio = "^0.3" 31 | Bijections = "^0.1" 32 | julia = "^1.6, ^1.9" 33 | LinearAlgebra = "^1.6, ^1.9" 34 | 35 | [extras] 36 | Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" 37 | 38 | [targets] 39 | test = ["Test"] 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TightBindingToolkit 2 | 3 | [![Build Status](https://github.com/sreekar-voleti/TightBindingToolkit.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/sreekar-voleti/TightBindingToolkit.jl/actions/workflows/CI.yml?query=branch%3Amain) 4 | 5 | TightBindingToolkit.jl is a Julia package meant for constructing, and obtaining useful properties of generic tight-binding models. It supports any lattice structure, with any user-defined bonds on that lattice. It also has support for any spin of the particle hopping on the lattice. 6 | 7 | ## Install 8 | In the Julia REPL, Push "]" to enter the package mode. 9 | ```julia 10 | add TightBindingToolkit 11 | ``` 12 | Or equivalently 13 | ```julia 14 | using Pkg 15 | Pkg.add("TightBindingToolkit") 16 | ``` 17 | 18 | ## Features 19 | Currently supported : 20 | * Custom Unit Cell Construction. (v1.3) : now also supports construction in dimensions upto d=3. Can change primitives, or expand unit cell on the go. 21 | |![Alt text](./docs/src/Figures/Honeycomb_123NN_UC.png)| 22 | |:--:| 23 | |*Honeycomb Model with 1st, 2nd and 3rd neighbour hoppings.*| 24 | * Corresponding Brillouin Zone Construction. (v1.3) : now also supports construction in dimensions upto d=3. 25 | * Hamiltonian, given a Unit Cell and a Brillouin Zone. (v1.3) : now also support BdG Hamiltonians. 26 | * Diagonalizing the Hamiltonian in momentum space to get band structures and wavefunctions. 27 | |![Alt text](./docs/src/Figures/Triangle_123NN_bandStructure.png)| 28 | |:--:| 29 | |*Band structure for a triangular lattice with 1st, 2nd and 3rd neighbour hopping.*| 30 | * Density of State 31 | |![Alt text](./docs/src/Figures/Graphene_DOS.png)| 32 | |:--:| 33 | |*Density of state of graphene.*| 34 | * Filling the model at given chemical potential, and calculating gaps. (v1.3) : also supported for BdG systems. 35 | |![Alt text](./docs/src/Figures/KitaevChain.png)| 36 | |:--:| 37 | |*Band structure of the Kitaev Chain in 1-d.*| 38 | * Fermi surfaces of systems 39 | |![Alt text](./docs/src/Figures/Triangle_123NN_FS.png)| 40 | |:--:| 41 | |*Fermi surface for a triangular lattice with 1st, 2nd and 3rd neighbour hopping.*| 42 | * Getting correlation functionsin momentum space and real space. (v1.3) : also supported for BdG systems. 43 | * Getting Berry curvature and Chern numbers. Also workjs for topological superconductors. 44 | |![Alt text](./docs/src/Figures/Haldane_Chern.png)| 45 | |:--:| 46 | |*Chern numbers in a spin-ful Haldane model as a function of t2.*| 47 | * Getting magnetic susceptibility in any direction, at any momentum, and energy. 48 | |![Alt text](./docs/src/Figures/Triangle_123NN_chi.png)| 49 | |:--:| 50 | |*imaginary part of zero-energy susceptibility for a triangular lattice with 1st, 2nd and 3rd neighbour hopping.*| 51 | * Real-sapce lattice construction with arbitrary boundary condition. (v2.1) 52 | 53 | ## Documentation 54 | For more details, please see [Documentation](https://anjishnubose.github.io/TightBindingToolkit.jl/dev/) 55 | -------------------------------------------------------------------------------- /Sample/ChernSC.jl: -------------------------------------------------------------------------------- 1 | using Plots, LinearAlgebra, LaTeXStrings#, TightBindingToolkit 2 | include("../src/TightBindingToolkit.jl") 3 | using .TightBindingToolkit 4 | 5 | ##### Triangle lattice 6 | const a1 = [ 0.5, sqrt(3)/2] 7 | const a2 = [-0.5, sqrt(3)/2] 8 | 9 | const b1 = [0.0, 0.0] 10 | 11 | HoppingUC = UnitCell([a1, a2], 1, 2) 12 | PairingUC = UnitCell([a1, a2], 1, 2) 13 | 14 | AddBasisSite!(HoppingUC, b1) 15 | AddBasisSite!(PairingUC, b1) 16 | 17 | const t = 1.0 18 | ##### HoppingParams 19 | const t1 = t 20 | t1Param = Param(-t1, 2) 21 | AddIsotropicBonds!(t1Param, HoppingUC, 1.0, 1.0, "s Hopping") 22 | 23 | const Δ1 = 1.0 * t 24 | f1Param = Param(Δ1, 2) 25 | AddAnisotropicBond!(f1Param, PairingUC, 1, 1, [ 1, 0], -1.0 , 1.0, "f_x^3-3xy^2 Hopping") 26 | AddAnisotropicBond!(f1Param, PairingUC, 1, 1, [ 0, 1], 1.0 , 1.0, "f_x^3-3xy^2 Hopping") 27 | AddAnisotropicBond!(f1Param, PairingUC, 1, 1, [ 1, -1], 1.0 , 1.0, "f_x^3-3xy^2 Hopping") 28 | 29 | const Δ2 = 1.0 * t 30 | f2Param = Param(-Δ2 / (3 * sqrt(3)), 2) 31 | AddAnisotropicBond!(f2Param, PairingUC, 1, 1, [ 1, 1], im, sqrt(3), "f_y^3-3yx^2 Hopping") 32 | AddAnisotropicBond!(f2Param, PairingUC, 1, 1, [ 2, -1], -im, sqrt(3), "f_y^3-3yx^2 Hopping") 33 | AddAnisotropicBond!(f2Param, PairingUC, 1, 1, [-1, 2], -im, sqrt(3), "f_y^3-3yx^2 Hopping") 34 | 35 | HoppingParams = [t1Param] 36 | CreateUnitCell!(HoppingUC, HoppingParams) 37 | 38 | PairingParams = [f1Param, f2Param] 39 | CreateUnitCell!(PairingUC, PairingParams) 40 | 41 | const n = 20 42 | const kSize = 6 * n + 3 43 | bz = BZ([kSize, kSize]) 44 | FillBZ!(bz, HoppingUC) 45 | 46 | ##### Thermodynamic parameters 47 | const T = 0.001 48 | const filling = 0.5 49 | const stat = -1 50 | 51 | mus = collect(LinRange(-8.0, 4.0, 61)) 52 | Cherns = Float64[] 53 | gaps = Float64[] 54 | fillings = Float64[] 55 | 56 | H = Hamiltonian(HoppingUC, PairingUC, bz) 57 | DiagonalizeHamiltonian!(H) 58 | 59 | M = BdGModel(HoppingUC, PairingUC, bz, H ; mu = 0.0, T = T) 60 | SolveModel!(M) 61 | 62 | # Plot_Band_Structure!(M ,[bz.HighSymPoints["G"], bz.HighSymPoints["K1"], bz.HighSymPoints["M2"]] ; labels=[L"$\Gamma$", L"K_1", L"M_2"], nearest = true) 63 | 64 | for mu in mus 65 | 66 | """ 67 | Filling the model with fermions at half-filling, with the default temperature T=0.0. 68 | """ 69 | M = BdGModel(HoppingUC, PairingUC, bz, H ; mu = mu, T = T) 70 | SolveModel!(M ; get_gap = true) 71 | 72 | push!(gaps, M.gap) 73 | push!(fillings, M.filling) 74 | 75 | """ 76 | Then the chern numbers of the lowest two bands (1 and 2) in the Haldane Model becomes : 77 | """ 78 | chern = ChernNumber(H, [1]) 79 | push!(Cherns, chern) 80 | end 81 | -------------------------------------------------------------------------------- /Sample/Haldane_Model.jl: -------------------------------------------------------------------------------- 1 | using Plots, LinearAlgebra, LaTeXStrings, TightBindingToolkit 2 | 3 | """ 4 | This script sets up the famous Haldane Model on a honeycomb lattice, and finds it chern numbers. 5 | """ 6 | 7 | """ 8 | The lattice primitive vector for a honeycomb lattice : a1 and a2. 9 | """ 10 | a1 = [ 1/2 , sqrt(3)/2 ] 11 | a2 = [-1/2 , sqrt(3)/2 ] 12 | UC = UnitCell( [a1 , a2] , 2, 2) ##### localDim = 2 cause we are Constructing a spinful version of the Haldane Model. rank = 2 cause we are constructing a free Hamiltonian. 13 | 14 | """ 15 | The two sub-lattices of a honeycomb lattice. 16 | """ 17 | b1 = [ 0.0 , 0.0 ] 18 | b2 = [ 0.0 , 1/sqrt(3) ] 19 | AddBasisSite!( UC , b1 ) 20 | AddBasisSite!( UC , b2 ) 21 | 22 | SpinVec = SpinMats(1//2) 23 | 24 | """ 25 | Adding structure to the lattice now, through the bond objects. 26 | """ 27 | ################## On-site ################### 28 | const M = 1.0 29 | MParam = Param(M, 2) 30 | AddAnisotropicBond!( MParam, UC , 1 , 1 , [ 0 , 0 ] , SpinVec[4], 0.0 , "M") 31 | AddAnisotropicBond!( MParam, UC , 2 , 2 , [ 0 , 0 ] , -SpinVec[4], 0.0 , "M") 32 | ################# Nearest Neighbour hopping ################# 33 | const t1 = -1.0 34 | t1Param = Param(t1, 2) 35 | const NNdistance = 1/sqrt(3) 36 | AddIsotropicBonds!(t1Param, UC , NNdistance, SpinVec[4] , "t1") 37 | ################# 2nd Nearest Neighbour ################# 38 | const t2 = (-1.0) 39 | t2Param = Param(t2, 2) 40 | const secondNNdistance = 1.0 41 | AddAnisotropicBond!(t2Param, UC , 1 , 1 , [ 1 , 0 ] , im * SpinVec[4], secondNNdistance , "t2") 42 | AddAnisotropicBond!(t2Param, UC , 1 , 1 , [ 0 , -1 ] , im * SpinVec[4], secondNNdistance , "t2") 43 | AddAnisotropicBond!(t2Param, UC , 1 , 1 , [ -1 , 1 ] , im * SpinVec[4], secondNNdistance , "t2") 44 | AddAnisotropicBond!(t2Param, UC , 2 , 2 , [ 0 , 1 ] , im * SpinVec[4], secondNNdistance , "t2") 45 | AddAnisotropicBond!(t2Param, UC , 2 , 2 , [ -1 , 0 ] , im * SpinVec[4], secondNNdistance , "t2") 46 | AddAnisotropicBond!(t2Param, UC , 2 , 2 , [ 1 , -1 ] , im * SpinVec[4], secondNNdistance , "t2") 47 | 48 | CreateUnitCell!(UC, [MParam, t1Param, t2Param]) 49 | 50 | """ 51 | Making the Brillouin Zone with kSize * kSize discrete points. 52 | """ 53 | const kSize = 6 * 20 + 3 ##### a Monkhorst grid of size N = 6n+3 covers all the High-symemtry points. 54 | bz = BZ([kSize, kSize]) ##### Make the BZ explicitly in 2d 55 | FillBZ!(bz, UC) 56 | 57 | """ 58 | Spanning over different t2/M values 59 | """ 60 | t2s = range(-1.0, 1.0, 51) * M 61 | gaps = Float64[] 62 | mus = Float64[] 63 | Cherns = Float64[] 64 | 65 | 66 | for t2Val in t2s 67 | 68 | push!(t2Param.value, t2Val) 69 | ModifyUnitCell!(UC, [t2Param]) 70 | 71 | """ 72 | Constructing the Hamiltonian in k-space, and diagonalizing it. 73 | """ 74 | H = Hamiltonian(UC, bz) 75 | DiagonalizeHamiltonian!(H) 76 | 77 | """ 78 | Filling the model with fermions at half-filling, with the default temperature T=0.0. 79 | """ 80 | HaldaneModel = Model(UC, bz, H ; filling=0.5) 81 | SolveModel!(HaldaneModel) 82 | push!(mus, HaldaneModel.mu) 83 | push!(gaps, HaldaneModel.gap) 84 | 85 | """ 86 | Then the chern numbers of the lowest two bands (1 and 2) in the Haldane Model becomes : 87 | """ 88 | chern = ChernNumber(H, [1, 2]) 89 | push!(Cherns, chern) 90 | end 91 | 92 | chernPlot = plot(t2s, Cherns, labels="chern", lw=3, markershape = :circle, markercolor=:red ) 93 | 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /Sample/HigherOrderTopo.jl: -------------------------------------------------------------------------------- 1 | using Plots, LinearAlgebra, LaTeXStrings, TightBindingToolkit 2 | 3 | ##### Triangle lattice 4 | const a1 = [ 0.5, sqrt(3)/2] 5 | const a2 = [-0.5, sqrt(3)/2] 6 | 7 | const b1 = [0.0, 0.0] 8 | 9 | HoppingUC = UnitCell([a1, a2], 2, 2) 10 | 11 | AddBasisSite!(HoppingUC, b1) 12 | 13 | SpinVec = SpinMats(1//2) 14 | const t = 1.0 15 | ##### HoppingParams 16 | const t1 = t 17 | t1Param = Param(-t1, 2) 18 | AddIsotropicBonds!(t1Param, HoppingUC, 1.0, SpinVec[3], "s Hopping") 19 | 20 | const Δ1 = 1.5 * t 21 | f1Param = Param(Δ1, 2) 22 | AddAnisotropicBond!(f1Param, HoppingUC, 1, 1, [ 1, 0], -im * SpinVec[2], 1.0, "f_x^3-3xy^2 Hopping") 23 | AddAnisotropicBond!(f1Param, HoppingUC, 1, 1, [ 0, 1], im * SpinVec[2], 1.0, "f_x^3-3xy^2 Hopping") 24 | AddAnisotropicBond!(f1Param, HoppingUC, 1, 1, [ 1, -1], im * SpinVec[2], 1.0, "f_x^3-3xy^2 Hopping") 25 | 26 | const Δ2 = 1.5 * t 27 | f2Param = Param(-Δ2 / (3 * sqrt(3)), 2) 28 | AddAnisotropicBond!(f2Param, HoppingUC, 1, 1, [ 1, 1], im * SpinVec[1], sqrt(3), "f_y^3-3yx^2 Hopping") 29 | AddAnisotropicBond!(f2Param, HoppingUC, 1, 1, [ 2, -1], -im * SpinVec[1], sqrt(3), "f_y^3-3yx^2 Hopping") 30 | AddAnisotropicBond!(f2Param, HoppingUC, 1, 1, [-1, 2], -im * SpinVec[1], sqrt(3), "f_y^3-3yx^2 Hopping") 31 | 32 | const δ = 0.5 * t 33 | dParam = Param((3 * t - δ), 2) 34 | AddIsotropicBonds!(dParam, HoppingUC, 0.0, SpinVec[3], "Oribital gap") 35 | 36 | HoppingParams = [t1Param, f1Param, f2Param, dParam] 37 | CreateUnitCell!(HoppingUC, HoppingParams) 38 | 39 | 40 | const n = 20 41 | const kSize = 6 * n + 3 42 | bz = BZ([kSize, kSize]) 43 | FillBZ!(bz, HoppingUC) 44 | 45 | ##### Thermodynamic parameters 46 | const T = 0.001 47 | const filling = 0.5 48 | const stat = -1 49 | 50 | deltas = collect(LinRange(-0.5, 0.5, 51)) 51 | Cherns = Float64[] 52 | 53 | # H = Hamiltonian(HoppingUC, bz) 54 | # DiagonalizeHamiltonian!(H) 55 | 56 | # M = Model(HoppingUC, bz, H ; filling=0.5) 57 | # SolveModel!(M) 58 | 59 | # Plot_Band_Structure!(M ,[bz.HighSymPoints["G"], bz.HighSymPoints["K1"], bz.HighSymPoints["M2"]] ; labels=[L"$\Gamma$", L"K_1", L"M_2"], nearest = true) 60 | 61 | for delta in deltas 62 | 63 | push!(dParam.value, (3 * t - delta)) 64 | ModifyUnitCell!(HoppingUC, [dParam]) 65 | 66 | """ 67 | Constructing the Hamiltonian in k-space, and diagonalizing it. 68 | """ 69 | H = Hamiltonian(HoppingUC, bz) 70 | DiagonalizeHamiltonian!(H) 71 | 72 | """ 73 | Filling the model with fermions at half-filling, with the default temperature T=0.0. 74 | """ 75 | M = Model(HoppingUC, bz, H ; filling=0.5) 76 | SolveModel!(M) 77 | 78 | """ 79 | Then the chern numbers of the lowest two bands (1 and 2) in the Haldane Model becomes : 80 | """ 81 | chern = ChernNumber(H, [1]) 82 | push!(Cherns, chern) 83 | end 84 | -------------------------------------------------------------------------------- /Sample/Honeycomb_123NN.jl: -------------------------------------------------------------------------------- 1 | using Plots, LaTeXStrings, TightBindingToolkit 2 | 3 | a1 = [ 1/2 , sqrt(3)/2 ] 4 | a2 = [-1/2 , sqrt(3)/2 ] 5 | 6 | b1 = [ 0.0 , 0.0 ] 7 | b2 = [ 0.0 , 1.0/sqrt(3) ] 8 | 9 | const firstNNdistance = 1.0/sqrt(3) 10 | const secondNNdistance = 1.0 11 | const thirdNNdistance = 2/sqrt(3) 12 | 13 | #### Parton Lattice 14 | UC = UnitCell( [a1 , a2] , 2 , 2) 15 | 16 | AddBasisSite!( UC , b1 ) 17 | AddBasisSite!( UC , b2 ) 18 | 19 | #################### Bonds ######################################### 20 | SpinVec = SpinMats(1//2) 21 | ######## NN ################# 22 | const t1 = -1.0 23 | AddIsotropicBonds!(UC , firstNNdistance, t1 * SpinVec[4] , "t1") 24 | ######## 2NN ################# 25 | const t2 = -0.00 * im 26 | AddAnisotropicBond!( UC , 1 , 1 , [ 1 , 0 ] , t2 * SpinVec[3] , secondNNdistance , "t2") 27 | AddAnisotropicBond!( UC , 1 , 1 , [ 0 , -1 ] , t2 * SpinVec[3] , secondNNdistance , "t2") 28 | AddAnisotropicBond!( UC , 1 , 1 , [ -1 , 1 ] , t2 * SpinVec[3] , secondNNdistance , "t2") 29 | AddAnisotropicBond!( UC , 2 , 2 , [ 0 , 1 ] , t2 * SpinVec[3] , secondNNdistance , "t2") 30 | AddAnisotropicBond!( UC , 2 , 2 , [ -1 , 0 ] , t2 * SpinVec[3] , secondNNdistance , "t2") 31 | AddAnisotropicBond!( UC , 2 , 2 , [ 1 , -1 ] , t2 * SpinVec[3] , secondNNdistance , "t2") 32 | ######## 3NN ################# 33 | const t3 = -0.00 34 | AddIsotropicBonds!(UC , thirdNNdistance, t3 * SpinVec[3] , "t3", checkOffsetRange = 3) 35 | # p = plot_UnitCell!(UC ; range=1) 36 | 37 | ##### BZ with size kSize 38 | const n = 100 39 | const kSize = 6 * n 40 | bz = BZ(kSize) 41 | FillBZ!(bz, UC) 42 | 43 | H = Hamiltonian(UC, bz) 44 | DiagonalizeHamiltonian!(H) 45 | 46 | Omegas = collect(range(H.bandwidth..., 501)) 47 | dos = DOS(Omegas, H) 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /Sample/KitaevChain.jl: -------------------------------------------------------------------------------- 1 | using Plots, LaTeXStrings, TightBindingToolkit 2 | 3 | """ 4 | This script sets up the famous Kitaev chain in 1d. 5 | """ 6 | 7 | """ 8 | The lattice primitive vector for a honeycomb lattice : a1 and a2. 9 | """ 10 | a1 = [ 1.0 ] 11 | UC_hop = UnitCell( [a1] , 1 , 2) ##### localDim = 1 cause its a spinless Hamiltonian 12 | UC_pair = UnitCell( [a1] , 1 , 2) ##### localDim = 1 cause its a spinless Hamiltonian 13 | 14 | """ 15 | The single sub-lattice of the chain 16 | """ 17 | b1 = [ 0.0 ] 18 | AddBasisSite!( UC_hop , b1 ) 19 | AddBasisSite!( UC_pair , b1 ) 20 | 21 | """ 22 | Adding structure to the lattice now, through the bond objects. 23 | """ 24 | ################# Nearest Neighbour hopping ################# 25 | const t1 = 1.0 26 | t1Param = Param(t1, 2) 27 | const NNdistance = 1.0 28 | AddIsotropicBonds!(t1Param, UC_hop , NNdistance, 1.0 , "t1") 29 | ################# Nearest Neighbour pairing ################# 30 | const p1 = 0.5 31 | p1Param = Param(p1, 2) 32 | AddIsotropicBonds!(p1Param, UC_pair , NNdistance, 1.0 , "p1") 33 | 34 | CreateUnitCell!(UC_hop , [t1Param]) 35 | CreateUnitCell!(UC_pair, [p1Param]) 36 | 37 | ##### BZ with size kSize 38 | const n = 40 39 | const kSize = 6 * n 40 | bz = BZ(kSize, 1) 41 | FillBZ!(bz, UC_hop) 42 | 43 | ##### Some model parameters 44 | const T = 0.001 45 | const mu = 0.0 46 | ModifyIsotropicFields!(UC_hop, mu, length(UC_hop.fields[begin])) 47 | H = Hamiltonian(UC_hop , UC_pair , bz) 48 | DiagonalizeHamiltonian!(H) 49 | 50 | KitaevModel = BdGModel(UC_hop , UC_pair, bz, H; T=T, mu=mu) 51 | SolveModel!(KitaevModel) 52 | 53 | kPoints = [bz.HighSymPoints["G"], bz.HighSymPoints["M1"]] 54 | 55 | Plot_Band_Structure!(KitaevModel, kPoints ; labels = [L"$\Gamma$", L"$M_1$"]) 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /Sample/Square_lattice.jl: -------------------------------------------------------------------------------- 1 | using LaTeXStrings, Plots, LinearAlgebra, TightBindingToolkit 2 | 3 | """ 4 | The lattice primitive vector for a square lattice : a1 and a2. 5 | """ 6 | a1 = [ 1.0 , 0.0 ] 7 | a2 = [ 0.0 , 1.0 ] 8 | UC = UnitCell( [a1 , a2] , 2, 2) ##### localDim=2 since we are working with spin-1/2 particles now 9 | 10 | """ 11 | Unit cell has only one sub-lattice. 12 | """ 13 | b1 = [ 0.0 , 0.0 ] 14 | AddBasisSite!( UC , b1 ) 15 | 16 | """ 17 | Adding structure to the lattice now, through the bond objects. 18 | """ 19 | SpinVec = SpinMats(1//2) ##### Working with spin-1/2 20 | ################ Nearest Neighbour ################# 21 | const t1 = -1.0 22 | const NNdistance = 1.0 23 | AddIsotropicBonds!(UC , NNdistance, t1 * SpinVec[4] , "t1") 24 | 25 | """ 26 | Now add a second unit cell for pairing 27 | """ 28 | UCp = UnitCell( [a1 , a2] , 2) 29 | AddBasisSite!( UCp , b1 ) 30 | J = 2.0 31 | AddIsotropicBonds!(UCp , NNdistance, J * SpinVec[2] , "p1" ; checkOffsetRange = 1) 32 | 33 | 34 | ##### BZ with size kSize 35 | const n = 40 36 | const kSize = 6 * n + 3 37 | bz = BZ(kSize, 2) 38 | FillBZ!(bz, UC) 39 | 40 | 41 | """ 42 | Some model parameters : temperature and chemical potential 43 | """ 44 | const T = 0.001 45 | const mu = -1.0 46 | # ModifyIsotropicFields!(UC, mu, 4) 47 | H = Hamiltonian(UC , UCp , bz) 48 | DiagonalizeHamiltonian!(H) 49 | 50 | """ 51 | Filling the model at this chemical potential with spin-1/2 fermions. 52 | """ 53 | SquareModel = BdGModel(UC , UCp , bz , H; T=T, mu = mu) 54 | SolveModel!(SquareModel) 55 | 56 | println("The filling at μ = ",SquareModel.mu ," is ", SquareModel.filling) 57 | Plot_UnitCell!(UC) 58 | 59 | 60 | 61 | # plot_band_structure!(SquareModel, [bz.HighSymPoints["G"], bz.HighSymPoints["M1"]] ; labels = [L"$\Gamma$", L"$M_1$"]) 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /Sample/Triangle_123NN.jl: -------------------------------------------------------------------------------- 1 | using Plots, LaTeXStrings#, TightBindingToolkit 2 | include("../src/TightBindingToolkit.jl") 3 | using .TightBindingToolkit 4 | 5 | """ 6 | This script sets up a simple triangular lattice with first, second, and third neighbour hoppings. 7 | It then calculates the magnetic susceptibility at zero energy, along a path in the brillouin zone through the Γ, K, and M points. 8 | """ 9 | 10 | """ 11 | The lattice primitive vector for a triangular lattice : a1 and a2. 12 | """ 13 | a1 = [ 1/2 , sqrt(3)/2 ] 14 | a2 = [-1/2 , sqrt(3)/2 ] 15 | UC = UnitCell( [a1 , a2] , 2) ##### localDim=2 since we are working with spin-1/2 particles now 16 | 17 | """ 18 | Unit cell has only one sub-lattice. 19 | """ 20 | b1 = [ 0.0 , 0.0 ] 21 | AddBasisSite!( UC , b1 ) 22 | 23 | """ 24 | Adding structure to the lattice now, through the bond objects. 25 | """ 26 | SpinVec = SpinMats(1//2) ##### Working with spin-1/2 27 | ################ Nearest Neighbour ################# 28 | const t1 = -1.0 29 | const NNdistance = 1.0 30 | AddIsotropicBonds!(UC , NNdistance, t1 * SpinVec[4] , "t1") 31 | ############### 2nd Nearest Neighbour ################# 32 | const t2 = 0.0 33 | const secondNNdistance = sqrt(3) 34 | AddIsotropicBonds!(UC , secondNNdistance, t2 * SpinVec[4] , "t2" ; checkOffsetRange = 2) 35 | ############### 3rd Nearest Neighbour ################# 36 | const t3 = -0.0 37 | const thirdNNdistance = 2.0 38 | AddIsotropicBonds!(UC , thirdNNdistance, t3 * SpinVec[4] , "t3", checkOffsetRange = 3) 39 | 40 | """ 41 | Interaction on the lattice 42 | """ 43 | JMatrix = zeros(Float64, 4, 4) 44 | JMatrix[1, 1] = 1.0 45 | JMatrix[2, 2] = 1.0 46 | JMatrix[3, 3] = 1.0 47 | 48 | JParam = Param(1.0, 2) 49 | AddIsotropicBonds!(JParam, UC, NNdistance, JMatrix, "NN Heisenberg") 50 | 51 | 52 | 53 | ##### BZ with size kSize 54 | const n = 40 55 | const kSize = 6 * n + 3 56 | bz = BZ(kSize) 57 | FillBZ!(bz, UC) 58 | 59 | """ 60 | Some model parameters : temperature and chemical potential 61 | """ 62 | const T = 0.001 63 | const mu = 1.0 64 | H = Hamiltonian(UC, bz) 65 | DiagonalizeHamiltonian!(H) 66 | p = Plot_UnitCell!(UC ; range=2) 67 | 68 | """ 69 | Filling the model at this chemical potential with spin-1/2 fermions. 70 | """ 71 | TriangleModel = Model(UC, bz, H; T=T, mu=mu) 72 | SolveModel!(TriangleModel) 73 | println("The filling at μ = 0 is ", TriangleModel.filling) 74 | 75 | kPoints = [bz.HighSymPoints["G"], bz.HighSymPoints["K1"], bz.HighSymPoints["M2"]] 76 | """ 77 | A path in the discretized BZ which goes through Γ -> K -> M -> Γ. 78 | """ 79 | path = CombinedBZPath(bz, kPoints ; nearest=true) 80 | 81 | # band_structure = Plot_Band_Structure!(TriangleModel, kPoints ; labels = [L"$\Gamma$", L"$K_1$", L"$M_2$"]) 82 | # FS = Plot_FS!(TriangleModel.Ham , TriangleModel.bz , [TriangleModel.mu] , [1]) 83 | 84 | Omegas = [0.0] 85 | const spread = 5e-3 86 | # dos = DOS(Omegas, H) ##### the single particle density of states 87 | 88 | suscep = Susceptibility(path, Omegas, TriangleModel ; eta=spread) 89 | # FillChis!(suscep, TriangleModel) 90 | FillRPAChis!(suscep, TriangleModel, JParam ; Generators = [[1, 1], [2, 2], [3, 3]]) 91 | ##### Plotting the real part of the magnetic susceptibility at 0 energy, and momentum points along the path. 92 | # plot(real.(suscep.chis["zz"][1, :]), labels="χ_zz(Ω=0, Q)", lw=2.0, markershape=:circle, markercolor="red") 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /Sample/Vortex.jl: -------------------------------------------------------------------------------- 1 | using LinearAlgebra, Plots, LaTeXStrings 2 | 3 | include("../src/TightBindingToolkit.jl") 4 | using .TightBindingToolkit 5 | 6 | a1 = [ 3/2, sqrt(3)/2] 7 | a2 = [-3/2, sqrt(3)/2] 8 | 9 | b1 = [ 0.0 , 0.0 ] 10 | b2 = [ 1/2 , -1/(2 * sqrt(3)) ] 11 | b3 = [ 1/2 , -sqrt(3)/(2) ] 12 | b4 = [ 0.0 , -2/sqrt(3) ] 13 | b5 = [-1/2 , -sqrt(3)/(2) ] 14 | b6 = [-1/2 , -1/(2 * sqrt(3))] 15 | 16 | firstNNdistance = 1.0/sqrt(3) 17 | secondNNdistance = 1.0 18 | thirdNNdistance = 2/sqrt(3) 19 | 20 | UC = UnitCell([a1, a2], 2, 2) 21 | UC.BC = zeros(ComplexF64, 2) 22 | UC.BC = [0.0, 0.0] 23 | 24 | AddBasisSite!.(Ref(UC), [b1, b2, b3, b4, b5, b6]) 25 | 26 | SpinVec = SpinMats(1//2) 27 | 28 | const tIntra = 1.0 29 | const tInter = -10.0 30 | const tInter = -1.5 31 | 32 | tIntraParam = Param(tIntra, 2) 33 | AddAnisotropicBond!(tIntraParam, UC, 1, 2, [ 0, 0], SpinVec[4], firstNNdistance, "Intra hopping") 34 | AddAnisotropicBond!(tIntraParam, UC, 2, 3, [ 0, 0], SpinVec[4], firstNNdistance, "Intra hopping") 35 | AddAnisotropicBond!(tIntraParam, UC, 3, 4, [ 0, 0], SpinVec[4], firstNNdistance, "Intra hopping") 36 | AddAnisotropicBond!(tIntraParam, UC, 4, 5, [ 0, 0], SpinVec[4], firstNNdistance, "Intra hopping") 37 | AddAnisotropicBond!(tIntraParam, UC, 5, 6, [ 0, 0], SpinVec[4], firstNNdistance, "Intra hopping") 38 | AddAnisotropicBond!(tIntraParam, UC, 6, 1, [ 0, 0], SpinVec[4], firstNNdistance, "Intra hopping") 39 | 40 | 41 | tInterParam = Param(tInter, 2) 42 | AddAnisotropicBond!(tInterParam, UC, 1, 4, [ 1, 1], SpinVec[4], firstNNdistance, "Inter hopping") 43 | AddAnisotropicBond!(tInterParam, UC, 3, 6, [ 0, -1], SpinVec[4], firstNNdistance, "Inter hopping") 44 | AddAnisotropicBond!(tInterParam, UC, 5, 2, [-1, 0], SpinVec[4], firstNNdistance, "Inter hopping") 45 | 46 | CreateUnitCell!(UC, [tIntraParam, tInterParam]) 47 | 48 | 49 | ###################### k-Space ################################# 50 | 51 | UCPlot = Plot_UnitCell!(UC ; bond_cmp=:viridis, site_size=10.0, plot_conjugate=false, plot_lattice=true) 52 | 53 | kSize = 6*50 54 | bz = BZ([kSize, kSize]) 55 | FillBZ!(bz, UC) 56 | 57 | H = Hamiltonian(UC, bz) 58 | DiagonalizeHamiltonian!(H) 59 | 60 | VortexModel = Model(UC, bz, H ; T=0.001, mu=0.0) 61 | SolveModel!(VortexModel ; get_gap = true) 62 | 63 | R3 = zeros(Float64, 6, 6) 64 | R3[3, 1]= 1.0 65 | R3[4, 2]= 1.0 66 | R3[5, 3]= 1.0 67 | R3[6, 4]= 1.0 68 | R3[1, 5]= 1.0 69 | R3[2, 6]= 1.0 70 | R3 = kron(R3, Matrix(I, 2, 2)) 71 | 72 | R2 = zeros(Float64, 6, 6) 73 | R2[4, 1]= 1.0 74 | R2[5, 2]= 1.0 75 | R2[6, 3]= 1.0 76 | R2[1, 4]= 1.0 77 | R2[2, 5]= 1.0 78 | R2[3, 6]= 1.0 79 | R2 = kron(R2, Matrix(I, 2, 2)) 80 | 81 | Gammaind= GetQIndex(bz.HighSymPoints["G"], bz) 82 | K1ind = GetQIndex(bz.HighSymPoints["K1"], bz) 83 | K2ind = GetQIndex(bz.HighSymPoints["K2"], bz) 84 | M1ind = GetQIndex(bz.HighSymPoints["-M1"], bz) 85 | M2ind = GetQIndex(bz.HighSymPoints["-M2"], bz) 86 | M3ind = GetQIndex(bz.HighSymPoints["-M3"], bz) 87 | 88 | OrbitalsGamma = H.states[Gammaind...] 89 | OrbitalsK1 = H.states[K1ind...] 90 | OrbitalsK2 = H.states[K2ind...] 91 | OrbitalsM1 = H.states[M1ind...] 92 | OrbitalsM2 = H.states[M2ind...] 93 | OrbitalsM3 = H.states[M3ind...] 94 | 95 | # REigGamma = eigen((OrbitalsGamma' * R3 * OrbitalsGamma)[1:6, 1:6]).values 96 | # REigK1 = eigen((OrbitalsK1' * R3 * OrbitalsK1)[1:6, 1:6]).values 97 | # REigK2 = eigen((OrbitalsK2' * R3 * OrbitalsK2)[1:6, 1:6]).values 98 | 99 | REigGamma = eigen((OrbitalsGamma' * R2 * OrbitalsGamma)[1:6, 1:6]).values 100 | REigM1 = eigen((OrbitalsM1' * R2 * OrbitalsM1)[1:6, 1:6]).values 101 | REigM2 = eigen((OrbitalsM2' * R2 * OrbitalsM2)[1:6, 1:6]).values 102 | REigM3 = eigen((OrbitalsM3' * R2 * OrbitalsM3)[1:6, 1:6]).values 103 | 104 | 105 | # Plot_FS!(H, bz, collect(0.05:0.05:0.5), [7]; cbar=true) 106 | # Plot_Band_Structure!(VortexModel , [bz.HighSymPoints["G"], bz.HighSymPoints["K1"], bz.HighSymPoints["M2"]] ; labels = [L"\Gamma", L"K_1", L"M_2"] ) 107 | 108 | ###################### Real Space ###################################### 109 | 110 | # lattice = Lattice(UC, [6, 6]) 111 | # FillLattice!(lattice) 112 | 113 | # # Plot_Lattice!(lattice ; bond_cmp=:viridis, site_size=8.0) 114 | 115 | # H = LatticeHamiltonian(lattice) 116 | # DiagonalizeHamiltonian!(H) 117 | 118 | # plot(H.bands, marker = :circle, label = "Energies") 119 | Plot_Lattice!(lattice ; bond_cmp=:viridis, site_size=10.0) 120 | 121 | # M = LatticeModel(lattice, H) 122 | 123 | # ######################## Symmetry ########################## 124 | 125 | # Ta1 = Translations(lattice ; primitive = 1, with_local = true) 126 | # Ta2 = Translations(lattice ; primitive = 2, with_local = true) 127 | # Ta1 = Translations(lattice ; primitive = 1, with_local = true) 128 | # Ta2 = Translations(lattice ; primitive = 2, with_local = true) 129 | 130 | # m1 = GetPhase.(FindQuantumNumbers(M, Ta1 ; till_band = length(H.bands)÷2)) 131 | # m2 = GetPhase.(FindQuantumNumbers(M, Ta2 ; till_band = length(H.bands)÷2)) 132 | # m1 = GetPhase.(FindQuantumNumbers(M, Ta1 ; till_band = length(H.bands)÷2)) 133 | # m2 = GetPhase.(FindQuantumNumbers(M, Ta2 ; till_band = length(H.bands)÷2)) 134 | 135 | # mGround = mod.([sum(sum.(m1)), sum(sum.(m2))], Ref(1.0)) 136 | # mGround = mod.([sum(sum.(m1)), sum(sum.(m2))], Ref(1.0)) -------------------------------------------------------------------------------- /docs/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | Documenter = "e30172f5-a6a5-5a46-863b-614d45cd2de4" 3 | 4 | [compat] 5 | Documenter = "0.27, 1" 6 | -------------------------------------------------------------------------------- /docs/assets/Graphene_DOS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/assets/Graphene_DOS.png -------------------------------------------------------------------------------- /docs/assets/Haldane_Chern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/assets/Haldane_Chern.png -------------------------------------------------------------------------------- /docs/assets/Honeycomb_123NN_UC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/assets/Honeycomb_123NN_UC.png -------------------------------------------------------------------------------- /docs/assets/KitaevChain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/assets/KitaevChain.png -------------------------------------------------------------------------------- /docs/assets/Triangle_123NN_FS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/assets/Triangle_123NN_FS.png -------------------------------------------------------------------------------- /docs/assets/Triangle_123NN_UC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/assets/Triangle_123NN_UC.png -------------------------------------------------------------------------------- /docs/assets/Triangle_123NN_bandStructure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/assets/Triangle_123NN_bandStructure.png -------------------------------------------------------------------------------- /docs/assets/Triangle_123NN_chi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/assets/Triangle_123NN_chi.png -------------------------------------------------------------------------------- /docs/make.jl: -------------------------------------------------------------------------------- 1 | using Documenter 2 | using TightBindingToolkit 3 | 4 | makedocs( 5 | build = "build" , 6 | sitename = "TightBindingToolkit.jl" , 7 | modules = [TightBindingToolkit, TightBindingToolkit.UCell, TightBindingToolkit.DesignUCell, TightBindingToolkit.ExpandUCell, TightBindingToolkit.Parameters, TightBindingToolkit.LatticeStruct, TightBindingToolkit.DesignLattice, TightBindingToolkit.BZone, TightBindingToolkit.Hams, TightBindingToolkit.TBModel, TightBindingToolkit.Chern, TightBindingToolkit.suscep, TightBindingToolkit.conduct] , 8 | pages = [ 9 | "Introduction" => "index.md", 10 | "Unit Cell" => "UnitCell.md", 11 | "Parameters" => "Params.md", 12 | "Lattice" => "Lattice.md", 13 | "Brillouin Zone" => "BZ.md", 14 | "Hamiltonian" => "Hamiltonian.md", 15 | "Tight Binding Model" => "Model.md", 16 | "BdG Model" => "BdGModel.md", 17 | "Chern Numbers" => "Chern.md", 18 | "Magnetic susceptibility" => "susceptibility.md" , 19 | "Electrical Conductivity" => "conductivity.md", 20 | "Plotting" => "Plot.md" 21 | ] 22 | ) 23 | 24 | deploydocs( 25 | repo = "github.com/Anjishnubose/TightBindingToolkit.jl.git", 26 | devbranch = "main" 27 | ) -------------------------------------------------------------------------------- /docs/src/BZ.md: -------------------------------------------------------------------------------- 1 | # TightBindingToolkit.BZ 2 | 3 | ```@autodocs 4 | Modules = [TightBindingToolkit, TightBindingToolkit.BZone] 5 | Private = false 6 | Pages = ["BZ.jl"] 7 | ``` -------------------------------------------------------------------------------- /docs/src/BdGModel.md: -------------------------------------------------------------------------------- 1 | # TightBindingToolkit.BdGModel 2 | 3 | ```@autodocs 4 | Modules = [TightBindingToolkit, TightBindingToolkit.BdG] 5 | Private = false 6 | Pages = ["BdGModel.jl"] 7 | ``` -------------------------------------------------------------------------------- /docs/src/Chern.md: -------------------------------------------------------------------------------- 1 | # TightBindingToolkit.Chern 2 | 3 | ```@autodocs 4 | Modules = [TightBindingToolkit, TightBindingToolkit.Chern] 5 | Private = false 6 | Pages = ["Chern.jl"] 7 | ``` -------------------------------------------------------------------------------- /docs/src/Figures/Graphene_DOS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/src/Figures/Graphene_DOS.png -------------------------------------------------------------------------------- /docs/src/Figures/Haldane_Chern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/src/Figures/Haldane_Chern.png -------------------------------------------------------------------------------- /docs/src/Figures/Honeycomb_123NN_UC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/src/Figures/Honeycomb_123NN_UC.png -------------------------------------------------------------------------------- /docs/src/Figures/KitaevChain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/src/Figures/KitaevChain.png -------------------------------------------------------------------------------- /docs/src/Figures/Triangle_123NN_FS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/src/Figures/Triangle_123NN_FS.png -------------------------------------------------------------------------------- /docs/src/Figures/Triangle_123NN_UC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/src/Figures/Triangle_123NN_UC.png -------------------------------------------------------------------------------- /docs/src/Figures/Triangle_123NN_bandStructure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/src/Figures/Triangle_123NN_bandStructure.png -------------------------------------------------------------------------------- /docs/src/Figures/Triangle_123NN_chi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/src/Figures/Triangle_123NN_chi.png -------------------------------------------------------------------------------- /docs/src/Hamiltonian.md: -------------------------------------------------------------------------------- 1 | # TightBindingToolkit.Hamiltonian 2 | 3 | ```@autodocs 4 | Modules = [TightBindingToolkit, TightBindingToolkit.Hams] 5 | Private = false 6 | Pages = ["Hamiltonian.jl"] 7 | ``` -------------------------------------------------------------------------------- /docs/src/Lattice.md: -------------------------------------------------------------------------------- 1 | # TightBindingToolkit.Lattice 2 | 3 | ```@autodocs 4 | Modules = [TightBindingToolkit, TightBindingToolkit.LatticeStruct, TightBindingToolkit.DesignLattice] 5 | Private = false 6 | Pages = ["Lattice.jl", "DesignLattice.jl"] 7 | ``` -------------------------------------------------------------------------------- /docs/src/Model.md: -------------------------------------------------------------------------------- 1 | # TightBindingToolkit.Model 2 | 3 | ```@autodocs 4 | Modules = [TightBindingToolkit, TightBindingToolkit.TBModel] 5 | Private = false 6 | Pages = ["Model.jl"] 7 | ``` -------------------------------------------------------------------------------- /docs/src/Params.md: -------------------------------------------------------------------------------- 1 | # TightBindingToolkit.Params 2 | 3 | ```@autodocs 4 | Modules = [TightBindingToolkit, TightBindingToolkit.Parameters] 5 | Private = false 6 | Pages = ["Params.jl"] 7 | ``` -------------------------------------------------------------------------------- /docs/src/Plot.md: -------------------------------------------------------------------------------- 1 | # TightBindingToolkit.Model 2 | 3 | ```@autodocs 4 | Modules = [TightBindingToolkit, TightBindingToolkit.PlotTB] 5 | Private = false 6 | Pages = ["Plot.jl"] 7 | ``` -------------------------------------------------------------------------------- /docs/src/UnitCell.md: -------------------------------------------------------------------------------- 1 | # TightBindingToolkit.UnitCell 2 | 3 | ```@autodocs 4 | Modules = [TightBindingToolkit, TightBindingToolkit.UCell, TightBindingToolkit.DesignUCell, TightBindingToolkit.ExpandUCell] 5 | Private = false 6 | Pages = ["UnitCell.jl", "DesignUnitCell.jl", "ExpandUnitCell.jl"] 7 | ``` -------------------------------------------------------------------------------- /docs/src/assets/Graphene_DOS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/src/assets/Graphene_DOS.png -------------------------------------------------------------------------------- /docs/src/assets/Haldane_Chern.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/src/assets/Haldane_Chern.png -------------------------------------------------------------------------------- /docs/src/assets/Honeycomb_123NN_UC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/src/assets/Honeycomb_123NN_UC.png -------------------------------------------------------------------------------- /docs/src/assets/KitaevChain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/src/assets/KitaevChain.png -------------------------------------------------------------------------------- /docs/src/assets/Triangle_123NN_FS.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/src/assets/Triangle_123NN_FS.png -------------------------------------------------------------------------------- /docs/src/assets/Triangle_123NN_UC.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/src/assets/Triangle_123NN_UC.png -------------------------------------------------------------------------------- /docs/src/assets/Triangle_123NN_bandStructure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/src/assets/Triangle_123NN_bandStructure.png -------------------------------------------------------------------------------- /docs/src/assets/Triangle_123NN_chi.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Anjishnubose/TightBindingToolkit.jl/9249751057435c940c6597e0467a4e365b47349d/docs/src/assets/Triangle_123NN_chi.png -------------------------------------------------------------------------------- /docs/src/conductivity.md: -------------------------------------------------------------------------------- 1 | # TightBindingToolkit.conduct 2 | 3 | ```@autodocs 4 | Modules = [TightBindingToolkit, TightBindingToolkit.conduct] 5 | Private = false 6 | Pages = ["Conductivity.jl"] 7 | ``` -------------------------------------------------------------------------------- /docs/src/index.md: -------------------------------------------------------------------------------- 1 | # TightBindingTookit.jl 2 | 3 | TightBindingToolkit.jl is a Julia package meant for constructing, and obtaining useful properties of generic tight-binding models. It supports any lattice structure, with any user-defined bonds on that lattice. It also has support for any spin of the particle hopping on the lattice. 4 | 5 | Currently supported : 6 | * Custom Unit Cell Construction. (v1.3) : now also supports construction in dimensions upto d=3. 7 | |![Alt text](./assets/Honeycomb_123NN_UC.png)| 8 | |*Honeycomb Model with 1st, 2nd and 3rd neighbour hoppings.*| 9 | * Corresponding Brillouin Zone Construction. (v1.3) : now also supports construction in dimensions upto d=3. 10 | * Hamiltonian, given a Unit Cell and a Brillouin Zone. (v1.3) : now also support BdG Hamiltonians. 11 | * Diagonalizing the Hamiltonian in momentum space to get band structures and wavefunctions. 12 | |![Alt text](./assets/Triangle_123NN_bandStructure.png)| 13 | |:--:| 14 | |*Band structure for a triangular lattice with 1st, 2nd and 3rd neighbour hopping.*| 15 | * Density of State 16 | |![Alt text](./assets/Graphene_DOS.png)| 17 | |:--:| 18 | |*Density of state of graphene.*| 19 | * Filling the model at given chemical potential, and calculating gaps. (v1.3) : also supported for BdG systems. 20 | |![Alt text](./assets/KitaevChain.png)| 21 | |:--:| 22 | |*Band structure of the Kitaev Chain in 1-d.*| 23 | * Fermi surfaces of systems 24 | |![Alt text](./assets/Triangle_123NN_FS.png)| 25 | |:--:| 26 | |*Fermi surface for a triangular lattice with 1st, 2nd and 3rd neighbour hopping.*| 27 | * Getting correlation functions. (v1.3) : also supported for BdG systems. 28 | * Getting Berry curvature and Chern numbers. 29 | |![Alt text](./assets/Haldane_Chern.png)| 30 | |:--:| 31 | |*Chern numbers in a spin-ful Haldane model as a function of t2.*| 32 | * Getting magnetic susceptibility in any direction, at any momentum, and energy. 33 | |![Alt text](./assets/Triangle_123NN_chi.png)| 34 | |:--:| 35 | |*imaginary part of zero-energy susceptibility for a triangular lattice with 1st, 2nd and 3rd neighbour hopping.*| 36 | -------------------------------------------------------------------------------- /docs/src/susceptibility.md: -------------------------------------------------------------------------------- 1 | # TightBindingToolkit.susceptibility 2 | 3 | ```@autodocs 4 | Modules = [TightBindingToolkit, TightBindingToolkit.suscep] 5 | Private = false 6 | Pages = ["Susceptibility.jl"] 7 | ``` -------------------------------------------------------------------------------- /src/BZ.jl: -------------------------------------------------------------------------------- 1 | module BZone 2 | export GetRLVs , BZ , Monkhorst , FillBZ! , ReduceQ , GetQIndex , CombinedIndexPath , GetBZPath , CombinedBZPath, MomentumPhaseFFT 3 | 4 | using LinearAlgebra, Logging 5 | 6 | using ..TightBindingToolkit.Useful: Meshgrid, VecAngle, GetIndexPath 7 | using ..TightBindingToolkit.UCell: UnitCell 8 | 9 | 10 | @doc """ 11 | ```julia 12 | GetRLVs( uc::UnitCell ) --> Vector{Vector{Float64}} 13 | ``` 14 | Returns the reciprocal lattice vectors corresponding to the given Unit Cell. 15 | 16 | """ 17 | function GetRLVs( uc::UnitCell ) :: Vector{Vector{Float64}} 18 | if length(uc.primitives) == 1 19 | a1 = uc.primitives[1] 20 | return [(2 *pi / dot(a1, a1)) * a1] 21 | 22 | elseif length(uc.primitives) == 2 23 | d = length(uc.primitives[begin]) 24 | 25 | if d==2 26 | a1 = vcat( uc.primitives[1] , 0.0 ) 27 | a2 = vcat( uc.primitives[2] , 0.0 ) 28 | end 29 | 30 | a3 = cross(a1, a2) 31 | V = dot( a1 , cross( a2 , a3 ) ) 32 | b1 = 2 * pi / V * ( cross( a2 , a3 ) ) 33 | b2 = 2 * pi / V * ( cross( a3 , a1 ) ) 34 | return [ b1[1:d] , b2[1:d] ] 35 | 36 | elseif length(uc.primitives) == 3 37 | V = dot( uc.primitives[1] , cross( uc.primitives[2] , uc.primitives[3] ) ) 38 | b1 = 2 * pi / V * ( cross( uc.primitives[2] , uc.primitives[3] ) ) 39 | b2 = 2 * pi / V * ( cross( uc.primitives[3] , uc.primitives[1] ) ) 40 | b3 = 2 * pi / V * ( cross( uc.primitives[1] , uc.primitives[2] ) ) 41 | return [ b1 , b2 , b3 ] 42 | 43 | else 44 | @warn "Getting reciprocal lattice vectors only works for upto d=3 lattices right now." 45 | end 46 | end 47 | 48 | @doc """ 49 | `BZ` is a data type representing a discretized Brillouin Zone in momentum space. 50 | 51 | # Attributes 52 | - `basis :: Vector{ Vector{ Float64 } }`: reciprocal lattice vectors of the Brillouin Zone. 53 | - `gridSize :: Vector{Int64}`: The number of points along each dimension of the grid. 54 | - `kInds :: Vector{Array{Float64}}`: the [`Monkhorst`](@ref) grid corresponding to k along bs. 55 | - `ks :: Array{Vector{Float64}}`: The grid of momentum vectors k. 56 | - `HighSymPoints :: Dict`: A dictionary containing the HIgh-Symmetry points Γ, K(2), and M(3). 57 | - `shift :: Vector{Int64}` : how shifted the grid is from its centre point at the Γ point, in units of `1/gridSize`. 58 | 59 | Initialize this structure using 60 | ```julia 61 | BZ(gridSize::Int64) --> defaults to dims = 2 62 | BZ(gridSize::Int64, dims) --> Uniform grid in dimension = dims 63 | BZ(gridSize::Vector{Int64}) 64 | ``` 65 | """ 66 | mutable struct BZ 67 | basis :: Vector{Vector{Float64}} 68 | gridSize :: Vector{Int64} 69 | kInds :: Vector{Array{Float64}} 70 | ks :: Array{Vector{Float64}} 71 | HighSymPoints :: Dict 72 | shift :: Vector{Int64} 73 | 74 | BZ(gridSize::Vector{Int64}) = new{}(Vector{Float64}[], gridSize, Array{Float64, length(gridSize)}[], Array{Vector{Float64}}(undef, zeros(Int64, length(gridSize))...), Dict(), Int64[]) 75 | BZ(gridSize::Int64, dims::Int64) = new{}(Vector{Float64}[], repeat([gridSize], dims), Array{Float64, dims}[], Array{Vector{Float64}}(undef, zeros(Int64, dims)...), Dict(), Int64[]) 76 | 77 | function BZ(gridSize::Int64) 78 | @warn "Positional argument `dims' not passed in BZ. Resorting to its default value of 2." 79 | dims = 2 80 | return new{}(Vector{Float64}[], repeat([gridSize], dims), Array{Float64, dims}[], Array{Vector{Float64}}(undef, zeros(Int64, dims)...), Dict(), Int64[]) 81 | end 82 | end 83 | 84 | 85 | @doc raw""" 86 | ```julia 87 | Monkhorst(ind::Int64, N::Int64) --> Float64 88 | Monkhorst(ind::Int64, N::Int64, shift::Int64, BC::Float64) --> Float64 89 | ``` 90 | The usual Monkhorst grid is defined as follows 91 | `` [\frac{2i - (N+1)}{2N}, i∈[1, N]] `` 92 | The modified Monkhorst grid takes into account the desired boundary condition `BC`, and an integer `shift` (if required, to change the starting point), and shifts the momentum grid accordingly. 93 | 94 | """ 95 | function Monkhorst(ind::Int64, N::Int64) :: Float64 96 | return (2 * ind - (N + 1)) / (2 * N) 97 | end 98 | 99 | function Monkhorst(ind::Int64, N::Int64, shift::Int64, BC::Float64) :: Float64 100 | return (1 / N) * (ind + shift - ((N + 2 - (N % 2)) / 2) + (BC / (2 * pi))) 101 | end 102 | 103 | 104 | ##### Adds high symmetry points to the bz dictionary 105 | function AddHighSymPoints!(bz::BZ) 106 | dims = length(bz.basis) 107 | 108 | ##### Gamma point at the origin. 109 | bz.HighSymPoints["G"] = zeros(Float64, dims) 110 | 111 | if dims == 1 ##### 1d BZ 112 | bz.HighSymPoints["M1"] = @. 0.5 * bz.basis[1] 113 | bz.HighSymPoints["-M1"] = @. -0.5 * bz.basis[1] ##### M point at the midpoint of the BZ (pi) 114 | bz.HighSymPoints["K1"] = @. (1/3) * bz.basis[1] ##### The two K points at pi/3 and 2pi/3 115 | bz.HighSymPoints["K2"] = @. (2/3) * bz.basis[1] ##### 116 | bz.HighSymPoints["K1"] = @. (1/3) * bz.basis[1] ##### The two K points at pi/3 and 2pi/3 117 | bz.HighSymPoints["K2"] = @. (2/3) * bz.basis[1] 118 | 119 | elseif dims == 2 ##### 2d BZ 120 | 121 | bz.HighSymPoints["M1"] = @. 0.5 * bz.basis[1] + 0.0 * bz.basis[2] ##### The 3 possible M points 122 | bz.HighSymPoints["M2"] = @. 0.0 * bz.basis[1] + 0.5 * bz.basis[2] 123 | bz.HighSymPoints["M3"] = @. 0.5 * bz.basis[1] + 0.5 * bz.basis[2] 124 | bz.HighSymPoints["-M1"] = -(@. 0.5 * bz.basis[1] + 0.0 * bz.basis[2]) ##### The 3 possible M points 125 | bz.HighSymPoints["-M2"] = -(@. 0.0 * bz.basis[1] + 0.5 * bz.basis[2]) 126 | bz.HighSymPoints["-M3"] = -(@. 0.5 * bz.basis[1] + 0.5 * bz.basis[2]) 127 | 128 | if isapprox(VecAngle(bz.basis[1], bz.basis[2]), 2*pi/3, atol=1e-4, rtol=1e-4) ##### The K points depend on the relative angle of the reciprocal basis. 129 | bz.HighSymPoints["K1"] = @. (2/3) * bz.basis[1] + (1/3) * bz.basis[2] 130 | bz.HighSymPoints["K2"] = @. (1/3) * bz.basis[1] + (2/3) * bz.basis[2] 131 | bz.HighSymPoints["K1'"] = @. (-1/3) * bz.basis[1] + (1/3) * bz.basis[2] 132 | bz.HighSymPoints["K2'"] = @. (-2/3) * bz.basis[1] + (-1/3) * bz.basis[2] 133 | bz.HighSymPoints["K1''"] = @. (-1/3) * bz.basis[1] + (-2/3) * bz.basis[2] 134 | bz.HighSymPoints["K2''"] = @. (1/3) * bz.basis[1] + (-1/3) * bz.basis[2] 135 | 136 | elseif isapprox(VecAngle(bz.basis[1], bz.basis[2]), pi/3, atol=1e-4, rtol=1e-4) 137 | bz.HighSymPoints["K1"] = @. (1/3) * bz.basis[1] + (1/3) * bz.basis[2] 138 | bz.HighSymPoints["K2"] = @. (-1/3) * bz.basis[1] + (-1/3) * bz.basis[2] 139 | bz.HighSymPoints["K1'"] = @. (-2/3) * bz.basis[1] + (1/3) * bz.basis[2] 140 | bz.HighSymPoints["K2'"] = @. (2/3) * bz.basis[1] + (-1/3) * bz.basis[2] 141 | bz.HighSymPoints["K1''"] = @. (1/3) * bz.basis[1] + (-2/3) * bz.basis[2] 142 | bz.HighSymPoints["K2''"] = @. (-1/3) * bz.basis[1] + (2/3) * bz.basis[2] 143 | end 144 | 145 | end 146 | 147 | end 148 | 149 | @doc """ 150 | ```julia 151 | FillBZ!(bz::BZ, uc::UnitCell ; shift::Vector{Float64}=zeros(Float64, length(uc.primitives))) 152 | ``` 153 | Fills the `BZ` object with the relevant attributes, after it has been initialized with `BZ(gridSize)`. 154 | 155 | """ 156 | function FillBZ!(bz::BZ, uc::UnitCell; shift::Vector{Int64}=zeros(Int64, length(uc.primitives))) 157 | 158 | bz.basis = GetRLVs(uc) ##### Get the reciprocal basis 159 | bz.shift = shift 160 | dims = length(uc.primitives) 161 | 162 | @assert length(bz.gridSize) == dims "Inconsistent dimensions of UnitCell and BZ" 163 | @assert length(bz.basis)<=3 "Sorry, the code is only written for upto 3 dimensions right now!" 164 | 165 | inds = Meshgrid(bz.gridSize) 166 | 167 | ##### Getting the Meshgrid of Monkhorst coefficients of the BZ grid 168 | for dim in 1:dims 169 | index = getindex.(inds, dim) 170 | push!(bz.kInds, Monkhorst.(index, Ref(bz.gridSize[dim]), Ref(shift[dim]), Ref(angle(uc.BC[dim])))) 171 | end 172 | 173 | bz.ks = (bz.kInds[1] .* Ref(bz.basis[1])) 174 | for dim in 2:dims 175 | bz.ks .+= (bz.kInds[dim] .* Ref(bz.basis[dim])) 176 | end 177 | 178 | AddHighSymPoints!(bz) 179 | 180 | end 181 | 182 | 183 | @doc """ 184 | ```julia 185 | ReduceQ(Q::Vector{Float64}, bz::BZ) --> Vector{Float64} 186 | ``` 187 | Reduces a given momentum back to the range covered by the discretized Brillouin Zone. 188 | 189 | """ 190 | function ReduceQ(Q::Vector{Float64}, bz::BZ) :: Vector{Float64} 191 | 192 | U = reduce(hcat, bz.basis) ##### basis transformation from x, y -> b1, b2 193 | Q_reduced = inv(U) * (Q) ##### Q in the basis of b1 and b2 194 | Q_reduced = Q_reduced .- round.(Q_reduced .- (bz.shift ./ bz.gridSize)) ##### Q in the basis of b1 and b2, and shifted back to the first BZ 195 | Q_reduced = sum((Q_reduced) .* bz.basis) ##### Q back in the x ,y basis, but shifted to the first BZ 196 | return Q_reduced 197 | end 198 | 199 | 200 | @doc """ 201 | ```julia 202 | GetQIndex(Q::Vector{Float64}, bz::BZ ; nearest::Bool = false) --> Vector{Int64} 203 | ``` 204 | Returns the index in the discretized `BZ` of the momentum point corresponding to the fiven momentum `Q`. 205 | If the input `nearest` is set to `true`, will return the index of the momentum point on the grid closest to `Q`, if `Q` does not exist on the grid. 206 | 207 | """ 208 | function GetQIndex(Q::Vector{Float64}, bz::BZ ; nearest::Bool = false) :: Vector{Int64} 209 | 210 | Q_reduced = ReduceQ(Q, bz) 211 | 212 | if !nearest 213 | inds = findfirst(isapprox(Q_reduced, rtol=1e-5, atol=1e-5), bz.ks) 214 | @assert !isnothing(inds) Q_reduced, "Given momentum does not exist on the BZ grid" 215 | else 216 | inds = findmin(norm.(bz.ks .- Ref(Q_reduced)))[2] 217 | end 218 | 219 | return collect(Tuple(inds)) 220 | end 221 | 222 | 223 | @doc """ 224 | ```julia 225 | CombinedIndexPath(bz::BZ, points::Vector{Vector{Float64}} ; nearest::Bool = false, closed::Bool = true) --> Vector{Vector{Int64}} 226 | ``` 227 | Returns a path in index-space joins the given points present in `points` as point[1] --> point[2] --> ... --> point[end] --> point[1]. 228 | The optional input `nearest` is the same as in [`GetQIndex`](@ref), and `closed` determines if the path is a closed loop or not. 229 | 230 | """ 231 | function CombinedIndexPath(bz::BZ, points::Vector{Vector{Float64}} ; nearest::Bool = false, closed::Bool = true) :: Vector{Vector{Int64}} 232 | 233 | if closed 234 | starts = copy(points) 235 | endings = circshift(starts, -1) 236 | else 237 | starts = copy(points[1:end-1]) 238 | endings = copy(points[2:end]) 239 | end 240 | 241 | paths = GetIndexPath.(GetQIndex.(starts, Ref(bz) ; nearest = nearest), GetQIndex.(endings, Ref(bz), nearest=nearest) ; exclusive = true) 242 | paths = reduce(vcat, paths) 243 | 244 | return paths 245 | end 246 | 247 | 248 | @doc """ 249 | ```julia 250 | GetBZPath(bz::BZ, start::Vector{Float64}, ending::Vector{Float64} ; nearest::Bool = false, exclusive::Bool = true) --> Vector{Vector{Float64}} 251 | ``` 252 | Returns the actual path in momentum-space of the discretized `BZ` which joins the two momentums `start` and `ending`. 253 | The optional input `nearest` is the same as in [`GetQIndex`](@ref), and `exclusive` is the same as in [`GetIndexPath`](@ref). 254 | 255 | """ 256 | function GetBZPath(bz::BZ, start::Vector{Float64}, ending::Vector{Float64} ; nearest::Bool = false, exclusive::Bool = true) :: Vector{Vector{Float64}} 257 | 258 | start_ind = GetQIndex(start, bz ; nearest=nearest) 259 | end_ind = GetQIndex(ending, bz ; nearest=nearest) 260 | path_ind = GetIndexPath(start_ind, end_ind ; exclusive = exclusive) 261 | 262 | return getindex.(Ref(bz.ks), CartesianIndex.(Tuple.(path_ind))) 263 | end 264 | 265 | 266 | @doc """ 267 | ```julia 268 | CombinedBZPath(bz::BZ, points::Vector{Vector{Float64}} ; nearest::Bool = false, closed::Bool = true) --> Vector{Vector{Float64}} 269 | ``` 270 | Returns a path in momentum-space of the discretized `BZ` which joins the given momentum points present in `points` as point[1] --> point[2] --> ... --> point[end] --> point[1]. 271 | The optional input `nearest` is the same as in [`GetQIndex`](@ref), and `closed` determines if the path is a closed loop or not. 272 | 273 | """ 274 | function CombinedBZPath(bz::BZ, points::Vector{Vector{Float64}} ; nearest::Bool = false, closed::Bool = true) :: Vector{Vector{Float64}} 275 | 276 | if closed 277 | starts = copy(points) 278 | endings = circshift(starts, -1) 279 | else 280 | starts = copy(points[1:end-1]) 281 | endings = copy(points[2:end]) 282 | end 283 | 284 | paths = GetBZPath.(Ref(bz), starts, endings ; nearest = nearest , exclusive = true) 285 | paths = reduce(vcat, paths) 286 | 287 | return paths 288 | end 289 | 290 | 291 | ##### Momentum pahse factors needed when doing FFT 292 | function MomentumPhaseFFT(bz::BZ, uc::UnitCell) 293 | 294 | momentumShift = (2 * pi) .* (bz.shift .+ Ref(1) .- ((bz.gridSize .+ 2 .- (bz.gridSize .% 2)) / 2) + (angle.(uc.BC) / (2 * pi))) ./ (bz.gridSize) 295 | grid = collect.(Meshgrid(bz.gridSize)) .- Ref(ones(Int64, length(bz.gridSize))) 296 | phaseShift = exp.(-im .* dot.(grid, Ref(momentumShift))) 297 | 298 | return phaseShift 299 | end 300 | 301 | end -------------------------------------------------------------------------------- /src/BdGModel.jl: -------------------------------------------------------------------------------- 1 | module BdG 2 | export BdGModel, FindFilling, GetMu!, GetFilling!, GetGk!, GetGr!, SolveModel!, GetGap!, FreeEnergy 3 | 4 | using ..TightBindingToolkit.Useful: DistFunction, DeriDistFunction, BinarySearch, FFTArrayofMatrix 5 | using ..TightBindingToolkit.UCell: UnitCell 6 | using ..TightBindingToolkit.DesignUCell: ModifyIsotropicFields! 7 | using ..TightBindingToolkit.BZone:BZ, MomentumPhaseFFT 8 | using ..TightBindingToolkit.Hams:Hamiltonian, ModifyHamiltonianField! 9 | 10 | using LinearAlgebra, Tullio, TensorCast, Logging, Statistics 11 | 12 | import ..TightBindingToolkit.TBModel:FindFilling, GetMu!, GetFilling!, GetGk!, GetGr!, SolveModel!, GetGap!, FreeEnergy 13 | 14 | @doc """ 15 | `BdGModel` is a data type representing a general Tight Binding system with pairing. 16 | 17 | # Attributes 18 | - `uc_hop :: UnitCell`: the Unit cell of the lattice with the hoppings. 19 | - `uc_pair :: UnitCell`: the Unit cell of the lattice with the pairings. 20 | - `bz :: BZ`: The discretized Brillouin Zone. 21 | - `Ham :: Hamiltonian`: the Hamiltonian at all momentum-points. 22 | - `T :: Float64`: the temperature of the system. 23 | - `filling :: Float64`: The filling of the system. 24 | - `mu :: Float64`: The chemical potential of the system. 25 | - `stat :: Int64` : ±1 for bosons and fermions. 26 | - `gap :: Float64` : the energy gap of excitations at the given filling. 27 | - `Gk :: Array{Matrix{ComplexF64}}` : An Array (corresponding to the grid of k-points in `BZ`) of Greens functions. 28 | - `Fk :: Array{Matrix{ComplexF64}}` : An Array (corresponding to the grid of k-points in `BZ`) of anomalous Greens functions. 29 | 30 | Initialize this structure using 31 | ```julia 32 | BdGModel(uc_hop::UnitCell, uc_pair::UnitCell, bz::BZ, Ham::Hamiltonian ; T::Float64=1e-3, filling::Float64=-1.0, mu::Float64=0.0, stat::Int64=-1) 33 | ``` 34 | You can either input a filling, or a chemical potential. The corresponding μ for a given filling, or filling for a given μ is automatically calculated. 35 | """ 36 | mutable struct BdGModel 37 | uc_hop :: UnitCell 38 | uc_pair :: UnitCell 39 | bz :: BZ 40 | Ham :: Hamiltonian 41 | """ 42 | Thermodynamic properties 43 | """ 44 | T :: Float64 ##### Temperature 45 | filling :: Float64 ##### Filling fraction 46 | mu :: Float64 ##### Chemical potential 47 | gap :: Float64 48 | stat :: Int64 ##### +1 for Boson, -1 for Fermions 49 | """ 50 | Correlations 51 | """ 52 | Gk :: Array{Matrix{ComplexF64}} 53 | Fk :: Array{Matrix{ComplexF64}} 54 | Gr :: Array{Matrix{ComplexF64}} 55 | Fr :: Array{Matrix{ComplexF64}} 56 | 57 | BdGModel(uc_hop::UnitCell, uc_pair::UnitCell, bz::BZ, Ham::Hamiltonian ; T::Float64=1e-3, filling::Float64=-1.0, mu::Float64=0.0, stat::Int64=-1) = new{}(uc_hop, uc_pair, bz, Ham, T, filling, mu, -999.0, stat ,Array{Matrix{ComplexF64}}(undef, zeros(Int64, length(uc_hop.primitives))...), Array{Matrix{ComplexF64}}(undef, zeros(Int64, length(uc_hop.primitives))...), Array{Matrix{ComplexF64}}(undef, zeros(Int64, length(uc_hop.primitives))...),Array{Matrix{ComplexF64}}(undef, zeros(Int64, length(uc_hop.primitives))...)) 58 | ##### Chosen the default value of filling to be -1 which is unphysical so that the code knows when filling has not been provided and has to be calculated from mu instead! 59 | end 60 | 61 | 62 | @doc """ 63 | ```julia 64 | FindFilling(M::BdGModel) --> Float64 65 | FindFilling(mu::Float64, M::BdGModel) 66 | ``` 67 | Find filling at given temperature and chemical potential that takes BdGModel object as argument instead of Hamiltonian, since for a BdG case, the filling depends on the wavefunctions also. 68 | Because of this, if you want to calculate the filling at a different chemical potential, you have to modify the Hamiltonian, the UnitCells, rediagonalize, and recalculate eveyrthing. 69 | 70 | """ 71 | function FindFilling(M::BdGModel) :: Float64 72 | @assert M.Ham.is_BdG==true "Use other format for pure hopping Hamiltonian" 73 | 74 | N = length(M.Ham.bands[begin]) 75 | Eks = reshape(getindex.(M.Ham.bands, Ref(1:N÷2)), prod(M.bz.gridSize)) 76 | U11s = reshape(getindex.(M.Ham.states, Ref(1:N÷2), Ref(1:N÷2)), prod(M.bz.gridSize)) 77 | U21s = reshape(getindex.(M.Ham.states, Ref(N÷2 + 1: N), Ref(1:N÷2)), prod(M.bz.gridSize)) 78 | U12s = reshape(getindex.(M.Ham.states, Ref(1:N÷2), Ref(N÷2 + 1: N)) , prod(M.bz.gridSize)) 79 | U22s = reshape(getindex.(M.Ham.states, Ref(N÷2 + 1: N), Ref(N÷2 + 1: N)) , prod(M.bz.gridSize)) 80 | 81 | nFs = DistFunction.(Eks; T=M.T, mu=0.0, stat=M.stat) 82 | 83 | @tullio filling := ((conj(U11s[k1][i, j]) * U11s[k1][i, j] * nFs[k1][j]) 84 | + (conj(U21s[k1][i, j]) * U21s[k1][i, j] * (1 - nFs[k1][j]))) 85 | 86 | return real(filling) / (prod(M.bz.gridSize) * M.uc_hop.localDim * length(M.uc_hop.basis)) 87 | end 88 | 89 | function FindFilling(mu::Float64, M::BdGModel) :: Float64 90 | ModifyHamiltonianField!(M.Ham, M.uc_hop, repeat([mu], outer=length(M.uc_hop.basis))) 91 | return FindFilling(M) 92 | end 93 | 94 | 95 | """ 96 | ```julia 97 | GetFilling!(M::BdGModel) 98 | ``` 99 | Function to get filling given a chemical potential! 100 | 101 | """ 102 | function GetFilling!(M::BdGModel) 103 | M.filling = FindFilling(M.mu, M) 104 | end 105 | 106 | 107 | """ 108 | ```julia 109 | GetMu!(M::BdGModel ; tol::Float64=0.001) 110 | ``` 111 | Function to get the correct chemical potential given a filling. 112 | """ 113 | function GetMu!(M::BdGModel ; guess::Float64 = 0.0, mu_tol::Float64 = 0.001, filling_tol::Float64 = 1e-6) 114 | M.mu = BinarySearch(M.filling, Tuple(2*collect(M.Ham.bandwidth)), FindFilling, (M,) ; initial = guess, x_tol = mu_tol, target_tol = filling_tol) 115 | @info "Found chemical potential μ = $(M.mu) for given filling = $(M.filling)." 116 | 117 | end 118 | 119 | 120 | """ 121 | ```julia 122 | GetGk!(M::BdGModel) 123 | ``` 124 | Finding the Greens functions, and anomalous greens functions in momentum space at some chemical potential. 125 | """ 126 | function GetGk!(M::BdGModel) 127 | 128 | N = length(M.Ham.bands[begin]) 129 | Eks = reshape(getindex.(M.Ham.bands, Ref(1:N÷2)) , prod(M.bz.gridSize)) ##### Only the negative energies from the bdG spectrum 130 | 131 | U11s = reshape(getindex.(M.Ham.states, Ref(1:N÷2), Ref(1:N÷2)) , prod(M.bz.gridSize)) ##### The 4 different quadrants in the Unitary relating the BdG quasiparticles and the nambu basis 132 | U21s = reshape(getindex.(M.Ham.states, Ref(N÷2 + 1: N), Ref(1:N÷2)) , prod(M.bz.gridSize)) 133 | U12s = reshape(getindex.(M.Ham.states, Ref(1:N÷2), Ref(N÷2 + 1: N)) , prod(M.bz.gridSize)) 134 | U22s = reshape(getindex.(M.Ham.states, Ref(N÷2 + 1: N), Ref(N÷2 + 1: N)) , prod(M.bz.gridSize)) 135 | 136 | nFs = DistFunction.(Eks; T=M.T, mu=0.0, stat=M.stat) 137 | 138 | @reduce Gk[k1][i, j] |= sum(l) ((conj(U11s[k1][i, l]) * U11s[k1][j, l] * nFs[k1][l]) 139 | + (conj(U21s[k1][i, l]) * U21s[k1][j, l] * (1 - nFs[k1][l]))) 140 | 141 | @reduce Fk[k1][i, j] |= sum(l) ((conj(U11s[k1][i, l]) * U21s[k1][j, l] * nFs[k1][l]) 142 | + (conj(U12s[k1][i, l]) * U22s[k1][j, l] * (1 - nFs[k1][l]))) 143 | M.Gk = reshape(Gk, M.bz.gridSize...) 144 | M.Fk = reshape(Fk, M.bz.gridSize...) 145 | 146 | end 147 | 148 | 149 | @doc """ 150 | ```julia 151 | GetGr!(M::BdGModel) 152 | ``` 153 | Finding the equal-time Greens functions and anomalous Greens function in real space of a `BdGModel`. 154 | 155 | """ 156 | function GetGr!(M::BdGModel) 157 | 158 | phaseShift = MomentumPhaseFFT(M.bz, M.uc_hop) 159 | 160 | Gr = FFTArrayofMatrix(M.Gk) 161 | M.Gr = Gr .* phaseShift 162 | 163 | Fr = FFTArrayofMatrix(M.Fk) 164 | M.Fr = Fr .* phaseShift 165 | end 166 | 167 | 168 | @doc """ 169 | ```julia 170 | SolveModel!(M::BdGModel) 171 | ``` 172 | one-step function to find all the attributes in BdGModel after it has been initialized. 173 | """ 174 | function SolveModel!(M::BdGModel ; mu_guess::Float64 = 2*M.Ham.bandwidth[1] + 2*M.filling * (M.Ham.bandwidth[2] - M.Ham.bandwidth[1]), get_correlations::Bool = true, get_gap::Bool = false, verbose::Bool = true, mu_tol::Float64 = 1e-3, filling_tol::Float64 = 1e-6) 175 | @assert M.Ham.is_BdG==true "Use other format for pure hopping Hamiltonian" 176 | # println(mu_guess) 177 | # println(M.Ham.bandwidth) 178 | if M.filling<0 ##### Must imply that filling was not provided by user and hence needs to be calculated from given mu 179 | GetFilling!(M) 180 | else 181 | GetMu!(M ; guess = mu_guess, mu_tol = mu_tol, filling_tol = filling_tol) 182 | end 183 | 184 | if get_gap 185 | 186 | energies = sort(reduce(vcat, M.Ham.bands)) 187 | M.gap = energies[min(floor(Int64, length(energies)*0.5) + 1, length(energies))] - energies[floor(Int64, length(energies)*0.5)] 188 | end 189 | 190 | if get_correlations 191 | GetGk!(M) 192 | GetGr!(M) 193 | end 194 | 195 | if verbose 196 | @info "System Filled!" 197 | end 198 | end 199 | 200 | 201 | @doc """ 202 | ```julia 203 | GetGap!(M::BdGModel) 204 | ``` 205 | Calculate the BdG gap of the system. 206 | """ 207 | function GetGap!(M::BdGModel) 208 | energies = sort(reduce(vcat, M.Ham.bands)) 209 | M.gap = energies[min(floor(Int64, length(energies)*0.5) + 1, length(energies))] - energies[floor(Int64, length(energies)*0.5)] 210 | 211 | end 212 | 213 | 214 | @doc """ 215 | ```julia 216 | FreeEnergy(M::BdGModel; F0::Float64 = 0.0) --> Float64 217 | ``` 218 | Calculate the free energy of the given `BdGModel`. 219 | 220 | """ 221 | function FreeEnergy(M::BdGModel ; F0::Float64 = 0.0) :: Float64 222 | 223 | Es = reduce(vcat, M.Ham.bands) 224 | F = log.(1 .+ exp.(-(Es .- M.mu) / (M.T))) 225 | F = -M.T * (sum(F) / length(F)) 226 | 227 | return F - F0 228 | end 229 | 230 | 231 | function entropy(M::BdGModel) 232 | Es = 2*reduce(vcat, M.Ham.bands) 233 | ps = DistFunction(Es; T=M.T, mu=M.mu, stat=M.stat) 234 | S = -sum(ps .* log.(ps)) - sum((1 .- ps) .* log.(1 .- ps)) 235 | 236 | return S/(2 * length(M.bz.ks)) 237 | end 238 | 239 | end 240 | -------------------------------------------------------------------------------- /src/Chern.jl: -------------------------------------------------------------------------------- 1 | module Chern 2 | export FindLinks , FieldStrength , ChernNumber, CheckValidity, PartialChernNumber, FilledChernNumber, OccupiedChernNumber, KuboChern 3 | 4 | using ..TightBindingToolkit.Hams:Hamiltonian, IsBandGapped, GetVelocity! 5 | using ..TightBindingToolkit.Useful: DistFunction 6 | using ..TightBindingToolkit.BZone:BZ 7 | 8 | using LinearAlgebra 9 | 10 | 11 | @doc """ 12 | ```julia 13 | FindLinks(Ham::Hamiltonian, subset::Vector{Int64}) --> Tuple{Matrix{ComplexF64}, Matrix{ComplexF64}} 14 | ``` 15 | Function to get the linking matrices on each neighbouring point in the `BZ`. 16 | On a bond connecting k_i and k_j, the linking matrix U is defined such that U[m, n] = where states[k_j[1], k_j[2]][:, m] = v^m[k_j], the mth eigenstate at momentum k_j. 17 | 18 | """ 19 | function FindLinks(Ham::Hamiltonian, subset::Vector{Int64})::Tuple{Matrix{ComplexF64}, Matrix{ComplexF64}} 20 | shifted_1 = circshift(Ham.states, [-1, 0]) 21 | shifted_2 = circshift(Ham.states, [0, -1]) 22 | 23 | Link_1 = det.(selectdim.(adjoint.(Ham.states), 1, Ref(subset)) .* selectdim.(shifted_1, 2, Ref(subset))) 24 | Link_2 = det.(selectdim.(adjoint.(Ham.states), 1, Ref(subset)) .* selectdim.(shifted_2, 2, Ref(subset))) 25 | ##### selectdim(x, 1, v) = x[v, :] and similarly selectdim(x, 2, v) = x[:, v] 26 | ##### selectdim.(M, 1, Ref(subset)) ---> [[M[subset, :] for all k points]] 27 | return (Link_1, Link_2) 28 | end 29 | 30 | 31 | @doc """ 32 | ```julia 33 | FieldStrength(Links::Tuple{Matrix{ComplexF64}, Matrix{ComplexF64}}) --> Matrix{ComplexF64} 34 | ``` 35 | Function to calculate the product of the links over each plaquette on the BZ grid. This is the generalized Bery curvature for multiple degenerate bands. 36 | 37 | """ 38 | function FieldStrength(Links::Tuple{Matrix{ComplexF64}, Matrix{ComplexF64}})::Matrix{ComplexF64} 39 | 40 | Fields = Links[1] .* circshift(Links[2], [-1, 0]) .* circshift(conj.(Links[1]), [0, -1]) .* conj.(Links[2]) 41 | """ 42 | (k+a2)*<--(Links[1])†[k+a2]---* 43 | | ^ 44 | | | 45 | (Links[2])†[k] Links[2][k+a1] 46 | | | 47 | | | 48 | (k)*-->Links[1][k]-------->*(k+a1) 49 | """ 50 | return Fields 51 | end 52 | 53 | 54 | function CheckValidity(Ham::Hamiltonian, subset::Vector{Int64}) 55 | bandGapped = IsBandGapped(Ham) 56 | bandGapped[begin, begin] = true 57 | bandGapped[end, end] = true 58 | 59 | occupied_bands = collect(extrema(subset)) #### Just need to check that the lowest and highest bands given are gapped or not 60 | @assert subset == collect(UnitRange(occupied_bands...)) "Cannot skip bands in between!" 61 | 62 | valence_bands = clamp.(occupied_bands + [-1, 1], Ref(1), Ref(length(Ham.bands[begin]))) ##### one below and one above the lowest and highest band given 63 | check = prod(getindex.(Ref(bandGapped), occupied_bands, valence_bands)) 64 | 65 | @assert check "Given subset of bands have band touchings / degeneracies with other bands. Chern number is not well defined! " 66 | 67 | end 68 | 69 | 70 | @doc """ 71 | ```julia 72 | ChernNumber(Ham::Hamiltonian, subset::Vector{Int64}) --> Float64 73 | ``` 74 | Function to get Chern numbers given a `Hamiltonian` and a `subset` of bands 75 | 76 | """ 77 | function ChernNumber(Ham::Hamiltonian, subset::Vector{Int64} ; check_validity::Bool = false)::Float64 78 | 79 | if check_validity 80 | CheckValidity(Ham, subset) 81 | end 82 | 83 | Links = FindLinks(Ham, subset) 84 | Field = FieldStrength(Links) 85 | chern = (1/(2*pi)) * sum(angle.(Field)) 86 | return chern 87 | end 88 | 89 | 90 | @doc """ 91 | ```julia 92 | PartialChernNumber(Ham::Hamiltonian, band::Int64, mu::Float64) --> Float64 93 | Function to get the Chern number of a partially filled band given a `Hamiltonian`, a `band` index, and a chemical potential `mu`. 94 | 95 | """ 96 | function PartialChernNumber(Ham::Hamiltonian, band::Int64, mu::Float64, T::Float64)::Float64 97 | 98 | @assert band in UnitRange(1, length(Ham.bands[begin])) "Band index out of range!" 99 | 100 | Links = FindLinks(Ham, [band]) 101 | Field = FieldStrength(Links) 102 | 103 | energies= getindex.(Ham.bands, Ref(band)) 104 | filled = DistFunction(energies; mu = mu, T = T, stat = -1) 105 | chern = (1/(2*pi)) * sum((angle.(Field)) .* filled ) 106 | 107 | return chern 108 | end 109 | 110 | 111 | @doc """ 112 | ```julia 113 | FilledChernNumber(Ham::Hamiltonian, mu::Float64) --> Float64 114 | Function to get the Chern number of bands filled upto a given chemical potential `mu`. 115 | 116 | """ 117 | function FilledChernNumber(Ham::Hamiltonian, mu::Float64, T::Float64)::Float64 118 | 119 | # filled_bands = searchsortedfirst.(Ham.bands, Ref(mu)) .- 1 120 | 121 | # if findmax(filled_bands)[1] == 0 122 | # @warn "Chemical potential is below the lowest band. Chern number is not well defined!" 123 | # return 0.0 124 | 125 | # else 126 | return sum(PartialChernNumber.(Ref(Ham), collect(1:length(Ham.bands[begin])), Ref(mu), Ref(T))) 127 | # end 128 | 129 | end 130 | 131 | function OccupiedChernNumber(Ham::Hamiltonian, mu::Float64, T::Float64) 132 | chern = 0.0 133 | 134 | for band in 1:length(Ham.bands[begin]) 135 | link = FindLinks(Ham, [band]) 136 | field = FieldStrength(link) 137 | occupation = DistFunction(getindex.(Ham.bands, band); mu = mu, T = T) 138 | chern += (1/(2*pi)) * sum((angle.(field)) .* occupation) 139 | end 140 | 141 | return chern 142 | end 143 | 144 | function KuboChern(Ham::Hamiltonian, bz::BZ, mu::Float64) 145 | 146 | Vx = conj.(permutedims.(Ham.states)) .* Ham.velocity[1] .* Ham.states 147 | Vy = conj.(permutedims.(Ham.states)) .* Ham.velocity[2] .* Ham.states 148 | 149 | chern = 0.0 + im*0.0 150 | for k in eachindex(Ham.bands) 151 | Es = Ham.bands[k] 152 | vx = Vx[k] 153 | vy = Vy[k] 154 | 155 | ind = searchsortedfirst(Es, mu) 156 | if ind == 1 || ind == length(Es) 157 | continue 158 | else 159 | for i in 1:ind-1 160 | for j in ind:length(Es) 161 | chern += (vx[i, j] * vy[j, i] - vx[j, i] * vy[i, j]) / ((Es[j] - Es[i])^2) 162 | end 163 | end 164 | end 165 | 166 | end 167 | 168 | b1 = [bz.basis[1];0.0] 169 | b2 = [bz.basis[2];0.0] 170 | bzUnitArea = cross(b1, b2)[3]/(4*pi^2) 171 | 172 | return imag(chern)*bzUnitArea*2*pi/length(Ham.bands) 173 | 174 | end 175 | 176 | 177 | end 178 | -------------------------------------------------------------------------------- /src/Conductivity.jl: -------------------------------------------------------------------------------- 1 | module conduct 2 | export Conductivity, SpectralFunction, SpectralContribution, GetConductivity! 3 | 4 | using LinearAlgebra, Logging 5 | 6 | using ..TightBindingToolkit.SpinMatrices:SpinMats 7 | using ..TightBindingToolkit.Useful: DistFunction, DeriDistFunction 8 | using ..TightBindingToolkit.Hams: GetVelocity! 9 | using ..TightBindingToolkit.BZone:BZ, GetQIndex 10 | using ..TightBindingToolkit.TBModel:Model 11 | 12 | directions = ["x" , "y" , "z"] 13 | 14 | 15 | @doc """ 16 | `Conductivity` is a data type to track electrical conductivity of a tight-binding model. 17 | 18 | # Attributes 19 | - `M :: Model`: the tight-binding model whose conductivity is to be calculated. 20 | - `omegas :: Vector{Float64}`: The range of energies to integrate over to get the DC conductivity. 21 | - `spread :: Float64`: the disorder/spread in the real-frequency Greens function. 22 | - `spectral:: Vector{Array{Matrix{ComplexF64}}}`: the matrix Spectral function at the given frequencies, and all momentum points. 23 | - `sigma :: Dict{String, Float64}`: The dictionary containing the conductivity along the different directios. 24 | 25 | Initialize this structure using 26 | ```julia 27 | Conductivity(M::Model ; spread::Float64 = 1e-3) 28 | Conductivity(M::Model , omegas::Vector{Float64} ; spread::Float64 = 1e-3) 29 | ``` 30 | You can either input a filling, or a chemical potential. The corresponding μ for a given filling, or filling for a given μ is automatically calculated. 31 | """ 32 | mutable struct Conductivity 33 | 34 | M :: Model 35 | omegas :: Vector{Float64} 36 | spread :: Float64 37 | spectral :: Vector{Array{Matrix{ComplexF64}}} 38 | sigma :: Dict{String, Float64} 39 | 40 | function Conductivity(M::Model ; spread::Float64 = 1e-3) 41 | 42 | return new{}(M, [0.0], spread, Array{Matrix{ComplexF64}}[], Dict{String, Float64}()) 43 | end 44 | 45 | function Conductivity(M::Model, omegas::Vector{Float64} ; spread::Float64 = 1e-3) 46 | 47 | return new{}(M, omegas, spread, Array{Matrix{ComplexF64}}[], Dict{String, Float64}()) 48 | end 49 | end 50 | 51 | 52 | @doc """ 53 | ```julia 54 | SpectralFunction(H::Array{Matrix{ComplexF64}, N}, omega::Float64 ; spread::Float64 = 1e-3):: Array{Matrix{ComplexF64}} where {N} 55 | ``` 56 | Returns the matrix spectral function given the Hamiltonian, the frequency, and the spread. 57 | 58 | """ 59 | function SpectralFunction(H::Array{Matrix{ComplexF64}, N}, omega::Float64 ; spread::Float64 = 1e-3):: Array{Matrix{ComplexF64}} where {N} 60 | 61 | if isapprox(abs(omega + im * spread), 0.0, atol=1e-6, rtol=1e-6) 62 | A = repeat([zeros(ComplexF64, size(H[begin])...)], size(H)...) 63 | 64 | else 65 | G = inv.(Ref((omega + im * spread) * I) .- H) 66 | A = G - adjoint.(G) 67 | 68 | end 69 | 70 | return A ./ (2 * pi * im) 71 | end 72 | 73 | 74 | @doc """ 75 | ```julia 76 | GetSpectralFunction!(cond::Conductivity) 77 | ``` 78 | Fills the spectral function in the conductivity class. 79 | 80 | """ 81 | function GetSpectralFunction!(cond::Conductivity) 82 | 83 | cond.spectral = SpectralFunction.(Ref(cond.M.Ham.H), cond.omegas; spread = cond.spread) 84 | end 85 | 86 | 87 | @doc """ 88 | ```julia 89 | SpectralContribution(v::Vector{Array{Matrix{ComplexF64}}}, H::Array{Matrix{ComplexF64}, N}, omega::Float64; spread::Float64 = 1e-3, a::Int64 = 1):: ComplexF64 where {N} 90 | SpectralContribution(v::Vector{Array{Matrix{ComplexF64}}}, A::Array{Matrix{ComplexF64}, N}; a::Int64 = 1):: ComplexF64 where {N} 91 | SpectralContribution(v::Vector{Array{Matrix{ComplexF64}}}, H::Array{Matrix{ComplexF64}, N}, omega1::Float64, omega2::Float64; spread::Float64 = 1e-3, a::Int64 = 1, b::Int64 = 2):: ComplexF64 where {N} 92 | SpectralContribution(v::Vector{Array{Matrix{ComplexF64}}}, A1::Array{Matrix{ComplexF64}, N}, A2::Array{Matrix{ComplexF64}, N} ; a::Int64 = 1, b::Int64 = 2):: ComplexF64 where {N} 93 | ``` 94 | The contribution to the conductivity coming from the spectral function for directions `a` (and `b`). Optionally can also pass the frequencies `omega` (and `omega2`). 95 | 96 | """ 97 | function SpectralContribution(v::Vector{Array{Matrix{ComplexF64}}}, H::Array{Matrix{ComplexF64}, N}, omega::Float64; spread::Float64 = 1e-3, a::Int64 = 1):: ComplexF64 where {N} 98 | 99 | A = SpectralFunction(H, omega; spread = spread) 100 | Tab = sum(tr.(v[a] .* A .* v[a] .* A)) / length(H) 101 | 102 | return Tab 103 | end 104 | 105 | function SpectralContribution(v::Vector{Array{Matrix{ComplexF64}}}, A::Array{Matrix{ComplexF64}, N}; a::Int64 = 1):: ComplexF64 where {N} 106 | 107 | Tab = sum(tr.(v[a] .* A .* v[a] .* A)) / length(A) 108 | 109 | return Tab 110 | end 111 | 112 | function SpectralContribution(v::Vector{Array{Matrix{ComplexF64}}}, H::Array{Matrix{ComplexF64}, N}, omega1::Float64, omega2::Float64; spread::Float64 = 1e-3, a::Int64 = 1, b::Int64 = 2):: ComplexF64 where {N} 113 | 114 | A1 = SpectralFunction(H, omega1; spread = spread) 115 | A2 = SpectralFunction(H, omega2; spread = spread) 116 | 117 | Tab = sum(tr.(v[a] .* A1 .* v[b] .* A2)) / length(H) 118 | 119 | return Tab 120 | end 121 | 122 | function SpectralContribution(v::Vector{Array{Matrix{ComplexF64}}}, A1::Array{Matrix{ComplexF64}, N}, A2::Array{Matrix{ComplexF64}, N} ; a::Int64 = 1, b::Int64 = 2):: ComplexF64 where {N} 123 | 124 | Tab = sum(tr.(v[a] .* A1 .* v[b] .* A2)) / length(A1) 125 | 126 | return Tab 127 | end 128 | 129 | 130 | @doc """ 131 | ```julia 132 | GetConductivity!(Cond::Conductivity, as::Vector{Int64} ; get_velocity::Bool = false, get_spectral::Bool = false) 133 | ``` 134 | Calculates the full DC conductivity. Optional Booleans to calculate the velocity matrix of the Hamiltonian, calculate spectral functions also. 135 | 136 | """ 137 | function GetConductivity!(Cond::Conductivity, as::Vector{Int64} ; get_velocity::Bool = false, get_spectral::Bool = true) 138 | 139 | if get_velocity 140 | GetVelocity!(Cond.M.Ham, Cond.M.bz) 141 | end 142 | 143 | if get_spectral 144 | GetSpectralFunction!(Cond) 145 | end 146 | 147 | local DistContribution 148 | 149 | if length(Cond.omegas)>1 150 | 151 | dw = (Cond.omegas[2] - Cond.omegas[1]) 152 | if dw >= 5 * Cond.M.T 153 | @warn "Frequency density, $(round(dw, digits = 4)), should be of the order of temperature = $(Cond.M.T)." 154 | end 155 | 156 | DistContribution = DeriDistFunction.(Cond.omegas ; T = Cond.M.T, mu = 0.0, stat = Cond.M.stat) 157 | @assert !(any(isnan, DistContribution) || any(isinf, DistContribution)) "Temperature might be too low : Derivative of nF is underflowing!" 158 | 159 | else 160 | dw = 1.0 161 | DistContribution = -1.0 162 | end 163 | 164 | for a in as 165 | 166 | spectralContribution= real.(SpectralContribution.(Ref(Cond.M.Ham.velocity), Cond.spectral; a = a)) 167 | Cond.sigma["$(directions[a])$(directions[a])"] = -dw * sum(DistContribution .* spectralContribution) * pi / length(Cond.M.uc.basis) 168 | end 169 | 170 | end 171 | 172 | 173 | end -------------------------------------------------------------------------------- /src/DesignLattice.jl: -------------------------------------------------------------------------------- 1 | module DesignLattice 2 | 3 | export CreateLattice, ModifyLattice!, ScaleLatticeBonds!, ScaleLattice!, RemoveLatticeBonds! 4 | 5 | using LinearAlgebra, Logging 6 | 7 | using ..TightBindingToolkit.UCell: Bond, UnitCell 8 | using ..TightBindingToolkit.Parameters: Param, CreateUnitCell!, ModifyUnitCell! 9 | using ..TightBindingToolkit.LatticeStruct: Lattice, FillLattice! 10 | 11 | 12 | @doc """ 13 | ```julia 14 | CreateLattice(uc::UnitCell{T}, param::Param{T}, size::Vector{Int64} , index::Int64=length(param.value) ; null_dist::Float64 = -1.0, null_label::String = "-") --> Lattice{T} where {T} 15 | CreateLattice(uc::UnitCell{T}, params::Vector{Param{T}}, size::Vector{Int64} , indices::Vector{Int64}=length.(getproperty.(params, :value)) ; null_dist::Float64 = -1.0, null_label::String = "-") :: Lattice{T} where {T} 16 | ``` 17 | Creates a lattice using `Param` objetcs given in `params`. 18 | 19 | """ 20 | function CreateLattice(uc::UnitCell{T}, param::Param{T, R}, size::Vector{Int64} ; index::Int64=length(param.value), null_dist::Float64 = -1.0, null_label::String = "-") :: Lattice{T} where {T, R} 21 | 22 | CreateUnitCell!(uc, param, index) 23 | lattice = Lattice(uc, size ; null_dist = null_dist, null_label = null_label) 24 | FillLattice!(lattice) 25 | 26 | return lattice 27 | end 28 | 29 | function CreateLattice(uc::UnitCell{T}, params::Vector{Param{T, R}}, size::Vector{Int64} ; null_dist::Float64 = -1.0, null_label::String = "-") :: Lattice{T} where {T, R} 30 | 31 | CreateUnitCell!(uc, params) 32 | lattice = Lattice(uc, size ; null_dist = null_dist, null_label = null_label) 33 | FillLattice!(lattice) 34 | 35 | return lattice 36 | end 37 | 38 | function CreateLattice(uc::UnitCell{T}, params::Vector{Param{T}}, size::Vector{Int64} ; null_dist::Float64 = -1.0, null_label::String = "-") :: Lattice{T} where {T} 39 | 40 | CreateUnitCell!(uc, params) 41 | lattice = Lattice(uc, size ; null_dist = null_dist, null_label = null_label) 42 | FillLattice!(lattice) 43 | 44 | return lattice 45 | end 46 | 47 | 48 | @doc """ 49 | ```julia 50 | ModifyLattice!(lattice::Lattice{T}, param::Param{T}) where {T} 51 | ModifyLattice!(lattice::Lattice{T}, params::Vector{Param{T}}) where {T} 52 | ``` 53 | Modifies a lattice when the `Param` objects given in `params` are modified. 54 | 55 | """ 56 | function ModifyLattice!(lattice::Lattice{T}, param::Param{T, R}) where {T, R} 57 | 58 | ModifyUnitCell!(lattice.uc, param) 59 | FillBonds!(lattice) 60 | end 61 | 62 | function ModifyLattice!(lattice::Lattice{T}, params::Vector{Param{T, R}} ) where {T, R} 63 | 64 | ModifyUnitCell!(lattice.uc, params) 65 | FillBonds!(lattice) 66 | end 67 | 68 | function ModifyLattice!(lattice::Lattice{T}, params::Vector{Param{T}} ) where {T} 69 | 70 | ModifyUnitCell!(lattice.uc, params) 71 | FillBonds!(lattice) 72 | end 73 | 74 | 75 | @doc """ 76 | ```julia 77 | ScaleLatticeBonds!(lattice::Lattice{T} , label::String , scaling::Float64) where {T} 78 | ``` 79 | Scales a lattice bond with the given `label` and by the given `scaling`. 80 | 81 | """ 82 | function ScaleLatticeBonds!(lattice::Lattice{T} , label::String , scaling::Float64) where {T} 83 | @assert !isnan(scaling) && !isinf(scaling) "scaling is NaN or Inf." 84 | lattice.BondMats[findall( ==(label) , lattice.BondLabels )] .= scaling .* lattice.BondMats[findall( ==(label) , lattice.BondLabels )] 85 | 86 | end 87 | 88 | 89 | @doc """ 90 | ```julia 91 | ScaleLattice!(lattice::Lattice{T}, param::Param{T}) where {T} 92 | ScaleLattice!(lattice::Lattice{T}, params::Vector{Param{T}}) where {T} 93 | ``` 94 | Scales a lattice bond assuming that the `Param` objects got their strengths modified. 95 | 96 | """ 97 | function ScaleLattice!(lattice::Lattice{T}, param::Param{T, R}) where {T, R} 98 | scaling = param.value[end] / param.value[end - 1] 99 | ScaleLatticeBonds!(lattice, param.label, scaling ) 100 | 101 | end 102 | 103 | function ScaleLattice!(lattice::Lattice{T}, params::Vector{Param{T, R}}) where {T, R} 104 | 105 | ScaleLattice!.(Ref(lattice), params) 106 | end 107 | 108 | function ScaleLattice!(lattice::Lattice{T}, params::Vector{Param{T}}) where {T} 109 | 110 | ScaleLattice!.(Ref(lattice), params) 111 | end 112 | 113 | 114 | @doc """ 115 | ```julia 116 | RemoveLatticeBonds!(lattice::Lattice{T} , label::String ; null_dist::Float64 = -1.0, null_label::String = "-") where {T} 117 | ``` 118 | Removes a lattice bond with the given `label`. 119 | 120 | """ 121 | function RemoveLatticeBonds!(lattice::Lattice{T} , label::String ; null_dist::Float64 = -1.0, null_label::String = "-") where {T} 122 | 123 | indices = findall( ==(label) , lattice.BondLabels ) 124 | 125 | lattice.BondMats[indices] .= 0.0 .* lattice.BondMats[indices] 126 | lattice.BondLabels[indices] .= Ref(null_label) 127 | lattice.BondDists[indices] .= Ref(null_dist) 128 | end 129 | 130 | 131 | 132 | 133 | 134 | end -------------------------------------------------------------------------------- /src/DesignUnitCell.jl: -------------------------------------------------------------------------------- 1 | module DesignUCell 2 | export AddAnisotropicBond! , AddIsotropicBonds! , ModifyBonds! , ScaleBonds! , RemoveBonds! , ModifyFields!, ModifyIsotropicFields!, Lookup 3 | 4 | using LinearAlgebra, Logging 5 | 6 | using ..TightBindingToolkit.Useful: GetAllOffsets 7 | using ..TightBindingToolkit.UCell: Bond, BondRank, UnitCell, IsSameBond 8 | 9 | 10 | @doc """ 11 | ```julia 12 | AddAnisotropicBond!( uc::UnitCell , base::Int64 , target::Int64 , offset::Vector{Int64} , mat::Number , dist::Float64, label::String ) 13 | AddAnisotropicBond!( uc::UnitCell , base::Int64 , target::Int64 , offset::Vector{Int64} , mat::Matrix{<:Number} , dist::Float64, label::String ) 14 | ``` 15 | Add a bond with the given attributes to `UnitCell`. 16 | If given `mat` attribute is a number, it is converted into a 1x1 matrix when entered into the bond. 17 | 18 | """ 19 | function AddAnisotropicBond!( uc::UnitCell{T} , base::Int64 , target::Int64 , offset::Vector{Int64} , mat::Array{<:Number, T} , dist::Float64, label::String ) where {T} 20 | 21 | dims = repeat([uc.localDim], T) 22 | @assert size(mat) == Tuple(dims) "Given Interaction matrix has the inconsistent dimensions as compared to UnitCell!" 23 | 24 | if base <= length(uc.basis) && target <= length(uc.basis) 25 | if norm( sum(offset .* uc.primitives) .+ (uc.basis[target] .- uc.basis[base] ) ) ≈ dist 26 | push!( uc.bonds , Bond( base , target , offset , ComplexF64.(mat) , dist, label ) ) 27 | else 28 | @warn "Inconsistency in bond with label $label" 29 | end 30 | else 31 | @warn "One or both of basis sites ($base, $target) have not been added to the UnitCell object." 32 | end 33 | end 34 | 35 | function AddAnisotropicBond!( uc::UnitCell{T} , base::Int64 , target::Int64 , offset::Vector{Int64} , mat::Number , dist::Float64, label::String ) where {T} 36 | 37 | @assert uc.localDim == 1 "Passing a scalar to a bond is only possible if localDim of UnitCell is 1" 38 | dims = repeat([uc.localDim], T) 39 | 40 | AddAnisotropicBond!( uc, base, target, offset, ComplexF64.(reshape([mat], dims...)), dist, label) 41 | end 42 | 43 | 44 | @doc raw""" 45 | ```julia 46 | AddIsotropicBonds!( uc::UnitCell{T} , dist::Float64 , mats::Number , label::String; checkOffsetRange::Int64=1 , subs::Vector{Int64}=collect(1:length(uc.basis))) 47 | AddIsotropicBonds!( uc::UnitCell{T} , dist::Float64 , mats::Array{<:Number, T} , label::String; checkOffsetRange::Int64=1 , subs::Vector{Int64}=collect(1:length(uc.basis)) ) 48 | ``` 49 | Add a set of "isotropic" bonds, which are the same for each pair of sites at the given distance. 50 | If given `mat` attribute is a number, it is converted into a 1x1 matrix when entered into the bond. 51 | The input `checkOffsetRange` must be adjusted depending on the input distance. 52 | The optional input `subs` is meant for isotropic bonds when only a subset of sublattices are involved. 53 | 54 | """ 55 | function AddIsotropicBonds!( uc::UnitCell{T} , dist::Float64 , mat::Array{<:Number, T} , label::String; checkOffsetRange::Int64=1 , subs::Vector{Int64}=collect(1:length(uc.basis)) ) where {T} 56 | 57 | dims = repeat([uc.localDim], T) 58 | @assert size(mat) == Tuple(dims) "Interaction matrix has the inconsistent dimensions as compared to UnitCell!" 59 | offsets = GetAllOffsets(checkOffsetRange, length(uc.primitives)) 60 | 61 | for i in subs 62 | for j in subs 63 | for offset in offsets 64 | if norm( sum( offset.*uc.primitives ) + (uc.basis[j] - uc.basis[i] ) ) ≈ dist 65 | proposal = Bond(i, j, offset, ComplexF64.(mat), dist, label) 66 | if sum(IsSameBond.( Ref(proposal) , uc.bonds ))==0 67 | push!( uc.bonds , proposal ) 68 | end 69 | end 70 | end 71 | 72 | end 73 | end 74 | end 75 | 76 | function AddIsotropicBonds!( uc::UnitCell{T} , dist::Float64 , mat::Number , label::String; checkOffsetRange::Int64=1 , subs::Vector{Int64}=collect(1:length(uc.basis))) where {T} 77 | 78 | @assert uc.localDim == 1 "Passing a scalar to a bond is only possible if localDim of UnitCell is 1" 79 | dims = repeat([uc.localDim], T) 80 | 81 | AddIsotropicBonds!( uc, dist, ComplexF64.(reshape([mat], dims...)), label; checkOffsetRange = checkOffsetRange, subs = subs ) 82 | end 83 | 84 | 85 | @doc """ 86 | ```julia 87 | ModifyBonds!(uc::UnitCell, dist::Float64, newMat::Matrix{<:Number}) 88 | ModifyBonds!(uc::UnitCell, label::String, newMat::Matrix{<:Number}) 89 | ``` 90 | Modify an existing bond in the `UnitCell` with the given `label`, or at a given distance=`dist`, to the given bond matrix. 91 | 92 | """ 93 | function ModifyBonds!(uc::UnitCell{T}, dist::Float64, newMat::Array{<:Number, T}) where {T} 94 | 95 | dims = repeat([uc.localDim], T) 96 | @assert size(newMat) == Tuple(dims) "New entry is incompatible with existing bonds!" 97 | 98 | distances = getfield.(uc.bonds, :dist) 99 | map(x -> x.mat = ComplexF64.(newMat), uc.bonds[findall(≈(dist), distances)]) 100 | end 101 | 102 | function ModifyBonds!(uc::UnitCell{T}, label::String, newMat::Array{<:Number, T}) where {T} 103 | 104 | dims = repeat([uc.localDim], T) 105 | @assert size(newMat) == Tuple(dims) "New entry is incompatible with existing bonds!" 106 | 107 | labels = getfield.(uc.bonds, :label) 108 | map(x -> x.mat = ComplexF64.(newMat), uc.bonds[findall(==(label), labels)]) 109 | end 110 | 111 | 112 | @doc """ 113 | ```julia 114 | ScaleBonds!(uc::UnitCell, dist::Float64, scale::Number) 115 | ScaleBonds!(uc::UnitCell, label::String, scale::Number) 116 | ``` 117 | Scale the matrix of an existing bond in the `UnitCell` with the given `label`, or at a given distance=`dist`, by the given scaling factor. 118 | 119 | """ 120 | function ScaleBonds!(uc::UnitCell, dist::Float64, scale::Number) 121 | distances = getfield.(uc.bonds, :dist) 122 | map(x -> x.mat = scale * x.mat, uc.bonds[findall(≈(dist), distances)]) 123 | end 124 | 125 | function ScaleBonds!(uc::UnitCell, label::String, scale::Number) 126 | labels = getfield.(uc.bonds, :label) 127 | map(x -> x.mat = scale * x.mat, uc.bonds[findall(==(label), labels)]) 128 | end 129 | 130 | 131 | @doc """ 132 | ```julia 133 | RemoveBonds!(uc::UnitCell, dist::Float64) 134 | ScaleBonds!(uc::UnitCell, label::String) 135 | ``` 136 | Remove an existing bond in the `UnitCell` with the given `label`, or at a given distance=`dist`. 137 | 138 | """ 139 | function RemoveBonds!(uc::UnitCell, label::String ) 140 | labels = getfield.(uc.bonds, :label) 141 | deleteat!( uc.bonds , findall(==(label), labels) ) 142 | end 143 | 144 | function RemoveBonds!(uc::UnitCell, dist::Float64 ) 145 | labels = getfield.(uc.bonds, :dist) 146 | deleteat!( uc.bonds , findall(≈(dist), labels) ) 147 | end 148 | 149 | 150 | @doc """ 151 | ```julia 152 | ModifyFields!(uc::UnitCell, site::Int64, newField::Vector{Float64}) 153 | ModifyFields!(uc::UnitCell, newField::Vector{Vector{Float64}}) 154 | ``` 155 | Modify the on-site fields in the `UnitCell`, either one at a time, or all of them. 156 | 157 | """ 158 | function ModifyFields!(uc::UnitCell, site::Int64, newField::Vector{Float64}) 159 | uc.fields[site] = newField 160 | end 161 | 162 | function ModifyFields!(uc::UnitCell, newField::Vector{Float64}, dim::Int64) 163 | @assert length(newField) == length(uc.basis) 164 | setindex!.(uc.fields, newField, Ref(dim)) 165 | end 166 | 167 | function ModifyFields!(uc::UnitCell, newField::Vector{Vector{Float64}}) 168 | uc.fields = newField 169 | end 170 | 171 | 172 | @doc """ 173 | ```julia 174 | ModifyIsotropicFields!(uc::UnitCell, newField::Vector{Float64}) 175 | ModifyIsotropicFields!(uc::UnitCell, newField::Float64, dim::Int64) 176 | ``` 177 | Modify the on site field uniformly, on all sublattices. The optional argument `dim` is if you want to only modify one of the 4 elements of on-site fields (3 Zeeman and 1 chemical potential). 178 | 179 | """ 180 | function ModifyIsotropicFields!(uc::UnitCell, newField::Vector{Float64}) 181 | uc.fields = repeat(newField, length(uc.basis)) 182 | end 183 | 184 | function ModifyIsotropicFields!(uc::UnitCell, newField::Float64, dim::Int64) 185 | map(x -> x[dim] = newField, uc.fields ) 186 | end 187 | 188 | 189 | @doc """ 190 | ```julia 191 | Lookup(uc::UnitCell) --> Dict 192 | ``` 193 | Returns a dictionary with keys = (base, target, offset) for bond ∈ `UnitCell` bond list, and the entry being the bond matrix. 194 | If there are multiple bonds with the same identifier, it adds them up. 195 | 196 | """ 197 | function Lookup(uc::UnitCell{T}) :: Dict where {T} 198 | lookupTable = Dict() 199 | 200 | for bond in uc.bonds 201 | identifier = (bond.base, bond.target, bond.offset) 202 | 203 | if haskey(lookupTable, identifier) 204 | lookupTable[identifier] = lookupTable[identifier] + bond.mat 205 | 206 | elseif haskey(lookupTable, (bond.target, bond.base, -bond.offset)) 207 | ##### ///TODO: Fix for arrays, when bond rank >2 208 | ##### TODO : TEST 209 | flippedIndices = collect(T:-1:1) 210 | lookupTable[(bond.target, bond.base, -bond.offset)] = lookupTable[(bond.target, bond.base, -bond.offset)] + collect(conj.(permutedims(bond.mat, flippedIndices))) 211 | 212 | else 213 | lookupTable[identifier] = bond.mat 214 | end 215 | end 216 | 217 | return lookupTable 218 | end 219 | 220 | end -------------------------------------------------------------------------------- /src/ExpandUnitCell.jl: -------------------------------------------------------------------------------- 1 | module ExpandUCell 2 | export ChangePrimitives!, ExpandUnitCell, ExpandBonds! 3 | 4 | using LinearAlgebra 5 | 6 | using ..TightBindingToolkit.Useful: Meshgrid 7 | using ..TightBindingToolkit.UCell: Bond, UnitCell, GetRealSpacePositions, AddBasisSite!, IsSameBond, FlipBond 8 | using ..TightBindingToolkit.DesignUCell: AddAnisotropicBond! 9 | 10 | 11 | @doc """ 12 | ```julia 13 | ChangePrimitives!(uc::UnitCell{T}, newPrimitives::Vector{Vector{Float64}}) 14 | ``` 15 | Changes the pirmitive vectors of the given `UnitCell` assuming the sublattices stay the same. Changes the bonds accordingly. 16 | 17 | """ 18 | function ChangePrimitives!(uc::UnitCell{T}, newPrimitives::Vector{Vector{Float64}} ; OffsetRange::Int64 = 2, accuracy::Int64 = 6) where {T} 19 | 20 | oldPrimitives = deepcopy(uc.primitives) 21 | uc.primitives = newPrimitives 22 | 23 | DistanceDict = GetRealSpacePositions(uc ; OffsetRange = OffsetRange, accuracy = accuracy) 24 | 25 | newBonds = Bond{T}[] 26 | 27 | for (sub, basis) in enumerate(uc.basis) 28 | 29 | bases = getproperty.(uc.bonds, :base) 30 | bonds = uc.bonds[findall(==(sub), bases)] 31 | 32 | for bond in bonds 33 | position = uc.basis[bond.target] + sum(bond.offset .* oldPrimitives) 34 | target, offset = DistanceDict[round.(position, digits = accuracy)] 35 | 36 | proposal = Bond(bond.base, target, offset, bond.mat, bond.dist, bond.label) 37 | if isempty(newBonds) 38 | push!( newBonds , proposal ) 39 | else 40 | if sum(IsSameBond.( Ref(proposal) , newBonds ))==0 41 | push!( newBonds , proposal ) 42 | end 43 | end 44 | end 45 | 46 | targets = getproperty.(uc.bonds, :target) 47 | bonds = FlipBond.(uc.bonds[findall(==(sub), targets)]) 48 | 49 | for bond in bonds 50 | position = uc.basis[bond.target] + sum(bond.offset .* oldPrimitives) 51 | target, offset = DistanceDict[round.(position, digits = accuracy)] 52 | 53 | proposal = Bond(bond.base, target, offset, bond.mat, bond.dist, bond.label) 54 | if isempty(newBonds) 55 | push!( newBonds , proposal ) 56 | else 57 | if sum(IsSameBond.( Ref(proposal) , newBonds ))==0 58 | push!( newBonds , proposal ) 59 | end 60 | end 61 | end 62 | 63 | 64 | end 65 | @assert length(newBonds) == length(uc.bonds) 66 | uc.bonds = newBonds 67 | end 68 | 69 | 70 | @doc """ 71 | ```julia 72 | ExpandUnitCell(ucOG::UnitCell{T}, scaling::Vector{Int64} ; OffsetRange::Int64 = 2) 73 | ``` 74 | Returns a UnitCell which is an integer multiple of the given UnitCell (the primitives are scaled by the given scalings along each direction). The bonds are properly redefined amongst the new sublattices and new primitive vectors. 75 | 76 | """ 77 | function ExpandUnitCell(ucOG::UnitCell{T}, scaling::Vector{Int64} ; OffsetRange::Int64 = 2, accuracy::Int64 = 6) :: UnitCell{T} where {T} 78 | 79 | asNew = scaling .* ucOG.primitives 80 | ucNew = UnitCell(asNew, ucOG.localDim, T) 81 | NewOffsets = Meshgrid(scaling .- 1 ; starts = zeros(Int64, length(scaling))) 82 | 83 | for offset in NewOffsets 84 | for (iBasis, basis) in enumerate(ucOG.basis) 85 | b = basis + sum(offset .* ucOG.primitives) 86 | AddBasisSite!(ucNew, b, ucOG.fields[iBasis], ucOG.OnSiteMats) 87 | end 88 | end 89 | 90 | DistanceDictNew = GetRealSpacePositions(ucNew ; OffsetRange = OffsetRange, accuracy = accuracy) 91 | 92 | bases = getproperty.(ucOG.bonds, :base) 93 | for sub in 1:length(ucNew.basis) 94 | oldSub = ((sub - 1) % length(ucOG.basis)) + 1 95 | bonds = ucOG.bonds[findall(==(oldSub), bases)] 96 | 97 | for bond in bonds 98 | targetPosition = ucNew.basis[sub] + ((ucOG.basis[bond.target] - ucOG.basis[bond.base] ) + sum(bond.offset .* ucOG.primitives)) 99 | 100 | target, offset = DistanceDictNew[round.(targetPosition, digits = accuracy)] 101 | AddAnisotropicBond!(ucNew, sub, target, offset, bond.mat, bond.dist, bond.label) 102 | end 103 | end 104 | 105 | return ucNew 106 | 107 | end 108 | 109 | 110 | @doc """ 111 | ```julia 112 | ExpandBonds!(ucOG::UnitCell{T}, ucNew::UnitCell{T} ; OffsetRange::Int64 = 2) 113 | ``` 114 | An in-place version of `ExpandUnitCell` which works with the new primitive vectors given in `ucNew`. 115 | 116 | """ 117 | function ExpandBonds!(ucOG::UnitCell{T}, ucNew::UnitCell{T} ; OffsetRange::Int64 = 2, accuracy::Int64 = 6) where {T} 118 | 119 | DistanceDictNew = GetRealSpacePositions(ucNew ; OffsetRange = OffsetRange, accuracy = accuracy) 120 | 121 | bases = getproperty.(ucOG.bonds, :base) 122 | 123 | for sub in 1:length(ucNew.basis) 124 | oldSub = ((sub - 1) % length(ucOG.basis)) + 1 125 | 126 | bonds = ucOG.bonds[findall(==(oldSub), bases)] 127 | for bond in bonds 128 | targetPosition = ucNew.basis[sub] + ((ucOG.basis[bond.target] - ucOG.basis[bond.base] ) + sum(bond.offset .* ucOG.primitives)) 129 | 130 | target, offset = DistanceDictNew[round.(targetPosition, digits = accuracy)] 131 | AddAnisotropicBond!(ucNew, sub, target, offset, bond.mat, bond.dist, bond.label) 132 | end 133 | end 134 | 135 | end 136 | 137 | 138 | 139 | end -------------------------------------------------------------------------------- /src/Flux.jl: -------------------------------------------------------------------------------- 1 | module Flux 2 | 3 | export GetBondPhase, CheckGaugeValidity, ModifyGauge!, GetStringPhase, InsertMonopolePair! 4 | 5 | using LinearAlgebra 6 | 7 | using ..TightBindingToolkit.Useful: SegmentIntersection 8 | using ..TightBindingToolkit.LatticeStruct: Lattice, FillLattice! 9 | 10 | 11 | @doc """ 12 | ```julia 13 | GetBondPhase(A::Function, r1::Vector{Float64}, r2::Vector{Float64} ; n::Int64 = 100, _kwargs::Dict = Dict()) --> ComplexF64 14 | ``` 15 | Returns the tight binding phase on a bond between two sites `r1` and `r2` on a lattice with a given gauge vector potential `A`. 16 | The phase is calculated by integrating the vector potential along the bond. 17 | """ 18 | function GetBondPhase(A::Function, r1::Vector{Float64}, r2::Vector{Float64} ; n::Int64 = 100, _kwargs::Dict{Symbol, <:Any} = Dict{Symbol, Any}()) :: ComplexF64 19 | 20 | ts = collect(range(0.0, 1.0, length = n + 1)) 21 | rs = Ref(r1) .+ (Ref(r2 - r1) .* ts) 22 | As = A.(rs ; _kwargs...) 23 | 24 | dr = (r2 - r1) ./ n 25 | ProjectedAs = dot.(As, Ref(dr)) 26 | 27 | return sum(ProjectedAs) - 1/2 * (ProjectedAs[begin] + ProjectedAs[end]) 28 | end 29 | 30 | 31 | @doc """ 32 | ```julia 33 | CheckGaugeValidity(lat::Lattice{T}, A::Function ; _kwargs::Dict = Dict(), accuracy::Int64 = 5) --> Bool 34 | ``` 35 | Checks if the gauge vector potential `A` is valid for the lattice `lat` under periodic boundary conditions. 36 | """ 37 | function CheckGaugeValidity(lat::Lattice{T}, A::Function ; _kwargs::Dict{Symbol, <:Any} = Dict{Symbol, Any}(), accuracy::Int64 = 5) :: Bool where{T} 38 | 39 | latticePrimitives = lat.size .* lat.uc.primitives 40 | AShifts = A.(latticePrimitives ; _kwargs...) 41 | 42 | return prod(isinteger.(round.(AShifts / (2 * pi), digits = accuracy))) 43 | end 44 | 45 | 46 | @doc """ 47 | ```julia 48 | ModifyGauge!(lat::Lattice{T}, A::Function ; n::Int64 = 100, _kwargs::Dict = Dict()) where{T} 49 | ``` 50 | Modifies the lattice hoppings using the fixed gauge vector potential `A` on the lattice `lat` by multiplying the bond matrices with the corresponding phase factors. 51 | """ 52 | function ModifyGauge!(lat::Lattice{T}, A::Function ; n::Int64 = 100, _kwargs::Dict{Symbol, <:Any} = Dict{Symbol, Any}()) where{T} 53 | 54 | positions = getindex.(Ref(lat.positions), collect(1:lat.length)) 55 | r1s = repeat(positions, 1, size(lat.bondSites, 2)) 56 | r2s = getindex.(Ref(lat.positions), lat.bondSites) .+ [sum(shift .* lat.size .* lat.uc.primitives) for shift in lat.bondShifts] 57 | 58 | phases = exp.( im .* GetBondPhase.(Ref(A), r1s, r2s ; n = n, _kwargs =_kwargs)) 59 | lat.bondMats= lat.bondMats .* phases 60 | 61 | end 62 | 63 | 64 | @doc """ 65 | ```julia 66 | GetStringPhase(Monopoles::Vector{Vector{Float64}}, r1::Vector{Float64}, r2::Vector{Float64} ; flux::Float64 = Float64(pi)) --> ComplexF64 67 | ``` 68 | Get the Dirac string between the given `Monopoles` phase cutting the bond on the lattice between sites `r1` and `r2`. 69 | """ 70 | function GetStringPhase(Monopoles::Vector{Vector{Float64}}, r1::Vector{Float64}, r2::Vector{Float64} ; flux::Float64 = Float64(pi)) :: ComplexF64 71 | 72 | t = SegmentIntersection(Monopoles, [r1, r2]) 73 | r = SegmentIntersection([r1, r2], Monopoles) 74 | 75 | if t>=0.0 && t<1.0 && r>=0.0 && r<1.0 76 | return exp(im * flux) 77 | else 78 | return 1.0 79 | end 80 | end 81 | 82 | 83 | @doc """ 84 | ```julia 85 | InsertMonopolePair!(lat::Lattice{T}, Monopoles::Vector{Vector{Float64}} ; flux::Float64 = Float64(pi) ) 86 | ``` 87 | Inserts a pair of monopole <--> Anti-Monopole on the lattice `lat` at the given positions `Monopoles` with the given flux `flux` through a Dirac 88 | """ 89 | function InsertMonopolePair!(lat::Lattice{T}, Monopoles::Vector{Vector{Float64}} ; flux::Float64 = Float64(pi) ) where{T} 90 | 91 | positions = getindex.(getindex.(Ref(lat.positions), collect(1:lat.length)) , Ref(1)) 92 | r1s = repeat(positions, 1, size(lat.bondSites, 2)) 93 | r2s = getindex.(getindex.(Ref(lat.positions), lat.bondSites) , Ref(1)) 94 | 95 | dists = norm.(r2s .- r1s) 96 | check = isapprox.(dists, lat.bondDists, atol = 1e-6, rtol = 1e-6) 97 | 98 | phases = GetStringPhase.(Ref(Monopoles), r1s, r2s ; flux = flux) 99 | phases = phases .^ check 100 | lat.bondMats= lat.bondMats .* phases 101 | 102 | return findall(==(false), isapprox.(phases, Ref(1.0))) 103 | 104 | end 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | end -------------------------------------------------------------------------------- /src/Gauge.jl: -------------------------------------------------------------------------------- 1 | module Gauge 2 | 3 | export LandauGauge, RadialGauge 4 | 5 | using LinearAlgebra 6 | 7 | 8 | function LandauGauge(r::Vector{Float64} ; direction::Vector{Float64} = [1.0, 0.0], normal::Vector{Float64} = [0.0, 1.0], B::Float64 = 1.0) :: Vector{Float64} 9 | 10 | distance = dot(r, normal) / norm(normal) 11 | return direction .* B .* distance / norm(direction) 12 | end 13 | 14 | 15 | function RadialGauge(r::Vector{Float64} ; B::Vector{Float64}) :: Vector{Float64} 16 | 17 | return -(1/2) * cross(r, B) 18 | end 19 | 20 | 21 | end -------------------------------------------------------------------------------- /src/Hamiltonian.jl: -------------------------------------------------------------------------------- 1 | module Hams 2 | export Hamiltonian , FillHoppingHamiltonian, FillPairingHamiltonian, FillHamiltonian , DiagonalizeHamiltonian! , DOS, ModifyHamiltonianField!, IsBandGapped, GetVelocity! 3 | 4 | using ..TightBindingToolkit.Useful: Central_Diff, Arrayfy, DeArrayfy 5 | using ..TightBindingToolkit.SpinMatrices:SpinMats 6 | using ..TightBindingToolkit.UCell:Bond, UnitCell, IsSameUnitCell 7 | using ..TightBindingToolkit.DesignUCell: ModifyFields!, ModifyIsotropicFields! 8 | using ..TightBindingToolkit.BZone:BZ 9 | 10 | using LinearAlgebra, TensorCast, Logging 11 | 12 | @doc """ 13 | ```julia 14 | FillHoppingHamiltonian(uc::UnitCell, k::Vector{Float64} ; OnSiteMatrices::Vector{Matrix{ComplexF64}}) --> Matrix{ComplexF64} 15 | ``` 16 | Returns the hopping Hamiltonian at momentum point `k`, corresponding to the bonds present in `UnitCell`. `OnSiteMatrices` are used for the fields, with the convention that the last matrix is the one corresponding to the chemimcal potential. 17 | 18 | """ 19 | function FillHoppingHamiltonian(uc::UnitCell{2}, k::Vector{Float64}) 20 | dims = uc.localDim * length(uc.basis) 21 | H = zeros(ComplexF64, dims, dims) 22 | 23 | ##### On-site terms in the Hamiltonian 24 | for site in 1:length(uc.basis) 25 | b1 = uc.localDim * (site - 1) + 1 26 | b2 = uc.localDim * (site - 1) + 1 27 | H[b1 : b1 + uc.localDim - 1, b2 : b2 + uc.localDim - 1] .-= sum(uc.fields[site] .* uc.OnSiteMats) 28 | end 29 | ##### Inter-site terms which depend on the momentum 30 | for bond in uc.bonds 31 | b1 = uc.localDim * (bond.base - 1) + 1 32 | b2 = uc.localDim * (bond.target - 1) + 1 33 | 34 | if b1==b2 && bond.offset==zeros(length(uc.primitives)) 35 | H[b1 : b1 + uc.localDim - 1, b2 : b2 + uc.localDim - 1] .+= (bond.mat + bond.mat') / 2 36 | 37 | else 38 | H[b1 : b1 + uc.localDim - 1, b2 : b2 + uc.localDim - 1] .+= exp( im .* dot(k, sum(bond.offset .* uc.primitives))) .* bond.mat 39 | H[b2 : b2 + uc.localDim - 1, b1 : b1 + uc.localDim - 1] .+= exp(-im .* dot(k, sum(bond.offset .* uc.primitives))) .* bond.mat' 40 | end 41 | end 42 | 43 | return H 44 | end 45 | 46 | 47 | @doc """ 48 | ```julia 49 | FillPairingHamiltonian(uc::UnitCell, k::Vector{Float64}) --> Matrix{ComplexF64} 50 | ``` 51 | Returns the pairing Hamiltonian at momentum point `k`, corresponding to the bonds present in `UnitCell`. 52 | 53 | """ 54 | function FillPairingHamiltonian(uc::UnitCell{2}, k::Vector{Float64}) :: Matrix{ComplexF64} 55 | dims = uc.localDim * length(uc.basis) 56 | H = zeros(ComplexF64, dims, dims) 57 | 58 | for bond in uc.bonds 59 | b1 = uc.localDim * (bond.base - 1) + 1 60 | b2 = uc.localDim * (bond.target - 1) + 1 61 | 62 | H[b1 : b1 + uc.localDim - 1, b2 : b2 + uc.localDim - 1] .+= exp( im .* dot(k, sum(bond.offset .* uc.primitives))) .* bond.mat 63 | 64 | end 65 | 66 | return H 67 | end 68 | 69 | 70 | @doc raw""" 71 | ```julia 72 | FullHamiltonian(uc::UnitCell, bz::BZ) --> Matrix{Matrix{ComplexF64}} 73 | ``` 74 | Returns the full Hamiltonian at all momentum points in `BZ`, corresponding to the bonds present in `UnitCell`. 75 | 76 | """ 77 | function FillHamiltonian(uc_hop::UnitCell{2}, uc_pair::UnitCell{2}, k::Vector{Float64}) :: Matrix{ComplexF64} 78 | 79 | @assert IsSameUnitCell(uc_hop, uc_pair) "Inconsistent unit cells for hopping and pairing!" 80 | 81 | Tk = FillHoppingHamiltonian( uc_hop, k) 82 | Tmk = FillHoppingHamiltonian( uc_hop, -k) 83 | Δk = FillPairingHamiltonian(uc_pair, k ) - transpose(FillPairingHamiltonian(uc_pair, -k )) 84 | 85 | ##### The full BdG Hamiltonian in the nambu basis. 86 | Hk = hcat(Tk , Δk) 87 | Hk = vcat(Hk , hcat(Δk' , -transpose(Tmk))) 88 | return (1/2 .* Hk) 89 | end 90 | 91 | function FillHamiltonian(uc::UnitCell{2}, bz::BZ) 92 | 93 | return FillHoppingHamiltonian.(Ref(uc), bz.ks) 94 | end 95 | 96 | function FillHamiltonian(uc_hop::UnitCell{2}, uc_pair::UnitCell{2}, bz::BZ) 97 | 98 | return FillHamiltonian.(Ref(uc_hop), Ref(uc_pair), bz.ks) 99 | end 100 | 101 | 102 | @doc """ 103 | `Hamiltonian` is a data type representing a general momentum-space Hamiltonian corresponding to the given `UnitCell` and `BZ` (or 2 Unit Cells if it is a BdG Hamiltonian). 104 | 105 | # Attributes 106 | - `H :: Array{Matrix{ComplexF64}}`: A Array (corresponding to the grid of k-points in `BZ`) of Hamiltonian matrices. 107 | - `bands :: Array{Vector{Float64}}`: A Array (corresponding to the grid of k-points in `BZ`) of band spectrums. 108 | - `states :: Array{Matrix{ComplexF64}}`: A Array (corresponding to the grid of k-points in `BZ`) of band wavefunctions. 109 | - `bandwidth :: Tuple{Float64, Float64}` : the tuple of minimum and maximum energies in the band structure. 110 | - `is_BdG :: Bool` : is the Hamiltonian a bdG hamiltonian or a pure hopping hamiltonian. 111 | 112 | Initialize this structure using 113 | ```julia 114 | Hamiltonian(uc::UnitCell, bz::BZ) --> Hopping Hamiltonian 115 | Hamiltonian(uc_hop::UnitCell, uc_pair::UnitCell, bz::BZ) --> BdG Hamiltonian 116 | ``` 117 | """ 118 | mutable struct Hamiltonian 119 | H :: Array{Matrix{ComplexF64}} 120 | bands :: Array{Vector{Float64}} 121 | states :: Array{Matrix{ComplexF64}} 122 | bandwidth :: Tuple{Float64, Float64} 123 | is_BdG :: Bool 124 | velocity :: Vector{Array{Matrix{ComplexF64}}} 125 | 126 | Hamiltonian(uc::UnitCell{2}, bz::BZ) = new{}(FillHamiltonian(uc, bz), Array{Vector{Float64}}(undef, zeros(Int64, length(uc.primitives))...), Array{Matrix{ComplexF64}}(undef, zeros(Int64, length(uc.primitives))...), (0.0, 0.0), false, Array{Matrix{ComplexF64}, length(uc.primitives)}[]) 127 | 128 | Hamiltonian(uc_hop::UnitCell{2}, uc_pair::UnitCell{2}, bz::BZ) = new{}(FillHamiltonian(uc_hop, uc_pair, bz), Array{Vector{Float64}}(undef, zeros(Int64, length(uc_hop.primitives))...), Array{Matrix{ComplexF64}}(undef,zeros(Int64, length(uc_hop.primitives))...), (0.0, 0.0), true, Array{Matrix{ComplexF64}, length(uc_hop.primitives)}[]) 129 | end 130 | 131 | 132 | @doc """ 133 | ```julia 134 | DiagonalizeHamiltonian!(Ham::Hamiltonian) 135 | ``` 136 | Diagonalize the `Hamiltonian` at all momentum points in the `BZ`. `verbose` is an optional argument to print when the Hamiltonian is diagonalized. 137 | 138 | """ 139 | function DiagonalizeHamiltonian!(Ham::Hamiltonian ; verbose::Bool=true) 140 | sols = eigen.(Hermitian.(Ham.H)) 141 | Ham.bands = getfield.(sols, :values) 142 | Ham.states = getfield.(sols, :vectors) 143 | Ham.bandwidth = (minimum(first.(Ham.bands)) , maximum(last.(Ham.bands))) 144 | if verbose 145 | @info "Hamiltonian Diagonalized!" 146 | end 147 | end 148 | 149 | 150 | @doc """ 151 | ```julia 152 | ModifyHamiltonianField!(Ham::Hamiltonian, uc::UnitCell, newFields::Vector{Float64} ; dim::Int64=4, verbose::Bool=false) 153 | ``` 154 | Faster implementation of modifying ONLY the on-site field part of a `Hamiltonian`. `newFields` must be a vector of the same length as `uc.basis`. 155 | `dim` is an optional argument which determines which element of on-site field is being replaced. 156 | 157 | """ 158 | function ModifyHamiltonianField!(Ham::Hamiltonian, uc::UnitCell, newFields::Vector{Float64} ; dim::Int64=length(uc.fields[begin]), verbose::Bool=false) 159 | 160 | @assert length(newFields)==length(uc.basis) "Inconsistent number of basis sites and fields given" 161 | 162 | if Ham.is_BdG==false 163 | Ham.H .+= Ref(kron(diagm(getindex.(uc.fields, dim) .- newFields) , uc.OnSiteMats[dim])) 164 | DiagonalizeHamiltonian!(Ham ; verbose=verbose) 165 | else 166 | Ham.H .+= Ref(kron(SpinMats(1//2)[3], kron(diagm(getindex.(uc.fields, dim) .- newFields) , uc.OnSiteMats[dim]))) ##### The extra Sz corresponds to the nambu basis. 167 | DiagonalizeHamiltonian!(Ham ; verbose=verbose) 168 | end 169 | 170 | ModifyFields!(uc, newFields, dim) 171 | end 172 | 173 | 174 | @doc """ 175 | ```julia 176 | isBandGapped(H::Hamiltonian ; tol::Float64 = 1e-3) --> BitMatrix 177 | ``` 178 | Returns a matrix of booleans marked as `true` if the band corresponding to the row and column of the matrix are gapped (greater than the tolerance), and false otherwise. 179 | 180 | """ 181 | function IsBandGapped(H::Hamiltonian ; tol::Float64 = 1e-3) :: BitMatrix 182 | bands = reshape(H.bands, length(H.bands)) 183 | @cast bandDiff[i, j][k] |= abs(bands[k][i] - bands[k][j]) 184 | @cast gapped[i, j] |= minimum(bandDiff[i, j]) > tol 185 | 186 | return gapped 187 | end 188 | 189 | 190 | @doc """ 191 | ```julia 192 | DOS(Omega::Float64, Ham::Hamiltonian; till_band::Int64=length(Ham.bands[1, 1]), spread::Float64=1e-3) --> Float64 193 | DOS(Omegas::Vector{Float64}, Ham::Hamiltonian; till_band::Int64=length(Ham.bands[1, 1]), spread::Float64=1e-3) --> Vector{Float64} 194 | ``` 195 | Calculate the Density of State correspondingto the given energies in `Omegas`, for the lowest bands upto `till_band`. 196 | The calculation is done at a finite `spread` of the delta-function sum. 197 | """ 198 | function DOS(Omega::Float64, energies::Vector{Float64}; spread::Float64=1e-2) :: Float64 199 | 200 | dos = @. 1 / ((Omega - energies) + im * spread) 201 | return sum(imag(dos)) 202 | end 203 | 204 | function DOS(Omegas::Vector{Float64}, Ham::Hamiltonian; till_band::Int64=length(Ham.bands[1, 1]), spread::Float64=1e-2) :: Vector{Float64} 205 | 206 | energies = reduce(vcat, Ham.bands) 207 | n_bands = length(Ham.bands[1, 1]) 208 | energies = energies[filter(i -> (i-1) % n_bands + 1 <= till_band, 1:length(energies))] 209 | 210 | dos = DOS.(Omegas, Ref(energies); spread=spread) 211 | 212 | dOmega = Omegas[2:end] .- Omegas[1:end-1] 213 | norm = sum(dos[1:end-1] .* dOmega) 214 | 215 | dos .= dos ./ norm 216 | return dos 217 | end 218 | 219 | 220 | @doc """ 221 | ```julia 222 | GetVelocity!(H::Hamiltonian, bz::BZ) --> Vector{Array{Matrix{ComplexF64}, T}} 223 | ``` 224 | returns a vector of velocity matrices at each k point defined as ∂H(k)/∂k^{μ}. 225 | """ 226 | function GetVelocity!(H::Hamiltonian, bz::BZ) :: Vector{typeof(H.H)} 227 | 228 | dH = Central_Diff(H.H ; delta = (1 ./ bz.gridSize), PBC = repeat([true], length(bz.gridSize))) 229 | dH = Arrayfy.(dH) 230 | 231 | bMatrix = inv(transpose(hcat(bz.basis...))) ##### Changing velocity from reciprocal basis to cartesian basis. 232 | 233 | v = bMatrix * dH 234 | H.velocity = DeArrayfy.(v, Ref(bz.gridSize)) 235 | 236 | end 237 | 238 | 239 | 240 | 241 | 242 | 243 | end -------------------------------------------------------------------------------- /src/Lattice.jl: -------------------------------------------------------------------------------- 1 | module LatticeStruct 2 | 3 | export Lattice, FillSites!, FillBonds!, FillLattice!, GetBCPhase, ApplyBCToSite 4 | 5 | using LinearAlgebra, Bijections 6 | 7 | using ..TightBindingToolkit.Useful: Meshgrid 8 | using ..TightBindingToolkit.UCell: Bond, UnitCell 9 | 10 | 11 | @doc """ 12 | ```julia 13 | GetMaxCoordinationNumber(uc::UnitCell) --> Int64 14 | ``` 15 | Returns the maximum coordination number amongs all the sublattices of the `UnitCell`. 16 | 17 | """ 18 | function GetMaxCoordinationNumber(uc::UnitCell) :: Int64 19 | 20 | coords = zeros(Int64, length(uc.basis)) 21 | for bond in uc.bonds 22 | 23 | coords[bond.base] += 1 24 | end 25 | 26 | return max(coords...) 27 | end 28 | 29 | 30 | @doc """ 31 | `Lattice{T}` is a data type representing a general real-space lattice constructed out of a `UnitCell{T}`. 32 | 33 | # Attributes 34 | - `uc :: UnitCell{T}`: Unit Cell of the lattice. 35 | - `size :: Vector{Int64}`: size of the lattice in units of the UnitCell `primitives`. 36 | - `length :: Int64`: total number of sites. 37 | - `sites :: Dict{ Tuple{Int64, Vector{Int64}}, Int64}` : a dictionary with key = (sublattice, offset), and the corresponding value being the site number on the lattice. 38 | - `positions :: Vector{ Vector{Float64}}`: real-space positions of all the lattice sites. 39 | - `fields :: Vector{ Vector{Float64}}`: the fields on all the lattice sites. 40 | - `bondSites :: Matrix{Int64}`: a matrix with `lattice.length` rows such that `bondSites[s, i]` is the site number of the ith neighbour of site-s on the lattice. 41 | - `bondDists :: Matrix{Float64}`: a matrix with `lattice.length` rows such that `bondDists[s, i]` is the distance to the ith neighbour of site-s on the lattice. 42 | - `bondLabels :: Matrix{String}`: a matrix with `lattice.length` rows such that `bondLabels[s, i]` is the label of the bond to the ith neighbour of site-s on the lattice. 43 | - `bondMats :: Matrix{Array{ComplexF64, T}}`: a matrix with `lattice.length` rows such that `bondMats[s, i]` is the `Array{ComplexF64, T}` of the bond connecting to the ith neighbour of site-s on the lattice. 44 | 45 | Initialize this structure using 46 | ```julia 47 | Lattice( uc::UnitCell{T}, size::Vector{Int64} ; null_dist::Float64 = -1.0, null_label::String = "-") 48 | ``` 49 | 50 | where `null_dist` and `null_label` are used for bonds which are not allowed due to given boundary conditions, but still tracked in the code. 51 | """ 52 | mutable struct Lattice{T} 53 | 54 | uc :: UnitCell{T} 55 | size :: Vector{Int64} 56 | length :: Int64 57 | """ 58 | All sites and fields 59 | """ 60 | sites :: Bijection{Int64, Tuple{Int64, Vector{Int64}}} 61 | positions :: Bijection{Int64, Vector{Float64}} 62 | fields :: Vector{ Vector{Float64}} 63 | """ 64 | Bond information 65 | """ 66 | bondSites :: Matrix{Int64} 67 | bondDists :: Matrix{Float64} 68 | bondLabels :: Matrix{String} 69 | bondMats :: Matrix{Array{ComplexF64, T}} 70 | bondShifts :: Matrix{Vector{Int64}} 71 | 72 | function Lattice( uc::UnitCell{T}, size::Vector{Int64} ; null_dist::Float64 = -1.0, null_label::String = "-") where {T} 73 | 74 | Ncoords = GetMaxCoordinationNumber(uc) 75 | L = length(uc.basis) * prod(size) 76 | 77 | bondSites = zeros( Int64, L, Ncoords) 78 | bondDists = null_dist .* ones( Float64, L, Ncoords) 79 | bondLabels = repeat([null_label], L, Ncoords) 80 | bondMats = repeat([zeros(ComplexF64, repeat([uc.localDim], T)...)], L, Ncoords) 81 | bondShifts = repeat([zeros(Int64, length(uc.primitives))], L, Ncoords) 82 | 83 | return new{T}( uc, size, L, Bijection{ Int64, Tuple{Int64, Vector{Int64}}}(), Bijection{ Int64, Vector{Float64}}(), Vector{Float64}[], bondSites, bondDists, bondLabels, bondMats, bondShifts) 84 | end 85 | end 86 | 87 | 88 | @doc """ 89 | ```julia 90 | FillSites!(lattice::Lattice{T}) 91 | ``` 92 | Fills all the sites, positions, and fields of the lattice using information in the `UnitCell`. 93 | 94 | """ 95 | function FillSites!(lattice::Lattice{T} ; precision::Int64 = 8) where {T} 96 | 97 | offsets = collect.(Meshgrid(lattice.size .- Ref(1) ; starts = zeros(Int64, length(lattice.size)))) 98 | site = 1 99 | 100 | for offset in offsets 101 | for (b, basis) in enumerate(lattice.uc.basis) 102 | 103 | position = sum(offset .* lattice.uc.primitives) + basis 104 | 105 | lattice.sites[site] = (b, offset) 106 | lattice.positions[site] = round.(position, digits = precision) 107 | site = site + 1 108 | 109 | push!(lattice.fields, lattice.uc.fields[b]) 110 | 111 | end 112 | end 113 | 114 | lattice.positions[0] = round.(999 * ones(Float64, length(lattice.uc.primitives)), digits = precision) 115 | lattice.sites[0] = (0, zeros(Int64, length(lattice.uc.primitives))) 116 | 117 | end 118 | 119 | @doc """ 120 | ```julia 121 | ApplyBCToSite(site::Vector{Int64}, L::Vector{Int64}, BC::Vector{ComplexF64}) --> Vector{Int64} 122 | ``` 123 | Returns the effective real-space offset of `site` given a lattice size `L`, and boundary conditions `BC`. 124 | 125 | """ 126 | function ApplyBCToSite(site::Vector{Int64}, L::Vector{Int64}, BC::Vector{ComplexF64}) :: Vector{Int64} 127 | 128 | BCStrength = Bool.(abs.(BC)) ##### strength of the boundary condition refers to it being 1 (for a periodic system with arbitrary phase), or 0 for open systems. 129 | BCPhase = angle.(BC) 130 | 131 | LEffective = L .* BCStrength + .!(BCStrength) ##### Leff_i = L if system is closed, or 1 if system is open along that dimension 132 | return mod.(site, LEffective) + (.!(BCStrength)) .* (site) ##### returns site_i mod L_i if system is closed, or site_i if system is open along that dimension. 133 | end 134 | 135 | 136 | @doc """ 137 | ```julia 138 | GetBCPhase(site::Vector{Int64}, L::Vector{Int64}, BC::Vector{ComplexF64}) --> ComplexF64 139 | ``` 140 | Returns the effective phase of `site` given a lattice size `L`, and boundary conditions `BC`. 141 | 142 | """ 143 | function GetBCPhase(site::Vector{Int64}, L::Vector{Int64}, BC::Vector{ComplexF64}) :: ComplexF64 144 | 145 | BCPhase = angle.(BC) ##### the phase, ϕ_i corresponding to the boundary condition along each dimension 146 | phase = sum((div.(site, L, Ref(RoundDown))) .* BCPhase) ##### a vector of [θ_i] s.t. θ_i = n_i * ϕ_i, where n_i = site_i ÷ L_i 147 | return exp(im * phase) 148 | 149 | end 150 | 151 | 152 | @doc """ 153 | ```julia 154 | FillBonds!(lattice::Lattice{T} ; null_dist::Float64 = -1.0, null_label::String = "-" ) where {T} 155 | ``` 156 | Fills all the bond information in the lattice using the bonds in `UnitCell`. 157 | 158 | """ 159 | function FillBonds!(lattice::Lattice{T} ; null_dist::Float64 = -1.0, null_label::String = "-" ) where {T} 160 | 161 | bases = getproperty.(lattice.uc.bonds, :base) 162 | coord = ones(Int64, lattice.length) 163 | 164 | lookup = inv(lattice.sites) 165 | 166 | flippedIndices = collect(T:-1:1) 167 | 168 | for (s, site) in lattice.sites 169 | 170 | sub, offset = site 171 | 172 | OutgoingBonds = lattice.uc.bonds[findall(==(sub), bases)] 173 | 174 | for bond in OutgoingBonds 175 | 176 | targetOffset = ApplyBCToSite(offset + bond.offset, lattice.size, lattice.uc.BC) 177 | targetPhase = GetBCPhase( offset + bond.offset, lattice.size, lattice.uc.BC) 178 | 179 | target = get(lookup, (bond.target, targetOffset), 0) 180 | 181 | lattice.bondSites[ s, coord[s]] = target 182 | lattice.bondDists[ s, coord[s]] = !iszero(target) * bond.dist + iszero(target) * null_dist 183 | lattice.bondLabels[s, coord[s]] = iszero(target) ? null_label : bond.label 184 | lattice.bondMats[ s, coord[s]] = !iszero(target) * targetPhase * bond.mat 185 | lattice.bondShifts[s, coord[s]] = div.((offset + bond.offset), lattice.size, Ref(RoundDown)) 186 | 187 | coord[s] = coord[s] + 1 188 | 189 | end 190 | 191 | end 192 | end 193 | 194 | 195 | @doc """ 196 | ```julia 197 | FillLattice!( lattice::Lattice{T} ; null_dist::Float64 = -1.0, null_label::String = "-") where {T} 198 | ``` 199 | Wrapper function to fill both site and bond information in the lattice. 200 | 201 | """ 202 | function FillLattice!( lattice::Lattice{T} ; null_dist::Float64 = -1.0, null_label::String = "-", precision::Int64 = 8) where {T} 203 | 204 | FillSites!(lattice ; precision = precision) 205 | FillBonds!(lattice ; null_dist = null_dist, null_label = null_label) 206 | 207 | end 208 | 209 | 210 | 211 | 212 | 213 | end -------------------------------------------------------------------------------- /src/LatticeHamiltonian.jl: -------------------------------------------------------------------------------- 1 | module LatHam 2 | export FillHamiltonian, LatticeHamiltonian, DiagonalizeHamiltonian!, Slater, SingleParticleFidelity, SlaterOverlap, GaugeFix! 3 | 4 | using ..TightBindingToolkit.LatticeStruct: Lattice 5 | import ..TightBindingToolkit.Hams: FillHamiltonian, DiagonalizeHamiltonian! 6 | 7 | using LinearAlgebra 8 | 9 | 10 | @doc """ 11 | ```julia 12 | FillHamiltonian( lattice::Lattice{2} ) 13 | ``` 14 | Fills the Hamiltonian matrix for a given `Lattice` object using the bonds stored in the `Lattice` object. 15 | """ 16 | function FillHamiltonian( lattice::Lattice{2} ) 17 | 18 | localDim = lattice.uc.localDim 19 | Hamsize = lattice.length * localDim 20 | Ham = zeros( ComplexF64 , Hamsize , Hamsize ) 21 | 22 | for site in 1:lattice.length 23 | ############# On-Site terms 24 | Ham[localDim * (site - 1) + 1 : localDim * (site), localDim * (site - 1) + 1 : localDim * (site)] += sum( lattice.fields[site] .* lattice.uc.OnSiteMats ) 25 | 26 | ############# Bond terms 27 | for neighbour in 1:length(lattice.bondSites[site, :]) 28 | 29 | if lattice.bondSites[site , neighbour] != 0 30 | Ham[ localDim*(site-1) + 1 : localDim*(site) , localDim*(lattice.bondSites[site, neighbour]-1) + 1 : localDim*(lattice.bondSites[site, neighbour]) ] += lattice.bondMats[site, neighbour] 31 | 32 | if lattice.bondDists[site, neighbour] > 0.0 33 | Ham[ localDim*(lattice.bondSites[site, neighbour]-1) + 1 : localDim*(lattice.bondSites[site, neighbour]) , localDim*(site-1) + 1 : localDim*(site)] += adjoint(lattice.bondMats[site, neighbour]) 34 | end 35 | end 36 | end 37 | end 38 | 39 | return Ham 40 | end 41 | 42 | @doc """ 43 | `LatticeHamiltonian` is a data type representing a general real-space Hamiltonian constructed using a `Lattice` object. 44 | 45 | # Attributes 46 | - `H :: Matrix{ComplexF64}`: Hamiltonian matrix. 47 | - `bands :: Vector{Float64}`: eigenvalues of the Hamiltonian. 48 | - `states :: Matrix{ComplexF64}`: eigenvectors of the Hamiltonian. 49 | - `is_BdG :: Bool`: whether the Hamiltonian is a BdG Hamiltonian or not. 50 | - `bandwidth :: Tuple{Float64, Float64}`: minimum and maximum eigenvalues of the Hamiltonian. 51 | 52 | Initialize this structure using 53 | ```julia 54 | LatticeHamiltonian( lattice::Lattice{2} ) 55 | ``` 56 | """ 57 | mutable struct LatticeHamiltonian 58 | 59 | H :: Matrix{ComplexF64} 60 | bands :: Vector{Float64} 61 | states :: Matrix{ComplexF64} 62 | is_BdG :: Bool 63 | bandwidth :: Tuple{Float64, Float64} 64 | 65 | function LatticeHamiltonian(lattice::Lattice{2}) 66 | 67 | localDim = lattice.uc.localDim 68 | Hamsize = lattice.length * localDim 69 | 70 | return new{}(FillHamiltonian(lattice), Vector{Float64}(undef, Hamsize), Matrix{ComplexF64}(undef, Hamsize, Hamsize), false, (0.0, 0.0)) 71 | end 72 | end 73 | 74 | 75 | @doc """ 76 | ```julia 77 | DiagonalizeHamiltonian!(H::LatticeHamiltonian) 78 | ``` 79 | Diagonalizes the Hamiltonian stored in `H` and stores the eigenvalues and eigenvectors in `H.bands` and `H.states` respectively. 80 | Calculates the bandwidth too. 81 | """ 82 | function DiagonalizeHamiltonian!(H::LatticeHamiltonian) 83 | 84 | sol = eigen(Hermitian(H.H)) 85 | H.bands = sol.values 86 | H.states = sol.vectors 87 | H.bandwidth = (H.bands[begin], H.bands[end]) 88 | 89 | end 90 | 91 | 92 | function GaugeFix!(H::LatticeHamiltonian, gauge::Tuple{Int64, Float64} = (1, 0.0)) 93 | 94 | site, DesiredPhase = gauge[1], gauge[2] 95 | 96 | NormCheck = (abs2.(H.states[site:end, :]) .> 0.0) 97 | NonTrivialNormSites = findfirst.(==(1), eachcol(NormCheck)) .+ (site - 1) 98 | 99 | phases = angle.(getindex.(Ref(H.states), NonTrivialNormSites, 1:length(H.bands))) 100 | phaseShift = exp.(im .* (DesiredPhase .- phases)) 101 | 102 | H.states = H.states .* phaseShift' 103 | 104 | return (NonTrivialNormSites, phaseShift) 105 | 106 | end 107 | 108 | 109 | function SingleParticleFidelity(H1::LatticeHamiltonian, H2::LatticeHamiltonian, states::Vector{Int64} = collect(1:length(H1.bands)) ; fixGauge::Bool = true, gauge::Tuple{Int64, Float64} = (1, 0.0)) :: Matrix{ComplexF64} 110 | 111 | @assert size(H1.H) == size(H2.H) "The two hamiltonians must be the same size!" 112 | 113 | if fixGauge 114 | GaugeFix!(H1, gauge) 115 | GaugeFix!(H2, gauge) 116 | end 117 | 118 | fidelity = adjoint(H1.states[:, states]) * H2.states[:, states] 119 | 120 | return fidelity 121 | end 122 | 123 | 124 | function Slater(H::LatticeHamiltonian, positions::Vector{Int64}, states::Vector{Int64} = collect(1:length(positions))) :: ComplexF64 125 | 126 | @assert length(positions) == length(states) "Number of particles must be equal to the number of positions to be filled!" 127 | 128 | SlaterDet = H.states[positions, states] 129 | SlaterDet = det(SlaterDet) 130 | 131 | return SlaterDet 132 | end 133 | 134 | 135 | function SlaterOverlap(H1::LatticeHamiltonian, H2::LatticeHamiltonian, states::Vector{Int64} = collect(1:Int64(length(H1.bands)//2)); fixGauge::Bool = true, gauge::Tuple{Int64, Float64} = (1, 0.0)) :: ComplexF64 136 | 137 | return det((SingleParticleFidelity(H1, H2, states ; fixGauge = fixGauge, gauge = gauge))) 138 | end 139 | 140 | end -------------------------------------------------------------------------------- /src/LatticeModel.jl: -------------------------------------------------------------------------------- 1 | module LatModel 2 | 3 | export LatticeModel , GetMu! , GetFilling! , GetGr!, SolveModel!, GetGap! 4 | 5 | using ..TightBindingToolkit.Useful: Meshgrid, DistFunction, DeriDistFunction, FFTArrayofMatrix, BinarySearch 6 | using ..TightBindingToolkit.LatticeStruct: Lattice 7 | using ..TightBindingToolkit.LatHam: LatticeHamiltonian 8 | using ..TightBindingToolkit.TBModel: FindFilling, GetCount 9 | 10 | import ..TightBindingToolkit.TBModel: GetMu!, GetFilling!, GetGr!, SolveModel!, GetGap! 11 | 12 | 13 | mutable struct LatticeModel 14 | lattice :: Lattice{2} 15 | Ham :: LatticeHamiltonian 16 | """ 17 | Thermodynamic properties 18 | """ 19 | T :: Float64 ##### Temperature 20 | filling :: Float64 ##### Filling fraction 21 | mu :: Float64 ##### Chemical potential 22 | stat :: Int64 ##### +1 for bosons, -1 for fermions 23 | gap :: Float64 24 | """ 25 | Correlations 26 | """ 27 | Gr :: Matrix{ComplexF64} 28 | 29 | function LatticeModel(lattice::Lattice{2}, Ham::LatticeHamiltonian ; T::Float64=1e-3, filling::Float64=-1.0, mu::Float64=0.0, stat::Int64=-1) 30 | 31 | localDim = lattice.uc.localDim 32 | Hamsize = lattice.length * localDim 33 | return new{}(lattice, Ham, T, filling, mu, stat, -999.0, Matrix{ComplexF64}(undef, Hamsize, Hamsize)) 34 | ##### Chosen the default value of filling to be -1 which is unphysical so that the code knows when filling has not been provided and has to be calculated from mu instead! 35 | end 36 | end 37 | 38 | 39 | function GetMu!(M::LatticeModel ; mu_tol::Float64 = 0.001, filling_tol::Float64 = 1e-6) 40 | 41 | if M.T≈0.0 42 | M.mu = (M.Ham.bands[floor(Int64, length(M.Ham.bands)*M.filling)] + M.Ham.bands[floor(Int64, length(M.Ham.bands)*M.filling)+1])/2 43 | else 44 | M.mu = BinarySearch(M.filling, M.Ham.bandwidth, FindFilling, (M.Ham.bands, M.T, M.stat) ; x_tol=mu_tol, target_tol = filling_tol) 45 | @info "Found chemical potential μ = $(M.mu) for given filling = $(M.filling)." 46 | end 47 | end 48 | 49 | 50 | function GetFilling!(M::LatticeModel) 51 | 52 | if M.T≈0.0 53 | M.filling = searchsortedlast(M.Ham.bands, M.mu) / length(M.Ham.bands) 54 | else 55 | M.filling = FindFilling( M.mu, M.Ham.bands, M.T, M.stat) 56 | end 57 | end 58 | 59 | 60 | function GetGr!(M::LatticeModel) 61 | 62 | quasiCount = GetCount(M.Ham.bands, M.mu, M.T, M.stat) ##### Matrix with 1s and 0s along the diagonal. The count of the quasiparticles at each k point determined by the bands and the chemical potential 63 | M.Gr = transpose(M.Ham.states * quasiCount * adjoint(M.Ham.states)) 64 | end 65 | 66 | 67 | function GetGap!(M::LatticeModel) 68 | 69 | index = floor(Int64, length(M.Ham.bands)*M.filling) 70 | M.gap = M.Ham.bands[clamp(index + 1, 1, length(M.Ham.bands))] - M.Ham.bands[clamp(index, 1, length(M.Ham.bands))] 71 | end 72 | 73 | function SolveModel!(M::LatticeModel ; get_correlations::Bool = true, get_gap::Bool = false, verbose::Bool = true, mu_tol::Float64 = 1e-3, filling_tol::Float64 = 1e-6) 74 | @assert M.Ham.is_BdG==false "BdG Hamiltonians should be solved using a BdGModel" 75 | 76 | if M.filling<0 ##### Must imply that filling was not provided by user and hence needs to be calculated from given mu 77 | GetFilling!(M) 78 | else 79 | GetMu!(M ; mu_tol = mu_tol, filling_tol = filling_tol) 80 | end 81 | 82 | if get_gap 83 | GetGap!(M) 84 | end 85 | 86 | if get_correlations 87 | GetGr!(M) 88 | end 89 | 90 | if verbose 91 | @info "System Filled!" 92 | end 93 | end 94 | 95 | 96 | end -------------------------------------------------------------------------------- /src/LatticeSymmetries.jl: -------------------------------------------------------------------------------- 1 | module LatticeSymmetries 2 | export Translations, Degeneracies, FindQuantumNumbers 3 | 4 | using LinearAlgebra, StatsBase 5 | 6 | using ..TightBindingToolkit.UCell: UnitCell 7 | using ..TightBindingToolkit.LatticeStruct: Lattice 8 | using ..TightBindingToolkit.LatHam: LatticeHamiltonian 9 | using ..TightBindingToolkit.LatModel: LatticeModel 10 | 11 | 12 | function Translations(lattice::Lattice{T} ; primitive::Int64, with_local::Bool = false) :: Matrix{ComplexF64} where {T} 13 | 14 | Ta = zeros(ComplexF64, lattice.length, lattice.length) 15 | id = Matrix(I, length(lattice.uc.primitives), length(lattice.uc.primitives)) 16 | 17 | for j in 1:lattice.length 18 | 19 | translated = mod.(lattice.sites[j][end] + id[primitive, :], lattice.size) 20 | Ta[lattice.sites((lattice.sites[j][begin], translated)), j] = 1.0 21 | end 22 | 23 | if with_local 24 | Ta = kron(Ta, Matrix(I, lattice.uc.localDim, lattice.uc.localDim)) 25 | end 26 | 27 | return Ta 28 | end 29 | 30 | 31 | function Degeneracies(H::LatticeHamiltonian ; tol::Int64 = 6) :: Vector{UnitRange{Int64}} 32 | 33 | counts = countmap(round.(H.bands, digits = tol)) 34 | 35 | if haskey(counts, 0.0) && haskey(counts, -0.0) 36 | counts[0.0] = counts[0.0] + counts[-0.0] 37 | delete!(counts, -0.0) 38 | end 39 | 40 | energies = sort(collect(keys(counts))) 41 | counts = getindex.(Ref(counts), energies) 42 | 43 | endings = cumsum(counts) 44 | beginnings = [1; endings[1:end-1] .+ 1] 45 | 46 | degeneracies= UnitRange.(beginnings, endings) 47 | 48 | return degeneracies 49 | end 50 | 51 | 52 | function FindQuantumNumbers(M::LatticeModel, SymMatrix::Matrix{ComplexF64} ; tol::Int64 = 6, till_band::Int64) :: Vector{Vector{ComplexF64}} 53 | 54 | degens = Degeneracies(M.Ham ; tol = tol) 55 | 56 | nBlocks = searchsortedfirst(cumsum(length.(collect.(degens))), till_band) 57 | wavefunctions = getindex.(Ref(M.Ham.states), Ref(1:size(M.Ham.states, 1)), degens[1:nBlocks]) 58 | 59 | SymMatrixBlocks = adjoint.(wavefunctions) .* Ref(SymMatrix) .* wavefunctions 60 | quantumNumbers = eigvals.(SymMatrixBlocks) 61 | 62 | return quantumNumbers 63 | end 64 | 65 | 66 | end -------------------------------------------------------------------------------- /src/Model.jl: -------------------------------------------------------------------------------- 1 | module TBModel 2 | 3 | export Model , FindFilling , GetMu! , GetFilling! , GetCount , GetGk! , GetGr!, SolveModel!, GetGap!, FreeEnergy, GetOrderParameter 4 | 5 | using ..TightBindingToolkit.Useful: Meshgrid, DistFunction, DeriDistFunction, FFTArrayofMatrix, BinarySearch 6 | using ..TightBindingToolkit.UCell: UnitCell, Bond 7 | using ..TightBindingToolkit.BZone: BZ, MomentumPhaseFFT 8 | using ..TightBindingToolkit.Parameters: Param 9 | using ..TightBindingToolkit.Hams:Hamiltonian 10 | 11 | using LinearAlgebra, Logging, Statistics 12 | 13 | 14 | @doc """ 15 | `Model` is a data type representing a general Tight Binding system. 16 | 17 | # Attributes 18 | - `uc :: UnitCell`: the Unit cell of the lattice. 19 | - `bz :: BZ`: The discretized Brillouin Zone. 20 | - `Ham :: Hamiltonian`: the Hamiltonian at all momentum-points. 21 | - `T :: Float64`: the temperature of the system. 22 | - `filling :: Float64`: The filling of the system. 23 | - `mu :: Float64`: The chemical potential of the system. 24 | - `stat :: Int64` : ±1 for bosons and fermions. 25 | - `gap :: Float64` : the energy gap of excitations at the given filling. 26 | - `Gk :: Array{Matrix{ComplexF64}}` : An array (corresponding to the grid of k-points in `BZ`) of Greens functions. 27 | 28 | Initialize this structure using 29 | ```julia 30 | Model(uc::UnitCell, bz::BZ, Ham::Hamiltonian ; T::Float64=1e-3, filling::Float64=-1.0, mu::Float64=0.0, stat::Int64=-1) 31 | ``` 32 | You can either input a filling, or a chemical potential. The corresponding μ for a given filling, or filling for a given μ is automatically calculated. 33 | """ 34 | mutable struct Model 35 | uc :: UnitCell{2} 36 | bz :: BZ 37 | Ham :: Hamiltonian 38 | """ 39 | Thermodynamic properties 40 | """ 41 | T :: Float64 ##### Temperature 42 | filling :: Float64 ##### Filling fraction 43 | mu :: Float64 ##### Chemical potential 44 | stat :: Int64 ##### +1 for bosons, -1 for fermions 45 | gap :: Float64 46 | """ 47 | Correlations 48 | """ 49 | Gk :: Array{Matrix{ComplexF64}} 50 | Gr :: Array{Matrix{ComplexF64}} 51 | 52 | Model(uc::UnitCell{2}, bz::BZ, Ham::Hamiltonian ; T::Float64=1e-3, filling::Float64=-1.0, mu::Float64=0.0, stat::Int64=-1) = new{}(uc, bz, Ham, T, filling, mu, stat, -999.0, Array{Matrix{ComplexF64}}(undef, zeros(Int64, length(uc.primitives))...), Array{Matrix{ComplexF64}}(undef, zeros(Int64, length(uc.primitives))...)) 53 | ##### Chosen the default value of filling to be -1 which is unphysical so that the code knows when filling has not been provided and has to be calculated from mu instead! 54 | end 55 | 56 | 57 | @doc """ 58 | ```julia 59 | FindFilling(bands::Vector{Float64}, mu::Float64, T::Float64, stat::Int64=-1) --> Float64 60 | ``` 61 | Find filling at given `T`=temperature and `mu`=chemical potential, for a given `bands`. 62 | 63 | """ 64 | function FindFilling( mu::Float64, bands::Vector{Float64}, T::Float64, stat::Int64=-1) :: Float64 65 | return sum(DistFunction(bands; T=T, mu=mu, stat=stat)) / length(bands) 66 | end 67 | 68 | function FindFilling( mu::Float64, bands::Array{Vector{Float64}, S}, T::Float64, stat::Int64) :: Float64 where {S} 69 | return sum(sum(DistFunction.(bands; T=T, mu=mu, stat=stat))) / sum(length.(bands)) 70 | end 71 | 72 | 73 | @doc """ 74 | ```julia 75 | GetMu!(M::Model; tol::Float64=0.001) 76 | ``` 77 | Function to get chemical potential for a given `Model`, within a tolerance. 78 | 79 | """ 80 | function GetMu!(M::Model ; guess::Float64 = 0.0, mu_tol::Float64 = 0.001, filling_tol::Float64 = 1e-6) 81 | ##### TODO Get an initial guess and pass to BinarySearch 82 | if M.T≈0.0 83 | energies = sort(reduce(vcat, M.Ham.bands)) 84 | M.mu = (energies[floor(Int64, length(energies)*M.filling)] + energies[floor(Int64, length(energies)*M.filling)+1])/2 85 | else 86 | M.mu = BinarySearch(M.filling, M.Ham.bandwidth, FindFilling, (M.Ham.bands, M.T, M.stat) ; initial = guess, x_tol=mu_tol, target_tol = filling_tol) 87 | @info "Found chemical potential μ = $(M.mu) for given filling = $(M.filling)." 88 | end 89 | end 90 | 91 | 92 | @doc """ 93 | ```julia 94 | GetFilling!(M::Model) 95 | ``` 96 | Find filling for a given `Model`. 97 | 98 | """ 99 | function GetFilling!(M::Model) 100 | if M.T≈0.0 101 | energies = sort(reduce(vcat, M.Ham.bands)) 102 | M.filling = searchsortedlast(energies, M.mu) / length(energies) 103 | else 104 | M.filling = FindFilling( M.mu, M.Ham.bands, M.T, M.stat) 105 | end 106 | end 107 | 108 | 109 | @doc """ 110 | ```julia 111 | GetCount(Es::Vector{Float64}, mu::Float64, T::Float64, stat::Int64) --> Matrix{Float64} 112 | ``` 113 | Function to return a diagonal matrix whose entries are M[i, i] = θ(-(E^i(k)-μ)) ----> 1 if the energy is below the chemical potential, otherwise 0. 114 | 115 | """ 116 | function GetCount(Es::Vector{Float64}, mu::Float64, T::Float64, stat::Int64) :: Matrix{Float64} 117 | return diagm(DistFunction(Es; T=T, mu=mu, stat=stat)) 118 | end 119 | 120 | 121 | @doc """ 122 | ```julia 123 | GetGk!(M::Model) 124 | ``` 125 | Finding the equal-time Greens functions in momentum space of a `Model`. 126 | 127 | """ 128 | function GetGk!(M::Model) 129 | quasiCount = GetCount.(M.Ham.bands, Ref(M.mu), Ref(M.T), Ref(M.stat)) ##### Matrix with 1s and 0s along the diagonal. The count of the quasiparticles at each k point determined by the bands and the chemical potential 130 | M.Gk = transpose.(M.Ham.states .* quasiCount .* adjoint.(M.Ham.states)) 131 | end 132 | 133 | 134 | @doc """ 135 | ```julia 136 | GetGr!(M::Model) 137 | ``` 138 | Finding the equal-time Greens functions in real space of a `Model`. 139 | 140 | """ 141 | function GetGr!(M::Model) 142 | 143 | Gr = FFTArrayofMatrix(M.Gk) 144 | phaseShift = MomentumPhaseFFT(M.bz, M.uc) 145 | M.Gr = Gr .* phaseShift 146 | end 147 | 148 | 149 | @doc """ 150 | ```julia 151 | SolveModel!(M::Model) 152 | ``` 153 | one-step function to find all the attributes in Model after it has been initialized. 154 | 155 | """ 156 | function SolveModel!(M::Model ; mu_guess::Float64 = M.Ham.bandwidth[1] + M.filling * (M.Ham.bandwidth[2] - M.Ham.bandwidth[1]), get_correlations::Bool = true, get_gap::Bool = false, verbose::Bool = true, mu_tol::Float64 = 1e-3, filling_tol::Float64 = 1e-6) 157 | @assert M.Ham.is_BdG==false "BdG Hamiltonians should be solved using a BdGModel" 158 | ##### TODO Pass initial guess of chemical potential to GetMu! if provided by user 159 | if M.filling<0 ##### Must imply that filling was not provided by user and hence needs to be calculated from given mu 160 | GetFilling!(M) 161 | else 162 | GetMu!(M ; guess = mu_guess, mu_tol = mu_tol, filling_tol = filling_tol) 163 | end 164 | 165 | if get_gap 166 | 167 | energies = sort(reduce(vcat, M.Ham.bands)) 168 | M.gap = energies[min(floor(Int64, length(energies)*M.filling) + 1, length(energies))] - energies[floor(Int64, length(energies)*M.filling)] 169 | end 170 | 171 | if get_correlations 172 | GetGk!(M) 173 | GetGr!(M) 174 | end 175 | 176 | if verbose 177 | @info "System Filled!" 178 | end 179 | end 180 | 181 | 182 | @doc """ 183 | ```julia 184 | GetGap!(M::BdGModel) 185 | ``` 186 | Calculate the BdG gap of the system. 187 | """ 188 | function GetGap!(M::Model) 189 | energies = sort(reduce(vcat, M.Ham.bands)) 190 | M.gap = energies[min(floor(Int64, length(energies)*M.filling) + 1, length(energies))] - energies[floor(Int64, length(energies)*M.filling)] 191 | 192 | end 193 | 194 | 195 | @doc """ 196 | ```julia 197 | FreeEnergy(M::Model; F0::Float64 = 0.0) --> Float64 198 | ``` 199 | Calculate the free energy of the given `Model`. 200 | 201 | """ 202 | function FreeEnergy(M::Model; F0::Float64 = 0.0) :: Float64 203 | 204 | Es = reduce(vcat, M.Ham.bands) 205 | F = log.(1 .+ exp.(-(Es .- M.mu) / (M.T))) 206 | F = -M.T * (sum(F) / length(F)) 207 | 208 | return F - F0 209 | end 210 | 211 | 212 | function GetOrderParameter(M::Model, param::Param) 213 | 214 | order = Float64[] 215 | for bond in param.unitBonds 216 | 217 | index = mod.((-bond.offset) , M.bz.gridSize) .+ ones(Int64, length(bond.offset)) 218 | ##### TODO : the extra - sign in offset is because right now G[r] = ===> NEED TO FIX THIS 219 | b1 = M.uc.localDim * (bond.base - 1) + 1 220 | b2 = M.uc.localDim * (bond.target - 1) + 1 221 | G = M.Gr[index...][b1 : b1 + M.uc.localDim - 1, b2 : b2 + M.uc.localDim - 1] 222 | 223 | decomposition = (tr( adjoint(bond.mat) * G) / (tr(adjoint(bond.mat) * bond.mat))) 224 | push!(order, real(decomposition)) 225 | end 226 | 227 | order = sum(order) / length(order) 228 | end 229 | 230 | end -------------------------------------------------------------------------------- /src/Params.jl: -------------------------------------------------------------------------------- 1 | module Parameters 2 | export Param, AddAnisotropicBond!, AddIsotropicBonds!, AddSimilarBonds! , CreateUnitCell!, ModifyUnitCell!, GetParams, Lookup 3 | 4 | using ..TightBindingToolkit.Useful: GetAllOffsets 5 | using ..TightBindingToolkit.UCell: Bond, UnitCell, IsSameBond 6 | using ..TightBindingToolkit.DesignUCell: RemoveBonds! 7 | import ..TightBindingToolkit.DesignUCell: AddAnisotropicBond!, AddIsotropicBonds!, Lookup 8 | 9 | using LinearAlgebra, Logging 10 | 11 | @doc """ 12 | `Param{T}` is a data type representing a general tight-binding parameter, which can span multiple bonds. 13 | 14 | # Attributes 15 | - `value :: Vector{ Float64 }`: the strength of the parameter (or even the full history of it if its changed). 16 | - `unitBonds :: Vector{ Bond{T} }`: All the bonds this parameter lives on. These bonds are supposed to have "unit" strength, and ultimately get scaled by the `value` when making the `UnitCell`. 17 | - `label :: String`: some string label to mark the parameter. 18 | - `dist :: Float64`: the distance of the bonds the parameter lives on. 19 | 20 | Initialize this structure using 21 | ```julia 22 | Param( value::Float64 ) 23 | Param( value::Float64 , rank::Int64 ) 24 | ``` 25 | 26 | """ 27 | mutable struct Param{T, R} 28 | value :: Vector{R} 29 | unitBonds :: Vector{Bond{T}} 30 | label :: String 31 | dist :: Float64 32 | 33 | function Param( value::R ) where {R<:Number} 34 | @warn "Rank not passed to Param object. Choosing default value of 2!" 35 | return new{2, R}( R[value] , Bond{2}[], "", -1.0 ) 36 | end 37 | 38 | function Param( value::R, rank::Int64 ) where {R<:Number} 39 | return new{rank, R}( R[value] , Bond{rank}[], "", -1.0 ) 40 | end 41 | end 42 | 43 | 44 | @doc """ 45 | ```julia 46 | AddAnisotropicBond!( param::Param, uc::UnitCell , base::Int64 , target::Int64 , offset::Vector{Int64} , mat::Number , dist::Float64, label::String ) 47 | AddAnisotropicBond!( param::Param, uc::UnitCell , base::Int64 , target::Int64 , offset::Vector{Int64} , mat::Matrix{<:Number} , dist::Float64, label::String ) 48 | ``` 49 | Add a bond with the given attributes to `param`. 50 | If given `mat` attribute is a number, it is converted into a 1x1 matrix when entered into the bond. 51 | 52 | """ 53 | function AddAnisotropicBond!( param::Param{T, R}, uc::UnitCell{T2} , base::Int64 , target::Int64 , offset::Vector{Int64} , mat::Array{<:Number, T} , dist::Float64, label::String ) where {T, R, T2} 54 | 55 | if base <= length(uc.basis) && target <= length(uc.basis) 56 | if norm( sum(offset .* uc.primitives) .+ (uc.basis[target] .- uc.basis[base] ) ) ≈ dist 57 | push!( param.unitBonds , Bond( base , target , offset , ComplexF64.(mat) , dist, label ) ) 58 | 59 | if param.label=="" 60 | param.label = label 61 | param.dist = dist 62 | else 63 | @assert param.label == label && param.dist == dist "Inconsistent label or distance given" 64 | end 65 | else 66 | @warn "Issue with bond between $(base) and $(target) at distance $(dist)" 67 | end 68 | else 69 | @warn "One or both of those basis sites, [$(base), $(target)] have not been added to the UnitCell object." 70 | end 71 | end 72 | 73 | function AddAnisotropicBond!( param::Param{T, R}, uc::UnitCell{T2} , base::Int64 , target::Int64 , offset::Vector{Int64} , mat::Number , dist::Float64, label::String ) where {T, R, T2} 74 | 75 | @assert uc.localDim == 1 76 | dims = repeat([uc.localDim], T) 77 | 78 | AddAnisotropicBond!( param, uc, base, target, offset, ComplexF64.(reshape([mat], dims...)), dist, label) 79 | end 80 | 81 | 82 | @doc """ 83 | ```julia 84 | AddIsotropicBonds!( param::Param, uc::UnitCell , dist::Float64 , mats::Number , label::String; checkOffsetRange::Int64=1 , subs::Vector{Int64}=collect(1:length(uc.basis))) 85 | AddIsotropicBonds!( param::Param, uc::UnitCell , dist::Float64 , mats::Matrix{<:Number} , label::String; checkOffsetRange::Int64=1 , subs::Vector{Int64}=collect(1:length(uc.basis)) ) 86 | ``` 87 | Add a set of "isotropic" bonds, which are the same for each pair of sites at the given distance. 88 | If given `mat` attribute is a number, it is converted into a 1x1 matrix when entered into the bond. 89 | The input `checkOffsetRange` must be adjusted depending on the input distance. 90 | The optional input `subs` is meant for isotropic bonds when only a subset of sublattices are involved. 91 | 92 | """ 93 | function AddIsotropicBonds!( param::Param{T, R}, uc::UnitCell{T2} , dist::Float64 , mat::Array{<:Number, T} , label::String; checkOffsetRange::Int64=2 , subs::Vector{Int64}=collect(1:length(uc.basis)) ) where {T, R, T2} 94 | 95 | offsets = GetAllOffsets(checkOffsetRange, length(uc.primitives)) 96 | 97 | for i in subs 98 | for j in subs 99 | for offset in offsets 100 | 101 | if norm( sum( offset.*uc.primitives ) + (uc.basis[j] - uc.basis[i] ) ) ≈ dist 102 | proposal = Bond(i, j, offset, ComplexF64.(mat), dist, label) 103 | 104 | if sum(IsSameBond.( Ref(proposal) , param.unitBonds ))==0 105 | push!( param.unitBonds , proposal ) 106 | 107 | if param.label=="" 108 | param.label = label 109 | param.dist = dist 110 | else 111 | @assert param.label == label && param.dist == dist "Inconsistent label or distance given" 112 | end 113 | end 114 | end 115 | end 116 | 117 | end 118 | end 119 | end 120 | 121 | function AddIsotropicBonds!( param::Param{T, R}, uc::UnitCell{T2} , dist::Float64 , mat::Number , label::String; checkOffsetRange::Int64=2 , subs::Vector{Int64}=collect(1:length(uc.basis))) where {T, R, T2} 122 | 123 | @assert uc.localDim == 1 124 | dims = repeat([uc.localDim], T) 125 | 126 | AddIsotropicBonds!( param, uc, dist, ComplexF64.(reshape([mat], dims...)), label; checkOffsetRange = checkOffsetRange , subs = subs) 127 | end 128 | 129 | 130 | @doc """ 131 | ```julia 132 | AddSimilarBond!(param::Param{T, R}, uc::UnitCell{T2}, bond::Bond{T} ; subs::Vector{Int64}=collect(1:length(uc.basis)), checkOffsetRange::Int64=2) where {T, R} 133 | AddSimilarBond!(param::Param{T, R}, uc::UnitCell{T2}, base::Int64, target::Int64, offset::Vector{Int64}, mat::Array{ComplexF64, T}, dist::Float64, label::String ; subs::Vector{Int64}=collect(1:length(uc.basis)), checkOffsetRange::Int64=2) where {T, R} 134 | ``` 135 | Function to add bonds which are not completely isotropic, but are still related by translation (not by the unit cell primitives but by the underlying lattice primitives). 136 | 137 | """ 138 | function AddSimilarBonds!(param::Param{T, R}, uc::UnitCell{T2}, bond::Bond{T} ; subs::Vector{Int64}=collect(1:length(uc.basis)), checkOffsetRange::Int64=2) where {T, R, T2} 139 | 140 | MotherParam = Param(1.0, 2) 141 | AddIsotropicBonds!(MotherParam, uc, bond.dist, bond.mat, bond.label ; checkOffsetRange = checkOffsetRange , subs = subs) 142 | 143 | offsetToMatch = (uc.basis[bond.target] - uc.basis[bond.base]) + sum(bond.offset .* uc.primitives) 144 | 145 | for trialBond in MotherParam.unitBonds 146 | 147 | if (trialBond.base in subs) && (trialBond.target in subs) 148 | offsetVector = (uc.basis[trialBond.target] - uc.basis[trialBond.base]) + sum(trialBond.offset .* uc.primitives) 149 | 150 | if isapprox(offsetVector, offsetToMatch, rtol = 1e-6, atol = 1e-6) 151 | AddAnisotropicBond!(param, uc, trialBond.base, trialBond.target, trialBond.offset, bond.mat, bond.dist, bond.label) 152 | 153 | elseif isapprox(offsetVector, -offsetToMatch, rtol = 1e-6, atol = 1e-6) 154 | AddAnisotropicBond!(param, uc, trialBond.target, trialBond.base, -trialBond.offset, bond.mat, bond.dist, bond.label) 155 | 156 | end 157 | end 158 | end 159 | 160 | end 161 | 162 | function AddSimilarBonds!(param::Param{T, R}, uc::UnitCell{T2}, base::Int64, target::Int64, offset::Vector{Int64}, mat::Array{ComplexF64, T}, dist::Float64, label::String ; subs::Vector{Int64}=collect(1:length(uc.basis)), checkOffsetRange::Int64=2) where {T, R, T2} 163 | 164 | AddSimilarBonds!(param, uc, Bond(base, target, offset, mat, dist, label) ; subs = subs, checkOffsetRange = checkOffsetRange) 165 | end 166 | 167 | 168 | @doc """ 169 | ```julia 170 | CreateUnitCell!(uc::UnitCell, param::Param , index::Int64=length(param.value)) 171 | CreateUnitCell!(uc::UnitCell, params::Vector{Param}, indices::Vector{Int64}=length.(getproperty.(params, :value))) 172 | ``` 173 | Add bonds corrsponding to a `param` to `UnitCell`, scaled with the `param.value[index]`. Also includes the broadcasted call. 174 | 175 | """ 176 | function CreateUnitCell!(uc::UnitCell{T}, param::Param{T, R} , index::Int64=length(param.value)) where {T, R} 177 | @assert !(param.label in getproperty.(uc.bonds, :label)) "Given label:$(param.label) already exists in the unit cell bonds" 178 | 179 | bonds = deepcopy(param.unitBonds) 180 | map(x -> x.mat = param.value[index] * x.mat, bonds) 181 | append!(uc.bonds, bonds) 182 | ##### ///TODO: Check this works properly!!! 183 | 184 | end 185 | 186 | function CreateUnitCell!(uc::UnitCell{T}, params::Vector{Param{T, R}}, indices::Vector{Int64}=length.(getproperty.(params, :value))) where {T, R} 187 | @assert length(indices)==length(params) "Inconsistent input of params" 188 | CreateUnitCell!.(Ref(uc), params, indices) 189 | end 190 | 191 | function CreateUnitCell!(uc::UnitCell{T}, params::Vector{Param{T}}, indices::Vector{Int64}=length.(getproperty.(params, :value))) where {T} 192 | @assert length(indices)==length(params) "Inconsistent input of params" 193 | CreateUnitCell!.(Ref(uc), params, indices) 194 | end 195 | 196 | 197 | @doc """ 198 | ```julia 199 | ModifyUnitCell!(uc::UnitCell, param::Param) 200 | ModifyUnitCell!(uc::UnitCell, params::Vector{Param}) 201 | ``` 202 | Modify all bonds in `UnitCell` corresponding to given `param`, taking the latest value in `param.value`. 203 | 204 | """ 205 | function ModifyUnitCell!(uc::UnitCell{T}, param::Param{T, R}) where {T, R} 206 | 207 | RemoveBonds!(uc, param.label) 208 | CreateUnitCell!(uc, param) 209 | end 210 | 211 | function ModifyUnitCell!(uc::UnitCell{T}, params::Vector{Param{T, R}}) where {T, R} 212 | 213 | ModifyUnitCell!.(Ref(uc), params) 214 | end 215 | 216 | function ModifyUnitCell!(uc::UnitCell{T}, params::Vector{Param{T}}) where {T} 217 | 218 | ModifyUnitCell!.(Ref(uc), params) 219 | end 220 | 221 | 222 | @doc """ 223 | ```julia 224 | GetParams(uc::UnitCell) --> Vector{Param} 225 | ``` 226 | For legacy purposes. 227 | If you have a `UnitCell` built using the old technique of adding bonds directly, you can get a vector of `Param` using this function, corresponding to each unique bond type already present in `UnitCell`. 228 | 229 | """ 230 | function GetParams(uc::UnitCell{T}) :: Vector{Param{T}} where {T} 231 | 232 | params = Param{T}[] 233 | for bond in uc.bonds 234 | labels = getproperty.(params, :label) 235 | 236 | if bond.label in labels 237 | index = findfirst(==(bond.label), labels) 238 | AddAnisotropicBond!(params[index], uc, bond.base, bond.target, bond.offset, bond.mat, bond.dist, bond.label) 239 | else 240 | newParam = Param(1.0, T) 241 | push!(params, newParam) 242 | AddAnisotropicBond!(params[end], uc, bond.base, bond.target, bond.offset, bond.mat, bond.dist, bond.label) 243 | end 244 | end 245 | 246 | return params 247 | 248 | end 249 | 250 | 251 | @doc """ 252 | ```julia 253 | Lookup(params::Vector{Param{T, R}}) 254 | ``` 255 | Returns a lookup dictionary for a vector of parameters, instead of a unit cell (refer to [`Lookup`](@ref)). 256 | 257 | """ 258 | function Lookup(params::Vector{Param{T, R}}) where {T, R} 259 | 260 | lookupTable = Dict() 261 | 262 | for param in params 263 | for bond in param.unitBonds 264 | identifier = (bond.base, bond.target, bond.offset) 265 | 266 | if haskey(lookupTable, identifier) 267 | lookupTable[identifier] = lookupTable[identifier] + param.value[end] * bond.mat 268 | 269 | elseif haskey(lookupTable, (bond.target, bond.base, -bond.offset)) 270 | ##### TODO : TEST 271 | flippedIndices = collect(T:-1:1) 272 | lookupTable[(bond.target, bond.base, -bond.offset)] = lookupTable[(bond.target, bond.base, -bond.offset)] + conj(param.value[end]) * collect(conj.(permutedims(bond.mat, flippedIndices))) 273 | 274 | else 275 | lookupTable[identifier] = param.value[end] * bond.mat 276 | end 277 | 278 | end 279 | end 280 | 281 | return lookupTable 282 | end 283 | 284 | function Lookup(params::Vector{Param{T}}) where {T} 285 | 286 | lookupTable = Dict() 287 | 288 | for param in params 289 | for bond in param.unitBonds 290 | identifier = (bond.base, bond.target, bond.offset) 291 | 292 | if haskey(lookupTable, identifier) 293 | lookupTable[identifier] = lookupTable[identifier] + param.value[end] * bond.mat 294 | 295 | elseif haskey(lookupTable, (bond.target, bond.base, -bond.offset)) 296 | ##### TODO : TEST 297 | flippedIndices = collect(T:-1:1) 298 | lookupTable[(bond.target, bond.base, -bond.offset)] = lookupTable[(bond.target, bond.base, -bond.offset)] + conj(param.value[end]) * collect(conj.(permutedims(bond.mat, flippedIndices))) 299 | 300 | else 301 | lookupTable[identifier] = param.value[end] * bond.mat 302 | end 303 | 304 | end 305 | end 306 | 307 | return lookupTable 308 | end 309 | 310 | ##### TODO : Add a function which takes in a bond, and a vector of Params, and checks if that given bond object exists anywhere in the list of unitBonds in the Param object. 311 | 312 | 313 | 314 | end 315 | -------------------------------------------------------------------------------- /src/Plaquettes.jl: -------------------------------------------------------------------------------- 1 | module Plaqs 2 | 3 | export Plaquette, PlaquetteParam, AddIsotropicPlaquettes!, GetPlaquetteFlux, GetPlaquetteSites 4 | 5 | using LinearAlgebra, Bijections 6 | 7 | using ..TightBindingToolkit.LatticeStruct: Lattice 8 | using ..TightBindingToolkit.UCell:UnitCell 9 | using ..TightBindingToolkit.DesignUCell: Lookup 10 | 11 | 12 | mutable struct Plaquette{T} 13 | 14 | loopVectors :: Vector{Vector{Float64}} 15 | sites :: Vector{Tuple{Int64, Vector{Int64}}} 16 | mat :: Array{ComplexF64, T} 17 | label :: String 18 | 19 | function Plaquette( loopVectors::Vector{Vector{Float64}}, sites::Vector{Tuple{Int64, Vector{Int64}}}, mat::Array{ComplexF64, T}, label::String ) where {T} 20 | 21 | return new{T}( loopVectors, sites, mat, label ) 22 | end 23 | 24 | end 25 | 26 | 27 | mutable struct PlaquetteParam{T, R} 28 | 29 | value :: R 30 | plaquettes :: Vector{Plaquette{T}} 31 | label :: String 32 | 33 | function PlaquetteParam( value::R, plaquettes::Vector{Plaquette{T}}, label::String ) where {T, R<:Number} 34 | 35 | return new{T, R}( value, plaquettes, label ) 36 | end 37 | 38 | function PlaquetteParam( value::R, rank::Int64) where {R <: Number} 39 | 40 | return new{rank, R}( value, Plaquette{rank}[], "" ) 41 | end 42 | 43 | end 44 | 45 | 46 | function AddAnisotropicPlaquette(param::PlaquetteParam{T, R}, plaq::Plaquette{T}) where {T, R} 47 | 48 | if length(param.plaquettes)==0 49 | param.label = plaq.label 50 | end 51 | 52 | push!(param.plaquettes, plaq) 53 | 54 | end 55 | 56 | 57 | function AddIsotropicPlaquettes!(param::PlaquetteParam{T, R}, lattice::Lattice{T2}, loopVectors::Vector{Vector{Float64}}, mat::Array{ComplexF64, T}, label::String ; precision::Int64 = 8) where {T, R<:Number, T2} 58 | 59 | positions = collect(image(lattice.positions)) 60 | 61 | PrimitiveBasis = (reduce(hcat, lattice.uc.primitives)) 62 | InvBasis = inv(PrimitiveBasis) 63 | 64 | BCStrength = Bool.(abs.(lattice.uc.BC)) ##### strength of the boundary condition refers to it being 1 (for a periodic system with arbitrary phase), or 0 for open systems. 65 | LEffective = lattice.size .* BCStrength + .!(BCStrength) ##### Leff_i = L if system is closed, or 1 if system is open along that dimension 66 | 67 | for (site, basis) in lattice.sites 68 | 69 | sub, offset = basis 70 | 71 | participants = [ (sub, offset) ] 72 | position = lattice.positions[site] 73 | 74 | for vector in loopVectors 75 | 76 | position = position + vector 77 | 78 | displacements = Ref(InvBasis) .* (positions .- Ref(position)) ##### displacements = (r - r_i) in the basis of the UnitCell primitives 79 | displacements = map.(x -> rem.(x, LEffective, Ref(RoundNearest)), displacements) ####### apply the boundary conditions to the displacements 80 | displacements = Ref(PrimitiveBasis) .* displacements ######## convert the displacements back to the original basis 81 | 82 | val, ind = findmin(norm.(displacements)) ##### find the site closest to the original site 83 | 84 | if val < 5 * 10.0^(-precision) 85 | newSite = lattice.positions(positions[ind]) 86 | push!(participants, lattice.sites[newSite]) 87 | 88 | else 89 | break 90 | end 91 | 92 | end 93 | 94 | if length(participants) == length(loopVectors) + 1 && participants[begin] == participants[end] 95 | AddAnisotropicPlaquette(param, Plaquette(loopVectors, participants[begin:end-1], mat, label)) 96 | end 97 | 98 | end 99 | 100 | end 101 | 102 | 103 | function GetPlaquetteSites(param::PlaquetteParam{T, R}, lattice::Lattice{T2}) where {T, R, T2} 104 | 105 | sites = getproperty.(param.plaquettes, :sites) 106 | return [lattice.sites.(site) for site in sites] 107 | 108 | end 109 | 110 | 111 | function GetPlaquetteFlux(plaq::Plaquette, uc::UnitCell{T} ; mat::Matrix{ComplexF64} = Matrix{ComplexF64}(I, uc.localDim, uc.localDim)) :: Float64 where {T} 112 | 113 | lookup = Lookup(uc) 114 | plaqSize = length(plaq.sites) 115 | 116 | FluxMat = Matrix{ComplexF64}(I, uc.localDim, uc.localDim) 117 | 118 | for (s, site) in enumerate(plaq.sites) 119 | 120 | nextSite = plaq.sites[(s % plaqSize) + 1] 121 | 122 | base = site[begin] 123 | target = nextSite[begin] 124 | offset = nextSite[end] - site[end] 125 | 126 | bond = get(lookup, (base, target, offset), zeros(ComplexF64, uc.localDim, uc.localDim)) 127 | FluxMat = FluxMat * bond 128 | 129 | end 130 | 131 | if norm(FluxMat) > 1e-8 132 | flux = tr(adjoint(mat) * FluxMat) / tr(adjoint(mat) * mat) 133 | flux = angle(flux) / (2 * pi) 134 | return flux 135 | 136 | else 137 | return 0.0 138 | end 139 | 140 | end 141 | 142 | 143 | function GetPlaquetteFlux(plaq::Plaquette, lattice::Lattice{T} ; mat::Matrix{ComplexF64} = Matrix{ComplexF64}(I, lattice.uc.localDim, lattice.uc.localDim)) :: Float64 where {T} 144 | 145 | plaqSize = length(plaq.sites) 146 | 147 | FluxMat = Matrix{ComplexF64}(I, lattice.uc.localDim, lattice.uc.localDim) 148 | 149 | for (s, site) in enumerate(plaq.sites) 150 | 151 | nextSite = plaq.sites[(s % plaqSize) + 1] 152 | 153 | base = lattice.sites(site) 154 | target = lattice.sites(nextSite) 155 | 156 | indsOut = findall(==(target), lattice.bondSites[base, :]) 157 | indsIn = findall(==(base), lattice.bondSites[target, :]) 158 | 159 | bondMat = zeros(ComplexF64, lattice.uc.localDim, lattice.uc.localDim) 160 | if !isempty(indsOut) 161 | bondMat += sum(lattice.bondMats[base, indsOut]) 162 | 163 | elseif !isempty(indsIn) 164 | bondMat += adjoint(sum(lattice.bondMats[target, indsIn])) 165 | end 166 | 167 | FluxMat = FluxMat * bondMat 168 | 169 | end 170 | 171 | if norm(FluxMat) > 1e-8 172 | flux = tr(adjoint(mat) * FluxMat) / tr(adjoint(mat) * mat) 173 | flux = angle(flux) / (2 * pi) 174 | return flux 175 | 176 | else 177 | return 0.0 178 | end 179 | 180 | end 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | end -------------------------------------------------------------------------------- /src/SpectralResponse.jl: -------------------------------------------------------------------------------- 1 | module SpecResponse 2 | 3 | using LinearAlgebra 4 | 5 | using ..TightBindingToolkit.BZone: BZ, GetQIndex 6 | using ..TightBindingToolkit.Hams: Hamiltonian 7 | using ..TightBindingToolkit.Useful: DistFunction 8 | 9 | export spectral, response 10 | 11 | 12 | 13 | function spectral(omega::Float64, eta::Float64, ham::Hamiltonian) 14 | 15 | G = Ref((omega+im*eta)*I) .- ham.H 16 | G = inv.(G) 17 | 18 | return (G - adjoint.(G)) / (2*im) 19 | end 20 | 21 | function spectral(eta::Float64, ham::Hamiltonian ; n_w::Int = 101) 22 | 23 | omegas = collect(range(ham.bandwidth..., n_w)) 24 | return spectral.(omegas, eta, Ref(ham)) 25 | end 26 | 27 | 28 | function response(omega::Float64, eta::Float64, QIndex::Vector{Int64}, Aws, bandwidth::Tuple{Float64, Float64} ; 29 | n_w::Int = 101, mu::Float64 = 0.0, T::Float64 = 0.001) 30 | 31 | es = collect(range(bandwidth..., n_w)) 32 | response = zeros(Complex{Float64}, size(Aws[1][1, 1]) .^ 2) 33 | for (i1, e1) in enumerate(es) 34 | for (i2, e2) in enumerate(es) 35 | Ak1 = Aws[i1] 36 | AQk2 = circshift(Aws[i2], -QIndex) 37 | Aksum = sum(kron.(AQk2, Ak1)) 38 | nfs = (DistFunction(e1 ; T=T, mu=mu, stat=-1) - DistFunction(e2 ; T=T, mu=mu, stat=1))/(omega +im*eta + e1 - e2) 39 | response += nfs * Aksum 40 | 41 | end 42 | end 43 | println("response done") 44 | return response 45 | end 46 | 47 | 48 | function response(omegas::Vector{Float64}, eta::Float64, Qs::Vector{Vector{Float64}}, ham::Hamiltonian, bz::BZ ; 49 | n_w::Int = 101, mu::Float64 = 0.0, T::Float64 = 0.001) 50 | 51 | QIndices = GetQIndex.(Qs, Ref(bz)) .- Ref(GetQIndex(zeros(length(bz.basis)), bz)) 52 | Aws = spectral(eta, ham; n_w=n_w) 53 | println("Aws done") 54 | return response.(omegas', Ref(eta), QIndices, Ref(Aws), Ref(ham.bandwidth); n_w=n_w, mu=mu, T=T) 55 | end 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 | end 88 | -------------------------------------------------------------------------------- /src/SpinMats.jl: -------------------------------------------------------------------------------- 1 | module SpinMatrices 2 | export SpinMats, HermitianBasis 3 | 4 | using LinearAlgebra 5 | 6 | @doc """ 7 | ```julia 8 | SpinMats(spin::Rational{Int64}) --> Vector{Matrix{ComplexF64}} 9 | ``` 10 | Returns the 3 generators of SU(2) in the given spin-representation, along with the Identity matrix of d = 2*spin+1, in the format of [Sx, Sy, Sz, Id] 11 | 12 | """ 13 | function SpinMats(spin::Rational{Int64}) :: Vector{Matrix{ComplexF64}} 14 | 15 | δ(x,y) = isapprox(x, y, atol=1e-5, rtol=1e-5) 16 | 17 | dims = Int(2*spin+1) 18 | Sx = zeros(ComplexF64, dims, dims) 19 | Sy = zeros(ComplexF64, dims, dims) 20 | Sz = zeros(ComplexF64, dims, dims) 21 | S4 = Matrix{ComplexF64}(1.0I, dims, dims) 22 | 23 | spins = Float64.([spin - (sz - 1) for sz in 1:dims]) 24 | Sz = diagm(spins) 25 | 26 | for i in spins 27 | for j in spins 28 | Sx[Int(1+spin-i), Int(1+spin-j)] = (1/2) * (δ(i, j+1) + δ(i, j-1)) * sqrt(spin * (spin+1) - (i * j)) 29 | Sy[Int(1+spin-i), Int(1+spin-j)] = -im * (1/2) * (δ(i, j+1) - δ(i, j-1)) * sqrt(spin * (spin+1) - (i * j)) 30 | end 31 | end 32 | 33 | return [Sx, Sy, Sz, S4] 34 | 35 | end 36 | 37 | function SpinMats(spin::Int64) :: Vector{Matrix{ComplexF64}} 38 | 39 | return SpinMats(spin//1) 40 | end 41 | 42 | 43 | @doc """ 44 | ```julia 45 | HermitianBasis(localDim::Int64) --> Vector{Matrix{ComplexF64}} 46 | ``` 47 | Returns a vector basis of Traceless, localDim x localDim, Hermitian matrices, normalized such that `` Tr(T^{a †} . T^{b}) = (1/2)δ_{ab} ``, along with the identity. 48 | 49 | """ 50 | function HermitianBasis(localDim::Int64) :: Vector{Matrix{ComplexF64}} 51 | 52 | basis = [zeros(ComplexF64, localDim, localDim) for i in 1:localDim^2] 53 | count = 1 54 | 55 | for i in 2:localDim 56 | for j in 1:i-1 57 | for phase in 1:2 58 | 59 | basis[count][i, j] = Bool(mod(count-1, 2)) ? 0.5 * im : 0.5 + 0.0 * im 60 | basis[count][j, i] = conj(basis[count][i, j]) 61 | 62 | count += 1 63 | end 64 | end 65 | end 66 | 67 | for i in 1:localDim - 1 68 | diag = vcat( ones(ComplexF64, i), -i * ones(ComplexF64, 1), zeros(ComplexF64, localDim - i - 1)) 69 | basis[count] = diagm(diag) / sqrt(2 * i * (i+1)) 70 | 71 | count += 1 72 | end 73 | 74 | basis[count] = Matrix{ComplexF64}(I, localDim, localDim) 75 | 76 | return basis 77 | end 78 | 79 | end -------------------------------------------------------------------------------- /src/Susceptibility.jl: -------------------------------------------------------------------------------- 1 | module suscep 2 | export Susceptibility , FindChi , FillChis!, FillRPAChis!, PerformRPA!, FindReducedChi, nF_factor, ResolvedQInteraction, ReduceChis! 3 | 4 | using ..TightBindingToolkit.SpinMatrices:SpinMats 5 | using ..TightBindingToolkit.Useful: DistFunction, SwitchKroneckerBasis, ImpIndices 6 | using ..TightBindingToolkit.BZone:BZ, GetQIndex 7 | using ..TightBindingToolkit.TBModel:Model 8 | using ..TightBindingToolkit.Parameters:Param 9 | using ..TightBindingToolkit.UCell:UnitCell 10 | 11 | 12 | using LinearAlgebra, TensorCast, Tullio, Logging 13 | 14 | directions = ["x" , "y" , "z"] 15 | 16 | 17 | function ResolvedQInteraction(Q::Vector{Float64}, interaction::Param{T, Float64}, uc::UnitCell{T2})::Matrix{Array{ComplexF64, T}} where {T, T2} 18 | 19 | resolvedInt = repeat([zeros(ComplexF64, size(interaction.unitBonds[begin].mat))], length(uc.basis), length(uc.basis)) ##### Initializing the sublattice resolved interaction matrix 20 | flippedIndices = collect(T:-1:1) 21 | 22 | 23 | for bond in interaction.unitBonds 24 | 25 | contribution = interaction.value[end] * bond.mat * exp.(im .* dot(Q, sum(bond.offset .* uc.primitives))) ##### Adding the bond contribution to the resolved interaction matrix 26 | contribution += interaction.value[end] * collect(conj.(permutedims(bond.mat, flippedIndices))) * exp.(-im .* dot(Q, sum(bond.offset .* uc.primitives))) ##### Adding the hermitian conjugate bond contribution to the resolved interaction matrix 27 | 28 | resolvedInt[bond.base, bond.target] += contribution / 2 29 | resolvedInt[bond.target, bond.base] += contribution / 2 30 | end 31 | 32 | return resolvedInt 33 | end 34 | 35 | 36 | 37 | 38 | 39 | @doc """ 40 | `Susceptibility` is a data type representing the magnetic response, ``χ^{ab}(Q , Ω)`` for a general tight-binding `Model`. 41 | 42 | # Attributes 43 | - `M :: Model `: the given model. 44 | - `Qs :: Vector{Vector{Float64}}`: the set of momentum points over which ``χ^{ab}(Q , Ω)`` is calculated. 45 | - `Omegas :: Vector{Float64}`: the set of energies over which ``χ^{ab}(Q , Ω)`` is calculated. 46 | - `Spread :: Float64` : the finite spread when summing over delta functions. 47 | - `chis :: Dict`: a dictionary containing ``χ^{ab}(Q , Ω)`` for the different directions e.g. `chis["xx"]` etc. 48 | 49 | Initialize this structure using 50 | ```julia 51 | Susceptibility(M::Model , Omegas::Vector{Float64} ; eta::Float64 = 1e-2) = new{}(M, [], Omegas, eta, Dict()) 52 | Susceptibility(M::Model , Qs::Vector{Vector{Float64}}, Omegas::Vector{Float64} ; eta::Float64 = 1e-2) = new{}(M, Qs, Omegas, eta, Dict()) 53 | ``` 54 | """ 55 | mutable struct Susceptibility 56 | 57 | Qs :: Vector{Vector{Float64}} 58 | Omegas :: Vector{Float64} 59 | Spread :: Float64 60 | Bare :: Matrix{Matrix{ComplexF64}} ##### The bare susceptibility 61 | BareReduced :: Matrix{Matrix{ComplexF64}} ##### The reduced bare susceptibility 62 | RPA :: Matrix{Matrix{ComplexF64}} ##### The RPA susceptibility 63 | RPAReduced :: Matrix{Matrix{ComplexF64}} ##### The reduced RPA susceptibility 64 | 65 | function Susceptibility(Omegas::Vector{Float64} , M::Model; fill_bz::Bool = true , eta::Float64 = 1e-2) 66 | 67 | if fill_bz 68 | Qs = reshape(M.bz.ks, length(M.bz.ks)) 69 | else 70 | Qs = [zeros(Float64, length(M.uc.primitives))] 71 | end 72 | 73 | Bare = [zeros(ComplexF64, length(M.uc.basis) * length(M.uc.OnSiteMats), length(M.uc.basis) * length(M.uc.OnSiteMats)) for i in 1:length(Omegas), j in 1:length(Qs)] 74 | BareReduced = [zeros(ComplexF64, length(M.uc.OnSiteMats), length(M.uc.OnSiteMats)) for i in 1:length(Omegas), j in 1:length(Qs)] 75 | 76 | RPA = [zeros(ComplexF64, length(M.uc.basis) * length(M.uc.OnSiteMats), length(M.uc.basis) * length(M.uc.OnSiteMats)) for i in 1:length(Omegas), j in 1:length(Qs)] 77 | RPAReduced = [zeros(ComplexF64, length(M.uc.OnSiteMats), length(M.uc.OnSiteMats)) for i in 1:length(Omegas), j in 1:length(Qs)] 78 | 79 | return new{}(Qs, Omegas, eta, Bare, BareReduced, RPA, RPAReduced) 80 | end 81 | 82 | function Susceptibility(Qs::Vector{Vector{Float64}}, Omegas::Vector{Float64}, M::Model; eta::Float64 = 1e-2) 83 | 84 | Bare = [zeros(ComplexF64, length(M.uc.basis) * length(M.uc.OnSiteMats), length(M.uc.basis) * length(M.uc.OnSiteMats)) for i in 1:length(Omegas), j in 1:length(Qs)] 85 | BareReduced = [zeros(ComplexF64, length(M.uc.OnSiteMats), length(M.uc.OnSiteMats)) for i in 1:length(Omegas), j in 1:length(Qs)] 86 | 87 | RPA = [zeros(ComplexF64, length(M.uc.basis) * length(M.uc.OnSiteMats), length(M.uc.basis) * length(M.uc.OnSiteMats)) for i in 1:length(Omegas), j in 1:length(Qs)] 88 | RPAReduced = [zeros(ComplexF64, length(M.uc.OnSiteMats), length(M.uc.OnSiteMats)) for i in 1:length(Omegas), j in 1:length(Qs)] 89 | 90 | return new{}(Qs, Omegas, eta, Bare, BareReduced, RPA, RPAReduced) 91 | end 92 | end 93 | 94 | 95 | @doc raw""" 96 | ```julia 97 | nF_factor(Q_index::Vector{Int64}, Omega::Float64 , M::Model; eta::Float64=1e-2) --> Matrix{Matrix{ComplexF64}} 98 | ``` 99 | function to calculate ``(nF(E(k)) - nF(E(k+Q))) / (Ω + i * η - (E(k+Q) - E(k)))`` for a general multi-band system, at all k-points. 100 | 101 | """ 102 | function nF_factor(Q_index::Vector{Int64}, Omega::Float64 , M::Model; eta::Float64=1e-2) :: Vector{Matrix{ComplexF64}} 103 | 104 | Es_k = M.Ham.bands 105 | Es_kpQ = circshift(Es_k, -Q_index) ##### The bands with momentum indices rolled to k+Q instead of k. 106 | dEs = reshape(map((x, y) -> x .- y', Es_kpQ, Es_k), length(M.Ham.bands)) ##### The energy differences between the bands at k+Q and k. Flattened momentum index. 107 | 108 | ##### The fermi distribution functions for each band at each k-point 109 | nf_k = DistFunction.(Es_k ; T=M.T, mu=M.mu, stat=M.stat) 110 | nf_kpQ = circshift(nf_k, -Q_index) 111 | dnFs = reshape(map((x, y) -> x .- y', nf_k, nf_kpQ), length(M.Ham.bands)) ##### The fermi distribution function differences between the bands at k+Q and k. Flatened momentum index. 112 | 113 | @cast nF[k][i, j] |= dnFs[k][i, j] / (Omega + im * eta - dEs[k][j, i]); 114 | return nF 115 | end 116 | 117 | 118 | @doc """ 119 | ```julia 120 | FindReducedChi(Q::Vector{Float64}, Omega::Float64 , M::Model; a::Int64=3, b::Int64=3, eta::Float64=1e-2) --> ComplexF64 121 | ``` 122 | function to calculate susceptibility at a fixed Ω=`Omega`, and `Q`, and along a fixed direction given by `a` and `b`. 123 | 124 | """ 125 | function FindReducedChi(Omega::Float64, Q::Vector{Float64}, M::Model; a::Int64=3, b::Int64=3, eta::Float64=1e-2, Gamma_index::Vector{Int64} = ((M.bz.gridSize .+ 1) .÷ 2)) ::ComplexF64 126 | 127 | expQ = diagm(exp.(-im .* dot.(Ref(Q) , M.uc.basis))) 128 | Qa = kron(expQ , M.uc.OnSiteMats[a]) 129 | Qb = kron(expQ , M.uc.OnSiteMats[b]) 130 | 131 | Q_index = GetQIndex(Q, M.bz) .- Gamma_index 132 | nF = nF_factor(Q_index, Omega , M; eta=eta) 133 | 134 | Uk = M.Ham.states 135 | UkQ = circshift(M.Ham.states, -Q_index) 136 | 137 | if a!=b 138 | Ma = reshape(adjoint.(UkQ) .* Ref(Qa) .* Uk, length(M.Ham.bands)) 139 | Mb = reshape(adjoint.(UkQ) .* Ref(Qb) .* Uk, length(M.Ham.bands)) 140 | @tullio chi := - Ma[k][i, j] * conj(Mb[k][i, j]) * nF[k][j, i] 141 | else 142 | Ma = reshape(adjoint.(UkQ) .* Ref(Qa) .* Uk, length(M.Ham.bands)) 143 | @tullio chi := - Ma[k][i, j] * conj(Ma[k][i, j]) * nF[k][j, i] 144 | end 145 | 146 | return chi 147 | end 148 | 149 | 150 | function FindChi(Omega::Float64, Q::Vector{Float64}, M::Model; a::Int64=3, b::Int64=3, eta::Float64=1e-2, Gamma_index::Vector{Int64} = ((M.bz.gridSize .+ 1) .÷ 2)) ::Matrix{ComplexF64} 151 | 152 | Q_index = GetQIndex(Q, M.bz) .- Gamma_index 153 | nF = nF_factor(Q_index, Omega , M; eta=eta) 154 | 155 | Uk = M.Ham.states 156 | UkQ = circshift(M.Ham.states, -Q_index) 157 | 158 | chiMat = zeros(ComplexF64, length(M.uc.basis), length(M.uc.basis)) 159 | 160 | for (i, base) in enumerate(M.uc.basis) 161 | for (j, target) in enumerate(M.uc.basis) 162 | 163 | Uis = selectdim.(Uk, 1, Ref((i - 1) * M.uc.localDim + 1 : i * M.uc.localDim)) 164 | Ujs = selectdim.(Uk, 1, Ref((j - 1) * M.uc.localDim + 1 : j * M.uc.localDim)) 165 | 166 | UisQ = selectdim.(adjoint.(UkQ), 2, Ref((i - 1) * M.uc.localDim + 1 : i * M.uc.localDim)) 167 | UjsQ = selectdim.(adjoint.(UkQ), 2, Ref((j - 1) * M.uc.localDim + 1 : j * M.uc.localDim)) 168 | 169 | Ma = reshape(UisQ .* Ref(M.uc.OnSiteMats[a]) .* Uis, length(M.Ham.bands)) 170 | Mb = reshape(UjsQ .* Ref(M.uc.OnSiteMats[b]) .* Ujs, length(M.Ham.bands)) 171 | 172 | @tullio chi := - Ma[k][i, j] * conj(Mb[k][i, j]) * nF[k][j, i] 173 | chiMat[i, j] += chi 174 | end 175 | end 176 | 177 | return chiMat 178 | end 179 | 180 | 181 | function PerformRPA!(chi::Susceptibility, GeneratorInteractions::Vector{Param{2, Float64}}, uc::UnitCell{T}) where {T} 182 | ##### GeneratorInteraction refers to the interaction written in the basis of all the generators of the local Hilbert space (including the identity matrix). Eg. Id + SU(2) spins -> GeneratorInteraction is a 4x4 matrix. 183 | resolvedInts = sum([ResolvedQInteraction.(chi.Qs, Ref(interaction), Ref(uc)) for interaction in GeneratorInteractions]) 184 | resolvedInts = map(x -> hvcat(length(uc.basis), permutedims(x, [2, 1])...), resolvedInts) ##### Reshaping the resolved interactions to be a vector of matrices, with the first index being the momentum index, and the second being the sublattice x Generator index. 185 | 186 | permutation = SwitchKroneckerBasis((length(uc.OnSiteMats), length(uc.basis))) 187 | map!(x -> x[permutation, permutation], resolvedInts, resolvedInts) ##### Switching the basis of the resolved interactions to be a vector of matrices into the generator x sublattice index 188 | 189 | resolvedInts = permutedims(reshape(repeat(resolvedInts, length(chi.Omegas)), (length(chi.Qs), length(chi.Omegas))), [2, 1]) ##### Repeating the resolved interactions to be a matrix of matrices, with the outermatrix having the (energy, momentum) index, and the inner being sublattice x Generator index. 190 | 191 | corrections = inv.(Ref(I) .+ (resolvedInts .* chi.Bare)) ##### The RPA corrections to the bare susceptibility coming from resummation 192 | chi.RPA = chi.Bare .* corrections ##### The RPA susceptibility 193 | end 194 | 195 | @doc """ 196 | ```julia 197 | FillChis!(chi::Susceptibility; fill_BZ::Bool=false, a::Int64=3, b::Int64=3) 198 | ``` 199 | function to calculate susceptibility at a all given Ω=`Omegas`, but for all `Q` present in the given path, and along a fixed direction given by `a` and `b`. 200 | 201 | """ 202 | function FillChis!(chi::Susceptibility, M::Model; a::Int64=3, b::Int64=3, reduce::Bool = true) 203 | Gamma_index = GetQIndex(zeros(Float64, length(M.uc.primitives)), M.bz ; nearest = false) ##### The index of the Gamma point in the BZ grid. 204 | @assert !isempty(Gamma_index) "The Gamma point needs to be in the BZ grid for this calculation to work!" 205 | 206 | if reduce ##### Calculate only the reduced susceptibility 207 | chis = FindReducedChi.(chi.Omegas, reshape(chi.Qs, 1, length(chi.Qs)), Ref(M), ; a=a, b=b, eta=chi.Spread) 208 | chis = chis ./ length(M.bz.ks) 209 | 210 | setindex!.(chi.BareReduced, chis, Ref(CartesianIndex(a, b))) 211 | @info "sublattice reduced bare Chis filled along $(a)-$(b)." 212 | 213 | else ##### Calculate the sublattice resolved susceptibility 214 | chis = FindChi.(chi.Omegas, reshape(chi.Qs, 1, length(chi.Qs)), Ref(M), ; a=a, b=b, eta=chi.Spread) 215 | chis = chis ./ length(M.bz.ks) 216 | 217 | inds = Ref(length(M.uc.basis) * (a - 1)) .+ collect(1:length(M.uc.basis)) 218 | ##### Setting the appropriate indices of the susceptibility matrix which is generator x sublattice. 219 | for i in 1:length(chi.Omegas), j in 1:length(chi.Qs) 220 | chi.Bare[i, j][inds, inds] = chis[i, j] 221 | end 222 | 223 | @info "bare Chis filled along $(a)-$(b)." 224 | end 225 | 226 | end 227 | 228 | 229 | function FillRPAChis!(chi::Susceptibility, M::Model, GeneratorInteractions::Vector{Param{2, Float64}} ; Generators::Vector{Vector{Int64}} = [[i, i] for i in 1:length(M.uc.OnSiteMats)]) 230 | 231 | for generator in Generators 232 | FillChis!(chi, M, ; a=generator[1], b=generator[2], reduce=false) 233 | end 234 | 235 | PerformRPA!(chi, GeneratorInteractions, M.uc) 236 | 237 | @info "RPA performed!." 238 | end 239 | 240 | 241 | function ReduceChis!(chi::Susceptibility, M::Model) 242 | 243 | subPhases = [zeros(ComplexF64, length(M.uc.basis) * length(M.uc.OnSiteMats), length(M.uc.OnSiteMats)) for i in 1:length(chi.Omegas), j in 1:length(chi.Qs)] 244 | 245 | for i in 1:length(chi.Omegas), j in 1:length(chi.Qs) 246 | subPhases[i, j] = kron(Matrix{ComplexF64}(I, length(M.uc.OnSiteMats), length(M.uc.OnSiteMats)), exp.(im .* dot.(Ref(chi.Qs[j]) , M.uc.basis))) 247 | end 248 | 249 | chi.BareReduced = adjoint.(subPhases) .* chi.Bare .* subPhases 250 | chi.RPAReduced = adjoint.(subPhases) .* chi.RPA .* subPhases 251 | end 252 | 253 | 254 | end 255 | 256 | 257 | -------------------------------------------------------------------------------- /src/TightBindingToolkit.jl: -------------------------------------------------------------------------------- 1 | module TightBindingToolkit 2 | 3 | ####### Write your package code here. 4 | 5 | ##### Module to get SU(2) representation for any half-integer / integer spin. Also have a function to return traceless matrix basis for Hermitian NxN matrices. 6 | include("SpinMats.jl") 7 | using .SpinMatrices 8 | export SpinMats, HermitianBasis 9 | 10 | include("Useful.jl") 11 | using .Useful 12 | export GetAllOffsets, VecAngle, Meshgrid, BinarySearch, DistFunction, DeriDistFunction , GetIndexPath, FFTArrayofMatrix, GetPhase #, Central_Diff, Arrayfy, DeArrayfy 13 | 14 | ##### Module for defining Unit Cell structure and some funcitons to manipulate it. 15 | include("UnitCell.jl") 16 | using .UCell 17 | export Bond , BondRank, IsSameBond , FlipBond , UnitCell , IsSameUnitCell , AddBasisSite! , GetDistance , GetRealSpacePositions, IsSameUnitCell 18 | 19 | include("DesignUnitCell.jl") 20 | using .DesignUCell 21 | export AddAnisotropicBond! , AddIsotropicBonds! , ModifyBonds! , ScaleBonds! , RemoveBonds! , ModifyFields!, ModifyIsotropicFields!, Lookup 22 | 23 | include("ExpandUnitCell.jl") 24 | using .ExpandUCell 25 | export ChangePrimitives!, ExpandUnitCell, ExpandBonds! 26 | 27 | ##### Module to define Param structure which represents a general hopping parameter, and can be used to construct a UnitCell. 28 | include("Params.jl") 29 | using .Parameters 30 | export Param, AddAnisotropicBond!, AddIsotropicBonds!, AddSimilarBonds! , CreateUnitCell!, ModifyUnitCell!, GetParams, Lookup 31 | 32 | ##### Module to make a lattice in real-space using a UnitCell. 33 | include("Lattice.jl") 34 | using .LatticeStruct 35 | export Lattice, FillSites!, FillBonds!, FillLattice!, GetBCPhase, ApplyBCToSite 36 | 37 | include("DesignLattice.jl") 38 | using .DesignLattice 39 | export CreateLattice, ModifyLattice!, ScaleLatticeBonds!, ScaleLattice!, RemoveLatticeBonds! 40 | 41 | include("Plaquettes.jl") 42 | using .Plaqs 43 | export Plaquette, PlaquetteParam, AddIsotropicPlaquettes!, GetPlaquetteFlux, GetPlaquetteSites 44 | 45 | include("Flux.jl") 46 | using .Flux 47 | export GetBondPhase, CheckGaugeValidity, ModifyGauge!, GetStringPhase, InsertMonopolePair! 48 | 49 | include("Gauge.jl") 50 | using .Gauge 51 | export LandauGauge, RadialGauge 52 | 53 | ##### Module to define Brillouin Zone structure and some functions to manipulate it. 54 | include("BZ.jl") 55 | using .BZone 56 | export GetRLVs , BZ , Monkhorst , FillBZ! , ReduceQ , GetQIndex , CombinedIndexPath , GetBZPath , CombinedBZPath , MomentumPhaseFFT 57 | 58 | ##### module to define a Hamiltonian structure and solving for some of its properties. 59 | include("Hamiltonian.jl") 60 | using .Hams 61 | export Hamiltonian , FillHoppingHamiltonian, FillPairingHamiltonian, FillHamiltonian , DiagonalizeHamiltonian! , DOS, ModifyHamiltonianField!, IsBandGapped, GetVelocity! 62 | 63 | include("LatticeHamiltonian.jl") 64 | using .LatHam 65 | export FillHamiltonian, LatticeHamiltonian, DiagonalizeHamiltonian!, Slater, SingleParticleFidelity, SlaterOverlap, GaugeFix! 66 | 67 | ##### Module to define a Tight-Binding Model structure which takes into account thermodynamical parameters such as temperature and filling etc. 68 | include("Model.jl") 69 | using .TBModel 70 | export Model , FindFilling , GetMu! , GetFilling! , GetCount , GetGk! , GetGr!, SolveModel!, GetGap!, FreeEnergy, GetOrderParameter 71 | 72 | include("LatticeModel.jl") 73 | using .LatModel 74 | export LatticeModel , GetMu! , GetFilling! , GetGr!, SolveModel!, GetGap! 75 | 76 | ################# Symmetries acting on lattice ################# 77 | include("LatticeSymmetries.jl") 78 | using .LatticeSymmetries 79 | export Translations, Degeneracies, FindQuantumNumbers 80 | 81 | ##### Module to define the equivalent but for bdG systems with pairing. 82 | include("BdGModel.jl") 83 | using .BdG 84 | export BdGModel, FindFilling , GetMu! , GetFilling! , GetGk! , GetGr!, SolveModel!, GetGap!, FreeEnergy 85 | 86 | ##### Module to calculate generalized Chern numbers 87 | include("Chern.jl") 88 | using .Chern 89 | export FindLinks , FieldStrength , ChernNumber, CheckValidity, PartialChernNumber, FilledChernNumber 90 | 91 | ##### Module to calculate generalized magnetic susceptibility 92 | include("Susceptibility.jl") 93 | using .suscep 94 | export Susceptibility , FindChi , FillChis!, FillRPAChis!, PerformRPA!, FindReducedChi, nF_factor, ResolvedQInteraction, ReduceChis! 95 | 96 | ##### Module to calculate generalized electrical conductivity 97 | include("Conductivity.jl") 98 | using .conduct 99 | export Conductivity, SpectralFunction, SpectralContribution, GetConductivity! 100 | 101 | ##### Module containing some plotting functions 102 | include("Plot.jl") 103 | using .PlotTB 104 | export Plot_UnitCell! , Plot_Band_Contour!, Plot_Band_Structure!, Plot_FS!, Plot_Fields!, Plot_Lattice! 105 | 106 | end 107 | -------------------------------------------------------------------------------- /src/UnitCell.jl: -------------------------------------------------------------------------------- 1 | module UCell 2 | export Bond , BondRank, IsSameBond , FlipBond , UnitCell , IsSameUnitCell , AddBasisSite! , GetDistance , GetRealSpacePositions, IsSameUnitCell 3 | 4 | using LinearAlgebra, Logging 5 | 6 | using ..TightBindingToolkit.SpinMatrices: HermitianBasis 7 | using ..TightBindingToolkit.Useful: GetAllOffsets 8 | 9 | @doc """ 10 | `Bond{T}` is a data type representing a general bond on a lattice. 11 | 12 | # Attributes 13 | - `base::Int64`: sub-lattice of the initial site on the bond. 14 | - `target::Int64`: sub-lattice of the final site on the bond. 15 | - `offset::Vector{Int64}`: the difference of the unit cells in which these sublattices belong to, in units of the lattice basis vectors. 16 | - `mat::Array{ComplexF64, T}`: an array describing this bond --> can be hopping for partons, or spin-exchange for spins. The rank of the array, `T`, is determined by the specific model being looked at. Eg. rank = 2 for a free parton Hamiltonian. 17 | - `dist::Float64`: the distance b/w the two sites = length of bond. 18 | - `label::String`: some string label to mark the bond type. 19 | 20 | """ 21 | mutable struct Bond{T} 22 | base :: Int64 23 | target :: Int64 24 | offset :: Vector{Int64} 25 | mat :: Array{ComplexF64, T} 26 | dist :: Float64 27 | label :: String 28 | end 29 | 30 | 31 | @doc """ 32 | ```julia 33 | BondRank(bond::Bond{T}) --> T 34 | BondRank(bonds::Array{Bond{T}}) --> T 35 | ``` 36 | 37 | Function to return rank of a bond or a collection of bonds. 38 | 39 | """ 40 | function BondRank(bond::Bond{T}) :: Int64 where{T} 41 | return T 42 | end 43 | 44 | function BondRank(bonds::Array{Bond{T}}) :: Int64 where{T} 45 | return T 46 | end 47 | 48 | 49 | @doc """ 50 | ```julia 51 | IsSameBond( Bond1::Bond , Bond2::Bond ) --> Bool 52 | ``` 53 | 54 | Function to check if two bond objects are describing the same physical bond, just inverted! 55 | """ 56 | function IsSameBond( Bond1::Bond , Bond2::Bond ) :: Bool 57 | return Bond1.base==Bond2.target && Bond1.target == Bond2.base && Bond1.offset == -Bond2.offset && Bond1.label==Bond2.label && BondRank(Bond1) == BondRank(Bond2) 58 | end 59 | 60 | 61 | @doc """ 62 | ```julia 63 | FlipBond(bond::Bond{T}) --> Bond{T} 64 | ``` 65 | 66 | Function to flip a bond. Only works on bonds of rank = 2 right now. 67 | """ 68 | function FlipBond(bond::Bond{T}) :: Bond{T} where {T} 69 | 70 | if T==2 71 | return Bond(bond.target, bond.base, -bond.offset, collect(adjoint(bond.mat)), bond.dist, bond.label) 72 | else 73 | flippedIndices = collect(T:-1:1) 74 | return Bond(bond.target, bond.base, -bond.offset, collect(conj.(permutedims(bond.mat, flippedIndices))), bond.dist, bond.label) 75 | end 76 | end 77 | 78 | 79 | @doc """ 80 | `UnitCell{T}` is a data type representing a general unit cell of a lattice. 81 | 82 | # Attributes 83 | - `primitives :: Vector{ Vector{ Float64 } }`: primitive bases vectors of the lattice. 84 | - `basis :: Vector{ Vector{ Float64 } }`: positions of the sub-lattices. 85 | - `bonds :: Vector{Bond{T}}`: the set of all bonds defining a lattice. 86 | - `fields :: Vector{ Vector{Float64}}` : the fields oneach basis site. 87 | - `localDim :: Int64`: Local Hilbert space dimension ( e.g. 3 for classical spins, 2 for spin-1/2 electrons ). 88 | - `BC :: Vector{ ComplexF64 }`: boundary conditions, in the form of [e^{ιθ_i}]. e.g. θ=0 for PBC, θ=π for APBC, and so on. 89 | 90 | Initialize this structure using 91 | ```julia 92 | UnitCell( as::Vector{Vector{Float64}} , localDim::Int64) 93 | UnitCell( as::Vector{Vector{Float64}} , localDim::Int64, rank::Int64) 94 | ``` 95 | """ 96 | mutable struct UnitCell{T} 97 | primitives :: Vector{ Vector{ Float64 } } # Lattice basis vectors 98 | basis :: Vector{ Vector{ Float64 } } # Sublattice positions 99 | 100 | """ 101 | The 'bonds' attribute contains ( base, target, offset, matrix , distance, label ) 102 | """ 103 | bonds :: Vector{Bond{T}} 104 | """ 105 | The fields and the On-Site matrices they correspond to. Convention is to have the chemical potential always as the last element of the field. 106 | Hence, the last element of the On-Site Matrices must be the identity. 107 | """ 108 | fields :: Vector{ Vector{Float64}} # Zeeman fields 109 | OnSiteMats :: Vector{ Matrix{ComplexF64}} # Zeeman fields 110 | 111 | localDim :: Int64 # Local Hilbert space dimension ( 3 for classical spins, 2 for partons ) 112 | BC :: Vector{ ComplexF64 } # Boundary condition 113 | 114 | function UnitCell( as::Vector{Vector{Float64}} , localDim::Int64) 115 | @warn "Bond rank not passed when constructing UnitCell. Choosing default value of 2." 116 | @assert length.(as) == repeat([length(as[begin])] , length(as)) "All primitives should have the same dimensions!" 117 | return new{2}( as , Vector{Float64}[] , Bond{2}[] , Vector{Float64}[] , Matrix{ComplexF64}[] , localDim , ones(ComplexF64, length(as)) ) 118 | end 119 | 120 | function UnitCell( as::Vector{Vector{Float64}} , localDim::Int64, rank::Int64) 121 | @assert length.(as) == repeat([length(as[begin])] , length(as)) "All primitives should have the same dimensions!" 122 | return new{rank}( as , Vector{Float64}[] , Bond{rank}[] , Vector{Float64}[] , Matrix{ComplexF64}[] , localDim , ones(ComplexF64, length(as)) ) 123 | end 124 | 125 | end 126 | 127 | 128 | @doc """ 129 | ```julia 130 | AddBasisSite!( uc::UnitCell , position::Vector{Float64} ) 131 | AddBasisSite!( uc::UnitCell , position::Vector{Float64} , field::Vector{Float64} ) 132 | ``` 133 | Add a sublattice to the `UnitCell` at the given real-space position, with an on-site `field`. 134 | 135 | """ 136 | function AddBasisSite!( uc::UnitCell , position::Vector{Float64} ) 137 | @assert !(position in uc.basis) "Cannot add the same basis site again!" 138 | push!( uc.basis , position ) 139 | 140 | d = (uc.localDim^2) 141 | @warn "No On-Site field and matrices passed to basis site. Choosing default value of $(zeros(Float64, d)) given the local Hilbert space of UnitCell." 142 | 143 | if length(uc.basis) == 1 ##### First sublattice being added 144 | uc.OnSiteMats = HermitianBasis(uc.localDim) 145 | else 146 | @assert prod(d .== length.(uc.fields)) "Inconsistent fields given or assumed!" 147 | end 148 | 149 | push!( uc.fields , zeros(Float64 , d) ) 150 | 151 | end 152 | 153 | function AddBasisSite!( uc::UnitCell , position::Vector{Float64} , field::Vector{Float64} , OnSiteMatrices::Vector{Matrix{ComplexF64}}) 154 | @assert !(position in uc.basis) "Cannot add the same basis site again!" 155 | push!( uc.basis , position ) 156 | 157 | push!( uc.fields , field ) 158 | 159 | if length(uc.basis) == 1 ##### First sublattice being added 160 | @assert adjoint.(OnSiteMatrices) == OnSiteMatrices "on-Site matrices must be Hermitian!" 161 | uc.OnSiteMats = OnSiteMatrices 162 | else 163 | @assert uc.OnSiteMats == OnSiteMatrices "Inconsistent On-Site Matrices given!" 164 | end 165 | end 166 | 167 | 168 | @doc """ 169 | ```julia 170 | GetDistance(uc::UnitCell, base::Int64, target::Int64, offset::Vector{Int64}) --> Float64 171 | ``` 172 | get the distance between site at position (0, `base`) and (R, `target`), where R = `offset`, when written in units of the unit cell primitive vectors. 173 | 174 | """ 175 | function GetDistance(uc::UnitCell, base::Int64, target::Int64, offset::Vector{Int64}) :: Float64 176 | return norm( sum( offset.*uc.primitives ) + (uc.basis[target] - uc.basis[base] ) ) 177 | end 178 | 179 | 180 | @doc """ 181 | ```julia 182 | GetRealSpacePositions(uc::UnitCell{T} ; OffsetRange::Int64 = 2) --> Dict 183 | ``` 184 | Returns a dictionary whose keys are vectors in the cartesian coordinates (rounded off to `accuracy` digits), with values giving the corresponding sublattice and Unit Cell coordinate of a lattice site at that position. 185 | 186 | """ 187 | function GetRealSpacePositions(uc::UnitCell{T} ; OffsetRange::Int64 = 2, accuracy::Int64 = 6) :: Dict{Vector{Float64}, Tuple{Int64, Vector{Int64}}} where {T} 188 | 189 | offsets = GetAllOffsets(OffsetRange, length(uc.primitives)) 190 | DistanceDict = Dict{Vector{Float64}, Tuple{Int64, Vector{Int64}}}() 191 | 192 | for offset in offsets 193 | for sub in 1:length(uc.basis) 194 | 195 | position = uc.basis[sub] + sum(offset .* uc.primitives) 196 | DistanceDict[round.(position, digits = accuracy)] = (sub, offset) 197 | 198 | end 199 | end 200 | 201 | return DistanceDict 202 | end 203 | 204 | 205 | @doc """ 206 | ```julia 207 | IsSameUnitCell(uc_1::UnitCell, uc_2::UnitCell) --> Bool 208 | ``` 209 | Function to check if two unit cell live on the same underlying lattice or not, i.e. have the same `primitives`, same `sublattices`, and same `localDim`. 210 | 211 | """ 212 | function IsSameUnitCell(uc_1::UnitCell, uc_2::UnitCell) :: Bool 213 | 214 | check = true 215 | for attribute in [:primitives, :basis, :localDim] 216 | prop1 = getproperty(uc_1, attribute) 217 | prop2 = getproperty(uc_2, attribute) 218 | 219 | if length(prop1)!=length(prop2) 220 | check = false 221 | return check 222 | else 223 | check = check && isapprox(prop1, prop2, atol=1e-3, rtol=1e-3) 224 | end 225 | end 226 | 227 | return check 228 | end 229 | 230 | 231 | end 232 | -------------------------------------------------------------------------------- /src/Useful.jl: -------------------------------------------------------------------------------- 1 | module Useful 2 | export GetAllOffsets, VecAngle, Meshgrid, BinarySearch, DistFunction, DeriDistFunction , GetIndexPath, FFTArrayofMatrix, Central_Diff, Arrayfy, DeArrayfy, GetPhase, SegmentIntersection, SwitchKroneckerBasis, SecantSearch, ImpIndices 3 | 4 | using LinearAlgebra, Statistics, FFTW, TensorCast 5 | 6 | @doc """ 7 | ```julia 8 | GetAllOffsets(OffsetRange::Int64, dim::Int64) --> Vector{Vector{Int64}} 9 | ``` 10 | Given a range, returns the set of all possible Bond offsets such that each element of the offset vector lies in [-`OffsetRange`, `OffsetRange`]. 11 | 12 | """ 13 | function GetAllOffsets(OffsetRange::Int64, dim::Int64) :: Vector{Vector{Int64}} 14 | if dim==1 15 | offsets = collect([i] for i in OffsetRange:-1:-OffsetRange) 16 | elseif dim==2 17 | offsets = reshape([[i, j] for i in OffsetRange:-1:-OffsetRange, j in OffsetRange:-1:-OffsetRange], (2*OffsetRange+1)^2) 18 | elseif dim==3 19 | offsets = reshape([[i, j, k] for i in OffsetRange:-1:-OffsetRange, j in OffsetRange:-1:-OffsetRange, k in OffsetRange:-1:-OffsetRange], (2*OffsetRange+1)^3) 20 | else 21 | println("Does not work for dimensions = ", dim) 22 | end 23 | return offsets 24 | end 25 | 26 | 27 | @doc """ 28 | ```julia 29 | VecAngle(a::Vector{Float64}, b::Vector{Float64}) 30 | ``` 31 | returns the angle b/w two given vectors. 32 | 33 | """ 34 | function VecAngle(a::Vector{Float64}, b::Vector{Float64}) 35 | return acos(clamp(a⋅b/(norm(a)*norm(b)), -1, 1)) 36 | end 37 | 38 | 39 | @doc """ 40 | ```julia 41 | Meshgrid(grid::Vector{Int64} ; starts::Vector{Int64} = zeros(Int64, length(grid))) 42 | ``` 43 | returns a meshgrid of (i_1, i_2, ..., i_n) such that i_j ∈ [starts[j], starts[j] + 1, ..., grid[j]] ∀ j in [1, 2, ..., n = length(grid)] 44 | 45 | """ 46 | function Meshgrid(grid::Vector{Int64} ; starts::Vector{Int64} = ones(Int64, length(grid))) 47 | inds = UnitRange.( starts, grid) 48 | inds = collect(Base.product(inds...)) 49 | return inds 50 | end 51 | 52 | 53 | @doc """ 54 | ```julia 55 | BinarySearch(target::Float64, xRange::Tuple{Float64, Float64}, f::T, args::Tuple ; tol::Float64=0.001) 56 | ``` 57 | General function implementing Binary search on a monotonic function f(x, args...)=target, in the range x ∈ xRange, with tolerance tol. 58 | 59 | """ 60 | function BinarySearch(target::Float64, xRange::Tuple{Float64, Float64}, f::T, args::Tuple ; initial::Float64 = mean(xRange), x_tol::Float64 = 1e-3, target_tol::Float64 = 1e-6)::Float64 where T<:Function 61 | ##### ///TODO pass an initial guess 62 | xExt = collect(xRange) 63 | ##### Choosing initial point to start Binary search with. 64 | if initial < xExt[begin] 65 | current = xExt[begin] 66 | elseif xExt[end] < initial 67 | current = xExt[end] 68 | else 69 | current = initial 70 | end 71 | 72 | ##### ///TODO : Fix bug when steps = 0 73 | if xExt[end]!= xExt[begin] 74 | steps = max(Int(ceil(log2((xExt[end]-xExt[begin])/(x_tol)))), 1) 75 | end 76 | 77 | for i in 1:steps 78 | check = f(current, args...) 79 | 80 | if check - target > target_tol 81 | xExt[end] = current 82 | elseif check - target < -target_tol 83 | xExt[begin] = current 84 | else 85 | break 86 | end 87 | 88 | current = mean(xExt) 89 | end 90 | 91 | return current 92 | end 93 | 94 | 95 | function SecantSearch(target::Float64, xInitials::Vector{Float64}, yInitials::Vector{Float64}, f::T, args::Tuple ; x_tol::Float64 = 1e-3, target_tol::Float64 = 1e-4, max_iter::Int64 = 10)::Float64 where T<:Function 96 | 97 | x0, x1 = xInitials 98 | y0, y1 = yInitials 99 | 100 | for i in 1:max_iter 101 | x = x1 - (x1 - x0) * (y1 - target) / (y1 - y0) 102 | check = f(x, args...) 103 | 104 | if abs(check - target) < target_tol || abs(x - x1) < x_tol 105 | break 106 | end 107 | 108 | x0, x1 = x1, x 109 | y0, y1 = y1, check 110 | end 111 | 112 | return x 113 | end 114 | 115 | 116 | @doc """ 117 | ```julia 118 | DistFunction(E ; T::Float64, mu::Float64=0.0, stat::Int64=-1) 119 | ``` 120 | Distribution function. `stat`=1 --> Bose-Einstein distribution, and `stat`=-1 --> Fermi distribution. 121 | 122 | """ 123 | function DistFunction(E ; T::Float64, mu::Float64=0.0, stat::Int64=-1) 124 | return @. 1 / (exp((E - mu) / T) - stat) 125 | end 126 | 127 | 128 | @doc """ 129 | ```julia 130 | DeriDistFunction(E ; T::Float64, mu::Float64=0.0, stat::Int64=-1) 131 | ``` 132 | derivative of [`dist`](@ref) w.r.t the energy. `stat`=1 --> Bose-Einstein distribution, and `stat`=-1 --> Fermi distribution. 133 | 134 | """ 135 | function DeriDistFunction(E ; T::Float64, mu::Float64=0.0, stat::Int64=-1) 136 | df = @. - (1/T) * exp((E - mu) / T) / ((exp((E - mu) / T) - stat) ^ 2) 137 | if isnan(df) 138 | df = 0.0 139 | end 140 | 141 | return df 142 | end 143 | 144 | 145 | @doc """ 146 | ```julia 147 | GetIndexPath(start::Vector{Int64}, ending::Vector{Int64} ; exclusive::Bool=true) --> Vector{Vector{Int64}} 148 | ``` 149 | Returns a path in index-space of a discretized lattice which joins the two sets of indices `start` and `ending`. 150 | If the input `exclusive` is set to `true`, the returned path will NOT contain the `ending` point itself. 151 | Works in any dimensions. 152 | 153 | """ 154 | function GetIndexPath(start::Vector{Int64}, ending::Vector{Int64} ; exclusive::Bool=true) :: Vector{Vector{Int64}} 155 | 156 | dx = ending - start 157 | i = findfirst(!=(0), dx) ##### find the first dimension where the two given points differ. 158 | ts = (1/abs(dx[i])) .* collect(0:abs(dx[i]) - exclusive) ##### choosing the paramtrization of a straight line in generalized dimensiosn s.t. the increment is ±1 along this dimension. 159 | 160 | coords = [] 161 | for j in 1:length(start) 162 | xj = start[j] .+ (dx[j] .* ts) ##### a straight line in generalized dimensions b/w two given points : (x[j] - start[j]) / (ending[j] - start[j]) = t ∈ [0, 1] 163 | push!(coords, round.(Int64, xj)) 164 | end 165 | 166 | coords = reduce(hcat, coords) ##### combining all sets of indices into sets of points 167 | path = Vector{eltype(coords)}[eachrow(coords)...] 168 | 169 | return path 170 | end 171 | 172 | 173 | function Arrayfy(A::Array{Matrix{T}, N}) :: Array{T, 3} where {T<:Number, N} 174 | 175 | grid = size(A) 176 | 177 | G = reshape(A, prod(grid)) ##### Flattening all super-indices for casting 178 | @cast G[k, i, j] |= G[k][i, j] 179 | 180 | return G 181 | end 182 | 183 | 184 | function DeArrayfy(A::Array{T, 3}, grid::Vector{Int64}) :: Array{Matrix{T}, length(grid)} where {T<:Number} 185 | 186 | @cast G[k][i, j] |= A[k, i, j] 187 | G = reshape(G, grid...) 188 | 189 | return G 190 | end 191 | 192 | function FFTArrayofMatrix(Gs::Array{Matrix{ComplexF64}, T}) where {T} 193 | 194 | N = size(Gs[begin]) ##### size of the matrix being FFTed 195 | grid = size(Gs) ##### size of the array of matrices 196 | 197 | G = reshape(Gs, prod(grid)) ##### Flattening all super-indices for casting 198 | @cast G[k, i, j] |= G[k][i, j] ##### Casting into an array for FFTW 199 | G = reshape(G, grid..., N...) ##### Unflattenng all super-indices back to their original ranks 200 | 201 | Gr = fft(G, collect(1 : length(grid))) ##### FFT on the momentum indices 202 | Gr = reshape(Gr, prod(grid), N...) 203 | @cast Gr[k][i, j] |= Gr[k, i, j] 204 | 205 | Gr = reshape(Gr, grid...) / prod(grid) 206 | 207 | return Gr 208 | end 209 | 210 | 211 | @doc """ 212 | f : Can be an array of anything (even static arrays) 213 | delta : vector of real numbers with length = spatial dimensions = rank of f ---> the displacement vector when calculating the gradient 214 | PBC : vector of boolean with length = spatial dimensions = rank of f ---> if the ith dimension has PBC or not. 215 | 216 | """ 217 | function Central_Diff(f::Array{Any} ; delta::Vector{Float64} = ones(Float64, length(size(f))), PBC::Vector{Bool} = repeat([true], length(size(f)))) 218 | dims = length(size(f)) 219 | IdMat = Matrix(1I, dims, dims) ##### Identity matrix of size dims x dims where dims = rank of f = "spatial "dimensions. Eg in 3d f=f(x, y, z) = f[ix, iy, iz] => dims=3 220 | f_plus = circshift.(Ref(f), eachrow(IdMat)) 221 | """ circshift(f, v) shifts the array indices of f by a vector v. 222 | eachrow(IdMat) basically gives [1, 0, ..., 0], [0, 1, 0, ..., 0] and so on. 223 | f_plus is a vector of arrays of same dimensions as f s.t. f_plus[i] is f shifted by one index to the right along the ith dimension""" 224 | f_minus = circshift.(Ref(f), eachrow(-IdMat)) 225 | """ f_minus is a vector of arrays of same dimensions as f s.t. f_minus[i] is f shifted by one index to the left along the ith dimension""" 226 | diff = (f_minus .- f_plus) ./ (2*delta) 227 | """ diff is another vector of arrays of same dimensions as f. 228 | diff[i][ix1,..., ixn] = d/dx^i(f)(ix1, ..., ixn)""" 229 | 230 | 231 | for i in 1:dims 232 | if !PBC[i] 233 | """ 234 | selectdim(x, i, ind) = x[:, :, ..., ind, :, ..., :] where ind is in the ith dimension 235 | the relevant boundary for d/dx^i is ind=1, end for the ith dimension 236 | For such cases, instead of f_plus-f_minus, we need f_plus-f, and f-f_minus 237 | """ 238 | left_edge = selectdim(diff[i], i, 1) 239 | left_edge .= (selectdim(f_minus[i], i, 1) .- selectdim(f, i, 1)) ./ delta[i] ##### Pointer black magic??? 240 | right_edge = selectdim(diff[i], i, size(f)[i]) 241 | right_edge .= (selectdim(f, i, size(f)[i]) .- selectdim(f_plus[i], i, size(f)[i])) ./ delta[i] 242 | end 243 | end 244 | 245 | return diff 246 | end 247 | 248 | function Central_Diff(f::Array{T, N} ; delta::Vector{Float64} = ones(Float64, length(size(f))), PBC::Vector{Bool} = repeat([true], length(size(f)))) where {T<:Union{Number, Vector{<:Number}, Matrix{<:Number}}, N} 249 | dims = length(size(f)) 250 | IdMat = Matrix(1I, dims, dims) ##### Identity matrix of size dims x dims where dims = rank of f = "spatial "dimensions. Eg in 3d f=f(x, y, z) = f[ix, iy, iz] => dims=3 251 | f_plus = circshift.(Ref(f), eachrow(IdMat)) 252 | """ circshift(f, v) shifts the array indices of f by a vector v. 253 | eachrow(IdMat) basically gives [1, 0, ..., 0], [0, 1, 0, ..., 0] and so on. 254 | f_plus is a vector of arrays of same dimensions as f s.t. f_plus[i] is f shifted by one index to the right along the ith dimension""" 255 | f_minus = circshift.(Ref(f), eachrow(-IdMat)) 256 | """ f_minus is a vector of arrays of same dimensions as f s.t. f_minus[i] is f shifted by one index to the left along the ith dimension""" 257 | diff = (f_minus .- f_plus) ./ (2*delta) 258 | """ diff is another vector of arrays of same dimensions as f. 259 | diff[i][ix1,..., ixn] = d/dx^i(f)(ix1, ..., ixn)""" 260 | 261 | 262 | for i in 1:dims 263 | if !PBC[i] 264 | """ 265 | selectdim(x, i, ind) = x[:, :, ..., ind, :, ..., :] where ind is in the ith dimension 266 | the relevant boundary for d/dx^i is ind=1, end for the ith dimension 267 | For such cases, instead of f_plus-f_minus, we need f_plus-f, and f-f_minus 268 | """ 269 | left_edge = selectdim(diff[i], i, 1) 270 | left_edge .= (selectdim(f_minus[i], i, 1) .- selectdim(f, i, 1)) ./ delta[i] ##### Pointer black magic??? 271 | right_edge = selectdim(diff[i], i, size(f)[i]) 272 | right_edge .= (selectdim(f, i, size(f)[i]) .- selectdim(f_plus[i], i, size(f)[i])) ./ delta[i] 273 | end 274 | end 275 | 276 | return diff 277 | end 278 | 279 | 280 | function GetPhase(v::Vector{ComplexF64}) :: Vector{Float64} 281 | 282 | return mod.(angle.(v) / (2 * pi), Ref(1.0)) 283 | end 284 | 285 | 286 | @doc """ 287 | ```julia 288 | SegmentIntersection(SegmentPoints::Vector{Vector{Float64}}, LinePoints::Vector{Vector{Float64}})-->Float64 289 | ``` 290 | Given two points on a line and two points on a segment, returns the parameter t such that the intersection point is given by (1-t) * SegmentPoints[1] + t * SegmentPoints[2]. 291 | If the intersection point is not on the segment, returns a value outside the range [0, 1]. 292 | """ 293 | function SegmentIntersection(SegmentPoints::Vector{Vector{Float64}}, LinePoints::Vector{Vector{Float64}})::Float64 294 | 295 | M1, M2 = SegmentPoints 296 | r1, r2 = LinePoints 297 | 298 | dx = (r2[1] - r1[1]) 299 | dy = (r2[2] - r1[2]) 300 | dMx = (M2[1] - M1[1]) 301 | dMy = (M2[2] - M1[2]) 302 | 303 | t = (dx * (r1[2] - M1[2]) - dy * (r1[1] - M1[1])) / (dx * dMy - dy * dMx) 304 | 305 | return t 306 | end 307 | 308 | function Linearize(Ns::Tuple{Int64, Int64}, index::Tuple{Int64, Int64})::Int64 309 | 310 | return Ns[2] * (index[1] - 1) + index[2] 311 | end 312 | 313 | function SwitchKroneckerBasis(Ns::Tuple{Int64, Int64})::Vector{Int64} 314 | 315 | indices = Meshgrid(collect(Ns)) 316 | indices = vcat(indices...) 317 | order = Linearize.(Ref(Ns), indices) 318 | 319 | return order 320 | end 321 | 322 | 323 | function ImpIndices(A::Matrix{Float64}, sizeB::Tuple{Int64, Int64})::Vector{Tuple{Int64, Int64}} 324 | inds = Tuple{Int64, Int64}[] 325 | 326 | for (i, j) in Meshgrid(collect(sizeB)) 327 | 328 | B = zeros(Float64, sizeB...) 329 | B[i, j] = 1.0 330 | 331 | C = B * A 332 | 333 | if sum(.!(isapprox.(C, zeros(Float64, size(C))))) > 0 ##### if the matrix product is not zero => this element of B can lead to non-zero result 334 | push!(inds, (i, j)) 335 | end 336 | end 337 | 338 | return inds 339 | end 340 | 341 | 342 | 343 | end -------------------------------------------------------------------------------- /test/Project.toml: -------------------------------------------------------------------------------- 1 | [deps] 2 | FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341" 3 | LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f" 4 | LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" 5 | Logging = "56ddb016-857b-54e1-b83d-db4d58db5568" 6 | MappedArrays = "dbb5928d-eab1-5f90-85c2-b9b0edb7c900" 7 | Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" 8 | PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee" 9 | Statistics = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" 10 | TensorCast = "02d47bb6-7ce6-556a-be16-bb1710789e2b" 11 | Tullio = "bc48ee85-29a4-5162-ae0b-a64e1601d4bc" 12 | Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" 13 | 14 | [compat] 15 | FFTW = "^1.6" 16 | Logging = "^1.6.7" 17 | LaTeXStrings = "1.3" 18 | MappedArrays = "^0.4" 19 | Plots = "^1.38" 20 | PyPlot = "2.11" 21 | Statistics = "1.0 - 2.0" 22 | TensorCast = "^0.4" 23 | Tullio = "^0.3" 24 | julia = "^1.8, ^1.9" 25 | 26 | 27 | -------------------------------------------------------------------------------- /test/runtests.jl: -------------------------------------------------------------------------------- 1 | using TightBindingToolkit 2 | using Test 3 | 4 | @testset "TightBindingToolkit.jl" begin 5 | @test 1==1 6 | end 7 | --------------------------------------------------------------------------------