-
Notifications
You must be signed in to change notification settings - Fork 8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support Dirac points in bandstructures #123
Comments
There is a very important issue that can arise in the approach above: the interpolated set of eigenstates at a given This doesn't mean that the general algorithm is invalid, it just might need to be modified. It essentially consists of two distinct elements: (1) degenerate eigenvalues carry a full basis of their degenerate space for purposes of connecting band vertices, and (2) simplices have eigenstates that do not need to be vertex-unique when compared with neighboring simplices. To solve the Dirac-point issue, what we actually need is (1), which resolves frustrations in the connectivity. We could dispense with (2), in fact. Once connected, we can actually choose vertex-unique states in degenerate subspaces. They might simply not be too similar to the other vertex states in a given simplex with vertex degeneracies. Interpolation of eigenstates will thus not be very smooth. This is inevitable in Dirac points, but not in trivial band crossings, where velocity codiagonalization is optimal. So I see two options to achieve a sane bandstructure with simplex-unique states: keep (1) and the current codiagonalization pass, dropping the last two paragraph in the OP above, or keep (1) and adapt the projection step, dropping the codiagonalization. I'm inclined towards the latter. How would it go? First we would never do copies of degenerate vertex subspaces. This way we have a built-in guarantee that all vertices end up with unique eigenstates, because all transformations we do of subspace states are shared between vertices. We can transform these subspaces in any way we want and, as long as in the end we have different degenerate vertices pick at the end a different column of their space (which will remain orthogonal) we're good. So the crucial change, apart from never doing copies of states, is to know how to transform subspaces at simplex-collection time. The ideal would be to be able to have views with shuffled columns, and use the first one as the axis to align. Views don't support that, but we can create a new wrapper for views that holds the desired alignement column for each vertex (i.e. store the So all steps above will proceed the same, except for replacing the
This is quite a nice method, because it guarantees a unique (and maximally continuous?) sampling of the eigenstate manifold, even in the non-Abelian case of degenerate . This might still have dislocations and jumps, but they will be unavoidable. The one loose end I see is the choice of |
While implementing this proposal I've realized that the complexity of organizing band simplices into groups with the same base simplex (the Marching meshes have the property that the minicuboid a simplex belongs to can be identified by the "lower left corner". That is, all simplices in a minicuboid share a vertex that can be found as the first in the simplex list of vertices when sorted lexicographically (this is not true of other meshings). Then, sorting simplices into minicuboids is straightforward. Accessing them with |
The isse #122 proposes to introduce the struct
Simplices
that contains asvecs::Vector{NTuple{D´,S}}
, whereS<:SubArray
. So,vecs
in each simplex are views of eigenvector matrices produced by diagonalization. This saves a lot of space and indirections, but it also opens the door to solving a long-standing issue in an elegant way: the correct formation of simplices with Dirac-point degeneracies at a vertex. As a bonus, we also solve the case of random non-Abelian connection in multiply degenerate bands.The issue with Dirac points in a vertex is that, however you choose the basis of the degenerate eigenstate in such a vertex, you will find a frustrated connectivity of band vertices, as defined by eigenstates. The Dirac point is actually a dislocation in connectivity. The issue cannot be solved as long as eigenstates remain uniquely associated to vertices. In the
svecs::Vector{NTuple{D´,S}}
implementation we have the freedom, however, to have two different eigenstate bases for the same Dirac-point vertex within two adjacent simplices that share said vertex. Each simplex needs to keep an independent copy of the degenerate view and rediagonalize independently. Exploiting this freedom, the Dirac point problem is solved.This idea is cool, but an actual implementation is far from trivial. It involves changing how we deal with degenerate eigenstates in the first place. We don't need to codiagonalize them as we diagonalize vertices. We do it on a simplex-by-simplex basis when extracting simplices.
This issue is a stab at designing such implementation. It involves dropping the whole codiagonalization code altogether, and has the potential of increasing bandstructure build performance. Since it is important to document the details for the future, we describe the algorithm is full depth here.
Revised Bandstructure algorithm
The redesign of
Bandstructure
would be, according to #122The construction of
Bandstructure
s would have three stepsStep 1: diagonalize
The base mesh is assumed a
BaseMesh{D}
, following #122. We create aevals::Array{Vector{T},D}
andevecs::Array{Vector{SubArray{2,T´}},D}
, whereT´
is the eltype of eigenvectors. These are left uninitialized. The size of theArray
s is the same asvertices(base)
.Iterating over base vertices,
(n, ϕs) in enumerate(vertices(base))
(heren
is aCartesianIndex
), we obtainϵs, ψs = diagonalize(h(ϕs...), ...)
on each.ϵs
should be sorted. We storeevals[n] = ϵs
, making sure to set any degenerateϵs[n]≈ϵs[n´]
to be exactly equalϵs[n]==ϵs[n´]
.Regarding the eigenstates, we allocate and store in
evecs[n]
a vector of eigenstate views of theψs
matriceswhere
The output of
approxruns(ϵs)
should be(rng, shift)
whererng
is the range of indicesj
ofϵ
aroundi in eachindex(ϵ)
such that allϵ[j]
are approximately equal (≈
).shift
should be the offset ofj
intorng
, i.e.shift = j - minimum(rng)
The interpretation of the above is the following:
evecs[n]
will correspond to a 1-column view into its corresponding eigenstate inψs
evecs[n]
will correspond to a d-column view into the corresponding subspace inψs
ψs
, and with a different first eigenstate (thanks to thecircshift
). This is important for step 3.Step 2: extract bands
We now call a version of
extract_band
adapted to the format of newevecs
andevals
.It proceeds by incrementally classifying all
evals
andevecs
into different bands, while at the same time pushing them into flatVector
s ofBandMesh
vertices and creating the corresponding adjacency matrix. We now revisiting howextract_band
works, and adapt it toevecs
andevals
We start by creating an array
vertexindices::Array{Vector{Int},D}
similar toevals
that serves double duty, as a registry of unprocessed vertices (0), already processed vertices (-1) and in the process of being added to a newBandMesh
(vertex index within the band). We initalize it with zeros.We create an empty list of band mesh vertices
verts
and reuseI,J
to construct sparseadjmat
that will be populated to create aBandMesh
.We also create a first-in-last-out queue
pending = [(srcidx, dstidx),...]
that contains vertex indices of connected vertex pairs that have still not been added to the band.srcidx
anddstidx
are actually of typeTuple{CartesianIndex{D},Int}
, and point to specific entries invertexindices
,evecs
andevals
. Thepending
queue starts empty.We need to keep also a
processed::Vector{Tuple{CartesianIndex{D},Int}}
which stores theevals
andevecs
indices of each processed vertex in order. Thisprocessed
is necessary to construct the overall set ofSimplices
in step 3.extract_band
algorithm:Create an empty
processed
,pending
,I
andJ
.(START) We identify the first non-zero element in
vertexindices
, with indexseedidx
. If none exist,break
.Resize reusable
pending
,I
andJ
to zero length. Create an emptyverts
vector.We
push!(pending, (seedidx, seedidx))
processed_offset = length(processed)
While !isempty(pending)
(srcidx, dstidx) = pop!(pending)
push!
a new vertexdstidx
intoverts
push!(processed, dstidx)
vertexindices[dstidx] = length(verts)
srcidx != dstidx
(i.e. not seed) wepush!
the adjacentvertexindices[srcidx]
andvertexindex[dstidx]
intoI,J
dstbase
ofsrcbase = first(srcidx)
inbase
(proj, m´) = findmostparallel(evecs, dstbase, srcidx, vertexindices[dstbase])
will select the most-parallel target vertexevals[dstbase][m´]
proj
is above threshold andm´ != 0
wepush!(pending, (srcidx, (dstbase, m´)))
After this concludes (
pending
is empty), constructadjmat
, buildsimpindices
from adjmat, and push aBandMesh
object into thebands
vector.Set all non-zero vertexindices to -1
Go back to (START)
I think there is currently a type-instability in the master version of this
extract_band
algorithm (srcidx
is not type-stable?), so I wouldn't be surprised if this version would run faster. We also assume in master that all base vertices have the same number of eigenvalues, which need not be true if we are using Arnoldi methods with some libraries.A key modification needs to be made to
findmostparallel
to handle Dirac points. We need to ensure that if a Dirac point occurs at a given base vertex (i.e. we have two copies of the Dirac 2D degenerate subspaces), the same copy will be selected from different angles within the same band. If a Dirac point occurs within a simplex, that will lead to a dislocation, and the corresponding simplex will be omitted (to be checked). So how do we do that?We have multicolumn views
evecs[n][m]
for degenerate subspaces, soproj(ψdst, ψsrc) = sum(abs2, ψdst' * ψsrc)
.m´
should be the state with maximum overlap, unless itsvertexindex
is non-zero, in which case we returnm´ = 0
. This sentinel value means "do not add any vertex from thisdstbase
column to the band". In the presence of degeneracies, however, different copies of degenerate vertices will exhibit the same overlap. If the maximum overlap has ties, anm´ = 0
should returned if either all tied vertexindices are-1
(dislocation) or if any of them is positive (already classified in this band). Otherwise, return anm´
with zerovertexindex
.Step 3: collect simplices
At the end of step 2 we will have constructed the list of base indices
processed
corresponding toband.verts for band in bands
. We now need to use this, in combination withband.simpinds for band in bands
to buildsverts
(simplex vertices),svecs
(simplex states) andsptrs
(mapping of base simplices to range of bandstructure simplices) that go intoBandstructure
'ssimplices::Simplices
.The strategy is the following
Create temporary arrays
sverts::Array{Vector{NTuple{D´,V}}, D´}
andsvecs::Array{Vector{NTuple{D´,S}}, D´}
, whereV = eltype(first(evals))
andS = eltype(first(evecs))
. We will collect all simplex vertices and vectors from all bands here.For each
is::NTuple{D´,Int}
inband.simpinds
we obtain their band indicesinds = getindex.(Ref(processed), is)
, where each element ofinds
is a(n,m)::Tuple{CartesianIndex{D},Int}
, i.e. a base index and a column index.We extract the base vertex indices
basevinds = first.(inds)
. These should correspond to a simplex in the base mesh, but with shuffled order. We dobasesinds = (n0..., s) = basesimplex(basevinds)
to get the indices of the simplex in the base mesh, andinds´ = sortsimplex(inds)
to get full(n,m)
indicesinds
into canonical order.We do
svert = NTuple{D´}(vcat(base[n]..., evals[n][m]) for (n,m) in inds´)
andpush!(sverts[basesinds...], svert)
The same with vectors:
evec = NTuple{D´}(evecs[n][m] for (n,m) in inds´)
andpush!(svecs[basesinds...], evec)
We do the above with all bands. Finally, we can count how many
svecs
andsverts
, and copy them to contiguous vectorssverts´::Vector{Ntuple{D´,V}}
andsvecs´::Vector{Ntuple{D´,S}}
, fillingsptrs::Array{UnitRange{Int}, D´}
along the way.There is one important detail that is missing above. We have simply copied
evecs[n,m]
tosvecs´
, but these areSubMatrix
, since they may come from degenerate subspaces. We need to project these ontoSubVector
s, with the specific direction in the subspace fixed simplex by simplex. Here is the core of the Dirac point resolution. The resultingsvecs´
will thus be of typesvecs´::Vector{NTuple{D´,S´}} where S´<:SubArray{T´,1}
.The first step is to take the least-degenerate subspaces in the simplex, and select the first column of each as the
SubVector
for that vertex. We denote byprojection_inds
their indices withinsvecs´
. No copy is involved here, just a view of a view. Due to thecircshift
in step 1 we are sure that theseSubVector
s will all be orthogonal to other copies atop the same base vertex, so no redundacies should occur. Then, the orientation of the remaining vertices with higher degeneracy (if any) will be decided by the (now 1D)SubVector
vertices. To avoid aliasing, these remaining states will be first copied, then projected, and then a view will be made of them, to preserve the eltype ofsvecs´
. The projection will be made ontovsum = sum(i -> svecs´[:,i], projection_inds) |> normalize!
, so as to maximize overlap with all projection vectors.The text was updated successfully, but these errors were encountered: